
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\public-api.ts

/*
 * Public API Surface of x-framework-components
 */

//
//#region Export Components ...
//
//#region Component Modules ...
export * from './lib/x-ide/x-ide.module';
export * from './lib/x-fab/x-fab.module';
export * from './lib/x-map/x-map.module';
export * from './lib/x-icon/x-icon.module';
export * from './lib/x-card/x-card.module';
export * from './lib/x-list/x-list.module';
export * from './lib/x-form/x-form.module';
export * from './lib/x-grid/x-grid.module';
export * from './lib/x-page/x-page.module';
export * from './lib/x-tabs/x-tabs.module';
export * from './lib/x-tree/x-tree.module';
export * from './lib/x-chart/x-chart.module';
export * from './lib/x-theme/x-theme.module';
export * from './lib/x-table/x-table.module';
export * from './lib/x-frame/x-frame.module';
export * from './lib/x-empty/x-empty.module';
export * from './lib/x-color/x-color.module';
export * from './lib/dialogs/x-dialog.module';
export * from './lib/x-button/x-button.module';
export * from './lib/x-slider/x-slider.module';
export * from './lib/x-slotter/x-slotter.module';
export * from './lib/x-spinner/x-spinner.module';
export * from './lib/x-stepper/x-stepper.module';
export * from './lib/x-counter/x-counter.module';
export * from './lib/x-markdown/x-markdown.module';
export * from './lib/x-thumbnail/x-thumbnail.module';
export * from './lib/x-data-pager/x-data-pager.module';
export * from './lib/x-search-bar/x-search-bar.module';
export * from './lib/x-action-bar/x-action-bar.module';
export * from './lib/x-file-upload/x-file-upload.module';
export * from './lib/x-progress-bar/x-progress-bar.module';
export * from './lib/x-color-picker/x-color-picker.module';
export * from './lib/x-data-scroller/x-data-scroller.module';
export * from './lib/x-image-cropper/x-image-cropper.module';
export * from './lib/x-tab-navigator/x-tab-navigator.module';
export * from './lib/x-avatar-upload/x-avatar-upload.module';
export * from './lib/x-file-drop-area/x-file-drop-area.module';
export * from './lib/x-screen-capture/x-screen-capture.module';
export * from './lib/x-model-presenter/x-model-presenter.module';
export * from './lib/x-expandable-list/x-expandable-list.module';
export * from './lib/x-expandable-panel/x-expandable-panel.module';
export * from './lib/x-description-list/x-description-list.module';
//#endregion

//
//#region Component ItSelfd ...
export * from './lib/x-ide/x-ide.component';
export * from './lib/x-fab/x-fab.component';
export * from './lib/x-map/x-map.component';
export * from './lib/x-form/x-form.component';
export * from './lib/x-icon/x-icon.component';
export * from './lib/x-card/x-card.component';
export * from './lib/x-list/x-list.component';
export * from './lib/x-grid/x-grid.component';
export * from './lib/x-page/x-page.component';
export * from './lib/x-tabs/x-tabs.component';
export * from './lib/x-tree/x-tree.component';
export * from './lib/x-chart/x-chart.component';
export * from './lib/x-color/x-color.component';
export * from './lib/x-empty/x-empty.component';
export * from './lib/x-frame/x-frame.component';
export * from './lib/x-table/x-table.component';
export * from './lib/x-button/x-button.component';
export * from './lib/x-slider/x-slider.component';
export * from './lib/x-tabs/x-tab/x-tab.component';
export * from './lib/x-spinner/x-spinner.component';
export * from './lib/x-slotter/x-slotter.component';
export * from './lib/x-stepper/x-stepper.component';
export * from './lib/x-counter/x-counter.component';
export * from './lib/x-markdown/x-markdown.component';
export * from './lib/x-stepper/x-step/x-step.component';
export * from './lib/x-slotter/x-slot/x-slot.component';
export * from './lib/x-thumbnail/x-thumbnail.component';
export * from './lib/x-slider/x-slide/x-slide.component';
export * from './lib/x-page/x-footer/x-footer.component';
export * from './lib/x-data-pager/x-data-pager.component';
export * from './lib/x-search-bar/x-search-bar.component';
export * from './lib/x-action-bar/x-action-bar.component';
export * from './lib/x-page/x-toolbar/x-toolbar.component';
export * from './lib/dialogs/x-popover/x-popover.component';
export * from './lib/x-file-upload/x-file-upload.component';
export * from './lib/x-progress-bar/x-progress-bar.component';
export * from './lib/x-color-picker/x-color-picker.component';
export * from './lib/x-page/x-navigator/x-navigator.component';
export * from './lib/x-data-scroller/x-data-scroller.component';
export * from './lib/x-map/x-map-dialog/x-map-dialog.component';
export * from './lib/x-image-cropper/x-image-cropper.component';
export * from './lib/x-tab-navigator/x-tab-navigator.component';
export * from './lib/x-avatar-upload/x-avatar-upload.component';
export * from './lib/x-file-drop-area/x-file-drop-area.component';
export * from './lib/x-screen-capture/x-screen-capture.component';
export * from './lib/x-expandable-panel/x-panel/x-panel.component';
export * from './lib/x-expandable-list/x-expandable-list.component';
export * from './lib/x-model-presenter/x-model-presenter.component';
export * from './lib/x-expandable-panel/x-expandable-panel.component';
export * from './lib/x-description-list/x-description-list.component';
export * from './lib/x-theme/x-theme-manager/x-theme-manager.component';
export * from './lib/dialogs/x-selector-dialog/x-selector-dialog.component';
export * from './lib/x-color-picker/x-color-picker-dialog/x-color-picker-dialog.component';
export * from './lib/x-model-presenter/x-model-presenter-item/x-model-presenter-item.component';
export * from './lib/x-description-list/x-description-list-item/x-description-list-item.component';
//#endregion

//
//#region Component Typings ...
export * from './lib/x-ide/x-ide.typings';
export * from './lib/x-fab/x-fab.typings';
export * from './lib/x-map/x-map.typings';
export * from './lib/x-grid/x-grid.typings';
export * from './lib/x-form/x-form.typings';
export * from './lib/x-icon/x-icon.typings';
export * from './lib/x-card/x-card.typings';
export * from './lib/x-list/x-list.typings';
export * from './lib/x-page/x-page.typings';
export * from './lib/x-tabs/x-tabs.typings';
export * from './lib/x-tree/x-tree.typings';
export * from './lib/x-empty/x-empty.typings';
export * from './lib/x-frame/x-frame.typings';
export * from './lib/x-page/x-page.container';
export * from './lib/x-chart/x-chart.typings';
export * from './lib/x-color/x-color.typings';
export * from './lib/x-theme/x-theme-typings';
export * from './lib/x-table/x-table.typings';
export * from './lib/x-button/x-button.typings';
export * from './lib/x-slider/x-slider.typings';
export * from './lib/x-stepper/x-stepper.typings';
export * from './lib/x-slotter/x-slotter.typings';
export * from './lib/x-counter/x-counter.typings';
export * from './lib/x-markdown/x-markdown.typings';
export * from './lib/x-thumbnail/x-thumbnail.typings';
export * from './lib/x-page/x-footer/x-footer.typings';
export * from './lib/x-data-pager/x-data-pager.typings';
export * from './lib/x-action-bar/x-action-bar.typings';
export * from './lib/x-search-bar/x-search-bar.typings';
export * from './lib/x-page/x-toolbar/x-toolbar.typings';
export * from './lib/dialogs/x-popover/x-popover.typings';
export * from './lib/x-file-upload/x-file-upload.typings';
export * from './lib/x-progress-bar/x-progress-bar.typings';
export * from './lib/x-color-picker/x-color-picker.typings';
export * from './lib/x-page/x-navigator/x-navigator.typings';
export * from './lib/x-data-scroller/x-data-scroller.typings';
export * from './lib/x-image-cropper/x-image-cropper.typings';
export * from './lib/x-tab-navigator/x-tab-navigator.typings';
export * from './lib/x-avatar-upload/x-avatar-upload.typings';
export * from './lib/x-map/x-map-dialog/x-map-dialog.typings';
export * from './lib/x-file-drop-area/x-file-drop-area.typings';
export * from './lib/x-screen-capture/x-screen-capture.typings';
export * from './lib/x-screen-capture/x-screen-capture.container';
export * from './lib/x-expandable-list/x-expandable-list.typings';
export * from './lib/x-model-presenter/x-model-presenter.typings';
export * from './lib/x-expandable-panel/x-expandable-panel.typings';
export * from './lib/x-description-list/x-description-list.typings';
export * from './lib/x-theme/x-theme-manager/x-theme-manager.typings';
export * from './lib/dialogs/x-selector-dialog/x-selector-dialog.typings';
export * from './lib/x-color-picker/x-color-picker-dialog/x-color-picker-dialog.typings';
export * from './lib/x-model-presenter/x-model-presenter-item/x-model-presenter-item.typings';
export * from './lib/x-description-list/x-description-list-item/x-description-list-item.typings';
//#endregion

//
//#region Component Tools ...
export * from './lib/x-map/x-map.tools';
export * from './lib/x-form/x-form.tools';
export * from './lib/x-theme/x-theme.tools';
export * from './lib/dialogs/x-popover/x-popover.tools';
export * from './lib/x-form/validators/x-form-validators';
export * from './lib/dialogs/x-popover/x-popover.service';
export * from './lib/x-slider/x-slide.animation.providers';
export * from './lib/x-page/x-navigator/x-navigator.tools';
export * from './lib/x-tab-navigator/x-tab-navigator.tools';
export * from './lib/x-map/x-map-dialog/x-map-dialog.service';
export * from './lib/x-image-cropper/x-image-cropper.service';
export * from './lib/x-screen-capture/x-screen-capture.service';
export * from './lib/x-expandable-list/x-expandable-list.tools';
export * from './lib/x-color-picker/directives/x-color-text.directive';
export * from './lib/dialogs/x-selector-dialog/x-selector-dialog.service';
export * from './lib/x-color-picker/x-color-picker-dialog/x-color-picker-dialog.service';
//#endregion
//#endregion

//
//#region Export Classes ...
//
export * from './lib/classes/x-base-page';
export * from './lib/classes/x-base-component';
export * from './lib/classes/i-x-base.container';
export * from './lib/classes/i-x-file.container';
export * from './lib/classes/i-x-toolbar-footer';
export * from './lib/classes/x-base-action.model';
export * from './lib/classes/x-base-dialog.service';
export * from './lib/classes/x-base.dialog.component';
export * from './lib/classes/x-base.observable.tools';
export * from './lib/classes/x-base.dialog.component';
export * from './lib/classes/i-x-card-empty.container';
export * from './lib/classes/i-x-base-dialog.container';
export * from './lib/classes/i-x-next-prev-reset.container';
//
export * from './lib/dialogs/x-dialog/x-dialog.typings';
export * from './lib/dialogs/x-dialog/x-dialog.component';
//#endregion

//
//#region Tokens ...
export * from './lib/tokens/x-injectable-tokens';
//#endregion

//
//#region Export Config ...
export * from './lib/config/x-framework-components.config';
//#endregion

//
//#region Export Modules ...
export * from './lib/modules/x-material.module';
export * from './lib/x-framework-components.module';
export * from './lib/modules/x-base-component.module';
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\test.ts

// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

declare const require: {
  context(path: string, deep?: boolean, filter?: RegExp): {
    keys(): string[];
    <T>(id: string): T;
  };
};

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-framework-components.module.ts

import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { XIDEModule } from './x-ide/x-ide.module';
import { XFabModule } from './x-fab/x-fab.module';
import { XMapModule } from './x-map/x-map.module';
import { XIconModule } from './x-icon/x-icon.module';
import { XCardModule } from './x-card/x-card.module';
import { XListModule } from './x-list/x-list.module';
import { XFormModule } from './x-form/x-form.module';
import { XGridModule } from './x-grid/x-grid.module';
import { XPageModule } from './x-page/x-page.module';
import { XTabsModule } from './x-tabs/x-tabs.module';
import { XFrameModule } from './x-frame/x-frame.module';
import { XEmptyModule } from './x-empty/x-empty.module';
import { XTableModule } from './x-table/x-table.module';
import { XFrameworkCoreModule } from 'x-framework-core';
import { XThemeModule } from './x-theme/x-theme.module';
import { XColorModule } from './x-color/x-color.module';
import { XChartModule } from './x-chart/x-chart.module';
import { XSliderModule } from './x-slider/x-slider.module';
import { XButtonModule } from './x-button/x-button.module';
import { XDialogsModule } from './dialogs/x-dialog.module';
import { XMaterialModule } from './modules/x-material.module';
import { XSlotterModule } from './x-slotter/x-slotter.module';
import { XSpinnerModule } from './x-spinner/x-spinner.module';
import { XStepperModule } from './x-stepper/x-stepper.module';
import { XCounterModule } from './x-counter/x-counter.module';
import { XFrameworkServicesModule } from 'x-framework-services';
import { XMarkdownModule } from './x-markdown/x-markdown.module';
import { XThumbnailModule } from './x-thumbnail/x-thumbnail.module';
import { XActionBarModule } from './x-action-bar/x-action-bar.module';
import { XSearchBarModule } from './x-search-bar/x-search-bar.module';
import { XDataPagerModule } from './x-data-pager/x-data-pager.module';
import { XBaseComponentModule } from './modules/x-base-component.module';
import { XFileUploadModule } from './x-file-upload/x-file-upload.module';
import { XColorPickerModule } from './x-color-picker/x-color-picker.module';
import { XProgressBarModule } from './x-progress-bar/x-progress-bar.module';
import { XImageCropperModule } from './x-image-cropper/x-image-cropper.module';
import { XTabNavigatorModule } from './x-tab-navigator/x-tab-navigator.module';
import { XAvatarUploadModule } from './x-avatar-upload/x-avatar-upload.module';
import { XDataScrollerModule } from './x-data-scroller/x-data-scroller.module';
import { XFileDropAreaModule } from './x-file-drop-area/x-file-drop-area.module';
import { XScreenCaptureModule } from './x-screen-capture/x-screen-capture.module';
import { XExpandableListModule } from './x-expandable-list/x-expandable-list.module';
import { XModelPresenterModule } from './x-model-presenter/x-model-presenter.module';
import { XExpandablePanelModule } from './x-expandable-panel/x-expandable-panel.module';
import { XDescriptionListModule } from './x-description-list/x-description-list.module';

@NgModule({
  declarations: [],
  imports: [
    //
    IonicModule,
    RouterModule,
    CommonModule,

    //
    XDialogsModule,

    //
    XMaterialModule,
    XBaseComponentModule,

    //
    XFrameworkCoreModule,
    XFrameworkServicesModule,

    //
    XIDEModule,
    XFabModule,
    XMapModule,
    XPageModule,
    XIconModule,
    XFormModule,
    XCardModule,
    XListModule,
    XTabsModule,
    XGridModule,
    XChartModule,
    XFrameModule,
    XEmptyModule,
    XColorModule,
    XTableModule,
    XThemeModule,
    XButtonModule,
    XSliderModule,
    XSlotterModule,
    XSpinnerModule,
    XStepperModule,
    XCounterModule,
    XMarkdownModule,
    XDataPagerModule,
    XSearchBarModule,
    XThumbnailModule,
    XActionBarModule,
    XFileUploadModule,
    XProgressBarModule,
    XColorPickerModule,
    XDataScrollerModule,
    XTabNavigatorModule,
    XImageCropperModule,
    XFileDropAreaModule,
    XAvatarUploadModule,
    XScreenCaptureModule,
    XExpandableListModule,
    XModelPresenterModule,
    XExpandablePanelModule,
    XDescriptionListModule,
  ],
  exports: [
    //
    IonicModule,
    RouterModule,
    CommonModule,

    //
    XDialogsModule,

    //
    XMaterialModule,
    XBaseComponentModule,

    //
    XFrameworkCoreModule,
    XFrameworkServicesModule,

    //
    XIDEModule,
    XFabModule,
    XMapModule,
    XPageModule,
    XIconModule,
    XFormModule,
    XCardModule,
    XListModule,
    XTabsModule,
    XGridModule,
    XEmptyModule,
    XChartModule,
    XFrameModule,
    XColorModule,
    XTableModule,
    XThemeModule,
    XButtonModule,
    XSliderModule,
    XSlotterModule,
    XSpinnerModule,
    XStepperModule,
    XCounterModule,
    XMarkdownModule,
    XDataPagerModule,
    XSearchBarModule,
    XActionBarModule,
    XThumbnailModule,
    XFileUploadModule,
    XProgressBarModule,
    XColorPickerModule,
    XDataScrollerModule,
    XTabNavigatorModule,
    XImageCropperModule,
    XFileDropAreaModule,
    XAvatarUploadModule,
    XScreenCaptureModule,
    XExpandableListModule,
    XModelPresenterModule,
    XExpandablePanelModule,
    XDescriptionListModule,
  ],
})
export class XFrameworkComponentsModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\i-x-base-dialog.container.ts

import { XResourceIdentifier, XStandardType } from 'x-framework-core';

export interface XBaseDialogConfig {
  title?: XStandardType<XResourceIdentifier>;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\i-x-base.container.ts

import { Observable } from 'rxjs';
import { Component } from '@angular/core';
import { XBaseComponent } from './x-base-component';

//
//#region Container ...
export interface IXBaseContainer {}

export abstract class XBaseContainer implements IXBaseContainer {}

@Component({
  template: '',
})
export abstract class XBaseContainerComponent extends XBaseComponent {
  //
  //#region Abstracts ...
  public abstract extractProps(container?: any): Observable<any>;
  //#endregion

  //
  //#region Props Actions ...
  public async extractPropsAsync(container?: any): Promise<any> {
    return await this.getValueAsync(this.extractProps(container));
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
export interface IXBaseComponentContainer {}

export abstract class XBaseComponentContainer
  implements IXBaseComponentContainer {}

@Component({
  template: '',
})
export abstract class XBaseComponentContainerComponent extends XBaseComponent {
  //
  //#region Abstracts ...
  public abstract extractProps(container?: any): Observable<any>;
  //#endregion

  //
  //#region Props Actions ...
  public async extractPropsAsync(container?: any): Promise<any> {
    return await this.getValueAsync(this.extractProps(container));
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\i-x-card-empty.container.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
} from 'x-framework-core';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from './i-x-base.container';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
} from '../x-card/x-card.typings';
import {
  XEmptyContainer,
  XEmptyActionModel,
  IXComponentEmptyContainer,
} from '../x-empty/x-empty.typings';
import {
  XActionBarItem,
  XActionBarActionModel,
  XActionBarActionFiredModel,
  XComponentActionBarContainer,
  IXComponentActionBarContainer,
} from '../x-action-bar/x-action-bar.typings';
import {
  XSearchBarActionModel,
  XSearchBarTypeIdentifier,
  XSearchBarEventIdentifier,
  XComponentSearchBarContainer,
  IXComponentSearchBarContainer,
} from '../x-search-bar/x-search-bar.typings';
import { concatMap, map } from 'rxjs/operators';
import { Observable, Subject, forkJoin } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region XCardEmptyContainer ...
//
type TXCardEmptyContainer = IXComponentCardContainer &
  IXComponentEmptyContainer;
export interface IXCardEmptyContainer extends TXCardEmptyContainer { }

//
@Injectable({
  providedIn: 'root',
})
export class XCardEmptyContainer
  extends XBaseContainer
  implements IXCardEmptyContainer {
  //
  //#region XComponentCard ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  //#endregion

  //
  //#region XComponentEmpty ...
  //
  readonly showEmpty: XStandardType<boolean> = true;
  readonly emptyHasRefresh: XStandardType<boolean> =
    this.emptyPropertyProvider.hasRefresh;

  //
  readonly emptyActionProvider: Subject<XEmptyActionModel> =
    this.emptyPropertyProvider.actionProvider;
  readonly emptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.emptyPropertyProvider.cssClass;
  readonly emptyColor: XStandardType<XColorIdentifier> =
    this.emptyPropertyProvider.color;
  readonly emptyForegroundColor: XStandardType<boolean> =
    this.emptyPropertyProvider.foregroundColor;
  readonly emptyMessageColor: XStandardType<XColorIdentifier> =
    this.emptyPropertyProvider.messageColor;
  readonly emptyForegroundMessageColor: XStandardType<boolean> =
    this.emptyPropertyProvider.foregroundMessageColor;
  readonly emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.emptyPropertyProvider.refreshButtonColor;
  readonly emptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.emptyPropertyProvider.foregroundRefreshButtonColor;
  readonly emptyMessage: XStandardType<XResourceIdentifier> =
    this.emptyPropertyProvider.message;
  readonly emptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.emptyPropertyProvider.refreshIcon;
  readonly emptyRefresh: EventEmitter<void> =
    this.emptyPropertyProvider.refresh;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected emptyPropertyProvider: XEmptyContainer
  ) {
    super();
  }
  //#endregion
}

//
@Injectable()
@Component({
  template: ``,
})
export class XCardEmptyContainerComponent
  extends XBaseContainerComponent
  implements IXCardEmptyContainer {
  //
  //#region Props ...
  //
  //#region XComponentCard ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;
  //#endregion

  //
  //#region XComponentEmpty ...
  //
  @Input()
  showEmpty: XStandardType<boolean> = this.propertyProvider.showEmpty;

  @Input()
  emptyHasRefresh: XStandardType<boolean> =
    this.propertyProvider.emptyHasRefresh;

  @Input()
  emptyActionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.emptyActionProvider;

  @Input()
  emptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.emptyCssClass;

  @Input()
  emptyColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyColor;

  @Input()
  emptyForegroundColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundColor;

  @Input()
  emptyMessageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyMessageColor;

  @Input()
  emptyForegroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundMessageColor;

  @Input()
  emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyRefreshButtonColor;

  @Input()
  emptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundRefreshButtonColor;

  @Input()
  emptyMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.emptyMessage;

  @Input()
  emptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.emptyRefreshIcon;

  @Output()
  emptyRefresh: EventEmitter<void> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardEmptyContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCardEmptyContainer
  ): Observable<IXCardEmptyContainer> {
    return forkJoin({
      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region XComponentEmpty ...
      //
      showEmpty: this.getValue(
        container ? container.showEmpty : this.showEmpty
      ),
      emptyHasRefresh: this.getValue(
        container ? container.emptyHasRefresh : this.emptyHasRefresh
      ),

      //
      emptyCssClass: this.getValue(
        container ? container.emptyCssClass : this.emptyCssClass
      ),
      emptyColor: this.getValue(
        container ? container.emptyColor : this.emptyColor
      ),
      emptyForegroundColor: this.getValue(
        container ? container.emptyForegroundColor : this.emptyForegroundColor
      ),
      emptyMessageColor: this.getValue(
        container ? container.emptyMessageColor : this.emptyMessageColor
      ),
      emptyForegroundMessageColor: this.getValue(
        container
          ? container.emptyForegroundMessageColor
          : this.emptyForegroundMessageColor
      ),
      emptyRefreshButtonColor: this.getValue(
        container
          ? container.emptyRefreshButtonColor
          : this.emptyRefreshButtonColor
      ),
      emptyForegroundRefreshButtonColor: this.getValue(
        container
          ? container.emptyForegroundRefreshButtonColor
          : this.emptyForegroundRefreshButtonColor
      ),
      emptyMessage: this.getValue(
        container ? container.emptyMessage : this.emptyMessage
      ),
      emptyRefreshIcon: this.getValue(
        container ? container.emptyRefreshIcon : this.emptyRefreshIcon
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          emptyActionProvider: container
            ? container.emptyActionProvider
            : this.emptyActionProvider,

          //
          emptyRefresh: this.emptyRefresh,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region XCardActionBarContainer ...
type TXCardActionBarContainer = IXComponentCardContainer & IXComponentActionBarContainer;
export interface IXCardActionBarContainer extends TXCardActionBarContainer { }

@Injectable({
  providedIn: 'root',
})
export class XCardActionBarContainer extends XBaseContainer implements IXCardActionBarContainer {
  //
  //#region Props ...
  //
  //#region ComponentCard Props ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  //#endregion

  //
  //#region ComponentActionBar Props ...
  readonly showActionBar: XStandardType<boolean> =
    this.actionBarPropertyProvider.showActionBar;
  readonly actionBarActionProvider: Subject<XActionBarActionModel> =
    this.actionBarPropertyProvider.actionBarActionProvider;
  readonly actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.actionBarPropertyProvider.actionBarCssClass;
  readonly actionBarColor: XStandardType<XColorIdentifier> =
    this.actionBarPropertyProvider.actionBarColor;
  readonly actionBarForegroundColor: XStandardType<boolean> =
    this.actionBarPropertyProvider.actionBarForegroundColor;
  readonly actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.actionBarPropertyProvider.actionBarActions;
  readonly actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.actionBarPropertyProvider.actionBarActionFired;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected actionBarPropertyProvider: XComponentActionBarContainer
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export class XCardActionBarContainerComponent
  extends XBaseContainerComponent
  implements IXCardActionBarContainer {
  //
  //#region Props ...
  //
  //#region ComponentCard Props ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;
  //#endregion

  //
  //#region ComponentActionBar Props ...
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  @Input()
  actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;

  @Input()
  actionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionBarActionProvider;

  @Output()
  actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.propertyProvider.actionBarActionFired;

  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actionBarActions;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardActionBarContainer,
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstracts ...
  public extractProps(container?: IXCardActionBarContainer): Observable<IXCardActionBarContainer> {
    return forkJoin({
      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region XComponentActionBar ...
      showActionBar: this.getValue(
        container ? container.showActionBar : this.showActionBar
      ),
      actionBarCssClass: this.getValue(
        container ? container.actionBarCssClass : this.actionBarCssClass
      ),
      actionBarColor: this.getValue(
        container ? container.actionBarColor : this.actionBarColor
      ),
      actionBarForegroundColor: this.getValue(
        container
          ? container.actionBarForegroundColor
          : this.actionBarForegroundColor
      ),
      actionBarActions: this.getValue(
        container ? container.actionBarActions : this.actionBarActions
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          actionBarActionProvider: container
            ? container.actionBarActionProvider
            : this.actionBarActionProvider,

          //
          actionBarActionFired: this.actionBarActionFired,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region XCardEmptyActionSearchBarContainer ...
//
type TXCardEmptyActionSearchBarContainer = IXCardEmptyContainer &
  IXComponentActionBarContainer &
  IXComponentSearchBarContainer;
export interface IXCardEmptyActionSearchBarContainer
  extends IXBaseContainer,
  TXCardEmptyActionSearchBarContainer { }

//
@Injectable({
  providedIn: 'root',
})
export class XCardEmptyActionSearchBarContainer
  extends XCardEmptyContainer
  implements IXCardEmptyActionSearchBarContainer {
  //
  //#region Props ...
  //
  //#region ActionBar ...
  //
  readonly showActionBar: XStandardType<boolean> =
    this.actionBarPropertyProvider.showActionBar;

  //
  readonly actionBarActionProvider: Subject<XActionBarActionModel> =
    this.actionBarPropertyProvider.actionBarActionProvider;
  readonly actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.actionBarPropertyProvider.actionBarCssClass;

  //
  readonly actionBarColor: XStandardType<XColorIdentifier> =
    this.actionBarPropertyProvider.actionBarColor;
  readonly actionBarForegroundColor: XStandardType<boolean> =
    this.actionBarPropertyProvider.actionBarForegroundColor;

  //
  readonly actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.actionBarPropertyProvider.actionBarActions;

  //
  readonly actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.actionBarPropertyProvider.actionBarActionFired;
  //#endregion

  //
  //#region SearchBar ...
  //
  readonly showSearchBar: XStandardType<boolean> =
    this.searchBarPropertyProvider.showSearchBar;

  //
  readonly searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.searchBarPropertyProvider.searchBarActionProvider;
  readonly searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.searchBarPropertyProvider.searchBarCssClass;

  //
  readonly searchBarColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarColor;
  readonly searchBarForegroundColor: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarForegroundColor;

  //
  readonly searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchInputColor;
  readonly searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarForegroundSearchInputColor;

  //
  readonly searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.searchBarPropertyProvider.searchBarType;
  readonly searchBarTitle: XStandardType<XResourceIdentifier> =
    this.searchBarPropertyProvider.searchBarTitle;
  readonly searchBarValue: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarValue;
  readonly searchBarEnterKeyHint: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarEnterKeyHint;
  readonly searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.searchBarPropertyProvider.searchBarKeyboardType;
  readonly searchBarSpellcheck: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarSpellcheck;
  readonly searchBarDebounce: XStandardType<number> =
    this.searchBarPropertyProvider.searchBarDebounce;

  //
  readonly searchBarShowCancelButton: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarShowCancelButton;
  readonly searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonIcon;
  readonly searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonTitle;
  readonly searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonIconColor;

  //
  readonly searchBarShowClearButton: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarShowClearButton;
  readonly searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarClearButtonIcon;
  readonly searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarClearButtonIconColor;

  //
  readonly searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchButtonIcon;
  readonly searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchButtonIconColor;

  //
  readonly searchBarQueryChange: EventEmitter<string> =
    this.searchBarPropertyProvider.searchBarQueryChange;
  readonly searchBarOnEvent: EventEmitter<XSearchBarEventIdentifier> =
    this.searchBarPropertyProvider.searchBarOnEvent;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected emptyPropertyProvider: XEmptyContainer,
    protected actionBarPropertyProvider: XComponentActionBarContainer,
    protected searchBarPropertyProvider: XComponentSearchBarContainer
  ) {
    super(cardPropertyProvider, emptyPropertyProvider);
  }
  //#endregion
}

//
@Injectable()
@Component({
  template: ``,
})
export class XCardEmptyActionSearchBarContainerComponent
  extends XCardEmptyContainerComponent
  implements IXCardEmptyActionSearchBarContainer {
  //
  //#region Props ...
  //
  //#region ActionBar ...
  //
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  //
  @Input()
  actionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionBarActionProvider;

  @Input()
  actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  //
  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;

  //
  @Input()
  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actionBarActions;

  //
  @Output()
  actionBarActionFired: EventEmitter<XActionBarActionFiredModel> = new EventEmitter<XActionBarActionFiredModel>();
  //#endregion

  //
  //#region SearchBar ...
  //
  @Input()
  showSearchBar: XStandardType<boolean> = this.propertyProvider.showSearchBar;

  //
  @Input()
  searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.searchBarActionProvider;

  @Input()
  searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.searchBarCssClass;

  //
  @Input()
  searchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarColor;

  @Input()
  searchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundColor;

  //
  @Input()
  searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchInputColor;

  @Input()
  searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundSearchInputColor;

  //
  @Input()
  searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarType;

  @Input()
  searchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarTitle;

  @Input()
  searchBarValue: XStandardType<string> = this.propertyProvider.searchBarValue;

  @Input()
  searchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.searchBarEnterKeyHint;

  @Input()
  searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarKeyboardType;

  @Input()
  searchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.searchBarSpellcheck;

  @Input()
  searchBarDebounce: XStandardType<number> =
    this.propertyProvider.searchBarDebounce;

  //
  @Input()
  searchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.searchBarShowCancelButton;

  @Input()
  searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarCancelButtonIcon;

  @Input()
  searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarCancelButtonTitle;

  @Input()
  searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarCancelButtonIconColor;

  //
  @Input()
  searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarClearButtonIcon;

  @Input()
  searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarClearButtonIconColor;

  //
  @Input()
  searchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.searchBarShowClearButton;

  @Input()
  searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarSearchButtonIcon;

  @Input()
  searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchButtonIconColor;

  //
  @Output()
  searchBarQueryChange: EventEmitter<string> = new EventEmitter();

  @Output()
  searchBarOnEvent: EventEmitter<XSearchBarEventIdentifier> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardEmptyActionSearchBarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCardEmptyActionSearchBarContainer
  ): Observable<IXCardEmptyActionSearchBarContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            //
            //#region ActionBar ...
            //
            showActionBar: this.getValue(
              container ? container.showActionBar : this.showActionBar
            ),
            actionBarCssClass: this.getValue(
              container ? container.actionBarCssClass : this.actionBarCssClass
            ),
            actionBarColor: this.getValue(
              container ? container.actionBarColor : this.actionBarColor
            ),
            actionBarForegroundColor: this.getValue(
              container
                ? container.actionBarForegroundColor
                : this.actionBarForegroundColor
            ),
            actionBarActions: this.getValue(
              container ? container.actionBarActions : this.actionBarActions
            ),
            //#endregion

            //
            //#region SearchBar ...
            //
            showSearchBar: this.getValue(
              container ? container.showSearchBar : this.showSearchBar
            ),
            searchBarCssClass: this.getValue(
              container ? container.searchBarCssClass : this.searchBarCssClass
            ),
            searchBarColor: this.getValue(
              container ? container.searchBarColor : this.searchBarColor
            ),
            searchBarForegroundColor: this.getValue(
              container
                ? container.searchBarForegroundColor
                : this.searchBarForegroundColor
            ),
            searchBarSearchInputColor: this.getValue(
              container
                ? container.searchBarSearchInputColor
                : this.searchBarSearchInputColor
            ),
            searchBarForegroundSearchInputColor: this.getValue(
              container
                ? container.searchBarForegroundSearchInputColor
                : this.searchBarForegroundSearchInputColor
            ),

            //
            searchBarType: this.getValue(
              container ? container.searchBarType : this.searchBarType
            ),
            searchBarTitle: this.getValue(
              container ? container.searchBarTitle : this.searchBarTitle
            ),
            searchBarValue: this.getValue(
              container ? container.searchBarValue : this.searchBarValue
            ),
            searchBarEnterKeyHint: this.getValue(
              container
                ? container.searchBarEnterKeyHint
                : this.searchBarEnterKeyHint
            ),
            searchBarKeyboardType: this.getValue(
              container
                ? container.searchBarKeyboardType
                : this.searchBarKeyboardType
            ),
            searchBarSpellcheck: this.getValue(
              container
                ? container.searchBarSpellcheck
                : this.searchBarSpellcheck
            ),
            searchBarDebounce: this.getValue(
              container ? container.searchBarDebounce : this.searchBarDebounce
            ),

            //
            searchBarShowCancelButton: this.getValue(
              container
                ? container.searchBarShowCancelButton
                : this.searchBarShowCancelButton
            ),
            searchBarCancelButtonIcon: this.getValue(
              container
                ? container.searchBarCancelButtonIcon
                : this.searchBarCancelButtonIcon
            ),
            searchBarCancelButtonTitle: this.getValue(
              container
                ? container.searchBarCancelButtonTitle
                : this.searchBarCancelButtonTitle
            ),
            searchBarCancelButtonIconColor: this.getValue(
              container
                ? container.searchBarCancelButtonIconColor
                : this.searchBarCancelButtonIconColor
            ),

            //
            searchBarShowClearButton: this.getValue(
              container
                ? container.searchBarShowClearButton
                : this.searchBarShowClearButton
            ),
            searchBarClearButtonIcon: this.getValue(
              container
                ? container.searchBarClearButtonIcon
                : this.searchBarClearButtonIcon
            ),
            searchBarClearButtonIconColor: this.getValue(
              container
                ? container.searchBarClearButtonIconColor
                : this.searchBarClearButtonIconColor
            ),

            //
            searchBarSearchButtonIcon: this.getValue(
              container
                ? container.searchBarSearchButtonIcon
                : this.searchBarSearchButtonIcon
            ),
            searchBarSearchButtonIconColor: this.getValue(
              container
                ? container.searchBarSearchButtonIconColor
                : this.searchBarSearchButtonIconColor
            ),
            //#endregion
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionBarActionProvider: container
              ? container.actionBarActionProvider
              : this.actionBarActionProvider,
            actionBarActionFired: this.actionBarActionFired,

            //
            searchBarActionProvider: container
              ? container.searchBarActionProvider
              : this.searchBarActionProvider,

            //
            searchBarQueryChange: this.searchBarQueryChange,
            searchBarOnEvent: this.searchBarOnEvent,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\i-x-file.container.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Component,
  Renderer2,
  Injectable,
  ElementRef,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XExceptionIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XExceptionIdentifier,
} from 'x-framework-core';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
} from '../x-card/x-card.typings';
import { map } from 'rxjs/operators';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import {
  XFileDropAreaActionModel,
  XComponentFileDropAreaContainer,
  IXComponentFileDropAreaContainer,
} from '../x-file-drop-area/x-file-drop-area.typings';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XBaseContainer, XBaseContainerComponent } from './i-x-base.container';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Max Allowed Files ...
export interface IXMaxAllowedFilesContainer {
  maxAllowedFiles: XStandardType<number>;
  maxAllowedFilesException: XStandardType<XExceptionIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XMaxAllowedFilesContainer
  implements Readonly<IXMaxAllowedFilesContainer>
{
  //
  //#region Props ...
  readonly maxAllowedFiles: XStandardType<number> = this.config.maxUploadFiles;

  readonly maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MaxAllowedFilesExceeded;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    private config: XFrameworkComponentsConfig
  ) {}
  //#endregion
}
//#endregion

//
//#region Max Allowed File Size ...
export interface IXMaxAllowedSizeContainer {
  maxAllowedSize: XStandardType<number>;
  maxAllowedSizeException: XStandardType<XExceptionIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XMaxAllowedSizeContainer
  implements Readonly<IXMaxAllowedSizeContainer>
{
  //
  //#region Props ...
  readonly maxAllowedSize: XStandardType<number> = this.config.maxAllowedSize;

  readonly maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MaxFilesSizeExceeded;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    private config: XFrameworkComponentsConfig
  ) {}
  //#endregion
}
//#endregion

//
//#region Min Allowed File Size ...
export interface IXMinAllowedFileSizeContainer {
  minAllowedFileSize: XStandardType<number>;
  minAllowedFileSizeException: XStandardType<XExceptionIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XMinAllowedFileSizeContainer
  implements Readonly<IXMinAllowedFileSizeContainer>
{
  //
  //#region Props ...
  readonly minAllowedFileSize: XStandardType<number> =
    this.config.minAllowedFileSize;

  readonly minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MinFilesSizeExceeded;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    private config: XFrameworkComponentsConfig
  ) {}
  //#endregion
}
//#endregion

//
//#region Max Allowed Size ...
export interface IXMaxAllowedFileSizeContainer {
  maxAllowedFileSize: XStandardType<number>;
  maxAllowedFileSizeException: XStandardType<XExceptionIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XMaxAllowedFileSizeContainer
  implements Readonly<IXMaxAllowedFileSizeContainer>
{
  //
  //#region Props ...
  readonly maxAllowedFileSize: XStandardType<number> =
    this.config.maxAllowedFileSize;

  readonly maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MaxFilesSizeExceeded;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    private config: XFrameworkComponentsConfig
  ) {}
  //#endregion
}
//#endregion

//
//#region Regular File ...
export interface IXFileRegularContainer {
  emptyFileException: XStandardType<XExceptionIdentifier>;
  notAllowedFileException: XStandardType<XExceptionIdentifier>;
  allowedExtensions: XStandardType<XOneOrManyType<string>>;
}

@Injectable({
  providedIn: 'root',
})
export class XFileRegularContainer implements Readonly<IXFileRegularContainer> {
  //
  //#region Props ...
  readonly emptyFileException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.EmptyFile;

  readonly notAllowedFileException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.InavlidFile;

  readonly allowedExtensions: XStandardType<XOneOrManyType<string>> = [];
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    private config: XFrameworkComponentsConfig
  ) {}
  //#endregion
}
//#endregion

//
//#region Combine ...
type XCombinedType = IXFileRegularContainer &
  IXMaxAllowedFilesContainer &
  IXMaxAllowedSizeContainer &
  IXMinAllowedFileSizeContainer &
  IXMaxAllowedFileSizeContainer;

// tslint:disable-next-line:no-empty-interface
export interface IXFileContainer extends XCombinedType {}

@Injectable({
  providedIn: 'root',
})
export class XFileContainer implements Readonly<IXFileContainer> {
  //
  //#region Props ...
  //
  readonly emptyFileException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.EmptyFile;

  readonly notAllowedFileException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.InavlidFile;

  readonly allowedExtensions: XStandardType<XOneOrManyType<string>> = [];

  //
  readonly maxAllowedFiles: XStandardType<number> = this.config.maxUploadFiles;

  readonly maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MaxAllowedFilesExceeded;

  //
  readonly maxAllowedSize: XStandardType<number> = this.config.maxAllowedSize;

  readonly maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MaxFilesSizeExceeded;

  //
  readonly minAllowedFileSize: XStandardType<number> =
    this.config.minAllowedFileSize;

  readonly minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MinFilesSizeExceeded;

  //
  readonly maxAllowedFileSize: XStandardType<number> =
    this.config.maxAllowedFileSize;

  readonly maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    XExceptionIDs.MaxFilesSizeExceeded;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    private config: XFrameworkComponentsConfig
  ) {}
  //#endregion
}
//#endregion

//
//#region XFileContainerComponent ...
@Injectable({
  providedIn: 'root',
})
@Component({
  template: `
    <!-- 
        <ng-container *ngIf="extractProps() | async as props">
        </ng-container> 
    -->
  `,
})
export abstract class XFileContainerComponent
  extends XBaseContainerComponent
  implements IXFileContainer
{
  //
  //#region Props ...
  //
  @Input()
  emptyFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.emptyFileException;

  @Input()
  notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.notAllowedFileException;

  @Input()
  allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.allowedExtensions;

  //
  @Input()
  maxAllowedFiles: XStandardType<number> =
    this.propertyProvider.maxAllowedFiles;

  @Input()
  maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFilesException;

  //
  @Input()
  maxAllowedSize: XStandardType<number> = this.propertyProvider.maxAllowedSize;

  @Input()
  maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedSizeException;

  //
  @Input()
  minAllowedFileSize: XStandardType<number> =
    this.propertyProvider.minAllowedFileSize;

  @Input()
  minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.minAllowedFileSizeException;

  //
  @Input()
  maxAllowedFileSize: XStandardType<number> =
    this.propertyProvider.maxAllowedFileSize;

  @Input()
  maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFileSizeException;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFileContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXFileContainer
  ): Observable<IXFileContainer> {
    return forkJoin({
      //
      emptyFileException: this.getValue(
        container ? container.emptyFileException : this.emptyFileException
      ),
      notAllowedFileException: this.getValue(
        container
          ? container.notAllowedFileException
          : this.notAllowedFileException
      ),
      allowedExtensions: this.getValue(
        container ? container.allowedExtensions : this.allowedExtensions
      ),
      maxAllowedFiles: this.getValue(
        container ? container.maxAllowedFiles : this.maxAllowedFiles
      ),
      maxAllowedFilesException: this.getValue(
        container
          ? container.maxAllowedFilesException
          : this.maxAllowedFilesException
      ),
      maxAllowedSize: this.getValue(
        container ? container.maxAllowedSize : this.maxAllowedSize
      ),
      maxAllowedSizeException: this.getValue(
        container
          ? container.maxAllowedSizeException
          : this.maxAllowedSizeException
      ),
      minAllowedFileSize: this.getValue(
        container ? container.minAllowedFileSize : this.minAllowedFileSize
      ),
      minAllowedFileSizeException: this.getValue(
        container
          ? container.minAllowedFileSizeException
          : this.minAllowedFileSizeException
      ),
      maxAllowedFileSize: this.getValue(
        container ? container.maxAllowedFileSize : this.maxAllowedFileSize
      ),
      maxAllowedFileSizeException: this.getValue(
        container
          ? container.maxAllowedFileSizeException
          : this.maxAllowedFileSizeException
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region XCardFileContainer ...
//
type TXCardFileContainer = IXComponentCardContainer & IXFileContainer;
export interface IXCardFileContainer extends TXCardFileContainer {}

//
@Injectable({
  providedIn: 'root',
})
export class XCardFileContainer
  extends XBaseContainer
  implements Readonly<IXCardFileContainer>
{
  //
  //#region XComponentCard ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  //#endregion

  //
  //#region XFile ...
  //
  readonly emptyFileException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.emptyFileException;

  readonly notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.notAllowedFileException;

  readonly allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.filePropertyProvider.allowedExtensions;

  //
  readonly maxAllowedFiles: XStandardType<number> =
    this.filePropertyProvider.maxAllowedFiles;

  readonly maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedFilesException;

  //
  readonly maxAllowedSize: XStandardType<number> =
    this.filePropertyProvider.maxAllowedSize;

  readonly maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedSizeException;

  //
  readonly minAllowedFileSize: XStandardType<number> =
    this.filePropertyProvider.minAllowedFileSize;

  readonly minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.minAllowedFileSizeException;

  //
  readonly maxAllowedFileSize: XStandardType<number> =
    this.filePropertyProvider.maxAllowedFileSize;

  readonly maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedFileSizeException;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected filePropertyProvider: XFileContainer
  ) {
    super();
  }
  //#endregion
}

//
@Injectable()
@Component({
  template: ``,
})
export class XCardFileContainerComponent
  extends XBaseContainerComponent
  implements IXCardFileContainer
{
  //
  //#region Props ...
  //
  //#region XComponentCard ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;
  //#endregion

  //
  //#region File ...
  //
  @Input()
  emptyFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.emptyFileException;

  @Input()
  notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.notAllowedFileException;

  @Input()
  allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.allowedExtensions;

  //
  @Input()
  maxAllowedFiles: XStandardType<number> =
    this.propertyProvider.maxAllowedFiles;

  @Input()
  maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFilesException;

  //
  @Input()
  maxAllowedSize: XStandardType<number> = this.propertyProvider.maxAllowedSize;

  @Input()
  maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedSizeException;

  //
  @Input()
  minAllowedFileSize: XStandardType<number> =
    this.propertyProvider.minAllowedFileSize;

  @Input()
  minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.minAllowedFileSizeException;

  //
  @Input()
  maxAllowedFileSize: XStandardType<number> =
    this.propertyProvider.maxAllowedFileSize;

  @Input()
  maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFileSizeException;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardFileContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCardFileContainer
  ): Observable<IXCardFileContainer> {
    return forkJoin({
      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region File ...
      emptyFileException: this.getValue(
        container ? container.emptyFileException : this.emptyFileException
      ),
      notAllowedFileException: this.getValue(
        container
          ? container.notAllowedFileException
          : this.notAllowedFileException
      ),
      allowedExtensions: this.getValue(
        container ? container.allowedExtensions : this.allowedExtensions
      ),
      maxAllowedFiles: this.getValue(
        container ? container.maxAllowedFiles : this.maxAllowedFiles
      ),
      maxAllowedFilesException: this.getValue(
        container
          ? container.maxAllowedFilesException
          : this.maxAllowedFilesException
      ),
      maxAllowedSize: this.getValue(
        container ? container.maxAllowedSize : this.maxAllowedSize
      ),
      maxAllowedSizeException: this.getValue(
        container
          ? container.maxAllowedSizeException
          : this.maxAllowedSizeException
      ),
      minAllowedFileSize: this.getValue(
        container ? container.minAllowedFileSize : this.minAllowedFileSize
      ),
      minAllowedFileSizeException: this.getValue(
        container
          ? container.minAllowedFileSizeException
          : this.minAllowedFileSizeException
      ),
      maxAllowedFileSize: this.getValue(
        container ? container.maxAllowedFileSize : this.maxAllowedFileSize
      ),
      maxAllowedFileSizeException: this.getValue(
        container
          ? container.maxAllowedFileSizeException
          : this.maxAllowedFileSizeException
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region XCardFileFileDropAreaContainer ...
//
type TXCardFileFileDropContainer = IXComponentCardContainer &
  IXFileContainer &
  IXComponentFileDropAreaContainer;
export interface IXCardFileFileDropContainer
  extends TXCardFileFileDropContainer {}

//
@Injectable({
  providedIn: 'root',
})
export class XCardFileFileDropContainer
  extends XBaseContainer
  implements Readonly<IXCardFileFileDropContainer>
{
  //
  //#region Props ...
  //
  //#region ComponentCard ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  //#endregion

  //
  //#region File ...
  //
  readonly emptyFileException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.emptyFileException;

  readonly notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.notAllowedFileException;

  readonly allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.filePropertyProvider.allowedExtensions;

  //
  readonly maxAllowedFiles: XStandardType<number> =
    this.filePropertyProvider.maxAllowedFiles;

  readonly maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedFilesException;

  //
  readonly maxAllowedSize: XStandardType<number> =
    this.filePropertyProvider.maxAllowedSize;

  readonly maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedSizeException;

  //
  readonly minAllowedFileSize: XStandardType<number> =
    this.filePropertyProvider.minAllowedFileSize;

  readonly minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.minAllowedFileSizeException;

  //
  readonly maxAllowedFileSize: XStandardType<number> =
    this.filePropertyProvider.maxAllowedFileSize;

  readonly maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedFileSizeException;
  //#endregion

  //
  //#region File Drop ...
  //
  readonly showFileDropArea: XStandardType<boolean> =
    this.fileDropPropertyProvider.showFileDropArea;

  //
  readonly fileDropAreaActionProvider: Subject<XFileDropAreaActionModel> =
    this.fileDropPropertyProvider.fileDropAreaActionProvider;

  readonly fileDropAreaCssClass: XStandardType<XOneOrManyType<string>> =
    this.fileDropPropertyProvider.fileDropAreaCssClass;

  //
  readonly fileDropAreaColor: XStandardType<XColorIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaColor;

  readonly fileDropAreaForegroundColor: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaForegroundColor;

  //
  readonly fileDropAreaIcon: XStandardType<XIconIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaIcon;

  readonly fileDropAreaMessage: XStandardType<XResourceIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaMessage;

  //
  readonly fileDropAreaDisableFileCheck: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaDisableFileCheck;

  //
  readonly fileDropAreaSupportsMultipleFiles: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaSupportsMultipleFiles;

  readonly fileDropAreaAllowedExtensions: XStandardType<
    XOneOrManyType<string>
  > = this.fileDropPropertyProvider.fileDropAreaAllowedExtensions;

  //
  readonly fileDropAreaDragDisabled: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaDragDisabled;

  readonly fileDropAreaContentTemplate: TemplateRef<any> =
    this.fileDropPropertyProvider.fileDropAreaContentTemplate;

  readonly fileDropAreaFilesChangeEmiter: EventEmitter<XOneOrManyType<File>> =
    this.fileDropPropertyProvider.fileDropAreaFilesChangeEmiter;

  //
  readonly fileDropAreaSuccessColor: XStandardType<XColorIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaSuccessColor;

  readonly fileDropAreaErrorColor: XStandardType<XColorIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaErrorColor;

  readonly fileDropAreaAccentColor: XStandardType<XColorIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaAccentColor;

  //
  //#region ComponentsCard ...
  readonly fileDropAreaIsInPage: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaIsInPage;
  readonly fileDropAreaWrapWithCard: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaWrapWithCard;
  readonly fileDropAreaCardShowFooter: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaCardShowFooter;
  readonly fileDropAreaCardShowActions: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaCardShowActions;
  readonly fileDropAreaCardColor: XStandardType<XColorIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaCardColor;
  readonly fileDropAreaCardForegroundColor: XStandardType<boolean> =
    this.fileDropPropertyProvider.fileDropAreaCardForegroundColor;
  readonly fileDropAreaCardTitle: XStandardType<XResourceIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaCardTitle;
  readonly fileDropAreaCardActionProvider: Subject<XCardActionModel> =
    this.fileDropPropertyProvider.fileDropAreaCardActionProvider;
  readonly fileDropAreaCardSubTitle: XStandardType<XResourceIdentifier> =
    this.fileDropPropertyProvider.fileDropAreaCardSubTitle;
  readonly fileDropAreaCardCssClass: XStandardType<XOneOrManyType<string>> =
    this.fileDropPropertyProvider.fileDropAreaCardCssClass;
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected fileDropPropertyProvider: XComponentFileDropAreaContainer,
    protected filePropertyProvider: XFileContainer
  ) {
    super();
  }
  //#endregion
}

//
@Injectable()
@Component({
  template: ``,
})
export class XCardFileFileDropContainerComponent
  extends XBaseContainerComponent
  implements IXCardFileContainer
{
  //
  //#region Props ...
  //
  //#region XComponentCard ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;
  //#endregion

  //
  //#region File ...
  //
  @Input()
  emptyFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.emptyFileException;

  @Input()
  notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.notAllowedFileException;

  @Input()
  allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.allowedExtensions;

  //
  @Input()
  maxAllowedFiles: XStandardType<number> =
    this.propertyProvider.maxAllowedFiles;

  @Input()
  maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFilesException;

  //
  @Input()
  maxAllowedSize: XStandardType<number> = this.propertyProvider.maxAllowedSize;

  @Input()
  maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedSizeException;

  //
  @Input()
  minAllowedFileSize: XStandardType<number> =
    this.propertyProvider.minAllowedFileSize;

  @Input()
  minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.minAllowedFileSizeException;

  //
  @Input()
  maxAllowedFileSize: XStandardType<number> =
    this.propertyProvider.maxAllowedFileSize;

  @Input()
  maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFileSizeException;
  //#endregion

  //
  //#region FileDrop ...
  //
  @Input()
  fileDropAreaActionProvider: Subject<XFileDropAreaActionModel> =
    this.propertyProvider.fileDropAreaActionProvider;

  @Input()
  fileDropAreaCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaCssClass;

  //
  @Input()
  fileDropAreaColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaColor;

  @Input()
  fileDropAreaForegroundColor: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaForegroundColor;

  //
  @Input()
  fileDropAreaIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.fileDropAreaIcon;

  @Input()
  fileDropAreaMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaMessage;

  //
  @Input()
  fileDropAreaDisableFileCheck: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaDisableFileCheck;

  //
  @Input()
  fileDropAreaSupportsMultipleFiles: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaSupportsMultipleFiles;

  @Input()
  fileDropAreaAllowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaAllowedExtensions;

  //
  @Input()
  fileDropAreaDragDisabled: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaDragDisabled;

  @Input()
  fileDropAreaContentTemplate: TemplateRef<any> =
    this.propertyProvider.fileDropAreaContentTemplate;

  @Output()
  fileDropAreaFilesChangeEmiter: EventEmitter<XOneOrManyType<File>> =
    new EventEmitter();

  //
  @Input()
  fileDropAreaSuccessColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaSuccessColor;

  @Input()
  fileDropAreaErrorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaErrorColor;

  @Input()
  fileDropAreaAccentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaAccentColor;

  //
  @Input()
  showFileDropArea: XStandardType<boolean> =
    this.propertyProvider.showFileDropArea;

  //
  //#region ComponentsCard ...
  @Input()
  fileDropAreaIsInPage: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaIsInPage;

  @Input()
  fileDropAreaWrapWithCard: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaWrapWithCard;

  @Input()
  fileDropAreaCardShowFooter: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaCardShowFooter;

  @Input()
  fileDropAreaCardShowActions: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaCardShowActions;

  @Input()
  fileDropAreaCardColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaCardColor;

  @Input()
  fileDropAreaCardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaCardForegroundColor;

  @Input()
  fileDropAreaCardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaCardTitle;

  @Input()
  fileDropAreaCardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.fileDropAreaCardActionProvider;

  @Input()
  fileDropAreaCardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaCardSubTitle;

  @Input()
  fileDropAreaCardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaCardCssClass;
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardFileFileDropContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCardFileFileDropContainer
  ): Observable<IXCardFileFileDropContainer> {
    return forkJoin({
      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region File ...
      emptyFileException: this.getValue(
        container ? container.emptyFileException : this.emptyFileException
      ),
      notAllowedFileException: this.getValue(
        container
          ? container.notAllowedFileException
          : this.notAllowedFileException
      ),
      allowedExtensions: this.getValue(
        container ? container.allowedExtensions : this.allowedExtensions
      ),
      maxAllowedFiles: this.getValue(
        container ? container.maxAllowedFiles : this.maxAllowedFiles
      ),
      maxAllowedFilesException: this.getValue(
        container
          ? container.maxAllowedFilesException
          : this.maxAllowedFilesException
      ),
      maxAllowedSize: this.getValue(
        container ? container.maxAllowedSize : this.maxAllowedSize
      ),
      maxAllowedSizeException: this.getValue(
        container
          ? container.maxAllowedSizeException
          : this.maxAllowedSizeException
      ),
      minAllowedFileSize: this.getValue(
        container ? container.minAllowedFileSize : this.minAllowedFileSize
      ),
      minAllowedFileSizeException: this.getValue(
        container
          ? container.minAllowedFileSizeException
          : this.minAllowedFileSizeException
      ),
      maxAllowedFileSize: this.getValue(
        container ? container.maxAllowedFileSize : this.maxAllowedFileSize
      ),
      maxAllowedFileSizeException: this.getValue(
        container
          ? container.maxAllowedFileSizeException
          : this.maxAllowedFileSizeException
      ),
      //#endregion

      //
      //#region FileDrop ...
      //
      fileDropAreaCssClass: this.getValue(
        container ? container.fileDropAreaCssClass : this.fileDropAreaCssClass
      ),
      fileDropAreaColor: this.getValue(
        container ? container.fileDropAreaColor : this.fileDropAreaColor
      ),
      fileDropAreaForegroundColor: this.getValue(
        container
          ? container.fileDropAreaForegroundColor
          : this.fileDropAreaForegroundColor
      ),
      fileDropAreaIcon: this.getValue(
        container ? container.fileDropAreaIcon : this.fileDropAreaIcon
      ),
      fileDropAreaMessage: this.getValue(
        container ? container.fileDropAreaMessage : this.fileDropAreaMessage
      ),
      fileDropAreaDisableFileCheck: this.getValue(
        container
          ? container.fileDropAreaDisableFileCheck
          : this.fileDropAreaDisableFileCheck
      ),
      fileDropAreaSupportsMultipleFiles: this.getValue(
        container
          ? container.fileDropAreaSupportsMultipleFiles
          : this.fileDropAreaSupportsMultipleFiles
      ),
      fileDropAreaAllowedExtensions: this.getValue(
        container
          ? container.fileDropAreaAllowedExtensions
          : this.fileDropAreaAllowedExtensions
      ),
      fileDropAreaDragDisabled: this.getValue(
        container
          ? container.fileDropAreaDragDisabled
          : this.fileDropAreaDragDisabled
      ),
      fileDropAreaContentTemplate: this.getValue(
        container
          ? container.fileDropAreaContentTemplate
          : this.fileDropAreaContentTemplate
      ),
      fileDropAreaSuccessColor: this.getValue(
        container
          ? container.fileDropAreaSuccessColor
          : this.fileDropAreaSuccessColor
      ),
      fileDropAreaErrorColor: this.getValue(
        container
          ? container.fileDropAreaErrorColor
          : this.fileDropAreaErrorColor
      ),
      fileDropAreaAccentColor: this.getValue(
        container
          ? container.fileDropAreaAccentColor
          : this.fileDropAreaAccentColor
      ),
      showFileDropArea: this.getValue(
        container ? container.showFileDropArea : this.showFileDropArea
      ),
      fileDropAreaIsInPage: this.getValue(
        container ? container.fileDropAreaIsInPage : this.fileDropAreaIsInPage
      ),
      fileDropAreaWrapWithCard: this.getValue(
        container
          ? container.fileDropAreaWrapWithCard
          : this.fileDropAreaWrapWithCard
      ),
      fileDropAreaCardShowFooter: this.getValue(
        container
          ? container.fileDropAreaCardShowFooter
          : this.fileDropAreaCardShowFooter
      ),
      fileDropAreaCardShowActions: this.getValue(
        container
          ? container.fileDropAreaCardShowActions
          : this.fileDropAreaCardShowActions
      ),
      fileDropAreaCardColor: this.getValue(
        container ? container.fileDropAreaCardColor : this.fileDropAreaCardColor
      ),
      fileDropAreaCardForegroundColor: this.getValue(
        container
          ? container.fileDropAreaCardForegroundColor
          : this.fileDropAreaCardForegroundColor
      ),
      fileDropAreaCardTitle: this.getValue(
        container ? container.fileDropAreaCardTitle : this.fileDropAreaCardTitle
      ),
      fileDropAreaCardSubTitle: this.getValue(
        container
          ? container.fileDropAreaCardSubTitle
          : this.fileDropAreaCardSubTitle
      ),
      fileDropAreaCardCssClass: this.getValue(
        container
          ? container.fileDropAreaCardCssClass
          : this.fileDropAreaCardCssClass
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          fileDropAreaActionProvider: container
            ? container.fileDropAreaActionProvider
            : this.fileDropAreaActionProvider,

          //
          fileDropAreaCardActionProvider: container
            ? container.fileDropAreaCardActionProvider
            : this.fileDropAreaCardActionProvider,

          //
          fileDropAreaFilesChangeEmiter: this.fileDropAreaFilesChangeEmiter,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\i-x-next-prev-reset.container.ts

import {
  Input,
  Inject,
  Output,
  NgZone,
  Renderer2,
  Component,
  Injectable,
  ElementRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from './i-x-base.container';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
} from '../x-card/x-card.typings';
import { map } from 'rxjs/operators';
import { Observable, forkJoin, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { IXButtonContainer, XButtonType } from '../x-button/x-button.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XSlotNameIdentifier, XSlotName } from '../x-slotter/x-slotter.typings';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
export enum XNPRButtonType {
  Next = 'next',
  Prev = 'prev',
  Reset = 'reset',
}

//
export type XNPRButtonTypeIdentifier = XNPRButtonType | string;

//
//#region Next ...
export interface IXNextHost {
  canNext: XStandardType<boolean>;
  onNext: EventEmitter<string>;
}

export interface IXNextContainer extends IXBaseContainer {
  //
  //#region Next ...
  hasNext: XStandardType<boolean>;
  disableNext: XStandardType<boolean>;
  nextConfirmer: () => XStandardType<boolean>;
  nextPosition: XStandardType<XSlotNameIdentifier>;
  nextCssClass: XStandardType<XOneOrManyType<string>>;
  nextButton: XStandardType<Partial<IXButtonContainer>>;
  //#endregion
}

export const defaultNextButton: Partial<IXButtonContainer> = {
  title: XResourceIDs.next,
  type: XButtonType.Raised,
  color: XColorWithBrightness.Primary,
};

@Injectable({
  providedIn: 'root',
})
export abstract class XNextContainer
  extends XBaseContainer
  implements Readonly<IXNextContainer>
{
  //
  //#region Next ...
  readonly hasNext: XStandardType<boolean>;
  readonly disableNext: XStandardType<boolean>;
  readonly nextConfirmer: () => XStandardType<boolean>;
  readonly nextPosition: XStandardType<XSlotNameIdentifier> = XSlotName.START;
  readonly nextCssClass: XStandardType<XOneOrManyType<string>> =
    'ion-text-start';
  readonly nextButton: XStandardType<Partial<IXButtonContainer>> = {
    ...defaultNextButton,
  };
  //#endregion
}
//#endregion

//
//#region Prev ...
export interface IXPrevHost {
  canPrev: XStandardType<boolean>;
  onPrev: EventEmitter<string>;
}

export interface IXPrevContainer extends IXBaseContainer {
  //
  //#region Prev ...
  hasPrev: XStandardType<boolean>;
  disablePrev: XStandardType<boolean>;
  prevConfirmer: () => XStandardType<boolean>;
  prevPosition: XStandardType<XSlotNameIdentifier>;
  prevCssClass: XStandardType<XOneOrManyType<string>>;
  prevButton: XStandardType<Partial<IXButtonContainer>>;
  //#endregion
}

export const defaultPrevButton: Partial<IXButtonContainer> = {
  type: XButtonType.Raised,
  title: XResourceIDs.previous,
  color: XColorWithBrightness.Primary,
};

@Injectable({
  providedIn: 'root',
})
export abstract class XPrevContainer
  extends XBaseContainer
  implements Readonly<IXPrevContainer>
{
  //
  //#region Prev ...
  readonly hasPrev: XStandardType<boolean>;
  readonly disablePrev: XStandardType<boolean>;
  readonly prevConfirmer: () => XStandardType<boolean>;
  readonly prevPosition: XStandardType<XSlotNameIdentifier> = XSlotName.END;
  readonly prevCssClass: XStandardType<XOneOrManyType<string>> = 'ion-text-end';
  readonly prevButton: XStandardType<Partial<IXButtonContainer>> = {
    ...defaultPrevButton,
  };
  //#endregion
}
//#endregion

//
//#region Reset ...
export interface IXResetHost {
  canReset: XStandardType<boolean>;
  onReset: EventEmitter<string>;
}

export interface IXResetContainer extends IXBaseContainer {
  //
  //#region Reset ...
  hasReset: XStandardType<boolean>;
  disableReset: XStandardType<boolean>;
  resetConfirmer: () => XStandardType<boolean>;
  resetPosition: XStandardType<XSlotNameIdentifier>;
  resetCssClass: XStandardType<XOneOrManyType<string>>;
  resetButton: XStandardType<Partial<IXButtonContainer>>;
  //#endregion
}

export const defaultResetButton: Partial<IXButtonContainer> = {
  type: XButtonType.Raised,
  title: XResourceIDs.reset,
  color: XColorWithBrightness.Warning,
};

@Injectable({
  providedIn: 'root',
})
export abstract class XResetContainer
  extends XBaseContainer
  implements Readonly<IXResetContainer>
{
  //
  //#region Reset ...
  readonly hasReset: XStandardType<boolean>;
  readonly disableReset: XStandardType<boolean>;
  readonly resetConfirmer: () => XStandardType<boolean>;
  readonly resetPosition: XStandardType<XSlotNameIdentifier> = XSlotName.CENTER;
  readonly resetCssClass: XStandardType<XOneOrManyType<string>> =
    'ion-text-center';
  readonly resetButton: XStandardType<Partial<IXButtonContainer>> = {
    ...defaultResetButton,
  };
  //#endregion
}
//#endregion

//
//#region Combined ...
//
//#region Host ...
//
type XCompleteHostInterface = IXNextHost & IXPrevHost & IXResetHost;
// tslint:disable-next-line:no-empty-interface
export interface IXNextPrevResetHostContainer extends XCompleteHostInterface {}

@Injectable({
  providedIn: 'root',
})
export class XNextPrevResetHostContainer
  extends XBaseContainer
  implements Readonly<IXNextPrevResetHostContainer>
{
  //
  //#region Props ...
  //
  //#region Next ...
  readonly canNext: XStandardType<boolean> = true;
  readonly onNext: EventEmitter<string> = new EventEmitter<string>();
  //#endregion

  //
  //#region Prev ...
  readonly canPrev: XStandardType<boolean> = true;
  readonly onPrev: EventEmitter<string> = new EventEmitter<string>();
  //#endregion

  //
  //#region Reset ...
  readonly canReset: XStandardType<boolean> = true;
  readonly onReset: EventEmitter<string> = new EventEmitter<string>();
  //#endregion
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XNextPrevResetHostContainerComponent
  extends XBaseContainerComponent
  implements IXNextPrevResetHostContainer
{
  //
  //#region Props ...
  //
  //#region Next ...
  @Input()
  canNext: XStandardType<boolean> = this.propertyProvider.canNext;

  @Output()
  onNext: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Prev ...
  @Input()
  canPrev: XStandardType<boolean> = this.propertyProvider.canPrev;

  @Output()
  onPrev: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Reset ...
  @Input()
  canReset: XStandardType<boolean> = this.propertyProvider.canReset;

  @Output()
  onReset: EventEmitter<string> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XNextPrevResetHostContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXNextPrevResetHostContainer
  ): Observable<IXNextPrevResetHostContainer> {
    return forkJoin({
      //
      canNext: this.getValue(container ? container.canNext : this.canNext),
      canPrev: this.getValue(container ? container.canPrev : this.canPrev),
      canReset: this.getValue(container ? container.canReset : this.canReset),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          onNext: this.onNext,
          onPrev: this.onPrev,
          onReset: this.onReset,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region XCardHost
//
type TXCardEmptyContainer = IXComponentCardContainer &
  IXNextPrevResetHostContainer;
export interface IXCardNextPrevResetHostContainer
  extends TXCardEmptyContainer {}

//
@Injectable({
  providedIn: 'root',
})
export class XCardNextPrevResetHostContainer
  extends XBaseContainer
  implements IXCardNextPrevResetHostContainer
{
  //
  //#region Props ...
  //
  //#region XComponentCard ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  //#endregion

  //
  //#region XNextPrevResetHost ...
  //
  //#region Next ...
  readonly canNext: XStandardType<boolean> =
    this.nextPrevResetHostPropertyProvider.canNext;
  readonly onNext: EventEmitter<string> =
    this.nextPrevResetHostPropertyProvider.onNext;
  //#endregion

  //
  //#region Prev ...
  readonly canPrev: XStandardType<boolean> =
    this.nextPrevResetHostPropertyProvider.canPrev;
  readonly onPrev: EventEmitter<string> =
    this.nextPrevResetHostPropertyProvider.onPrev;
  //#endregion

  //
  //#region Reset ...
  readonly canReset: XStandardType<boolean> =
    this.nextPrevResetHostPropertyProvider.canReset;
  readonly onReset: EventEmitter<string> =
    this.nextPrevResetHostPropertyProvider.onReset;
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected nextPrevResetHostPropertyProvider: XNextPrevResetHostContainer
  ) {
    super();
  }
  //#endregion
}

//
@Injectable()
@Component({
  template: ``,
})
export class XCardNextPrevResetHostContainerComponent
  extends XBaseContainerComponent
  implements IXCardNextPrevResetHostContainer
{
  //
  //#region Props ...
  //
  //#region XComponentCard ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;
  //#endregion

  //
  //#region XNextPrevResetHost ...
  //
  //#region Next ...
  @Input()
  canNext: XStandardType<boolean> = this.propertyProvider.canNext;

  @Output()
  onNext: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Prev ...
  @Input()
  canPrev: XStandardType<boolean> = this.propertyProvider.canPrev;

  @Output()
  onPrev: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Reset ...
  @Input()
  canReset: XStandardType<boolean> = this.propertyProvider.canReset;

  @Output()
  onReset: EventEmitter<string> = new EventEmitter();
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardNextPrevResetHostContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCardNextPrevResetHostContainer
  ): Observable<IXCardNextPrevResetHostContainer> {
    return forkJoin({
      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region XNextPrevResetHost ...
      //
      canNext: this.getValue(container ? container.canNext : this.canNext),
      canPrev: this.getValue(container ? container.canPrev : this.canPrev),
      canReset: this.getValue(container ? container.canReset : this.canReset),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          onNext: this.onNext,
          onPrev: this.onPrev,
          onReset: this.onReset,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region Interface ...
//
type TXNextPrevResetContainer = IXNextContainer &
  IXPrevContainer &
  IXResetContainer;
// tslint:disable-next-line:no-empty-interface
export interface IXNextPrevResetContainer
  extends IXBaseContainer,
    TXNextPrevResetContainer {}

@Injectable({
  providedIn: 'root',
})
export class XNextPrevResetContainer
  extends XBaseContainer
  implements Readonly<IXNextPrevResetContainer>
{
  //
  //#region Props ...
  //
  //#region Next ...
  hasNext: XStandardType<boolean> = this.nextPropertyProvider.hasNext;
  disableNext: XStandardType<boolean> = this.nextPropertyProvider.disableNext;
  nextConfirmer: () => XStandardType<boolean> =
    this.nextPropertyProvider.nextConfirmer;
  nextPosition: XStandardType<XSlotNameIdentifier> =
    this.nextPropertyProvider.nextPosition;
  nextCssClass: XStandardType<XOneOrManyType<string>> =
    this.nextPropertyProvider.nextCssClass;
  nextButton: XStandardType<Partial<IXButtonContainer>> =
    this.nextPropertyProvider.nextButton;
  //#endregion

  //
  //#region Prev ...
  hasPrev: XStandardType<boolean> = this.prevPropertyProvider.hasPrev;
  disablePrev: XStandardType<boolean> = this.prevPropertyProvider.disablePrev;
  prevConfirmer: () => XStandardType<boolean> =
    this.prevPropertyProvider.prevConfirmer;
  prevPosition: XStandardType<XSlotNameIdentifier> =
    this.prevPropertyProvider.prevPosition;
  prevCssClass: XStandardType<XOneOrManyType<string>> =
    this.prevPropertyProvider.prevCssClass;
  prevButton: XStandardType<Partial<IXButtonContainer>> =
    this.prevPropertyProvider.prevButton;
  //#endregion

  //
  //#region Reset ...
  hasReset: XStandardType<boolean> = this.resetpropertyProvider.hasReset;
  disableReset: XStandardType<boolean> =
    this.resetpropertyProvider.disableReset;
  resetConfirmer: () => XStandardType<boolean> =
    this.resetpropertyProvider.resetConfirmer;
  resetPosition: XStandardType<XSlotNameIdentifier> =
    this.resetpropertyProvider.resetPosition;
  resetCssClass: XStandardType<XOneOrManyType<string>> =
    this.resetpropertyProvider.resetCssClass;
  resetButton: XStandardType<Partial<IXButtonContainer>> =
    this.resetpropertyProvider.resetButton;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected prevPropertyProvider: XPrevContainer,
    protected nextPropertyProvider: XNextContainer,
    protected resetpropertyProvider: XResetContainer
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XNextPrevResetContainerComponent
  extends XBaseContainerComponent
  implements IXNextPrevResetContainer
{
  //
  //#region Props ...
  //
  //#region Next ...
  @Input()
  hasNext: XStandardType<boolean> = this.propertyProvider.hasNext;

  @Input()
  disableNext: XStandardType<boolean> = this.propertyProvider.disableNext;

  @Input()
  nextConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.nextConfirmer;

  @Input()
  nextPosition: XStandardType<XSlotNameIdentifier> =
    this.propertyProvider.nextPosition;

  @Input()
  nextCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.nextCssClass;

  @Input()
  nextButton: XStandardType<Partial<IXButtonContainer>> =
    this.propertyProvider.nextButton;
  //#endregion

  //
  //#region Prev ...
  @Input()
  hasPrev: XStandardType<boolean> = this.propertyProvider.hasPrev;

  @Input()
  disablePrev: XStandardType<boolean> = this.propertyProvider.disablePrev;

  @Input()
  prevConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.prevConfirmer;

  @Input()
  prevPosition: XStandardType<XSlotNameIdentifier> =
    this.propertyProvider.prevPosition;

  @Input()
  prevCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.prevCssClass;

  @Input()
  prevButton: XStandardType<Partial<IXButtonContainer>> =
    this.propertyProvider.prevButton;
  //#endregion

  //
  //#region Reset ...
  @Input()
  hasReset: XStandardType<boolean> = this.propertyProvider.hasReset;

  @Input()
  disableReset: XStandardType<boolean> = this.propertyProvider.disableReset;

  @Input()
  resetConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.resetConfirmer;

  @Input()
  resetPosition: XStandardType<XSlotNameIdentifier> =
    this.propertyProvider.resetPosition;

  @Input()
  resetCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.resetCssClass;

  @Input()
  resetButton: XStandardType<Partial<IXButtonContainer>> =
    this.propertyProvider.resetButton;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XNextPrevResetContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXNextPrevResetContainer
  ): Observable<IXNextPrevResetContainer> {
    return forkJoin({
      //
      hasNext: this.getValue(container ? container.hasNext : this.hasNext),
      disableNext: this.getValue(
        container ? container.disableNext : this.disableNext
      ),
      nextConfirmer: this.getValue(
        container ? container.nextConfirmer : this.nextConfirmer
      ),
      nextPosition: this.getValue(
        container ? container.nextPosition : this.nextPosition
      ),
      nextCssClass: this.getValue(
        container ? container.nextCssClass : this.nextCssClass
      ),
      nextButton: this.getValue(
        container ? container.nextButton : this.nextButton
      ),
      hasPrev: this.getValue(container ? container.hasPrev : this.hasPrev),
      disablePrev: this.getValue(
        container ? container.disablePrev : this.disablePrev
      ),
      prevConfirmer: this.getValue(
        container ? container.prevConfirmer : this.prevConfirmer
      ),
      prevPosition: this.getValue(
        container ? container.prevPosition : this.prevPosition
      ),
      prevCssClass: this.getValue(
        container ? container.prevCssClass : this.prevCssClass
      ),
      prevButton: this.getValue(
        container ? container.prevButton : this.prevButton
      ),
      hasReset: this.getValue(container ? container.hasReset : this.hasReset),
      disableReset: this.getValue(
        container ? container.disableReset : this.disableReset
      ),
      resetConfirmer: this.getValue(
        container ? container.resetConfirmer : this.resetConfirmer
      ),
      resetPosition: this.getValue(
        container ? container.resetPosition : this.resetPosition
      ),
      resetCssClass: this.getValue(
        container ? container.resetCssClass : this.resetCssClass
      ),
      resetButton: this.getValue(
        container ? container.resetButton : this.resetButton
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region IXNextPrevResetAndHostContainer ...
type TXNextPrevResetAndHostContainer = IXNextPrevResetContainer &
  IXCardNextPrevResetHostContainer;
export interface IXNextPrevResetAndHostContainer
  extends IXBaseContainer,
    TXNextPrevResetAndHostContainer {}

@Injectable({
  providedIn: 'root',
})
export class XNextPrevResetAndHostContainer
  extends XBaseContainer
  implements Readonly<IXNextPrevResetAndHostContainer>
{
  //
  //#region Props ...
  //
  //#region Inteface ...
  //
  //#region Next ...
  hasNext: XStandardType<boolean> = this.nextPrevResetPropertyProvider.hasNext;
  disableNext: XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.disableNext;
  nextConfirmer: () => XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.nextConfirmer;
  nextPosition: XStandardType<XSlotNameIdentifier> =
    this.nextPrevResetPropertyProvider.nextPosition;
  nextCssClass: XStandardType<XOneOrManyType<string>> =
    this.nextPrevResetPropertyProvider.nextCssClass;
  nextButton: XStandardType<Partial<IXButtonContainer>> =
    this.nextPrevResetPropertyProvider.nextButton;
  //#endregion

  //
  //#region Prev ...
  hasPrev: XStandardType<boolean> = this.nextPrevResetPropertyProvider.hasPrev;
  disablePrev: XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.disablePrev;
  prevConfirmer: () => XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.prevConfirmer;
  prevPosition: XStandardType<XSlotNameIdentifier> =
    this.nextPrevResetPropertyProvider.prevPosition;
  prevCssClass: XStandardType<XOneOrManyType<string>> =
    this.nextPrevResetPropertyProvider.prevCssClass;
  prevButton: XStandardType<Partial<IXButtonContainer>> =
    this.nextPrevResetPropertyProvider.prevButton;
  //#endregion

  //
  //#region Reset ...
  hasReset: XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.hasReset;
  disableReset: XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.disableReset;
  resetConfirmer: () => XStandardType<boolean> =
    this.nextPrevResetPropertyProvider.resetConfirmer;
  resetPosition: XStandardType<XSlotNameIdentifier> =
    this.nextPrevResetPropertyProvider.resetPosition;
  resetCssClass: XStandardType<XOneOrManyType<string>> =
    this.nextPrevResetPropertyProvider.resetCssClass;
  resetButton: XStandardType<Partial<IXButtonContainer>> =
    this.nextPrevResetPropertyProvider.resetButton;
  //#endregion
  //#endregion

  //
  //#region Host ...
  //
  //#region Next ...
  readonly canNext: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.canNext;
  readonly onNext: EventEmitter<string> =
    this.cardNextPrevResetPropertyProvider.onNext;
  //#endregion

  //
  //#region Prev ...
  readonly canPrev: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.canPrev;
  readonly onPrev: EventEmitter<string> =
    this.cardNextPrevResetPropertyProvider.onPrev;
  //#endregion

  //
  //#region Reset ...
  readonly canReset: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.canReset;
  readonly onReset: EventEmitter<string> =
    this.cardNextPrevResetPropertyProvider.onReset;
  //#endregion
  //#endregion

  //
  //#region XComponentCard ...
  readonly isInPage: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.wrapWithCard;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.cardShowActions;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardNextPrevResetPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardNextPrevResetPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardNextPrevResetPropertyProvider.cardTitle;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardNextPrevResetPropertyProvider.cardActionProvider;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardNextPrevResetPropertyProvider.cardSubTitle;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardNextPrevResetPropertyProvider.cardCssClass;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected nextPrevResetPropertyProvider: XNextPrevResetContainer,
    protected cardNextPrevResetPropertyProvider: XCardNextPrevResetHostContainer
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export class XNextPrevResetAndHostContainerComponent
  extends XBaseContainerComponent
  implements IXNextPrevResetAndHostContainer
{
  //
  //#region Props ...
  //
  //#region XComponentCard ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;
  //#endregion

  //
  //#region XNextPrevResetHost ...
  //
  //#region Next ...
  @Input()
  canNext: XStandardType<boolean> = this.propertyProvider.canNext;

  @Output()
  onNext: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Prev ...
  @Input()
  canPrev: XStandardType<boolean> = this.propertyProvider.canPrev;

  @Output()
  onPrev: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Reset ...
  @Input()
  canReset: XStandardType<boolean> = this.propertyProvider.canReset;

  @Output()
  onReset: EventEmitter<string> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Props ...
  //
  //#region Next ...
  @Input()
  hasNext: XStandardType<boolean> = this.propertyProvider.hasNext;

  @Input()
  disableNext: XStandardType<boolean> = this.propertyProvider.disableNext;

  @Input()
  nextConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.nextConfirmer;

  @Input()
  nextPosition: XStandardType<XSlotNameIdentifier> =
    this.propertyProvider.nextPosition;

  @Input()
  nextCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.nextCssClass;

  @Input()
  nextButton: XStandardType<Partial<IXButtonContainer>> =
    this.propertyProvider.nextButton;
  //#endregion

  //
  //#region Prev ...
  @Input()
  hasPrev: XStandardType<boolean> = this.propertyProvider.hasPrev;

  @Input()
  disablePrev: XStandardType<boolean> = this.propertyProvider.disablePrev;

  @Input()
  prevConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.prevConfirmer;

  @Input()
  prevPosition: XStandardType<XSlotNameIdentifier> =
    this.propertyProvider.prevPosition;

  @Input()
  prevCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.prevCssClass;

  @Input()
  prevButton: XStandardType<Partial<IXButtonContainer>> =
    this.propertyProvider.prevButton;
  //#endregion

  //
  //#region Reset ...
  @Input()
  hasReset: XStandardType<boolean> = this.propertyProvider.hasReset;

  @Input()
  disableReset: XStandardType<boolean> = this.propertyProvider.disableReset;

  @Input()
  resetConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.resetConfirmer;

  @Input()
  resetPosition: XStandardType<XSlotNameIdentifier> =
    this.propertyProvider.resetPosition;

  @Input()
  resetCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.resetCssClass;

  @Input()
  resetButton: XStandardType<Partial<IXButtonContainer>> =
    this.propertyProvider.resetButton;
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XNextPrevResetAndHostContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXNextPrevResetAndHostContainer
  ): Observable<IXNextPrevResetAndHostContainer> {
    return forkJoin({
      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region XNextPrevResetHost ...
      //
      canNext: this.getValue(container ? container.canNext : this.canNext),
      canPrev: this.getValue(container ? container.canPrev : this.canPrev),
      canReset: this.getValue(container ? container.canReset : this.canReset),
      //#endregion

      //
      //#region Interface ...
      //
      hasNext: this.getValue(container ? container.hasNext : this.hasNext),
      disableNext: this.getValue(
        container ? container.disableNext : this.disableNext
      ),
      nextConfirmer: this.getValue(
        container ? container.nextConfirmer : this.nextConfirmer
      ),
      nextPosition: this.getValue(
        container ? container.nextPosition : this.nextPosition
      ),
      nextCssClass: this.getValue(
        container ? container.nextCssClass : this.nextCssClass
      ),
      nextButton: this.getValue(
        container ? container.nextButton : this.nextButton
      ),
      hasPrev: this.getValue(container ? container.hasPrev : this.hasPrev),
      disablePrev: this.getValue(
        container ? container.disablePrev : this.disablePrev
      ),
      prevConfirmer: this.getValue(
        container ? container.prevConfirmer : this.prevConfirmer
      ),
      prevPosition: this.getValue(
        container ? container.prevPosition : this.prevPosition
      ),
      prevCssClass: this.getValue(
        container ? container.prevCssClass : this.prevCssClass
      ),
      prevButton: this.getValue(
        container ? container.prevButton : this.prevButton
      ),
      hasReset: this.getValue(container ? container.hasReset : this.hasReset),
      disableReset: this.getValue(
        container ? container.disableReset : this.disableReset
      ),
      resetConfirmer: this.getValue(
        container ? container.resetConfirmer : this.resetConfirmer
      ),
      resetPosition: this.getValue(
        container ? container.resetPosition : this.resetPosition
      ),
      resetCssClass: this.getValue(
        container ? container.resetCssClass : this.resetCssClass
      ),
      resetButton: this.getValue(
        container ? container.resetButton : this.resetButton
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          onNext: this.onNext,
          onPrev: this.onPrev,
          onReset: this.onReset,
        };
      })
    );
  }
  //#endregion
}
//#endregion
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\i-x-toolbar-footer.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
} from 'x-framework-core';
import {
  XBackSlotIdentifier,
  XLogoSlotIdentifier,
  XMenuSlotIdentifier,
  XTitleSlotIdentifier,
  XContentSlotIdentifier,
} from '../x-page/x-page.typings';
import { map } from 'rxjs/operators';
import {
  XFooterActionModel,
  XComponentFooterContainer,
  IXComponentFooterContainer,
} from '../x-page/x-footer/x-footer.typings';
import {
  XToolbarActionModel,
  XComponentToolbarContainer,
  IXComponentToolbarContainer,
} from '../x-page/x-toolbar/x-toolbar.typings';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XBaseContainer, XBaseContainerComponent } from './i-x-base.container';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Container ...
type TXToolbarFooterContainer = IXComponentToolbarContainer &
  IXComponentFooterContainer;
export interface IXToolbarFooterContainer extends TXToolbarFooterContainer {}

@Injectable({
  providedIn: 'root',
})
export class XToolbarFooterContainer
  extends XBaseContainer
  implements Readonly<IXToolbarFooterContainer>
{
  //
  //#region Props ...
  //
  //#region Toolbar ...
  //
  readonly showToolbar: XStandardType<boolean> =
    this.toolbarPropertyProvider.showToolbar;

  //
  readonly toolbarActionProvider: Subject<XToolbarActionModel> =
    this.toolbarPropertyProvider.toolbarActionProvider;
  readonly toolbarCssClass: XStandardType<XOneOrManyType<string>> =
    this.toolbarPropertyProvider.toolbarCssClass;

  //
  readonly toolbarColor: XStandardType<XColorIdentifier> =
    this.toolbarPropertyProvider.toolbarColor;

  //
  readonly toolbarShowProgressBar: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarShowProgressBar;
  readonly toolbarProgressBarColor: XStandardType<XColorIdentifier> =
    this.toolbarPropertyProvider.toolbarProgressBarColor;

  //
  readonly toolbarShowTitle: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarShowTitle;
  readonly toolbarTitleSlot: XStandardType<XTitleSlotIdentifier> =
    this.toolbarPropertyProvider.toolbarTitleSlot;
  readonly toolbarTitle: XStandardType<string> =
    this.toolbarPropertyProvider.toolbarTitle;

  //
  readonly toolbarShowSubTitle: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarShowSubTitle;
  readonly toolbarSubTitle: XStandardType<string> =
    this.toolbarPropertyProvider.toolbarSubTitle;

  //
  readonly toolbarShowLogo: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarShowLogo;
  readonly toolbarLogoSlot: XStandardType<XLogoSlotIdentifier> =
    this.toolbarPropertyProvider.toolbarLogoSlot;
  readonly toolbarLogoUrl: XStandardType<string> =
    this.toolbarPropertyProvider.toolbarLogoUrl;

  //
  readonly toolbarHasBack: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarHasBack;
  readonly toolbarShowBack: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarShowBack;
  readonly toolbarBackSlot: XStandardType<XBackSlotIdentifier> =
    this.toolbarPropertyProvider.toolbarBackSlot;
  readonly toolbarBackHandler?: () => XStandardType<void> =
    this.toolbarPropertyProvider.toolbarBackHandler;
  readonly toolbarDefaultHref: string =
    this.toolbarPropertyProvider.toolbarDefaultHref;

  //
  readonly toolbarHasMenu: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarHasMenu;
  readonly toolbarShowMenu: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarShowMenu;
  readonly toolbarAutoHideMenu: XStandardType<boolean> =
    this.toolbarPropertyProvider.toolbarAutoHideMenu;
  readonly toolbarMenuSlot: XStandardType<XMenuSlotIdentifier> =
    this.toolbarPropertyProvider.toolbarMenuSlot;

  //
  readonly toolbarStartSlotButtonsTemplate: TemplateRef<any> =
    this.toolbarPropertyProvider.toolbarStartSlotButtonsTemplate;
  readonly toolbarEndSlotButtonsTemplate: TemplateRef<any> =
    this.toolbarPropertyProvider.toolbarEndSlotButtonsTemplate;

  //
  readonly toolbarContentSlot: XStandardType<XContentSlotIdentifier> =
    this.toolbarPropertyProvider.toolbarContentSlot;
  readonly toolbarContentTemplate: TemplateRef<any> =
    this.toolbarPropertyProvider.toolbarContentTemplate;
  //#endregion

  //
  //#region Footer ...
  //
  readonly showFooter: XStandardType<boolean> =
    this.footerPropertyProvider.showFooter;

  //
  readonly footerActionProvider: Subject<XFooterActionModel> =
    this.footerPropertyProvider.footerActionProvider;
  readonly footerCssClass: XStandardType<XOneOrManyType<string>> =
    this.footerPropertyProvider.footerCssClass;

  //
  readonly footerColor: XStandardType<XColorIdentifier> =
    this.footerPropertyProvider.footerColor;
  readonly footerContentTemplate: TemplateRef<any> =
    this.footerPropertyProvider.footerContentTemplate;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected toolbarPropertyProvider: XComponentToolbarContainer,
    protected footerPropertyProvider: XComponentFooterContainer
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XToolbarFooterContainerComponent
  extends XBaseContainerComponent
  implements IXToolbarFooterContainer
{
  //
  //#region Props ...
  //
  //#region Toolbar ...
  //
  @Input()
  showToolbar: XStandardType<boolean> = this.propertyProvider.showToolbar;

  //
  @Input()
  toolbarActionProvider: Subject<XToolbarActionModel> =
    this.propertyProvider.toolbarActionProvider;

  @Input()
  toolbarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.toolbarCssClass;

  //
  @Input()
  toolbarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarColor;

  //
  @Input()
  toolbarShowProgressBar: XStandardType<boolean> =
    this.propertyProvider.toolbarShowProgressBar;

  @Input()
  toolbarProgressBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarProgressBarColor;

  //
  @Input()
  toolbarShowTitle: XStandardType<boolean> =
    this.propertyProvider.toolbarShowTitle;

  @Input()
  toolbarTitleSlot: XStandardType<XTitleSlotIdentifier> =
    this.propertyProvider.toolbarTitleSlot;

  @Input()
  toolbarTitle: XStandardType<string> = this.propertyProvider.toolbarTitle;

  //
  @Input()
  toolbarShowSubTitle: XStandardType<boolean> =
    this.propertyProvider.toolbarShowSubTitle;

  @Input()
  toolbarSubTitle: XStandardType<string> =
    this.propertyProvider.toolbarSubTitle;

  //
  @Input()
  toolbarShowLogo: XStandardType<boolean> =
    this.propertyProvider.toolbarShowLogo;

  @Input()
  toolbarLogoSlot: XStandardType<XLogoSlotIdentifier> =
    this.propertyProvider.toolbarLogoSlot;

  @Input()
  toolbarLogoUrl: XStandardType<string> = this.propertyProvider.toolbarLogoUrl;

  //
  @Input()
  toolbarHasBack: XStandardType<boolean> = this.propertyProvider.toolbarHasBack;

  @Input()
  toolbarShowBack: XStandardType<boolean> =
    this.propertyProvider.toolbarShowBack;

  @Input()
  toolbarBackSlot: XStandardType<XBackSlotIdentifier> =
    this.propertyProvider.toolbarBackSlot;

  @Input()
  toolbarBackHandler?: () => XStandardType<void> =
    this.propertyProvider.toolbarBackHandler;

  @Input()
  toolbarDefaultHref: string = this.propertyProvider.toolbarDefaultHref;

  //
  @Input()
  toolbarHasMenu: XStandardType<boolean> = this.propertyProvider.toolbarHasMenu;

  @Input()
  toolbarShowMenu: XStandardType<boolean> =
    this.propertyProvider.toolbarShowMenu;

  @Input()
  toolbarAutoHideMenu: XStandardType<boolean> =
    this.propertyProvider.toolbarAutoHideMenu;

  @Input()
  toolbarMenuSlot: XStandardType<XMenuSlotIdentifier> =
    this.propertyProvider.toolbarMenuSlot;

  //
  @Input()
  toolbarStartSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarStartSlotButtonsTemplate;

  @Input()
  toolbarEndSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarEndSlotButtonsTemplate;

  //
  @Input()
  toolbarContentSlot: XStandardType<XContentSlotIdentifier> =
    this.propertyProvider.toolbarContentSlot;

  @Input()
  toolbarContentTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarContentTemplate;
  //#endregion

  //
  //#region Footer ...
  //
  @Input()
  showFooter: XStandardType<boolean> = this.propertyProvider.showFooter;

  //
  @Input()
  footerActionProvider: Subject<XFooterActionModel> =
    this.propertyProvider.footerActionProvider;

  @Input()
  footerCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.footerCssClass;

  //
  @Input()
  footerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.footerColor;

  @Input()
  footerContentTemplate: TemplateRef<any> =
    this.propertyProvider.footerContentTemplate;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XToolbarFooterContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXToolbarFooterContainer
  ): Observable<IXToolbarFooterContainer> {
    return forkJoin({
      //
      //#region Toolbar ...
      showToolbar: this.getValue(
        container ? container.showToolbar : this.showToolbar
      ),
      toolbarCssClass: this.getValue(
        container ? container.toolbarCssClass : this.toolbarCssClass
      ),
      toolbarColor: this.getValue(
        container ? container.toolbarColor : this.toolbarColor
      ),
      toolbarShowProgressBar: this.getValue(
        container
          ? container.toolbarShowProgressBar
          : this.toolbarShowProgressBar
      ),
      toolbarProgressBarColor: this.getValue(
        container
          ? container.toolbarProgressBarColor
          : this.toolbarProgressBarColor
      ),
      toolbarShowTitle: this.getValue(
        container ? container.toolbarShowTitle : this.toolbarShowTitle
      ),
      toolbarTitleSlot: this.getValue(
        container ? container.toolbarTitleSlot : this.toolbarTitleSlot
      ),
      toolbarTitle: this.getValue(
        container ? container.toolbarTitle : this.toolbarTitle
      ),
      toolbarShowSubTitle: this.getValue(
        container ? container.toolbarShowSubTitle : this.toolbarShowSubTitle
      ),
      toolbarSubTitle: this.getValue(
        container ? container.toolbarSubTitle : this.toolbarSubTitle
      ),
      toolbarShowLogo: this.getValue(
        container ? container.toolbarShowLogo : this.toolbarShowLogo
      ),
      toolbarLogoSlot: this.getValue(
        container ? container.toolbarLogoSlot : this.toolbarLogoSlot
      ),
      toolbarLogoUrl: this.getValue(
        container ? container.toolbarLogoUrl : this.toolbarLogoUrl
      ),
      toolbarHasBack: this.getValue(
        container ? container.toolbarHasBack : this.toolbarHasBack
      ),
      toolbarShowBack: this.getValue(
        container ? container.toolbarShowBack : this.toolbarShowBack
      ),
      toolbarBackSlot: this.getValue(
        container ? container.toolbarBackSlot : this.toolbarBackSlot
      ),
      toolbarBackHandler: this.getValue(
        container ? container.toolbarBackHandler : this.toolbarBackHandler
      ),
      toolbarDefaultHref: this.getValue(
        container ? container.toolbarDefaultHref : this.toolbarDefaultHref
      ),
      toolbarHasMenu: this.getValue(
        container ? container.toolbarHasMenu : this.toolbarHasMenu
      ),
      toolbarShowMenu: this.getValue(
        container ? container.toolbarShowMenu : this.toolbarShowMenu
      ),
      toolbarAutoHideMenu: this.getValue(
        container ? container.toolbarAutoHideMenu : this.toolbarAutoHideMenu
      ),
      toolbarMenuSlot: this.getValue(
        container ? container.toolbarMenuSlot : this.toolbarMenuSlot
      ),
      toolbarStartSlotButtonsTemplate: this.getValue(
        container
          ? container.toolbarStartSlotButtonsTemplate
          : this.toolbarStartSlotButtonsTemplate
      ),
      toolbarEndSlotButtonsTemplate: this.getValue(
        container
          ? container.toolbarEndSlotButtonsTemplate
          : this.toolbarEndSlotButtonsTemplate
      ),
      toolbarContentSlot: this.getValue(
        container ? container.toolbarContentSlot : this.toolbarContentSlot
      ),
      toolbarContentTemplate: this.getValue(
        container
          ? container.toolbarContentTemplate
          : this.toolbarContentTemplate
      ),
      //#endregion

      //
      //#region Footer ...
      showFooter: this.getValue(
        container ? container.showFooter : this.showFooter
      ),
      footerCssClass: this.getValue(
        container ? container.footerCssClass : this.footerCssClass
      ),
      footerColor: this.getValue(
        container ? container.footerColor : this.footerColor
      ),
      footerContentTemplate: this.getValue(
        container ? container.footerContentTemplate : this.footerContentTemplate
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          toolbarActionProvider: container
            ? container.toolbarActionProvider
            : this.toolbarActionProvider,

          //
          footerActionProvider: container
            ? container.footerActionProvider
            : this.footerActionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base-action.model.ts

export interface XBaseActionModel<T> {
  action: T;
  payload?: any;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base-component.ts

import {
  Input,
  OnInit,
  Inject,
  Output,
  NgZone,
  OnDestroy,
  Component,
  Renderer2,
  OnChanges,
  ElementRef,
  EventEmitter,
  AfterViewInit,
  SimpleChanges,
  AfterContentInit,
  AfterViewChecked,
  ChangeDetectorRef,
  AfterContentChecked,
} from '@angular/core';
import {
  tap,
  map,
  first,
  filter,
  takeUntil,
  concatMap,
  catchError,
} from 'rxjs/operators';
import {
  keys,
  XTask,
  XColor,
  XParam,
  toArray,
  getValue,
  notValue,
  isNumber,
  XUiEvent,
  hasChild,
  XRenderer,
  isFunction,
  XException,
  formatDate,
  isValueDate,
  applyLocale,
  toObservable,
  XResourceIDs,
  XExceptionIDs,
  XStandardType,
  fromUtcString,
  XOneOrManyType,
  toNormalString,
  stringFormatter,
  getDefaultValue,
  XFormControlState,
  isNullOrUndefined,
  getErrorException,
  getDateFormatString,
  isNullOrEmptyString,
  XResourceIdentifier,
  XExceptionIdentifier,
  XModalButtonRoleIdentifier,
  nameof,
  generateUuid,
  XKey,
} from 'x-framework-core';
import { NgModel } from '@angular/forms';
import { NavigationExtras } from '@angular/router';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { IXBaseObservableTools } from './x-base.observable.tools';
import { Subject, of, Observable, throwError, forkJoin } from 'rxjs';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';
import { XBaseActionModel } from './x-base-action.model';

@Component({
  template: '',
})
export abstract class XBaseComponent
  extends XRenderer
  implements
    OnInit,
    OnDestroy,
    OnChanges,
    AfterViewInit,
    AfterViewChecked,
    AfterContentInit,
    AfterContentChecked,
    IXBaseObservableTools
{
  //
  //#region Props ...
  //
  public readonly isMobileUi$ = this.managerService.isMobileUi$;
  public readonly isNotMobileUi$ = this.managerService.isNotMobileUi$;

  //
  public readonly ResourceIDs = Object.assign({}, XResourceIDs);
  public readonly ExceptionIDs = Object.assign({}, XExceptionIDs);

  //
  public TaskRepo: XTask<any>[] = [];
  public RunningTasks: XTask<any>[] = [];

  //
  public unsubscriber = new Subject();

  //
  protected applyThemeColors = false;

  /**
   * Unique Component Identifier ...
   */
  readonly uuid = generateUuid('X[' + this.constructor.name + ']');

  //
  //#region Component reload ...
  private SHOW_COMPONENT: boolean = true;
  set showComponent(v: boolean) {
    //
    if (this.SHOW_COMPONENT !== v) {
      this.onComponentVisibilityChanged(v);
    }

    //
    this.SHOW_COMPONENT = v;
  }
  get showComponent() {
    return this.SHOW_COMPONENT;
  }
  async reloadComponent(delay?: number) {
    //
    if (!isNullOrUndefined(delay) && delay < 100) {
      delay = 100;
    }

    //
    this.showComponent = false;
    this.detectChanges();
    return this.waitForElement(this.element.nativeElement.tagName).then(
      (el) => {
        //
        setTimeout(() => {
          //
          this.showComponent = true;
          this.detectChanges();
        }, delay);
      }
    );
  }
  onComponentVisibilityChanged(visible: boolean) {}
  //#endregion

  //
  //#region Loading ...
  /**
   * Notifiy Loading Changed ...
   */
  @Output()
  loadingNotifier = new EventEmitter<{ id: string; state: boolean }>();

  private LOADING: boolean = false;

  /**
   * Loading State of Component Representation ...
   */
  @Input()
  set loading(v: boolean) {
    //
    this.LOADING = v;
    this.onLoading(v);
    this.loadingNotifier.emit({ id: this.uuid, state: v });
  }
  get loading() {
    return this.LOADING;
  }

  onLoading(state: boolean) {}
  //#endregion

  //
  //#region Ui Disabled ...
  /**
   * Notifiy Ui Disabled Changed ...
   */
  @Output()
  uiDisabledNotifier = new EventEmitter<{ id: string; state: boolean }>();

  private UI_DISABLED: boolean = false;

  /**
   * Set UI State Disables / Enabled of Component ...
   */
  @Input()
  set uiDisabled(v: boolean) {
    //
    this.UI_DISABLED = v;
    this.onUiDisabled(v);
    this.uiDisabledNotifier.emit({ id: this.uuid, state: v });
  }
  get uiDisabled() {
    return this.UI_DISABLED;
  }

  onUiDisabled(state: boolean) {}
  //#endregion

  //
  //#region Error ...
  /**
   * Notify Error Changed ...
   */
  errorNotifier = new EventEmitter<{ id: string; state: boolean }>();

  private ERROR: XException = undefined;

  /**
   * Set Specified Error of Component if Exists ...
   */
  @Input()
  set error(v: XException) {
    //
    this.ERROR = v;
    this.onError(v);

    //
    const hasError = isNullOrUndefined(v);
    this.errorNotifier.emit({ id: this.uuid, state: hasError });
  }
  get error() {
    return this.ERROR;
  }

  onError(error: XException) {}
  //#endregion

  //
  //#region Lock ...
  /**
   * Notify Lock Changed ...
   */
  @Output()
  lockNotifier = new EventEmitter<{ id: string; state: boolean }>();

  private LOCK: boolean = false;

  /**
   * Specified Component Locked or not ...
   */
  @Input()
  set lock(v: boolean) {
    //
    this.LOCK = v;
    this.onLock(v);
    this.lockNotifier.emit({ id: this.uuid, state: v });
  }
  get lock() {
    return this.LOCK;
  }

  onLock(state: boolean) {}
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef
  ) {
    super(config, element, renderer, ruler, zone);
  }
  //#endregion

  //
  //#region Abstracted ...
  //#endregion

  //
  //#region LifeCycle ...
  ngOnInit(): void {
    //
    this.clearTasks();
    this.registerComponentHandlers();

    //
    this.onInit();
  }

  ngAfterViewInit(): void {
    //
    this.registerViewHandlers();

    //
    // Apply theme Colors only when it's required ...
    if (this.applyThemeColors) {
      this.managerService.themeManagerService
        .applyCurrentTheme(this.element)
        .then(() => this.detectChanges());
    }

    //
    // Handle Apply Manager Service UI State Changed Events is Base Components ...
    this.applyTakeUntilWrapper(
      this.managerService.uiStateChanged.asObservable()
    ).subscribe((state) => {
      //
      if (state === XUiEvent.StartLoading && !this.loading) {
        this.loading = true;
      } else if (state === XUiEvent.StopLoading && this.loading) {
        this.loading = false;
      } else if (state === XUiEvent.Disabled && !this.uiDisabled) {
        this.uiDisabled = true;
      } else if (state === XUiEvent.Enabled && this.uiDisabled) {
        this.uiDisabled = false;
      }

      //
      this.detectChanges();
    });

    //
    // Set UUID attribute ...
    this.setAttribute('xFramework', this.uuid);

    //
    this.afterViewInit();
  }

  ngAfterViewChecked(): void {
    this.afterViewChecked();
  }

  ngAfterContentInit(): void {
    this.afterContentInit();
  }

  ngAfterContentChecked(): void {
    this.afterContentChecked();
  }

  ngOnDestroy(): void {
    //
    this.clearTasks();
    this.unregisterHandlers();

    //
    this.onDestroy();
  }

  ngOnChanges(changes: SimpleChanges): void {
    //
    const changeKeys = keys(changes);

    //
    let isChanged = false;
    let applyChanges = false;

    //
    // Error Changes ...
    isChanged = changeKeys.includes(nameof<XBaseComponent>('error'));
    if (isChanged) {
      //
      const hasError = !isNullOrUndefined(this.error);
      if (hasError) {
        this.addClass('x-has-error');
      } else {
        this.removeClass('x-has-error');
      }

      //
      applyChanges = true;
      this.onError(this.error);
      this.errorNotifier.emit({ id: this.uuid, state: hasError });
    }

    //
    // Loading Changes ...
    isChanged = changeKeys.includes(nameof<XBaseComponent>('loading'));
    if (isChanged) {
      //
      if (this.loading) {
        this.addClass('x-loading');
      } else {
        this.removeClass('x-loading');
      }

      //
      applyChanges = true;
      this.onLoading(this.loading);
      this.loadingNotifier.emit({ id: this.uuid, state: this.loading });
    }

    //
    // uiDisabled Changes ...
    isChanged = changeKeys.includes(nameof<XBaseComponent>('uiDisabled'));
    if (isChanged) {
      //
      if (this.uiDisabled) {
        this.addClass('x-disabled');
      } else {
        this.removeClass('x-disabled');
      }

      //
      applyChanges = true;
      this.onUiDisabled(this.uiDisabled);
      this.uiDisabledNotifier.emit({ id: this.uuid, state: this.uiDisabled });
    }

    //
    // Lock Change ...
    isChanged = changeKeys.includes(nameof<XBaseComponent>('lock'));
    if (isChanged) {
      //
      if (this.lock) {
        this.addClass('x-locked');
      } else {
        this.removeClass('x-locked');
      }

      //
      applyChanges = true;
      this.onLock(this.lock);
      this.lockNotifier.emit({ id: this.uuid, state: this.lock });
    }

    //
    if (applyChanges) {
      this.detectChanges();
    }

    //
    this.onChange(changeKeys);
  }

  //
  //#region Ionic LifeCycles ...
  ionViewWillEnter() {
    this.willEnter();
  }

  ionViewDidEnter() {
    this.didEnter();
  }

  ionViewWillLeave() {
    this.willLeave();
  }

  ionViewDidLeave() {
    this.didLeave();
  }
  //#endregion

  //
  //#region Abstracted LifeCycles ...
  public onInit() {}

  public willEnter() {}

  public didEnter() {}

  public afterViewInit() {}

  public afterViewChecked() {}

  public afterContentInit() {}

  public afterContentChecked() {}

  public onChange(changeKeys: string[]) {}

  public willLeave() {}

  public didLeave() {}

  public onDestroy() {}
  //#endregion
  //#endregion

  //
  //#region Providers ...
  public registerInstanceHandlers(): void {}
  public registerViewHandlers(): void {}

  /**
   * Retrieve Specified Property Names ...
   */
  getFieldName<T>(obj: T, name: XKey<T>): string {
    return name.toString();
  }
  //#endregion

  //
  //#region Notifier Handlers ...
  public onResize(event?: Event) {}

  /**
   * notify a Task Start ...
   *
   * @param name task name
   */
  public onTaskStart(name: string, task: XTask<any>) {}

  /**
   * notify an error on running task ...
   *
   * @param name task name
   * @param task task object
   */
  public onTaskFailed(name: string, task: XTask<any>) {}

  /**
   * notify a task finished successfully ...
   *
   * @param name task name
   * @param task task object
   */
  public onTaskFinished(name: string, task: XTask<any>) {}
  //#endregion

  //
  //#region Handlers ...
  /**
   * Clear Error of Component ...
   */
  public clearError() {
    //
    setTimeout(() => {
      //
      this.error = null;
      this.managerService.error = null;
    }, 500);
  }

  /**
   * show error ...
   */
  public showError(
    exception:
      | Error
      | string
      | XException
      | XExceptionIDs
      | XExceptionIdentifier
      | any = undefined,
    next?: () => void
  ) {
    //
    let mException: any = getErrorException(exception);
    if (isNullOrUndefined(mException) && !isNullOrUndefined(this.error)) {
      mException = this.error;
    } else if (isNullOrUndefined(mException)) {
      mException = XExceptionIDs.ActionFailed;
    }

    //
    this.managerService.notificationService.presentErrorNotification({
      exception: mException,
      dissmissable: true,
      onWillDismiss: () => {
        //
        this.clearError();

        //
        if (next && isFunction(next)) {
          next();
        }
      },
    });
  }

  /**
   * handle error state ...
   */
  public async handleErrorState(
    exception?: XExceptionIdentifier,
    presentNotification: boolean = true,
    redirectToUrl?: string,
    returnUrl?: string,
    params?: NavigationExtras,
    duration: number = this.config.defaultNotificationDelay,
    replaceUrl = true
  ) {
    //
    //#region Inside Content ...
    let errorMessage =
      this.managerService.localizationService.getErrorMessage(exception);

    //
    if (
      exception.hasOwnProperty('payload') &&
      hasChild((exception as any)['payload'])
    ) {
      //
      const payload = (exception as any)['payload'] as Array<string>;

      //
      const localizedPayload = payload.map((pl) => {
        //
        if (isValueDate(pl)) {
          //
          const plDate = fromUtcString(pl);
          const locale = this.managerService.currentLocale;

          //
          const result = formatDate(
            plDate,
            locale,
            getDateFormatString(locale)
          );

          //
          return result;
        } else {
          return this.applyLocale(pl);
        }
      });

      //
      errorMessage = stringFormatter(errorMessage, localizedPayload);
    }

    //
    if (!params || !params.state) {
      params = {
        ...params,
        state: {},
      };
    }

    //
    params.state[XParam.ReturnUrl] = returnUrl;
    params.state[XParam.Exception] = exception;

    //
    if (presentNotification) {
      //
      if (duration < 500 || duration > 10000) {
        duration = this.config.defaultNotificationDelay;
      }

      //
      await this.managerService.notificationService.presentDangerNotification({
        message: errorMessage,
        dissmissable: true,
        opt: {
          duration,
        },
      });
    }

    //
    if (redirectToUrl && !isNullOrEmptyString(redirectToUrl)) {
      await this.managerService.navigateByUrlReplace(
        redirectToUrl,
        params,
        replaceUrl
      );
    }
    //#endregion
  }

  /**
   * show success ...
   */
  public showSuccess(message?: XResourceIdentifier, next?: () => void) {
    //
    if (!message) {
      message = XResourceIDs.success_msg;
    }

    //
    this.managerService.notificationService.presentSuccessNotification({
      message,
      dissmissable: true,
      onWillDismiss: () => {
        //
        if (next && isFunction(next)) {
          next();
        }
      },
    });
  }

  /**
   * present an action modal with yes and no button ...
   *
   * @param header the action title resource/msg ...
   * @param yesHandler the handler which must run when user accept action ...
   * @param yesRole the yes button role ...
   * @param noHandler the handler which must run when user reject action ...
   * @param noRole the no button role ...
   */
  public async presentYesNoDialog(
    header: XResourceIdentifier,
    yesHandler: () => void,
    yesRole?: XModalButtonRoleIdentifier,
    noHandler?: () => void,
    noRole?: XModalButtonRoleIdentifier
  ) {
    //
    if (isNullOrUndefined(header)) {
      header = this.ResourceIDs.are_you_sure;
    }

    //
    await this.managerService.dialogService.presentActionModal({
      header,
      buttons: [],
      keyboardClose: true,
      backdropDismiss: true,
      hasNo: true,
      noRole,
      noHandler,
      hasYes: true,
      yesRole,
      yesHandler,
    });
  }

  /**
   * present an action modal with yes and no button ...
   *
   * @param yesHandler the handler which must run when user accept action ...
   * @param yesRole the yes button role ...
   * @param noHandler the handler which must run when user reject action ...
   * @param noRole the no button role ...
   */
  public async presentDefaultYesNoDialog(
    yesHandler: () => void,
    yesRole?: XModalButtonRoleIdentifier,
    noHandler?: () => void,
    noRole?: XModalButtonRoleIdentifier
  ) {
    //
    await this.managerService.dialogService.presentActionModal({
      header: this.ResourceIDs.are_you_sure,
      buttons: [],
      keyboardClose: true,
      backdropDismiss: true,
      hasNo: true,
      noRole,
      noHandler,
      hasYes: true,
      yesRole,
      yesHandler,
    });
  }

  /**
   * lock page for an action
   *
   * @param isLoading specify state
   */
  public async setLoadingState(isLoading: boolean) {
    //
    this.loading = isLoading;
    this.uiDisabled = isLoading;

    //
    this.detectChanges();
  }

  /**
   * handle Default Action for Closing open Dialog ...
   */
  public handleDefaultDialogCloseAction() {
    //
    this.managerService.dialogService.modalController.dismiss({
      dismissed: true,
    });
  }

  /**
   * Release all Subscription ...
   */
  public releaseUnsubscriber() {
    this.unregisterHandlers();
  }

  /**
   * apply localization on numbers of a text ...
   *
   * @param content the text
   */
  public applyLocale(content: string | any) {
    return applyLocale(
      content || isNumber(content) ? content.toString() : '',
      this.managerService.localizationService.locale
    );
  }

  /**
   * apply locale and get current resource translated value ...
   *
   * @param id tes resource id
   */
  public resourceProvider(id: XResourceIdentifier): string {
    return (
      this.managerService.localizationService.getValue(id) || id.toString()
    );
  }

  /**
   * default join strings ...
   */
  public combinedResourceProvider(...ids: XResourceIdentifier[]) {
    return this.managerService.localizationService.combine(
      this.managerService.currentLocale,
      ' ',
      ...ids
    );
  }

  /**
   * get translation of exception id ...
   *
   * @param id exception id
   */
  public exceptionResourceProvider(id: XExceptionIdentifier) {
    return this.managerService.localizationService.getErrorMessage(id);
  }

  /**
   * retrieve property value ...
   *
   * @param object the context object ...
   * @param prop the property name ...
   */
  public getPropValue<T>(object: T, prop: keyof T) {
    return getValue(object, prop);
  }

  /**
   * retrieve desiered color name based on form control state ...
   *
   * @param status form status
   */
  public getIconColor(status: XFormControlState) {
    switch (status) {
      case 'DISABLED':
      case 'PENDING':
      default:
        return XColor.Warning;

      case 'VALID':
        return XColor.Primary;

      case 'INVALID':
        return XColor.Danger;
    }
  }

  /**
   * determines an element contains child nodes or not ...
   *
   * @param ref the element refrence
   */
  public hasChildNodes(ref: ElementRef) {
    return ref && (ref.nativeElement as HTMLElement).children.length > 0;
  }

  public getLengthError(model: NgModel, identifier: string) {
    //
    if (!model || !model.errors) {
      return '';
    }

    //
    const errorObject = model.getError(identifier);
    const requiredLength = errorObject.requiredLength;
    const actualLength = errorObject.actualLength;

    //
    return `(${actualLength}/${requiredLength})`;
  }

  /**
   * usefull for doing nothing ...
   */
  public nothing() {}
  //#endregion

  //
  //#region Task Methods ...
  /**
   * check a task is Exists or not ...
   *
   * @param name task name
   */
  public async hasTask(name: string) {
    //
    const hasTask = this.TaskRepo.findIndex((t) => t.name === name) > -1;
    return hasTask;
  }

  /**
   * retrieve a task by it's name ...
   *
   * @param name task name
   */
  public async getTask(name: string) {
    //
    const isExists = await this.hasTask(name);
    if (!isExists) {
      return null;
    }

    //
    const task = this.TaskRepo.find((t) => t.name === name);
    return task;
  }

  /**
   * add or update a task in Task Repo ...
   *
   * @param task the task object
   */
  public async addOrUpdateTask(task: XTask<any>) {
    //
    if (!task) {
      return;
    }

    //
    const isExists = await this.hasTask(task.name);
    if (!isExists) {
      this.TaskRepo.push(task);
    } else {
      await this.updateTask(task);
    }
  }

  /**
   * Add a New Task to Task Repo ...
   *
   * @param task task object
   */
  public async addTask(task: XTask<any>) {
    //
    if (!task) {
      return;
    }

    //
    const isExists = await this.hasTask(task.name);
    if (isExists) {
      return;
    }

    //
    this.TaskRepo.push(task);
  }

  /**
   * Update a Task in Task Repo ...
   *
   * @param task the task object
   */
  public async updateTask(task: XTask<any>) {
    //
    const isExists = await this.hasTask(task.name);
    if (!isExists) {
      return;
    }

    //
    await this.removeTask(task.name);
    this.TaskRepo.push(task);
  }

  /**
   * remove a task by it's name ...
   *
   * @param name the task name
   */
  public async removeTask(name: string) {
    //
    const isExists = await this.hasTask(name);
    if (!isExists) {
      return;
    }

    //
    if (this.managerService.taskManagerService.has(name)) {
      this.managerService.taskManagerService.delete(name);
    }

    //
    this.TaskRepo = this.TaskRepo.filter((t) => t.name !== name);
  }

  /**
   * call a task for running on task manager ...
   *
   * @param name task name
   */
  public async dispatchTask(name: string) {
    //
    if (isNullOrEmptyString(name)) {
      return;
    }

    //
    const task = await this.getTask(name);
    if (!task) {
      return;
    }

    //
    this.managerService.taskManagerService.add(task);
  }

  /**
   * check a Task is running or not by it's name
   *
   * @param name task name
   */
  public async isTaskRunning(name: string) {
    //
    if (isNullOrEmptyString(name)) {
      return false;
    }

    //
    name = toNormalString(name);

    //
    return (
      this.RunningTasks.findIndex((rt) => name === toNormalString(rt.name)) > -1
    );
  }

  /**
   * clear all tasks for component ...
   */
  public async clearTasks() {
    //
    this.TaskRepo = [];
    this.RunningTasks = [];
  }

  /**
   * do some actions related to Task Repo when a Task Start ...
   *
   * @param name task name
   * @param task task object
   */
  private async handlTaskStart(name: string, task: XTask<any>) {
    //
    const isRunning = await this.isTaskRunning(name);
    if (isRunning) {
      return;
    }

    //
    this.RunningTasks.push(task);
  }

  /**
   * do some actions related to Task Repo when a Task Failed ...
   *
   * @param name task name
   * @param task task object
   */
  private async handleTaskFailed(name: string, task: XTask<any>) {
    //
    const isRunning = await this.isTaskRunning(name);
    if (!isRunning) {
      return;
    }

    //
    const tName = toNormalString(task.name);
    this.RunningTasks = this.RunningTasks.filter(
      (t) => tName !== toNormalString(t.name)
    );
  }

  /**
   * do some actions related to Task Repo when a Task Finished ...
   *
   * @param name task name
   * @param task task object
   */
  private async handleTaskFinished(name: string, task: XTask<any>) {
    //
    const isRunning = await this.isTaskRunning(name);
    if (!isRunning) {
      return;
    }

    //
    const tName = toNormalString(task.name);
    this.TaskRepo = this.TaskRepo.filter(
      (t) => tName !== toNormalString(t.name)
    );
    this.RunningTasks = this.RunningTasks.filter(
      (t) => tName !== toNormalString(t.name)
    );
  }
  //#endregion

  //
  //#region Observable Actions ...
  /**
   * Check if items has childs or not ...
   *
   * @param items a list item ...
   */
  public hasChild(items: XOneOrManyType<any>) {
    return hasChild(items);
  }

  /**
   * convert an standard UI Value to Observable for consuming ...
   *
   * @param value the standard UI Value
   */
  public getValue<T>(value: XStandardType<T>) {
    return toObservable<T>(value);
  }

  /**
   * get stream Observble of specified standard value
   */
  public getValueStream<T>(value: XStandardType<T>) {
    return this.applyTakeUntilWrapper(this.getValue(value));
  }

  /**
   * retrieve an standard type var in promise
   *
   * @param value an standard type var
   */
  public async getValueAsync<T>(value: XStandardType<T>) {
    //
    if (!value) {
      return null;
    }

    //
    return await this.toPromise(this.getValue(value));
  }

  /**
   * convert to array ...
   *
   * @param value an standard type one or many arg ...
   */
  public getArrayValue<T>(value: XStandardType<XOneOrManyType<T>>) {
    return this.getValue(value).pipe(map((valueType) => toArray(valueType)));
  }

  /**
   * convert to array ...
   *
   * @param value an standard type one or many arg ...
   */
  public async getArrayValueAsync<T>(value: XStandardType<XOneOrManyType<T>>) {
    return await this.getValueAsync(this.getArrayValue(value));
  }

  /**
   * check a value has child or not ...
   *
   * @param value an standard one or many of type
   */
  public hasChildValue<T>(value: XStandardType<XOneOrManyType<T>>) {
    //
    return this.getValue(value).pipe(
      map((valueType) => hasChild(toArray(valueType)))
    );
  }

  /**
   * check a value has child or not ...
   *
   * @param value an standard one or many of type
   */
  public async hasChildValueAsync<T>(value: XStandardType<XOneOrManyType<T>>) {
    //
    const valueType = await this.getValueAsync(value);
    if (!valueType) {
      return false;
    }

    //
    return hasChild(toArray(valueType));
  }

  /**
   * count a value's child ...
   *
   * @param value an standard one or many of type
   */
  public countChildsValue<T>(value: XStandardType<XOneOrManyType<T>>) {
    return this.getValue(value).pipe(
      map((valueType) => toArray(valueType).length)
    );
  }

  /**
   * count a value's child ...
   *
   * @param value an standard one or many of type
   */
  public async countChildsValueAsync<T>(
    value: XStandardType<XOneOrManyType<T>>
  ) {
    //
    const valueType = await this.getValueAsync(value);
    if (!valueType) {
      return 0;
    }

    //
    return toArray(valueType).length;
  }

  /**
   * providing string resources ...
   *
   * @param value an standard one or many of type
   */
  public getResourceValue(value: XStandardType<XResourceIdentifier>) {
    //
    return !value
      ? of('')
      : this.getValue(value).pipe(
          map((val) => {
            return this.resourceProvider(val) || (value as string);
          })
        );
  }

  /**
   * providing string resources ...
   *
   * @param value an standard one or many of type
   */
  public async getResourceValueAsync(
    value: XStandardType<XResourceIdentifier>
  ) {
    //
    if (!value) {
      return '';
    }

    //
    const val = await this.getValueAsync(value);
    return this.resourceProvider(val) || (val as string);
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of string
   */
  public notEmptyValue(value: XStandardType<string | any>) {
    return this.getValue(value).pipe(map((val) => !isNullOrEmptyString(val)));
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of string
   */
  public async notEmptyValueAsync(value: any) {
    return await this.toPromise(this.notEmptyValue(value));
  }

  /**
   * determines a given value has not content and empty
   *
   * @param value an Standard type of string
   */
  public emptyValue(value: XStandardType<string | any>) {
    return this.getValue(value).pipe(map((val) => isNullOrEmptyString(val)));
  }

  /**
   * determines a given value has not content and empty
   *
   * @param value an Standard type of string
   */
  public async emptyValueAsync(value: XStandardType<string | any>) {
    return await this.toPromise(this.emptyValue(value));
  }

  /**
   * apply not operator to a boolean ...
   */
  public notValue(value: XStandardType<boolean>) {
    return notValue(this.getValue(value));
  }

  /**
   * apply not operator to a boolean ...
   */
  public async notValueAsync(value: XStandardType<boolean>) {
    return await this.getValueAsync(this.notValue(value));
  }

  /**
   * check all input args be same as value ...
   *
   * @param value any value which going to check with all args to be same as them ...
   * @param values the input args which be checked by value ...
   */
  public mustBeValue(value: any, ...values: XStandardType<any>[]) {
    //
    return forkJoin(values.map((v) => toObservable(v))).pipe(
      map((res) => {
        return res.every((i) => i === value);
      })
    );
  }

  /**
   * check all input args be same as value ...
   *
   * @param value any value which going to check with all args to be same as them ...
   * @param values the input args which be checked by value ...
   */
  public async mustBeValueAsync(value: any, ...values: XStandardType<any>[]) {
    //
    return (
      await Promise.all(values.map(async (v) => await this.getValueAsync(v)))
    ).every((v) => v === value);
  }

  /**
   * check all input args contains at least one value ...
   *
   * @param value any value which going to check with all args to find one same ...
   * @param values the input args which be checked by value ...
   */
  public atLeastHasOneValue(value: any, ...values: XStandardType<any>[]) {
    //
    return forkJoin(values.map((v) => toObservable(v))).pipe(
      map((res) => {
        return res.includes(value);
      })
    );
  }

  /**
   * check all input args contains at least one value ...
   *
   * @param value any value which going to check with all args to find one same ...
   * @param values the input args which be checked by value ...
   */
  public async atLeastHasOneValueAsync(
    value: any,
    ...values: XStandardType<any>[]
  ) {
    //
    return (
      await Promise.all(values.map(async (v) => await this.getValueAsync(v)))
    ).includes(value);
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of any
   */
  public notNullOrUndefinedValue(value: XStandardType<any>) {
    return this.getValue(value).pipe(map((val) => !isNullOrUndefined(val)));
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of any
   */
  public async notNullOrUndefinedValueAsync(value: XStandardType<any>) {
    return await this.toPromise(this.notNullOrUndefinedValue(value));
  }

  /**
   * determines a given value content must empty
   *
   * @param value an Standard type of any
   */
  public nullOrUndefinedValue(value: XStandardType<any>) {
    return this.getValue(value).pipe(map((val) => isNullOrUndefined(val)));
  }

  /**
   * determines a given value content must empty
   *
   * @param value an Standard type of any
   */
  public async nullOrUndefinedValueAsync(value: XStandardType<any>) {
    return await this.toPromise(this.nullOrUndefinedValue(value));
  }

  /**
   * retrieve an optional value ...
   *
   * @param value the optional standard value ...
   */
  public getOptionalValue<T>(value?: XStandardType<T>): Observable<T> {
    //
    if (value) {
      return this.getValue(value);
    }

    //
    return this.getValue(getDefaultValue(value));
  }

  /**
   * retrieve an optional value ...
   *
   * @param value the optional standard value ...
   */
  public async getOptionalValueAsync<T>(value?: XStandardType<T>) {
    return await this.toPromise(this.getOptionalValue(value));
  }

  /**
   * convert all values as observable of them ...
   *
   * @param values the standard list of specific type ...
   * @returns observable of values array of specific type ...
   */
  public getValues<T>(values?: XOneOrManyType<XStandardType<T>>) {
    //
    interface ForkSourceType<T> {
      [index: number]: XStandardType<T>;
    }

    //
    return this.getArrayValue(values).pipe(
      concatMap((values) => {
        //
        if (!hasChild(values)) {
          return this.getValue([] as T[]);
        } else {
          //
          const forkSource: ForkSourceType<T> = {};
          values.forEach((value, index) => {
            forkSource[index] = this.getValue(value);
          });

          //
          return forkJoin(forkSource).pipe(
            map((forkResult) => {
              //
              const respObj = forkResult as any;
              const mKeys = Object.keys(forkResult)
                .map((key) => key)
                .sort((a, b) => +a - +b);
              const result = mKeys.map((mKey: any) => respObj[mKey] as T);

              //
              return result;
            })
          );
        }
      })
    );
  }

  /**
   * convert all values as observable of them ...
   *
   * @param values the standard list of specific type ...
   * @returns observable of values array of specific type ...
   */
  public async getValuesAsync<T>(values?: XOneOrManyType<XStandardType<T>>) {
    return await this.toPromise(this.getValues(values));
  }

  /**
   * apply take until wrapper arround an observable ...
   *
   * @param task the observable object
   */
  public applyTakeUntilWrapper<T>(task: Observable<T>): Observable<T> {
    return task.pipe(takeUntil(this.unsubscriber));
  }

  /**
   * apply inside component loading wrapper arround an observable ...
   *
   * @param task the observable object
   */
  public applyLoadingWrapper<T>(task: Observable<T>): Observable<T> {
    return of(null).pipe(
      tap((_) => {
        this.uiDisabled = true;
        this.loading = true;
      }),
      concatMap((_) =>
        task.pipe(
          tap(() => {
            this.uiDisabled = false;
            this.loading = false;
          })
        )
      ),
      catchError((err) => {
        //
        this.uiDisabled = false;
        this.loading = false;
        return throwError(err);
      })
    );
  }

  /**
   * apply get first result of an observable ...
   *
   * @param task the observable object
   */
  public applyFirstWrapper<T>(task: Observable<T>): Observable<T> {
    return task.pipe(first());
  }

  /**
   * convert an observable to promise object ...
   *
   * @param task the observable object
   */
  public toPromise<T>(task: Observable<T>): Promise<T> {
    return this.applyFirstWrapper(task).toPromise();
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * Call Change Detector's Detect Change method ...
   */
  detectChanges() {
    this.changeDetector.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  /**
   * Register All Required Subscriptions ...
   */
  private registerComponentHandlers() {
    //
    this.registerUiHandlers();
    this.registerTaskManager();
    this.registerInstanceHandlers();
  }

  /**
   * Release all Subscriptions based on the Component ...
   */
  private unregisterHandlers() {
    //
    this.unsubscriber.next();
    this.unsubscriber.complete();
  }

  /**
   * register Ui Event Handlers ...
   */
  private registerUiHandlers() {
    //
    this.applyTakeUntilWrapper(this.ruler.change()).subscribe((resizeEvent) =>
      this.onResize(resizeEvent)
    );

    //
    this.applyTakeUntilWrapper(
      this.managerService.themeManagerService.notifyChanges.asObservable()
    ).subscribe(() => {
      this.detectChanges();
    });
  }

  /**
   * Register Task Handlers for Finish and Error of Tasks ...
   */
  private registerTaskManager() {
    //
    // Task Start ...
    this.applyTakeUntilWrapper(
      this.managerService.taskManagerService.taskStart.asObservable()
    )
      .pipe(filter((task) => !isNullOrUndefined(task)))
      .subscribe((task) => {
        //
        this.onTaskStart(task.name, task);

        //
        this.handlTaskStart(task.name, task);
      });

    //
    // Task Error ...
    this.applyTakeUntilWrapper(
      this.managerService.taskManagerService.taskError.asObservable()
    )
      .pipe(filter((task) => !isNullOrUndefined(task)))
      .subscribe((task) => {
        //
        this.onTaskFailed(task.name, task);

        //
        this.handleTaskFailed(task.name, task);
      });

    //
    // Task Finished ...
    this.applyTakeUntilWrapper(
      this.managerService.taskManagerService.taskFinished.asObservable()
    )
      .pipe(filter((task) => !isNullOrUndefined(task)))
      .subscribe((task) => {
        //
        this.onTaskFinished(task.name, task);

        //
        this.handleTaskFinished(task.name, task);
      });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base-dialog.service.ts

import {
  XBaseService,
  XDialogResult,
  isNullOrUndefined,
  XResourceIdentifier,
} from 'x-framework-core';
import {
  MatDialog,
  MatDialogRef,
  MatDialogConfig,
} from '@angular/material/dialog';
import {
  IXBaseDialogConfig,
  IXBaseDialogResult,
  XBaseDialogComponent,
  IXBaseDialogActionModel,
} from './x-base.dialog.component';
import { XManagerService } from 'x-framework-services';
import { EventEmitter, Inject, Injectable, Output } from '@angular/core';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

@Injectable({
  providedIn: 'root'
})
export abstract class XBaseDialogService<
  TItem,
  TActionProviderModel extends IXBaseDialogActionModel,
  TConfig extends IXBaseDialogConfig<TActionProviderModel>,
  TDialogCompnent extends XBaseDialogComponent<
    TItem,
    TActionProviderModel,
    TConfig
  >
> extends XBaseService {
  //
  //#region Props ...
  private MOBILE_CLASS_NAME = 'mobile';
  private DIALOG_REF: MatDialogRef<TDialogCompnent>;

  //
  isMobile$ = this.managerService.isMobileUi$;

  // tslint:disable-next-line:no-output-on-prefix
  @Output()
  onResult = new EventEmitter<IXBaseDialogResult>();
  //#endregion

  //
  //#region Abstract ...
  abstract defaultTitleResourceIdentifier: XResourceIdentifier;

  abstract openDialogAndReturnRef(
    config: MatDialogConfig
  ): MatDialogRef<TDialogCompnent>;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected dialog: MatDialog,
    protected managerService: XManagerService,
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig
  ) {
    super(config);
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * Open Select Map Dialog ...
   */
  async openDialog(config?: TConfig) {
    //
    // Prevent Double Bubble ...
    if (this.DIALOG_REF) {
      return;
    }

    //
    // Prepare Config ...
    if (!config) {
      config = {} as TConfig;
    }

    //
    //#region Default Config for Map Component ...
    //
    // Set Default Title ...
    if (config && !config.title) {
      config.title = this.defaultTitleResourceIdentifier;
    }
    //#endregion

    //
    // Prepare Dialog Config ...
    const data: MatDialogConfig = {
      data: config,
    };

    //
    data.panelClass = 'fullscreen-dialog';

    //
    // Show Cropper inside a Dialog ...
    this.DIALOG_REF = this.openDialogAndReturnRef(data);
    this.DIALOG_REF.disableClose = true;

    //
    // Attach Mobile Class ...
    this.isMobile$.subscribe((isMobile) => {
      //
      if (isMobile) {
        if (this.DIALOG_REF) {
          this.DIALOG_REF.addPanelClass(this.MOBILE_CLASS_NAME);
        }
      } else {
        if (this.DIALOG_REF) {
          this.DIALOG_REF.removePanelClass(this.MOBILE_CLASS_NAME);
        }
      }
    });

    //
    // Handle Backdrop ...
    this.DIALOG_REF.backdropClick().subscribe(() => {
      //
      // Cancel Dialog ...
      this.onResult.emit({ result: XDialogResult.Cancel });

      //
      this.closeDialog();
    });

    //
    // Handle Dialog Result ...
    this.DIALOG_REF.afterClosed().subscribe((model: IXBaseDialogResult) => {
      //
      if (isNullOrUndefined(model) || model.result !== XDialogResult.Ok) {
        //
        // Cancel Dialog ...
        this.onResult.emit({ result: XDialogResult.Cancel });
      } else {
        //
        // Handle Results ...
        this.onResult.emit(model);
      }

      //
      this.closeDialog();
    });
  }

  /**
   * Close Select Map Dialog ...
   */
  closeDialog() {
    //
    // Prevent Go Forward ...
    if (!this.DIALOG_REF) {
      return;
    }

    //
    // Close Dialog ...
    this.DIALOG_REF.close();
    this.DIALOG_REF = null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base-page.ts

import {
  NgZone,
  Inject,
  Output,
  Renderer2,
  Component,
  ElementRef,
  EventEmitter,
  HostListener,
  ChangeDetectorRef,
} from '@angular/core';
import {
  nameof,
  XResourceIDs,
  XStandardType,
  XCanDeactivate,
  XResourceIdentifier,
  XBehaviourEventType,
  isNullOrUndefined,
} from 'x-framework-core';
import { Subject, Observable } from 'rxjs';
import { XBaseComponent } from './x-base-component';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

@Component({
  template: '',
})
export abstract class XBasePage
  extends XBaseComponent
  implements XCanDeactivate
{
  /**
   * Since Page Components Manipulate Angular Router ...
   * and Angular Router has Deactivate Guards ...
   * and it Works where component is Locked ...
   * this Param Specified for Deactivation Guard to Do Unlock by Confirmation or not ...
   */
  protected confirmUnlock: boolean = true;

  /**
   * Specified when Page Component is Locked, make Guards available for Prevent Back ...
   */
  protected preventBackHandlingWhenHasLock: boolean = true;

  //
  @Output()
  backPreventNotifier = new EventEmitter<{ id: string; state: boolean }>();

  //
  private PREVENT_BACK = false;
  get isBackPreveted() {
    return this.PREVENT_BACK;
  }
  set preventBack(v: boolean) {
    //
    if (v && !this.PREVENT_BACK) {
      this.managerService.historyService.pushEmptyState();
    } else if (v && this.PREVENT_BACK) {
      this.managerService.historyService.replaceEmptyState();
    }

    //
    this.PREVENT_BACK = v;

    //
    if (v) {
      this.addClass('x-back-prevented');
    } else {
      this.addClass('x-back-prevented');
    }

    //
    this.detectChanges();
    this.onBackPrevent(v);
    this.backPreventNotifier.emit({ id: this.uuid, state: v });
  }

  //
  onBackPrevent(state: boolean) {}

  //
  private IS_PARSING_PARAMS = false;
  private NOT_PREVENT_REFRESH = true;

  //
  public titleRes?: XResourceIdentifier;

  //
  public scrollEventSimulator = new Subject<XBehaviourEventType>();

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Guards ...
  public canDeactivate(
    component: XCanDeactivate,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {
    return this.handleCanDeactivateGuard(component, route, state);
  }

  @HostListener('window:beforeunload', ['$event'])
  beforeUnloadHandler($event: any) {
    //
    if (!this.NOT_PREVENT_REFRESH) {
      $event.returnValue = true;
    }
  }
  //#endregion

  //
  //#region LifeCycle ...
  onInit() {
    super.onInit();

    //
    this.handleParseParams();
    this.registerPageHandlers();
  }

  public afterViewInit(): void {
    super.afterViewInit();

    //
    // Register Lock Notifier for Prevent Handling ...
    this.applyTakeUntilWrapper(this.lockNotifier.asObservable()).subscribe(
      (event) => {
        //
        if (isNullOrUndefined(event)) {
          return;
        }

        //
        if (this.preventBackHandlingWhenHasLock) {
          this.preventBack = event.state;
        }

        //
        this.detectChanges();
      }
    );
  }

  public onChange(changeKeys: string[]): void {
    super.onChange(changeKeys);

    //
    let isChanged = false;
    let applyChanges = false;

    //
    // Looking for Lock Changes ...
    isChanged = changeKeys.includes(nameof<XBasePage>('lock'));
    if (isChanged) {
      //
      applyChanges = true;
      if (this.preventBackHandlingWhenHasLock) {
        this.preventBack = this.lock;
      }
    }

    //
    if (applyChanges) {
      this.detectChanges();
    }
  }

  public onBack() {}

  didEnter() {
    super.didEnter();

    //
    this.handleResourcePresenting();
  }

  willLeave() {
    super.willLeave();

    //
    this.clearError();
    this.clearTasks();
  }

  public parseParams() {}
  //#endregion

  //
  //#region Notifiers ...
  public simulateScrollStart() {
    this.scrollEventSimulator.next('scrollStart');
  }

  public simulateScrollEnd() {
    this.scrollEventSimulator.next('scrollEnd');
  }

  public simulateScrolling() {
    this.scrollEventSimulator.next('scrolling');
  }
  //#endregion

  //
  //#region Protected ...
  /**
   * Do Lock Breakes Works ...
   *
   * @param acceptCb
   * @param cancelCb
   */
  protected async presentDeactiveGuardDialog(
    acceptCb?: () => XStandardType<void>,
    cancelCb?: () => XStandardType<void>
  ) {
    if (this.lock) {
      //
      const a = await this.managerService.dialogService.presentActionModal({
        header: XResourceIDs.locked_msg,
        hasAccept: true,
        acceptHandler: () => {
          //
          if (acceptCb) {
            acceptCb();
          }

          //
          this.lock = false;
        },
        hasCancel: true,
        cancelHandler: () => {
          //
          if (cancelCb) {
            cancelCb();
          }

          //
          this.lock = true;
        },
        buttons: [],
      });

      //
      await a.onDidDismiss();
    }
  }
  //#endregion

  //
  //#region Private ...
  private handleParseParams() {
    //
    if (this.IS_PARSING_PARAMS) {
      return;
    }

    //
    this.IS_PARSING_PARAMS = true;

    //
    this.getValueAsync(this.parseParams())
      .then(() => (this.IS_PARSING_PARAMS = false))
      .catch(
        () =>
          //
          (this.IS_PARSING_PARAMS = false)
      );
  }

  private registerPageHandlers() {
    //
    this.registerBackHandler();
    this.registerParamsHandler();
  }

  private handleResourcePresenting() {
    //
    if (!this.titleRes) {
      return;
    }

    //
    this.managerService.settingsService.setTitle(
      this.managerService.localizationService.getValue(this.titleRes)
    );
  }

  private async handleCanDeactivateGuard(
    component: XCanDeactivate,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ) {
    //
    let result = !this.lock;
    if (this.lock && this.confirmUnlock) {
      //
      await this.presentDeactiveGuardDialog(
        () => {
          result = true;
        },
        () => {
          result = false;
        }
      );
    }

    //
    return result;
  }

  private registerBackHandler() {
    //
    this.managerService.historyService.platformLocation.onPopState(() => {
      //
      if (this.PREVENT_BACK) {
        this.managerService.historyService.pushEmptyState();
      }
    });

    //
    this.applyTakeUntilWrapper(
      this.managerService.historyService.backHandler.asObservable()
    ).subscribe(() => {
      this.onBack();
    });
  }

  private registerParamsHandler() {
    //
    this.applyTakeUntilWrapper(this.managerService.routeDataChanged$).subscribe(
      (res) => {
        this.handleParseParams();
      }
    );

    //
    this.applyTakeUntilWrapper(this.managerService.routeParmChanged$).subscribe(
      (res) => {
        this.handleParseParams();
      }
    );

    //
    this.applyTakeUntilWrapper(
      this.managerService.routeQueryParmChanged$
    ).subscribe((res) => {
      this.handleParseParams();
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base.dialog.component.html

<div class="x-dialog-wrapper">
  <!-- Toolbar -->
  <ion-header class="x-dialog-header">
    <!-- Title Toolbar -->
    <ion-toolbar
      class="v-toolbar"
      [xColor]="'primary'"
      *ngIf="showToolbar | async"
    >
      <!-- Slots -->
      <div class="toolbar-area">
        <x-slotter>
          <!-- Start -->
          <x-slot
            [name]="SlotNames.START"
            [cssClass]="['ion-text-start', 'ion-float-start']"
          >
            <div
              matRipple
              (click)="handleBack()"
              class="toolbar-item toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="backIcon"
                [loading]="isLoading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>

          <!-- Center -->
          <x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
            <ion-title class="toolbar-item center-vertical-flex">
              <h3>
                {{ title | async }}
              </h3>
            </ion-title>
          </x-slot>

          <!-- End -->
          <x-slot
            [name]="SlotNames.END"
            [cssClass]="['ion-text-end', 'ion-float-end']"
          >
            <!-- Select -->
            <div
              matRipple
              (click)="handleSelect()"
              *ngIf="dialogCanSelect()"
              class="toolbar-item toolbar-inline-item toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="selectIcon"
                [loading]="isLoading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>
        </x-slotter>
      </div>

      <!-- Progressbar for Loading -->
      <ion-progress-bar
        *ngIf="isLoading"
        type="indeterminate"
        [color]="progressBarColor | async"
      ></ion-progress-bar>
    </ion-toolbar>
  </ion-header>

  <!-- Component -->
  <ion-content
    class="x-dialog-content-wrapper"
    [ngClass]="cssDialogClass | async"
    [class.has-toolbar]="showToolbar | async"
  >
  </ion-content>
</div>

<!-- Default Item Ref -->
<ng-template #vItemRef let-item>
  {{ item.data | json }}
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base.dialog.component.scss

::ng-deep.fullscreen-dialog {
  //
  width: 80vw !important;
  max-width: 80vw !important;

  //
  height: 80vh !important;
  max-height: 80vh !important;

  //
  &.mobile {
    //
    width: 100vw !important;
    max-width: 100vw !important;

    //
    height: 100vh !important;
    max-height: 100vh !important;
  }

  //
  .mat-dialog-container {
    padding: 0 !important;
  }
}

.x-dialog-wrapper {
  width: 100% !important;
  height: 100% !important;
  position: relative !important;
}

.x-dialog-header {
  position: sticky !important;
  top: 0;
  z-index: 1000;
}

.x-dialog-content-wrapper {
  height: calc(100%);

  &.has-toolbar {
    height: calc(100% - 56px);
  }
}

x-icon {
  --size: 24px;
}

.toolbar-area {
  margin: 0px;
}

.toolbar-item {
  width: auto;
  height: 48px;
}

.toolbar-inline-item {
  display: inline-flex !important;
}

.toolbar-button {
  width: 32px;
  margin: {
    left: 4px;
    right: 4px;
  }
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base.dialog.component.ts

import {
  Inject,
  NgZone,
  Output,
  Renderer2,
  Component,
  ElementRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XTask,
  XColor,
  toArray,
  isFunction,
  getTaskName,
  XStandardType,
  XDialogResult,
  XOneOrManyType,
  XColorIdentifier,
  isNullOrUndefined,
  isNullOrEmptyString,
  XResourceIdentifier,
} from 'x-framework-core';
import { of, Subject } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { XBaseComponent } from './x-base-component';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XIconNames } from '../x-icon/x-icon.typings';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from './x-base-action.model';
import { XButtonType } from '../x-button/x-button.typings';
import { XSlotLayout, XSlotName } from '../x-slotter/x-slotter.typings';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XBaseDialogAction {
  Refresh = 'refresh',
}

export type XBaseDialogActionIdentifier = XBaseDialogAction | string;

export interface IXBaseDialogActionModel
  extends XBaseActionModel<XBaseDialogActionIdentifier> {}

export interface IXBaseDialogConfig<
  TActionProviderModel extends IXBaseDialogActionModel
> {
  //
  showToolbar?: boolean;

  //
  title?: XResourceIdentifier;
  cssClass?: XOneOrManyType<string>;
  progressBarColor?: XColorIdentifier;

  //
  showSearchBar?: boolean;
  searchBarColor?: XColorIdentifier;
  searchBarInputColor?: XColorIdentifier;

  //
  actionProvider?: Subject<TActionProviderModel>;

  //
  canMultipleSelect?: boolean;

  //
  onItemSelect?: (item: XOneOrManyType<any>) => XStandardType<void>;

  //
  onTaskStart?: (name: string) => XStandardType<void>;
  onTaskFinished?: (name: string) => XStandardType<void>;
  onTaskFailed?: (name: string) => XStandardType<void>;
}

export interface IXBaseDialogResult {
  payload?: any;
  result: XDialogResult;
}

@Component({
  template: '',
})
export abstract class XBaseDialogComponent<
  TItem,
  TActionProviderModel extends IXBaseDialogActionModel,
  TConfig extends IXBaseDialogConfig<TActionProviderModel>
> extends XBaseComponent {
  //
  //#region Props ...
  //
  public cancelIcon = XIconNames.close;
  public selectIcon = XIconNames.checked;
  public backIcon = XIconNames.arrow_back;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly ButtonTypes = Object.assign({}, XButtonType);
  readonly SlotLayouts = Object.assign({}, XSlotLayout);

  //
  readonly isMobile$ = this.managerService.isMobileUi$;
  readonly isNotMobile$ = this.managerService.isNotMobileUi$;

  //
  public selectedItem: XOneOrManyType<TItem>;

  //
  //#region Prevent Back ...
  @Output()
  backPreventNotifier = new EventEmitter<{ id: string; state: boolean }>();

  //
  private PREVENT_BACK = false;
  get isBackPreveted() {
    return this.PREVENT_BACK;
  }
  set preventBack(v: boolean) {
    //
    if (v && !this.PREVENT_BACK) {
      this.managerService.historyService.pushEmptyState();
    } else if (v && this.PREVENT_BACK) {
      this.managerService.historyService.replaceEmptyState();
    }

    //
    this.PREVENT_BACK = v;

    //
    if (v) {
      this.addClass('x-back-prevented');
    } else {
      this.addClass('x-back-prevented');
    }

    //
    this.detectChanges();
    this.onBackPrevent(v);
    this.backPreventNotifier.emit({ id: this.uuid, state: v });
  }

  //
  onBackPrevent(state: boolean) {}
  //#endregion

  //
  public baseActionProvider = new Subject<TActionProviderModel>();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    public zone: NgZone,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public managerService: XManagerService,
    public changeDetector: ChangeDetectorRef,
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    @Inject(MAT_DIALOG_DATA) public data: any,
    public dialogRef: MatDialogRef<
      XBaseDialogComponent<TItem, TActionProviderModel, TConfig>
    >
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    this.preventBack = true;
  }

  public afterViewInit(): void {
    super.afterViewInit();

    //
    // Register Lock Notifier for Prevent Handling ...
    this.applyTakeUntilWrapper(this.lockNotifier.asObservable()).subscribe(
      (event) => {
        //
        if (isNullOrUndefined(event)) {
          return;
        }

        //
        this.preventBack = event.state;
        this.detectChanges();
      }
    );
  }
  //#endregion

  //
  //#region Task Handlers ...
  async onTaskStart(name: string, task: XTask<any>) {
    super.onTaskStart(name, task);

    //
    const isDialogTask = await this.isDialogTask(name);

    //
    if (isDialogTask) {
      //
      this.setLoadingState(true);
      await this.notifyTaskStartEvent(name);
    }
  }

  async onTaskFinished(name: string, task: XTask<any>) {
    super.onTaskFinished(name, task);

    //
    const isDialogTask = await this.isDialogTask(name);

    //
    if (isDialogTask) {
      //
      this.setLoadingState(false);
      await this.notifyTaskFinishedEvent(name);
    }
  }

  async onTaskFailed(name: string, task: XTask<any>) {
    super.onTaskFailed(name, task);

    //
    const isDialogTask = await this.isDialogTask(name);

    //
    if (isDialogTask) {
      //
      this.showError(task.error);
      this.setLoadingState(false);
      await this.notifyTaskFailedEvent(name);
    }
  }
  //#endregion

  //
  //#region Handlers ...
  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerBackHandler();
  }
  //#endregion

  //
  //#region UI Providers ...
  /**
   * retrieve map dialog config ...
   */
  public getData() {
    return this.getValue(this.data as TConfig);
  }

  /**
   * determines map dialog config exists or not ...
   */
  public hasData() {
    return !isNullOrUndefined(this.getData());
  }

  /**
   * determines dialog can select result or not ...
   */
  public dialogCanSelect() {
    return this.hasChild(this.selectedItem);
  }

  /**
   * retrieve dialog title ...
   */
  public get title() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config ? config.title : ''))
    );
  }

  /**
   * retrieve dialog showTollbar ...
   */
  public get showToolbar() {
    return this.getData().pipe(
      concatMap((config) =>
        !config || isNullOrUndefined(config.showToolbar)
          ? of(true)
          : this.getOptionalValue(config.showToolbar)
      )
    );
  }

  public get cssClass() {
    return this.getData().pipe(
      concatMap((config) => this.getArrayValue(config ? config.cssClass : []))
    );
  }

  public get progressBarColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.progressBarColor : XColor.Warning)
      ),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Warning))
    );
  }

  public get actionProvider() {
    return this.getData().pipe(
      map((config) =>
        config ? config.actionProvider : this.baseActionProvider
      ),
      map((actionProvider) =>
        !isNullOrUndefined(actionProvider)
          ? actionProvider
          : this.baseActionProvider
      )
    );
  }

  public get canMultipleSelect() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config ? config.canMultipleSelect : false)
      )
    );
  }

  //
  //#region SearchBar ...
  public get showSearchBar() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config ? config.showSearchBar : false)
      )
    );
  }

  public get searchBarColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.searchBarColor : XColor.Primary)
      ),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Primary))
    );
  }

  public get searchBarInputColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.searchBarInputColor : XColor.Primary)
      ),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Primary))
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleBack() {
    this.dialogRef.close();
  }

  async handleSelect() {
    //
    if (!this.dialogCanSelect()) {
      return;
    }

    //
    this.dialogRef.close({
      result: XDialogResult.Ok,
      payload: this.selectedItem,
    });
  }

  async handleDeselect() {
    //
    if (!this.dialogCanSelect()) {
      return;
    }

    //
    const canSelectMultipleItems = await this.getValueAsync(
      this.canMultipleSelect
    );
    if (canSelectMultipleItems) {
      await this.deselectAllListItems();
    } else {
      //
      const selectedItem = toArray(this.selectedItem)[0];
      if (!isNullOrUndefined(selectedItem)) {
        await this.deselectItemInList(selectedItem);
      }
    }

    //
    this.selectedItem = undefined;
    this.detectChanges();
  }
  //#endregion

  //
  //#region Abstract ...
  abstract getTaskNames(): Promise<Array<string>>;

  abstract deselectAllListItems(): Promise<void>;

  abstract deselectItemInList(item: TItem): Promise<void>;

  abstract handleSearchbarQuryChanged(query: string): Promise<void>;
  //#endregion

  //
  //#region Protected ...
  public async onBack() {}
  //#endregion

  //
  //#region Private ...
  private async isDialogTask(name: string) {
    //
    const pageTasks = await this.getTaskNames();
    const mTaskKey = getTaskName(name);
    const result = pageTasks.includes(mTaskKey);

    //
    return result;
  }

  private async notifyTaskStartEvent(name: string) {
    //
    if (isNullOrEmptyString(name)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getData());
    if (config && config.onTaskStart && isFunction(config.onTaskStart)) {
      await this.getValueAsync(config.onTaskStart(name));
    }
  }

  private async notifyTaskFinishedEvent(name: string) {
    //
    if (isNullOrEmptyString(name)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getData());
    if (config && config.onTaskFinished && isFunction(config.onTaskFinished)) {
      await this.getValueAsync(config.onTaskFinished(name));
    }
  }

  private async notifyTaskFailedEvent(name: string) {
    //
    if (isNullOrEmptyString(name)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getData());
    if (config && config.onTaskFailed && isFunction(config.onTaskFailed)) {
      await this.getValueAsync(config.onTaskFailed(name));
    }
  }

  private registerBackHandler() {
    //
    this.managerService.historyService.platformLocation.onPopState(() => {
      //
      if (this.PREVENT_BACK) {
        this.managerService.historyService.pushEmptyState();
      }
    });

    //
    this.applyTakeUntilWrapper(
      this.managerService.historyService.backHandler.asObservable()
    ).subscribe(async () => {
      await this.onBack();
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\classes\x-base.observable.tools.ts

import {
  toArray,
  hasChild,
  notValue,
  toPromise,
  toObservable,
  XStandardType,
  XOneOrManyType,
  getDefaultValue,
  isNullOrUndefined,
  XResourceIdentifier,
  isNullOrEmptyString,
} from 'x-framework-core';
import { concatMap, map } from 'rxjs/operators';
import { Observable, forkJoin, of } from 'rxjs';

export interface IXBaseObservableTools {
  //
  //#region Action ...
  hasChild: (items: XOneOrManyType<any>) => boolean;
  //#endregion

  //
  //#region Observables ...
  getValue: <T>(value: XStandardType<T>) => Observable<T>;

  getArrayValue: <T>(
    value: XStandardType<XOneOrManyType<T>>
  ) => Observable<T[]>;

  hasChildValue: <T>(
    value: XStandardType<XOneOrManyType<T>>
  ) => Observable<boolean>;

  countChildsValue: <T>(
    value: XStandardType<XOneOrManyType<T>>
  ) => Observable<number>;

  getResourceValue: (
    value: XStandardType<XResourceIdentifier>
  ) => Observable<string>;

  notEmptyValue: (value: XStandardType<string | any>) => Observable<boolean>;

  emptyValue: (value: XStandardType<string | any>) => Observable<boolean>;

  notValue: (value: XStandardType<boolean>) => Observable<boolean>;

  mustBeValue: (
    value: any,
    ...values: XStandardType<any>[]
  ) => Observable<boolean>;

  atLeastHasOneValue: (
    value: any,
    ...values: XStandardType<any>[]
  ) => Observable<boolean>;

  notNullOrUndefinedValue: (value: XStandardType<any>) => Observable<boolean>;

  nullOrUndefinedValue: (value: XStandardType<any>) => Observable<boolean>;

  getOptionalValue: <T>(value?: XStandardType<T>) => Observable<T>;

  getValues: <T>(values?: XOneOrManyType<XStandardType<T>>) => Observable<T[]>;
  //#endregion

  //
  //#region Async of Observables ...
  getValueAsync: <T>(value: XStandardType<T>) => Promise<T>;

  getArrayValueAsync: <T>(
    value: XStandardType<XOneOrManyType<T>>
  ) => Promise<T[]>;

  hasChildValueAsync: <T>(
    value: XStandardType<XOneOrManyType<T>>
  ) => Promise<boolean>;

  countChildsValueAsync: <T>(
    value: XStandardType<XOneOrManyType<T>>
  ) => Promise<number>;

  getResourceValueAsync: (
    value: XStandardType<XResourceIdentifier>
  ) => Promise<string>;

  notEmptyValueAsync: (value: XStandardType<string | any>) => Promise<boolean>;

  emptyValueAsync: (value: XStandardType<string | any>) => Promise<boolean>;

  notValueAsync: (value: XStandardType<boolean>) => Promise<boolean>;

  mustBeValueAsync: (
    value: any,
    ...values: XStandardType<any>[]
  ) => Promise<boolean>;

  atLeastHasOneValueAsync: (
    value: any,
    ...values: XStandardType<any>[]
  ) => Promise<boolean>;

  notNullOrUndefinedValueAsync: (value: XStandardType<any>) => Promise<boolean>;

  nullOrUndefinedValueAsync: (value: XStandardType<any>) => Promise<boolean>;

  getOptionalValueAsync: <T>(value?: XStandardType<T>) => Promise<T>;

  getValuesAsync: <T>(
    values?: XOneOrManyType<XStandardType<T>>
  ) => Promise<T[]>;
  //#endregion
}

export abstract class XBaseObservableTools implements IXBaseObservableTools {
  //
  //#region Actions ...
  //
  //#region Observable Actions ...
  /**
   * Check if items has childs or not ...
   *
   * @param items a list item ...
   */
  public hasChild(items: XOneOrManyType<any>) {
    return hasChild(items);
  }

  /**
   * convert an standard UI Value to Observable for consuming ...
   *
   * @param value the standard UI Value
   */
  public getValue<T>(value: XStandardType<T>) {
    return toObservable<T>(value);
  }

  /**
   * retrieve an standard type var in promise
   *
   * @param value an standard type var
   */
  public async getValueAsync<T>(value: XStandardType<T>) {
    //
    if (!value) {
      return null;
    }

    //
    return await toPromise(this.getValue(value));
  }

  /**
   * convert to array ...
   *
   * @param value an standard type one or many arg ...
   */
  public getArrayValue<T>(value: XStandardType<XOneOrManyType<T>>) {
    return this.getValue(value).pipe(map((valueType) => toArray(valueType)));
  }

  /**
   * convert to array ...
   *
   * @param value an standard type one or many arg ...
   */
  public async getArrayValueAsync<T>(value: XStandardType<XOneOrManyType<T>>) {
    return this.getValueAsync(value).then((valueType) => toArray(valueType));
  }

  /**
   * check a value has child or not ...
   *
   * @param value an standard one or many of type
   */
  public hasChildValue<T>(value: XStandardType<XOneOrManyType<T>>) {
    //
    return this.getValue(value).pipe(
      map((valueType) => hasChild(toArray(valueType)))
    );
  }

  /**
   * check a value has child or not ...
   *
   * @param value an standard one or many of type
   */
  public async hasChildValueAsync<T>(value: XStandardType<XOneOrManyType<T>>) {
    //
    const valueType = await this.getValueAsync(value);
    if (!valueType) {
      return false;
    }

    //
    return hasChild(toArray(valueType));
  }

  /**
   * count a value's child ...
   *
   * @param value an standard one or many of type
   */
  public countChildsValue<T>(value: XStandardType<XOneOrManyType<T>>) {
    return this.getValue(value).pipe(
      map((valueType) => toArray(valueType).length)
    );
  }

  /**
   * count a value's child ...
   *
   * @param value an standard one or many of type
   */
  public async countChildsValueAsync<T>(
    value: XStandardType<XOneOrManyType<T>>
  ) {
    //
    const valueType = await this.getValueAsync(value);
    if (!valueType) {
      return 0;
    }

    //
    return toArray(valueType).length;
  }

  /**
   * providing string resources ...
   *
   * @param value an standard one or many of type
   */
  public getResourceValue(value: XStandardType<XResourceIdentifier>) {
    //
    return !value
      ? of('')
      : this.getValue(value).pipe(
          map((val) => {
            return val.toString();
            // return this.resourceProvider(val) || (value as string);
          })
        );
  }

  /**
   * providing string resources ...
   *
   * @param value an standard one or many of type
   */
  public async getResourceValueAsync(
    value: XStandardType<XResourceIdentifier>
  ) {
    //
    if (!value) {
      return '';
    }

    //
    const val = await this.getValueAsync(value);
    return val.toString();
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of string
   */
  public notEmptyValue(value: XStandardType<string | any>) {
    return this.getValue(value).pipe(map((val) => !isNullOrEmptyString(val)));
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of string
   */
  public async notEmptyValueAsync(value: any) {
    return await toPromise(this.notEmptyValue(value));
  }

  /**
   * determines a given value has not content and empty
   *
   * @param value an Standard type of string
   */
  public emptyValue(value: XStandardType<string | any>) {
    return this.getValue(value).pipe(map((val) => isNullOrEmptyString(val)));
  }

  /**
   * determines a given value has not content and empty
   *
   * @param value an Standard type of string
   */
  public async emptyValueAsync(value: XStandardType<string | any>) {
    return await toPromise(this.emptyValue(value));
  }

  /**
   * apply not operator to a boolean ...
   */
  public notValue(value: XStandardType<boolean>) {
    return notValue(this.getValue(value));
  }

  /**
   * apply not operator to a boolean ...
   */
  public async notValueAsync(value: XStandardType<boolean>) {
    return await this.getValueAsync(this.notValue(value));
  }

  /**
   * check all input args be same as value ...
   *
   * @param value any value which going to check with all args to be same as them ...
   * @param values the input args which be checked by value ...
   */
  public mustBeValue(value: any, ...values: XStandardType<any>[]) {
    //
    return forkJoin(values.map((v) => toObservable(v))).pipe(
      map((res) => {
        return res.every((i) => i === value);
      })
    );
  }

  /**
   * check all input args be same as value ...
   *
   * @param value any value which going to check with all args to be same as them ...
   * @param values the input args which be checked by value ...
   */
  public async mustBeValueAsync(value: any, ...values: XStandardType<any>[]) {
    //
    return (
      await Promise.all(values.map(async (v) => await this.getValueAsync(v)))
    ).every((v) => v === value);
  }

  /**
   * check all input args contains at least one value ...
   *
   * @param value any value which going to check with all args to find one same ...
   * @param values the input args which be checked by value ...
   */
  public atLeastHasOneValue(value: any, ...values: XStandardType<any>[]) {
    //
    return forkJoin(values.map((v) => toObservable(v))).pipe(
      map((res) => {
        return res.includes(value);
      })
    );
  }

  /**
   * check all input args contains at least one value ...
   *
   * @param value any value which going to check with all args to find one same ...
   * @param values the input args which be checked by value ...
   */
  public async atLeastHasOneValueAsync(
    value: any,
    ...values: XStandardType<any>[]
  ) {
    //
    return (
      await Promise.all(values.map(async (v) => await this.getValueAsync(v)))
    ).includes(value);
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of any
   */
  public notNullOrUndefinedValue(value: XStandardType<any>) {
    return this.getValue(value).pipe(map((val) => !isNullOrUndefined(val)));
  }

  /**
   * determines a given value has content and not empty
   *
   * @param value an Standard type of any
   */
  public async notNullOrUndefinedValueAsync(value: XStandardType<any>) {
    return await toPromise(this.notNullOrUndefinedValue(value));
  }

  /**
   * determines a given value content must empty
   *
   * @param value an Standard type of any
   */
  public nullOrUndefinedValue(value: XStandardType<any>) {
    return this.getValue(value).pipe(map((val) => isNullOrUndefined(val)));
  }

  /**
   * determines a given value content must empty
   *
   * @param value an Standard type of any
   */
  public async nullOrUndefinedValueAsync(value: XStandardType<any>) {
    return await toPromise(this.nullOrUndefinedValue(value));
  }

  /**
   * retrieve an optional value ...
   *
   * @param value the optional standard value ...
   */
  public getOptionalValue<T>(value?: XStandardType<T>): Observable<T> {
    //
    if (value) {
      return this.getValue(value);
    }

    //
    return this.getValue(getDefaultValue(value));
  }

  /**
   * retrieve an optional value ...
   *
   * @param value the optional standard value ...
   */
  public async getOptionalValueAsync<T>(value?: XStandardType<T>) {
    return await toPromise(this.getOptionalValue(value));
  }

  /**
   * convert all values as observable of them ...
   *
   * @param values the standard list of specific type ...
   * @returns observable of values array of specific type ...
   */
  public getValues<T>(values?: XOneOrManyType<XStandardType<T>>) {
    //
    interface ForkSourceType<T> {
      [index: number]: XStandardType<T>;
    }

    //
    return this.getArrayValue(values).pipe(
      concatMap((values) => {
        //
        if (!hasChild(values)) {
          return this.getValue([] as T[]);
        } else {
          //
          const forkSource: ForkSourceType<T> = {};
          values.forEach((value, index) => {
            forkSource[index] = this.getValue(value);
          });

          //
          return forkJoin(forkSource).pipe(
            map((forkResult) => {
              //
              const respObj = forkResult as any;
              const mKeys = Object.keys(forkResult)
                .map((key) => key)
                .sort((a, b) => +a - +b);
              const result = mKeys.map((mKey: any) => respObj[mKey] as T);

              //
              return result;
            })
          );
        }
      })
    );
  }

  /**
   * convert all values as observable of them ...
   *
   * @param values the standard list of specific type ...
   * @returns observable of values array of specific type ...
   */
  public async getValuesAsync<T>(values?: XOneOrManyType<XStandardType<T>>) {
    return await toPromise(this.getValues(values));
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\component-templates.module.ts

import {
  XCardTestComponent,
  XComponentCardTestComponent,
} from './x-card.component.templates';
import {
  XListTestComponent,
  XComponentListTestComponent,
} from './x-list.component.templates';
import {
  XTabTestComponent,
  XTabsTestComponent,
} from './x-tabs.component.templates';
import {
  XEmptyTestComponent,
  XComponentEmptyTestComponent,
} from './x-empty.component.templates';
import {
  XSlideTestComponent,
  XSliderTestComponent,
} from './x-slider.component.templates';
import {
  XSlotTestComponent,
  XSlotterTestComponent,
} from './x-slotter.component.templates';
import {
  XStepTestComponent,
  XStepperTestComponent,
} from './x-stepper.component.templates';
import { NgModule } from '@angular/core';
import {
  XActionBarTestComponent,
  XComponentActionBarTestComponent,
} from './x-action-bar.component.templates';
import {
  XSearchBarTestComponent,
  XComponentSearchBarTestComponent,
} from './x-search-bar.component.templates';
import { CommonModule } from '@angular/common';
import {
  XDescriptionListTestComponent,
  XDescriptionListItemTestComponent,
} from './x-description-list.component.templates';
import { XMapModule } from '../x-map/x-map.module';
import { XFabModule } from '../x-fab/x-fab.module';
import { XIDEModule } from '../x-ide/x-ide.module';
import { XFormModule } from '../x-form/x-form.module';
import { XGridModule } from '../x-grid/x-grid.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XListModule } from '../x-list/x-list.module';
import { XPageModule } from '../x-page/x-page.module';
import { XTabsModule } from '../x-tabs/x-tabs.module';
import { XCardModule } from '../x-card/x-card.module';
import { XTableModule } from '../x-table/x-table.module';
import { XThemeModule } from '../x-theme/x-theme.module';
import { XColorModule } from '../x-color/x-color.module';
import { XEmptyModule } from '../x-empty/x-empty.module';
import { XChartModule } from '../x-chart/x-chart.module';
import { XSliderModule } from '../x-slider/x-slider.module';
import { XButtonModule } from '../x-button/x-button.module';
import { XIdeTestComponent } from './x-ide.component.template';
import { XCounterModule } from '../x-counter/x-counter.module';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XSpinnerModule } from '../x-spinner/x-spinner.module';
import { XStepperModule } from '../x-stepper/x-stepper.module';
import { XMapTestComponent } from './x-map.component.templates';
import { XFabTestComponent } from './x-fab.component.templates';
import { XPageTestComponent } from './x-page.component.templates';
import { XMarkdownModule } from '../x-markdown/x-markdown.module';
import { XFormTestComponent } from './x-form.component.templates';
import { XGridTestComponent } from './x-grid.component.templates';
import { XIconTestComponent } from './x-icon.component.templates';
import { XChartTestComponent } from './x-chart.component.template';
import { XTableTestComponent } from './x-table.component.templates';
import { XColorTestComponent } from './x-color.component.templates';
import { XThumbnailModule } from '../x-thumbnail/x-thumbnail.module';
import { XFooterTestComponent } from './x-footer.component.templates';
import { XButtonTestComponent } from './x-button.component.templates';
import { XSearchBarModule } from '../x-search-bar/x-search-bar.module';
import { XActionBarModule } from '../x-action-bar/x-action-bar.module';
import { XToolbarTestComponent } from './x-toolbar.component.templates';
import { XSpinnerTestComponent } from './x-spinner.component.templates';
import { XCounterTestComponent } from './x-counter.component.templates';
import { XDataPagerModule } from './../x-data-pager/x-data-pager.module';
import { XMarkdownTestComponent } from './x-markdown.component.templates';
import { XFileUploadModule } from '../x-file-upload/x-file-upload.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XThumbnailTestComponent } from './x-thumbnail.component.templates';
import { XNavigatorTestComponent } from './x-navigator.component.templates';
import { XDataPagerTestComponent } from './x-data-pager.component.templates';
import { XColorPickerModule } from '../x-color-picker/x-color-picker.module';
import { XFileUploadTestComponent } from './x-file-upload.component.templates';
import { XImageCropperModule } from '../x-image-cropper/x-image-cropper.module';
import { XAvatarUploadModule } from '../x-avatar-upload/x-avatar-upload.module';
import { XTabNavigatorModule } from '../x-tab-navigator/x-tab-navigator.module';
import { XDataScrollerModule } from '../x-data-scroller/x-data-scroller.module';
import { XColorPickerTestComponent } from './x-color-picker.component.templates';
import { XFileDropAreaModule } from '../x-file-drop-area/x-file-drop-area.module';
import { XTabNavigatorTestComponent } from './x-tab-navigator.component.templates';
import { XThemeManagerTestComponent } from './x-theme-manager.component.templates';
import { XAvatarUploadTestComponent } from './x-avatar-upload.component.templates';
import { XImageCropperTestComponent } from './x-image-cropper.component.templates';
import { XDataScrollerTestComponent } from './x-data-scroller.component.templates';
import { XFileDropAreaTestComponent } from './x-file-drop-area.component.templates';
import { XScreenCaptureTestComponent } from './x-screen-capture.component.templates';
import { XModelPresenterModule } from '../x-model-presenter/x-model-presenter.module';
import { XExpandableListModule } from '../x-expandable-list/x-expandable-list.module';
import { XExpandableListTestComponent } from './x-expandable-list.component.templates';
import { XModelPresenterTestComponent } from './x-model-presenter.component.templates';
import { XDescriptionListModule } from '../x-description-list/x-description-list.module';
import { XExpandablePanelModule } from '../x-expandable-panel/x-expandable-panel.module';
import { XExpandablePandelTestComponent } from './x-expandable-panel.component.templates';

@NgModule({
  declarations: [
    //
    // x-action-bar ...
    XActionBarTestComponent,
    XComponentActionBarTestComponent,
    //
    // x-avatar-upload ...
    XAvatarUploadTestComponent,
    //
    // x-button ...
    XButtonTestComponent,
    //
    // x-ide ...
    XIdeTestComponent,
    //
    // x-chart ...
    XChartTestComponent,
    //
    // x-card ...
    XCardTestComponent,
    XComponentCardTestComponent,
    //
    // x-color ...
    XColorTestComponent,
    //
    // x-color-picker ...
    XColorPickerTestComponent,
    //
    // x-counter ...
    XCounterTestComponent,
    //
    // x-data-pager ...
    XDataPagerTestComponent,
    //
    // x-data-scroller ...
    XDataScrollerTestComponent,
    //
    // x-description-list ...
    XDescriptionListTestComponent,
    XDescriptionListItemTestComponent,
    //
    // x-empty ...
    XEmptyTestComponent,
    XComponentEmptyTestComponent,
    //
    // x-expandable-list ...
    XExpandableListTestComponent,
    //
    // x-expandable-panel ...
    XExpandablePandelTestComponent,
    //
    // x-fab ...
    XFabTestComponent,
    //
    // x-file-drop-area ...
    XFileDropAreaTestComponent,
    //
    // x-file-upload ...
    XFileUploadTestComponent,
    //
    // x-form ...
    XFormTestComponent,
    //
    // x-grid ...
    XGridTestComponent,
    //
    // x-icon ...
    XIconTestComponent,
    //
    // x-image-cropper ...
    XImageCropperTestComponent,
    //
    // x-list ...
    XListTestComponent,
    XComponentListTestComponent,
    //
    // x-map ...
    XMapTestComponent,
    //
    // x-markdown ...
    XMarkdownTestComponent,
    //
    // x-model-presenter ...
    XModelPresenterTestComponent,
    //
    // x-page ...
    XPageTestComponent,
    //
    // x-footer ...
    XFooterTestComponent,
    //
    // x-navigator ...
    XNavigatorTestComponent,
    //
    // x-toolbar ...
    XToolbarTestComponent,
    //
    // x-screen-capture ...
    XScreenCaptureTestComponent,
    //
    // x-search-bar ...
    XSearchBarTestComponent,
    XComponentSearchBarTestComponent,
    //
    // x-slider ...
    XSlideTestComponent,
    XSliderTestComponent,
    //
    // x-slotter ...
    XSlotTestComponent,
    XSlotterTestComponent,
    //
    // x-spinner ...
    XSpinnerTestComponent,
    //
    // x-stepper ...
    XStepTestComponent,
    XStepperTestComponent,
    //
    // x-tab-navigator ...
    XTabNavigatorTestComponent,
    //
    // x-table ...
    XTableTestComponent,
    //
    // x-tabs ...
    XTabTestComponent,
    XTabsTestComponent,
    //
    // x-theme-manager ...
    XThemeManagerTestComponent,
    //
    // x-thumbnail ...
    XThumbnailTestComponent,
  ],
  imports: [
    //
    CommonModule,

    //
    XEmptyModule,
    XBaseComponentModule,

    //
    XIDEModule,
    XFabModule,
    XMapModule,
    XPageModule,
    XCardModule,
    XIconModule,
    XFormModule,
    XTabsModule,
    XTabsModule,
    XListModule,
    XGridModule,
    XChartModule,
    XColorModule,
    XTableModule,
    XThemeModule,
    XButtonModule,
    XSliderModule,
    XSpinnerModule,
    XStepperModule,
    XSlotterModule,
    XCounterModule,
    XMarkdownModule,
    XDataPagerModule,
    XSearchBarModule,
    XThumbnailModule,
    XActionBarModule,
    XFileUploadModule,
    XColorPickerModule,
    XImageCropperModule,
    XFileDropAreaModule,
    XDataScrollerModule,
    XTabNavigatorModule,
    XAvatarUploadModule,
    XExpandableListModule,
    XModelPresenterModule,
    XExpandablePanelModule,
    XDescriptionListModule,
  ],
  exports: [],
})
export class XComponentTemplatesModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-action-bar.component.templates.ts

import { Component } from '@angular/core';
import {
  XActionBarContainerComponent,
  XComponentActionBarContainerComponent,
} from '../x-action-bar/x-action-bar.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-action-bar
        [color]="props.color"
        [actions]="props.actions"
        [cssClass]="props.cssClass"
        (actionFired)="(props.actionFired)"
        [actionProvider]="props.actionProvider"
        [foregroundColor]="props.foregroundColor"
      >
      </x-action-bar>
    </ng-container>
  `,
})
export class XActionBarTestComponent extends XActionBarContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <ng-container *ngIf="props.showActionBar">
        <x-action-bar
          [color]="props.actionBarColor"
          [actions]="props.actionBarActions"
          [cssClass]="props.actionBarCssClass"
          (actionFired)="(props.actionBarActionFired)"
          [actionProvider]="props.actionBarActionProvider"
          [foregroundColor]="props.actionBarForegroundColor"
        ></x-action-bar>
      </ng-container>
    </ng-container>
  `,
})
export class XComponentActionBarTestComponent extends XComponentActionBarContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-avatar-upload.component.templates.ts

import { Component } from '@angular/core';
import { XAvatarUploadContainerComponent } from '../x-avatar-upload/x-avatar-upload.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-avatar-upload
        [color]="props.color"
        [actions]="props.actions"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [cardSubTitle]="props.cardSubTitle"
        [cardCssClass]="props.cardCssClass"
        [wrapWithCard]="props.wrapWithCard"
        (avatarChange)="(props.avatarChange)"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        [maxAllowedSize]="props.maxAllowedSize"
        [fileDropAreaCardForegroundColor]="
          props.fileDropAreaCardForegroundColor
        "
        [foregroundColor]="props.foregroundColor"
        [maxAllowedFiles]="props.maxAllowedFiles"
        [fileDropAreaSupportsMultipleFiles]="
          props.fileDropAreaSupportsMultipleFiles
        "
        [showFileDropArea]="props.showFileDropArea"
        [fileDropAreaIcon]="props.fileDropAreaIcon"
        [fileDropAreaColor]="props.fileDropAreaColor"
        [placeHolderAvatar]="props.placeHolderAvatar"
        [allowedExtensions]="props.allowedExtensions"
        [minAllowedFileSize]="props.minAllowedFileSize"
        [maxAllowedFileSize]="props.maxAllowedFileSize"
        [cardActionProvider]="props.cardActionProvider"
        [emptyFileException]="props.emptyFileException"
        [fileDropAreaMessage]="props.fileDropAreaMessage"
        [cardForegroundColor]="props.cardForegroundColor"
        [fileDropAreaIsInPage]="props.fileDropAreaIsInPage"
        [fileDropAreaCssClass]="props.fileDropAreaCssClass"
        [fileDropAreaCardColor]="props.fileDropAreaCardColor"
        [fileDropAreaCardTitle]="props.fileDropAreaCardTitle"
        [fileDropAreaErrorColor]="props.fileDropAreaErrorColor"
        [fileDropAreaAccentColor]="props.fileDropAreaAccentColor"
        [maxAllowedSizeException]="props.maxAllowedSizeException"
        [notAllowedFileException]="props.notAllowedFileException"
        [fileDropAreaSuccessColor]="props.fileDropAreaSuccessColor"
        [fileDropAreaWrapWithCard]="props.fileDropAreaWrapWithCard"
        [maxAllowedFilesException]="props.maxAllowedFilesException"
        [fileDropAreaDragDisabled]="props.fileDropAreaDragDisabled"
        [fileDropAreaCardSubTitle]="props.fileDropAreaCardSubTitle"
        [fileDropAreaCardCssClass]="props.fileDropAreaCardCssClass"
        [fileDropAreaCardShowFooter]="props.fileDropAreaCardShowFooter"
        [fileDropAreaActionProvider]="props.fileDropAreaActionProvider"
        [fileDropAreaContentTemplate]="props.fileDropAreaContentTemplate"
        [minAllowedFileSizeException]="props.minAllowedFileSizeException"
        [maxAllowedFileSizeException]="props.maxAllowedFileSizeException"
        [fileDropAreaForegroundColor]="props.fileDropAreaForegroundColor"
        [fileDropAreaDisableFileCheck]="props.fileDropAreaDisableFileCheck"
        [fileDropAreaAllowedExtensions]="props.fileDropAreaAllowedExtensions"
        (fileDropAreaFilesChangeEmiter)="(props.fileDropAreaFilesChangeEmiter)"
        [fileDropAreaCardActionProvider]="props.fileDropAreaCardActionProvider"
      >
      </x-avatar-upload>
    </ng-container>
  `,
})
export class XAvatarUploadTestComponent extends XAvatarUploadContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-button.component.templates.ts

import { Component } from '@angular/core';
import { XButtonContainerComponent } from '../x-button/x-button.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-button
        [type]="props.type"
        [icon]="props.icon"
        [title]="props.title"
        [color]="props.color"
        (clicked)="(props.clicked)"
        [cssClass]="props.cssClass"
        [iconColor]="props.iconColor"
        [iconPosition]="props.iconPosition"
        [actionProvider]="props.actionProvider"
        [foregroundColor]="props.foregroundColor"
      >
      </x-button>
    </ng-container>
  `,
})
export class XButtonTestComponent extends XButtonContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-card.component.templates.ts

import { Component } from '@angular/core';
import {
  XCardContainerComponent,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-card
        [title]="props.title"
        [color]="props.color"
        [isInPage]="props.isInPage"
        [cssClass]="props.cssClass"
        [imageUrl]="props.imageUrl"
        [subTitle]="props.subTitle"
        [avatarUrl]="props.avatarUrl"
        [showHeader]="props.showHeader"
        [showFooter]="props.showFooter"
        [showContent]="props.showContent"
        [showActions]="props.showActions"
        [actionProvider]="props.actionProvider"
        [foregroundColor]="props.foregroundColor"
        [imageDescription]="props.imageDescription"
      >
      </x-card>
    </ng-container>
  `,
})
export class XCardTestComponent extends XCardContainerComponent {}

@Component({
  template: `
    <!-- Standalone Template -->
    <ng-container *ngIf="extractProps() | async as props">
      <ng-container *ngIf="props.wrapWithCard">
        <x-card
          [title]="props.cardTitle"
          [color]="props.cardColor"
          [isInPage]="props.isInPage"
          [cssClass]="props.cardCssClass"
          [subTitle]="props.cardSubTitle"
          [showFooter]="props.cardShowFooter"
          [actionProvider]="props.cardActionProvider"
          [foregroundColor]="props.cardForegroundColor"
        >
        </x-card>
      </ng-container>
    </ng-container>

    <!-- Component Present Template -->
    <ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
      <ng-container *ngIf="extractProps() | async as props">
        <x-card
          class="x-card"
          [showActions]="true"
          [color]="props.cardColor"
          [ngClass]="props.cardCssClass"
          [isInPage]="props.isInPage"
          [showHeader]="
            (notEmptyValue(props.cardTitle) | async) ||
            (notEmptyValue(props.cardSubTitle) | async)
          "
          [subTitle]="getResourceValue(props.cardSubTitle)"
          [title]="getResourceValue(props.cardTitle) | async"
          [showFooter]="getValue(props.cardShowFooter) | async"
          [foregroundColor]="getValue(props.cardForegroundColor) | async"
        >
          <!-- Content -->
          <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

          <!-- Actions -->
          <div class="x-actions" [ngClass]="props.cardCssClass" actions>
            <ng-content select="[actions]"></ng-content>
          </div>

          <!-- Footer -->
          <div footer *ngIf="props.cardShowFooter">
            <ng-content select="[footer]"></ng-content>
          </div>
        </x-card>
      </ng-container>
    </ng-container>

    <!-- Component's itSelf Template -->
    <ng-template #componentsRef> </ng-template>
  `,
})
export class XComponentCardTestComponent extends XComponentCardContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-chart.component.template.ts

import { Component } from '@angular/core';
import { XChartContainerComponent } from '../x-chart/x-chart.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-chart
        [series]="props.series"
        [options]="props.options"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [autoUpdate]="props.autoUpdate"
        (zoomed)="props.zoomed($event)"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        [cardShowActions]="props.cardShowActions"
        (chartRendered)="props.chartRendered($event)"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        (dataPointSelected)="props.dataPointSelected($event)"
      >
      </x-chart>
    </ng-container>
  `,
})
export class XChartTestComponent extends XChartContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-color-picker.component.templates.ts

import { Component } from '@angular/core';
import { XColorPickerContainerComponent } from '../x-color-picker/x-color-picker.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-color-picker
        [color]="props.color"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardTitle]="props.cardTitle"
        [cardColor]="props.cardColor"
        [cursorColor]="props.cursorColor"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [presetColors]="props.presetColors"
        [outputFormat]="props.outputFormat"
        [selectedColor]="props.selectedColor"
        [colorTextColor]="props.colorTextColor"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        (colorSelected)="(props.colorSelected)"
        [showColorInput]="props.showColorInput"
        [showHueChannel]="props.showHueChannel"
        [showColorValue]="props.showColorValue"
        [foregroundColor]="props.foregroundColor"
        [showAlphaChannel]="props.showAlphaChannel"
        [showPresetColors]="props.showPresetColors"
        [presetColorsLable]="props.presetColorsLable"
        [showPaletteChannel]="props.showPaletteChannel"
        [cardForegroundColor]="props.cardForegroundColor"
        [colorInputIconColor]="props.colorInputIconColor"
        [clearColorIconColor]="props.clearColorIconColor"
        [colorInputIconLabel]="props.colorInputIconLabel"
        [clearColorIconLabel]="props.clearColorIconLabel"
        [showCopyToClipboard]="props.showCopyToClipboard"
        [representColorFormats]="props.representColorFormats"
        [presetColorsLableColor]="props.presetColorsLableColor"
        [copyToClipboardIconLabel]="props.copyToClipboardIconLabel"
        [showSelectedColorPreview]="props.showSelectedColorPreview"
        [copyToClipboardIconColor]="props.copyToClipboardIconColor"
      >
      </x-color-picker>
    </ng-container>
  `,
})
export class XColorPickerTestComponent extends XColorPickerContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-color.component.templates.ts

import { Component } from "@angular/core";
import { XColorContainerComponent } from "../x-color/x-color.typings";

@Component({
    template: `
        <ng-container *ngIf="extractProps() | async as props">
          <x-color
            [size]="props.size"
            [value]="props.value"
            [cssClass]="props.cssClass"
            [showLabel]="props.showLabel"
            [labelColor]="props.labelColor"
            [borderWidth]="props.borderWidth"
            [borderStyle]="props.borderStyle"
            [borderColor]="props.borderColor"
            [actionProvider]="props.actionProvider"
          >            
          </x-color>
        </ng-container> 
    `
})
export class XColorTestComponent extends XColorContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-counter.component.templates.ts


import { Component } from "@angular/core";
import { XCounterContainerComponent } from "../x-counter/x-counter.typings";

@Component({
    template: `
        <ng-container *ngIf="extractProps() | async as props">
        <x-counter
            [type]="props.type"
            [delay]="props.delay"
            [cssClass]="props.cssClass"
            [isInPage]="props.isInPage"
            [cardTitle]="props.cardTitle"
            [stopValue]="props.stopValue"
            [cardColor]="props.cardColor"
            [startValue]="props.startValue"
            [currentValue]="props.currentValue"
            (counterStop)="(props.counterStop)"
            [wrapWithCard]="props.wrapWithCard"
            [cardSubTitle]="props.cardSubTitle"
            (counterStart)="(props.counterStart)"
            (counterTtick)="(props.counterTtick)"
            [displayPattern]="props.displayPattern"
            [actionProvider]="props.actionProvider"
            [cardShowFooter]="props.cardShowFooter"
            [cardForegroundColor]="props.cardForegroundColor"
          >
          </x-counter>
        </ng-container> 
    `
})
export class XCounterTestComponent extends XCounterContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-data-pager.component.templates.ts

import { Component } from '@angular/core';
import { XDataPagerContainerComponent } from '../x-data-pager/x-data-pager.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-data-pager
        [type]="props.type"
        [task]="props.task"
        [query]="props.query"
        [result]="props.result"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardTitle]="props.cardTitle"
        [cardColor]="props.cardColor"
        [emptyColor]="props.emptyColor"
        (taskReady)="(props.taskReady)"
        (pageChanged)="(props.pageChanged)"
        (queryChange)="(props.queryChange)"
        [itemTemplate]="props.itemTemplate"
        [cardCssClass]="props.cardCssClass"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [emptyMessage]="props.emptyMessage"
        [emptyCssClass]="props.emptyCssClass"
        [showPaginator]="props.showPaginator"
        (emptyRefresh)="(props.emptyRefresh)"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        [paginatorColor]="props.paginatorColor"
        [emptyForegroundRefreshButtonColor]="
          props.emptyForegroundRefreshButtonColor
        "
        [emptyHasRefresh]="props.emptyHasRefresh"
        [emptyRefreshIcon]="props.emptyRefreshIcon"
        [paginatorOptions]="props.paginatorOptions"
        [emptyListTemplate]="props.emptyListTemplate"
        [paginatorPosition]="props.paginatorPosition"
        [emptyMessageColor]="props.emptyMessageColor"
        [cardActionProvider]="props.cardActionProvider"
        [emptyActionProvider]="props.emptyActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [emptyForegroundColor]="props.emptyForegroundColor"
        [emptyRefreshButtonColor]="props.emptyRefreshButtonColor"
        [emptyForegroundMessageColor]="props.emptyForegroundMessageColor"
      >
      </x-data-pager>
    </ng-container>
  `,
})
export class XDataPagerTestComponent extends XDataPagerContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-data-scroller.component.templates.ts

import { Component } from '@angular/core';
import { XDataScrollerContainerComponent } from '../x-data-scroller/x-data-scroller.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-data-scroller
        [task]="props.task"
        [query]="props.query"
        [result]="props.result"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardTitle]="props.cardTitle"
        [cardColor]="props.cardColor"
        [emptyColor]="props.emptyColor"
        (taskReady)="(props.taskReady)"
        [cardCssClass]="props.cardCssClass"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [emptyMessage]="props.emptyMessage"
        (emptyRefresh)="(props.emptyRefresh)"
        [emptyCssClass]="props.emptyCssClass"
        (queryChange)="(props.queryChange)"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        [emptyForegroundRefreshButtonColor]="
          props.emptyForegroundRefreshButtonColor
        "
        [emptyHasRefresh]="props.emptyHasRefresh"
        [emptyRefreshIcon]="props.emptyRefreshIcon"
        [emptyListTemplate]="props.emptyListTemplate"
        [emptyMessageColor]="props.emptyMessageColor"
        [cardActionProvider]="props.cardActionProvider"
        [emptyActionProvider]="props.emptyActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [emptyForegroundColor]="props.emptyForegroundColor"
        [emptyRefreshButtonColor]="props.emptyRefreshButtonColor"
        [emptyForegroundMessageColor]="props.emptyForegroundMessageColor"
      >
      </x-data-scroller>
    </ng-container>
  `,
})
export class XDataScrollerTestComponent extends XDataScrollerContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-description-list.component.templates.ts

import { Component } from '@angular/core';
import { XDescriptionListContainerComponent } from '../x-description-list/x-description-list.typings';
import { XDescriptionListItemContainerComponent } from '../x-description-list/x-description-list-item/x-description-list-item.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-description-list
        [color]="props.color"
        [model]="props.model"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [isLegacy]="props.isLegacy"
        [cardTitle]="props.cardTitle"
        [cardColor]="props.cardColor"
        [showLabels]="props.showLabels"
        [labelColor]="props.labelColor"
        [valueColor]="props.valueColor"
        [cardCssClass]="props.cardCssClass"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        (onModelChanged)="(props.onModelChanged)"
        [foregroundColor]="props.foregroundColor"
        [hiddenModelKeys]="props.hiddenModelKeys"
        [modelKeyProvider]="props.modelKeyProvider"
        [modelValueProvider]="props.modelValueProvider"
        [hideOptionalValues]="props.hideOptionalValues"
        [availableModelKeys]="props.availableModelKeys"
        [sortOrderModelKeys]="props.sortOrderModelKeys"
        [cardForegroundColor]="props.cardForegroundColor"
        [hiddenLabelModelKeys]="props.hiddenLabelModelKeys"
        [propertyActionTemplate]="props.propertyActionTemplate"
      >
      </x-description-list>
    </ng-container>
  `,
})
export class XDescriptionListTestComponent extends XDescriptionListContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-description-list-item
        [name]="props.name"
        [value]="props.value"
        [cssClass]="props.cssClass"
        [actionProvider]="props.actionProvider"
      >
      </x-description-list-item>
    </ng-container>
  `,
})
export class XDescriptionListItemTestComponent extends XDescriptionListItemContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-empty.component.templates.ts

import { Component } from '@angular/core';
import {
  XComponentEmptyContainerComponent,
  XEmptyContainerComponent,
} from '../x-empty/x-empty.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-empty
        [color]="props.color"
        [message]="props.message"
        (refresh)="(props.refresh)"
        [cssClass]="props.cssClass"
        [hasRefresh]="props.hasRefresh"
        [refreshIcon]="props.refreshIcon"
        [messageColor]="props.messageColor"
        [actionProvider]="props.actionProvider"
        [foregroundColor]="props.foregroundColor"
        [refreshButtonColor]="props.refreshButtonColor"
        [foregroundMessageColor]="props.foregroundMessageColor"
        [foregroundRefreshButtonColor]="props.foregroundRefreshButtonColor"
      >
      </x-empty>
    </ng-container>
  `,
})
export class XEmptyTestComponent extends XEmptyContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <ng-container *ngIf="props.showEmpty">
        <x-empty
          [color]="props.emptyColor"
          [message]="props.emptyMessage"
          (refresh)="(props.emptyRefresh)"
          [cssClass]="props.emptyCssClass"
          [hasRefresh]="props.emptyHasRefresh"
          [refreshIcon]="props.emptyRefreshIcon"
          [messageColor]="props.emptyMessageColor"
          [actionProvider]="props.emptyActionProvider"
          [foregroundColor]="props.emptyForegroundColor"
          [refreshButtonColor]="props.emptyRefreshButtonColor"
          [foregroundMessageColor]="props.emptyForegroundMessageColor"
          [foregroundRefreshButtonColor]="
            props.emptyForegroundRefreshButtonColor
          "
        >
        </x-empty>
      </ng-container>
    </ng-container>
  `,
})
export class XComponentEmptyTestComponent extends XComponentEmptyContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-expandable-list.component.templates.ts

import { Component } from '@angular/core';
import { XExpandableListContainerComponent } from '../x-expandable-list/x-expandable-list.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-expandable-list
        [items]="props.items"
        [isInPage]="props.isInPage"
        [cssClass]="props.cssClass"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [childColor]="props.childColor"
        [parentColor]="props.parentColor"
        [dividerColor]="props.dividerColor"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [itemCssClass]="props.itemCssClass"
        [startupState]="props.startupState"
        (itemSelected)="(props.itemSelected)"
        [closeSiblings]="props.closeSiblings"
        [childTemplate]="props.childTemplate"
        [highlightColor]="props.highlightColor"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        [supportRouting]="props.supportRouting"
        [parentTemplate]="props.parentTemplate"
        [childActiveColor]="props.childActiveColor"
        [parentActiveColor]="props.parentActiveColor"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [showChildDetailIcon]="props.showChildDetailIcon"
        [disableParentRouting]="props.disableParentRouting"
        [showParentDetailIcon]="props.showParentDetailIcon"
      >
      </x-expandable-list>
    </ng-container>
  `,
})
export class XExpandableListTestComponent extends XExpandableListContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-expandable-panel.component.templates.ts

import { Component } from '@angular/core';
import { XExpandablePandelContainerComponent } from '../x-expandable-panel/x-expandable-panel.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-expandable-panel
        [canNext]="props.canNext"
        (onNext)="(props.onNext)"
        [canPrev]="props.canPrev"
        (onPrev)="(props.onPrev)"
        [cssClass]="props.cssClass"
        [selected]="props.selected"
        [isInPage]="props.isInPage"
        [canReset]="props.canReset"
        (onReset)="(props.onReset)"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [isAccordion]="props.isAccordion"
        [applyColors]="props.applyColors"
        [headerColor]="props.headerColor"
        [startupState]="props.startupState"
        [cardSubTitle]="props.cardSubTitle"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [multiExpanded]="props.multiExpanded"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        [resetConfirmer]="props.resetConfirmer"
        (selectedChange)="(props.selectedChange)"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [headerHighlightColor]="props.headerHighlightColor"
        [hideDescriptionOnSmallScreens]="props.hideDescriptionOnSmallScreens"
      >
      </x-expandable-panel>
    </ng-container>
  `,
})
export class XExpandablePandelTestComponent extends XExpandablePandelContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-fab.component.templates.ts

import { Component } from '@angular/core';
import { XFabContainerComponent } from '../x-fab/x-fab.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-fab
        [item]="props.item"
        [cssClass]="props.cssClass"
        [actionProvider]="props.actionProvider"
        [childsCssClass]="props.childsCssClass"
      >
      </x-fab>
    </ng-container>
  `,
})
export class XFabTestComponent extends XFabContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-file-drop-area.component.templates.ts

import { Component } from '@angular/core';
import { XFileDropAreaContainerComponent } from '../x-file-drop-area/x-file-drop-area.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-file-drop-area
        [icon]="props.icon"
        [color]="props.color"
        [message]="props.message"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardTitle]="props.cardTitle"
        [cardColor]="props.cardColor"
        [errorColor]="props.errorColor"
        [accentColor]="props.accentColor"
        [dragDisabled]="props.dragDisabled"
        [successColor]="props.successColor"
        [cardCssClass]="props.cardCssClass"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        [foregroundColor]="props.foregroundColor"
        [contentTemplate]="props.contentTemplate"
        [foregroundColor]="props.foregroundColor"
        [disableFileCheck]="props.disableFileCheck"
        [allowedExtensions]="props.allowedExtensions"
        (filesChangeEmiter)="(props.filesChangeEmiter)"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [supportsMultipleFiles]="props.supportsMultipleFiles"
      ></x-file-drop-area>
    </ng-container>
  `,
})
export class XFileDropAreaTestComponent extends XFileDropAreaContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-file-upload.component.templates.ts

import { Component } from '@angular/core';
import { XFileUploadContainerComponent } from '../x-file-upload/x-file-upload.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-file-upload
        [color]="props.color"
        [isInPage]="props.isInPage"
        [showList]="props.showList"
        [cssClass]="props.cssClass"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [showEmpty]="props.showEmpty"
        [emptyColor]="props.emptyColor"
        (fileChange)="(props.fileChange)"
        [emptyMessage]="props.emptyMessage"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [cardCssClass]="props.cardCssClass"
        [listCssClass]="props.listCssClass"
        (emptyRefresh)="(props.emptyRefresh)"
        [emptyCssClass]="props.emptyCssClass"
        [searchBarType]="props.searchBarType"
        [showActionBar]="props.showActionBar"
        [showSearchBar]="props.showSearchBar"
        [searchBarTitle]="props.searchBarTitle"
        [searchBarValue]="props.searchBarValue"
        [emptyForegroundRefreshButtonColor]="
          props.emptyForegroundRefreshButtonColor
        "
        [maxAllowedSize]="props.maxAllowedSize"
        [cardShowFooter]="props.cardShowFooter"
        [actionBarColor]="props.actionBarColor"
        [searchBarColor]="props.searchBarColor"
        [actionProvider]="props.actionProvider"
        [maxAllowedFiles]="props.maxAllowedFiles"
        [emptyHasRefresh]="props.emptyHasRefresh"
        [listShowToolbar]="props.listShowToolbar"
        [listShowDivider]="props.listShowDivider"
        [foregroundColor]="props.foregroundColor"
        [fileDropAreaSupportsMultipleFiles]="
          props.fileDropAreaSupportsMultipleFiles
        "
        [actionBarActions]="props.actionBarActions"
        [showFileDropArea]="props.showFileDropArea"
        [fileDropAreaIcon]="props.fileDropAreaIcon"
        [emptyRefreshIcon]="props.emptyRefreshIcon"
        [searchBarForegroundSearchInputColor]="
          props.searchBarForegroundSearchInputColor
        "
        [allowedExtensions]="props.allowedExtensions"
        [emptyMessageColor]="props.emptyMessageColor"
        [searchBarDebounce]="props.searchBarDebounce"
        (searchBarOnEvent)="(props.searchBarOnEvent)"
        [listConfirmRemove]="props.listConfirmRemove"
        [listStickyToolbar]="props.listStickyToolbar"
        [actionBarCssClass]="props.actionBarCssClass"
        [searchBarCssClass]="props.searchBarCssClass"
        [fileDropAreaColor]="props.fileDropAreaColor"
        [emptyFileException]="props.emptyFileException"
        [minAllowedFileSize]="props.minAllowedFileSize"
        [maxAllowedFileSize]="props.maxAllowedFileSize"
        [searchBarClearIcon]="props.searchBarClearIcon"
        [cardActionProvider]="props.cardActionProvider"
        [listActionProvider]="props.listActionProvider"
        [emptyActionProvider]="props.emptyActionProvider"
        [searchBarSearchIcon]="props.searchBarSearchIcon"
        [searchBarSpellcheck]="props.searchBarSpellcheck"
        [cardForegroundColor]="props.cardForegroundColor"
        [fileDropAreaMessage]="props.fileDropAreaMessage"
        [emptyForegroundColor]="props.emptyForegroundColor"
        [listEnableReordering]="props.listEnableReordering"
        [listFilteringHandler]="props.listFilteringHandler"
        [fileDropAreaCssClass]="props.fileDropAreaCssClass"
        (searchBarQueryChange)="(props.searchBarQueryChange)"
        [searchBarEnterKeyHint]="props.searchBarEnterKeyHint"
        [searchBarKeyboardType]="props.searchBarKeyboardType"
        (actionBarActionFired)="(props.actionBarActionFired)"
        [fileDropAreaErrorColor]="props.fileDropAreaErrorColor"
        [emptyRefreshButtonColor]="props.emptyRefreshButtonColor"
        [notAllowedFileException]="props.notAllowedFileException"
        [maxAllowedSizeException]="props.maxAllowedSizeException"
        [searchBarClearIconColor]="props.searchBarClearIconColor"
        [actionBarActionProvider]="props.actionBarActionProvider"
        [searchBarActionProvider]="props.searchBarActionProvider"
        [fileDropAreaAccentColor]="props.fileDropAreaAccentColor"
        [maxAllowedFilesException]="props.maxAllowedFilesException"
        [searchBarSearchIconColor]="props.searchBarSearchIconColor"
        [actionBarForegroundColor]="props.actionBarForegroundColor"
        [searchBarForegroundColor]="props.searchBarForegroundColor"
        [fileDropAreaDragDisabled]="props.fileDropAreaDragDisabled"
        [fileDropAreaSuccessColor]="props.fileDropAreaSuccessColor"
        [searchBarShowCancelButton]="props.searchBarShowCancelButton"
        [searchBarCancelButtonIcon]="props.searchBarCancelButtonIcon"
        [searchBarSearchInputColor]="props.searchBarSearchInputColor"
        [searchBarCancelButtonTitle]="props.searchBarCancelButtonTitle"
        [fileDropAreaActionProvider]="props.fileDropAreaActionProvider"
        [emptyForegroundMessageColor]="props.emptyForegroundMessageColor"
        [minAllowedFileSizeException]="props.minAllowedFileSizeException"
        [maxAllowedFileSizeException]="props.maxAllowedFileSizeException"
        [fileDropAreaForegroundColor]="props.fileDropAreaForegroundColor"
        [fileDropAreaContentTemplate]="props.fileDropAreaContentTemplate"
        [fileDropAreaDisableFileCheck]="props.fileDropAreaDisableFileCheck"
        [fileDropAreaAllowedExtensions]="props.fileDropAreaAllowedExtensions"
        (fileDropAreaFilesChangeEmiter)="(props.fileDropAreaFilesChangeEmiter)"
      >
      </x-file-upload>
    </ng-container>
  `,
})
export class XFileUploadTestComponent extends XFileUploadContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-footer.component.templates.ts

import { Component } from '@angular/core';
import { XFooterContainerComponent } from '../x-page/x-footer/x-footer.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-footer
        [color]="props.color"
        [cssClass]="props.cssClass"
        [actionProvider]="props.actionProvider"
        [contentTemplate]="props.contentTemplate"
      >
      </x-footer>
    </ng-container>
  `,
})
export class XFooterTestComponent extends XFooterContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-form.component.templates.ts

import { Component } from '@angular/core';
import { XFormContainerComponent } from '../x-form/x-form.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-form
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [hintColor]="props.hintColor"
        [fillColor]="props.fillColor"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [formConfig]="props.formConfig"
        [validColor]="props.validColor"
        [accentColor]="props.accentColor"
        [activeColor]="props.activeColor"
        [regularColor]="props.regularColor"
        [invalidColor]="props.invalidColor"
        [pendingColor]="props.pendingColor"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [disabledColor]="props.disabledColor"
        (valueChanged)="(props.valueChanged)"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        (statusChanged)="(props.statusChanged)"
        (controlBlured)="(props.controlBlured)"
        (controlFocused)="(props.controlFocused)"
        [disabledFillColor]="props.disabledFillColor"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        (controlValueChanged)="(props.controlValueChanged)"
        [applyStatusOnControls]="props.applyStatusOnControls"
        (controlStatusChanged)="(props.controlStatusChanged)"
      >
      </x-form>
    </ng-container>
  `,
})
export class XFormTestComponent extends XFormContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-grid.component.templates.ts

import { Component } from '@angular/core';
import { XGridContainerComponent } from '../x-grid/x-grid.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-grid
        [gap]="props.gap"
        [cols]="props.cols"
        [type]="props.type"
        [align]="props.align"
        [items]="props.items"
        [layout]="props.layout"
        [options]="props.options"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [showEmpty]="props.showEmpty"
        [emptyColor]="props.emptyColor"
        [itemTemplate]="props.itemTemplate"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [emptyMessage]="props.emptyMessage"
        (itemSelected)="(props.itemSelected)"
        (emptyRefresh)="(props.emptyRefresh)"
        [emptyCssClass]="props.emptyCssClass"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        [emptyHasRefresh]="props.emptyHasRefresh"
        [selectableItems]="props.selectableItems"
        [emptyForegroundRefreshButtonColor]="
          props.emptyForegroundRefreshButtonColor
        "
        [emptyRefreshIcon]="props.emptyRefreshIcon"
        [emptyMessageColor]="props.emptyMessageColor"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [emptyActionProvider]="props.emptyActionProvider"
        [emptyForegroundColor]="props.emptyForegroundColor"
        [emptyRefreshButtonColor]="props.emptyRefreshButtonColor"
        [emptyForegroundMessageColor]="props.emptyForegroundMessageColor"
      ></x-grid>
    </ng-container>
  `,
})
export class XGridTestComponent extends XGridContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-icon.component.templates.ts

import { Component } from '@angular/core';
import { XIconContainerComponent } from '../x-icon/x-icon.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-icon
        [slot]="props.slot"
        [name]="props.name"
        [color]="props.color"
        (clicked)="(props.clicked)"
        [cssClass]="props.cssClass"
        [clickable]="props.clickable"
        [selectable]="props.selectable"
        [actionProvider]="props.actionProvider"
        [applyDirection]="props.applyDirection"
        [foregroundColor]="props.foregroundColor"
      >
      </x-icon>
    </ng-container>
  `,
})
export class XIconTestComponent extends XIconContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-ide.component.template.ts

import { Component } from '@angular/core';
import { XIDEContainerComponent } from '../x-ide/x-ide.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-ide
        [mode]="props.mode"
        [theme]="props.theme"
        [content]="props.content"
        [cssClass]="props.cssClass"
        [minLines]="props.minLines"
        [maxLines]="props.maxLines"
        [fontSize]="props.fontSize"
        [readonly]="props.readonly"
        [isInPage]="props.isInPage"
        [alignTabs]="props.alignTabs"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [cardSubTitle]="props.cardSubTitle"
        [wordWrapping]="props.wordWrapping"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [showActionBar]="props.showActionBar"
        [actionBarColor]="props.actionBarColor"
        [cardShowFooter]="props.cardShowFooter"
        [defaultTabName]="props.defaultTabName"
        [actionProvider]="props.actionProvider"
        [tabHeaderColor]="props.tabHeaderColor"
        [activeTabColor]="props.activeTabColor"
        [cardShowActions]="props.cardShowActions"
        [showLineNumbers]="props.showLineNumbers"
        [showPrintMargin]="props.showPrintMargin"
        [showFoldWidgets]="props.showFoldWidgets"
        [actionBarActions]="props.actionBarActions"
        [actionBarCssClass]="props.actionBarCssClass"
        (contentChange)="props.contentChange($event)"
        [cardActionProvider]="props.cardActionProvider"
        [highlightActiveLine]="props.highlightActiveLine"
        [cardForegroundColor]="props.cardForegroundColor"
        [basicAutocompletion]="props.basicAutocompletion"
        [hasCloseOnDefaultTab]="props.hasCloseOnDefaultTab"
        [availableActionBarItems]="props.availableActionBarItems"
        [actionBarActionProvider]="props.actionBarActionProvider"
        (actionBarActionFired)="props.actionBarActionFired($event)"
        [actionBarForegroundColor]="props.actionBarForegroundColor"
        [defaultActionBarItemColor]="props.defaultActionBarItemColor"
      >
      </x-ide>
    </ng-container>
  `,
})
export class XIdeTestComponent extends XIDEContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-image-cropper.component.templates.ts

import { Component } from '@angular/core';
import { XImageCropperContainerComponent } from '../x-image-cropper/x-image-cropper.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-image-cropper
        [format]="props.format"
        [cropper]="props.cropper"
        [autoCrop]="props.autoCrop"
        [cssClass]="props.cssClass"
        [alignImage]="props.alignImage"
        [outputType]="props.outputType"
        [imageBase64]="props.imageBase64"
        [aspectRatio]="props.aspectRatio"
        [imageQuality]="props.imageQuality"
        [roundCropper]="props.roundCropper"
        (imageLoaded)="(props.imageLoaded)"
        [onlyScaleDown]="props.onlyScaleDown"
        [resizeToWidth]="props.resizeToWidth"
        (imageCropped)="(props.imageCropped)"
        (cropperReady)="(props.cropperReady)"
        [actionProvider]="props.actionProvider"
        [cropperMinWidth]="props.cropperMinWidth"
        (startCropImage)="(props.startCropImage)"
        (loadImageFailed)="(props.loadImageFailed)"
        [imageFileChanged]="props.imageFileChanged"
        [cropperMinHeight]="props.cropperMinHeight"
        (imageCroppedFile)="(props.imageCroppedFile)"
        [imageChangedEvent]="props.imageChangedEvent"
        [maintainAspectRatio]="props.maintainAspectRatio"
        (imageCroppedBase64)="(props.imageCroppedBase64)"
      >
      </x-image-cropper>
    </ng-container>
  `,
})
export class XImageCropperTestComponent extends XImageCropperContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-list.component.templates.ts

import { Component } from '@angular/core';
import {
  XComponentListContainerComponent,
  XListContainerComponent,
} from '../x-list/x-list.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-list
        [items]="props.items"
        [color]="props.color"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [showEmpty]="props.showEmpty"
        [emptyColor]="props.emptyColor"
        [showToolbar]="props.showToolbar"
        [showDivider]="props.showDivider"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [wrapWithCard]="props.wrapWithCard"
        (removeItems)="(props.removeItems)"
        [itemTemplate]="props.itemTemplate"
        [emptyMessage]="props.emptyMessage"
        (itemsChanged)="(props.itemsChanged)"
        (orderChanged)="(props.orderChanged)"
        [emptyCssClass]="props.emptyCssClass"
        (itemSelected)="(props.itemSelected)"
        [confirmRemove]="props.confirmRemove"
        [stickyToolbar]="props.stickyToolbar"
        (emptyRefresh)="(props.emptyRefresh)"
        [showActionBar]="props.showActionBar"
        [showSearchBar]="props.showSearchBar"
        [searchBarType]="props.searchBarType"
        [searchBarTitle]="props.searchBarTitle"
        [searchBarValue]="props.searchBarValue"
        [actionProvider]="props.actionProvider"
        [clickableItems]="props.clickableItems"
        [cardShowFooter]="props.cardShowFooter"
        [actionBarColor]="props.actionBarColor"
        [searchBarColor]="props.searchBarColor"
        [emptyForegroundRefreshButtonColor]="
          props.emptyForegroundRefreshButtonColor
        "
        (optionSelected)="(props.optionSelected)"
        [showItemOptions]="props.showItemOptions"
        [emptyHasRefresh]="props.emptyHasRefresh"
        [selectableItems]="props.selectableItems"
        [foregroundColor]="props.foregroundColor"
        [searchBarForegroundSearchInputColor]="
          props.searchBarForegroundSearchInputColor
        "
        [filteringHandler]="props.filteringHandler"
        [enableReordering]="props.enableReordering"
        [actionBarActions]="props.actionBarActions"
        [emptyRefreshIcon]="props.emptyRefreshIcon"
        [showOptionsAsMenuWhenSlidingAvailable]="
          props.showOptionsAsMenuWhenSlidingAvailable
        "
        [searchBarDebounce]="props.searchBarDebounce"
        (searchBarOnEvent)="(props.searchBarOnEvent)"
        (selectionChanged)="(props.selectionChanged)"
        [emptyMessageColor]="props.emptyMessageColor"
        [actionBarCssClass]="props.actionBarCssClass"
        [searchBarCssClass]="props.searchBarCssClass"
        [searchBarClearIcon]="props.searchBarClearIcon"
        [showOptionsAsSlide]="props.showOptionsAsSlide"
        [cardActionProvider]="props.cardActionProvider"
        [searchBarSearchIcon]="props.searchBarSearchIcon"
        [searchBarSpellcheck]="props.searchBarSpellcheck"
        [cardForegroundColor]="props.cardForegroundColor"
        [emptyActionProvider]="props.emptyActionProvider"
        [emptyForegroundColor]="props.emptyForegroundColor"
        [searchBarEnterKeyHint]="props.searchBarEnterKeyHint"
        [searchBarKeyboardType]="props.searchBarKeyboardType"
        (searchBarQueryChange)="(props.searchBarQueryChange)"
        [slidingOptionTemplate]="props.slidingOptionTemplate"
        (actionBarActionFired)="(props.actionBarActionFired)"
        [searchBarClearIconColor]="props.searchBarClearIconColor"
        [closeOptionsAfterSelect]="props.closeOptionsAfterSelect"
        [actionBarActionProvider]="props.actionBarActionProvider"
        [searchBarActionProvider]="props.searchBarActionProvider"
        [emptyRefreshButtonColor]="props.emptyRefreshButtonColor"
        [searchBarSearchIconColor]="props.searchBarSearchIconColor"
        [nonSlidingOptionTemplate]="props.nonSlidingOptionTemplate"
        [actionBarForegroundColor]="props.actionBarForegroundColor"
        [searchBarForegroundColor]="props.searchBarForegroundColor"
        [searchBarShowCancelButton]="props.searchBarShowCancelButton"
        [searchBarCancelButtonIcon]="props.searchBarCancelButtonIcon"
        [searchBarSearchInputColor]="props.searchBarSearchInputColor"
        [searchBarCancelButtonTitle]="props.searchBarCancelButtonTitle"
        [emptyForegroundMessageColor]="props.emptyForegroundMessageColor"
      >
      </x-list>
    </ng-container>
  `,
})
export class XListTestComponent extends XListContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <ng-container *ngIf="props.showList">
        <x-list
          [items]="props.listItems"
          [color]="props.listColor"
          [cssClass]="props.listCssClass"
          [isInPage]="props.listIsInPage"
          [cardColor]="props.listCardColor"
          [cardTitle]="props.listCardTitle"
          [showEmpty]="props.listShowEmpty"
          [emptyColor]="props.listEmptyColor"
          [showToolbar]="props.listShowToolbar"
          [showDivider]="props.listShowDivider"
          [cardCssClass]="props.listCardCssClass"
          [cardSubTitle]="props.listCardSubTitle"
          [wrapWithCard]="props.listWrapWithCard"
          (removeItems)="(props.listRemoveItems)"
          [itemTemplate]="props.listItemTemplate"
          [emptyMessage]="props.listEmptyMessage"
          (itemsChanged)="(props.listItemsChanged)"
          (orderChanged)="(props.listOrderChanged)"
          [emptyCssClass]="props.listEmptyCssClass"
          (itemSelected)="(props.listItemSelected)"
          [confirmRemove]="props.listConfirmRemove"
          [stickyToolbar]="props.listStickyToolbar"
          (emptyRefresh)="(props.listEmptyRefresh)"
          [showActionBar]="props.listShowActionBar"
          [showSearchBar]="props.listShowSearchBar"
          [searchBarType]="props.listSearchBarType"
          [searchBarTitle]="props.listSearchBarTitle"
          [searchBarValue]="props.listSearchBarValue"
          [actionProvider]="props.listActionProvider"
          [clickableItems]="props.listClickableItems"
          [cardShowFooter]="props.listCardShowFooter"
          [actionBarColor]="props.listActionBarColor"
          [searchBarColor]="props.listSearchBarColor"
          [emptyForegroundRefreshButtonColor]="
            props.listEmptyForegroundRefreshButtonColor
          "
          (optionSelected)="(props.listOptionSelected)"
          [showItemOptions]="props.listShowItemOptions"
          [emptyHasRefresh]="props.listEmptyHasRefresh"
          [selectableItems]="props.listSelectableItems"
          [foregroundColor]="props.listForegroundColor"
          [searchBarForegroundSearchInputColor]="
            props.listSearchBarForegroundSearchInputColor
          "
          [filteringHandler]="props.listFilteringHandler"
          [enableReordering]="props.listEnableReordering"
          [actionBarActions]="props.listActionBarActions"
          [emptyRefreshIcon]="props.listEmptyRefreshIcon"
          [showOptionsAsMenuWhenSlidingAvailable]="
            props.listShowOptionsAsMenuWhenSlidingAvailable
          "
          [searchBarDebounce]="props.listSearchBarDebounce"
          (searchBarOnEvent)="(props.listSearchBarOnEvent)"
          (selectionChanged)="(props.listSelectionChanged)"
          [emptyMessageColor]="props.listEmptyMessageColor"
          [actionBarCssClass]="props.listActionBarCssClass"
          [searchBarCssClass]="props.listSearchBarCssClass"
          [searchBarClearIcon]="props.listSearchBarClearIcon"
          [showOptionsAsSlide]="props.listShowOptionsAsSlide"
          [cardActionProvider]="props.listCardActionProvider"
          [searchBarSearchIcon]="props.listSearchBarSearchIcon"
          [searchBarSpellcheck]="props.listSearchBarSpellcheck"
          [cardForegroundColor]="props.listCardForegroundColor"
          [emptyActionProvider]="props.listEmptyActionProvider"
          [emptyForegroundColor]="props.listEmptyForegroundColor"
          [searchBarEnterKeyHint]="props.listSearchBarEnterKeyHint"
          [searchBarKeyboardType]="props.listSearchBarKeyboardType"
          (searchBarQueryChange)="(props.listSearchBarQueryChange)"
          [slidingOptionTemplate]="props.listSlidingOptionTemplate"
          (actionBarActionFired)="(props.listActionBarActionFired)"
          [searchBarClearIconColor]="props.listSearchBarClearIconColor"
          [closeOptionsAfterSelect]="props.listCloseOptionsAfterSelect"
          [actionBarActionProvider]="props.listActionBarActionProvider"
          [searchBarActionProvider]="props.listSearchBarActionProvider"
          [emptyRefreshButtonColor]="props.listEmptyRefreshButtonColor"
          [searchBarSearchIconColor]="props.listSearchBarSearchIconColor"
          [nonSlidingOptionTemplate]="props.listNonSlidingOptionTemplate"
          [actionBarForegroundColor]="props.listActionBarForegroundColor"
          [searchBarForegroundColor]="props.listSearchBarForegroundColor"
          [searchBarShowCancelButton]="props.listSearchBarShowCancelButton"
          [searchBarCancelButtonIcon]="props.listSearchBarCancelButtonIcon"
          [searchBarSearchInputColor]="props.listSearchBarSearchInputColor"
          [searchBarCancelButtonTitle]="props.listSearchBarCancelButtonTitle"
          [emptyForegroundMessageColor]="props.listEmptyForegroundMessageColor"
        >
        </x-list>
      </ng-container>
    </ng-container>
  `,
})
export class XComponentListTestComponent extends XComponentListContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-map.component.templates.ts

import { Component } from '@angular/core';
import { XMapContainerComponent } from '../x-map/x-map.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-map
        [zoom]="props.zoom"
        [cssClass]="props.cssClass"
        (isReady)="(props.isReady)"
        [latitude]="props.latitude"
        [showZoom]="props.showZoom"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [longitude]="props.longitude"
        [canSelect]="props.canSelect"
        [showRotate]="props.showRotate"
        [showLocator]="props.showLocator"
        [markerStyle]="props.markerStyle"
        [wrapWithCard]="props.wrapWithCard"
        [cardSubTitle]="props.cardSubTitle"
        [cardCssClass]="props.cardCssClass"
        (viewChanged)="(props.viewChanged)"
        [searchBarType]="props.searchBarType"
        [showSearchBar]="props.showSearchBar"
        [searchBarTitle]="props.searchBarTitle"
        [searchBarValue]="props.searchBarValue"
        [cardShowFooter]="props.cardShowFooter"
        [showZoomSlider]="props.showZoomSlider"
        [searchBarColor]="props.searchBarColor"
        [actionProvider]="props.actionProvider"
        [showProgressBar]="props.showProgressBar"
        [showClearMarker]="props.showClearMarker"
        [locatorPosition]="props.locatorPosition"
        [progressBarColor]="props.progressBarColor"
        [searchBarPosition]="props.searchBarPosition"
        [searchBarCssClass]="props.searchBarCssClass"
        [searchBarDebounce]="props.searchBarDebounce"
        (searchBarOnEvent)="(props.searchBarOnEvent)"
        (locationSelected)="(props.locationSelected)"
        [showGoMarkedPlace]="props.showGoMarkedPlace"
        [cardActionProvider]="props.cardActionProvider"
        [searchBarClearIcon]="props.searchBarClearIcon"
        [searchPlaceProvider]="props.searchPlaceProvider"
        [searchBarSearchIcon]="props.searchBarSearchIcon"
        [cardForegroundColor]="props.cardForegroundColor"
        [searchBarSpellcheck]="props.searchBarSpellcheck"
        [clearMarkerPosition]="props.clearMarkerPosition"
        [progressBarPosition]="props.progressBarPosition"
        [showSelectedLocation]="props.showSelectedLocation"
        [searchResultListColor]="props.searchResultListColor"
        (searchBarQueryChange)="(props.searchBarQueryChange)"
        [searchBarEnterKeyHint]="props.searchBarEnterKeyHint"
        [searchBarKeyboardType]="props.searchBarKeyboardType"
        [goMarkedPlacePosition]="props.goMarkedPlacePosition"
        [searchBarActionProvider]="props.searchBarActionProvider"
        [searchBarClearIconColor]="props.searchBarClearIconColor"
        [searchBarForegroundColor]="props.searchBarForegroundColor"
        [searchBarSearchIconColor]="props.searchBarSearchIconColor"
        [selectedLocationPosition]="props.selectedLocationPosition"
        [searchBarSearchInputColor]="props.searchBarSearchInputColor"
        [searchBarShowCancelButton]="props.searchBarShowCancelButton"
        [searchBarCancelButtonIcon]="props.searchBarCancelButtonIcon"
        [searchBarCancelButtonTitle]="props.searchBarCancelButtonTitle"
        [addMarkerOnSelectedLocation]="props.addMarkerOnSelectedLocation"
        [searchBarForegroundSearchInputColor]="
          props.searchBarForegroundSearchInputColor
        "
      >
      </x-map>
    </ng-container>
  `,
})
export class XMapTestComponent extends XMapContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-markdown.component.templates.ts

import { Component } from '@angular/core';
import { XMarkdownContainerComponent } from '../x-markdown/x-markdown.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-markdown
        [mode]="props.mode"
        [content]="props.content"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [hasToolbar]="props.hasToolbar"
        [toolbarColor]="props.toolbarColor"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [toolbarConfig]="props.toolbarConfig"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        (contentChange)="(props.contentChange)"
        [editorBoxColor]="props.editorBoxColor"
        [toolbarCssClass]="props.toolbarCssClass"
        [previewBoxColor]="props.previewBoxColor"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [toolbarForegroundColor]="props.toolbarForegroundColor"
      ></x-markdown>
    </ng-container>
  `,
})
export class XMarkdownTestComponent extends XMarkdownContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-model-presenter.component.templates.ts

import { Component } from '@angular/core';
import { XModelPresenterContainerComponent } from '../x-model-presenter/x-model-presenter.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-model-presenter
        [color]="props.color"
        [model]="props.model"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [labelColor]="props.labelColor"
        [valueColor]="props.valueColor"
        [showLabels]="props.showLabels"
        [headerColor]="props.headerColor"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardSubTitle]="props.cardSubTitle"
        [contentColor]="props.contentColor"
        [startupState]="props.startupState"
        [panelCssClass]="props.panelCssClass"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        [foregroundColor]="props.foregroundColor"
        [hiddenModelKeys]="props.hiddenModelKeys"
        (onModelChanged)="(props.onModelChanged)"
        [modelKeyProvider]="props.modelKeyProvider"
        [sortOrderModelKeys]="props.sortOrderModelKeys"
        [modelValueProvider]="props.modelValueProvider"
        [hideOptionalValues]="props.hideOptionalValues"
        [availableModelKeys]="props.availableModelKeys"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        [panelActionProvider]="props.panelActionProvider"
        [hiddenLabelModelKeys]="props.hiddenLabelModelKeys"
        [headerHighlightColor]="props.headerHighlightColor"
        [propertyActionTemplate]="props.propertyActionTemplate"
      >
      </x-model-presenter>
    </ng-container>
  `,
})
export class XModelPresenterTestComponent extends XModelPresenterContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-navigator.component.templates.ts

import { Component } from '@angular/core';
import { XNavigatorContainerComponent } from '../x-page/x-navigator/x-navigator.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-navigator
        [items]="props.items"
        [cssClass]="props.cssClass"
        [iconSlot]="props.iconSlot"
        [childColor]="props.childColor"
        [parentColor]="props.parentColor"
        [dividerColor]="props.dividerColor"
        [startupState]="props.startupState"
        [itemCssClass]="props.itemCssClass"
        (itemSelected)="(props.itemSelected)"
        [closeSiblings]="props.closeSiblings"
        [showChildIcon]="props.showChildIcon"
        [actionProvider]="props.actionProvider"
        [showParentIcon]="props.showParentIcon"
        [highlightColor]="props.highlightColor"
        [childActiveColor]="props.childActiveColor"
        [parentActiveColor]="props.parentActiveColor"
        [showChildDetailIcon]="props.showChildDetailIcon"
        [disableParentRouting]="props.disableParentRouting"
        [showParentDetailIcon]="props.showParentDetailIcon"
      >
      </x-navigator>
    </ng-container>
  `,
})
export class XNavigatorTestComponent extends XNavigatorContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-page.component.templates.ts


import { Component } from "@angular/core";
import { XPageContainerComponent } from "../x-page/x-page.container";

@Component({
    template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-page
        [color]="props.color"
        [hasSide]="props.hasSide"
        [actions]="props.actions"
        [cssClass]="props.cssClass"
        [sideSlot]="props.sideSlot"
        [sideType]="props.sideType"
        [sideColor]="props.sideColor"
        (loadMore)="(props.loadMore)"
        [showFooter]="props.showFooter"
        [showToolbar]="props.showToolbar"
        [footerColor]="props.footerColor"
        [toolbarTitle]="props.toolbarTitle"
        (refreshPage)="(props.refreshPage)"
        [toolbarColor]="props.toolbarColor"
        [sideMenuWidth]="props.sideMenuWidth"
        [toolbarLogoUrl]="props.toolbarLogoUrl"
        [toolbarHasBack]="props.toolbarHasBack"
        [toolbarHasMenu]="props.toolbarHasMenu"
        [footerCssClass]="props.footerCssClass"
        [refreshingText]="props.refreshingText"
        (scrolledEvent)="(props.scrolledEvent)"
        [toggleMenuWhen]="props.toggleMenuWhen"
        (imageCaptured)="(props.imageCaptured)"
        [toolbarStartSlotButtonsTemplate]="
          props.toolbarStartSlotButtonsTemplate
        "
        [toolbarShowBack]="props.toolbarShowBack"
        [toolbarBackSlot]="props.toolbarBackSlot"
        [toolbarSubTitle]="props.toolbarSubTitle"
        [toolbarShowLogo]="props.toolbarShowLogo"
        [toolbarLogoSlot]="props.toolbarLogoSlot"
        [toolbarShowTitle]="props.toolbarShowTitle"
        [toolbarTitleSlot]="props.toolbarTitleSlot"
        [toolbarShowMenu]="props.toolbarShowMenu"
        [toolbarMenuSlot]="props.toolbarMenuSlot"
        [toolbarCssClass]="props.toolbarCssClass"
        [scrollBehaviour]="props.scrollBehaviour"
        [sideMenuMaxWidth]="props.sideMenuMaxWidth"
        [scrollingClasses]="props.scrollingClasses"
        [sideMenuMinWidth]="props.sideMenuMinWidth"
        [scrollEndClasses]="props.scrollEndClasses"
        [captureTopOffset]="props.captureTopOffset"
        [showPageRefresher]="props.showPageRefresher"
        [refreshingSpinner]="props.refreshingSpinner"
        [enableCaptureMode]="props.enableCaptureMode"
        [toolbarBackHandler]="props.toolbarBackHandler"
        [toolbarDefaultHref]="props.toolbarDefaultHref"
        [refreshPullingText]="props.refreshPullingText"
        [refreshPullingIcon]="props.refreshPullingIcon"
        [toolbarContentSlot]="props.toolbarContentSlot"
        [scrollStartClasses]="props.scrollStartClasses"
        [toolbarShowSubTitle]="props.toolbarShowSubTitle"
        [captureBottomOffset]="props.captureBottomOffset"
        [provideScrollEvents]="props.provideScrollEvents"
        [footerActionProvider]="props.footerActionProvider"
        [footerContentTemplate]="props.footerContentTemplate"
        [downloadCapturedImage]="props.downloadCapturedImage"
        [actionsActionProvider]="props.actionsActionProvider"
        [toolbarActionProvider]="props.toolbarActionProvider"
        [toolbarContentTemplate]="props.toolbarContentTemplate"
        [infinityScrollDisabled]="props.infinityScrollDisabled"
        [toolbarShowProgressBar]="props.toolbarShowProgressBar"
        [toolbarProgressBarColor]="props.toolbarProgressBarColor"
        [infinityScrollThreshold]="props.infinityScrollThreshold"
        [showPageInfinityScroller]="props.showPageInfinityScroller"
        [scrollEventActionProvider]="props.scrollEventActionProvider"
        [infinityScrollerLoadingText]="props.infinityScrollerLoadingText"
        [toolbarEndSlotButtonsTemplate]="props.toolbarEndSlotButtonsTemplate"
        [infinityScrollerLoadingSpinner]="props.infinityScrollerLoadingSpinner"
      >
      </x-page>
    </ng-container>
    `
})
export class XPageTestComponent extends XPageContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-screen-capture.component.templates.ts

import { Component } from '@angular/core';
import { XScreenCaptureContainerComponent } from '../x-screen-capture/x-screen-capture.container';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-screen-capture
        [enable]="props.enable"
        [topOffset]="props.topOffset"
        [bottomOffset]="props.bottomOffset"
        (imageCaptured)="(props.imageCaptured)"
        [downloadCapturedImage]="props.downloadCapturedImage"
      >
      </x-screen-capture>
    </ng-container>
  `,
})
export class XScreenCaptureTestComponent extends XScreenCaptureContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-search-bar.component.templates.ts

import { Component } from '@angular/core';
import {
  XComponentSearchBarContainerComponent,
  XSearchBarContainerComponent,
} from '../x-search-bar/x-search-bar.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-search-bar
        [type]="props.type"
        [color]="props.color"
        [title]="props.title"
        [value]="props.value"
        [cssClass]="props.cssClass"
        (onEvent)="(props.onEvent)"
        [debounce]="props.debounce"
        [clearIcon]="props.clearIcon"
        [spellcheck]="props.spellcheck"
        [searchIcon]="props.searchIcon"
        [enterKeyHint]="props.enterKeyHint"
        [keyboardType]="props.keyboardType"
        (queryChange)="(props.queryChange)"
        [clearIconColor]="props.clearIconColor"
        [actionProvider]="props.actionProvider"
        [searchIconColor]="props.searchIconColor"
        [foregroundColor]="props.foregroundColor"
        [showCancelButton]="props.showCancelButton"
        [cancelButtonIcon]="props.cancelButtonIcon"
        [searchInputColor]="props.searchInputColor"
        [cancelButtonTitle]="props.cancelButtonTitle"
        [foregroundSearchInputColor]="props.foregroundSearchInputColor"
      >
      </x-search-bar>
    </ng-container>
  `,
})
export class XSearchBarTestComponent extends XSearchBarContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <ng-container *ngIf="props.showSearchBar">
        <x-search-bar
          [color]="props.searchBarColor"
          [title]="props.searchBarTitle"
          [value]="props.searchBarValue"
          [cssClass]="props.searchBarCssClass"
          [debounce]="props.searchBarDebounce"
          (onEvent)="(props.searchBarOnEvent)"
          [clearIcon]="props.searchBarClearIcon"
          [spellcheck]="props.searchBarSpellcheck"
          [searchIcon]="props.searchBarSearchIcon"
          [foregroundSearchInputColor]="
            props.searchBarForegroundSearchInputColor
          "
          [type]="props.searchBarType"
          [enterKeyHint]="props.searchBarEnterKeyHint"
          [keyboardType]="props.searchBarKeyboardType"
          (queryChange)="(props.searchBarQueryChange)"
          [actionProvider]="props.searchBarActionProvider"
          [clearIconColor]="props.searchBarClearIconColor"
          [searchIconColor]="props.searchBarSearchIconColor"
          [foregroundColor]="props.searchBarForegroundColor"
          [searchInputColor]="props.searchBarSearchInputColor"
          [showCancelButton]="props.searchBarShowCancelButton"
          [cancelButtonIcon]="props.searchBarCancelButtonIcon"
          [cancelButtonTitle]="props.searchBarCancelButtonTitle"
        >
        </x-search-bar>
      </ng-container>
    </ng-container>
  `,
})
export class XComponentSearchBarTestComponent extends XComponentSearchBarContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-slider.component.templates.ts

import { Component } from '@angular/core';
import { XSliderContainerComponent } from '../x-slider/x-slider.typings';
import { XSlideContainerComponent } from '../x-slider/x-slide/x-slide.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-slider
        [options]="props.options"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [animation]="props.animation"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [cardSubTitle]="props.cardSubTitle"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [cardShowFooter]="props.cardShowFooter"
        [actionProvider]="props.actionProvider"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
      >
      </x-slider>
    </ng-container>
  `,
})
export class XSliderTestComponent extends XSliderContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-slide [actionProvider]="props.actionProvider"></x-slide>
    </ng-container>
  `,
})
export class XSlideTestComponent extends XSlideContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-slotter.component.templates.ts

import { Component } from '@angular/core';
import { XSlotterContainerComponent } from '../x-slotter/x-slotter.typings';
import { XSlotContainerComponent } from '../x-slotter/x-slot/x-slot.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-slotter
        [layout]="props.layout"
        [cssClass]="props.cssClass"
        [hasCenterSlot]="props.hasCenterSlot"
        [actionProvider]="props.actionProvider"
        [hasStartOrTopSlot]="props.hasStartOrTopSlot"
        [centerSlotCssClass]="props.centerSlotCssClass"
        [hasEndOrBottomSlot]="props.hasEndOrBottomSlot"
        [startTopSlotCssClass]="props.startTopSlotCssClass"
        [endBottomSlotCssClass]="props.endBottomSlotCssClass"
      >
      </x-slotter>
    </ng-container>
  `,
})
export class XSlotterTestComponent extends XSlotterContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-slot
        [name]="props.name"
        [cssClass]="props.cssClass"
        [actionProvider]="props.actionProvider"
      >
      </x-slot>
    </ng-container>
  `,
})
export class XSlotTestComponent extends XSlotContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-spinner.component.templates.ts

import { Component } from '@angular/core';
import { XSpinnerContainerComponent } from '../x-spinner/x-spinner.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-spinner
        [name]="props.name"
        [color]="props.color"
        [paused]="props.paused"
        [cssClass]="props.cssClass"
        [duration]="props.duration"
        [actionProvider]="props.actionProvider"
      >
      </x-spinner>
    </ng-container>
  `,
})
export class XSpinnerTestComponent extends XSpinnerContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-stepper.component.templates.ts

import { Component } from '@angular/core';
import { XStepperContainerComponent } from '../x-stepper/x-stepper.typings';
import { XStepContainerComponent } from '../x-stepper/x-step/x-step.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-stepper
        [type]="props.type"
        [hasNext]="props.hasNext"
        [hasPrev]="props.hasPrev"
        [canNext]="props.canNext"
        (onNext)="(props.onNext)"
        [canPrev]="props.canPrev"
        (onPrev)="(props.onPrev)"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [canReset]="props.canReset"
        (onReset)="(props.onReset)"
        [hasReset]="props.hasReset"
        [cardColor]="props.cardColor"
        [showLabel]="props.showLabel"
        [cardTitle]="props.cardTitle"
        [nextButton]="props.nextButton"
        [prevButton]="props.prevButton"
        [disablePrev]="props.disablePrev"
        [resetButton]="props.resetButton"
        [disableNext]="props.disableNext"
        [prevPosition]="props.prevPosition"
        [prevCssClass]="props.prevCssClass"
        [disableReset]="props.disableReset"
        [cardSubTitle]="props.cardSubTitle"
        [cardCssClass]="props.cardCssClass"
        [wrapWithCard]="props.wrapWithCard"
        [nextPosition]="props.nextPosition"
        [nextCssClass]="props.nextCssClass"
        [nextConfirmer]="props.nextConfirmer"
        [labelPosition]="props.labelPosition"
        [selectedIndex]="props.selectedIndex"
        [prevConfirmer]="props.prevConfirmer"
        [resetPosition]="props.resetPosition"
        [resetCssClass]="props.resetCssClass"
        [actionProvider]="props.actionProvider"
        [cardShowFooter]="props.cardShowFooter"
        [resetConfirmer]="props.resetConfirmer"
        (selectedChange)="(props.selectedChange)"
        [cardActionProvider]="props.cardActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
      >
      </x-stepper>
    </ng-container>
  `,
})
export class XStepperTestComponent extends XStepperContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-step
        [id]="props.id"
        [hasNext]="props.hasNext"
        [hasPrev]="props.hasPrev"
        [hasReset]="props.hasReset"
        [nextButton]="props.nextButton"
        [prevButton]="props.prevButton"
        [disableNext]="props.disableNext"
        [disablePrev]="props.disablePrev"
        [resetButton]="props.resetButton"
        [nextCssClass]="props.nextCssClass"
        [prevPosition]="props.prevPosition"
        [prevCssClass]="props.prevCssClass"
        [disableReset]="props.disableReset"
        [nextPosition]="props.nextPosition"
        [prevConfirmer]="props.prevConfirmer"
        [resetPosition]="props.resetPosition"
        [nextConfirmer]="props.nextConfirmer"
        [resetCssClass]="props.resetCssClass"
        [resetConfirmer]="props.resetConfirmer"
        [actionProvider]="props.actionProvider"
        [hasCustomActions]="props.hasCustomActions"
      >
      </x-step>
    </ng-container>
  `,
})
export class XStepTestComponent extends XStepContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-tab-navigator.component.templates.ts

import { Component } from '@angular/core';
import { XTabNavigatorContainerComponent } from '../x-tab-navigator/x-tab-navigator.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-tab-navigator
        [items]="props.items"
        [color]="props.color"
        [cssClass]="props.cssClass"
        [iconColor]="props.iconColor"
        [badgeColor]="props.badgeColor"
        [navigatorSlot]="props.navigatorSlot"
        [selectedColor]="props.selectedColor"
        [actionProvider]="props.actionProvider"
        [highlightColor]="props.highlightColor"
        [foregroundColor]="props.foregroundColor"
        [hideTabLabelsOnSmallScreens]="props.hideTabLabelsOnSmallScreens"
      >
      </x-tab-navigator>
    </ng-container>
  `,
})
export class XTabNavigatorTestComponent extends XTabNavigatorContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-table.component.templates.ts

import { Component } from '@angular/core';
import { XTableContainerComponent } from '../x-table/x-table.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-table
        [color]="props.color"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [showEmpty]="props.showEmpty"
        [emptyColor]="props.emptyColor"
        [properties]="props.properties"
        [showHeader]="props.showHeader"
        (rowClicked)="(props.rowClicked)"
        [headerColor]="props.headerColor"
        [searchTitle]="props.searchTitle"
        (rowSelected)="(props.rowSelected)"
        [canClearSort]="props.canClearSort"
        [cellCssClass]="props.cellCssClass"
        [stickyHeader]="props.stickyHeader"
        [cellTemplate]="props.cellTemplate"
        (pageChanged)="(props.pageChanged)"
        [cardSubTitle]="props.cardSubTitle"
        (removeItems)="(props.removeItems)"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        [emptyMessage]="props.emptyMessage"
        (emptyRefresh)="(props.emptyRefresh)"
        [clickableRows]="props.clickableRows"
        [showSearchBar]="props.showSearchBar"
        [showActionBar]="props.showActionBar"
        [showPaginator]="props.showPaginator"
        [actionOptions]="props.actionOptions"
        [emptyCssClass]="props.emptyCssClass"
        [searchBarColor]="props.searchBarColor"
        [cardShowFooter]="props.cardShowFooter"
        [paginatorColor]="props.paginatorColor"
        [actionBarColor]="props.actionBarColor"
        [selectableRows]="props.selectableRows"
        [actionProvider]="props.actionProvider"
        [emptyHasRefresh]="props.emptyHasRefresh"
        [foregroundColor]="props.foregroundColor"
        [emptyForegroundRefreshButtonColor]="
          props.emptyForegroundRefreshButtonColor
        "
        [emptyRefreshIcon]="props.emptyRefreshIcon"
        [paginatorOptions]="props.paginatorOptions"
        [allowMultiSelect]="props.allowMultiSelect"
        [searchBarPosition]="props.searchBarPosition"
        [paginatorPosition]="props.paginatorPosition"
        [actionBarPosition]="props.actionBarPosition"
        [emptyMessageColor]="props.emptyMessageColor"
        [cardActionProvider]="props.cardActionProvider"
        [headerCellCssClass]="props.headerCellCssClass"
        [customCellTemplate]="props.customCellTemplate"
        [emptyActionProvider]="props.emptyActionProvider"
        [cardForegroundColor]="props.cardForegroundColor"
        (rowSelectionChanged)="(props.rowSelectionChanged)"
        [emptyForegroundColor]="props.emptyForegroundColor"
        [headerForegroundColor]="props.headerForegroundColor"
        [initialSelectedValues]="props.initialSelectedValues"
        [emptyRefreshButtonColor]="props.emptyRefreshButtonColor"
        [emptyForegroundMessageColor]="props.emptyForegroundMessageColor"
      >
      </x-table>
    </ng-container>
  `,
})
export class XTableTestComponent extends XTableContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-tabs.component.templates.ts

import { Component } from '@angular/core';
import { XTabsContainerComponent } from '../x-tabs/x-tabs.typings';
import { XTabContainerComponent } from '../x-tabs/x-tab/x-tab.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-tabs
        [lock]="props.lock"
        [color]="props.color"
        [cssClass]="props.cssClass"
        [loopTabs]="props.loopTabs"
        [swipable]="props.swipable"
        [alignTabs]="props.alignTabs"
        [showHeader]="props.showHeader"
        [headerColor]="props.headerColor"
        [selectedIndex]="props.selectedIndex"
        [dynamicHeight]="props.dynamicHeight"
        [disableRipple]="props.disableRipple"
        [activeTabColor]="props.activeTabColor"
        [headerPosition]="props.headerPosition"
        [actionProvider]="props.actionProvider"
        (animationDone)="(props.animationDone)"
        [disablePagination]="props.disablePagination"
        [animationDuration]="props.animationDuration"
        [tabIndicatorColor]="props.tabIndicatorColor"
        [tabPaginatorColor]="props.tabPaginatorColor"
        (selectedTabChange)="(props.selectedTabChange)"
        (selectedIndexChange)="(props.selectedIndexChange)"
      >
      </x-tabs>
    </ng-container>
  `,
})
export class XTabsTestComponent extends XTabsContainerComponent {}

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-tab [actionProvider]="props.actionProvider"></x-tab>
    </ng-container>
  `,
})
export class XTabTestComponent extends XTabContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-theme-manager.component.templates.ts

import { Component } from '@angular/core';
import { XThemeManagerContainerComponent } from '../x-theme/x-theme-manager/x-theme-manager.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-theme-manager
        [color]="props.color"
        [cssClass]="props.cssClass"
        [isInPage]="props.isInPage"
        [cardColor]="props.cardColor"
        [cardTitle]="props.cardTitle"
        [saveColor]="props.saveColor"
        [saveLabel]="props.saveLabel"
        [resetColor]="props.resetColor"
        [resetLabel]="props.resetLabel"
        (tabChange)="(props.tabChange)"
        [cardSubTitle]="props.cardSubTitle"
        [wrapWithCard]="props.wrapWithCard"
        [cardCssClass]="props.cardCssClass"
        (themeChange)="(props.themeChange)"
        [listCssClass]="props.listCssClass"
        [listShowEmpty]="props.listShowEmpty"
        [showActionBar]="props.showActionBar"
        [themeUploadFileDropAreaCssClass]="
          props.themeUploadFileDropAreaCssClass
        "
        [listEmptyForegroundMessageColor]="
          props.listEmptyForegroundMessageColor
        "
        [cardShowFooter]="props.cardShowFooter"
        [maxAllowedSize]="props.maxAllowedSize"
        [listEmptyColor]="props.listEmptyColor"
        [actionBarColor]="props.actionBarColor"
        [actionProvider]="props.actionProvider"
        [themeUploadFileDropAreaErrorColor]="
          props.themeUploadFileDropAreaErrorColor
        "
        [maxAllowedFiles]="props.maxAllowedFiles"
        [listShowDivider]="props.listShowDivider"
        [themeUploadFileDropAreaAccentColor]="
          props.themeUploadFileDropAreaAccentColor
        "
        [themeUploadFileDropAreaSuccessColor]="
          props.themeUploadFileDropAreaSuccessColor
        "
        [propertyEditIcon]="props.propertyEditIcon"
        [listEmptyMessage]="props.listEmptyMessage"
        [themeUploadColor]="props.themeUploadColor"
        [listEmptyForegroundRefreshButtonColor]="
          props.listEmptyForegroundRefreshButtonColor
        "
        [listShowSearchBar]="props.listShowSearchBar"
        (listEmptyRefresh)="(props.listEmptyRefresh)"
        [listEmptyCssClass]="props.listEmptyCssClass"
        [actionBarCssClass]="props.actionBarCssClass"
        [propertyClearIcon]="props.propertyClearIcon"
        [themeUploadFileDropAreaForegroundColor]="
          props.themeUploadFileDropAreaForegroundColor
        "
        [themeUploadEmptyForegroundMessageColor]="
          props.themeUploadEmptyForegroundMessageColor
        "
        [listSearchBarForegroundSearchInputColor]="
          props.listSearchBarForegroundSearchInputColor
        "
        [minAllowedFileSize]="props.minAllowedFileSize"
        [maxAllowedFileSize]="props.maxAllowedFileSize"
        [cardActionProvider]="props.cardActionProvider"
        [listSearchBarTitle]="props.listSearchBarTitle"
        [listSearchBarValue]="props.listSearchBarValue"
        [listSearchBarColor]="props.listSearchBarColor"
        [propertyLabelColor]="props.propertyLabelColor"
        [propertyValueColor]="props.propertyValueColor"
        [cardForegroundColor]="props.cardForegroundColor"
        [listEmptyHasRefresh]="props.listEmptyHasRefresh"
        [themeUploadCssClass]="props.themeUploadCssClass"
        [listEmptyRefreshIcon]="props.listEmptyRefreshIcon"
        [colorViewBorderColor]="props.colorViewBorderColor"
        [restartAfterSetTheme]="props.restartAfterSetTheme"
        [colorViewBorderWidth]="props.colorViewBorderWidth"
        [colorViewBorderStyle]="props.colorViewBorderStyle"
        [presenterHeaderColor]="props.presenterHeaderColor"
        [themeUploadFileDropAreaSupportsMultipleFiles]="
          props.themeUploadFileDropAreaSupportsMultipleFiles
        "
        [listSearchBarCssClass]="props.listSearchBarCssClass"
        (listSearchBarOnEvent)="(props.listSearchBarOnEvent)"
        [listEmptyMessageColor]="props.listEmptyMessageColor"
        [listSearchBarDebounce]="props.listSearchBarDebounce"
        [presenterContentColor]="props.presenterContentColor"
        [propertyEditIconColor]="props.propertyEditIconColor"
        [listSearchBarClearIcon]="props.listSearchBarClearIcon"
        [propertyActionMenuIcon]="props.propertyActionMenuIcon"
        [propertyClearIconColor]="props.propertyClearIconColor"
        [themeUploadEmptyMessage]="props.themeUploadEmptyMessage"
        [listSearchBarSpellcheck]="props.listSearchBarSpellcheck"
        [listSearchBarSearchIcon]="props.listSearchBarSearchIcon"
        [listEmptyActionProvider]="props.listEmptyActionProvider"
        [listEmptyForegroundColor]="props.listEmptyForegroundColor"
        [actionBarForegroundColor]="props.actionBarForegroundColor"
        [listSearchBarEnterKeyHint]="props.listSearchBarEnterKeyHint"
        [listSearchBarKeyboardType]="props.listSearchBarKeyboardType"
        (listActionBarActionFired)="(props.listActionBarActionFired)"
        (listSearchBarQueryChange)="(props.listSearchBarQueryChange)"
        [themeUploadForegroundColor]="props.themeUploadForegroundColor"
        [emptyPropertyEditIconColor]="props.emptyPropertyEditIconColor"
        [themeUploadFileDropAreaIcon]="props.themeUploadFileDropAreaIcon"
        [themeUploadShowFileDropArea]="props.themeUploadShowFileDropArea"
        [listSearchBarClearIconColor]="props.listSearchBarClearIconColor"
        [listEmptyRefreshButtonColor]="props.listEmptyRefreshButtonColor"
        [propertyActionMenuIconColor]="props.propertyActionMenuIconColor"
        [themeUploadFileDropAreaColor]="props.themeUploadFileDropAreaColor"
        [listSearchBarSearchIconColor]="props.listSearchBarSearchIconColor"
        [listSearchBarForegroundColor]="props.listSearchBarForegroundColor"
        [listSearchBarShowCancelButton]="props.listSearchBarShowCancelButton"
        [listSearchBarCancelButtonIcon]="props.listSearchBarCancelButtonIcon"
        [listSearchBarSearchInputColor]="props.listSearchBarSearchInputColor"
        [presenterHeaderHighlightColor]="props.presenterHeaderHighlightColor"
        [themeUploadFileDropAreaMessage]="props.themeUploadFileDropAreaMessage"
        [listSearchBarCancelButtonTitle]="props.listSearchBarCancelButtonTitle"
      ></x-theme-manager>
    </ng-container>
  `,
})
export class XThemeManagerTestComponent extends XThemeManagerContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-thumbnail.component.templates.ts

import { Component } from '@angular/core';
import { XThumbnailContainerComponent } from '../x-thumbnail/x-thumbnail.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-thumbnail
        [type]="props.type"
        (clicked)="(props.clicked)"
        [cssClass]="props.cssClass"
        [circular]="props.circular"
        [clickable]="props.clickable"
        [selectable]="props.selectable"
        [identifier]="props.identifier"
        [actionProvider]="props.actionProvider"
      >
      </x-thumbnail>
    </ng-container>
  `,
})
export class XThumbnailTestComponent extends XThumbnailContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\component-templates\x-toolbar.component.templates.ts

import { Component } from '@angular/core';
import { XToolbarContainerComponent } from '../x-page/x-toolbar/x-toolbar.typings';

@Component({
  template: `
    <ng-container *ngIf="extractProps() | async as props">
      <x-toolbar
        [color]="props.color"
        [title]="props.title"
        [logoUrl]="props.logoUrl"
        [hasBack]="props.hasBack"
        [hasMenu]="props.hasMenu"
        [subTitle]="props.subTitle"
        [showLogo]="props.showLogo"
        [logoSlot]="props.logoSlot"
        [showBack]="props.showBack"
        [backSlot]="props.backSlot"
        [showMenu]="props.showMenu"
        [menuSlot]="props.menuSlot"
        [cssClass]="props.cssClass"
        [showTitle]="props.showTitle"
        [titleSlot]="props.titleSlot"
        [backHandler]="props.backHandler"
        [defaultHref]="props.defaultHref"
        [contentSlot]="props.contentSlot"
        [showSubTitle]="props.showSubTitle"
        [actionProvider]="props.actionProvider"
        [contentTemplate]="props.contentTemplate"
        [showProgressBar]="props.showProgressBar"
        [progressBarColor]="props.progressBarColor"
        [endSlotButtonsTemplate]="props.endSlotButtonsTemplate"
        [startSlotButtonsTemplate]="props.startSlotButtonsTemplate"
      >
      </x-toolbar>
    </ng-container>
  `,
})
export class XToolbarTestComponent extends XToolbarContainerComponent {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\config\x-framework-components.config.ts

import { XFrameworkCoreConfig } from 'x-framework-core';
import { XFrameworkServicesConfig } from 'x-framework-services';

export type XSharedConfig = XFrameworkCoreConfig & XFrameworkServicesConfig;

// tslint:disable-next-line:no-empty-interface
export interface XFrameworkComponentsConfig extends XSharedConfig {
  //
  // File Config ...
  maxAllowedSize: number;
  maxUploadFiles: number;
  minAllowedFileSize: number;
  maxAllowedFileSize: number;

  //
  defaultSearchDebounceTime: number;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-dialog.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XListModule } from '../x-list/x-list.module';
import { XPopoverService } from './x-popover/x-popover.service';
import { XSlotterModule } from './../x-slotter/x-slotter.module';
import { XPopoverComponent } from './x-popover/x-popover.component';
import { XSearchBarModule } from '../x-search-bar/x-search-bar.module';
import { XDataPagerModule } from '../x-data-pager/x-data-pager.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XSelectorDialogService } from './x-selector-dialog/x-selector-dialog.service';
import { XSelectorDialogComponent } from './x-selector-dialog/x-selector-dialog.component';

@NgModule({
  imports: [
    XListModule,
    CommonModule,
    XSlotterModule,
    XSearchBarModule,
    XDataPagerModule,
    XBaseComponentModule,
  ],
  exports: [
    XListModule,
    CommonModule,
    XSlotterModule,
    XSearchBarModule,
    XDataPagerModule,
    XBaseComponentModule,
  ],
  providers: [XPopoverService, XSelectorDialogService],
  declarations: [XPopoverComponent, XSelectorDialogComponent],
  entryComponents: [XPopoverComponent, XSelectorDialogComponent],
})
export class XDialogsModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-dialog\x-dialog.component.html

<section
  #xBaseDialogContainer
  class="x-base-dialog"
  [isForeground]="false"
  [xColor]="getBackgroundColor() | async"
  [ngClass]="getDialogContainerClasses() | async"
>
  <!-- Title Toolbar -->
  <section #xBaseDialogToolbar>
    <!-- Toolbar -->
    <ion-toolbar
      [xColor]="getToolbarColor() | async"
      [ngClass]="getDialogToolbarClasses() | async"
    >
      <!-- Slots -->
      <div class="x-base-dialog-toolbar-area">
        <x-slotter>
          <!-- Start -->
          <x-slot
            [name]="SlotNames.START"
            [cssClass]="['ion-text-start', 'ion-float-start']"
          >
            <div
              matRipple
              (click)="handleClosePopup()"
              class="x-base-dialog-toolbar-item x-base-dialog-toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="backIcon"
                [loading]="isLoading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>

          <!-- Center -->
          <x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
            <ion-title class="x-base-dialog-toolbar-item center-vertical-flex">
              <h3>
                {{ getTitle() | async }}
              </h3>
            </ion-title>
          </x-slot>

          <!-- End -->
          <x-slot
            [name]="SlotNames.END"
            [cssClass]="['ion-text-end', 'ion-float-end']"
          >
            <div
              matRipple
              *ngIf="canSelect() | async"
              (click)="handleSelect(selectedValue)"
              [matRippleDisabled]="!(hasSelectedValue() | async)"
              class="x-base-dialog-toolbar-item x-base-dialog-toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="selectIcon"
                [loading]="isLoading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>
        </x-slotter>
      </div>
    </ion-toolbar>

    <!-- Progressbar -->
    <ion-progress-bar
      type="indeterminate"
      *ngIf="canShowProgressBar() | async"
      [color]="getProgressBarColor() | async"
    ></ion-progress-bar>
  </section>

  <!-- Components -->
  <section class="x-base-dialog-content">
    <!-- Content -->
  </section>
</section>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-dialog\x-dialog.component.scss

x-icon {
  --size: 24px;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 !important;
}

.x-base-dialog-container {
  height: 100%;
}

.x-base-dialog-content {
  height: 100%;
  overflow-y: scroll;
}

.x-base-dialog-toolbar-area {
  margin: 8px;
}

.x-base-dialog-toolbar-item {
  width: auto;
  height: 48px;
}

.x-base-dialog-toolbar-button {
  width: 32px;
  margin: {
    left: 4px;
    right: 4px;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-dialog\x-dialog.component.ts

import {
  XDialogConfig,
  XDialogContext,
  defaultBaseDialogConfig,
} from './x-dialog.typings';
import { concatMap, map } from 'rxjs/operators';
import { Input, Component, ViewChild, ElementRef } from '@angular/core';
import { XIconNames } from '../../x-icon/x-icon.typings';
import { XSlotName } from '../../x-slotter/x-slotter.typings';
import { XBaseComponent } from '../../classes/x-base-component';
import { XStandardType, isNullOrUndefined } from 'x-framework-core';

@Component({
  template: '',
})
export abstract class XDialogComponent extends XBaseComponent {
  //
  //#region Props ...
  //
  //#region Private/Readonly ...
  readonly SlotNames = Object.assign({}, XSlotName);
  //#endregion

  //
  //#region Self Props ...
  //
  @Input()
  context: XStandardType<XDialogConfig>;
  //#endregion

  //
  //#region ViewChilds ...
  @ViewChild('xBaseDialogToolbar')
  dialogToolbar: ElementRef<any>;
  //#endregion

  //
  selectedValue: any;
  applyThemeColors = true;
  selectIcon = XIconNames.checked;
  backIcon = XIconNames.arrow_back;
  //#endregion

  //
  //#region Constructor ...
  //#endregion

  //
  //#region LifeCycles ...
  //#endregion

  //
  //#region Register Handlers ...
  //#endregion

  //
  //#region Task Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  //
  //#region Context Options UI Providers ...
  /**
   * retrieve id of dialog ...
   *
   * @returns Observable of Id ...
   */
  getId() {
    return this.getData().pipe(map((config) => config.id));
  }

  /**
   * retrieve map dialog config ...
   */
  getData() {
    return this.getValue(this.context);
  }

  /**
   * retrieve dialog title ...
   */
  getTitle() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.title))
    );
  }

  /**
   * retrieve dialog can Select values or not ...
   *
   * @returns observable of boolean ...
   */
  canSelect() {
    return this.getData().pipe(map((config) => !!config.canSelect));
  }

  /**
   * retrieve payload of data for dialog ...
   *
   * @returns observable of any ...
   */
  getPayload() {
    return this.getData().pipe(map((config) => config.payload));
  }

  /**
   * retrieve toolbar color ...
   *
   * @returns observable of XColorIdentifier ...
   */
  getToolbarColor() {
    return this.getData().pipe(
      map(
        (config) => config.toolbarColor || defaultBaseDialogConfig.toolbarColor
      )
    );
  }

  /**
   * retrieve background color ...
   *
   * @returns observable of XColorIdentifier ...
   */
  getBackgroundColor() {
    return this.getData().pipe(
      map(
        (config) =>
          config.backgroundColor || defaultBaseDialogConfig.backgroundColor
      )
    );
  }

  /**
   * retrieve progressBar color ...
   *
   * @returns observable of XColorIdentifier ...
   */
  getProgressBarColor() {
    return this.getData().pipe(
      map(
        (config) =>
          config.progressBarColor || defaultBaseDialogConfig.progressBarColor
      )
    );
  }
  //#endregion

  //
  //#region Class Providers ...
  getDialogToolbarClasses() {
    return this.getValue(this.getId()).pipe(
      map((id) => {
        return ['x-base-dialog-toolbar', `x-base-dialog-${id}-toolbar`];
      })
    );
  }

  getDialogContainerClasses() {
    return this.getValue(this.getId()).pipe(
      map((id) => {
        return ['x-base-dialog-container', `x-base-dialog-${id}-container`];
      })
    );
  }
  //#endregion

  hasSelectedValue() {
    return this.notNullOrUndefinedValue(this.selectedValue);
  }

  canShowProgressBar() {
    return this.getValue(this.loading || this.uiDisabled);
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleClosePopup(payload: any = undefined) {
    //
    const id = await this.getValueAsync(this.getId());
    const context = await this.getValueAsync(this.context);
    if (isNullOrUndefined(context)) {
      return;
    }

    //
    switch (context.type) {
      //
      case XDialogContext.Modal:
        await this.managerService.dialogService.modalController.dismiss(
          payload,
          undefined,
          id
        );
        break;

      //
      case XDialogContext.PopOver:
        await this.managerService.dialogService.popoverController.dismiss(
          payload,
          undefined,
          id
        );
        break;
    }
  }

  async handleSelect(payload: any) {
    //
    if (isNullOrUndefined(payload)) {
      return;
    }

    //
    const hasSelectedValue = await this.getValueAsync(this.hasSelectedValue());
    if (!hasSelectedValue) {
      return;
    }

    //
    await this.handleClosePopup(payload);
  }
  //#endregion

  //
  //#region Actions ...
  //#endregion

  //
  //#region Abstracts ...

  //#endregion

  //
  //#region Private ...
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-dialog\x-dialog.typings.ts

import { XColorIdentifier, XColorWithBrightness } from 'x-framework-core';

export type XDialogConfigKeys = 'title' | string;

export type XDialogConfigType = {
  [key in XDialogConfigKeys]: any;
};

export enum XDialogContext {
  Modal = 'modal',
  PopOver = 'pop_over',
}

export interface XDialogConfig extends XDialogConfigType {
  id: string;
  title: string;
  payload?: any;
  canSelect?: boolean;
  type: XDialogContext;
  toolbarColor?: XColorIdentifier;
  backgroundColor?: XColorIdentifier;
  progressBarColor?: XColorIdentifier;
}

export const defaultBaseDialogConfig: Partial<XDialogConfig> = {
  //
  toolbarColor: XColorWithBrightness.Primary,
  backgroundColor: XColorWithBrightness.Light,
  progressBarColor: XColorWithBrightness.Warning,
};

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-popover\x-popover.component.html

<mat-card class="x-card mobile x-popover-wrapper">
  <ion-list #list class="ion-no-padding">
    <!-- Options -->
    <ng-container
      *ngIf="
        (hasChildValue(options) | async) && !(hasChildValue(items) | async)
      "
    >
      <ng-container *ngFor="let item of getArrayValue(options) | async">
        <ng-container
          *ngTemplateOutlet="
            listItemRef;
            context: { $implicit: item, isItem: false }
          "
        ></ng-container>
      </ng-container>
    </ng-container>

    <!-- Items -->
    <ng-container
      *ngIf="
        !(hasChildValue(options) | async) && (hasChildValue(items) | async)
      "
    >
      <ng-container *ngFor="let item of getArrayValue(items) | async">
        <ng-container
          *ngTemplateOutlet="
            listItemRef;
            context: { $implicit: item, isItem: true }
          "
        ></ng-container>
      </ng-container>
    </ng-container>
  </ion-list>
</mat-card>

<!--           -->
<!-- Templates -->
<!--           -->

<!-- List Item Template -->
<ng-template #listItemRef let-item let-isItem="isItem">
  <ion-item
    button
    lines="full"
    [isForeground]="true"
    [xColor]="getValue(item.color) | async"
    (click)="handleMenuSelected(isItem, item, $event)"
    [matTooltip]="getResourceValue(item.title) | async"
    [matTooltipDisabled]="!(getValue(item.onlyIcon) | async)"
    [detail]="
      (getValue(item.showDetailedIcon) | async) &&
      (hasChildValue(item.options) | async)
    "
    [disabled]="!(getValue(item) | async) || (getValue(item.disabled) | async)"
  >
    <ng-container
      *ngTemplateOutlet="
        listItemContentRef;
        context: {
          icon: item.icon,
          title: item.title,
          onlyIcon: item.onlyIcon,
          iconSlot: item.slot || item.iconSlot,
          iconColor: item.color || item.iconColor
        }
      "
    ></ng-container>
  </ion-item>
</ng-template>

<!-- List Item Content Template -->
<ng-template
  #listItemContentRef
  let-icon="icon"
  let-title="title"
  let-iconSlot="iconSlot"
  let-onlyIcon="onlyIcon"
  let-iconColor="iconColor"
>
  <x-slotter
    class="full-with"
    [cssClass]="'full-with'"
    [layout]="SlotLayouts.HORIZONTAL"
    [startTopSlotCssClass]="'dynamic-slot'"
    [endBottomSlotCssClass]="'dynamic-slot'"
    [centerSlotCssClass]="
      'align-self-center ion-text-start ion-margin-horizontal'
    "
    [hasEndOrBottomSlot]="
      (notEmptyValue(icon) | async) && (getValue(iconSlot) | async) === 'end'
    "
    [hasStartOrTopSlot]="
      (notEmptyValue(icon) | async) && (getValue(iconSlot) | async) === 'start'
    "
    [hasCenterSlot]="notEmptyValue(title) && !(getValue(onlyIcon) | async)"
  >
    <!-- Start -->
    <x-slot [name]="SlotNames.START">
      <!-- Icon -->
      <ng-container
        *ngTemplateOutlet="iconRef; context: { icon: icon, color: iconColor }"
      ></ng-container>
    </x-slot>

    <!-- Center -->
    <x-slot [name]="SlotNames.CENTER">
      <ng-container
        *ngTemplateOutlet="labelRef; context: { $implicit: title }"
      ></ng-container>
    </x-slot>

    <!-- End -->
    <x-slot [name]="SlotNames.END">
      <!-- Icon -->
      <ng-container
        *ngTemplateOutlet="iconRef; context: { icon: icon, color: iconColor }"
      ></ng-container>
    </x-slot>
  </x-slotter>
</ng-template>

<!-- Icon Template -->
<ng-template #iconRef let-icon="icon" let-color="color">
  <x-icon
    [foregroundColor]="true"
    *ngIf="notEmptyValue(icon)"
    [name]="getValue(icon) | async"
    [color]="
      (notEmptyValue(color) | async)
        ? (getValue(color) | async)
        : ColorNames.Tertiary
    "
  ></x-icon>
</ng-template>

<!-- Label Template -->
<ng-template #labelRef let-title>
  <ion-label *ngIf="notEmptyValue(title)">{{
    getResourceValue(title) | async
  }}</ion-label>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-popover\x-popover.component.scss

:host {
  --max-popover-height: 260px;
  --background-color: var(--x-color-light);
  --divider-color: var(--x-color-dark-shade);
}

:host::ng-deep {
  mat-card {
    background-color: var(--background-color) !important;
  }
}

:host::ng-deep.align-self-center {
  align-self: center !important;
}

ion-list,
ion-item.item,
ion-item-divider,
ion-list-header {
  --background: var(--background-color);
}

ion-item-divider {
  --color: var(--divider-color);
}

ion-list-header.x-list-header {
  text-align: center !important;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
}

.x-popover-wrapper {
  max-height: var(--max-popover-height);
  overflow-y: scroll;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-popover\x-popover.component.ts

import {
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  ChangeDetectorRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  isFunction,
  parseXColor,
  XExceptionIDs,
  XOneOrManyType,
  XColorIdentifier,
  isNullOrUndefined,
  XColorWithBrightness,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XPopoverItem,
  XPopoverCssVar,
  XPopoverOption,
  XPopoverResult,
} from './x-popover.typings';
import { NavParams } from '@ionic/angular';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XPopoverService } from './x-popover.service';
import { generatePopoverId } from './x-popover.tools';
import { XManagerService } from 'x-framework-services';
import { XBaseComponent } from '../../classes/x-base-component';
import { XSlotLayout, XSlotName } from '../../x-slotter/x-slotter.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-popover',
  templateUrl: './x-popover.component.html',
  styleUrls: ['./x-popover.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XPopoverComponent extends XBaseComponent {
  //
  //#region Props ...
  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly SlotLayouts = Object.assign({}, XSlotLayout);
  readonly ColorNames = Object.assign({}, XColorWithBrightness);

  //
  items: XOneOrManyType<XPopoverItem>;
  options: XOneOrManyType<XPopoverOption>;

  //
  height: string;
  dividerColor: XColorIdentifier;
  backgroundColor: XColorIdentifier;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    public zone: NgZone,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public navParams: NavParams,
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public changeDetector: ChangeDetectorRef,
    protected popOverService: XPopoverService
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );

    //
    // Options ...
    const options = this.navParams.get(
      nameof<XPopoverComponent>('options')
    ) as XPopoverOption[];
    this.options = options;

    //
    // Items ...
    const items = this.navParams.get(
      nameof<XPopoverComponent>('items')
    ) as XPopoverItem[];
    this.items = items;

    //
    // Error if not Options or Items ...
    if (!this.hasChild(options) && !this.hasChild(items)) {
      this.showError(XExceptionIDs.InvalidArgs, () => {
        this.managerService.dialogService.popoverController.dismiss();
      });
    }
  }
  //#endregion

  //
  //#region LifeCycles ...
  public afterViewInit(): void {
    super.afterViewInit();

    //
    // Height ...
    this.height = this.navParams.get(nameof<XPopoverComponent>('height'));
    if (!isNullOrEmptyString(this.height)) {
      this.applyHeight();
    }

    //
    // DividerColor ...
    this.dividerColor = this.navParams.get(
      nameof<XPopoverComponent>('dividerColor')
    );
    if (!isNullOrEmptyString(this.dividerColor)) {
      this.applyDividerColor();
    }

    //
    // BackgroundColor ...
    this.backgroundColor = this.navParams.get(
      nameof<XPopoverComponent>('backgroundColor')
    );
    if (!isNullOrEmptyString(this.backgroundColor)) {
      this.applyBackgroundColor();
    }
  }
  //#endregion

  //
  //#region UI Providers ...
  //#endregion

  //
  //#region UI Handlers ...
  handleMenuSelected(
    isItem: boolean,
    item: XPopoverItem | XPopoverOption,
    event: Event
  ) {
    //
    if (!!isItem) {
      this.handleItemSelected(item as XPopoverItem, event);
    } else {
      this.handleOptionSelected(item as XPopoverOption);
    }
  }
  //#endregion

  //
  //#region Privates ...
  private async handleItemSelected(item: XPopoverItem, event: Event) {
    //
    const itemResult =
      !isNullOrUndefined(item) &&
      !isNullOrUndefined(item.handler) &&
      isFunction(item.handler)
        ? await this.getValueAsync(item.handler())
        : undefined;

    //
    const optionsResultObj = !this.hasChild(item.options)
      ? undefined
      : await this.popOverService.presentContextMenu(item.options, event);

    //
    if (
      isNullOrUndefined(
        optionsResultObj || isNullOrUndefined(optionsResultObj.option)
      )
    ) {
      return;
    }

    //
    const popoverId = generatePopoverId(this.items);

    //
    await this.managerService.dialogService.popoverController.dismiss(
      {
        item,
        itemResult,
        ...optionsResultObj,
      } as XPopoverResult,
      undefined,
      popoverId
    );
  }

  private async handleOptionSelected(option: XPopoverOption) {
    //
    const optionResult =
      !isNullOrUndefined(option) &&
      !isNullOrUndefined(option.handler) &&
      isFunction(option.handler)
        ? await this.getValueAsync(option.handler())
        : undefined;

    //
    await this.managerService.dialogService.popoverController.dismiss({
      option,
      optionResult,
    } as XPopoverResult);
  }

  private applyHeight() {
    //
    if (isNullOrEmptyString(this.height)) {
      return;
    }

    //
    this.setCssVar(XPopoverCssVar.MaxPopoverHeight, this.height);
    this.detectChanges();
  }

  private applyDividerColor() {
    //
    if (isNullOrEmptyString(this.dividerColor)) {
      return;
    }

    //
    const color = this.dividerColor;
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XPopoverCssVar.DividerColor, `var(${bgColor})`);
    this.detectChanges();
  }

  private applyBackgroundColor() {
    //
    if (isNullOrEmptyString(this.backgroundColor)) {
      return;
    }

    //
    const color = this.backgroundColor;
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XPopoverCssVar.BackgroundColor, `var(${bgColor})`);
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-popover\x-popover.service.ts

import {
  toArray,
  hasChild,
  XBaseService,
  XOneOrManyType,
  isNullOrUndefined,
} from 'x-framework-core';
import {
  XPopoverItem,
  XPopoverOption,
  XPopoverResult,
  XPopoverComponentProps,
  XPopoverPresentOptions,
  XPopoverPresenterOptions,
} from './x-popover.typings';
import { Inject, Injectable } from '@angular/core';
import { generatePopoverId } from './x-popover.tools';
import { XManagerService } from 'x-framework-services';
import { XPopoverComponent } from './x-popover.component';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

@Injectable({
  providedIn: 'root',
})
export class XPopoverService extends XBaseService {
  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService
  ) {
    super(config);
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * Present Context Menu by Providinf XPopOverOption instance array ...
   *
   * @param options menu options which going to show to user ...
   * @param event click event for positioning ...
   */
  public async presentContextMenu(
    options: XOneOrManyType<XPopoverOption>,
    event: Event,
    props?: XPopoverPresentOptions,
    presentProps?: XPopoverPresenterOptions
  ) {
    //
    if (!hasChild(options)) {
      return null;
    }

    //
    const overrideWidth = toArray(options).every((option) => !!option.onlyIcon);
    if (overrideWidth) {
      //
      let currentCssClass = !isNullOrUndefined(presentProps)
        ? toArray(presentProps.cssClass).join(' ')
        : '';
      currentCssClass += `${
        currentCssClass.length > 0 ? ' ' : ''
      }x-minimal-popover`;

      //
      presentProps = {
        ...presentProps,
        cssClass: currentCssClass,
      };
    }

    //
    const mProps: XPopoverComponentProps = {
      options,
      ...props,
    };

    //
    // Present Popover as Context Menu ...
    const popOver = await this.managerService.dialogService.presentPopover({
      //
      ...presentProps,
      //
      event,
      translucent: true,
      showBackdrop: true,
      componentProps: mProps,
      component: XPopoverComponent,
    });

    //
    // Extract Data from PopOver ...
    const { data } = await popOver.onWillDismiss();
    if (!data) {
      return undefined;
    }

    //
    const result = data as XPopoverResult;
    return result;
  }

  /**
   * Present Context Menu by Providinf XPopoverItem instance array ...
   *
   * @param items
   * @param event
   */
  public async presentContextMenuItems(
    items: XOneOrManyType<XPopoverItem>,
    event: Event,
    props?: XPopoverPresentOptions,
    presentProps?: XPopoverPresenterOptions
  ) {
    //
    if (!hasChild(items)) {
      return null;
    }

    //
    const overrideWidth = toArray(items).every((i) => !!i.onlyIcon);
    if (overrideWidth) {
      //
      let currentCssClass = !isNullOrUndefined(presentProps)
        ? toArray(presentProps.cssClass).join(' ')
        : '';
      currentCssClass += `${
        currentCssClass.length > 0 ? ' ' : ''
      }x-minimal-popover`;

      //
      presentProps = {
        ...presentProps,
        cssClass: currentCssClass,
      };
    }

    //
    // Generate unique id from items ...
    const popoverId = generatePopoverId(items);

    //
    const mProps: XPopoverComponentProps = {
      items,
      ...props,
    };

    //
    // Present Popover as Context Menu ...
    const popOver = await this.managerService.dialogService.presentPopover({
      //
      ...presentProps,
      //
      event,
      id: popoverId,
      translucent: true,
      showBackdrop: true,
      componentProps: mProps,
      component: XPopoverComponent,
    });

    //
    // Extract Data from PopOver ...
    const { data } = await popOver.onWillDismiss();
    if (!data) {
      return null;
    }

    //
    const result = data as XPopoverResult;
    return result;
  }
  //#endregion

  //
  //#region Private ...
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-popover\x-popover.tools.ts

import { XListItem } from '../../x-list/x-list.typings';
import {
  toMd5,
  toArray,
  hasChild,
  XOneOrManyType,
  isNullOrUndefined,
} from 'x-framework-core';
import { XPopoverItem, XPopoverOption } from './x-popover.typings';

export const generatePopoverId: (
  items: XOneOrManyType<XPopoverItem>
) => string = (items) => {
  //
  const itemIds = toArray(items)
    .map((i) => i.id)
    .join('|');
  const result = toMd5(itemIds);

  //
  return result;
};

export const toPopoverOptions: (
  item: XListItem<any>
) => XOneOrManyType<XPopoverOption> = (item) => {
  //
  let result: XPopoverOption[] = [];

  //
  if (isNullOrUndefined(item) || !hasChild(item.slideOptions)) {
    return result;
  }

  //
  result = item.slideOptions.map((so) => {
    return {
      id: so.id,
      onlyIcon: so.onlyIcon,
      disabled: so.disabled,
      icon: so.icon,
      color: so.color,
      title: so.title,
      slot: so.slot,
      handler: so.handler,
    } as XPopoverOption;
  });

  //
  return result;
};

export const defaultPopoverItem: XPopoverItem = {
  id: '',
  icon: '',
  color: '',
  title: '',
  options: [],
  iconColor: '',
  iconSlot: 'end',
  onlyIcon: false,
  disabled: false,
  handler: () => {},
  showDetailedIcon: false,
};

export const defaultPopoverOption: XPopoverOption = {
  id: '',
  icon: '',
  color: '',
  title: '',
  slot: 'end',
  onlyIcon: false,
  disabled: false,
  handler: () => {},
};

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-popover\x-popover.typings.ts

import {
  XStandardType,
  XOneOrManyType,
  XPopoverOptions,
  XColorIdentifier,
  XResourceIdentifier,
} from 'x-framework-core';
import { XPopoverComponent } from './x-popover.component';
import { XIconIdentifier } from '../../x-icon/x-icon.typings';

export type XPopoverItemOptionSlot = 'end' | 'start';

export enum XPopoverCssVar {
  DividerColor = 'divider-color',
  BackgroundColor = 'background-color',
  MaxPopoverHeight = 'max-popover-height',
}

export type XPopoverCssVarIdentifier = XPopoverCssVar | string;

export interface XPopoverOption {
  id: string;
  onlyIcon?: boolean;
  disabled?: boolean;
  icon?: XIconIdentifier;
  color?: XColorIdentifier;
  title?: XResourceIdentifier;
  slot?: XPopoverItemOptionSlot;
  handler?: () => XStandardType<any>;
}

export interface XPopoverItem {
  id: string;
  disabled?: boolean;
  onlyIcon?: boolean;
  icon?: XIconIdentifier;
  color: XColorIdentifier;
  title: XResourceIdentifier;
  showDetailedIcon?: boolean;
  iconColor?: XColorIdentifier;
  options?: XPopoverOption[];
  iconSlot?: XPopoverItemOptionSlot;
  handler?: () => XStandardType<any>;
}

export interface XPopoverResult {
  //
  item?: XPopoverItem;
  itemResult?: any;
  //
  option?: XPopoverOption;
  optionResult?: any;
}

type SelectedKeysOfXpopoverComponent = Pick<
  Partial<XPopoverComponent>,
  'items' | 'options' | 'height' | 'dividerColor' | 'backgroundColor'
>;
export type XPopoverComponentPropsType = {
  [key in keyof SelectedKeysOfXpopoverComponent]: any;
};

export interface XPopoverComponentProps extends XPopoverComponentPropsType {
  height?: string;
  dividerColor?: XColorIdentifier;
  backgroundColor?: XColorIdentifier;
  items?: XOneOrManyType<XPopoverItem>;
  options?: XOneOrManyType<XPopoverOption>;
}

export type XPopoverPresentOptions = Pick<
  Partial<XPopoverComponentProps>,
  'height' | 'dividerColor' | 'backgroundColor'
>;

export type XPopoverPresenterOptions = Partial<
  Omit<
    XPopoverOptions,
    | 'backdropDismiss'
    | 'componentProps'
    | 'component'
    | 'id'
    | 'translucent'
    | 'showBackdrop'
    | 'event'
  >
>;

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-selector-dialog\x-selector-dialog.component.html

<div class="x-dialog-wrapper">
  <!-- Toolbar -->
  <ion-header class="x-dialog-header">
    <!-- Title Toolbar -->
    <ion-toolbar
      class="v-toolbar"
      [xColor]="'primary'"
      *ngIf="showToolbar | async"
    >
      <!-- Slots -->
      <div class="toolbar-area">
        <x-slotter>
          <!-- Start -->
          <x-slot
            [name]="SlotNames.START"
            [cssClass]="['ion-text-start', 'ion-float-start']"
          >
            <div
              matRipple
              (click)="handleBack()"
              class="toolbar-item toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="backIcon"
                [loading]="loading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>

          <!-- Center -->
          <x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
            <ion-title class="toolbar-item center-vertical-flex">
              <h3>
                {{ title | async }}
              </h3>
            </ion-title>
          </x-slot>

          <!-- End -->
          <x-slot
            [name]="SlotNames.END"
            [cssClass]="['ion-text-end', 'ion-float-end']"
          >
            <!-- De Select -->
            <div
              matRipple
              (click)="handleDeselect()"
              *ngIf="dialogCanSelect()"
              class="
                toolbar-item toolbar-inline-item toolbar-button
                center-vertical-flex
              "
            >
              <x-icon
                [name]="cancelIcon"
                [loading]="loading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>

            <!-- Select -->
            <div
              matRipple
              (click)="handleSelect()"
              *ngIf="dialogCanSelect()"
              class="
                toolbar-item toolbar-inline-item toolbar-button
                center-vertical-flex
              "
            >
              <x-icon
                [name]="selectIcon"
                [loading]="loading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>
        </x-slotter>
      </div>

      <!-- Progressbar for Loading -->
      <ion-progress-bar
        *ngIf="loading"
        type="indeterminate"
        [color]="progressBarColor | async"
      ></ion-progress-bar>
    </ion-toolbar>
  </ion-header>

  <!-- Component -->
  <ion-content
    [ngClass]="cssClass | async"
    class="x-dialog-content-wrapper"
    [class.has-toolbar]="showToolbar | async"
  >
    <!-- Search -->
    <x-search-bar
      class="x-selector-search"
      [color]="searchBarColor | async"
      [value]="dataPagerQuery.filter"
      [searchInputColor]="searchBarInputColor | async"
      (queryChange)="handleSearchbarQuryChanged($event)"
      *ngIf="(showSearchBar | async) && (getValue(canSearch) | async)"
    ></x-search-bar>

    <!-- Data Pager -->
    <x-data-pager
      [loading]="loading"
      [itemTemplate]="listRef"
      [query]="dataPagerQuery"
      [uiDisabled]="uiDisabled"
      [result]="dataPagerResult"
      [task]="queryFetcher | async"
      clas="x-selector-data-pager"
      [type]="DataPagerPresentTypes.Page"
      [paginatorColor]="paginatorColor | async"
      [actionProvider]="actionProvider | async"
      (emptyRefresh)="handleResetDataPagerQuery()"
      (taskReady)="handleDataPagerTaskReady($event)"
      [paginatorPosition]="paginatorPosition | async"
      (queryChange)="handleDataPagerQueryChanged($event)"
      [showPaginator]="(showPaginator | async) && showDataPagerPaginator"
    ></x-data-pager>
  </ion-content>
</div>

<!-- List Template -->
<ng-template #listRef>
  <ng-container *ngIf="hasChildValue(dataPagerResultListItems) | async">
    <x-list
      [showEmpty]="true"
      [showDivider]="true"
      class="x-selector-list"
      [emptyHasRefresh]="true"
      [items]="dataPagerResultListItems"
      [actionProvider]="pageListActionProvider"
      (emptyRefresh)="handleResetDataPagerQuery()"
      [selectableItems]="canMultipleSelect | async"
      [clickableItems]="!(canMultipleSelect | async)"
      (itemSelected)="handleListItemSelected($event)"
      [itemTemplate]="
        (notNullOrUndefinedValue(itemTemplate) | async)
          ? (itemTemplate | async)
          : vItemRef
      "
      (selectionChanged)="handleListItemsSelected($event)"
    >
    </x-list>
  </ng-container>
</ng-template>

<ng-template #vItemRef let-item>
  {{ item.data | json }}
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-selector-dialog\x-selector-dialog.component.scss

::ng-deep.fullscreen-dialog {
  //
  width: 80vw !important;
  max-width: 80vw !important;

  //
  height: 80vh !important;
  max-height: 80vh !important;

  //
  &.mobile {
    //
    width: 100vw !important;
    max-width: 100vw !important;

    //
    height: 100vh !important;
    max-height: 100vh !important;
  }

  //
  .mat-dialog-container {
    padding: 0 !important;
  }
}

.x-dialog-wrapper {
  width: 100% !important;
  height: 100% !important;
  position: relative !important;
}

.x-dialog-header {
  position: sticky !important;
  top: 0;
  z-index: 1000;
}

.x-dialog-content-wrapper {
  height: calc(100%);

  &.has-toolbar {
    height: calc(100% - 56px);
  }
}

x-icon {
  --size: 24px;
}

.toolbar-area {
  margin: 0px;
}

.toolbar-item {
  width: auto;
  height: 48px;
}

.toolbar-inline-item {
  display: inline-flex !important;
}

.toolbar-button {
  width: 32px;
  margin: {
    left: 4px;
    right: 4px;
  }
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-selector-dialog\x-selector-dialog.component.ts

import {
  Inject,
  NgZone,
  Renderer2,
  Component,
  ElementRef,
  ChangeDetectorRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XTask,
  XColor,
  toArray,
  isFunction,
  generateUuid,
  XOneOrManyType,
  isNullOrUndefined,
  isNullOrEmptyString,
  XBaseDataPagerQuery,
  XBaseDataPagerQueryResult,
} from 'x-framework-core';
import { Observable, Subject } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import {
  XDataPagerActionModel,
  XDataPagerPresentType,
  XDataPagerPaginatorPosition,
} from '../../x-data-pager/x-data-pager.typings';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XSelectorDialogConfig } from './x-selector-dialog.typings';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { XListItem, XListActionModel } from '../../x-list/x-list.typings';
import { XBaseDialogComponent } from '../../classes/x-base.dialog.component';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-selector-dialog',
  templateUrl: './x-selector-dialog.component.html',
  styleUrls: ['./x-selector-dialog.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSelectorDialogComponent extends XBaseDialogComponent<
  any,
  XDataPagerActionModel,
  XSelectorDialogConfig
> {
  //
  //#region Props ...
  //
  uuid = generateUuid('XFormSelector');

  //
  canSearch: boolean;
  dataPagerQuery: XBaseDataPagerQuery = {
    ...this.getDataPagerQuery(),
  };
  showDataPagerPaginator: boolean = true;
  dataPagerResultListItems: XListItem<any>[] = [];
  dataPagerResult: XBaseDataPagerQueryResult<any>;
  dataPagerActionProvider = new Subject<XDataPagerActionModel>();

  //
  pageListActionProvider = new Subject<XListActionModel>();

  //
  readonly DataPagerPresentTypes = Object.assign({}, XDataPagerPresentType);
  //#endregion

  //
  //#region Constructor ...
  constructor(
    public zone: NgZone,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public managerService: XManagerService,
    public changeDetector: ChangeDetectorRef,
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    @Inject(MAT_DIALOG_DATA) public data: any,
    public dialogRef: MatDialogRef<XSelectorDialogComponent>
  ) {
    super(
      zone,
      element,
      renderer,
      ruler,
      managerService,
      changeDetector,
      config,
      data,
      dialogRef
    );
  }
  //#endregion

  //
  //#region Task Handlers ...
  async onTaskFinished(name: string, task: XTask<any>) {
    await super.onTaskFinished(name, task);

    //
    const tName = await this.getValueAsync(this.taskName);
    if (name === tName) {
      //
      this.setLoadingState(false);
      await this.handleDataPagerTaskFinished(task.result);
    }
  }
  //#endregion

  //
  //#region UI Providers ...
  //
  get taskName() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.taskName : this.uuid)
      ),
      map((name) => (!isNullOrEmptyString(name) ? name : this.uuid))
    );
  }

  get queryFetcher() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.queryFetcher : undefined)
      )
    );
  }

  get itemTemplate() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.itemTemplate : undefined)
      )
    );
  }

  //
  //#region Data Pager Paginator ...
  get showPaginator() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config ? config.showPaginator : false)
      )
    );
  }

  get paginatorColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config ? config.paginatorColor : XColor.Primary)
      ),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Primary))
    );
  }

  get paginatorPosition() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(
          config ? config.paginatorPosition : XDataPagerPaginatorPosition.Above
        )
      ),
      map((position) =>
        !isNullOrEmptyString(position)
          ? position
          : XDataPagerPaginatorPosition.Above
      )
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleResetDataPagerQuery() {
    //
    this.dataPagerQuery = {
      ...this.getDataPagerQuery(),
    };

    //
    this.detectChanges();
  }

  async handleDataPagerTaskReady(
    task: Observable<XBaseDataPagerQueryResult<any>>
  ) {
    //
    // XBaseDataPagerQueryResult<any> ...
    if (!task || this.uiDisabled || this.loading) {
      return;
    }

    //
    await this.issueDataPagerTask(task);
  }

  async handleDataPagerQueryChanged(query: XBaseDataPagerQuery) {
    //
    if (!query) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getData());
    if (config && config.onQueryChanged && isFunction(config.onQueryChanged)) {
      await this.getValueAsync(config.onQueryChanged(query));
    }
  }

  async handleListItemSelected(event: XListItem<any>) {
    //
    const canSelectMultipleItems = await this.getValueAsync(
      this.canMultipleSelect
    );

    //
    if (!event || !event.data || canSelectMultipleItems) {
      return;
    }

    //
    const index = this.dataPagerResultListItems.findIndex((i) =>
      Object.is(i, event)
    );
    if (index > -1) {
      //
      // Cancel Previous Selected Item(s) ...
      this.dataPagerResultListItems.forEach((i) => {
        i.disabled = false;
      });

      //
      // Disable Current Selected Item ...
      this.dataPagerResultListItems[index].disabled = true;

      //
      this.detectChanges();
    }

    //
    await this.handleSelectedItems(event);
  }

  async handleSelectedItems(event: XOneOrManyType<XListItem<any>>) {
    //
    this.selectedItem = toArray(event).map((i) => i.data);

    //
    const config = await this.getValueAsync(this.getData());
    if (config && config.onItemSelect && isFunction(config.onItemSelect)) {
      await this.getValueAsync(config.onItemSelect(event));
    }

    //
    this.detectChanges();
  }

  async handleListItemsSelected(event: XListItem<any>[]) {
    //
    const canSelectMultipleItems = await this.getValueAsync(
      this.canMultipleSelect
    );

    //
    if (!this.hasChild(event) || !canSelectMultipleItems) {
      return;
    }

    //
    await this.handleSelectedItems(event);
  }
  //#endregion

  //
  //#region Abstracts ...
  async getTaskNames(): Promise<string[]> {
    return [await this.getValueAsync(this.taskName)];
  }

  async deselectAllListItems(): Promise<void> {
    //
    if (!this.hasChild(this.dataPagerResultListItems)) {
      return;
    }

    //
    this.dataPagerResultListItems.forEach((i) => {
      i.disabled = false;
    });

    //
    this.dataPagerResultListItems = [...this.dataPagerResultListItems];

    //
    this.detectChanges();
  }

  async deselectItemInList(item: any): Promise<void> {
    //
    if (
      isNullOrUndefined(item) ||
      !this.hasChild(this.dataPagerResultListItems)
    ) {
      return;
    }

    //
    const index = this.dataPagerResultListItems.findIndex((i) =>
      Object.is(i.data, item)
    );
    if (index > -1) {
      //
      this.dataPagerResultListItems[index].disabled = false;

      //
      this.dataPagerResultListItems = [...this.dataPagerResultListItems];

      //
      this.detectChanges();
    }
  }

  async handleSearchbarQuryChanged(query: string): Promise<void> {
    //
    this.dataPagerQuery = {
      ...this.dataPagerQuery,
      filter: query,
    };

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  private getDataPagerQuery(): XBaseDataPagerQuery {
    return {
      filter: '',
      pageNumber: 1,
      rowsInPage: this.config.defaultPageSize,
    } as XBaseDataPagerQuery;
  }

  private async issueDataPagerTask(
    task: Observable<XBaseDataPagerQueryResult<any>>
  ) {
    //
    if (!task) {
      return;
    }

    //
    const mTaskName = await this.getValueAsync(this.taskName);
    const mTask = task;

    //
    await this.addOrUpdateTask({
      priority: 0,
      affectLoading: true,
      name: mTaskName,
      task: mTask,
    });

    //
    await this.dispatchTask(mTaskName);
  }

  private async handleDataPagerTaskFinished(
    result: XBaseDataPagerQueryResult<any>
  ) {
    //
    this.dataPagerResult = null;
    this.dataPagerResultListItems = [];
    this.detectChanges();

    //
    if (!result) {
      return;
    }

    //
    this.dataPagerResult = {
      ...result,
    };

    //
    this.canSearch =
      (this.hasChild(this.dataPagerResult.items) &&
        this.dataPagerResult.items.length > 1) ||
      !isNullOrEmptyString(this.dataPagerQuery.filter);

    //
    this.showDataPagerPaginator =
      this.hasChild(this.dataPagerResult.items) &&
      this.dataPagerResult.items.length > 1;

    //
    const config = await this.getValueAsync(this.getData());
    if (config && config.onPageLoad && isFunction(config.onPageLoad)) {
      await this.getValueAsync(config.onPageLoad(this.dataPagerResult));
    }

    //
    this.dataPagerResultListItems = await this.prepareListItems();

    //
    this.detectChanges();
  }

  private async prepareListItems() {
    //
    if (!this.dataPagerResult || !this.hasChild(this.dataPagerResult.items)) {
      return [];
    }

    //
    return this.dataPagerResult.items.map((sr) => {
      return {
        data: sr,
      } as XListItem<any>;
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-selector-dialog\x-selector-dialog.service.ts

import {
  MatDialog,
  MatDialogRef,
  MatDialogConfig,
} from '@angular/material/dialog';
import { Inject, Injectable } from '@angular/core';
import { XResourceIdentifier } from 'x-framework-core';
import { XManagerService } from 'x-framework-services';
import { XSelectorDialogConfig } from './x-selector-dialog.typings';
import { XSelectorDialogComponent } from './x-selector-dialog.component';
import { XBaseDialogService } from '../../classes/x-base-dialog.service';
import { XDataPagerActionModel } from '../../x-data-pager/x-data-pager.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

@Injectable({
  providedIn: 'root',
})
export class XSelectorDialogService extends XBaseDialogService<
  any,
  XDataPagerActionModel,
  XSelectorDialogConfig,
  XSelectorDialogComponent
> {
  //
  defaultTitleResourceIdentifier: XResourceIdentifier;

  //
  //#region Constructor ...
  constructor(
    protected dialog: MatDialog,
    protected managerService: XManagerService,
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig
  ) {
    super(dialog, managerService, config);
  }
  //#endregion

  //
  openDialogAndReturnRef(
    config: MatDialogConfig<any>
  ): MatDialogRef<XSelectorDialogComponent, any> {
    return this.dialog.open(XSelectorDialogComponent, config);
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\dialogs\x-selector-dialog\x-selector-dialog.typings.ts

import {
  XStandardType,
  XColorIdentifier,
  XBaseDataPagerQuery,
  XBaseDataPagerQueryResult,
} from 'x-framework-core';
import { Observable } from 'rxjs';
import { TemplateRef } from '@angular/core';
import {
  IXBaseDialogConfig,
  IXBaseDialogResult,
} from '../../classes/x-base.dialog.component';
import {
  XDataPagerActionModel,
  XDataPagerPaginatorPositionIdentifier,
} from '../../x-data-pager/x-data-pager.typings';

export interface XSelectorDialogResult extends IXBaseDialogResult {}

export interface XSelectorDialogConfig
  extends IXBaseDialogConfig<XDataPagerActionModel> {
  //
  showPaginator?: XStandardType<boolean>;
  paginatorColor?: XStandardType<XColorIdentifier>;
  paginatorPosition?: XStandardType<XDataPagerPaginatorPositionIdentifier>;

  //
  queryFetcher?: (
    query: XBaseDataPagerQuery
  ) => Observable<XBaseDataPagerQueryResult<any>>;
  onQueryChanged?: (query: XBaseDataPagerQuery) => XStandardType<void>;
  onPageLoad?: (result: XBaseDataPagerQueryResult<any>) => XStandardType<void>;
  itemTemplate?: XStandardType<TemplateRef<any>>;

  //
  taskName?: XStandardType<string>;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\modules\x-base-component.module.ts

import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { XMaterialModule } from './x-material.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { XDirectivesModule as XFrameworkCoreDirectivesModule } from 'x-framework-core';
import { XDirectivesModule as XFrameworkServicesDirectivesModule } from 'x-framework-services';

@NgModule({
  imports: [
    IonicModule,
    FormsModule,
    CommonModule,
    RouterModule,
    XMaterialModule,
    ReactiveFormsModule,
    XFrameworkCoreDirectivesModule,
    XFrameworkServicesDirectivesModule,
  ],
  exports: [
    FormsModule,
    IonicModule,
    RouterModule,
    CommonModule,
    XMaterialModule,
    ReactiveFormsModule,
    XFrameworkCoreDirectivesModule,
    XFrameworkServicesDirectivesModule,
  ],
})
export class XBaseComponentModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\modules\x-material.module.ts

//
import { NgModule } from '@angular/core';

//
import { DragDropModule } from '@angular/cdk/drag-drop';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { CdkTableModule } from '@angular/cdk/table';
import { CdkTreeModule } from '@angular/cdk/tree';

//
// Form Controls ...
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSliderModule } from '@angular/material/slider';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';

//
// Navigations / Menus ...
import { MatMenuModule } from '@angular/material/menu';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatToolbarModule } from '@angular/material/toolbar';

//
// Layout ...
import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatListModule } from '@angular/material/list';
import { MatStepperModule } from '@angular/material/stepper';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTreeModule } from '@angular/material/tree';

//
// Buttons and Indicators ...
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatBadgeModule } from '@angular/material/badge';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatRippleModule } from '@angular/material/core';
import { MatNativeDateModule } from '@angular/material/core';

//
// Popups and Dialogs ...
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatDialogModule } from '@angular/material/dialog';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip';

//
// Data Table ...
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';

//
import {
  MomentDateModule,
  MatMomentDateModule,
} from '@angular/material-moment-adapter';
import { LayoutModule } from '@angular/cdk/layout';
import { FlexLayoutModule } from '@angular/flex-layout';
import { CommonModule } from '@angular/common';

@NgModule({
  imports: [
    CommonModule,

    //
    CdkTableModule,
    CdkTreeModule,

    //
    DragDropModule,
    ScrollingModule,

    //
    LayoutModule,
    FlexLayoutModule,

    //
    MatAutocompleteModule,
    MatBadgeModule,
    MatBottomSheetModule,
    MatButtonModule,
    MatButtonToggleModule,
    MatCardModule,
    MatCheckboxModule,
    MatChipsModule,
    MatStepperModule,
    MatDatepickerModule,
    MatDialogModule,
    MatDividerModule,
    MatExpansionModule,
    MatGridListModule,
    MatIconModule,
    MatInputModule,
    MatListModule,
    MatNativeDateModule,
    MatPaginatorModule,
    MatProgressBarModule,
    MatProgressSpinnerModule,
    MatRadioModule,
    MatRippleModule,
    MatSelectModule,
    MatSidenavModule,
    MatSliderModule,
    MatSlideToggleModule,
    MatSnackBarModule,
    MatSortModule,
    MatTableModule,
    MatTabsModule,
    MatToolbarModule,
    MatTooltipModule,
    MatTreeModule,
    MatFormFieldModule,
    MatMenuModule,

    //
    MomentDateModule,
    MatMomentDateModule,
  ],
  exports: [
    //
    CdkTableModule,
    CdkTreeModule,

    //
    DragDropModule,
    ScrollingModule,

    //
    LayoutModule,
    FlexLayoutModule,

    //
    MatAutocompleteModule,
    MatBadgeModule,
    MatBottomSheetModule,
    MatButtonModule,
    MatButtonToggleModule,
    MatCardModule,
    MatCheckboxModule,
    MatChipsModule,
    MatStepperModule,
    MatDatepickerModule,
    MatDialogModule,
    MatDividerModule,
    MatExpansionModule,
    MatGridListModule,
    MatIconModule,
    MatInputModule,
    MatListModule,
    MatNativeDateModule,
    MatPaginatorModule,
    MatProgressBarModule,
    MatProgressSpinnerModule,
    MatRadioModule,
    MatRippleModule,
    MatSelectModule,
    MatSidenavModule,
    MatSliderModule,
    MatSlideToggleModule,
    MatSnackBarModule,
    MatSortModule,
    MatTableModule,
    MatTabsModule,
    MatToolbarModule,
    MatTooltipModule,
    MatTreeModule,
    MatFormFieldModule,
    MatMenuModule,

    //
    MomentDateModule,
    MatMomentDateModule,
  ],
})
export class XMaterialModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\_tools.scss

@import "./animate.scss";

//
//#region Tools ...

//
//#region Dimensions ...
//
// These are default values which can be used for margins and
// paddings ...
$d-xsm: 2px;
$d-sm: 4px;
$d-md: 8px;
$d-l: 16px;
$d-el: 32px;
$d-exl: 48px;

//
// Box Sizing ...
$d-min: 300px;
$d-medium: 500px;
$d-max: 800px;

//
// Percentages ...
$d-25p: 25%;
$d-50p: 50%;
$d-75p: 75%;
$d-100p: 100%;
//#endregion

//
//#region Margin adnd Padding mixins ...
.m-0 {
  margin: 0 !important;
}

.p-0 {
  padding: 0 !important;
}

@mixin m-rl($size: $d-sm) {
  margin-left: $size;
  margin-right: $size;
}

@mixin p-rl($size: $d-sm) {
  padding-left: $size;
  padding-right: $size;
}

@mixin m-tb($size: $d-sm) {
  margin-top: $size;
  margin-bottom: $size;
}

@mixin p-tb($size: $d-sm) {
  padding-top: $size;
  padding-bottom: $size;
}

@mixin m($size: $d-sm) {
  margin: $size !important;
}

@mixin p($size: $d-sm) {
  padding: $size !important;
}

@mixin m-full($top: 0px, $left: 0px, $bottom: 0px, $right: 0px) {
  margin: {
    top: $top;
    left: $left;
    bottom: $bottom;
    right: $right;
  }
}

@mixin p-full($top: 0px, $left: 0px, $bottom: 0px, $right: 0px) {
  padding: {
    top: $top;
    left: $left;
    bottom: $bottom;
    right: $right;
  }
}

@mixin push-auto {
  margin: {
    left: auto;
    right: auto;
  }
}

.push-auto {
  margin: {
    left: auto;
    right: auto;
  }
}

// #endregion

//
//#region Animation mixins and classes ...
@mixin animation(
  $animation-name: ".fadeIn",
  $animation-duration: 500ms,
  $animation-delay: "",
  $animation-repeat: 1
) {
  @extend .animated;
  @extend .#{$animation-name};
  animation-delay: $animation-delay;
  animation-duration: $animation-duration;
  animation-iteration-count: $animation-repeat;
}

@mixin transition-duration($duration: 500ms, $delay: 0ms) {
  //
  transition-delay: $delay;
  -webkit-transition-delay: $delay;

  //
  transition-duration: $duration;
  -webkit-transition-duration: $duration;
}

@mixin transformer($transform, $duration: 500ms, $delay: 0ms) {
  //
  transform: $transform;
  -ms-transform: $transform;
  -webkit-transform: $transform;

  //
  @include transition(transform, $duration, $delay);
}

@mixin transition($transition, $duration: 500ms, $delay: 0ms) {
  //
  transition: $transition;
  -webkit-transition: $transition;

  @include transition-duration($duration, $delay);
}

@mixin show {
  @include animation("fadeIn", "250ms");
  @include transition("height", "250ms");
}

@mixin hide {
  @include animation("fadeOut", "250ms");

  height: 0;
  @include transition("height", "250ms", "250ms");
}

.show {
  @include show();
}

.hide {
  @include hide();
}

//#endregion

//
//#region Width Height and Alignment ...
@mixin applyWidthHeight($size: $d-75p, $min-width: $d-min, $max-width: $d-max) {
  width: calc(#{$size});
  max-width: $max-width;
  min-width: $min-width;
}

@mixin horizontal-spacer {
  flex: 1 1 auto;
}

.horizontal-spacer {
  @include horizontal-spacer();
}

@mixin center-vertical-flex {
  //
  display: flex !important;
  flex-direction: column !important;
  justify-content: center !important;
}

.center-vertical-flex {
  @include center-vertical-flex();
}

@mixin center-vertical {
  position: relative;
  top: 50%;
  transform: translateY(-50%);
}

.center-vertical {
  @include center-vertical();
}

@mixin center {
  @extend .center;
}

.center {
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}

@mixin text-truncate {
  @extend .text-truncate;
}

.text-truncate {
  display: inline-block;
  overflow-x: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap;
  min-width: 0;
}

//#endregion

//
//#region Shadows Tools ...
@mixin add-shadow-box($vert, $horz, $blur, $size, $color: $shadow-color) {
  //
  $box-shadow-data: $vert $horz $blur $size $shadow-color !important;

  //
  box-shadow: $box-shadow-data;
  -moz-box-shadow: $box-shadow-data;
  -webkit-box-shadow: $box-shadow-data;
}

@mixin remove-shadow-box() {
  //
  $box-shadow-data: none !important;

  //
  box-shadow: $box-shadow-data;
  -moz-box-shadow: $box-shadow-data;
  -webkit-box-shadow: $box-shadow-data;
}

@mixin filterBlur($level: 4px) {
  -webkit-filter: blur($level);
  filter: blur($level);
}

@mixin filterBrightness($level: 0.3) {
  -webkit-filter: brightness($level);
  filter: brightness($level);
}

@mixin filterContrast($level: 180%) {
  -webkit-filter: contrast($level);
  filter: contrast($level);
}

@mixin filterGrayscale($level: 100%) {
  -webkit-filter: grayscale($level);
  filter: grayscale($level);
}

@mixin filterHuerotate($level: 180deg) {
  -webkit-filter: hue-rotate($level);
  filter: hue-rotate($level);
}

@mixin filterInvert($level: 100%) {
  -webkit-filter: invert($level);
  filter: invert($level);
}

@mixin filterOpacity($level: 50%) {
  -webkit-filter: opacity($level);
  filter: opacity($level);
}

@mixin filterSaturate($level: 7) {
  -webkit-filter: saturate($level);
  filter: saturate($level);
}

@mixin filterSepia($level: 100%) {
  -webkit-filter: sepia($level);
  filter: sepia(100%);
}

//#endregion

//
//#region Other ...
@mixin disableLink {
  pointer-events: none;

  * {
    pointer-events: none;
  }
}
//#endregion
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\animate.scss

@charset "UTF-8";

/*!
 * animate.css -http://daneden.me/animate
 * Version - 3.7.0
 * Licensed under the MIT license - http://opensource.org/licenses/MIT
 *
 * Copyright (c) 2018 Daniel Eden
 */

@-webkit-keyframes bounce {
  from,
  20%,
  53%,
  80%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  40%,
  43% {
    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    -webkit-transform: translate3d(0, -30px, 0);
    transform: translate3d(0, -30px, 0);
  }

  70% {
    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    -webkit-transform: translate3d(0, -15px, 0);
    transform: translate3d(0, -15px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, -4px, 0);
    transform: translate3d(0, -4px, 0);
  }
}

@keyframes bounce {
  from,
  20%,
  53%,
  80%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  40%,
  43% {
    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    -webkit-transform: translate3d(0, -30px, 0);
    transform: translate3d(0, -30px, 0);
  }

  70% {
    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
    -webkit-transform: translate3d(0, -15px, 0);
    transform: translate3d(0, -15px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, -4px, 0);
    transform: translate3d(0, -4px, 0);
  }
}

.bounce {
  -webkit-animation-name: bounce;
  animation-name: bounce;
  -webkit-transform-origin: center bottom;
  transform-origin: center bottom;
}

@-webkit-keyframes flash {
  from,
  50%,
  to {
    opacity: 1;
  }

  25%,
  75% {
    opacity: 0;
  }
}

@keyframes flash {
  from,
  50%,
  to {
    opacity: 1;
  }

  25%,
  75% {
    opacity: 0;
  }
}

.flash {
  -webkit-animation-name: flash;
  animation-name: flash;
}

/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */

@-webkit-keyframes pulse {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  50% {
    -webkit-transform: scale3d(1.05, 1.05, 1.05);
    transform: scale3d(1.05, 1.05, 1.05);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

@keyframes pulse {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  50% {
    -webkit-transform: scale3d(1.05, 1.05, 1.05);
    transform: scale3d(1.05, 1.05, 1.05);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

.pulse {
  -webkit-animation-name: pulse;
  animation-name: pulse;
}

@-webkit-keyframes rubberBand {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  30% {
    -webkit-transform: scale3d(1.25, 0.75, 1);
    transform: scale3d(1.25, 0.75, 1);
  }

  40% {
    -webkit-transform: scale3d(0.75, 1.25, 1);
    transform: scale3d(0.75, 1.25, 1);
  }

  50% {
    -webkit-transform: scale3d(1.15, 0.85, 1);
    transform: scale3d(1.15, 0.85, 1);
  }

  65% {
    -webkit-transform: scale3d(0.95, 1.05, 1);
    transform: scale3d(0.95, 1.05, 1);
  }

  75% {
    -webkit-transform: scale3d(1.05, 0.95, 1);
    transform: scale3d(1.05, 0.95, 1);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

@keyframes rubberBand {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  30% {
    -webkit-transform: scale3d(1.25, 0.75, 1);
    transform: scale3d(1.25, 0.75, 1);
  }

  40% {
    -webkit-transform: scale3d(0.75, 1.25, 1);
    transform: scale3d(0.75, 1.25, 1);
  }

  50% {
    -webkit-transform: scale3d(1.15, 0.85, 1);
    transform: scale3d(1.15, 0.85, 1);
  }

  65% {
    -webkit-transform: scale3d(0.95, 1.05, 1);
    transform: scale3d(0.95, 1.05, 1);
  }

  75% {
    -webkit-transform: scale3d(1.05, 0.95, 1);
    transform: scale3d(1.05, 0.95, 1);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

.rubberBand {
  -webkit-animation-name: rubberBand;
  animation-name: rubberBand;
}

@-webkit-keyframes shake {
  from,
  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  10%,
  30%,
  50%,
  70%,
  90% {
    -webkit-transform: translate3d(-10px, 0, 0);
    transform: translate3d(-10px, 0, 0);
  }

  20%,
  40%,
  60%,
  80% {
    -webkit-transform: translate3d(10px, 0, 0);
    transform: translate3d(10px, 0, 0);
  }
}

@keyframes shake {
  from,
  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  10%,
  30%,
  50%,
  70%,
  90% {
    -webkit-transform: translate3d(-10px, 0, 0);
    transform: translate3d(-10px, 0, 0);
  }

  20%,
  40%,
  60%,
  80% {
    -webkit-transform: translate3d(10px, 0, 0);
    transform: translate3d(10px, 0, 0);
  }
}

.shake {
  -webkit-animation-name: shake;
  animation-name: shake;
}

@-webkit-keyframes headShake {
  0% {
    -webkit-transform: translateX(0);
    transform: translateX(0);
  }

  6.5% {
    -webkit-transform: translateX(-6px) rotateY(-9deg);
    transform: translateX(-6px) rotateY(-9deg);
  }

  18.5% {
    -webkit-transform: translateX(5px) rotateY(7deg);
    transform: translateX(5px) rotateY(7deg);
  }

  31.5% {
    -webkit-transform: translateX(-3px) rotateY(-5deg);
    transform: translateX(-3px) rotateY(-5deg);
  }

  43.5% {
    -webkit-transform: translateX(2px) rotateY(3deg);
    transform: translateX(2px) rotateY(3deg);
  }

  50% {
    -webkit-transform: translateX(0);
    transform: translateX(0);
  }
}

@keyframes headShake {
  0% {
    -webkit-transform: translateX(0);
    transform: translateX(0);
  }

  6.5% {
    -webkit-transform: translateX(-6px) rotateY(-9deg);
    transform: translateX(-6px) rotateY(-9deg);
  }

  18.5% {
    -webkit-transform: translateX(5px) rotateY(7deg);
    transform: translateX(5px) rotateY(7deg);
  }

  31.5% {
    -webkit-transform: translateX(-3px) rotateY(-5deg);
    transform: translateX(-3px) rotateY(-5deg);
  }

  43.5% {
    -webkit-transform: translateX(2px) rotateY(3deg);
    transform: translateX(2px) rotateY(3deg);
  }

  50% {
    -webkit-transform: translateX(0);
    transform: translateX(0);
  }
}

.headShake {
  -webkit-animation-timing-function: ease-in-out;
  animation-timing-function: ease-in-out;
  -webkit-animation-name: headShake;
  animation-name: headShake;
}

@-webkit-keyframes swing {
  20% {
    -webkit-transform: rotate3d(0, 0, 1, 15deg);
    transform: rotate3d(0, 0, 1, 15deg);
  }

  40% {
    -webkit-transform: rotate3d(0, 0, 1, -10deg);
    transform: rotate3d(0, 0, 1, -10deg);
  }

  60% {
    -webkit-transform: rotate3d(0, 0, 1, 5deg);
    transform: rotate3d(0, 0, 1, 5deg);
  }

  80% {
    -webkit-transform: rotate3d(0, 0, 1, -5deg);
    transform: rotate3d(0, 0, 1, -5deg);
  }

  to {
    -webkit-transform: rotate3d(0, 0, 1, 0deg);
    transform: rotate3d(0, 0, 1, 0deg);
  }
}

@keyframes swing {
  20% {
    -webkit-transform: rotate3d(0, 0, 1, 15deg);
    transform: rotate3d(0, 0, 1, 15deg);
  }

  40% {
    -webkit-transform: rotate3d(0, 0, 1, -10deg);
    transform: rotate3d(0, 0, 1, -10deg);
  }

  60% {
    -webkit-transform: rotate3d(0, 0, 1, 5deg);
    transform: rotate3d(0, 0, 1, 5deg);
  }

  80% {
    -webkit-transform: rotate3d(0, 0, 1, -5deg);
    transform: rotate3d(0, 0, 1, -5deg);
  }

  to {
    -webkit-transform: rotate3d(0, 0, 1, 0deg);
    transform: rotate3d(0, 0, 1, 0deg);
  }
}

.swing {
  -webkit-transform-origin: top center;
  transform-origin: top center;
  -webkit-animation-name: swing;
  animation-name: swing;
}

@-webkit-keyframes tada {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  10%,
  20% {
    -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
    transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
  }

  30%,
  50%,
  70%,
  90% {
    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
  }

  40%,
  60%,
  80% {
    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

@keyframes tada {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  10%,
  20% {
    -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
    transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
  }

  30%,
  50%,
  70%,
  90% {
    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
  }

  40%,
  60%,
  80% {
    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

.tada {
  -webkit-animation-name: tada;
  animation-name: tada;
}

/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */

@-webkit-keyframes wobble {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  15% {
    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
  }

  30% {
    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
  }

  45% {
    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
  }

  60% {
    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
  }

  75% {
    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes wobble {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  15% {
    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
  }

  30% {
    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
  }

  45% {
    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
  }

  60% {
    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
  }

  75% {
    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.wobble {
  -webkit-animation-name: wobble;
  animation-name: wobble;
}

@-webkit-keyframes jello {
  from,
  11.1%,
  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  22.2% {
    -webkit-transform: skewX(-12.5deg) skewY(-12.5deg);
    transform: skewX(-12.5deg) skewY(-12.5deg);
  }

  33.3% {
    -webkit-transform: skewX(6.25deg) skewY(6.25deg);
    transform: skewX(6.25deg) skewY(6.25deg);
  }

  44.4% {
    -webkit-transform: skewX(-3.125deg) skewY(-3.125deg);
    transform: skewX(-3.125deg) skewY(-3.125deg);
  }

  55.5% {
    -webkit-transform: skewX(1.5625deg) skewY(1.5625deg);
    transform: skewX(1.5625deg) skewY(1.5625deg);
  }

  66.6% {
    -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg);
    transform: skewX(-0.78125deg) skewY(-0.78125deg);
  }

  77.7% {
    -webkit-transform: skewX(0.390625deg) skewY(0.390625deg);
    transform: skewX(0.390625deg) skewY(0.390625deg);
  }

  88.8% {
    -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
    transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
  }
}

@keyframes jello {
  from,
  11.1%,
  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  22.2% {
    -webkit-transform: skewX(-12.5deg) skewY(-12.5deg);
    transform: skewX(-12.5deg) skewY(-12.5deg);
  }

  33.3% {
    -webkit-transform: skewX(6.25deg) skewY(6.25deg);
    transform: skewX(6.25deg) skewY(6.25deg);
  }

  44.4% {
    -webkit-transform: skewX(-3.125deg) skewY(-3.125deg);
    transform: skewX(-3.125deg) skewY(-3.125deg);
  }

  55.5% {
    -webkit-transform: skewX(1.5625deg) skewY(1.5625deg);
    transform: skewX(1.5625deg) skewY(1.5625deg);
  }

  66.6% {
    -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg);
    transform: skewX(-0.78125deg) skewY(-0.78125deg);
  }

  77.7% {
    -webkit-transform: skewX(0.390625deg) skewY(0.390625deg);
    transform: skewX(0.390625deg) skewY(0.390625deg);
  }

  88.8% {
    -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
    transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
  }
}

.jello {
  -webkit-animation-name: jello;
  animation-name: jello;
  -webkit-transform-origin: center;
  transform-origin: center;
}

@-webkit-keyframes heartBeat {
  0% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }

  14% {
    -webkit-transform: scale(1.3);
    transform: scale(1.3);
  }

  28% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }

  42% {
    -webkit-transform: scale(1.3);
    transform: scale(1.3);
  }

  70% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}

@keyframes heartBeat {
  0% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }

  14% {
    -webkit-transform: scale(1.3);
    transform: scale(1.3);
  }

  28% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }

  42% {
    -webkit-transform: scale(1.3);
    transform: scale(1.3);
  }

  70% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}

.heartBeat {
  -webkit-animation-name: heartBeat;
  animation-name: heartBeat;
  -webkit-animation-duration: 1.3s;
  animation-duration: 1.3s;
  -webkit-animation-timing-function: ease-in-out;
  animation-timing-function: ease-in-out;
}

@-webkit-keyframes bounceIn {
  from,
  20%,
  40%,
  60%,
  80%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }

  20% {
    -webkit-transform: scale3d(1.1, 1.1, 1.1);
    transform: scale3d(1.1, 1.1, 1.1);
  }

  40% {
    -webkit-transform: scale3d(0.9, 0.9, 0.9);
    transform: scale3d(0.9, 0.9, 0.9);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(1.03, 1.03, 1.03);
    transform: scale3d(1.03, 1.03, 1.03);
  }

  80% {
    -webkit-transform: scale3d(0.97, 0.97, 0.97);
    transform: scale3d(0.97, 0.97, 0.97);
  }

  to {
    opacity: 1;
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

@keyframes bounceIn {
  from,
  20%,
  40%,
  60%,
  80%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }

  20% {
    -webkit-transform: scale3d(1.1, 1.1, 1.1);
    transform: scale3d(1.1, 1.1, 1.1);
  }

  40% {
    -webkit-transform: scale3d(0.9, 0.9, 0.9);
    transform: scale3d(0.9, 0.9, 0.9);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(1.03, 1.03, 1.03);
    transform: scale3d(1.03, 1.03, 1.03);
  }

  80% {
    -webkit-transform: scale3d(0.97, 0.97, 0.97);
    transform: scale3d(0.97, 0.97, 0.97);
  }

  to {
    opacity: 1;
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

.bounceIn {
  -webkit-animation-duration: 0.75s;
  animation-duration: 0.75s;
  -webkit-animation-name: bounceIn;
  animation-name: bounceIn;
}

@-webkit-keyframes bounceInDown {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    -webkit-transform: translate3d(0, -3000px, 0);
    transform: translate3d(0, -3000px, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(0, 25px, 0);
    transform: translate3d(0, 25px, 0);
  }

  75% {
    -webkit-transform: translate3d(0, -10px, 0);
    transform: translate3d(0, -10px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, 5px, 0);
    transform: translate3d(0, 5px, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes bounceInDown {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    -webkit-transform: translate3d(0, -3000px, 0);
    transform: translate3d(0, -3000px, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(0, 25px, 0);
    transform: translate3d(0, 25px, 0);
  }

  75% {
    -webkit-transform: translate3d(0, -10px, 0);
    transform: translate3d(0, -10px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, 5px, 0);
    transform: translate3d(0, 5px, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.bounceInDown {
  -webkit-animation-name: bounceInDown;
  animation-name: bounceInDown;
}

@-webkit-keyframes bounceInLeft {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    -webkit-transform: translate3d(-3000px, 0, 0);
    transform: translate3d(-3000px, 0, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(25px, 0, 0);
    transform: translate3d(25px, 0, 0);
  }

  75% {
    -webkit-transform: translate3d(-10px, 0, 0);
    transform: translate3d(-10px, 0, 0);
  }

  90% {
    -webkit-transform: translate3d(5px, 0, 0);
    transform: translate3d(5px, 0, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes bounceInLeft {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  0% {
    opacity: 0;
    -webkit-transform: translate3d(-3000px, 0, 0);
    transform: translate3d(-3000px, 0, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(25px, 0, 0);
    transform: translate3d(25px, 0, 0);
  }

  75% {
    -webkit-transform: translate3d(-10px, 0, 0);
    transform: translate3d(-10px, 0, 0);
  }

  90% {
    -webkit-transform: translate3d(5px, 0, 0);
    transform: translate3d(5px, 0, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.bounceInLeft {
  -webkit-animation-name: bounceInLeft;
  animation-name: bounceInLeft;
}

@-webkit-keyframes bounceInRight {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  from {
    opacity: 0;
    -webkit-transform: translate3d(3000px, 0, 0);
    transform: translate3d(3000px, 0, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(-25px, 0, 0);
    transform: translate3d(-25px, 0, 0);
  }

  75% {
    -webkit-transform: translate3d(10px, 0, 0);
    transform: translate3d(10px, 0, 0);
  }

  90% {
    -webkit-transform: translate3d(-5px, 0, 0);
    transform: translate3d(-5px, 0, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes bounceInRight {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  from {
    opacity: 0;
    -webkit-transform: translate3d(3000px, 0, 0);
    transform: translate3d(3000px, 0, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(-25px, 0, 0);
    transform: translate3d(-25px, 0, 0);
  }

  75% {
    -webkit-transform: translate3d(10px, 0, 0);
    transform: translate3d(10px, 0, 0);
  }

  90% {
    -webkit-transform: translate3d(-5px, 0, 0);
    transform: translate3d(-5px, 0, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.bounceInRight {
  -webkit-animation-name: bounceInRight;
  animation-name: bounceInRight;
}

@-webkit-keyframes bounceInUp {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 3000px, 0);
    transform: translate3d(0, 3000px, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(0, -20px, 0);
    transform: translate3d(0, -20px, 0);
  }

  75% {
    -webkit-transform: translate3d(0, 10px, 0);
    transform: translate3d(0, 10px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, -5px, 0);
    transform: translate3d(0, -5px, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes bounceInUp {
  from,
  60%,
  75%,
  90%,
  to {
    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 3000px, 0);
    transform: translate3d(0, 3000px, 0);
  }

  60% {
    opacity: 1;
    -webkit-transform: translate3d(0, -20px, 0);
    transform: translate3d(0, -20px, 0);
  }

  75% {
    -webkit-transform: translate3d(0, 10px, 0);
    transform: translate3d(0, 10px, 0);
  }

  90% {
    -webkit-transform: translate3d(0, -5px, 0);
    transform: translate3d(0, -5px, 0);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.bounceInUp {
  -webkit-animation-name: bounceInUp;
  animation-name: bounceInUp;
}

@-webkit-keyframes bounceOut {
  20% {
    -webkit-transform: scale3d(0.9, 0.9, 0.9);
    transform: scale3d(0.9, 0.9, 0.9);
  }

  50%,
  55% {
    opacity: 1;
    -webkit-transform: scale3d(1.1, 1.1, 1.1);
    transform: scale3d(1.1, 1.1, 1.1);
  }

  to {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }
}

@keyframes bounceOut {
  20% {
    -webkit-transform: scale3d(0.9, 0.9, 0.9);
    transform: scale3d(0.9, 0.9, 0.9);
  }

  50%,
  55% {
    opacity: 1;
    -webkit-transform: scale3d(1.1, 1.1, 1.1);
    transform: scale3d(1.1, 1.1, 1.1);
  }

  to {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }
}

.bounceOut {
  -webkit-animation-duration: 0.75s;
  animation-duration: 0.75s;
  -webkit-animation-name: bounceOut;
  animation-name: bounceOut;
}

@-webkit-keyframes bounceOutDown {
  20% {
    -webkit-transform: translate3d(0, 10px, 0);
    transform: translate3d(0, 10px, 0);
  }

  40%,
  45% {
    opacity: 1;
    -webkit-transform: translate3d(0, -20px, 0);
    transform: translate3d(0, -20px, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, 2000px, 0);
    transform: translate3d(0, 2000px, 0);
  }
}

@keyframes bounceOutDown {
  20% {
    -webkit-transform: translate3d(0, 10px, 0);
    transform: translate3d(0, 10px, 0);
  }

  40%,
  45% {
    opacity: 1;
    -webkit-transform: translate3d(0, -20px, 0);
    transform: translate3d(0, -20px, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, 2000px, 0);
    transform: translate3d(0, 2000px, 0);
  }
}

.bounceOutDown {
  -webkit-animation-name: bounceOutDown;
  animation-name: bounceOutDown;
}

@-webkit-keyframes bounceOutLeft {
  20% {
    opacity: 1;
    -webkit-transform: translate3d(20px, 0, 0);
    transform: translate3d(20px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(-2000px, 0, 0);
    transform: translate3d(-2000px, 0, 0);
  }
}

@keyframes bounceOutLeft {
  20% {
    opacity: 1;
    -webkit-transform: translate3d(20px, 0, 0);
    transform: translate3d(20px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(-2000px, 0, 0);
    transform: translate3d(-2000px, 0, 0);
  }
}

.bounceOutLeft {
  -webkit-animation-name: bounceOutLeft;
  animation-name: bounceOutLeft;
}

@-webkit-keyframes bounceOutRight {
  20% {
    opacity: 1;
    -webkit-transform: translate3d(-20px, 0, 0);
    transform: translate3d(-20px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(2000px, 0, 0);
    transform: translate3d(2000px, 0, 0);
  }
}

@keyframes bounceOutRight {
  20% {
    opacity: 1;
    -webkit-transform: translate3d(-20px, 0, 0);
    transform: translate3d(-20px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(2000px, 0, 0);
    transform: translate3d(2000px, 0, 0);
  }
}

.bounceOutRight {
  -webkit-animation-name: bounceOutRight;
  animation-name: bounceOutRight;
}

@-webkit-keyframes bounceOutUp {
  20% {
    -webkit-transform: translate3d(0, -10px, 0);
    transform: translate3d(0, -10px, 0);
  }

  40%,
  45% {
    opacity: 1;
    -webkit-transform: translate3d(0, 20px, 0);
    transform: translate3d(0, 20px, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, -2000px, 0);
    transform: translate3d(0, -2000px, 0);
  }
}

@keyframes bounceOutUp {
  20% {
    -webkit-transform: translate3d(0, -10px, 0);
    transform: translate3d(0, -10px, 0);
  }

  40%,
  45% {
    opacity: 1;
    -webkit-transform: translate3d(0, 20px, 0);
    transform: translate3d(0, 20px, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, -2000px, 0);
    transform: translate3d(0, -2000px, 0);
  }
}

.bounceOutUp {
  -webkit-animation-name: bounceOutUp;
  animation-name: bounceOutUp;
}

@-webkit-keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.fadeIn {
  -webkit-animation-name: fadeIn;
  animation-name: fadeIn;
}

@-webkit-keyframes fadeInDown {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInDown {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInDown {
  -webkit-animation-name: fadeInDown;
  animation-name: fadeInDown;
}

@-webkit-keyframes fadeInDownBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, -2000px, 0);
    transform: translate3d(0, -2000px, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInDownBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, -2000px, 0);
    transform: translate3d(0, -2000px, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInDownBig {
  -webkit-animation-name: fadeInDownBig;
  animation-name: fadeInDownBig;
}

@-webkit-keyframes fadeInLeft {
  from {
    opacity: 0;
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInLeft {
  from {
    opacity: 0;
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInLeft {
  -webkit-animation-name: fadeInLeft;
  animation-name: fadeInLeft;
}

@-webkit-keyframes fadeInLeftBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(-2000px, 0, 0);
    transform: translate3d(-2000px, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInLeftBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(-2000px, 0, 0);
    transform: translate3d(-2000px, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInLeftBig {
  -webkit-animation-name: fadeInLeftBig;
  animation-name: fadeInLeftBig;
}

@-webkit-keyframes fadeInRight {
  from {
    opacity: 0;
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInRight {
  from {
    opacity: 0;
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInRight {
  -webkit-animation-name: fadeInRight;
  animation-name: fadeInRight;
}

@-webkit-keyframes fadeInRightBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(2000px, 0, 0);
    transform: translate3d(2000px, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInRightBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(2000px, 0, 0);
    transform: translate3d(2000px, 0, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInRightBig {
  -webkit-animation-name: fadeInRightBig;
  animation-name: fadeInRightBig;
}

@-webkit-keyframes fadeInUp {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInUp {
  -webkit-animation-name: fadeInUp;
  animation-name: fadeInUp;
}

@-webkit-keyframes fadeInUpBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 2000px, 0);
    transform: translate3d(0, 2000px, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes fadeInUpBig {
  from {
    opacity: 0;
    -webkit-transform: translate3d(0, 2000px, 0);
    transform: translate3d(0, 2000px, 0);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.fadeInUpBig {
  -webkit-animation-name: fadeInUpBig;
  animation-name: fadeInUpBig;
}

@-webkit-keyframes fadeOut {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

.fadeOut {
  -webkit-animation-name: fadeOut;
  animation-name: fadeOut;
}

@-webkit-keyframes fadeOutDown {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }
}

@keyframes fadeOutDown {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }
}

.fadeOutDown {
  -webkit-animation-name: fadeOutDown;
  animation-name: fadeOutDown;
}

@-webkit-keyframes fadeOutDownBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, 2000px, 0);
    transform: translate3d(0, 2000px, 0);
  }
}

@keyframes fadeOutDownBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, 2000px, 0);
    transform: translate3d(0, 2000px, 0);
  }
}

.fadeOutDownBig {
  -webkit-animation-name: fadeOutDownBig;
  animation-name: fadeOutDownBig;
}

@-webkit-keyframes fadeOutLeft {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}

@keyframes fadeOutLeft {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}

.fadeOutLeft {
  -webkit-animation-name: fadeOutLeft;
  animation-name: fadeOutLeft;
}

@-webkit-keyframes fadeOutLeftBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(-2000px, 0, 0);
    transform: translate3d(-2000px, 0, 0);
  }
}

@keyframes fadeOutLeftBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(-2000px, 0, 0);
    transform: translate3d(-2000px, 0, 0);
  }
}

.fadeOutLeftBig {
  -webkit-animation-name: fadeOutLeftBig;
  animation-name: fadeOutLeftBig;
}

@-webkit-keyframes fadeOutRight {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
  }
}

@keyframes fadeOutRight {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
  }
}

.fadeOutRight {
  -webkit-animation-name: fadeOutRight;
  animation-name: fadeOutRight;
}

@-webkit-keyframes fadeOutRightBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(2000px, 0, 0);
    transform: translate3d(2000px, 0, 0);
  }
}

@keyframes fadeOutRightBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(2000px, 0, 0);
    transform: translate3d(2000px, 0, 0);
  }
}

.fadeOutRightBig {
  -webkit-animation-name: fadeOutRightBig;
  animation-name: fadeOutRightBig;
}

@-webkit-keyframes fadeOutUp {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
  }
}

@keyframes fadeOutUp {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
  }
}

.fadeOutUp {
  -webkit-animation-name: fadeOutUp;
  animation-name: fadeOutUp;
}

@-webkit-keyframes fadeOutUpBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, -2000px, 0);
    transform: translate3d(0, -2000px, 0);
  }
}

@keyframes fadeOutUpBig {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(0, -2000px, 0);
    transform: translate3d(0, -2000px, 0);
  }
}

.fadeOutUpBig {
  -webkit-animation-name: fadeOutUpBig;
  animation-name: fadeOutUpBig;
}

@-webkit-keyframes flip {
  from {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, -360deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }

  40% {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -190deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -190deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }

  50% {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -170deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -170deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  80% {
    -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, 0deg);
    transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, 0deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  to {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, 0deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
}

@keyframes flip {
  from {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, -360deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }

  40% {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -190deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -190deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }

  50% {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -170deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px)
      rotate3d(0, 1, 0, -170deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  80% {
    -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, 0deg);
    transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, 0deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  to {
    -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0)
      rotate3d(0, 1, 0, 0deg);
    transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
}

.animated.flip {
  -webkit-backface-visibility: visible;
  backface-visibility: visible;
  -webkit-animation-name: flip;
  animation-name: flip;
}

@-webkit-keyframes flipInX {
  from {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0;
  }

  40% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  60% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    opacity: 1;
  }

  80% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
  }

  to {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }
}

@keyframes flipInX {
  from {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0;
  }

  40% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  60% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    opacity: 1;
  }

  80% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
  }

  to {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }
}

.flipInX {
  -webkit-backface-visibility: visible !important;
  backface-visibility: visible !important;
  -webkit-animation-name: flipInX;
  animation-name: flipInX;
}

@-webkit-keyframes flipInY {
  from {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0;
  }

  40% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  60% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
    transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
    opacity: 1;
  }

  80% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
  }

  to {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }
}

@keyframes flipInY {
  from {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0;
  }

  40% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }

  60% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
    transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
    opacity: 1;
  }

  80% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
  }

  to {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }
}

.flipInY {
  -webkit-backface-visibility: visible !important;
  backface-visibility: visible !important;
  -webkit-animation-name: flipInY;
  animation-name: flipInY;
}

@-webkit-keyframes flipOutX {
  from {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }

  30% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    opacity: 1;
  }

  to {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    opacity: 0;
  }
}

@keyframes flipOutX {
  from {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }

  30% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    opacity: 1;
  }

  to {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    opacity: 0;
  }
}

.flipOutX {
  -webkit-animation-duration: 0.75s;
  animation-duration: 0.75s;
  -webkit-animation-name: flipOutX;
  animation-name: flipOutX;
  -webkit-backface-visibility: visible !important;
  backface-visibility: visible !important;
}

@-webkit-keyframes flipOutY {
  from {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }

  30% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
    opacity: 1;
  }

  to {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    opacity: 0;
  }
}

@keyframes flipOutY {
  from {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
  }

  30% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
    opacity: 1;
  }

  to {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
    opacity: 0;
  }
}

.flipOutY {
  -webkit-animation-duration: 0.75s;
  animation-duration: 0.75s;
  -webkit-backface-visibility: visible !important;
  backface-visibility: visible !important;
  -webkit-animation-name: flipOutY;
  animation-name: flipOutY;
}

@-webkit-keyframes lightSpeedIn {
  from {
    -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg);
    transform: translate3d(100%, 0, 0) skewX(-30deg);
    opacity: 0;
  }

  60% {
    -webkit-transform: skewX(20deg);
    transform: skewX(20deg);
    opacity: 1;
  }

  80% {
    -webkit-transform: skewX(-5deg);
    transform: skewX(-5deg);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes lightSpeedIn {
  from {
    -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg);
    transform: translate3d(100%, 0, 0) skewX(-30deg);
    opacity: 0;
  }

  60% {
    -webkit-transform: skewX(20deg);
    transform: skewX(20deg);
    opacity: 1;
  }

  80% {
    -webkit-transform: skewX(-5deg);
    transform: skewX(-5deg);
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.lightSpeedIn {
  -webkit-animation-name: lightSpeedIn;
  animation-name: lightSpeedIn;
  -webkit-animation-timing-function: ease-out;
  animation-timing-function: ease-out;
}

@-webkit-keyframes lightSpeedOut {
  from {
    opacity: 1;
  }

  to {
    -webkit-transform: translate3d(100%, 0, 0) skewX(30deg);
    transform: translate3d(100%, 0, 0) skewX(30deg);
    opacity: 0;
  }
}

@keyframes lightSpeedOut {
  from {
    opacity: 1;
  }

  to {
    -webkit-transform: translate3d(100%, 0, 0) skewX(30deg);
    transform: translate3d(100%, 0, 0) skewX(30deg);
    opacity: 0;
  }
}

.lightSpeedOut {
  -webkit-animation-name: lightSpeedOut;
  animation-name: lightSpeedOut;
  -webkit-animation-timing-function: ease-in;
  animation-timing-function: ease-in;
}

@-webkit-keyframes rotateIn {
  from {
    -webkit-transform-origin: center;
    transform-origin: center;
    -webkit-transform: rotate3d(0, 0, 1, -200deg);
    transform: rotate3d(0, 0, 1, -200deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: center;
    transform-origin: center;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes rotateIn {
  from {
    -webkit-transform-origin: center;
    transform-origin: center;
    -webkit-transform: rotate3d(0, 0, 1, -200deg);
    transform: rotate3d(0, 0, 1, -200deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: center;
    transform-origin: center;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

.rotateIn {
  -webkit-animation-name: rotateIn;
  animation-name: rotateIn;
}

@-webkit-keyframes rotateInDownLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, -45deg);
    transform: rotate3d(0, 0, 1, -45deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes rotateInDownLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, -45deg);
    transform: rotate3d(0, 0, 1, -45deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

.rotateInDownLeft {
  -webkit-animation-name: rotateInDownLeft;
  animation-name: rotateInDownLeft;
}

@-webkit-keyframes rotateInDownRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, 45deg);
    transform: rotate3d(0, 0, 1, 45deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes rotateInDownRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, 45deg);
    transform: rotate3d(0, 0, 1, 45deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

.rotateInDownRight {
  -webkit-animation-name: rotateInDownRight;
  animation-name: rotateInDownRight;
}

@-webkit-keyframes rotateInUpLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, 45deg);
    transform: rotate3d(0, 0, 1, 45deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes rotateInUpLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, 45deg);
    transform: rotate3d(0, 0, 1, 45deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

.rotateInUpLeft {
  -webkit-animation-name: rotateInUpLeft;
  animation-name: rotateInUpLeft;
}

@-webkit-keyframes rotateInUpRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, -90deg);
    transform: rotate3d(0, 0, 1, -90deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

@keyframes rotateInUpRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, -90deg);
    transform: rotate3d(0, 0, 1, -90deg);
    opacity: 0;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    opacity: 1;
  }
}

.rotateInUpRight {
  -webkit-animation-name: rotateInUpRight;
  animation-name: rotateInUpRight;
}

@-webkit-keyframes rotateOut {
  from {
    -webkit-transform-origin: center;
    transform-origin: center;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: center;
    transform-origin: center;
    -webkit-transform: rotate3d(0, 0, 1, 200deg);
    transform: rotate3d(0, 0, 1, 200deg);
    opacity: 0;
  }
}

@keyframes rotateOut {
  from {
    -webkit-transform-origin: center;
    transform-origin: center;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: center;
    transform-origin: center;
    -webkit-transform: rotate3d(0, 0, 1, 200deg);
    transform: rotate3d(0, 0, 1, 200deg);
    opacity: 0;
  }
}

.rotateOut {
  -webkit-animation-name: rotateOut;
  animation-name: rotateOut;
}

@-webkit-keyframes rotateOutDownLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, 45deg);
    transform: rotate3d(0, 0, 1, 45deg);
    opacity: 0;
  }
}

@keyframes rotateOutDownLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, 45deg);
    transform: rotate3d(0, 0, 1, 45deg);
    opacity: 0;
  }
}

.rotateOutDownLeft {
  -webkit-animation-name: rotateOutDownLeft;
  animation-name: rotateOutDownLeft;
}

@-webkit-keyframes rotateOutDownRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, -45deg);
    transform: rotate3d(0, 0, 1, -45deg);
    opacity: 0;
  }
}

@keyframes rotateOutDownRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, -45deg);
    transform: rotate3d(0, 0, 1, -45deg);
    opacity: 0;
  }
}

.rotateOutDownRight {
  -webkit-animation-name: rotateOutDownRight;
  animation-name: rotateOutDownRight;
}

@-webkit-keyframes rotateOutUpLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, -45deg);
    transform: rotate3d(0, 0, 1, -45deg);
    opacity: 0;
  }
}

@keyframes rotateOutUpLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, -45deg);
    transform: rotate3d(0, 0, 1, -45deg);
    opacity: 0;
  }
}

.rotateOutUpLeft {
  -webkit-animation-name: rotateOutUpLeft;
  animation-name: rotateOutUpLeft;
}

@-webkit-keyframes rotateOutUpRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, 90deg);
    transform: rotate3d(0, 0, 1, 90deg);
    opacity: 0;
  }
}

@keyframes rotateOutUpRight {
  from {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    opacity: 1;
  }

  to {
    -webkit-transform-origin: right bottom;
    transform-origin: right bottom;
    -webkit-transform: rotate3d(0, 0, 1, 90deg);
    transform: rotate3d(0, 0, 1, 90deg);
    opacity: 0;
  }
}

.rotateOutUpRight {
  -webkit-animation-name: rotateOutUpRight;
  animation-name: rotateOutUpRight;
}

@-webkit-keyframes hinge {
  0% {
    -webkit-transform-origin: top left;
    transform-origin: top left;
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
  }

  20%,
  60% {
    -webkit-transform: rotate3d(0, 0, 1, 80deg);
    transform: rotate3d(0, 0, 1, 80deg);
    -webkit-transform-origin: top left;
    transform-origin: top left;
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
  }

  40%,
  80% {
    -webkit-transform: rotate3d(0, 0, 1, 60deg);
    transform: rotate3d(0, 0, 1, 60deg);
    -webkit-transform-origin: top left;
    transform-origin: top left;
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
    opacity: 1;
  }

  to {
    -webkit-transform: translate3d(0, 700px, 0);
    transform: translate3d(0, 700px, 0);
    opacity: 0;
  }
}

@keyframes hinge {
  0% {
    -webkit-transform-origin: top left;
    transform-origin: top left;
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
  }

  20%,
  60% {
    -webkit-transform: rotate3d(0, 0, 1, 80deg);
    transform: rotate3d(0, 0, 1, 80deg);
    -webkit-transform-origin: top left;
    transform-origin: top left;
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
  }

  40%,
  80% {
    -webkit-transform: rotate3d(0, 0, 1, 60deg);
    transform: rotate3d(0, 0, 1, 60deg);
    -webkit-transform-origin: top left;
    transform-origin: top left;
    -webkit-animation-timing-function: ease-in-out;
    animation-timing-function: ease-in-out;
    opacity: 1;
  }

  to {
    -webkit-transform: translate3d(0, 700px, 0);
    transform: translate3d(0, 700px, 0);
    opacity: 0;
  }
}

.hinge {
  -webkit-animation-duration: 2s;
  animation-duration: 2s;
  -webkit-animation-name: hinge;
  animation-name: hinge;
}

@-webkit-keyframes jackInTheBox {
  from {
    opacity: 0;
    -webkit-transform: scale(0.1) rotate(30deg);
    transform: scale(0.1) rotate(30deg);
    -webkit-transform-origin: center bottom;
    transform-origin: center bottom;
  }

  50% {
    -webkit-transform: rotate(-10deg);
    transform: rotate(-10deg);
  }

  70% {
    -webkit-transform: rotate(3deg);
    transform: rotate(3deg);
  }

  to {
    opacity: 1;
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}

@keyframes jackInTheBox {
  from {
    opacity: 0;
    -webkit-transform: scale(0.1) rotate(30deg);
    transform: scale(0.1) rotate(30deg);
    -webkit-transform-origin: center bottom;
    transform-origin: center bottom;
  }

  50% {
    -webkit-transform: rotate(-10deg);
    transform: rotate(-10deg);
  }

  70% {
    -webkit-transform: rotate(3deg);
    transform: rotate(3deg);
  }

  to {
    opacity: 1;
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}

.jackInTheBox {
  -webkit-animation-name: jackInTheBox;
  animation-name: jackInTheBox;
}

/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */

@-webkit-keyframes rollIn {
  from {
    opacity: 0;
    -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
    transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes rollIn {
  from {
    opacity: 0;
    -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
    transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
  }

  to {
    opacity: 1;
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.rollIn {
  -webkit-animation-name: rollIn;
  animation-name: rollIn;
}

/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */

@-webkit-keyframes rollOut {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
    transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
  }
}

@keyframes rollOut {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
    -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
    transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
  }
}

.rollOut {
  -webkit-animation-name: rollOut;
  animation-name: rollOut;
}

@-webkit-keyframes zoomIn {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }

  50% {
    opacity: 1;
  }
}

@keyframes zoomIn {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }

  50% {
    opacity: 1;
  }
}

.zoomIn {
  -webkit-animation-name: zoomIn;
  animation-name: zoomIn;
}

@-webkit-keyframes zoomInDown {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

@keyframes zoomInDown {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

.zoomInDown {
  -webkit-animation-name: zoomInDown;
  animation-name: zoomInDown;
}

@-webkit-keyframes zoomInLeft {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

@keyframes zoomInLeft {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

.zoomInLeft {
  -webkit-animation-name: zoomInLeft;
  animation-name: zoomInLeft;
}

@-webkit-keyframes zoomInRight {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

@keyframes zoomInRight {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

.zoomInRight {
  -webkit-animation-name: zoomInRight;
  animation-name: zoomInRight;
}

@-webkit-keyframes zoomInUp {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

@keyframes zoomInUp {
  from {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  60% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

.zoomInUp {
  -webkit-animation-name: zoomInUp;
  animation-name: zoomInUp;
}

@-webkit-keyframes zoomOut {
  from {
    opacity: 1;
  }

  50% {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }

  to {
    opacity: 0;
  }
}

@keyframes zoomOut {
  from {
    opacity: 1;
  }

  50% {
    opacity: 0;
    -webkit-transform: scale3d(0.3, 0.3, 0.3);
    transform: scale3d(0.3, 0.3, 0.3);
  }

  to {
    opacity: 0;
  }
}

.zoomOut {
  -webkit-animation-name: zoomOut;
  animation-name: zoomOut;
}

@-webkit-keyframes zoomOutDown {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  to {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
    -webkit-transform-origin: center bottom;
    transform-origin: center bottom;
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

@keyframes zoomOutDown {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  to {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
    -webkit-transform-origin: center bottom;
    transform-origin: center bottom;
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

.zoomOutDown {
  -webkit-animation-name: zoomOutDown;
  animation-name: zoomOutDown;
}

@-webkit-keyframes zoomOutLeft {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0);
    transform: scale(0.1) translate3d(-2000px, 0, 0);
    -webkit-transform-origin: left center;
    transform-origin: left center;
  }
}

@keyframes zoomOutLeft {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0);
    transform: scale(0.1) translate3d(-2000px, 0, 0);
    -webkit-transform-origin: left center;
    transform-origin: left center;
  }
}

.zoomOutLeft {
  -webkit-animation-name: zoomOutLeft;
  animation-name: zoomOutLeft;
}

@-webkit-keyframes zoomOutRight {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: scale(0.1) translate3d(2000px, 0, 0);
    transform: scale(0.1) translate3d(2000px, 0, 0);
    -webkit-transform-origin: right center;
    transform-origin: right center;
  }
}

@keyframes zoomOutRight {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
  }

  to {
    opacity: 0;
    -webkit-transform: scale(0.1) translate3d(2000px, 0, 0);
    transform: scale(0.1) translate3d(2000px, 0, 0);
    -webkit-transform-origin: right center;
    transform-origin: right center;
  }
}

.zoomOutRight {
  -webkit-animation-name: zoomOutRight;
  animation-name: zoomOutRight;
}

@-webkit-keyframes zoomOutUp {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  to {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
    -webkit-transform-origin: center bottom;
    transform-origin: center bottom;
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

@keyframes zoomOutUp {
  40% {
    opacity: 1;
    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  }

  to {
    opacity: 0;
    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
    -webkit-transform-origin: center bottom;
    transform-origin: center bottom;
    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
  }
}

.zoomOutUp {
  -webkit-animation-name: zoomOutUp;
  animation-name: zoomOutUp;
}

@-webkit-keyframes slideInDown {
  from {
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes slideInDown {
  from {
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.slideInDown {
  -webkit-animation-name: slideInDown;
  animation-name: slideInDown;
}

@-webkit-keyframes slideInLeft {
  from {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes slideInLeft {
  from {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.slideInLeft {
  -webkit-animation-name: slideInLeft;
  animation-name: slideInLeft;
}

@-webkit-keyframes slideInRight {
  from {
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes slideInRight {
  from {
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.slideInRight {
  -webkit-animation-name: slideInRight;
  animation-name: slideInRight;
}

@-webkit-keyframes slideInUp {
  from {
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

@keyframes slideInUp {
  from {
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
    visibility: visible;
  }

  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}

.slideInUp {
  -webkit-animation-name: slideInUp;
  animation-name: slideInUp;
}

@-webkit-keyframes slideOutDown {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }
}

@keyframes slideOutDown {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(0, 100%, 0);
    transform: translate3d(0, 100%, 0);
  }
}

.slideOutDown {
  -webkit-animation-name: slideOutDown;
  animation-name: slideOutDown;
}

@-webkit-keyframes slideOutLeft {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}

@keyframes slideOutLeft {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
  }
}

.slideOutLeft {
  -webkit-animation-name: slideOutLeft;
  animation-name: slideOutLeft;
}

@-webkit-keyframes slideOutRight {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
  }
}

@keyframes slideOutRight {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
  }
}

.slideOutRight {
  -webkit-animation-name: slideOutRight;
  animation-name: slideOutRight;
}

@-webkit-keyframes slideOutUp {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
  }
}

@keyframes slideOutUp {
  from {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }

  to {
    visibility: hidden;
    -webkit-transform: translate3d(0, -100%, 0);
    transform: translate3d(0, -100%, 0);
  }
}

.slideOutUp {
  -webkit-animation-name: slideOutUp;
  animation-name: slideOutUp;
}

.animated {
  -webkit-animation-duration: 1s;
  animation-duration: 1s;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
}

.animated.infinite {
  -webkit-animation-iteration-count: infinite;
  animation-iteration-count: infinite;
}

.animated.delay-1s {
  -webkit-animation-delay: 1s;
  animation-delay: 1s;
}

.animated.delay-2s {
  -webkit-animation-delay: 2s;
  animation-delay: 2s;
}

.animated.delay-3s {
  -webkit-animation-delay: 3s;
  animation-delay: 3s;
}

.animated.delay-4s {
  -webkit-animation-delay: 4s;
  animation-delay: 4s;
}

.animated.delay-5s {
  -webkit-animation-delay: 5s;
  animation-delay: 5s;
}

.animated.fast {
  -webkit-animation-duration: 800ms;
  animation-duration: 800ms;
}

.animated.faster {
  -webkit-animation-duration: 500ms;
  animation-duration: 500ms;
}

.animated.slow {
  -webkit-animation-duration: 2s;
  animation-duration: 2s;
}

.animated.slower {
  -webkit-animation-duration: 3s;
  animation-duration: 3s;
}

@media (prefers-reduced-motion) {
  .animated {
    -webkit-animation: unset !important;
    animation: unset !important;
    -webkit-transition: none !important;
    transition: none !important;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\base-theme.scss

// Import all the theming functionality.
// We can use relative imports for imports from the cdk because we bundle everything
// up into a single flat scss file for material.
// We want overlays to always appear over user content, so set a baseline
// very high z-index for the overlay container, which is where we create the new
// stacking context for all overlays.
$cdk-z-index-overlay-container: 1000 !default;
$cdk-z-index-overlay: 1000 !default;
$cdk-z-index-overlay-backdrop: 1000 !default;

// Background color for all of the backdrops
$cdk-overlay-dark-backdrop-background: rgba(0, 0, 0, 0.32) !default;

// Default backdrop animation is based on the Material Design swift-ease-out.
$backdrop-animation-duration: 400ms !default;
$backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;


@mixin cdk-overlay() {
  .cdk-overlay-container, .cdk-global-overlay-wrapper {
    // Disable events from being captured on the overlay container.
    pointer-events: none;

    // The container should be the size of the viewport.
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
  }

  // The overlay-container is an invisible element which contains all individual overlays.
  .cdk-overlay-container {
    position: fixed;
    z-index: $cdk-z-index-overlay-container;

    &:empty {
      // Hide the element when it doesn't have any child nodes. This doesn't
      // include overlays that have been detached, rather than disposed.
      display: none;
    }
  }

  // We use an extra wrapper element in order to use make the overlay itself a flex item.
  // This makes centering the overlay easy without running into the subpixel rendering
  // problems tied to using `transform` and without interfering with the other position
  // strategies.
  .cdk-global-overlay-wrapper {
    display: flex;
    position: absolute;
    z-index: $cdk-z-index-overlay;
  }

  // A single overlay pane.
  .cdk-overlay-pane {
    // Note: it's important for this one to start off `absolute`,
    // in order for us to be able to measure it correctly.
    position: absolute;
    pointer-events: auto;
    box-sizing: border-box;
    z-index: $cdk-z-index-overlay;

    // For connected-position overlays, we set `display: flex` in
    // order to force `max-width` and `max-height` to take effect.
    display: flex;
    max-width: 100%;
    max-height: 100%;
  }

  .cdk-overlay-backdrop {
    // TODO(jelbourn): reuse sidenav fullscreen mixin.
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;

    z-index: $cdk-z-index-overlay-backdrop;
    pointer-events: auto;
    -webkit-tap-highlight-color: transparent;
    transition: opacity $backdrop-animation-duration $backdrop-animation-timing-function;
    opacity: 0;

    &.cdk-overlay-backdrop-showing {
      opacity: 1;

      // In high contrast mode the rgba background will become solid so we need to fall back
      // to making it opaque using `opacity`. Note that we can't use the `cdk-high-contrast`
      // mixin, because we can't normalize the import path to the _a11y.scss both for the
      // source and when this file is distributed. See #10908.
      @media screen and (-ms-high-contrast: active) {
        opacity: 0.6;
      }
    }
  }

  .cdk-overlay-dark-backdrop {
    background: $cdk-overlay-dark-backdrop-background;
  }

  .cdk-overlay-transparent-backdrop {
    // Note: as of Firefox 57, having the backdrop be `background: none` will prevent it from
    // capturing the user's mouse scroll events. Since we also can't use something like
    // `rgba(0, 0, 0, 0)`, we work around the inconsistency by not setting the background at
    // all and using `opacity` to make the element transparent.
    &, &.cdk-overlay-backdrop-showing {
      opacity: 0;
    }
  }

  // Overlay parent element used with the connected position strategy. Used to constrain the
  // overlay element's size to fit within the viewport.
  .cdk-overlay-connected-position-bounding-box {
    position: absolute;
    z-index: $cdk-z-index-overlay;

    // We use `display: flex` on this element exclusively for centering connected overlays.
    // When *not* centering, a top/left/bottom/right will be set which overrides the normal
    // flex layout.
    display: flex;

    // We use the `column` direction here to avoid some flexbox issues in Edge
    // when using the "grow after open" options.
    flex-direction: column;

    // Add some dimensions so the element has an `innerText` which some people depend on in tests.
    min-width: 1px;
    min-height: 1px;
  }

  // Used when disabling global scrolling.
  .cdk-global-scrollblock {
    position: fixed;

    // Necessary for the content not to lose its width. Note that we're using 100%, instead of
    // 100vw, because 100vw includes the width plus the scrollbar, whereas 100% is the width
    // that the element had before we made it `fixed`.
    width: 100%;

    // Note: this will always add a scrollbar to whatever element it is on, which can
    // potentially result in double scrollbars. It shouldn't be an issue, because we won't
    // block scrolling on a page that doesn't have a scrollbar in the first place.
    overflow-y: scroll;
  }
}

@mixin cdk-a11y {
  .cdk-visually-hidden {
    border: 0;
    clip: rect(0 0 0 0);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;

    // Avoid browsers rendering the focus ring in some cases.
    outline: 0;

    // Avoid some cases where the browser will still render the native controls (see #9049).
    -webkit-appearance: none;
    -moz-appearance: none;
  }
}

// Applies styles for users in high contrast mode. Note that this only applies
// to Microsoft browsers. Chrome can be included by checking for the `html[hc]`
// attribute, however Chrome handles high contrast differently.
//
// @param target Which kind of high contrast setting to target. Defaults to `active`, can be
//    `white-on-black` or `black-on-white`.
@mixin cdk-high-contrast($target: active) {
  @media (-ms-high-contrast: $target) {
    @content;
  }
}

// Core styles that enable monitoring autofill state of text fields.
@mixin cdk-text-field {
  // Keyframes that apply no styles, but allow us to monitor when an text field becomes autofilled
  // by watching for the animation events that are fired when they start. Note: the /*!*/ comment is
  // needed to prevent LibSass from stripping the keyframes out.
  // Based on: https://medium.com/@brunn/detecting-autofilled-fields-in-javascript-aed598d25da7
  @keyframes cdk-text-field-autofill-start {/*!*/}
  @keyframes cdk-text-field-autofill-end {/*!*/}

  .cdk-text-field-autofill-monitored:-webkit-autofill {
    animation-name: cdk-text-field-autofill-start;
  }

  .cdk-text-field-autofill-monitored:not(:-webkit-autofill) {
    animation-name: cdk-text-field-autofill-end;
  }

  // Remove the resize handle on autosizing textareas, because whatever height
  // the user resized to will be overwritten once they start typing again.
  textarea.cdk-textarea-autosize {
    resize: none;
  }

  // This class is temporarily applied to the textarea when it is being measured. It is immediately
  // removed when measuring is complete. We use `!important` rules here to make sure user-specified
  // rules do not interfere with the measurement.
  textarea.cdk-textarea-autosize-measuring {
    height: auto !important;
    overflow: hidden !important;
    // Having 2px top and bottom padding seems to fix a bug where Chrome gets an incorrect
    // measurement. We just have to account for it later and subtract it off the final result.
    padding: 2px 0 !important;
    box-sizing: content-box !important;
  }
}

// Used to generate UIDs for keyframes used to change the text field autofill styles.
$cdk-text-field-autofill-color-frame-count: 0;

// Mixin used to apply custom background and foreground colors to an autofilled text field.
// Based on: https://stackoverflow.com/questions/2781549/
// removing-input-background-colour-for-chrome-autocomplete#answer-37432260
@mixin cdk-text-field-autofill-color($background, $foreground:'') {
  @keyframes cdk-text-field-autofill-color-#{$cdk-text-field-autofill-color-frame-count} {
    to {
      background: $background;
      @if $foreground != '' { color: $foreground; }
    }
  }

  &:-webkit-autofill {
    animation-name: cdk-text-field-autofill-color-#{$cdk-text-field-autofill-color-frame-count};
    animation-fill-mode: both;
  }

  &.cdk-text-field-autofill-monitored:-webkit-autofill {
    animation-name: cdk-text-field-autofill-start,
                    cdk-text-field-autofill-color-#{$cdk-text-field-autofill-color-frame-count};
  }

  $cdk-text-field-autofill-color-frame-count:
      $cdk-text-field-autofill-color-frame-count + 1 !global;
}


// Core styles that can be used to apply material design treatments to any element.
// Media queries
// TODO(josephperrott): Change $mat-xsmall and $mat-small usages to rely on BreakpointObserver,
$mat-xsmall: 'max-width: 599px';
$mat-small: 'max-width: 959px';

// TODO: Revisit all z-indices before beta
// z-index master list

$z-index-fab: 20 !default;
$z-index-drawer: 100 !default;

// Global constants
$pi: 3.14159265;

// Padding between input toggles and their labels
$mat-toggle-padding: 8px !default;
// Width and height of input toggles
$mat-toggle-size: 20px !default;

// Easing Curves
// TODO(jelbourn): all of these need to be revisited

// The default animation curves used by material design.
$mat-linear-out-slow-in-timing-function: cubic-bezier(0, 0, 0.2, 0.1) !default;
$mat-fast-out-slow-in-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !default;
$mat-fast-out-linear-in-timing-function: cubic-bezier(0.4, 0, 1, 1) !default;

$ease-in-out-curve-function: cubic-bezier(0.35, 0, 0.25, 1) !default;

$swift-ease-out-duration: 400ms !default;
$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default;

$swift-ease-in-duration: 300ms !default;
$swift-ease-in-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2) !default;
$swift-ease-in: all $swift-ease-in-duration $swift-ease-in-timing-function !default;

$swift-ease-in-out-duration: 500ms !default;
$swift-ease-in-out-timing-function: $ease-in-out-curve-function !default;
$swift-ease-in-out: all $swift-ease-in-out-duration $swift-ease-in-out-timing-function !default;

$swift-linear-duration: 80ms !default;
$swift-linear-timing-function: linear !default;
$swift-linear: all $swift-linear-duration $swift-linear-timing-function !default;



// A collection of mixins and CSS classes that can be used to apply elevation to a material
// element.
// See: https://material.io/design/environment/elevation.html
// Examples:
//
//
// .mat-foo {
//   @include $mat-elevation(2);
//
//   &:active {
//     @include $mat-elevation(8);
//   }
// }
//
// <div id="external-card" class="mat-elevation-z2"><p>Some content</p></div>
//
// For an explanation of the design behind how elevation is implemented, see the design doc at
// https://goo.gl/Kq0k9Z.

// Colors for umbra, penumbra, and ambient shadows. As described in the design doc, each elevation
// level is created using a set of 3 shadow values, one for umbra (the shadow representing the
// space completely obscured by an object relative to its light source), one for penumbra (the
// space partially obscured by an object), and one for ambient (the space which contains the object
// itself). For a further explanation of these terms and their meanings, see
// https://en.wikipedia.org/wiki/Umbra,_penumbra_and_antumbra.

// Maps for the different shadow sets and their values within each z-space. These values were
// created by taking a few reference shadow sets created by Google's Designers and interpolating
// all of the values between them.

@function _get-umbra-map($color, $opacity) {
  $shadow-color: if(type-of($color) == color, rgba($color, $opacity * 0.2), $color);

  @return (
    0: '0px 0px 0px 0px #{$shadow-color}',
    1: '0px 2px 1px -1px #{$shadow-color}',
    2: '0px 3px 1px -2px #{$shadow-color}',
    3: '0px 3px 3px -2px #{$shadow-color}',
    4: '0px 2px 4px -1px #{$shadow-color}',
    5: '0px 3px 5px -1px #{$shadow-color}',
    6: '0px 3px 5px -1px #{$shadow-color}',
    7: '0px 4px 5px -2px #{$shadow-color}',
    8: '0px 5px 5px -3px #{$shadow-color}',
    9: '0px 5px 6px -3px #{$shadow-color}',
    10: '0px 6px 6px -3px #{$shadow-color}',
    11: '0px 6px 7px -4px #{$shadow-color}',
    12: '0px 7px 8px -4px #{$shadow-color}',
    13: '0px 7px 8px -4px #{$shadow-color}',
    14: '0px 7px 9px -4px #{$shadow-color}',
    15: '0px 8px 9px -5px #{$shadow-color}',
    16: '0px 8px 10px -5px #{$shadow-color}',
    17: '0px 8px 11px -5px #{$shadow-color}',
    18: '0px 9px 11px -5px #{$shadow-color}',
    19: '0px 9px 12px -6px #{$shadow-color}',
    20: '0px 10px 13px -6px #{$shadow-color}',
    21: '0px 10px 13px -6px #{$shadow-color}',
    22: '0px 10px 14px -6px #{$shadow-color}',
    23: '0px 11px 14px -7px #{$shadow-color}',
    24: '0px 11px 15px -7px #{$shadow-color}'
  );
}

@function _get-penumbra-map($color, $opacity) {
  $shadow-color: if(type-of($color) == color, rgba($color, $opacity * 0.14), $color);

  @return (
    0: '0px 0px 0px 0px #{$shadow-color}',
    1: '0px 1px 1px 0px #{$shadow-color}',
    2: '0px 2px 2px 0px #{$shadow-color}',
    3: '0px 3px 4px 0px #{$shadow-color}',
    4: '0px 4px 5px 0px #{$shadow-color}',
    5: '0px 5px 8px 0px #{$shadow-color}',
    6: '0px 6px 10px 0px #{$shadow-color}',
    7: '0px 7px 10px 1px #{$shadow-color}',
    8: '0px 8px 10px 1px #{$shadow-color}',
    9: '0px 9px 12px 1px #{$shadow-color}',
    10: '0px 10px 14px 1px #{$shadow-color}',
    11: '0px 11px 15px 1px #{$shadow-color}',
    12: '0px 12px 17px 2px #{$shadow-color}',
    13: '0px 13px 19px 2px #{$shadow-color}',
    14: '0px 14px 21px 2px #{$shadow-color}',
    15: '0px 15px 22px 2px #{$shadow-color}',
    16: '0px 16px 24px 2px #{$shadow-color}',
    17: '0px 17px 26px 2px #{$shadow-color}',
    18: '0px 18px 28px 2px #{$shadow-color}',
    19: '0px 19px 29px 2px #{$shadow-color}',
    20: '0px 20px 31px 3px #{$shadow-color}',
    21: '0px 21px 33px 3px #{$shadow-color}',
    22: '0px 22px 35px 3px #{$shadow-color}',
    23: '0px 23px 36px 3px #{$shadow-color}',
    24: '0px 24px 38px 3px #{$shadow-color}'
  );
}

@function _get-ambient-map($color, $opacity) {
  $shadow-color: if(type-of($color) == color, rgba($color, $opacity * 0.12), $color);

  @return (
    0: '0px 0px 0px 0px #{$shadow-color}',
    1: '0px 1px 3px 0px #{$shadow-color}',
    2: '0px 1px 5px 0px #{$shadow-color}',
    3: '0px 1px 8px 0px #{$shadow-color}',
    4: '0px 1px 10px 0px #{$shadow-color}',
    5: '0px 1px 14px 0px #{$shadow-color}',
    6: '0px 1px 18px 0px #{$shadow-color}',
    7: '0px 2px 16px 1px #{$shadow-color}',
    8: '0px 3px 14px 2px #{$shadow-color}',
    9: '0px 3px 16px 2px #{$shadow-color}',
    10: '0px 4px 18px 3px #{$shadow-color}',
    11: '0px 4px 20px 3px #{$shadow-color}',
    12: '0px 5px 22px 4px #{$shadow-color}',
    13: '0px 5px 24px 4px #{$shadow-color}',
    14: '0px 5px 26px 4px #{$shadow-color}',
    15: '0px 6px 28px 5px #{$shadow-color}',
    16: '0px 6px 30px 5px #{$shadow-color}',
    17: '0px 6px 32px 5px #{$shadow-color}',
    18: '0px 7px 34px 6px #{$shadow-color}',
    19: '0px 7px 36px 6px #{$shadow-color}',
    20: '0px 8px 38px 7px #{$shadow-color}',
    21: '0px 8px 40px 7px #{$shadow-color}',
    22: '0px 8px 42px 7px #{$shadow-color}',
    23: '0px 9px 44px 8px #{$shadow-color}',
    24: '0px 9px 46px 8px #{$shadow-color}'
  );
}

// The default duration value for elevation transitions.
$mat-elevation-transition-duration: 280ms !default;

// The default easing value for elevation transitions.
$mat-elevation-transition-timing-function: $mat-fast-out-slow-in-timing-function;

// The default color for elevation shadows.
$mat-elevation-color: black !default;

// The default opacity scaling value for elevation shadows.
$mat-elevation-opacity: 1 !default;

// Prefix for elevation-related selectors.
$_mat-elevation-prefix: 'mat-elevation-z';

// Applies the correct css rules to an element to give it the elevation specified by $zValue.
// The $zValue must be between 0 and 24.
@mixin mat-elevation($zValue, $color: $mat-elevation-color, $opacity: $mat-elevation-opacity) {
  @if type-of($zValue) != number or not unitless($zValue) {
    @error '$zValue must be a unitless number';
  }
  @if $zValue < 0 or $zValue > 24 {
    @error '$zValue must be between 0 and 24';
  }

  box-shadow: #{map-get(_get-umbra-map($color, $opacity), $zValue)},
              #{map-get(_get-penumbra-map($color, $opacity), $zValue)},
              #{map-get(_get-ambient-map($color, $opacity), $zValue)};
}

@mixin _mat-theme-elevation($zValue, $theme, $opacity: $mat-elevation-opacity) {
  $foreground: map-get($theme, foreground);
  $elevation-color: map-get($foreground, elevation);
  $elevation-color-or-default: if($elevation-color == null, $mat-elevation-color, $elevation-color);

  @include mat-elevation($zValue, $elevation-color-or-default, $opacity);
}

// Applies the elevation to an element in a manner that allows
// consumers to override it via the Material elevation classes.
@mixin mat-overridable-elevation(
    $zValue,
    $color: $mat-elevation-color,
    $opacity: $mat-elevation-opacity) {
  &:not([class*='#{$_mat-elevation-prefix}']) {
    @include mat-elevation($zValue, $color, $opacity);
  }
}

@mixin _mat-theme-overridable-elevation($zValue, $theme, $opacity: $mat-elevation-opacity) {
  $foreground: map-get($theme, foreground);
  $elevation-color: map-get($foreground, elevation);
  $elevation-color-or-default: if($elevation-color == null, $mat-elevation-color, $elevation-color);

  @include mat-overridable-elevation($zValue, $elevation-color-or-default, $opacity);
}

// Returns a string that can be used as the value for a transition property for elevation.
// Calling this function directly is useful in situations where a component needs to transition
// more than one property.
//
// .foo {
//   transition: mat-elevation-transition-property-value(), opacity 100ms ease;
// }
@function mat-elevation-transition-property-value(
    $duration: $mat-elevation-transition-duration,
    $easing: $mat-elevation-transition-timing-function) {
  @return box-shadow #{$duration} #{$easing};
}

// Applies the correct css rules needed to have an element transition between elevations.
// This mixin should be applied to elements whose elevation values will change depending on their
// context (e.g. when active or disabled).
//
// NOTE(traviskaufman): Both this mixin and the above function use default parameters so they can
// be used in the same way by clients.
@mixin mat-elevation-transition(
    $duration: $mat-elevation-transition-duration,
    $easing: $mat-elevation-transition-timing-function) {
  transition: mat-elevation-transition-property-value($duration, $easing);
}

// Color palettes from the Material Design spec.
// See https://material.io/design/color/
//
// Contrast colors are hard-coded because it is too difficult (probably impossible) to
// calculate them. These contrast colors are pulled from the public Material Design spec swatches.
// While the contrast colors in the spec are not prescriptive, we use them for convenience.


// @deprecated renamed to $dark-primary-text.
// @breaking-change 8.0.0
$black-87-opacity: rgba(black, 0.87);
// @deprecated renamed to $light-primary-text.
// @breaking-change 8.0.0
$white-87-opacity: rgba(white, 0.87);
// @deprecated use $dark-[secondary-text,disabled-text,dividers,focused] instead.
// @breaking-change 8.0.0
$black-12-opacity: rgba(black, 0.12);
// @deprecated use $light-[secondary-text,disabled-text,dividers,focused] instead.
// @breaking-change 8.0.0
$white-12-opacity: rgba(white, 0.12);
// @deprecated use $dark-[secondary-text,disabled-text,dividers,focused] instead.
// @breaking-change 8.0.0
$black-6-opacity: rgba(black, 0.06);
// @deprecated use $light-[secondary-text,disabled-text,dividers,focused] instead.
// @breaking-change 8.0.0
$white-6-opacity: rgba(white, 0.06);

$dark-primary-text: rgba(black, 0.87);
$dark-secondary-text: rgba(black, 0.54);
$dark-disabled-text: rgba(black, 0.38);
$dark-dividers: rgba(black, 0.12);
$dark-focused: rgba(black, 0.12);
$light-primary-text: white;
$light-secondary-text: rgba(white, 0.7);
$light-disabled-text: rgba(white, 0.5);
$light-dividers: rgba(white, 0.12);
$light-focused: rgba(white, 0.12);

$mat-red: (
  50: #ffebee,
  100: #ffcdd2,
  200: #ef9a9a,
  300: #e57373,
  400: #ef5350,
  500: #f44336,
  600: #e53935,
  700: #d32f2f,
  800: #c62828,
  900: #b71c1c,
  A100: #ff8a80,
  A200: #ff5252,
  A400: #ff1744,
  A700: #d50000,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $light-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-pink: (
  50: #fce4ec,
  100: #f8bbd0,
  200: #f48fb1,
  300: #f06292,
  400: #ec407a,
  500: #e91e63,
  600: #d81b60,
  700: #c2185b,
  800: #ad1457,
  900: #880e4f,
  A100: #ff80ab,
  A200: #ff4081,
  A400: #f50057,
  A700: #c51162,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $light-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-purple: (
  50: #f3e5f5,
  100: #e1bee7,
  200: #ce93d8,
  300: #ba68c8,
  400: #ab47bc,
  500: #9c27b0,
  600: #8e24aa,
  700: #7b1fa2,
  800: #6a1b9a,
  900: #4a148c,
  A100: #ea80fc,
  A200: #e040fb,
  A400: #d500f9,
  A700: #aa00ff,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $light-primary-text,
    400: $light-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $light-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-deep-purple: (
  50: #ede7f6,
  100: #d1c4e9,
  200: #b39ddb,
  300: #9575cd,
  400: #7e57c2,
  500: #673ab7,
  600: #5e35b1,
  700: #512da8,
  800: #4527a0,
  900: #311b92,
  A100: #b388ff,
  A200: #7c4dff,
  A400: #651fff,
  A700: #6200ea,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $light-primary-text,
    400: $light-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $light-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-indigo: (
  50: #e8eaf6,
  100: #c5cae9,
  200: #9fa8da,
  300: #7986cb,
  400: #5c6bc0,
  500: #3f51b5,
  600: #3949ab,
  700: #303f9f,
  800: #283593,
  900: #1a237e,
  A100: #8c9eff,
  A200: #536dfe,
  A400: #3d5afe,
  A700: #304ffe,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $light-primary-text,
    400: $light-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $light-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-blue: (
  50: #e3f2fd,
  100: #bbdefb,
  200: #90caf9,
  300: #64b5f6,
  400: #42a5f5,
  500: #2196f3,
  600: #1e88e5,
  700: #1976d2,
  800: #1565c0,
  900: #0d47a1,
  A100: #82b1ff,
  A200: #448aff,
  A400: #2979ff,
  A700: #2962ff,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $light-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-light-blue: (
  50: #e1f5fe,
  100: #b3e5fc,
  200: #81d4fa,
  300: #4fc3f7,
  400: #29b6f6,
  500: #03a9f4,
  600: #039be5,
  700: #0288d1,
  800: #0277bd,
  900: #01579b,
  A100: #80d8ff,
  A200: #40c4ff,
  A400: #00b0ff,
  A700: #0091ea,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $light-primary-text,
  )
);

$mat-cyan: (
  50: #e0f7fa,
  100: #b2ebf2,
  200: #80deea,
  300: #4dd0e1,
  400: #26c6da,
  500: #00bcd4,
  600: #00acc1,
  700: #0097a7,
  800: #00838f,
  900: #006064,
  A100: #84ffff,
  A200: #18ffff,
  A400: #00e5ff,
  A700: #00b8d4,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-teal: (
  50: #e0f2f1,
  100: #b2dfdb,
  200: #80cbc4,
  300: #4db6ac,
  400: #26a69a,
  500: #009688,
  600: #00897b,
  700: #00796b,
  800: #00695c,
  900: #004d40,
  A100: #a7ffeb,
  A200: #64ffda,
  A400: #1de9b6,
  A700: #00bfa5,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-green: (
  50: #e8f5e9,
  100: #c8e6c9,
  200: #a5d6a7,
  300: #81c784,
  400: #66bb6a,
  500: #4caf50,
  600: #43a047,
  700: #388e3c,
  800: #2e7d32,
  900: #1b5e20,
  A100: #b9f6ca,
  A200: #69f0ae,
  A400: #00e676,
  A700: #00c853,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-light-green: (
  50: #f1f8e9,
  100: #dcedc8,
  200: #c5e1a5,
  300: #aed581,
  400: #9ccc65,
  500: #8bc34a,
  600: #7cb342,
  700: #689f38,
  800: #558b2f,
  900: #33691e,
  A100: #ccff90,
  A200: #b2ff59,
  A400: #76ff03,
  A700: #64dd17,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $dark-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-lime: (
  50: #f9fbe7,
  100: #f0f4c3,
  200: #e6ee9c,
  300: #dce775,
  400: #d4e157,
  500: #cddc39,
  600: #c0ca33,
  700: #afb42b,
  800: #9e9d24,
  900: #827717,
  A100: #f4ff81,
  A200: #eeff41,
  A400: #c6ff00,
  A700: #aeea00,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $dark-primary-text,
    700: $dark-primary-text,
    800: $dark-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-yellow: (
  50: #fffde7,
  100: #fff9c4,
  200: #fff59d,
  300: #fff176,
  400: #ffee58,
  500: #ffeb3b,
  600: #fdd835,
  700: #fbc02d,
  800: #f9a825,
  900: #f57f17,
  A100: #ffff8d,
  A200: #ffff00,
  A400: #ffea00,
  A700: #ffd600,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $dark-primary-text,
    700: $dark-primary-text,
    800: $dark-primary-text,
    900: $dark-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-amber: (
  50: #fff8e1,
  100: #ffecb3,
  200: #ffe082,
  300: #ffd54f,
  400: #ffca28,
  500: #ffc107,
  600: #ffb300,
  700: #ffa000,
  800: #ff8f00,
  900: #ff6f00,
  A100: #ffe57f,
  A200: #ffd740,
  A400: #ffc400,
  A700: #ffab00,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $dark-primary-text,
    700: $dark-primary-text,
    800: $dark-primary-text,
    900: $dark-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $dark-primary-text,
  )
);

$mat-orange: (
  50: #fff3e0,
  100: #ffe0b2,
  200: #ffcc80,
  300: #ffb74d,
  400: #ffa726,
  500: #ff9800,
  600: #fb8c00,
  700: #f57c00,
  800: #ef6c00,
  900: #e65100,
  A100: #ffd180,
  A200: #ffab40,
  A400: #ff9100,
  A700: #ff6d00,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $dark-primary-text,
    700: $dark-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: black,
  )
);

$mat-deep-orange: (
  50: #fbe9e7,
  100: #ffccbc,
  200: #ffab91,
  300: #ff8a65,
  400: #ff7043,
  500: #ff5722,
  600: #f4511e,
  700: #e64a19,
  800: #d84315,
  900: #bf360c,
  A100: #ff9e80,
  A200: #ff6e40,
  A400: #ff3d00,
  A700: #dd2c00,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-brown: (
  50: #efebe9,
  100: #d7ccc8,
  200: #bcaaa4,
  300: #a1887f,
  400: #8d6e63,
  500: #795548,
  600: #6d4c41,
  700: #5d4037,
  800: #4e342e,
  900: #3e2723,
  A100: #d7ccc8,
  A200: #bcaaa4,
  A400: #8d6e63,
  A700: #5d4037,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $light-primary-text,
    400: $light-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

$mat-grey: (
  50: #fafafa,
  100: #f5f5f5,
  200: #eeeeee,
  300: #e0e0e0,
  400: #bdbdbd,
  500: #9e9e9e,
  600: #757575,
  700: #616161,
  800: #424242,
  900: #212121,
  A100: #ffffff,
  A200: #eeeeee,
  A400: #bdbdbd,
  A700: #616161,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $dark-primary-text,
    500: $dark-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $dark-primary-text,
    A700: $light-primary-text,
  )
);

// Alias for alternate spelling.
$mat-gray: $mat-grey;

$mat-blue-grey: (
  50: #eceff1,
  100: #cfd8dc,
  200: #b0bec5,
  300: #90a4ae,
  400: #78909c,
  500: #607d8b,
  600: #546e7a,
  700: #455a64,
  800: #37474f,
  900: #263238,
  A100: #cfd8dc,
  A200: #b0bec5,
  A400: #78909c,
  A700: #455a64,
  contrast: (
    50: $dark-primary-text,
    100: $dark-primary-text,
    200: $dark-primary-text,
    300: $dark-primary-text,
    400: $light-primary-text,
    500: $light-primary-text,
    600: $light-primary-text,
    700: $light-primary-text,
    800: $light-primary-text,
    900: $light-primary-text,
    A100: $dark-primary-text,
    A200: $dark-primary-text,
    A400: $light-primary-text,
    A700: $light-primary-text,
  )
);

// Alias for alternate spelling.
$mat-blue-gray: $mat-blue-grey;


// Background palette for light themes.
$mat-light-theme-background: (
  status-bar: map_get($mat-grey, 300),
  app-bar:    map_get($mat-grey, 100),
  background: map_get($mat-grey, 50),
  hover:      rgba(black, 0.04), // TODO(kara): check style with Material Design UX
  card:       white,
  dialog:     white,
  disabled-button: rgba(black, 0.12),
  raised-button: white,
  focused-button: $dark-focused,
  selected-button: map_get($mat-grey, 300),
  selected-disabled-button: map_get($mat-grey, 400),
  disabled-button-toggle: map_get($mat-grey, 200),
  unselected-chip: map_get($mat-grey, 300),
  disabled-list-option: map_get($mat-grey, 200),
);

// Background palette for dark themes.
$mat-dark-theme-background: (
  status-bar: black,
  app-bar:    map_get($mat-grey, 900),
  background: #303030,
  hover:      rgba(white, 0.04), // TODO(kara): check style with Material Design UX
  card:       map_get($mat-grey, 800),
  dialog:     map_get($mat-grey, 800),
  disabled-button: rgba(white, 0.12),
  raised-button: map-get($mat-grey, 800),
  focused-button: $light-focused,
  selected-button: map_get($mat-grey, 900),
  selected-disabled-button: map_get($mat-grey, 800),
  disabled-button-toggle: black,
  unselected-chip: map_get($mat-grey, 700),
  disabled-list-option: black,
);

// Foreground palette for light themes.
$mat-light-theme-foreground: (
  base:              black,
  divider:           $dark-dividers,
  dividers:          $dark-dividers,
  disabled:          $dark-disabled-text,
  disabled-button:   rgba(black, 0.26),
  disabled-text:     $dark-disabled-text,
  elevation:         black,
  hint-text:         $dark-disabled-text,
  secondary-text:    $dark-secondary-text,
  icon:              rgba(black, 0.54),
  icons:             rgba(black, 0.54),
  text:              rgba(black, 0.87),
  slider-min:        rgba(black, 0.87),
  slider-off:        rgba(black, 0.26),
  slider-off-active: rgba(black, 0.38),
);

// Foreground palette for dark themes.
$mat-dark-theme-foreground: (
  base:              white,
  divider:           $light-dividers,
  dividers:          $light-dividers,
  disabled:          $light-disabled-text,
  disabled-button:   rgba(white, 0.3),
  disabled-text:     $light-disabled-text,
  elevation:         black,
  hint-text:         $light-disabled-text,
  secondary-text:    $light-secondary-text,
  icon:              white,
  icons:             white,
  text:              white,
  slider-min:        white,
  slider-off:        rgba(white, 0.3),
  slider-off-active: rgba(white, 0.3),
);



// For a given hue in a palette, return the contrast color from the map of contrast palettes.
// @param $color-map
// @param $hue
@function mat-contrast($palette, $hue) {
  @return map-get(map-get($palette, contrast), $hue);
}


// Creates a map of hues to colors for a theme. This is used to define a theme palette in terms
// of the Material Design hues.
// @param $color-map
// @param $primary
// @param $lighter
@function mat-palette($base-palette, $default: 500, $lighter: 100, $darker: 700, $text: $default) {
  $result: map_merge($base-palette, (
    default: map-get($base-palette, $default),
    lighter: map-get($base-palette, $lighter),
    darker: map-get($base-palette, $darker),
    text: map-get($base-palette, $text),

    default-contrast: mat-contrast($base-palette, $default),
    lighter-contrast: mat-contrast($base-palette, $lighter),
    darker-contrast: mat-contrast($base-palette, $darker)
  ));

  // For each hue in the palette, add a "-contrast" color to the map.
  @each $hue, $color in $base-palette {
    $result: map_merge($result, (
      '#{$hue}-contrast': mat-contrast($base-palette, $hue)
    ));
  }

  @return $result;
}


// Gets a color from a theme palette (the output of mat-palette).
// The hue can be one of the standard values (500, A400, etc.), one of the three preconfigured
// hues (default, lighter, darker), or any of the aforementioned prefixed with "-contrast".
//
// @param $color-map The theme palette (output of mat-palette).
// @param $hue The hue from the palette to use. If this is a value between 0 and 1, it will
//     be treated as opacity.
// @param $opacity The alpha channel value for the color.
@function mat-color($palette, $hue: default, $opacity: null) {
  // If hueKey is a number between zero and one, then it actually contains an
  // opacity value, so recall this function with the default hue and that given opacity.
  @if type-of($hue) == number and $hue >= 0 and $hue <= 1 {
    @return mat-color($palette, default, $hue);
  }

  $color: map-get($palette, $hue);

  @if (type-of($color) != color) {
    // If the $color resolved to something different from a color (e.g. a CSS variable),
    // we can't apply the opacity anyway so we return the value as is, otherwise Sass can
    // throw an error or output something invalid.
    @return $color;
  }

  @return rgba($color, if($opacity == null, opacity($color), $opacity));
}


// Creates a container object for a light theme to be given to individual component theme mixins.
@function mat-light-theme($primary, $accent, $warn: mat-palette($mat-red)) {
  @return (
    primary: $primary,
    accent: $accent,
    warn: $warn,
    is-dark: false,
    foreground: $mat-light-theme-foreground,
    background: $mat-light-theme-background,
  );
}


// Creates a container object for a dark theme to be given to individual component theme mixins.
@function mat-dark-theme($primary, $accent, $warn: mat-palette($mat-red)) {
  @return (
    primary: $primary,
    accent: $accent,
    warn: $warn,
    is-dark: true,
    foreground: $mat-dark-theme-foreground,
    background: $mat-dark-theme-background,
  );
}



$mat-ripple-color-opacity: 0.1;

@mixin mat-ripple() {

  // The host element of an mat-ripple directive should always have a position of "absolute" or
  // "relative" so that the ripples inside are correctly positioned relatively to the container.
  .mat-ripple {
    overflow: hidden;

    // By default, every ripple container should have position: relative in favor of creating an
    // easy API for developers using the MatRipple directive.
    position: relative;
  }

  .mat-ripple.mat-ripple-unbounded {
    overflow: visible;
  }

  .mat-ripple-element {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;

    transition: opacity, transform 0ms cubic-bezier(0, 0, 0.2, 1);
    transform: scale(0);

    // In high contrast mode the ripple is opaque, causing it to obstruct the content.
    @include cdk-high-contrast {
      display: none;
    }
  }
}

/* Theme for the ripple elements.*/
@mixin mat-ripple-theme($theme) {
  $foreground: map_get($theme, foreground);
  $foreground-base: map_get($foreground, base);

  .mat-ripple-element {
    // If the ripple color is resolves to a color *type*, we can use it directly, otherwise
    // (e.g. it resolves to a CSS variable) we fall back to using the color and setting an opacity.
    @if (type-of($foreground-base) == color) {
      background-color: rgba($foreground-base, $mat-ripple-color-opacity);
    }
    @else {
      background-color: $foreground-base;
      opacity: $mat-ripple-color-opacity;
    }
  }
}



// Utility for fetching a nested value from a typography config.
@function _mat-get-type-value($config, $level, $name) {
  @return map-get(map-get($config, $level), $name);
}

// Gets the font size for a level inside a typography config.
@function mat-font-size($config, $level) {
  @return _mat-get-type-value($config, $level, font-size);
}

// Gets the line height for a level inside a typography config.
@function mat-line-height($config, $level) {
  @return _mat-get-type-value($config, $level, line-height);
}

// Gets the font weight for a level inside a typography config.
@function mat-font-weight($config, $level) {
  @return _mat-get-type-value($config, $level, font-weight);
}

// Gets the letter spacing for a level inside a typography config.
@function mat-letter-spacing($config, $level) {
  @return _mat-get-type-value($config, $level, letter-spacing);
}

// Gets the font-family from a typography config and removes the quotes around it.
@function mat-font-family($config, $level: null) {
  $font-family: map-get($config, font-family);

  @if $level != null {
    $font-family: _mat-get-type-value($config, $level, font-family);
  }

  // Guard against unquoting non-string values, because it's deprecated.
  @return if(type-of($font-family) == string, unquote($font-family), $font-family);
}

// Outputs the shorthand `font` CSS property, based on a set of typography values. Falls back to
// the individual properties if a value that isn't allowed in the shorthand is passed in.
@mixin mat-typography-font-shorthand($font-size, $font-weight, $line-height, $font-family) {
  // If any of the values are set to `inherit`, we can't use the shorthand
  // so we fall back to passing in the individual properties.
  @if ($font-size == inherit or
       $font-weight == inherit or
       $line-height == inherit or
       $font-family == inherit or
       $font-size == null or
       $font-weight == null or
       $line-height == null or
       $font-family == null) {

    font-size: $font-size;
    font-weight: $font-weight;
    line-height: $line-height;
    font-family: $font-family;
  }
  @else {
    // Otherwise use the shorthand `font`, because it's the least amount of bytes. Note
    // that we need to use interpolation for `font-size/line-height` in order to prevent
    // Sass from dividing the two values.
    font: $font-weight #{$font-size}/#{$line-height} $font-family;
  }
}

// Converts a typography level into CSS styles.
@mixin mat-typography-level-to-styles($config, $level) {
  $font-size: mat-font-size($config, $level);
  $font-weight: mat-font-weight($config, $level);
  $line-height: mat-line-height($config, $level);
  $font-family: mat-font-family($config, $level);

  @include mat-typography-font-shorthand($font-size, $font-weight, $line-height, $font-family);
  letter-spacing: mat-letter-spacing($config, $level);
}


@mixin mat-option-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);

  .mat-option {
    color: mat-color($foreground, text);

    &:hover:not(.mat-option-disabled),
    &:focus:not(.mat-option-disabled) {
      background: mat-color($background, hover);
    }

    // In multiple mode there is a checkbox to show that the option is selected.
    &.mat-selected:not(.mat-option-multiple):not(.mat-option-disabled) {
      background: mat-color($background, hover);
    }

    &.mat-active {
      background: mat-color($background, hover);
      color: mat-color($foreground, text);
    }

    &.mat-option-disabled {
      color: mat-color($foreground, hint-text);
    }
  }

  .mat-primary .mat-option.mat-selected:not(.mat-option-disabled) {
    color: mat-color($primary, text);
  }

  .mat-accent .mat-option.mat-selected:not(.mat-option-disabled) {
    color: mat-color($accent, text);
  }

  .mat-warn .mat-option.mat-selected:not(.mat-option-disabled) {
    color: mat-color($warn, text);
  }
}

@mixin mat-option-typography($config) {
  .mat-option {
    font: {
      family: mat-font-family($config);
      size: mat-font-size($config, subheading-2);
    }
  }
}





@mixin mat-optgroup-theme($theme) {
  $foreground: map-get($theme, foreground);

  .mat-optgroup-label {
    color: mat-color($foreground, secondary-text);
  }

  .mat-optgroup-disabled .mat-optgroup-label {
    color: mat-color($foreground, hint-text);
  }
}

@mixin mat-optgroup-typography($config) {
  .mat-optgroup-label {
    @include mat-typography-level-to-styles($config, body-2);
  }
}



@mixin mat-pseudo-checkbox-theme($theme) {
  $is-dark-theme: map-get($theme, is-dark);
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);

  // NOTE(traviskaufman): While the spec calls for translucent blacks/whites for disabled colors,
  // this does not work well with elements layered on top of one another. To get around this we
  // blend the colors together based on the base color and the theme background.
  $white-30pct-opacity-on-dark: #686868;
  $black-26pct-opacity-on-light: #b0b0b0;
  $disabled-color: if($is-dark-theme, $white-30pct-opacity-on-dark, $black-26pct-opacity-on-light);
  $colored-box-selector: '.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-indeterminate';

  .mat-pseudo-checkbox {
    color: mat-color(map-get($theme, foreground), secondary-text);

    &::after {
      color: mat-color($background, background);
    }
  }

  .mat-pseudo-checkbox-disabled {
    color: $disabled-color;
  }

  .mat-primary .mat-pseudo-checkbox-checked,
  .mat-primary .mat-pseudo-checkbox-indeterminate {
    background: mat-color(map-get($theme, primary));
  }

  // Default to the accent color. Note that the pseudo checkboxes are meant to inherit the
  // theme from their parent, rather than implementing their own theming, which is why we
  // don't attach to the `mat-*` classes. Also note that this needs to be below `.mat-primary`
  // in order to allow for the color to be overwritten if the checkbox is inside a parent that
  // has `mat-accent` and is placed inside another parent that has `mat-primary`.
  .mat-pseudo-checkbox-checked,
  .mat-pseudo-checkbox-indeterminate,
  .mat-accent .mat-pseudo-checkbox-checked,
  .mat-accent .mat-pseudo-checkbox-indeterminate {
    background: mat-color(map-get($theme, accent));
  }

  .mat-warn .mat-pseudo-checkbox-checked,
  .mat-warn .mat-pseudo-checkbox-indeterminate {
    background: mat-color(map-get($theme, warn));
  }

  .mat-pseudo-checkbox-checked,
  .mat-pseudo-checkbox-indeterminate {
    &.mat-pseudo-checkbox-disabled {
      background: $disabled-color;
    }
  }
}



// Represents a typography level from the Material design spec.
@function mat-typography-level(
  $font-size,
  $line-height: $font-size,
  $font-weight: 400,
  $font-family: null,
  $letter-spacing: null) {

  @return (
    font-size: $font-size,
    line-height: $line-height,
    font-weight: $font-weight,
    font-family: $font-family,
    letter-spacing: $letter-spacing
  );
}

// Represents a collection of typography levels.
// Defaults come from https://material.io/guidelines/style/typography.html
// Note: The spec doesn't mention letter spacing. The values here come from
// eyeballing it until it looked exactly like the spec examples.
@function mat-typography-config(
  $font-family:   'Roboto, "Helvetica Neue", sans-serif',
  $display-4:     mat-typography-level(112px, 112px, 300, $letter-spacing: -0.05em),
  $display-3:     mat-typography-level(56px, 56px, 400, $letter-spacing: -0.02em),
  $display-2:     mat-typography-level(45px, 48px, 400, $letter-spacing: -0.005em),
  $display-1:     mat-typography-level(34px, 40px, 400),
  $headline:      mat-typography-level(24px, 32px, 400),
  $title:         mat-typography-level(20px, 32px, 500),
  $subheading-2:  mat-typography-level(16px, 28px, 400),
  $subheading-1:  mat-typography-level(15px, 24px, 400),
  $body-2:        mat-typography-level(14px, 24px, 500),
  $body-1:        mat-typography-level(14px, 20px, 400),
  $caption:       mat-typography-level(12px, 20px, 400),
  $button:        mat-typography-level(14px, 14px, 500),
  // Line-height must be unit-less fraction of the font-size.
  $input:         mat-typography-level(inherit, 1.125, 400)
) {

  // Declare an initial map with all of the levels.
  $config: (
    display-4:      $display-4,
    display-3:      $display-3,
    display-2:      $display-2,
    display-1:      $display-1,
    headline:       $headline,
    title:          $title,
    subheading-2:   $subheading-2,
    subheading-1:   $subheading-1,
    body-2:         $body-2,
    body-1:         $body-1,
    caption:        $caption,
    button:         $button,
    input:          $input,
  );

  // Loop through the levels and set the `font-family` of the ones that don't have one to the base.
  // Note that Sass can't modify maps in place, which means that we need to merge and re-assign.
  @each $key, $level in $config {
    @if map-get($level, font-family) == null {
      $new-level: map-merge($level, (font-family: $font-family));
      $config: map-merge($config, ($key: $new-level));
    }
  }

  // Add the base font family to the config.
  @return map-merge($config, (font-family: $font-family));
}

// Adds the base typography styles, based on a config.
@mixin mat-base-typography($config, $selector: '.mat-typography') {
  .mat-h1, .mat-headline, #{$selector} h1 {
    @include mat-typography-level-to-styles($config, headline);
    margin: 0 0 16px;
  }

  .mat-h2, .mat-title, #{$selector} h2 {
    @include mat-typography-level-to-styles($config, title);
    margin: 0 0 16px;
  }

  .mat-h3, .mat-subheading-2, #{$selector} h3 {
    @include mat-typography-level-to-styles($config, subheading-2);
    margin: 0 0 16px;
  }

  .mat-h4, .mat-subheading-1, #{$selector} h4 {
    @include mat-typography-level-to-styles($config, subheading-1);
    margin: 0 0 16px;
  }

  // Note: the spec doesn't have anything that would correspond to h5 and h6, but we add these for
  // consistency. The font sizes come from the Chrome user agent styles which have h5 at 0.83em
  // and h6 at 0.67em.
  .mat-h5, #{$selector} h5 {
    @include mat-typography-font-shorthand(
       // calc is used here to support css variables
      calc(#{mat-font-size($config, body-1)} * 0.83),
      mat-font-weight($config, body-1),
      mat-line-height($config, body-1),
      mat-font-family($config, body-1)
    );

    margin: 0 0 12px;
  }

  .mat-h6, #{$selector} h6 {
    @include mat-typography-font-shorthand(
       // calc is used here to support css variables
      calc(#{mat-font-size($config, body-1)} * 0.67),
      mat-font-weight($config, body-1),
      mat-line-height($config, body-1),
      mat-font-family($config, body-1)
    );

    margin: 0 0 12px;
  }

  .mat-body-strong, .mat-body-2 {
    @include mat-typography-level-to-styles($config, body-2);
  }

  .mat-body, .mat-body-1, #{$selector} {
    @include mat-typography-level-to-styles($config, body-1);

    p {
      margin: 0 0 12px;
    }
  }

  .mat-small, .mat-caption {
    @include mat-typography-level-to-styles($config, caption);
  }

  .mat-display-4, #{$selector} .mat-display-4 {
    @include mat-typography-level-to-styles($config, display-4);
    margin: 0 0 56px;
  }

  .mat-display-3, #{$selector} .mat-display-3 {
    @include mat-typography-level-to-styles($config, display-3);
    margin: 0 0 64px;
  }

  .mat-display-2, #{$selector} .mat-display-2 {
    @include mat-typography-level-to-styles($config, display-2);
    margin: 0 0 64px;
  }

  .mat-display-1, #{$selector} .mat-display-1 {
    @include mat-typography-level-to-styles($config, display-1);
    margin: 0 0 64px;
  }
}




@mixin mat-autocomplete-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);

  .mat-autocomplete-panel {
    @include _mat-theme-overridable-elevation(4, $theme);
    background: mat-color($background, card);
    color: mat-color($foreground, text);

    // Selected options in autocompletes should not be gray, but we
    // only want to override the background for selected options if
    // they are *not* in hover or focus state. This change has to be
    // made here because base option styles are shared between the
    // autocomplete and the select.
    .mat-option.mat-selected:not(.mat-active):not(:hover) {
      background: mat-color($background, card);

      &:not(.mat-option-disabled) {
        color: mat-color($foreground, text);
      }
    }
  }

}

@mixin mat-autocomplete-typography($config) { }

// This contains all of the styles for the badge
// rather than just the color/theme because of
// no style sheet support for directives.





$mat-badge-font-size: 12px;
$mat-badge-font-weight: 600;
$mat-badge-default-size: 22px !default;
$mat-badge-small-size: $mat-badge-default-size - 6;
$mat-badge-large-size: $mat-badge-default-size + 6;

// Mixin for building offset given different sizes
@mixin _mat-badge-size($size) {
  .mat-badge-content {
    width: $size;
    height: $size;
    line-height: $size;
  }

  &.mat-badge-above {
    .mat-badge-content {
      top: -$size / 2;
    }
  }

  &.mat-badge-below {
    .mat-badge-content {
      bottom: -$size / 2;
    }
  }

  &.mat-badge-before {
    .mat-badge-content {
      left: -$size;
    }
  }

  [dir='rtl'] &.mat-badge-before {
    .mat-badge-content {
      left: auto;
      right: -$size;
    }
  }

  &.mat-badge-after {
    .mat-badge-content {
      right: -$size;
    }
  }

  [dir='rtl'] &.mat-badge-after {
    .mat-badge-content {
      right: auto;
      left: -$size;
    }
  }

  &.mat-badge-overlap {
    &.mat-badge-before {
      .mat-badge-content {
        left: -$size / 2;
      }
    }

    [dir='rtl'] &.mat-badge-before {
      .mat-badge-content {
        left: auto;
        right: -$size / 2;
      }
    }

    &.mat-badge-after {
      .mat-badge-content {
        right: -$size / 2;
      }
    }

    [dir='rtl'] &.mat-badge-after {
      .mat-badge-content {
        right: auto;
        left: -$size / 2;
      }
    }
  }
}

@mixin mat-badge-theme($theme) {
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $primary: map-get($theme, primary);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-badge-content {
    color: mat-color($primary, default-contrast);
    background: mat-color($primary);

    @include cdk-high-contrast {
      outline: solid 1px;
      border-radius: 0;
    }
  }

  .mat-badge-accent {
    .mat-badge-content {
      background: mat-color($accent);
      color: mat-color($accent, default-contrast);
    }
  }

  .mat-badge-warn {
    .mat-badge-content {
      color: mat-color($warn, default-contrast);
      background: mat-color($warn);
    }
  }

  .mat-badge {
    position: relative;
  }

  .mat-badge-hidden {
    .mat-badge-content {
      display: none;
    }
  }

  .mat-badge-disabled {
    .mat-badge-content {
      $app-background: mat-color($background, 'background');
      $badge-color: mat-color($foreground, disabled-button);

      // The disabled color usually has some kind of opacity, but because the badge is overlayed
      // on top of something else, it won't look good if it's opaque. If it is a color *type*,
      // we convert it into a solid color by taking the opacity from the rgba value and using
      // the value to determine the percentage of the background to put into foreground when
      // mixing the colors together.
      @if (type-of($badge-color) == color and type-of($app-background) == color) {
        $badge-opacity: opacity($badge-color);
        background: mix($app-background, rgba($badge-color, 1), (1 - $badge-opacity) * 100%);
      }
      @else {
        background: $badge-color;
      }

      color: mat-color($foreground, disabled-text);
    }
  }

  .mat-badge-content {
    position: absolute;
    text-align: center;
    display: inline-block;
    border-radius: 50%;
    transition: transform 200ms ease-in-out;
    transform: scale(0.6);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    pointer-events: none;
  }

  .ng-animate-disabled .mat-badge-content,
  .mat-badge-content._mat-animation-noopable {
    transition: none;
  }

  // The active class is added after the element is added
  // so it can animate scale to default
  .mat-badge-content.mat-badge-active {
    // Scale to `none` instead of `1` to avoid blurry text in some browsers.
    transform: none;
  }

  .mat-badge-small {
    @include _mat-badge-size($mat-badge-small-size);
  }
  .mat-badge-medium {
    @include _mat-badge-size($mat-badge-default-size);
  }
  .mat-badge-large {
    @include _mat-badge-size($mat-badge-large-size);
  }
}

@mixin mat-badge-typography($config) {
  .mat-badge-content {
    font-weight: $mat-badge-font-weight;
    font-size: $mat-badge-font-size;
    font-family: mat-font-family($config);
  }

  .mat-badge-small .mat-badge-content {
    // Set the font size to 75% of the original.
    font-size: $mat-badge-font-size * 0.75;
  }

  .mat-badge-large .mat-badge-content {
    font-size: $mat-badge-font-size * 2;
  }
}





@mixin mat-bottom-sheet-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-bottom-sheet-container {
    @include _mat-theme-elevation(16, $theme);
    background: mat-color($background, dialog);
    color: mat-color($foreground, text);
  }
}

@mixin mat-bottom-sheet-typography($config) {
  .mat-bottom-sheet-container {
    @include mat-typography-level-to-styles($config, body-1);
  }
}





$_mat-button-ripple-opacity: 0.1;

// Applies a focus style to an mat-button element for each of the supported palettes.
@mixin _mat-button-focus-overlay-color($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);

  &.mat-primary .mat-button-focus-overlay {
    background-color: mat-color($primary);
  }

  &.mat-accent .mat-button-focus-overlay {
    background-color: mat-color($accent);
  }

  &.mat-warn .mat-button-focus-overlay {
    background-color: mat-color($warn);
  }

  &[disabled] .mat-button-focus-overlay {
    background-color: transparent;
  }
}

// Applies the background color for a ripple. If the value provided is not a Sass color,
// we assume that we've been given a CSS variable. Since we can't perform alpha-blending
// on a CSS variable, we instead add the opacity directly to the ripple element.
@mixin _mat-button-ripple-background($palette, $hue, $opacity) {
  $background-color: mat-color($palette, $hue, $opacity);
  background-color: $background-color;
  @if (type-of($background-color) != color) {
    opacity: $opacity;
  }
}

@mixin _mat-button-ripple-color($theme, $hue, $opacity: $_mat-button-ripple-opacity) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);

  &.mat-primary .mat-ripple-element {
    @include _mat-button-ripple-background($primary, $hue, $opacity);
  }

  &.mat-accent .mat-ripple-element {
    @include _mat-button-ripple-background($accent, $hue, $opacity);
  }

  &.mat-warn .mat-ripple-element {
    @include _mat-button-ripple-background($warn, $hue, $opacity);
  }
}

// Applies a property to an mat-button element for each of the supported palettes.
@mixin _mat-button-theme-property($theme, $property, $hue) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  &.mat-primary {
    #{$property}: mat-color($primary, $hue);
  }
  &.mat-accent {
    #{$property}: mat-color($accent, $hue);
  }
  &.mat-warn {
    #{$property}: mat-color($warn, $hue);
  }

  &.mat-primary, &.mat-accent, &.mat-warn, &[disabled] {
    &[disabled] {
      $palette: if($property == 'color', $foreground, $background);
      #{$property}: mat-color($palette, disabled-button);
    }
  }
}

@mixin mat-button-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-button, .mat-icon-button, .mat-stroked-button {
    // Buttons without a background color should inherit the font color. This is necessary to
    // ensure that the button is readable on custom background colors. It's wrong to always assume
    // that those buttons are always placed inside of containers with the default background
    // color of the theme (e.g. themed toolbars).
    color: inherit;
    background: transparent;

    @include _mat-button-theme-property($theme, 'color', text);
    @include _mat-button-focus-overlay-color($theme);

    // Setup the ripple color to be based on the text color. This ensures that the ripples
    // are matching with the current theme palette and are in contrast to the background color
    // (e.g in themed toolbars).
    .mat-ripple-element {
      opacity: $_mat-button-ripple-opacity;
      background-color: currentColor;
    }
  }

  .mat-button-focus-overlay {
    background: map_get($foreground, base);
  }

  // Note: this needs a bit extra specificity, because we're not guaranteed the inclusion
  // order of the theme styles and the button reset may end up resetting this as well.
  .mat-stroked-button:not([disabled]) {
    border-color: mat-color($foreground, divider);
  }

  .mat-flat-button, .mat-raised-button, .mat-fab, .mat-mini-fab {
    // Default font and background color when not using any color palette.
    color: mat-color($foreground, text);
    background-color: mat-color($background, raised-button);

    @include _mat-button-theme-property($theme, 'color', default-contrast);
    @include _mat-button-theme-property($theme, 'background-color', default);
    @include _mat-button-ripple-color($theme, default-contrast);
  }

  .mat-stroked-button, .mat-flat-button {
    @include _mat-theme-overridable-elevation(0, $theme);
  }

  .mat-raised-button {
    @include _mat-theme-overridable-elevation(2, $theme);

    &:not([disabled]):active {
      @include _mat-theme-overridable-elevation(8, $theme);
    }

    &[disabled] {
      @include _mat-theme-overridable-elevation(0, $theme);
    }
  }

  .mat-fab, .mat-mini-fab {
    @include _mat-theme-overridable-elevation(6, $theme);

    &:not([disabled]):active {
      @include _mat-theme-overridable-elevation(12, $theme);
    }

    &[disabled] {
      @include _mat-theme-overridable-elevation(0, $theme);
    }
  }
}

@mixin mat-button-typography($config) {
  .mat-button, .mat-raised-button, .mat-icon-button, .mat-stroked-button,
  .mat-flat-button, .mat-fab, .mat-mini-fab {
    font: {
      family: mat-font-family($config, button);
      size: mat-font-size($config, button);
      weight: mat-font-weight($config, button);
    }
  }
}






@mixin mat-button-toggle-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);
  $divider-color: mat-color($foreground, divider);

  .mat-button-toggle-standalone,
  .mat-button-toggle-group {
    @include _mat-theme-elevation(2, $theme);
  }

  .mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
  .mat-button-toggle-group-appearance-standard {
    box-shadow: none;
  }

  .mat-button-toggle {
    color: mat-color($foreground, hint-text);

    .mat-button-toggle-focus-overlay {
      background-color: mat-color($background, focused-button);
    }
  }

  .mat-button-toggle-appearance-standard {
    color: mat-color($foreground, text);
    background: mat-color($background, card);

    .mat-button-toggle-focus-overlay {
      background-color: mat-color($background, focused-button, 1);
    }
  }

  .mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
    border-left: solid 1px $divider-color;
  }

  [dir='rtl'] .mat-button-toggle-group-appearance-standard .mat-button-toggle + .mat-button-toggle {
    border-left: none;
    border-right: solid 1px $divider-color;
  }

  .mat-button-toggle-group-appearance-standard.mat-button-toggle-vertical {
    .mat-button-toggle + .mat-button-toggle {
      border-left: none;
      border-right: none;
      border-top: solid 1px $divider-color;
    }
  }

  .mat-button-toggle-checked {
    background-color: mat-color($background, selected-button);
    color: mat-color($foreground, secondary-text);

    &.mat-button-toggle-appearance-standard {
      color: mat-color($foreground, text);
    }
  }

  .mat-button-toggle-disabled {
    color: mat-color($foreground, disabled-button);
    background-color: mat-color($background, disabled-button-toggle);

    &.mat-button-toggle-appearance-standard {
      background: mat-color($background, card);
    }

    &.mat-button-toggle-checked {
      background-color: mat-color($background, selected-disabled-button);
    }
  }

  .mat-button-toggle-standalone.mat-button-toggle-appearance-standard,
  .mat-button-toggle-group-appearance-standard {
    border: solid 1px $divider-color;
  }
}

@mixin mat-button-toggle-typography($config) {
  .mat-button-toggle {
    font-family: mat-font-family($config);
  }
}







@mixin mat-card-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-card {
    @include _mat-theme-overridable-elevation(1, $theme);
    background: mat-color($background, card);
    color: mat-color($foreground, text);

    // Needs extra specificity to be able to override the elevation selectors.
    &.mat-card-flat {
      @include _mat-theme-overridable-elevation(0, $theme);
    }
  }

  .mat-card-subtitle {
    color: mat-color($foreground, secondary-text);
  }
}

@mixin mat-card-typography($config) {
  .mat-card {
    font-family: mat-font-family($config);
  }

  .mat-card-title {
    font: {
      size: mat-font-size($config, headline);
      weight: mat-font-weight($config, title);
    }
  }

  .mat-card-header .mat-card-title {
    font-size: mat-font-size($config, title);
  }

  .mat-card-subtitle,
  .mat-card-content {
    font-size: mat-font-size($config, body-1);
  }
}






@mixin mat-checkbox-theme($theme) {
  $is-dark-theme: map-get($theme, is-dark);
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);


  // The color of the checkbox's checkmark / mixedmark.
  $checkbox-mark-color: mat-color($background, background);

  // NOTE(traviskaufman): While the spec calls for translucent blacks/whites for disabled colors,
  // this does not work well with elements layered on top of one another. To get around this we
  // blend the colors together based on the base color and the theme background.
  $white-30pct-opacity-on-dark: #686868;
  $black-26pct-opacity-on-light: #b0b0b0;
  $disabled-color: if($is-dark-theme, $white-30pct-opacity-on-dark, $black-26pct-opacity-on-light);

  .mat-checkbox-frame {
    border-color: mat-color($foreground, secondary-text);
  }

  .mat-checkbox-checkmark {
    fill: $checkbox-mark-color;
  }

  .mat-checkbox-checkmark-path {
    // !important is needed here because a stroke must be set as an
    // attribute on the SVG in order for line animation to work properly.
    stroke: $checkbox-mark-color !important;

    @include cdk-high-contrast(black-on-white) {
      // Having the one above be !important ends up overriding the browser's automatic
      // color inversion so we need to re-invert it ourselves for black-on-white.
      stroke: #000 !important;
    }
  }

  .mat-checkbox-mixedmark {
    background-color: $checkbox-mark-color;
  }

  .mat-checkbox-indeterminate, .mat-checkbox-checked {
    &.mat-primary .mat-checkbox-background {
      background-color: mat-color($primary);
    }

    &.mat-accent .mat-checkbox-background {
      background-color: mat-color($accent);
    }

    &.mat-warn .mat-checkbox-background {
      background-color: mat-color($warn);
    }
  }

  .mat-checkbox-disabled {
    &.mat-checkbox-checked,
    &.mat-checkbox-indeterminate {
      .mat-checkbox-background {
        background-color: $disabled-color;
      }
    }

    &:not(.mat-checkbox-checked) {
      .mat-checkbox-frame {
        border-color: $disabled-color;
      }
    }

    .mat-checkbox-label {
      color: mat-color($foreground, secondary-text);
    }

    @include cdk-high-contrast {
      opacity: 0.5;
    }
  }

  // This one is moved down here so it can target both
  // the theme colors and the disabled state.
  @include cdk-high-contrast {
    .mat-checkbox-background {
      // Needs to be removed because it hides the checkbox outline.
      background: none;
    }
  }

  // Switch this to a solid color since we're using `opacity`
  // to control how opaque the ripple should be.
  .mat-checkbox .mat-ripple-element {
    background-color: map_get(map-get($theme, foreground), base);
  }

  .mat-checkbox-checked:not(.mat-checkbox-disabled),
  .mat-checkbox:active:not(.mat-checkbox-disabled) {
    &.mat-primary .mat-ripple-element {
      background: mat-color($primary);
    }

    &.mat-accent .mat-ripple-element {
      background: mat-color($accent);
    }

    &.mat-warn .mat-ripple-element {
      background: mat-color($warn);
    }
  }
}

@mixin mat-checkbox-typography($config) {
  .mat-checkbox {
    font-family: mat-font-family($config);
  }

  // TODO(kara): Remove this style when fixing vertical baseline
  .mat-checkbox-layout .mat-checkbox-label {
    line-height: mat-line-height($config, body-2);
  }
}






$mat-chip-remove-font-size: 18px;

@mixin mat-chips-color($foreground, $background) {
  background-color: $background;
  color: $foreground;

  .mat-chip-remove {
    color: $foreground;
    opacity: 0.4;
  }
}

@mixin mat-chips-theme-color($palette) {
  @include mat-chips-color(mat-color($palette, default-contrast), mat-color($palette));

  .mat-ripple-element {
    background: mat-color($palette, default-contrast, 0.1);
  }
}

@mixin mat-chips-theme($theme) {
  $is-dark-theme: map-get($theme, is-dark);
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  $unselected-background: mat-color($background, unselected-chip);
  $unselected-foreground: mat-color($foreground, text);

  .mat-chip.mat-standard-chip {
    @include mat-chips-color($unselected-foreground, $unselected-background);

    &:not(.mat-chip-disabled) {
      &:active {
        @include _mat-theme-elevation(3, $theme);
      }

      .mat-chip-remove:hover {
        opacity: 0.54;
      }
    }

    &.mat-chip-disabled {
      opacity: 0.4;
    }

    &::after {
      background: map_get($foreground, base);
    }
  }

  .mat-chip.mat-standard-chip.mat-chip-selected {
    &.mat-primary {
      @include mat-chips-theme-color($primary);
    }

    &.mat-warn {
      @include mat-chips-theme-color($warn);
    }

    &.mat-accent {
      @include mat-chips-theme-color($accent);
    }
  }
}

@mixin mat-chips-typography($config) {
  .mat-chip {
    font-size: mat-font-size($config, body-2);
    font-weight: mat-font-weight($config, body-2);

    .mat-chip-trailing-icon.mat-icon,
    .mat-chip-remove.mat-icon {
      font-size: $mat-chip-remove-font-size;
    }
  }
}





@mixin mat-table-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-table {
    background: mat-color($background, 'card');
  }

  .mat-table thead, .mat-table tbody, .mat-table tfoot,
  mat-header-row, mat-row, mat-footer-row,
  [mat-header-row], [mat-row], [mat-footer-row],
  .mat-table-sticky {
    background: inherit;
  }

  mat-row, mat-header-row, mat-footer-row,
  th.mat-header-cell, td.mat-cell, td.mat-footer-cell {
    border-bottom-color: mat-color($foreground, divider);
  }

  .mat-header-cell {
    color: mat-color($foreground, secondary-text);
  }

  .mat-cell, .mat-footer-cell {
    color: mat-color($foreground, text);
  }
}

@mixin mat-table-typography($config) {
  .mat-table {
    font-family: mat-font-family($config);
  }

  .mat-header-cell {
    font-size: mat-font-size($config, caption);
    font-weight: mat-font-weight($config, body-2);
  }

  .mat-cell, .mat-footer-cell {
    font-size: mat-font-size($config, body-1);
  }
}







$mat-datepicker-selected-today-box-shadow-width: 1px;
$mat-datepicker-selected-fade-amount: 0.6;
$mat-datepicker-today-fade-amount: 0.2;
$mat-calendar-body-font-size: 13px !default;
$mat-calendar-weekday-table-font-size: 11px !default;

@mixin _mat-datepicker-color($palette) {
  .mat-calendar-body-selected {
    background-color: mat-color($palette);
    color: mat-color($palette, default-contrast);
  }

  .mat-calendar-body-disabled > .mat-calendar-body-selected {
    $background: mat-color($palette);

    @if (type-of($background) == color) {
      background-color: fade-out($background, $mat-datepicker-selected-fade-amount);
    }
    @else {
      // If we couldn't resolve to background to a color (e.g. it's a CSS variable),
      // fall back to fading the content out via `opacity`.
      opacity: $mat-datepicker-today-fade-amount;
    }
  }

  .mat-calendar-body-today.mat-calendar-body-selected {
    box-shadow: inset 0 0 0 $mat-datepicker-selected-today-box-shadow-width
                mat-color($palette, default-contrast);
  }
}

@mixin mat-datepicker-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);

  .mat-calendar-arrow {
    border-top-color: mat-color($foreground, icon);
  }

  // The prev/next buttons need a bit more specificity to
  // avoid being overwritten by the .mat-icon-button.
  .mat-datepicker-toggle,
  .mat-datepicker-content .mat-calendar-next-button,
  .mat-datepicker-content .mat-calendar-previous-button {
    color: mat-color($foreground, icon);
  }

  .mat-calendar-table-header {
    color: mat-color($foreground, hint-text);
  }

  .mat-calendar-table-header-divider::after {
    background: mat-color($foreground, divider);
  }

  .mat-calendar-body-label {
    color: mat-color($foreground, secondary-text);
  }

  .mat-calendar-body-cell-content {
    color: mat-color($foreground, text);
    border-color: transparent;
  }

  .mat-calendar-body-disabled > .mat-calendar-body-cell-content:not(.mat-calendar-body-selected) {
    color: mat-color($foreground, disabled-text);
  }

  .mat-calendar-body-cell:not(.mat-calendar-body-disabled):hover,
  .cdk-keyboard-focused .mat-calendar-body-active,
  .cdk-program-focused .mat-calendar-body-active {
    & > .mat-calendar-body-cell-content:not(.mat-calendar-body-selected) {
      background-color: mat-color($background, hover);
    }
  }

  .mat-calendar-body-today:not(.mat-calendar-body-selected) {
    // Note: though it's not text, the border is a hint about the fact that this is today's date,
    // so we use the hint color.
    border-color: mat-color($foreground, hint-text);
  }

  .mat-calendar-body-disabled > .mat-calendar-body-today:not(.mat-calendar-body-selected) {
    $color: mat-color($foreground, hint-text);

    @if (type-of($color) == color) {
      border-color: fade-out($color, $mat-datepicker-today-fade-amount);
    }
    @else {
      // If the color didn't resolve to a color value, but something like a CSS variable, we can't
      // fade it out so we fall back to reducing the element opacity. Note that we don't use the
      // $mat-datepicker-today-fade-amount, because hint text usually has some opacity applied
      // to it already and we don't want them to stack on top of each other.
      opacity: 0.5;
    }
  }

  @include _mat-datepicker-color(map-get($theme, primary));

  .mat-datepicker-content {
    @include _mat-theme-elevation(4, $theme);
    background-color: mat-color($background, card);
    color: mat-color($foreground, text);

    &.mat-accent {
      @include _mat-datepicker-color(map-get($theme, accent));
    }

    &.mat-warn {
      @include _mat-datepicker-color(map-get($theme, warn));
    }
  }

  .mat-datepicker-content-touch {
    @include _mat-theme-elevation(0, $theme);
  }

  .mat-datepicker-toggle-active {
    color: mat-color(map-get($theme, primary), text);

    &.mat-accent {
      color: mat-color(map-get($theme, accent), text);
    }

    &.mat-warn {
      color: mat-color(map-get($theme, warn), text);
    }
  }
}

@mixin mat-datepicker-typography($config) {
  .mat-calendar {
    font-family: mat-font-family($config);
  }

  .mat-calendar-body {
    font-size: $mat-calendar-body-font-size;
  }

  .mat-calendar-body-label,
  .mat-calendar-period-button {
    font: {
      size: mat-font-size($config, button);
      weight: mat-font-weight($config, button);
    }
  }

  .mat-calendar-table-header th {
    font: {
      size: $mat-calendar-weekday-table-font-size;
      weight: mat-font-weight($config, body-1);
    }
  }
}







@mixin mat-dialog-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-dialog-container {
    @include _mat-theme-elevation(24, $theme);
    background: mat-color($background, dialog);
    color: mat-color($foreground, text);
  }
}

@mixin mat-dialog-typography($config) {
  .mat-dialog-title {
    @include mat-typography-level-to-styles($config, title);
  }
}






@mixin mat-expansion-panel-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-expansion-panel {
    @include _mat-theme-overridable-elevation(2, $theme);
    background: mat-color($background, card);
    color: mat-color($foreground, text);
  }

  .mat-action-row {
    border-top-color: mat-color($foreground, divider);
  }

  .mat-expansion-panel {
    & .mat-expansion-panel-header.cdk-keyboard-focused,
    & .mat-expansion-panel-header.cdk-program-focused,
    &:not(.mat-expanded) .mat-expansion-panel-header:hover {
      &:not([aria-disabled='true']) {
        background: mat-color($background, hover);
      }
    }
  }

  // Disable the hover on touch devices since it can appear like it is stuck. We can't use
  // `@media (hover)` above, because the desktop support browser support isn't great.
  @media (hover: none) {
    .mat-expansion-panel:not(.mat-expanded):not([aria-disabled='true'])
      .mat-expansion-panel-header:hover {
      background: mat-color($background, card);
    }
  }

  .mat-expansion-panel-header-title {
    color: mat-color($foreground, text);
  }

  .mat-expansion-panel-header-description,
  .mat-expansion-indicator::after {
    color: mat-color($foreground, secondary-text);
  }

  .mat-expansion-panel-header[aria-disabled='true'] {
    color: mat-color($foreground, disabled-button);

    .mat-expansion-panel-header-title,
    .mat-expansion-panel-header-description {
      color: inherit;
    }
  }
}

@mixin mat-expansion-panel-typography($config) {
  .mat-expansion-panel-header {
    font: {
      family: mat-font-family($config, subheading-1);
      size: mat-font-size($config, subheading-1);
      weight: mat-font-weight($config, subheading-1);
    }
  }

  .mat-expansion-panel-content {
    @include mat-typography-level-to-styles($config, body-1);
  }
}




// This mixin will ensure that lines that overflow the container will hide the overflow and
// truncate neatly with an ellipsis.
@mixin mat-truncate-line() {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

// Mixin to provide all mat-line styles, changing secondary font size based on whether the list
// is in dense mode.
@mixin mat-line-base($secondary-font-size) {
  .mat-line {
    @include mat-truncate-line();
    display: block;
    box-sizing: border-box;

    // all lines but the top line should have smaller text
    &:nth-child(n+2) {
      font-size: $secondary-font-size;
    }
  }
}

// This mixin normalizes default element styles, e.g. font weight for heading text.
@mixin mat-normalize-text() {
  & > * {
    margin: 0;
    padding: 0;
    font-weight: normal;
    font-size: inherit;
  }
}

// This mixin provides base styles for the wrapper around mat-line elements in a list.
@mixin mat-line-wrapper-base() {
  @include mat-normalize-text();

  display: flex;
  flex-direction: column;
  width: 100%;
  box-sizing: border-box;
  overflow: hidden;

  // Must remove wrapper when lines are empty or it takes up horizontal
  // space and pushes other elements to the right.
  &:empty {
    display: none;
  }
}



// Include this empty mixin for consistency with the other components.
@mixin mat-grid-list-theme($theme) { }

@mixin mat-grid-list-typography($config) {
  .mat-grid-tile-header,
  .mat-grid-tile-footer {
    @include mat-line-base(mat-font-size($config, caption));
    font-size: mat-font-size($config, body-1);
  }
}




// Include this empty mixin for consistency with the other components.
@mixin mat-icon-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-icon {
    &.mat-primary {
      color: mat-color($primary, text);
    }

    &.mat-accent {
      color: mat-color($accent, text);
    }

    &.mat-warn {
      color: mat-color($warn, text);
    }
  }
}

@mixin mat-icon-typography($config) { }





// Renders a gradient for showing the dashed line when the input is disabled.
// Unlike using a border, a gradient allows us to adjust the spacing of the dotted line
// to match the Material Design spec.
@mixin mat-control-disabled-underline($color) {
  background-image: linear-gradient(to right, $color 0%, $color 33%, transparent 0%);
  background-size: 4px 100%;
  background-repeat: repeat-x;
}

// Figures out the color of the placeholder for a form control.
// Used primarily to prevent the various form controls from
// becoming out of sync since these colors aren't in a palette.
@function _mat-control-placeholder-color($theme) {
  $foreground: map-get($theme, foreground);
  $is-dark-theme: map-get($theme, is-dark);
  @return mat-color($foreground, secondary-text, if($is-dark-theme, 0.5, 0.42));
}


/* stylelint-disable material/no-prefixes */
@mixin user-select($value) {
  -webkit-user-select: $value;
  -moz-user-select: $value;
  -ms-user-select: $value;
  user-select: $value;
}

@mixin input-placeholder {
  &::placeholder {
    @content;
  }

  &::-moz-placeholder {
    @content;
  }

  &::-webkit-input-placeholder {
    @content;
  }

  &:-ms-input-placeholder {
    @content;
  }
}

@mixin cursor-grab {
  cursor: -webkit-grab;
  cursor: grab;
}

@mixin cursor-grabbing {
  cursor: -webkit-grabbing;
  cursor: grabbing;
}

@mixin backface-visibility($value) {
  -webkit-backface-visibility: $value;
  backface-visibility: $value;
}
/* stylelint-enable */



@mixin mat-input-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $foreground: map-get($theme, foreground);

  .mat-form-field-type-mat-native-select .mat-form-field-infix::after {
    color: mat-color($foreground, secondary-text);
  }

  .mat-input-element:disabled,
  .mat-form-field-type-mat-native-select.mat-form-field-disabled .mat-form-field-infix::after {
    color: mat-color($foreground, disabled-text);
  }

  .mat-input-element {
    caret-color: mat-color($primary, text);

    @include input-placeholder {
      color: _mat-control-placeholder-color($theme);
    }

    // On dark themes we set the native `select` color to some shade of white,
    // however the color propagates to all of the `option` elements, which are
    // always on a white background inside the dropdown, causing them to blend in.
    // Since we can't change background of the dropdown, we need to explicitly
    // reset the color of the options to something dark.
    @if (map-get($theme, is-dark)) {
      option {
        color: $dark-primary-text;
      }

      option:disabled {
        color: $dark-disabled-text;
      }
    }
  }

  .mat-accent .mat-input-element {
    caret-color: mat-color($accent, text);
  }

  .mat-warn .mat-input-element,
  .mat-form-field-invalid .mat-input-element {
    caret-color: mat-color($warn, text);
  }

  .mat-form-field-type-mat-native-select.mat-form-field-invalid .mat-form-field-infix::after {
    color: mat-color($warn, text);
  }
}

@mixin mat-input-typography($config) {
  // The unit-less line-height from the font config.
  $line-height: mat-line-height($config, input);

  // The amount of space between the top of the line and the top of the actual text
  // (as a fraction of the font-size).
  $line-spacing: ($line-height - 1) / 2;

  // <input> elements seem to have their height set slightly too large on Safari causing the text to
  // be misaligned w.r.t. the placeholder. Adding this margin corrects it.
  input.mat-input-element {
    margin-top: -$line-spacing * 1em;
  }
}







@mixin mat-list-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-list-base {
    .mat-list-item {
      color: mat-color($foreground, text);
    }

    .mat-list-option {
      color: mat-color($foreground, text);
    }

    .mat-subheader {
      color: mat-color($foreground, secondary-text);
    }
  }

  .mat-list-item-disabled {
    background-color: mat-color($background, disabled-list-option);
  }

  .mat-list-option,
  .mat-nav-list .mat-list-item,
  .mat-action-list .mat-list-item {
    &:hover, &:focus {
      background: mat-color($background, 'hover');
    }
  }
}

@mixin mat-list-typography($config) {
  $font-family: mat-font-family($config);

  .mat-list-item {
    font-family: $font-family;
  }

  .mat-list-option {
    font-family: $font-family;
  }

  // Default list
  .mat-list-base {
    .mat-list-item {
      font-size: mat-font-size($config, subheading-2);
      @include mat-line-base(mat-font-size($config, body-1));
    }

    .mat-list-option {
      font-size: mat-font-size($config, subheading-2);
      @include mat-line-base(mat-font-size($config, body-1));
    }

    .mat-subheader {
      font-family: mat-font-family($config, body-2);
      font-size: mat-font-size($config, body-2);
      font-weight: mat-font-weight($config, body-2);
    }
  }

  // Dense list
  .mat-list-base[dense] {
    .mat-list-item {
      font-size: mat-font-size($config, caption);
      @include mat-line-base(mat-font-size($config, caption));
    }

    .mat-list-option {
      font-size: mat-font-size($config, caption);
      @include mat-line-base(mat-font-size($config, caption));
    }

    .mat-subheader {
      font-family: $font-family;
      font-size: mat-font-size($config, caption);
      font-weight: mat-font-weight($config, body-2);
    }
  }
}







@mixin mat-menu-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-menu-panel {
    @include _mat-theme-overridable-elevation(4, $theme);
    background: mat-color($background, 'card');
  }

  .mat-menu-item {
    background: transparent;
    color: mat-color($foreground, 'text');

    &[disabled] {
      &, &::after {
        color: mat-color($foreground, 'disabled');
      }
    }
  }

  .mat-menu-item .mat-icon-no-color,
  .mat-menu-item-submenu-trigger::after {
    color: mat-color($foreground, 'icon');
  }

  .mat-menu-item:hover,
  .mat-menu-item.cdk-program-focused,
  .mat-menu-item.cdk-keyboard-focused,
  .mat-menu-item-highlighted {
    &:not([disabled]) {
      background: mat-color($background, 'hover');
    }
  }
}

@mixin mat-menu-typography($config) {
  .mat-menu-item {
    font: {
      family: mat-font-family($config, body-1);
      size: mat-font-size($config, body-1);
      weight: mat-font-weight($config, body-1);
    }
  }
}






@mixin mat-paginator-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);

  .mat-paginator {
    background: mat-color($background, 'card');
  }

  .mat-paginator,
  .mat-paginator-page-size .mat-select-trigger {
    color: mat-color($foreground, secondary-text);
  }

  .mat-paginator-decrement,
  .mat-paginator-increment {
    border-top: 2px solid mat-color($foreground, 'icon');
    border-right: 2px solid mat-color($foreground, 'icon');
  }

  .mat-paginator-first,
  .mat-paginator-last {
    border-top: 2px solid mat-color($foreground, 'icon');
  }

  .mat-icon-button[disabled] {
    .mat-paginator-decrement,
    .mat-paginator-increment,
    .mat-paginator-first,
    .mat-paginator-last {
      border-color: mat-color($foreground, 'disabled');
    }
  }
}

@mixin mat-paginator-typography($config) {
  .mat-paginator,
  .mat-paginator-page-size .mat-select-trigger {
    font: {
      family: mat-font-family($config, caption);
      size: mat-font-size($config, caption);
    }
  }
}





@mixin mat-progress-bar-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);

  .mat-progress-bar-background {
    fill: mat-color($primary, lighter);
  }

  .mat-progress-bar-buffer {
    background-color: mat-color($primary, lighter);
  }

  .mat-progress-bar-fill::after {
    background-color: mat-color($primary);
  }

  .mat-progress-bar.mat-accent {
    .mat-progress-bar-background {
      fill: mat-color($accent, lighter);
    }

    .mat-progress-bar-buffer {
      background-color: mat-color($accent, lighter);
    }

    .mat-progress-bar-fill::after {
      background-color: mat-color($accent);
    }
  }

  .mat-progress-bar.mat-warn {
    .mat-progress-bar-background {
      fill: mat-color($warn, lighter);
    }

    .mat-progress-bar-buffer {
      background-color: mat-color($warn, lighter);
    }

    .mat-progress-bar-fill::after {
      background-color: mat-color($warn);
    }
  }
}

@mixin mat-progress-bar-typography($config) { }






@mixin mat-progress-spinner-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);

  .mat-progress-spinner, .mat-spinner {
    circle {
      stroke: mat-color($primary);
    }

    &.mat-accent circle {
      stroke: mat-color($accent);
    }

    &.mat-warn circle {
      stroke: mat-color($warn);
    }
  }
}

@mixin mat-progress-spinner-typography($config) { }





@mixin _mat-radio-color($palette) {
  &.mat-radio-checked .mat-radio-outer-circle {
    border-color: mat-color($palette);
  }

  .mat-radio-inner-circle,
  .mat-radio-ripple .mat-ripple-element:not(.mat-radio-persistent-ripple),
  &.mat-radio-checked .mat-radio-persistent-ripple,
  &:active .mat-radio-persistent-ripple {
    background-color: mat-color($palette);
  }
}

@mixin mat-radio-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-radio-outer-circle {
    border-color: mat-color($foreground, secondary-text);
  }

  .mat-radio-button {
    &.mat-primary {
      @include _mat-radio-color($primary);
    }

    &.mat-accent {
      @include _mat-radio-color($accent);
    }

    &.mat-warn {
      @include _mat-radio-color($warn);
    }

    // This needs extra specificity, because the classes above are combined
    // (e.g. `.mat-radio-button.mat-accent`) which increases their specificity a lot.
    // TODO: consider making the selectors into descendants (`.mat-primary .mat-radio-button`).
    &.mat-radio-disabled {
      &.mat-radio-checked .mat-radio-outer-circle,
      .mat-radio-outer-circle {
        border-color: mat-color($foreground, disabled);
      }

      .mat-radio-ripple .mat-ripple-element,
      .mat-radio-inner-circle {
        background-color: mat-color($foreground, disabled);
      }

      .mat-radio-label-content {
        color: mat-color($foreground, disabled);
      }
    }

    // Switch this to a solid color since we're using `opacity`
    // to control how opaque the ripple should be.
    .mat-ripple-element {
      background-color: map_get($foreground, base);
    }
  }
}

@mixin mat-radio-typography($config) {
  .mat-radio-button {
    font-family: mat-font-family($config);
  }
}








@mixin mat-select-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);

  .mat-select-value {
    color: mat-color($foreground, text);
  }

  .mat-select-placeholder {
    color: _mat-control-placeholder-color($theme);
  }

  .mat-select-disabled .mat-select-value {
    color: mat-color($foreground, disabled-text);
  }

  .mat-select-arrow {
    color: mat-color($foreground, secondary-text);
  }

  .mat-select-panel {
    background: mat-color($background, card);
    @include _mat-theme-overridable-elevation(4, $theme);

    .mat-option.mat-selected:not(.mat-option-multiple) {
      background: mat-color($background, hover, 0.12);
    }
  }

  .mat-form-field {
    &.mat-focused {
      &.mat-primary .mat-select-arrow {
        color: mat-color($primary, text);
      }

      &.mat-accent .mat-select-arrow {
        color: mat-color($accent, text);
      }

      &.mat-warn .mat-select-arrow {
        color: mat-color($warn, text);
      }
    }

    .mat-select.mat-select-invalid .mat-select-arrow {
      color: mat-color($warn, text);
    }

    .mat-select.mat-select-disabled .mat-select-arrow {
      color: mat-color($foreground, disabled-text);
    }
  }
}

@mixin mat-select-typography($config) {
  // The unit-less line-height from the font config.
  $line-height: mat-line-height($config, input);

  .mat-select {
    font-family: mat-font-family($config);
  }

  .mat-select-trigger {
    height: $line-height * 1em;
  }
}






@mixin mat-sidenav-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  $drawer-background-color: mat-color($background, dialog);
  $drawer-container-background-color:  mat-color($background, background);
  $drawer-push-background-color: mat-color($background, dialog);
  $drawer-side-border: solid 1px mat-color($foreground, divider);

  .mat-drawer-container {
    background-color: $drawer-container-background-color;
    color: mat-color($foreground, text);
  }

  .mat-drawer {
    background-color: $drawer-background-color;
    color: mat-color($foreground, text);

    &.mat-drawer-push {
      background-color: $drawer-push-background-color;
    }

    &:not(.mat-drawer-side) {
      // The elevation of z-16 is noted in the design specifications.
      // See https://material.io/design/components/navigation-drawer.html
      @include _mat-theme-elevation(16, $theme);
    }
  }

  .mat-drawer-side {
    border-right: $drawer-side-border;

    &.mat-drawer-end {
      border-left: $drawer-side-border;
      border-right: none;
    }
  }

  [dir='rtl'] .mat-drawer-side {
    border-left: $drawer-side-border;
    border-right: none;

    &.mat-drawer-end {
      border-left: none;
      border-right: $drawer-side-border;
    }
  }

  .mat-drawer-backdrop.mat-drawer-shown {
    $opacity: 0.6;
    $backdrop-color: mat-color($background, card, $opacity);

    @if (type-of($backdrop-color) == color) {
      // We use invert() here to have the darken the background color expected to be used. If the
      // background is light, we use a dark backdrop. If the background is dark,
      // we use a light backdrop.
      background-color: invert($backdrop-color);
    }
    @else {
      // If we couldn't resolve the backdrop color to a color value, fall back to using
      // `opacity` to make it opaque since its end value could be a solid color.
      background-color: $backdrop-color;
      opacity: $opacity;
    }
  }
}

@mixin mat-sidenav-typography($config) { }






@mixin _mat-slide-toggle-checked($palette, $thumb-checked-hue) {
  &.mat-checked {
    .mat-slide-toggle-thumb {
      background-color: mat-color($palette, $thumb-checked-hue);
    }

    .mat-slide-toggle-bar {
      // Opacity is determined from the specs for the selection controls.
      // See: https://material.io/design/components/selection-controls.html#specs
      background-color: mat-color($palette, $thumb-checked-hue, 0.54);
    }

    .mat-ripple-element {
      // Set no opacity for the ripples because the ripple opacity will be adjusted dynamically
      // based on the type of interaction with the slide-toggle (e.g. for hover, focus)
      background-color: mat-color($palette, $thumb-checked-hue);
    }
  }
}

@mixin mat-slide-toggle-theme($theme) {
  $is-dark: map_get($theme, is-dark);
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  // Color hues are based on the specs which briefly show the hues that are applied to a switch.
  // The 2018 specs no longer describe how dark switches should look like. Due to the lack of
  // information for dark themed switches, we partially keep the old behavior that is based on
  // the previous specifications. For the checked color we always use the `default` hue because
  // that follows MDC and also makes it easier for people to create a custom theme without needing
  // to specify each hue individually.
  $thumb-unchecked-hue: if($is-dark, 400, 50);
  $thumb-checked-hue: default;

  $bar-unchecked-color: mat-color($foreground, disabled);
  $ripple-unchecked-color: mat-color($foreground, base);

  .mat-slide-toggle {
    @include _mat-slide-toggle-checked($accent, $thumb-checked-hue);

    &.mat-primary {
      @include _mat-slide-toggle-checked($primary, $thumb-checked-hue);
    }

    &.mat-warn {
      @include _mat-slide-toggle-checked($warn, $thumb-checked-hue);
    }

    &:not(.mat-checked) .mat-ripple-element {
      // Set no opacity for the ripples because the ripple opacity will be adjusted dynamically
      // based on the type of interaction with the slide-toggle (e.g. for hover, focus)
      background-color: $ripple-unchecked-color;
    }
  }

  .mat-slide-toggle-thumb {
    @include _mat-theme-elevation(1, $theme);
    background-color: mat-color($mat-grey, $thumb-unchecked-hue);
  }

  .mat-slide-toggle-bar {
    background-color: $bar-unchecked-color;
  }
}

@mixin mat-slide-toggle-typography($config) {
  .mat-slide-toggle-content {
    font-family: mat-font-family($config);
  }
}





@mixin _mat-slider-inner-content-theme($palette) {
  .mat-slider-track-fill,
  .mat-slider-thumb,
  .mat-slider-thumb-label {
    background-color: mat-color($palette);
  }

  .mat-slider-thumb-label-text {
    color: mat-color($palette, default-contrast);
  }
}

@mixin mat-slider-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  $mat-slider-off-color: mat-color($foreground, slider-off);
  $mat-slider-off-focused-color: mat-color($foreground, slider-off-active);
  $mat-slider-disabled-color: mat-color($foreground, slider-off);
  $mat-slider-labeled-min-value-thumb-color: mat-color($foreground, slider-min);
  $mat-slider-labeled-min-value-thumb-label-color: mat-color($foreground, slider-off);
  $mat-slider-focus-ring-color: mat-color($accent, default, 0.2);
  $mat-slider-focus-ring-min-value-color: mat-color($foreground, base, 0.12);
  $mat-slider-tick-color: mat-color($foreground, base, 0.7);
  $mat-slider-tick-size: 2px;

  .mat-slider-track-background {
    background-color: $mat-slider-off-color;
  }

  .mat-primary {
    @include _mat-slider-inner-content-theme($primary);
  }

  .mat-accent {
    @include _mat-slider-inner-content-theme($accent);
  }

  .mat-warn {
    @include _mat-slider-inner-content-theme($warn);
  }

  .mat-slider-focus-ring {
    background-color: $mat-slider-focus-ring-color;
  }

  .mat-slider:hover,
  .cdk-focused {
    .mat-slider-track-background {
      background-color: $mat-slider-off-focused-color;
    }
  }

  .mat-slider-disabled {
    .mat-slider-track-background,
    .mat-slider-track-fill,
    .mat-slider-thumb {
      background-color: $mat-slider-disabled-color;
    }

    &:hover {
      .mat-slider-track-background {
        background-color: $mat-slider-disabled-color;
      }
    }
  }

  .mat-slider-min-value {
    .mat-slider-focus-ring {
      background-color: $mat-slider-focus-ring-min-value-color;
    }

    &.mat-slider-thumb-label-showing {
      .mat-slider-thumb,
      .mat-slider-thumb-label {
        background-color: $mat-slider-labeled-min-value-thumb-color;
      }

      &.cdk-focused {
        .mat-slider-thumb,
        .mat-slider-thumb-label {
          background-color: $mat-slider-labeled-min-value-thumb-label-color;
        }
      }
    }

    &:not(.mat-slider-thumb-label-showing) {
      .mat-slider-thumb {
        border-color: $mat-slider-off-color;
        background-color: transparent;
      }

      &:hover,
      &.cdk-focused {
        .mat-slider-thumb {
          border-color: $mat-slider-off-focused-color;
        }

        &.mat-slider-disabled .mat-slider-thumb {
          border-color: $mat-slider-disabled-color;
        }
      }
    }
  }

  .mat-slider-has-ticks .mat-slider-wrapper::after {
    border-color: $mat-slider-tick-color;
  }

  .mat-slider-horizontal .mat-slider-ticks {
    background-image: repeating-linear-gradient(to right, $mat-slider-tick-color,
        $mat-slider-tick-color $mat-slider-tick-size, transparent 0, transparent);
    // Firefox doesn't draw the gradient correctly with 'to right'
    // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1314319).
    background-image: -moz-repeating-linear-gradient(0.0001deg, $mat-slider-tick-color,
        $mat-slider-tick-color $mat-slider-tick-size, transparent 0, transparent);
  }

  .mat-slider-vertical .mat-slider-ticks {
    background-image: repeating-linear-gradient(to bottom, $mat-slider-tick-color,
        $mat-slider-tick-color $mat-slider-tick-size, transparent 0, transparent);
  }
}

@mixin mat-slider-typography($config) {
  .mat-slider-thumb-label-text {
    font: {
      family: mat-font-family($config);
      size: mat-font-size($config, caption);
      weight: mat-font-weight($config, body-2);
    }
  }
}





@mixin mat-stepper-theme($theme) {
  $foreground: map-get($theme, foreground);
  $background: map-get($theme, background);
  $primary: map-get($theme, primary);
  $warn: map-get($theme, warn);

  .mat-step-header {
    &.cdk-keyboard-focused,
    &.cdk-program-focused,
    &:hover {
      background-color: mat-color($background, hover);
    }

    // On touch devices the :hover state will linger on the element after a tap.
    // Reset it via `@media` after the declaration, because the media query isn't
    // supported by all browsers yet.
    @media (hover: none) {
      &:hover {
        background: none;
      }
    }

    .mat-step-label,
    .mat-step-optional {
      // TODO(josephperrott): Update to using a corrected disabled-text contrast
      // instead of secondary-text.
      color: mat-color($foreground, secondary-text);
    }

    .mat-step-icon {
      // TODO(josephperrott): Update to using a corrected disabled-text contrast
      // instead of secondary-text.
      background-color: mat-color($foreground, secondary-text);
      color: mat-color($primary, default-contrast);
    }

    .mat-step-icon-selected,
    .mat-step-icon-state-done,
    .mat-step-icon-state-edit {
      background-color: mat-color($primary);
      color: mat-color($primary, default-contrast);
    }

    .mat-step-icon-state-error {
      background-color: transparent;
      color: mat-color($warn, text);
    }

    .mat-step-label.mat-step-label-active {
      color: mat-color($foreground, text);
    }

    .mat-step-label.mat-step-label-error {
      color: mat-color($warn, text);
    }
  }

  .mat-stepper-horizontal, .mat-stepper-vertical {
    background-color: mat-color($background, card);
  }

  .mat-stepper-vertical-line::before {
    border-left-color: mat-color($foreground, divider);
  }

  .mat-horizontal-stepper-header::before,
  .mat-horizontal-stepper-header::after,
  .mat-stepper-horizontal-line {
    border-top-color: mat-color($foreground, divider);
  }
}

@mixin mat-stepper-typography($config) {
  .mat-stepper-vertical, .mat-stepper-horizontal {
    font-family: mat-font-family($config);
  }

  .mat-step-label {
    font: {
      size: mat-font-size($config, body-1);
      weight: mat-font-weight($config, body-1);
    };
  }

  .mat-step-sub-label-error {
    font-weight: normal;
  }

  .mat-step-label-error {
    font-size: mat-font-size($config, body-2);
  }

  .mat-step-label-selected {
    font: {
      size: mat-font-size($config, body-2);
      weight: mat-font-weight($config, body-2);
    };
  }
}

@mixin mat-sort-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-sort-header-arrow {
    $table-background: mat-color($background, 'card');
    $text-color: mat-color($foreground, secondary-text);

    // Because the arrow is made up of multiple elements that are stacked on top of each other,
    // we can't use the semi-trasparent color from the theme directly. If the value is a color
    // *type*, we convert it into a solid color by taking the opacity from the rgba value and
    // using the value to determine the percentage of the background to put into foreground
    // when mixing the colors together. Otherwise, if it resolves to something different
    // (e.g. it resolves to a CSS variable), we use the color directly.
    @if (type-of($table-background) == color and type-of($text-color) == color) {
      $text-opacity: opacity($text-color);
      color: mix($table-background, rgba($text-color, 1), (1 - $text-opacity) * 100%);
    }
    @else {
      color: $text-color;
    }
  }
}

@mixin mat-sort-typography($config) { }





@mixin mat-tabs-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);
  $header-border: 1px solid mat-color($foreground, divider);

  .mat-tab-nav-bar,
  .mat-tab-header {
    border-bottom: $header-border;
  }

  .mat-tab-group-inverted-header {
    .mat-tab-nav-bar,
    .mat-tab-header {
      border-top: $header-border;
      border-bottom: none;
    }
  }

  .mat-tab-label, .mat-tab-link {
    color: mat-color($foreground, text);

    &.mat-tab-disabled {
      color: mat-color($foreground, disabled-text);
    }
  }

  .mat-tab-header-pagination-chevron {
    border-color: mat-color($foreground, text);
  }

  .mat-tab-header-pagination-disabled .mat-tab-header-pagination-chevron {
    border-color: mat-color($foreground, disabled-text);
  }

  // Remove header border when there is a background color
  .mat-tab-group[class*='mat-background-'] .mat-tab-header,
  .mat-tab-nav-bar[class*='mat-background-'] {
    border-bottom: none;
    border-top: none;
  }

  .mat-tab-group, .mat-tab-nav-bar {
    $theme-colors: (
      primary: $primary,
      accent: $accent,
      warn: $warn
    );

    @each $name, $color in $theme-colors {
      // Set the foreground color of the tabs
      &.mat-#{$name} {
        @include _mat-tab-label-focus($color);
        @include _mat-ink-bar($color);

        // Override ink bar when background color is the same
        &.mat-background-#{$name} {
          @include _mat-ink-bar($color, default-contrast);
        }
      }
    }

    @each $name, $color in $theme-colors {
      // Set background color of the tabs and override focus color
      &.mat-background-#{$name} {
        @include _mat-tab-label-focus($color);
        @include _mat-tabs-background($color);
      }
    }
  }
}

@mixin _mat-ink-bar($color, $hue: default) {
  .mat-ink-bar {
    background-color: mat-color($color, $hue);
  }
}

@mixin _mat-tab-label-focus($tab-focus-color) {
  .mat-tab-label,
  .mat-tab-link {
    &.cdk-keyboard-focused,
    &.cdk-program-focused {
      &:not(.mat-tab-disabled) {
        background-color: mat-color($tab-focus-color, lighter, 0.3);
      }
    }
  }
}

@mixin _mat-tabs-background($background-color) {
  // Set background color for the tab group
  .mat-tab-header, .mat-tab-links, .mat-tab-header-pagination {
    background-color: mat-color($background-color);
  }

  // Set labels to contrast against background
  .mat-tab-label, .mat-tab-link {
    color: mat-color($background-color, default-contrast);

    &.mat-tab-disabled {
      color: mat-color($background-color, default-contrast, 0.4);
    }
  }

  // Set pagination chevrons to contrast background
  .mat-tab-header-pagination-chevron {
    border-color: mat-color($background-color, default-contrast);
  }

  .mat-tab-header-pagination-disabled .mat-tab-header-pagination-chevron {
    border-color: mat-color($background-color, default-contrast, 0.4);
  }

  // Set ripples color to be the contrast color of the new background. Otherwise the ripple
  // color will be based on the app background color.
  .mat-ripple-element {
    background-color: mat-color($background-color, default-contrast, 0.12);
  }
}

@mixin mat-tabs-typography($config) {
  .mat-tab-group {
    font-family: mat-font-family($config);
  }

  .mat-tab-label, .mat-tab-link {
    font: {
      family: mat-font-family($config, button);
      size: mat-font-size($config, button);
      weight: mat-font-weight($config, button);
    }
  }
}






@mixin _mat-toolbar-color($palette) {
  background: mat-color($palette);
  color: mat-color($palette, default-contrast);
}

@mixin _mat-toolbar-form-field-overrides {
  .mat-form-field-underline,
  .mat-form-field-ripple,
  .mat-focused .mat-form-field-ripple {
    background-color: currentColor;
  }

  .mat-form-field-label,
  .mat-focused .mat-form-field-label,
  .mat-select-value,
  .mat-select-arrow,
  .mat-form-field.mat-focused .mat-select-arrow {
    color: inherit;
  }

  .mat-input-element {
    caret-color: currentColor;
  }
}

@mixin mat-toolbar-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-toolbar {
    background: mat-color($background, app-bar);
    color: mat-color($foreground, text);

    &.mat-primary {
      @include _mat-toolbar-color($primary);
    }

    &.mat-accent {
      @include _mat-toolbar-color($accent);
    }

    &.mat-warn {
      @include _mat-toolbar-color($warn);
    }

    @include _mat-toolbar-form-field-overrides;
  }
}

@mixin mat-toolbar-typography($config) {
  .mat-toolbar,
  .mat-toolbar h1,
  .mat-toolbar h2,
  .mat-toolbar h3,
  .mat-toolbar h4,
  .mat-toolbar h5,
  .mat-toolbar h6 {
    @include mat-typography-level-to-styles($config, title);
    margin: 0;
  }
}





$mat-tooltip-target-height: 22px;
$mat-tooltip-font-size: 10px;
$mat-tooltip-vertical-padding: ($mat-tooltip-target-height - $mat-tooltip-font-size) / 2;

$mat-tooltip-handset-target-height: 30px;
$mat-tooltip-handset-font-size: 14px;
$mat-tooltip-handset-vertical-padding:
    ($mat-tooltip-handset-target-height - $mat-tooltip-handset-font-size) / 2;

@mixin mat-tooltip-theme($theme) {
  .mat-tooltip {
    background: mat-color($mat-grey, 700, 0.9);
  }
}

@mixin mat-tooltip-typography($config) {
  .mat-tooltip {
    font-family: mat-font-family($config);
    font-size: $mat-tooltip-font-size;
    padding-top: $mat-tooltip-vertical-padding;
    padding-bottom: $mat-tooltip-vertical-padding;
  }

  .mat-tooltip-handset {
    font-size: $mat-tooltip-handset-font-size;
    padding-top: $mat-tooltip-handset-vertical-padding;
    padding-bottom: $mat-tooltip-handset-vertical-padding;
  }
}





@mixin mat-snack-bar-theme($theme) {
  $is-dark-theme: map-get($theme, is-dark);
  $accent: map-get($theme, accent);

  .mat-snack-bar-container {
    // Use the primary text on the dark theme, even though the lighter one uses
    // a secondary, because the contrast on the light primary text is poor.
    color: if($is-dark-theme, $dark-primary-text, $light-secondary-text);
    background: if($is-dark-theme, map-get($mat-grey, 50), #323232);

    @include _mat-theme-elevation(6, $theme);
  }

  .mat-simple-snackbar-action {
    color: if($is-dark-theme, inherit, mat-color($accent, text));
  }
}

@mixin mat-snack-bar-typography($config) {
  .mat-simple-snackbar {
    font: {
      family: mat-font-family($config, body-1);
      size: mat-font-size($config, body-1);
    }
  }

  .mat-simple-snackbar-action {
    line-height: 1;
    font: {
      family: inherit;
      size: inherit;
      weight: mat-font-weight($config, button);
    }
  }
}














// Theme styles that only apply to the fill appearance of the form-field.

@mixin mat-form-field-fill-theme($theme) {
  $foreground: map-get($theme, foreground);
  $is-dark-theme: map-get($theme, is-dark);

  $fill-background: mat-color($foreground, base, if($is-dark-theme, 0.1, 0.04));
  $fill-disabled-background: mat-color($foreground, base, if($is-dark-theme, 0.05, 0.02));
  $underline-color: mat-color($foreground, divider, if($is-dark-theme, 0.5, 0.42));
  $label-disabled-color: mat-color($foreground, disabled-text);

  .mat-form-field-appearance-fill {
    .mat-form-field-flex {
      background-color: $fill-background;
    }

    &.mat-form-field-disabled .mat-form-field-flex {
      background-color: $fill-disabled-background;
    }

    .mat-form-field-underline::before {
      background-color: $underline-color;
    }

    &.mat-form-field-disabled {
      .mat-form-field-label {
        color: $label-disabled-color;
      }

      .mat-form-field-underline::before {
        background-color: transparent;
      }
    }
  }
}

// Used to make instances of the _mat-form-field-label-floating mixin negligibly different,
// and prevent Google's CSS Optimizer from collapsing the declarations. This is needed because some
// of the selectors contain pseudo-classes not recognized in all browsers. If a browser encounters
// an unknown pseudo-class it will discard the entire rule set.
$mat-form-field-fill-dedupe: 0;

// Applies a floating label above the form field control itself.
@mixin _mat-form-field-fill-label-floating($font-scale, $infix-padding, $infix-margin-top) {
  transform: translateY(-$infix-margin-top - $infix-padding + $mat-form-field-fill-dedupe)
             scale($font-scale);
  width: 100% / $font-scale + $mat-form-field-fill-dedupe;

  $mat-form-field-fill-dedupe: $mat-form-field-fill-dedupe + 0.00001 !global;
}

@mixin mat-form-field-fill-typography($config) {
  // The unit-less line-height from the font config.
  $line-height: mat-line-height($config, input);
  // The amount to scale the font for the floating label and subscript.
  $subscript-font-scale: 0.75;
  // The padding on top of the infix.
  $infix-padding-top: 0.25em;
  // The padding below the infix.
  $infix-padding-bottom: 0.75em;
  // The margin applied to the form-field-infix to reserve space for the floating label.
  $infix-margin-top: 1em * $line-height * $subscript-font-scale;
  // The amount we offset the label from the input text in the fill appearance.
  $fill-appearance-label-offset: -0.5em;

  .mat-form-field-appearance-fill {
    .mat-form-field-infix {
      padding: $infix-padding-top 0 $infix-padding-bottom 0;
    }

    .mat-form-field-label {
      top: $infix-margin-top + $infix-padding-top;
      margin-top: $fill-appearance-label-offset;
    }

    &.mat-form-field-can-float {
      &.mat-form-field-should-float .mat-form-field-label,
      .mat-input-server:focus + .mat-form-field-label-wrapper .mat-form-field-label {
        @include _mat-form-field-fill-label-floating(
                $subscript-font-scale, $infix-padding-top + $fill-appearance-label-offset,
                $infix-margin-top);
      }

      // Server-side rendered matInput with a label attribute but label not shown
      // (used as a pure CSS stand-in for mat-form-field-should-float).
      .mat-input-server[label]:not(:label-shown) + .mat-form-field-label-wrapper
      .mat-form-field-label {
        @include _mat-form-field-fill-label-floating(
                $subscript-font-scale, $infix-padding-top + $fill-appearance-label-offset,
                $infix-margin-top);
      }
    }
  }
}







// Theme styles that only apply to the legacy appearance of the form-field.

@mixin mat-form-field-legacy-theme($theme) {
  $foreground: map-get($theme, foreground);
  $is-dark-theme: map-get($theme, is-dark);

  $label-color: mat-color($foreground, secondary-text);
  $underline-color: mat-color($foreground, divider, if($is-dark-theme, 0.7, 0.42));

  .mat-form-field-appearance-legacy {
    .mat-form-field-label {
      color: $label-color;
    }

    .mat-hint {
      color: $label-color;
    }

    .mat-form-field-underline {
      background-color: $underline-color;
    }

    &.mat-form-field-disabled .mat-form-field-underline {
      @include mat-control-disabled-underline($underline-color);
    }
  }
}

// Used to make instances of the _mat-form-field-label-floating mixin negligibly different,
// and prevent Google's CSS Optimizer from collapsing the declarations. This is needed because some
// of the selectors contain pseudo-classes not recognized in all browsers. If a browser encounters
// an unknown pseudo-class it will discard the entire rule set.
$mat-form-field-legacy-dedupe: 0;

// Applies a floating label above the form field control itself.
@mixin _mat-form-field-legacy-label-floating($font-scale, $infix-padding, $infix-margin-top) {
  // We use perspective to fix the text blurriness as described here:
  // http://www.useragentman.com/blog/2014/05/04/fixing-typography-inside-of-2-d-css-transforms/
  // This results in a small jitter after the label floats on Firefox, which the
  // translateZ fixes.
  transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale) perspective(100px)
  translateZ(0.001px + $mat-form-field-legacy-dedupe);
  // The tricks above used to smooth out the animation on chrome and firefox actually make things
  // worse on IE, so we don't include them in the IE version.
  -ms-transform: translateY(-$infix-margin-top - $infix-padding + $mat-form-field-legacy-dedupe)
                  scale($font-scale);

  width: 100% / $font-scale + $mat-form-field-legacy-dedupe;

  $mat-form-field-legacy-dedupe: $mat-form-field-legacy-dedupe + 0.00001 !global;
}

// Same as mixin above, but omits the translateZ for printing purposes.
@mixin _mat-form-field-legacy-label-floating-print($font-scale, $infix-padding, $infix-margin-top) {
  // This results in a small jitter after the label floats on Firefox, which the
  // translateZ fixes.
  transform: translateY(-$infix-margin-top - $infix-padding + $mat-form-field-legacy-dedupe)
                  scale($font-scale);
  // The tricks above used to smooth out the animation on chrome and firefox actually make things
  // worse on IE, so we don't include them in the IE version.
  $mat-form-field-legacy-dedupe: $mat-form-field-legacy-dedupe + 0.00001 !global;
}

@mixin mat-form-field-legacy-typography($config) {
  // The unit-less line-height from the font config.
  $line-height: mat-line-height($config, input);
  // The amount to scale the font for the floating label and subscript.
  $subscript-font-scale: 0.75;
  // The amount of space between the top of the line and the top of the actual text
  // (as a fraction of the font-size).
  $line-spacing: ($line-height - 1) / 2;
  // The padding on the infix. Mocks show half of the text size, but seem to measure from the edge
  // of the text itself, not the edge of the line; therefore we subtract off the line spacing.
  $infix-padding: 0.5em - $line-spacing;
  // The margin applied to the form-field-infix to reserve space for the floating label.
  $infix-margin-top: 1em * $line-height * $subscript-font-scale;
  // The space between the bottom of the .mat-form-field-flex area and the subscript wrapper.
  // Mocks show half of the text size, but this margin is applied to an element with the subscript
  // text font size, so we need to divide by the scale factor to make it half of the original text
  // size. We again need to subtract off the line spacing since the mocks measure to the edge of the
  // text, not the  edge of the line.
  $subscript-margin-top: 0.5em / $subscript-font-scale - ($line-spacing * 2);
  // The padding applied to the form-field-wrapper to reserve space for the subscript, since it's
  // absolutely positioned. This is a combination of the subscript's margin and line-height, but we
  // need to multiply by the subscript font scale factor since the wrapper has a larger font size.
  $wrapper-padding-bottom: ($subscript-margin-top + $line-height) * $subscript-font-scale;

  .mat-form-field-appearance-legacy {
    .mat-form-field-wrapper {
      padding-bottom: $wrapper-padding-bottom;
    }

    .mat-form-field-infix {
      padding: $infix-padding 0;
    }

    &.mat-form-field-can-float {
      &.mat-form-field-should-float .mat-form-field-label,
      .mat-input-server:focus + .mat-form-field-label-wrapper .mat-form-field-label {
        @include _mat-form-field-legacy-label-floating(
                $subscript-font-scale, $infix-padding, $infix-margin-top);
      }

      // @breaking-change 8.0.0 will rely on AutofillMonitor instead.
      .mat-form-field-autofill-control:-webkit-autofill + .mat-form-field-label-wrapper
      .mat-form-field-label {
        @include _mat-form-field-legacy-label-floating(
                $subscript-font-scale, $infix-padding, $infix-margin-top);
      }

      // Server-side rendered matInput with a label attribute but label not shown
      // (used as a pure CSS stand-in for mat-form-field-should-float).
      .mat-input-server[label]:not(:label-shown) + .mat-form-field-label-wrapper
      .mat-form-field-label {
        @include _mat-form-field-legacy-label-floating(
                $subscript-font-scale, $infix-padding, $infix-margin-top);
      }
    }

    .mat-form-field-label {
      top: $infix-margin-top + $infix-padding;
    }

    .mat-form-field-underline {
      // We want the underline to start at the end of the content box, not the padding box,
      // so we move it up by the padding amount.
      bottom: $wrapper-padding-bottom;
    }

    .mat-form-field-subscript-wrapper {
      margin-top: $subscript-margin-top;

      // We want the subscript to start at the end of the content box, not the padding box,
      // so we move it up by the padding amount (adjusted for the smaller font size);
      top: calc(100% - #{$wrapper-padding-bottom / $subscript-font-scale});
    }
  }

  // translateZ causes the label to not appear while printing, so we override it to not
  // apply translateZ while printing
  @media print {
    .mat-form-field-appearance-legacy {
      &.mat-form-field-can-float {
        &.mat-form-field-should-float .mat-form-field-label,
        .mat-input-server:focus + .mat-form-field-label-wrapper .mat-form-field-label {
          @include _mat-form-field-legacy-label-floating-print(
                  $subscript-font-scale, $infix-padding, $infix-margin-top);
        }

        // @breaking-change 8.0.0 will rely on AutofillMonitor instead.
        .mat-form-field-autofill-control:-webkit-autofill + .mat-form-field-label-wrapper
        .mat-form-field-label {
          @include _mat-form-field-legacy-label-floating-print(
                  $subscript-font-scale, $infix-padding, $infix-margin-top);
        }

        // Server-side rendered matInput with a label attribute but label not shown
        // (used as a pure CSS stand-in for mat-form-field-should-float).
        .mat-input-server[label]:not(:label-shown) + .mat-form-field-label-wrapper
        .mat-form-field-label {
          @include _mat-form-field-legacy-label-floating-print(
                  $subscript-font-scale, $infix-padding, $infix-margin-top);
        }
      }
    }
  }
}







// Theme styles that only apply to the outline appearance of the form-field.

@mixin mat-form-field-outline-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $foreground: map-get($theme, foreground);
  $is-dark-theme: map-get($theme, is-dark);

  $label-disabled-color: mat-color($foreground, disabled-text);
  $outline-color: mat-color($foreground, divider, if($is-dark-theme, 0.3, 0.12));
  $outline-color-hover: mat-color($foreground, divider, if($is-dark-theme, 1, 0.87));
  $outline-color-primary: mat-color($primary);
  $outline-color-accent: mat-color($accent);
  $outline-color-warn: mat-color($warn);
  $outline-color-disabled: mat-color($foreground, divider, if($is-dark-theme, 0.15, 0.06));

  .mat-form-field-appearance-outline {
    .mat-form-field-outline {
      color: $outline-color;
    }

    .mat-form-field-outline-thick {
      color: $outline-color-hover;
    }

    &.mat-focused {
      .mat-form-field-outline-thick {
        color: $outline-color-primary;
      }

      &.mat-accent .mat-form-field-outline-thick {
        color: $outline-color-accent;
      }

      &.mat-warn .mat-form-field-outline-thick {
        color: $outline-color-warn;
      }
    }

    // Class repeated so that rule is specific enough to override focused accent color case.
    &.mat-form-field-invalid.mat-form-field-invalid {
      .mat-form-field-outline-thick {
        color: $outline-color-warn;
      }
    }

    &.mat-form-field-disabled {
      .mat-form-field-label {
        color: $label-disabled-color;
      }

      .mat-form-field-outline {
        color: $outline-color-disabled;
      }
    }
  }
}

// Used to make instances of the _mat-form-field-label-floating mixin negligibly different,
// and prevent Google's CSS Optimizer from collapsing the declarations. This is needed because some
// of the selectors contain pseudo-classes not recognized in all browsers. If a browser encounters
// an unknown pseudo-class it will discard the entire rule set.
$mat-form-field-outline-dedupe: 0;

// Applies a floating label above the form field control itself.
@mixin _mat-form-field-outline-label-floating($font-scale, $infix-padding, $infix-margin-top) {
  transform: translateY(-$infix-margin-top - $infix-padding + $mat-form-field-outline-dedupe)
  scale($font-scale);
  width: 100% / $font-scale + $mat-form-field-outline-dedupe;

  $mat-form-field-outline-dedupe: $mat-form-field-outline-dedupe + 0.00001 !global;
}

@mixin mat-form-field-outline-typography($config) {
  // The unit-less line-height from the font config.
  $line-height: mat-line-height($config, input);
  // The amount to scale the font for the floating label and subscript.
  $subscript-font-scale: 0.75;
  // The padding above and below the infix.
  $infix-padding: 1em;
  // The margin applied to the form-field-infix to reserve space for the floating label.
  $infix-margin-top: 1em * $line-height * $subscript-font-scale;
  // The space between the bottom of the .mat-form-field-flex area and the subscript wrapper.
  // Mocks show half of the text size, but this margin is applied to an element with the subscript
  // text font size, so we need to divide by the scale factor to make it half of the original text
  // size.
  $subscript-margin-top: 0.5em / $subscript-font-scale;
  // The padding applied to the form-field-wrapper to reserve space for the subscript, since it's
  // absolutely positioned. This is a combination of the subscript's margin and line-height, but we
  // need to multiply by the subscript font scale factor since the wrapper has a larger font size.
  $wrapper-padding-bottom: ($subscript-margin-top + $line-height) * $subscript-font-scale;
  // The amount we offset the label from the input text in the outline appearance.
  $outline-appearance-label-offset: -0.25em;

  .mat-form-field-appearance-outline {
    .mat-form-field-infix {
      padding: $infix-padding 0 $infix-padding 0;
    }

    .mat-form-field-label {
      top: $infix-margin-top + $infix-padding;
      margin-top: $outline-appearance-label-offset;
    }

    &.mat-form-field-can-float {
      &.mat-form-field-should-float .mat-form-field-label,
      .mat-input-server:focus + .mat-form-field-label-wrapper .mat-form-field-label {
        @include _mat-form-field-outline-label-floating(
                $subscript-font-scale, $infix-padding + $outline-appearance-label-offset,
                $infix-margin-top);
      }

      // Server-side rendered matInput with a label attribute but label not shown
      // (used as a pure CSS stand-in for mat-form-field-should-float).
      .mat-input-server[label]:not(:label-shown) + .mat-form-field-label-wrapper
      .mat-form-field-label {
        @include _mat-form-field-outline-label-floating(
                $subscript-font-scale, $infix-padding + $outline-appearance-label-offset,
                $infix-margin-top);
      }
    }
  }
}







// Theme styles that only apply to the standard appearance of the form-field.

@mixin mat-form-field-standard-theme($theme) {
  $foreground: map-get($theme, foreground);
  $is-dark-theme: map-get($theme, is-dark);

  $underline-color: mat-color($foreground, divider, if($is-dark-theme, 0.7, 0.42));

  .mat-form-field-appearance-standard {
    .mat-form-field-underline {
      background-color: $underline-color;
    }

    &.mat-form-field-disabled .mat-form-field-underline {
      @include mat-control-disabled-underline($underline-color);
    }
  }
}

@mixin mat-form-field-standard-typography($config) {}


// Theme styles that apply to all appearances of the form-field.
@mixin mat-form-field-theme($theme) {
  $primary: map-get($theme, primary);
  $accent: map-get($theme, accent);
  $warn: map-get($theme, warn);
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);
  $is-dark-theme: map-get($theme, is-dark);

  // Label colors. Required is used for the `*` star shown in the label.
  $label-color: mat-color($foreground, secondary-text, if($is-dark-theme, 0.7, 0.6));
  $focused-label-color: mat-color($primary, text);
  $required-label-color: mat-color($accent, text);

  // Underline colors.
  $underline-color-base: mat-color($foreground, divider, if($is-dark-theme, 1, 0.87));
  $underline-color-accent: mat-color($accent, text);
  $underline-color-warn: mat-color($warn, text);
  $underline-focused-color: mat-color($primary, text);

  .mat-form-field-label {
    color: $label-color;
  }

  .mat-hint {
    color: $label-color;
  }

  .mat-form-field.mat-focused .mat-form-field-label {
    color: $focused-label-color;

    &.mat-accent {
      color: $underline-color-accent;
    }

    &.mat-warn {
      color: $underline-color-warn;
    }
  }

  .mat-focused .mat-form-field-required-marker {
    color: $required-label-color;
  }

  .mat-form-field-ripple {
    background-color: $underline-color-base;
  }

  .mat-form-field.mat-focused {
    .mat-form-field-ripple {
      background-color: $underline-focused-color;

      &.mat-accent {
        background-color: $underline-color-accent;
      }

      &.mat-warn {
        background-color: $underline-color-warn;
      }
    }
  }

  .mat-form-field-type-mat-native-select.mat-focused:not(.mat-form-field-invalid) {
    .mat-form-field-infix::after {
      color: $underline-focused-color;
    }

    &.mat-accent .mat-form-field-infix::after {
      color: $underline-color-accent;
    }

    &.mat-warn .mat-form-field-infix::after {
      color: $underline-color-warn;
    }
  }

  // Styling for the error state of the form field. Note that while the same can be
  // achieved with the ng-* classes, we use this approach in order to ensure that the same
  // logic is used to style the error state and to show the error messages.
  .mat-form-field.mat-form-field-invalid {
    .mat-form-field-label {
      color: $underline-color-warn;

      &.mat-accent,
      .mat-form-field-required-marker {
        color: $underline-color-warn;
      }
    }

    .mat-form-field-ripple,
    .mat-form-field-ripple.mat-accent {
      background-color: $underline-color-warn;
    }
  }

  .mat-error {
    color: $underline-color-warn;
  }

  @include mat-form-field-legacy-theme($theme);
  @include mat-form-field-standard-theme($theme);
  @include mat-form-field-fill-theme($theme);
  @include mat-form-field-outline-theme($theme);
}

// Used to make instances of the _mat-form-field-label-floating mixin negligibly different,
// and prevent Google's CSS Optimizer from collapsing the declarations. This is needed because some
// of the selectors contain pseudo-classes not recognized in all browsers. If a browser encounters
// an unknown pseudo-class it will discard the entire rule set.
$mat-form-field-dedupe: 0;

// Applies a floating label above the form field control itself.
@mixin _mat-form-field-label-floating($font-scale, $infix-padding, $infix-margin-top) {
  transform: translateY(-$infix-margin-top - $infix-padding + $mat-form-field-dedupe)
             scale($font-scale);
  width: 100% / $font-scale + $mat-form-field-dedupe;

  $mat-form-field-dedupe: $mat-form-field-dedupe + 0.00001 !global;
}

@mixin mat-form-field-typography($config) {
  // The unit-less line-height from the font config.
  $line-height: mat-line-height($config, input);

  // The amount to scale the font for the floating label and subscript.
  $subscript-font-scale: 0.75;
  // The amount to scale the font for the prefix and suffix icons.
  $prefix-suffix-icon-font-scale: 1.5;

  // The padding on the infix. Mocks show half of the text size.
  $infix-padding: 0.5em;
  // The margin applied to the form-field-infix to reserve space for the floating label.
  $infix-margin-top: 1em * $line-height * $subscript-font-scale;
  // Font size to use for the label and subscript text.
  $subscript-font-size: $subscript-font-scale * 100%;
  // Font size to use for the for the prefix and suffix icons.
  $prefix-suffix-icon-font-size: $prefix-suffix-icon-font-scale * 100%;
  // The space between the bottom of the .mat-form-field-flex area and the subscript wrapper.
  // Mocks show half of the text size, but this margin is applied to an element with the subscript
  // text font size, so we need to divide by the scale factor to make it half of the original text
  // size.
  $subscript-margin-top: 0.5em / $subscript-font-scale;
  // The padding applied to the form-field-wrapper to reserve space for the subscript, since it's
  // absolutely positioned. This is a combination of the subscript's margin and line-height, but we
  // need to multiply by the subscript font scale factor since the wrapper has a larger font size.
  $wrapper-padding-bottom: ($subscript-margin-top + $line-height) * $subscript-font-scale;

  .mat-form-field {
    @include mat-typography-level-to-styles($config, input);
  }

  .mat-form-field-wrapper {
    padding-bottom: $wrapper-padding-bottom;
  }

  .mat-form-field-prefix,
  .mat-form-field-suffix {
    // Allow icons in a prefix or suffix to adapt to the correct size.
    .mat-icon {
      font-size: $prefix-suffix-icon-font-size;
      line-height: $line-height;
    }

    // Allow icon buttons in a prefix or suffix to adapt to the correct size.
    .mat-icon-button {
      height: $prefix-suffix-icon-font-scale * 1em;
      width: $prefix-suffix-icon-font-scale * 1em;

      .mat-icon {
        height: $line-height * 1em;
        line-height: $line-height;
      }
    }
  }

  .mat-form-field-infix {
    padding: $infix-padding 0;
    // Throws off the baseline if we do it as a real margin, so we do it as a border instead.
    border-top: $infix-margin-top solid transparent;
  }

  .mat-form-field-can-float {
    &.mat-form-field-should-float .mat-form-field-label,
    .mat-input-server:focus + .mat-form-field-label-wrapper .mat-form-field-label {
      @include _mat-form-field-label-floating(
              $subscript-font-scale, $infix-padding, $infix-margin-top);
    }

    // Server-side rendered matInput with a label attribute but label not shown
    // (used as a pure CSS stand-in for mat-form-field-should-float).
    .mat-input-server[label]:not(:label-shown) + .mat-form-field-label-wrapper
        .mat-form-field-label {
      @include _mat-form-field-label-floating(
              $subscript-font-scale, $infix-padding, $infix-margin-top);
    }
  }

  .mat-form-field-label-wrapper {
    top: -$infix-margin-top;
    padding-top: $infix-margin-top;
  }

  .mat-form-field-label {
    top: $infix-margin-top + $infix-padding;
  }

  .mat-form-field-underline {
    // We want the underline to start at the end of the content box, not the padding box,
    // so we move it up by the padding amount.
    bottom: $wrapper-padding-bottom;
  }

  .mat-form-field-subscript-wrapper {
    font-size: $subscript-font-size;
    margin-top: $subscript-margin-top;

    // We want the subscript to start at the end of the content box, not the padding box,
    // so we move it up by the padding amount (adjusted for the smaller font size);
    top: calc(100% - #{$wrapper-padding-bottom / $subscript-font-scale});
  }

  @include mat-form-field-legacy-typography($config);
  @include mat-form-field-standard-typography($config);
  @include mat-form-field-fill-typography($config);
  @include mat-form-field-outline-typography($config);
}





@mixin mat-tree-theme($theme) {
  $background: map-get($theme, background);
  $foreground: map-get($theme, foreground);

  .mat-tree {
    background: mat-color($background, 'card');
  }

  .mat-tree-node,
  .mat-nested-tree-node {
    color: mat-color($foreground, text);
  }
}

@mixin mat-tree-typography($config) {
  .mat-tree {
    font-family: mat-font-family($config);
  }

  .mat-tree-node,
  .mat-nested-tree-node {
    font-weight: mat-font-weight($config, body-1);
    font-size: mat-font-size($config, body-1);
  }
}



// Includes all of the typographic styles.
@mixin angular-material-typography($config: null) {
  @if $config == null {
    $config: mat-typography-config();
  }

  @include mat-badge-typography($config);
  @include mat-base-typography($config);
  @include mat-autocomplete-typography($config);
  @include mat-bottom-sheet-typography($config);
  @include mat-button-typography($config);
  @include mat-button-toggle-typography($config);
  @include mat-card-typography($config);
  @include mat-checkbox-typography($config);
  @include mat-chips-typography($config);
  @include mat-table-typography($config);
  @include mat-datepicker-typography($config);
  @include mat-dialog-typography($config);
  @include mat-expansion-panel-typography($config);
  @include mat-form-field-typography($config);
  @include mat-grid-list-typography($config);
  @include mat-icon-typography($config);
  @include mat-input-typography($config);
  @include mat-menu-typography($config);
  @include mat-paginator-typography($config);
  @include mat-progress-bar-typography($config);
  @include mat-progress-spinner-typography($config);
  @include mat-radio-typography($config);
  @include mat-select-typography($config);
  @include mat-sidenav-typography($config);
  @include mat-slide-toggle-typography($config);
  @include mat-slider-typography($config);
  @include mat-stepper-typography($config);
  @include mat-sort-typography($config);
  @include mat-tabs-typography($config);
  @include mat-toolbar-typography($config);
  @include mat-tooltip-typography($config);
  @include mat-list-typography($config);
  @include mat-option-typography($config);
  @include mat-optgroup-typography($config);
  @include mat-snack-bar-typography($config);
  @include mat-tree-typography($config);
}


// Mixin that renders all of the core styles that are not theme-dependent.
@mixin mat-core($typography-config: null) {
  @include angular-material-typography($typography-config);
  @include mat-ripple();
  @include cdk-a11y();
  @include cdk-overlay();
  @include cdk-text-field();
}

// Mixin that renders all of the core styles that depend on the theme.
@mixin mat-core-theme($theme) {
  @include mat-ripple-theme($theme);
  @include mat-option-theme($theme);
  @include mat-optgroup-theme($theme);
  @include mat-pseudo-checkbox-theme($theme);

  // Provides external CSS classes for each elevation value. Each CSS class is formatted as
  // `mat-elevation-z$zValue` where `$zValue` corresponds to the z-space to which the element is
  // elevated.
  @for $zValue from 0 through 24 {
    .#{$_mat-elevation-prefix}#{$zValue} {
      @include _mat-theme-elevation($zValue, $theme);
    }
  }

  // Wrapper element that provides the theme background when the user's content isn't
  // inside of a `mat-sidenav-container`. Note that we need to exclude the ampersand
  // selector in case the mixin is included at the top level.
  .mat-app-background#{if(&, ', &.mat-app-background', '')} {
    $background: map-get($theme, background);
    $foreground: map-get($theme, foreground);

    background-color: mat-color($background, background);
    color: mat-color($foreground, text);
  }

  // Marker that is used to determine whether the user has added a theme to their page.
  @at-root {
    .mat-theme-loaded-marker {
      display: none;
    }
  }
}
















@mixin mat-divider-theme($theme) {
  $foreground: map-get($theme, foreground);

  .mat-divider {
    border-top-color: mat-color($foreground, divider);
  }

  .mat-divider-vertical {
    border-right-color: mat-color($foreground, divider);
  }
}

























// Create a theme.
@mixin angular-material-theme($theme) {
  @include mat-core-theme($theme);
  @include mat-autocomplete-theme($theme);
  @include mat-badge-theme($theme);
  @include mat-bottom-sheet-theme($theme);
  @include mat-button-theme($theme);
  @include mat-button-toggle-theme($theme);
  @include mat-card-theme($theme);
  @include mat-checkbox-theme($theme);
  @include mat-chips-theme($theme);
  @include mat-table-theme($theme);
  @include mat-datepicker-theme($theme);
  @include mat-dialog-theme($theme);
  @include mat-divider-theme($theme);
  @include mat-expansion-panel-theme($theme);
  @include mat-form-field-theme($theme);
  @include mat-grid-list-theme($theme);
  @include mat-icon-theme($theme);
  @include mat-input-theme($theme);
  @include mat-list-theme($theme);
  @include mat-menu-theme($theme);
  @include mat-paginator-theme($theme);
  @include mat-progress-bar-theme($theme);
  @include mat-progress-spinner-theme($theme);
  @include mat-radio-theme($theme);
  @include mat-select-theme($theme);
  @include mat-sidenav-theme($theme);
  @include mat-slide-toggle-theme($theme);
  @include mat-slider-theme($theme);
  @include mat-stepper-theme($theme);
  @include mat-sort-theme($theme);
  @include mat-tabs-theme($theme);
  @include mat-toolbar-theme($theme);
  @include mat-tooltip-theme($theme);
  @include mat-tree-theme($theme);
  @include mat-snack-bar-theme($theme);
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\custom-effects.scss

//
button:hover:not([disabled]),
ion-button:hover:not([disabled]) {
  @include animation("flash", 1500ms);
}

.picker-opt-disabled {
  opacity: 0.5;
}

.action-sheet-cancel,
.action-sheet-setructive,
.action-sheet-group-cancel {
  color: var(--x-color-danger) !important;
}

//
// Dialog Theme ...
::ng-deep.mat-card,
::ng-deep.mat-expansion-panel,
::ng-deep.mat-dialog-container {
  color: var(--x-color-light-contrast);
  background-color: var(--x-color-light);
}

::ng-deep.full-with {
  .x-wrapper {
    width: 100%;
  }
}

.full-with {
  width: 100%;
}

.x-minimal-popover {
  .popover-content {
    width: 90px !important;
    max-width: 90px !important;
  }
}

.dynamic-slot {
  flex: 0 auto !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\fab-theme.scss

//
//#region Fab ...
$isRTL: var(--is-rtl);
$fabMargin: 16px;

x-fab[pageFab] {
  //
  ion-fab {
    //
    @if $isRTL {
      left: $fabMargin !important;
      right: auto !important;
     } @else {
      right: $fabMargin !important;
      left: auto !important;
     }

    //
    position: fixed !important;
    margin-top: calc(var(--content-offset-bottom) * -1);
    margin-bottom: calc(var(--content-offset-bottom) * -1);

    //
    transition: transform 250ms ease 0s, opacity 250ms ease 0s;
  }

  //
  &.start-scroll {
    ion-fab {
      transform: translateY(200px) scale3d(0, 0, 0) !important;
    }
  }
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\markdown-theme.scss

/**
 * MIT License
 * Copyright (c) 2021 Ayush Saini
 * Holi Theme for prism.js
 * @author Ayush Saini <@AyushCodes on Twitter>
 */

 code[class*='language-'],
 pre[class*='language-'] {
   color: #d6e7ff;
   background: #030314;
   text-shadow: none;
   font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
   font-size: 1em;
   line-height: 1.5;
   letter-spacing: .2px;
   white-space: pre;
   word-spacing: normal;
   word-break: normal;
   word-wrap: normal;
   text-align: left;
 
   -moz-tab-size: 4;
   -o-tab-size: 4;
   tab-size: 4;
 
   -webkit-hyphens: none;
   -moz-hyphens: none;
   -ms-hyphens: none;
   hyphens: none;
 }
 
 pre[class*='language-']::-moz-selection,
 pre[class*='language-'] ::-moz-selection,
 code[class*='language-']::-moz-selection,
 code[class*='language-'] ::-moz-selection,
 pre[class*='language-']::selection,
 pre[class*='language-'] ::selection,
 code[class*='language-']::selection,
 code[class*='language-'] ::selection {
   color: inherit;
   background: #1d3b54;
   text-shadow: none;
 }
 
 pre[class*='language-'] {
   border: 1px solid #2a4555;
   border-radius: 5px;
   padding: 1.5em 1em;
   margin: 1em 0;
   overflow: auto;
 }
 
 :not(pre) > code[class*='language-'] {
   color: #f0f6f6;
   background: #2a4555;
   padding: 0.2em 0.3em;
   border-radius: 0.2em;
   box-decoration-break: clone;
 }
 
 .token.comment,
 .token.prolog,
 .token.doctype,
 .token.cdata {
   color: #446e69;
 }
 
 .token.punctuation {
   color: #d6b007;
 }
 
 .token.property,
 .token.tag,
 .token.boolean,
 .token.number,
 .token.constant,
 .token.symbol,
 .token.deleted {
   color: #d6e7ff;
 }
 
 .token.selector,
 .token.attr-name,
 .token.builtin,
 .token.inserted {
   color: #e60067;
 }
 
 .token.string,
 .token.char {
   color: #49c6ec;
 }
 
 .token.operator,
 .token.entity,
 .token.url,
 .language-css .token.string,
 .style .token.string {
   color: #ec8e01;
   background: transparent;
 }
 
 .token.atrule,
 .token.attr-value,
 .token.keyword {
   color: #0fe468;
 }
 
 .token.function,
 .token.class-name {
   color: #78f3e9;
 }
 
 .token.regex,
 .token.important,
 .token.variable {
   color: #d6e7ff;
 }

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\material-fonts.scss

//
//#region Material Fonts ...
/* cyrillic-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fCRc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,
    U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fABc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fCBc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fBxc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fCxc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: local("Roboto Light"), local("Roboto-Light"),
    url("../assets/font/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2")
      format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
/* Persian */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 300;
  src: url("../assets/font/irsans.ttf");
  unicode-renge: U+0621, U+0623, U+0626, U+0624, U+0627, U+0628, U+067E, U+062A,
    U+062B, U+062C, U+0686, U+062D, U+062E, U+062F, U+0630, U+0631, U+0632,
    U+0698, U+0633, U+0634, U+0635, U+0636, U+0637, U+0638, U+0639, U+063A,
    U+0641, U+0642, U+06A9, U+06AF, U+0644, U+0645, U+0646, U+0648, U+0647,
    U+06CC, U+0622, U+06C0, U+0644, U+0627, U+0640, U+067E, U+0686, U+0698,
    U+06AF, U+0660, U+0661, U+0662, U+0663, U+0664, U+0665, U+0666, U+0667,
    U+0668, U+0669, U+064A, U+0643;
}

/* cyrillic-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2")
      format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,
    U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2")
      format("woff2");
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2")
      format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2")
      format("woff2");
  unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2")
      format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2")
      format("woff2");
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"), local("Roboto-Regular"),
    url("../assets/font/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2")
      format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
/* Persian */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: url("../assets/font/irsans.ttf");
  unicode-renge: U+0621, U+0623, U+0626, U+0624, U+0627, U+0628, U+067E, U+062A,
    U+062B, U+062C, U+0686, U+062D, U+062E, U+062F, U+0630, U+0631, U+0632,
    U+0698, U+0633, U+0634, U+0635, U+0636, U+0637, U+0638, U+0639, U+063A,
    U+0641, U+0642, U+06A9, U+06AF, U+0644, U+0645, U+0646, U+0648, U+0647,
    U+06CC, U+0622, U+06C0, U+0644, U+0627, U+0640, U+067E, U+0686, U+0698,
    U+06AF, U+0660, U+0661, U+0662, U+0663, U+0664, U+0665, U+0666, U+0667,
    U+0668, U+0669, U+064A, U+0643;
}


/* cyrillic-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fCRc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,
    U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fCBc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fBxc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fCxc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2")
      format("woff2");
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: local("Roboto Medium"), local("Roboto-Medium"),
    url("../assets/font/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2")
      format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
/* Persian */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  src: url("../assets/font/irsans.ttf");
  unicode-renge: U+0621, U+0623, U+0626, U+0624, U+0627, U+0628, U+067E, U+062A,
    U+062B, U+062C, U+0686, U+062D, U+062E, U+062F, U+0630, U+0631, U+0632,
    U+0698, U+0633, U+0634, U+0635, U+0636, U+0637, U+0638, U+0639, U+063A,
    U+0641, U+0642, U+06A9, U+06AF, U+0644, U+0645, U+0646, U+0648, U+0647,
    U+06CC, U+0622, U+06C0, U+0644, U+0627, U+0640, U+067E, U+0686, U+0698,
    U+06AF, U+0660, U+0661, U+0662, U+0663, U+0664, U+0665, U+0666, U+0667,
    U+0668, U+0669, U+064A, U+0643;
}
//#endregion

//
//#region Material Design Icons ...
@font-face {
  font-family: "Material Icons";
  font-style: normal;
  font-weight: 400;
  src: url("../assets/font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2")
    format("woff2");
}

.material-icons {
  font-family: "Material Icons";
  font-weight: normal;
  font-style: normal;
  font-size: 24px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
}
//#endregion


@mixin x-toolbar-typography($config) {
  .mat-toolbar {
    @include mat-typography-level-to-styles($config, body-2);
    margin: 0;
  }

  .mat-toolbar h1 {
    @include mat-typography-level-to-styles($config, headline);
    margin: 0;
  }

  .mat-toolbar h2 {
    @include mat-typography-level-to-styles($config, title);
    margin: 0;
  }

  .mat-toolbar h3 {
    @include mat-typography-level-to-styles($config, subheading-2);
    margin: 0;
  }

  .mat-toolbar h4 {
    @include mat-typography-level-to-styles($config, subheading-1);
    margin: 0;
  }

  .mat-toolbar h5 {
    @include mat-typography-level-to-styles($config, body-1);
    margin: 0;
  }

  .mat-toolbar h6 {
    @include mat-typography-level-to-styles($config, caption);
    margin: 0;
  }
}


body {
  @extend .mat-typography;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\material-icons.scss

@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url('../assets/font/MaterialIcons-Regular.eot'); /* For IE6-8 */
  src: local('Material Icons'), local('MaterialIcons-Regular'),
    url('../assets/font/MaterialIcons-Regular.woff2') format('woff2'),
    url('../assets/font/MaterialIcons-Regular.woff') format('woff'),
    url('../assets/font/MaterialIcons-Regular.ttf') format('truetype');
}

.material-icons {
  font-family: 'Material Icons';
  font-weight: normal;
  font-style: normal;
  font-size: 24px; /* Preferred icon size */
  display: inline-block;
  line-height: 1;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: normal;
  white-space: nowrap;
  direction: ltr;

  /* Support for all WebKit browsers. */
  -webkit-font-smoothing: antialiased;
  /* Support for Safari and Chrome. */
  text-rendering: optimizeLegibility;

  /* Support for Firefox. */
  -moz-osx-font-smoothing: grayscale;

  /* Support for IE. */
  font-feature-settings: 'liga';
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\theme\theme.scss

@import "./base-theme.scss";
@import "./material-icons.scss";
@import "./material-fonts.scss";
@import "./markdown-theme.scss";
@import "./fab-theme.scss";

@include mat-core();

$x-primary: mat-palette($mat-blue);
$x-accent: mat-palette($mat-cyan);
$x-warn: mat-palette($mat-red);

// Create the theme object (a Sass map containing all of the palettes).
$x-theme: mat-light-theme($x-primary, $x-accent, $x-warn);
@include angular-material-theme($x-theme);

//
@function generate-x-theme($primary, $accent, $warn) {
  //
  $x-primary: $primary;
  $x-accent: $accent;
  $x-warn: $warn;

  //
  $x-theme: mat-light-theme($primary, $accent, $warn);

  //
  @return $x-theme;
}

//
@mixin x-theme($theme) {
  @include angular-material-theme($theme);
}

//
@mixin x-ion-color() {
  //
  // XFramework Color Mechanism ...
  :root {
    --x-color-primary: var(--ion-color-primary);
    --x-color-primary-shade: var(--ion-color-primary-shade);
    --x-color-primary-tint: var(--ion-color-primary-tint);

    --x-color-secondary: var(--ion-color-secondary);
    --x-color-secondary-shade: var(--ion-color-secondary-shade);
    --x-color-secondary-tint: var(--ion-color-secondary-tint);

    --x-color-tertiary: var(--ion-color-tertiary);
    --x-color-tertiary-shade: var(--ion-color-tertiary-shade);
    --x-color-tertiary-tint: var(--ion-color-tertiary-tint);

    --x-color-success: var(--ion-color-success);
    --x-color-success-shade: var(--ion-color-success-shade);
    --x-color-success-tint: var(--ion-color-success-tint);

    --x-color-warning: var(--ion-color-warning);
    --x-color-warning-shade: var(--ion-color-warning-shade);
    --x-color-warning-tint: var(--ion-color-warning-tint);

    --x-color-danger: var(--ion-color-danger);
    --x-color-danger-shade: var(--ion-color-danger-shade);
    --x-color-danger-tint: var(--ion-color-danger-tint);

    --x-color-dark: var(--ion-color-dark);
    --x-color-dark-shade: var(--ion-color-dark-shade);
    --x-color-dark-tint: var(--ion-color-dark-tint);

    --x-color-medium: var(--ion-color-medium);
    --x-color-medium-shade: var(--ion-color-medium-shade);
    --x-color-medium-tint: var(--ion-color-medium-tint);

    --x-color-light: var(--ion-color-light);
    --x-color-light-shade: var(--ion-color-light-shade);
    --x-color-light-tint: var(--ion-color-light-tint);

    --x-color-primary-contrast: var(--ion-color-primary-contrast);
    --x-color-primary-shade-contrast: var(--ion-color-primary-contrast);
    --x-color-primary-tint-contrast: var(--ion-color-primary-contrast);

    --x-color-secondary-contrast: var(--ion-color-secondary-contrast);
    --x-color-secondary-shade-contrast: var(--ion-color-secondary-contrast);
    --x-color-secondary-tint-contrast: var(--ion-color-secondary-contrast);

    --x-color-tertiary-contrast: var(--ion-color-tertiary-contrast);
    --x-color-tertiary-shade-contrast: var(--ion-color-tertiary-contrast);
    --x-color-tertiary-tint-contrast: var(--ion-color-tertiary-contrast);

    --x-color-success-contrast: var(--ion-color-success-contrast);
    --x-color-success-shade-contrast: var(--ion-color-success-contrast);
    --x-color-success-tint-contrast: var(--ion-color-success-contrast);

    --x-color-warning-contrast: var(--ion-color-warning-contrast);
    --x-color-warning-shade-contrast: var(--ion-color-warning-contrast);
    --x-color-warning-tint-contrast: var(--ion-color-warning-contrast);

    --x-color-danger-contrast: var(--ion-color-danger-contrast);
    --x-color-danger-shade-contrast: var(--ion-color-danger-contrast);
    --x-color-danger-tint-contrast: var(--ion-color-danger-contrast);

    --x-color-dark-contrast: var(--ion-color-dark-contrast);
    --x-color-dark-shade-contrast: var(--ion-color-dark-contrast);
    --x-color-dark-tint-contrast: var(--ion-color-dark-contrast);

    --x-color-medium-contrast: var(--ion-color-medium-contrast);
    --x-color-medium-shade-contrast: var(--ion-color-medium-contrast);
    --x-color-medium-tint-contrast: var(--ion-color-medium-contrast);

    --x-color-light-contrast: var(--ion-color-light-contrast);
    --x-color-light-shade-contrast: var(--ion-color-light-contrast);
    --x-color-light-tint-contras: var(--ion-color-light-contrast);
  }

  //
  // Define a Palette Based on Collection ...
  $m-color-palette: mat-palette($m-color-collection, m-ionic);

  //
  // Merge Defined Palette to Theme Object ...
  $x-theme: map_merge(
    $x-theme,
    (
      m-ionic: $m-color-palette,
    )
  );

  //
  // Generate a General Object for feauture Usages ...
  $x-ionic: map-get($x-theme, m-ionic);
}

//
@mixin x-to-ion-color() {
  //
  // XFramework Color Mechanism ...
  :root {
    --ion-color-primary: var(--x-color-primary);
    --ion-color-primary-shade: var(--x-color-primary-shade);
    --ion-color-primary-tint: var(--x-color-primary-tint);

    --ion-color-primary-contrast: var(--x-color-primary-contrast);
    --ion-color-primary-contrast: var(--x-color-primary-contrast);
    --ion-color-primary-contrast: var(--x-color-primary-contrast);

    --ion-color-secondary: var(--x-color-secondary);
    --ion-color-secondary-shade: var(--x-color-secondary-shade);
    --ion-color-secondary-tint: var(--x-color-secondary-tint);

    --ion-color-secondary-contrast: var(--x-color-secondary-contrast);
    --ion-color-secondary-contrast: var(--x-color-secondary-contrast);
    --ion-color-secondary-contrast: var(--x-color-secondary-contrast);

    --ion-color-tertiary: var(--x-color-tertiary);
    --ion-color-tertiary-shade: var(--x-color-tertiary-shade);
    --ion-color-tertiary-tint: var(--x-color-tertiary-tint);

    --ion-color-tertiary-contrast: var(--x-color-tertiary-contrast);
    --ion-color-tertiary-contrast: var(--x-color-tertiary-contrast);
    --ion-color-tertiary-contrast: var(--x-color-tertiary-contrast);

    --ion-color-success: var(--x-color-success);
    --ion-color-success-shade: var(--x-color-success-shade);
    --ion-color-success-tint: var(--x-color-success-tint);

    --ion-color-success-contrast: var(--x-color-success-contrast);
    --ion-color-success-contrast: var(--x-color-success-contrast);
    --ion-color-success-contrast: var(--x-color-success-contrast);

    --ion-color-warning: var(--x-color-warning);
    --ion-color-warning-shade: var(--x-color-warning-shade);
    --ion-color-warning-tint: var(--x-color-warning-tint);

    --ion-color-warning-contrast: var(--x-color-warning-contrast);
    --ion-color-warning-contrast: var(--x-color-warning-contrast);
    --ion-color-warning-contrast: var(--x-color-warning-contrast);

    --ion-color-danger: var(--x-color-danger);
    --ion-color-danger-shade: var(--x-color-danger-shade);
    --ion-color-danger-tint: var(--x-color-danger-tint);

    --ion-color-danger-contrast: var(--x-color-danger-contrast);
    --ion-color-danger-contrast: var(--x-color-danger-contrast);
    --ion-color-danger-contrast: var(--x-color-danger-contrast);

    --ion-color-dark: var(--x-color-dark);
    --ion-color-dark-shade: var(--x-color-dark-shade);
    --ion-color-dark-tint: var(--x-color-dark-tint);

    --ion-color-dark-contrast: var(--x-color-dark-contrast);
    --ion-color-dark-contrast: var(--x-color-dark-contrast);
    --ion-color-dark-contrast: var(--x-color-dark-contrast);

    --ion-color-medium: var(--x-color-medium);
    --ion-color-medium-shade: var(--x-color-medium-shade);
    --ion-color-medium-tint: var(--x-color-medium-tint);

    --ion-color-medium-contrast: var(--x-color-medium-contrast);
    --ion-color-medium-contrast: var(--x-color-medium-contrast);
    --ion-color-medium-contrast: var(--x-color-medium-contrast);

    --ion-color-light: var(--x-color-light);
    --ion-color-light-shade: var(--x-color-light-shade);
    --ion-color-light-tint: var(--x-color-light-tint);

    --ion-color-light-contrast: var(--x-color-light-contrast);
    --ion-color-light-contrast: var(--x-color-light-contrast);
    --ion-color-light-contrast: var(--x-color-light-contrast);
  }

  //
  // Define a Palette Based on Collection ...
  $m-color-palette: mat-palette($m-color-collection, m-ionic);

  //
  // Merge Defined Palette to Theme Object ...
  $x-theme: map_merge(
    $x-theme,
    (
      m-ionic: $m-color-palette,
    )
  );

  //
  // Generate a General Object for feauture Usages ...
  $x-ionic: map-get($x-theme, m-ionic);
}

//
//#region Custom Ionic Based Colors ...

//
//#region Define a Collection ...
$m-color-collection: (
  primary: var(--x-color-primary),
  primary-shade: var(--x-color-primary-shade),
  primary-tint: var(--x-color-primary-tint),
  secondary: var(--x-color-secondary),
  secondary-shade: var(--x-color-secondary-shade),
  secondary-tint: var(--x-color-secondary-tint),
  tertiary: var(--x-color-tertiary),
  tertiary-shade: var(--x-color-tertiary-shade),
  tertiary-tint: var(--x-color-tertiary-tint),
  success: var(--x-color-success),
  success-shade: var(--x-color-success-shade),
  success-tint: var(--x-color-success-tint),
  warning: var(--x-color-warning),
  warning-shade: var(--x-color-warning-shade),
  warning-tint: var(--x-color-warning-tint),
  danger: var(--x-color-danger),
  danger-shade: var(--x-color-danger-shade),
  danger-tint: var(--x-color-danger-tint),
  dark: var(--x-color-dark),
  dark-shade: var(--x-color-dark-shade),
  dark-tint: var(--x-color-dark-tint),
  medium: var(--x-color-medium),
  medium-shade: var(--x-color-medium-shade),
  medium-tint: var(--x-color-medium-tint),
  light: var(--x-color-light),
  light-shade: var(--x-color-light-shade),
  light-tint: var(--x-color-light-tint),
  contrast: (
    primary: var(--x-color-primary-contrast),
    primary-shade: var(--x-color-primary-shade-contrast),
    primary-tint: var(--x-color-primary-tint-contrast),
    secondary: var(--x-color-secondary-contrast),
    secondary-shade: var(--x-color-secondary-shade-contrast),
    secondary-tint: var(--x-color-secondary-tint-contrast),
    tertiary: var(--x-color-tertiary-contrast),
    tertiary-shade: var(--x-color-tertiary-shade-contrast),
    tertiary-tint: var(--x-color-tertiary-tint-contrast),
    success: var(--x-color-success-contrast),
    success-shade: var(--x-color-success-shade-contrast),
    success-tint: var(--x-color-success-tint-contrast),
    warning: var(--x-color-warning-contrast),
    warning-shade: var(--x-color-warning-shade-contrast),
    warning-tint: var(--x-color-warning-tint-contrast),
    danger: var(--x-color-danger-contrast),
    danger-shade: var(--x-color-danger-shade-contrast),
    danger-tint: var(--x-color-danger-tint-contrast),
    dark: var(--x-color-dark-contrast),
    dark-shade: var(--x-color-dark-shade-contrast),
    dark-tint: var(--x-color-dark-tint-contrast),
    medium: var(--x-color-medium-contrast),
    medium-shade: var(--x-color-medium-shade-contrast),
    medium-tint: var(--x-color-medium-tint-contrast),
    light: var(--x-color-light-contrast),
    light-shade: var(--x-color-light-shade-contrast),
    light-tint: var(--x-color-light-tint-contrast),
  ),
);
//#endregion

//
// Define a Palette Based on Collection ...
$m-color-palette: mat-palette($m-color-collection, m-ionic);

//
// Merge Defined Palette to Theme Object ...
$x-theme: map_merge(
  $x-theme,
  (
    m-ionic: $m-color-palette,
  )
);

//
// Generate a General Object for feauture Usages ...
$x-ionic: map-get($x-theme, m-ionic);

//
//#region Generate General Classes for Support these Colors ...
//
//#region Primary ...
.mat-primary {
  //
  color: mat-color($x-ionic, primary-contrast) !important;
  background-color: mat-color($x-ionic, primary) !important;

  //
  --color: mat-color($x-ionic, primary-contrast) !important;
  --background: mat-color($x-ionic, primary) !important;
}

.mat-primary-shade {
  //
  color: mat-color($x-ionic, primary-shade-contrast) !important;
  background-color: mat-color($x-ionic, primary-shade) !important;

  //
  --color: mat-color($x-ionic, primary-shade-contrast) !important;
  --background: mat-color($x-ionic, primary-shade) !important;
}

.mat-primary-tint {
  //
  color: mat-color($x-ionic, primary-tint-contrast) !important;
  background-color: mat-color($x-ionic, primary-tint) !important;

  //
  --color: mat-color($x-ionic, primary-tint-contrast) !important;
  --background: mat-color($x-ionic, primary-tint) !important;
}

.c-primary {
  //
  color: mat-color($x-ionic, primary) !important;

  //
  --color: mat-color($x-ionic, primary) !important;
}

.c-primary-shade {
  //
  color: mat-color($x-ionic, primary-shade) !important;

  //
  --color: mat-color($x-ionic, primary-shade) !important;
}

.c-primary-tint {
  //
  color: mat-color($x-ionic, primary-tint) !important;

  //
  --color: mat-color($x-ionic, primary-tint) !important;
}

//#endregion

//
//#region Secondary ...
.mat-secondary {
  //
  color: mat-color($x-ionic, secondary-contrast) !important;
  background-color: mat-color($x-ionic, secondary) !important;

  //
  --color: mat-color($x-ionic, secondary-contrast) !important;
  --background: mat-color($x-ionic, secondary) !important;
}

.mat-secondary-shade {
  //
  color: mat-color($x-ionic, secondary-shade-contrast) !important;
  background-color: mat-color($x-ionic, secondary-shade) !important;

  //
  --color: mat-color($x-ionic, secondary-shade-contrast) !important;
  --background: mat-color($x-ionic, secondary-shade) !important;
}

.mat-secondary-tint {
  //
  color: mat-color($x-ionic, secondary-tint-contrast) !important;
  background-color: mat-color($x-ionic, secondary-tint) !important;

  //
  --color: mat-color($x-ionic, secondary-tint-contrast) !important;
  --background: mat-color($x-ionic, secondary-tint) !important;
}

.c-secondary {
  //
  color: mat-color($x-ionic, secondary) !important;

  //
  --color: mat-color($x-ionic, secondary) !important;
}

.c-secondary-shade {
  //
  color: mat-color($x-ionic, secondary-shade) !important;

  //
  --color: mat-color($x-ionic, secondary-shade) !important;
}

.c-secondary-tint {
  //
  color: mat-color($x-ionic, secondary-tint) !important;

  //
  --color: mat-color($x-ionic, secondary-tint) !important;
}

//#endregion

//
//#region Tertiary ...
.mat-tertiary {
  //
  color: mat-color($x-ionic, tertiary-contrast) !important;
  background-color: mat-color($x-ionic, tertiary) !important;

  //
  --color: mat-color($x-ionic, tertiary-contrast) !important;
  --background: mat-color($x-ionic, tertiary) !important;
}

.mat-tertiary-shade {
  //
  color: mat-color($x-ionic, tertiary-shade-contrast) !important;
  background-color: mat-color($x-ionic, tertiary-shade) !important;

  //
  --color: mat-color($x-ionic, tertiary-shade-contrast) !important;
  --background: mat-color($x-ionic, tertiary-shade) !important;
}

.mat-tertiary-tint {
  //
  color: mat-color($x-ionic, tertiary-tint-contrast) !important;
  background-color: mat-color($x-ionic, tertiary-tint) !important;

  //
  --color: mat-color($x-ionic, tertiary-tint-contrast) !important;
  --background: mat-color($x-ionic, tertiary-tint) !important;
}

.c-tertiary {
  //
  color: mat-color($x-ionic, tertiary) !important;

  //
  --color: mat-color($x-ionic, tertiary) !important;
}

.c-tertiary-shade {
  //
  color: mat-color($x-ionic, tertiary-shade) !important;

  //
  --color: mat-color($x-ionic, tertiary-shade) !important;
}

.c-tertiary-tint {
  //
  color: mat-color($x-ionic, tertiary-tint) !important;

  //
  --color: mat-color($x-ionic, tertiary-tint) !important;
}

//#endregion

//
//#region Success ...
.mat-success {
  //
  color: mat-color($x-ionic, success-contrast) !important;
  background-color: mat-color($x-ionic, success) !important;

  //
  --color: mat-color($x-ionic, success-contrast) !important;
  --background: mat-color($x-ionic, success) !important;
}

.mat-success-shade {
  //
  color: mat-color($x-ionic, success-shade-contrast) !important;
  background-color: mat-color($x-ionic, success-shade) !important;

  //
  --color: mat-color($x-ionic, success-shade-contrast) !important;
  --background: mat-color($x-ionic, success-shade) !important;
}

.mat-success-tint {
  //
  color: mat-color($x-ionic, success-tint-contrast) !important;
  background-color: mat-color($x-ionic, success-tint) !important;

  //
  --color: mat-color($x-ionic, success-tint-contrast) !important;
  --background: mat-color($x-ionic, success-tint) !important;
}

.c-success {
  //
  color: mat-color($x-ionic, success) !important;

  //
  --color: mat-color($x-ionic, success) !important;
}

.c-success-shade {
  //
  color: mat-color($x-ionic, success-shade) !important;

  //
  --color: mat-color($x-ionic, success-shade) !important;
}

.c-success-tint {
  //
  color: mat-color($x-ionic, success-tint) !important;

  //
  --color: mat-color($x-ionic, success-tint) !important;
}

//#endregion

//
//#region Warning ...
.mat-warning {
  //
  color: mat-color($x-ionic, warning-contrast) !important;
  background-color: mat-color($x-ionic, warning) !important;

  //
  --color: mat-color($x-ionic, warning-contrast) !important;
  --background: mat-color($x-ionic, warning) !important;
}

.mat-warning-shade {
  //
  color: mat-color($x-ionic, warning-shade-contrast) !important;
  background-color: mat-color($x-ionic, warning-shade) !important;

  //
  --color: mat-color($x-ionic, warning-shade-contrast) !important;
  --background: mat-color($x-ionic, warning-shade) !important;
}

.mat-warning-tint {
  //
  color: mat-color($x-ionic, warning-tint-contrast) !important;
  background-color: mat-color($x-ionic, warning-tint) !important;

  //
  --color: mat-color($x-ionic, warning-tint-contrast) !important;
  --background: mat-color($x-ionic, warning-tint) !important;
}

.c-warning {
  //
  color: mat-color($x-ionic, warning) !important;

  //
  --color: mat-color($x-ionic, warning) !important;
}

.c-warning-shade {
  //
  color: mat-color($x-ionic, warning-shade) !important;

  //
  --color: mat-color($x-ionic, warning-shade) !important;
}

.c-warning-tint {
  //
  color: mat-color($x-ionic, warning-tint) !important;

  //
  --color: mat-color($x-ionic, warning-tint) !important;
}

//#endregion

//
//#region Danger ...
.mat-danger {
  //
  color: mat-color($x-ionic, danger-contrast) !important;
  background-color: mat-color($x-ionic, danger) !important;

  //
  --color: mat-color($x-ionic, danger-contrast) !important;
  --background: mat-color($x-ionic, danger) !important;
}

.mat-danger-shade {
  //
  color: mat-color($x-ionic, danger-shade-contrast) !important;
  background-color: mat-color($x-ionic, danger-shade) !important;

  //
  --color: mat-color($x-ionic, danger-shade-contrast) !important;
  --background: mat-color($x-ionic, danger-shade) !important;
}

.mat-danger-tint {
  //
  color: mat-color($x-ionic, danger-tint-contrast) !important;
  background-color: mat-color($x-ionic, danger-tint) !important;

  //
  --color: mat-color($x-ionic, danger-tint-contrast) !important;
  --background: mat-color($x-ionic, danger-tint) !important;
}

.c-danger {
  //
  color: mat-color($x-ionic, danger) !important;

  //
  --color: mat-color($x-ionic, danger) !important;
}

.c-danger-shade {
  //
  color: mat-color($x-ionic, danger-shade) !important;

  //
  --color: mat-color($x-ionic, danger-shade) !important;
}

.c-danger-tint {
  //
  color: mat-color($x-ionic, danger-tint) !important;

  //
  --color: mat-color($x-ionic, danger-tint) !important;
}

//#endregion

//
//#region Dark ...
.mat-dark {
  //
  color: mat-color($x-ionic, dark-contrast) !important;
  background-color: mat-color($x-ionic, dark) !important;

  //
  --color: mat-color($x-ionic, dark-contrast) !important;
  --background: mat-color($x-ionic, dark) !important;
}

.mat-dark-shade {
  //
  color: mat-color($x-ionic, dark-shade-contrast) !important;
  background-color: mat-color($x-ionic, dark-shade) !important;

  //
  --color: mat-color($x-ionic, dark-shade-contrast) !important;
  --background: mat-color($x-ionic, dark-shade) !important;
}

.mat-dark-tint {
  //
  color: mat-color($x-ionic, dark-tint-contrast) !important;
  background-color: mat-color($x-ionic, dark-tint) !important;

  //
  --color: mat-color($x-ionic, dark-tint-contrast) !important;
  --background: mat-color($x-ionic, dark-tint) !important;
}

.c-dark {
  //
  color: mat-color($x-ionic, dark) !important;

  //
  --color: mat-color($x-ionic, dark) !important;
}

.c-dark-shade {
  //
  color: mat-color($x-ionic, dark-shade) !important;

  //
  --color: mat-color($x-ionic, dark-shade) !important;
}

.c-dark-tint {
  //
  color: mat-color($x-ionic, dark-tint) !important;

  //
  --color: mat-color($x-ionic, dark-tint) !important;
}

//#endregion

//
//#region Medium ...
.mat-medium {
  //
  color: mat-color($x-ionic, medium-contrast) !important;
  background-color: mat-color($x-ionic, medium) !important;

  //
  --color: mat-color($x-ionic, medium-contrast) !important;
  --background: mat-color($x-ionic, medium) !important;
}

.mat-medium-shade {
  //
  color: mat-color($x-ionic, medium-shade-contrast) !important;
  background-color: mat-color($x-ionic, medium-shade) !important;

  //
  --color: mat-color($x-ionic, medium-shade-contrast) !important;
  --background: mat-color($x-ionic, medium-shade) !important;
}

.mat-medium-tint {
  //
  color: mat-color($x-ionic, medium-tint-contrast) !important;
  background-color: mat-color($x-ionic, medium-tint) !important;

  //
  --color: mat-color($x-ionic, medium-tint-contrast) !important;
  --background: mat-color($x-ionic, medium-tint) !important;
}

.c-medium {
  //
  color: mat-color($x-ionic, medium) !important;

  //
  --color: mat-color($x-ionic, medium) !important;
}

.c-medium-shade {
  //
  color: mat-color($x-ionic, medium-shade) !important;

  //
  --color: mat-color($x-ionic, medium-shade) !important;
}

.c-medium-tint {
  //
  color: mat-color($x-ionic, medium-tint) !important;

  //
  --color: mat-color($x-ionic, medium-tint) !important;
}

//#endregion

//
//#region Light ...
.mat-light {
  //
  color: mat-color($x-ionic, light-contrast) !important;
  background-color: mat-color($x-ionic, light) !important;

  //
  --color: mat-color($x-ionic, light-contrast) !important;
  --background: mat-color($x-ionic, light) !important;
}

.mat-light-shade {
  //
  color: mat-color($x-ionic, light-shade-contrast) !important;
  background-color: mat-color($x-ionic, light-shade) !important;

  //
  --color: mat-color($x-ionic, light-shade-contrast) !important;
  --background: mat-color($x-ionic, light-shade) !important;
}

.mat-light-tint {
  //
  color: mat-color($x-ionic, light-tint-contrast) !important;
  background-color: mat-color($x-ionic, light-tint) !important;

  //
  --color: mat-color($x-ionic, light-tint-contrast) !important;
  --background: mat-color($x-ionic, light-tint) !important;
}

.c-light {
  //
  color: mat-color($x-ionic, light) !important;

  //
  --color: mat-color($x-ionic, light) !important;
}

.c-light-shade {
  //
  color: mat-color($x-ionic, light-shade) !important;

  //
  --color: mat-color($x-ionic, light-shade) !important;
}

.c-light-tint {
  //
  color: mat-color($x-ionic, light-tint) !important;

  //
  --color: mat-color($x-ionic, light-tint) !important;
}

//#endregion
//#endregion

.cb-inherit {
  //
  color: inherit !important;
  --color: inherit !important;

  //
  background-color: inherit !important;
  --background-color: inherit !important;
}

.transparent {
  --background: transparent !important;
  background-color: transparent !important;
}

//#endregion

//
//#region Custom Styles ...
html,
body {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;
  font-family: Roboto, "Helvetica Neue", sans-serif;
}

* {
  box-sizing: border-box;
}

*::-webkit-scrollbar {
  display: none;
}

[hidden] {
  display: none !important;
}
//#endregion

//
@import "_tools";

//
// Since here we must able to use all Capabilities,
// so we import this at the end ...
@import "./custom-effects.scss";

//
// Implement to ionic them ...
@include x-to-ion-color();

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\tokens\x-injectable-tokens.ts

import { InjectionToken } from '@angular/core';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export const X_FRAMEWORK_COMPONENTS_CONFIG = new InjectionToken<
  XFrameworkComponentsConfig
>('x_config');

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-action-bar\x-action-bar.component.html

<ion-toolbar
  class="x-actions-bar x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  *ngIf="hasChild(preparedActions)"
  [xColor]="getValue(color) | async"
  [ngClass]="getValue(cssClass) | async"
  [isForeground]="getValue(foregroundColor) | async"
>
  <!-- Start Slot Actions -->
  <ion-buttons slot="start">
    <ng-container *ngFor="let item of getStartSoltItems() | async">
      <ng-container
        *ngTemplateOutlet="actionRef; context: { $implicit: item }"
      ></ng-container>
    </ng-container>
  </ion-buttons>

  <!-- End Slot Actions -->
  <ion-buttons slot="end">
    <ng-container *ngFor="let item of getEndSoltItems() | async">
      <ng-container
        *ngTemplateOutlet="actionRef; context: { $implicit: item }"
      ></ng-container>
    </ng-container>
  </ion-buttons>
</ion-toolbar>

<!-- Action Template -->
<ng-template #actionRef let-action>
  <ng-container *ngIf="action && !(isHidedAction(action) | async)">
    <ng-container
      *ngTemplateOutlet="
        (isOnlyIcon(action) | async) ? iconRef : buttonRef;
        context: {
          $implicit: action,
          clickable: true,
          color:
            (isOnlyIcon(action) | async)
              ? (getValue(action.iconColor) | async)
              : (getValue(action.color) | async)
        }
      "
    ></ng-container>
  </ng-container>
</ng-template>

<!-- Button Template -->
<ng-template #buttonRef let-action>
  <x-button
    [loading]="loading"
    class="x-action-bar-item"
    [type]="ButtonTypes.Raised"
    [color]="getValue(action.color) | async"
    [ngClass]="getValue(action.cssClass) | async"
    (clicked)="handleActionClicked(action, $event)"
    *ngIf="!(getOptionalValue(action.hidden) | async)"
    [uiDisabled]="
      uiDisabled ||
      (isDisabledAction(action) | async) ||
      (getOptionalValue(action.disabled) | async)
    "
    [class.disabled]="
      uiDisabled ||
      (isDisabledAction(action) | async) ||
      (getOptionalValue(action.disabled) | async)
    "
  >
    <div class="x-action-bar-item-inner">
      <ng-container
        *ngTemplateOutlet="
          iconRef;
          context: {
            $implicit: action,
            clickable: false,
            color: getValue(action.iconColor) | async
          }
        "
      ></ng-container>

      <span
        [isForeground]="true"
        [xColor]="getValue(action.titleColor) | async"
        *ngIf="action.title && (notEmptyValue(action.title) | async)"
        >{{ getResourceValue(action.title) | async }}</span
      >
    </div>
  </x-button>
</ng-template>

<!-- Icon Template -->
<ng-template #iconRef let-action let-color="color" let-clickable="clickable">
  <x-icon
    [color]="color"
    [loading]="loading"
    class="x-action-bar-item"
    [clickable]="
      clickable &&
      !loading &&
      !uiDisabled &&
      !(getValue(action.disabled) | async)
    "
    [name]="getValue(action.icon) | async"
    (clicked)="handleActionClicked(action, $event)"
    [matTooltip]="getResourceValue(action.title) | async"
    [uiDisabled]="
      uiDisabled ||
      (isDisabledAction(action) | async) ||
      (getOptionalValue(action.disabled) | async)
    "
    [class.disabled]="
      uiDisabled ||
      (isDisabledAction(action) | async) ||
      (getOptionalValue(action.disabled) | async)
    "
  ></x-icon>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-action-bar\x-action-bar.component.scss

$action-item-margin: var(--action-items-offset, 2px);
$action-item-gutter: var(--action-items-gutter, 8px);

.x-actions-bar {
  padding: {
    left: $action-item-gutter !important;
    right: $action-item-gutter !important;
  }
}

.x-action-bar-item {
  margin: {
    left: $action-item-margin !important;
    right: $action-item-margin !important;
  }
}

.x-action-bar-item-inner {
  display: flex;
  align-items: center;

  > * {
    margin: {
      left: 2px !important;
      right: 2px !important;
    }
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-action-bar\x-action-bar.component.ts

import {
  sort,
  nameof,
  toArray,
  isFunction,
  filterList,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XActionBarItem,
  XActionBarAction,
  XActionBarItemSlot,
  XActionBarActionModel,
  XActionBarActionIdentifier,
  XActionBarItemSlotIdentifier,
  XActionBarContainerComponent,
} from './x-action-bar.typings';
import { map } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import { XButtonType } from '../x-button/x-button.typings';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-action-bar',
  templateUrl: './x-action-bar.component.html',
  styleUrls: ['./x-action-bar.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XActionBarComponent extends XActionBarContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  private ACTIONS: XActionBarItem[] = [];
  private ACTIONS_HIDDEN: Map<XActionBarActionIdentifier, boolean> = new Map();
  private ACTIONS_DISABLED: Map<XActionBarActionIdentifier, boolean> =
    new Map();

  //
  readonly ButtonTypes = Object.assign({}, XButtonType);

  get preparedActions() {
    return this.ACTIONS;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Actions Changed ...
    //
    const isActionsChanged = changeKeys.includes(
      nameof<XActionBarComponent>('actions')
    );
    if (isActionsChanged) {
      await this.prepareActions();
    }
    //#endregion

    //
    //#region Action Provider Changed ...
    //
    const isActionProviderChaged = changeKeys.includes(
      nameof<XActionBarComponent>('actionProvider')
    );
    if (isActionProviderChaged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //
  //#region Slot Based Items Fetcher ...
  getSlotItems(slot: XActionBarItemSlotIdentifier) {
    return filterList(this.actions, (action) =>
      this.getValue(action.slot).pipe(map((iSlot) => iSlot === slot))
    );
  }

  getStartSoltItems() {
    return this.getSlotItems(XActionBarItemSlot.START);
  }

  getEndSoltItems() {
    return this.getSlotItems(XActionBarItemSlot.END);
  }

  isOnlyIcon(item: XActionBarItem) {
    return this.getOptionalValue(item.onlyIcon).pipe(
      map((onlyIcon) => !!onlyIcon)
    );
  }

  isDisabledAction(item: XActionBarItem) {
    //
    if (!item) {
      return this.getValue(false);
    }

    //
    return this.getValue(item.id).pipe(
      map((id) =>
        isNullOrEmptyString(id)
          ? false
          : this.ACTIONS_DISABLED.has(id)
          ? this.ACTIONS_DISABLED.get(id)
          : false
      )
    );
  }

  isHidedAction(item: XActionBarItem) {
    //
    if (!item) {
      return this.getValue(false);
    }

    //
    return this.getValue(item.id).pipe(
      map((id) =>
        isNullOrEmptyString(id)
          ? false
          : this.ACTIONS_HIDDEN.has(id)
          ? this.ACTIONS_HIDDEN.get(id)
          : false
      )
    );
  }
  //#endregion

  async getActionIds() {
    //
    if (!this.hasChild(this.preparedActions)) {
      return [];
    }

    //
    const IDS = this.preparedActions.map((a) => this.getValueAsync(a.id));

    //
    return await Promise.all(IDS);
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleActionClicked(item: XActionBarItem, event: Event) {
    //
    const disabled = await this.getValueAsync(item.disabled);
    if (disabled || this.uiDisabled || this.loading) {
      return;
    }

    //
    const id = await this.getValueAsync(item.id);

    //
    if (item.handler && isFunction(item.handler)) {
      await item.handler({
        action: id,
        event,
      });
    }

    //
    this.actionFired.emit({
      action: id,
      event,
    });
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * check an action with specific id exists in actions or not ...
   *
   * @param id required action id ...
   */
  async isActionExists(id: string) {
    return (await this.getActionIds()).findIndex((i) => i === id) > -1;
  }

  /**
   * retrieve all actions ...
   */
  async getActions() {
    //
    const ACTIONS_TYPE = await this.getValueAsync(this.actions);
    if (!ACTIONS_TYPE) {
      return [];
    }

    //
    return toArray(ACTIONS_TYPE);
  }

  /**
   * retrieve an action ...
   *
   * @param id action id
   */
  async getAction(id: string) {
    //
    const isExists = await this.isActionExists(id);
    if (!isExists) {
      return null;
    }

    //
    const ACTIONS = await this.getActions();
    if (!this.hasChild(ACTIONS)) {
      return null;
    }

    //
    const existsAction = ACTIONS.find(
      async (a) => (await this.getValueAsync(a.id)) === id
    );

    //
    return this.toPreparedAction(existsAction);
  }

  /**
   * manually fire a registered action
   *
   * @param id action id
   */
  async fireAction(id: string, event?: Event) {
    //
    const isExist = await this.isActionExists(id);
    if (!isExist) {
      return;
    }

    //
    const action = await this.getAction(id);
    if (!action) {
      return;
    }

    //
    const canFire =
      !this.uiDisabled &&
      !this.loading &&
      !(await this.getOptionalValueAsync(action.disabled));
    if (!canFire) {
      return;
    }

    //
    await this.handleActionClicked(action, event);
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XActionBarActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(
        async (action) => await this.handleActionProviderAction(action)
      );
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  private async prepareActions() {
    //
    this.ACTIONS = null;
    if (!this.actions) {
      //
      this.detectChanges();
      return;
    }

    //
    const ACTIONS_TYPE = await this.getValueAsync(this.actions);
    if (!ACTIONS_TYPE) {
      //
      this.detectChanges();
      return;
    }

    //
    const ACTIONS = toArray(ACTIONS_TYPE);
    if (!this.hasChild(ACTIONS)) {
      //
      this.detectChanges();
      return;
    }

    //
    this.ACTIONS = [
      ...sort(ACTIONS, (a, b) => (a.index && b.index ? a.index - b.index : -1)),
    ];

    //
    for (const a of this.ACTIONS) {
      //
      const id = await this.getValueAsync(a.id);

      //
      if (!this.ACTIONS_DISABLED.has(id)) {
        this.ACTIONS_DISABLED.set(id, false);
      }

      //
      if (!this.ACTIONS_HIDDEN.has(id)) {
        this.ACTIONS_HIDDEN.set(id, false);
      }
    }

    //
    this.detectChanges();
  }

  private async actionsCount() {
    return (await this.getActions()).length || 0;
  }

  private async findActionIdex(id: string) {
    //
    const isExists = await this.isActionExists(id);
    if (!isExists) {
      return -1;
    }

    //
    const ACTIONS_TYPE = await this.getValueAsync(this.actions);
    if (!ACTIONS_TYPE) {
      return null;
    }

    //
    const ACTIONS = toArray(ACTIONS_TYPE);
    if (!this.hasChild(ACTIONS)) {
      return null;
    }

    //
    return ACTIONS.findIndex(
      async (a) => (await this.getValueAsync(a.id)) === id
    );
  }

  private async toPreparedAction(item: XActionBarItem) {
    //
    if (!item) {
      return null;
    }

    //
    return {
      index: item.index,
      handler: item.handler,
      id: await this.getValueAsync(item.id),
      icon: await this.getValueAsync(item.icon),
      slot: await this.getValueAsync(item.slot),
      color: await this.getValueAsync(item.color),
      title: await this.getValueAsync(item.title),
      hidden: await this.getValueAsync(item.hidden),
      onlyIcon: await this.getValueAsync(item.onlyIcon),
      disabled: await this.getValueAsync(item.disabled),
      cssClass: await this.getValueAsync(item.cssClass),
      iconColor: await this.getValueAsync(item.iconColor),
      titleColor: await this.getValueAsync(item.titleColor),
    } as XActionBarItem;
  }

  private async handleActionProviderAction(model: XActionBarActionModel) {
    //
    if (!model || isNullOrEmptyString(model.action)) {
      return;
    }

    //
    const actionId = await this.getValueAsync(model.id);

    //
    switch (model.action) {
      //
      case XActionBarAction.Refresh:
        this.detectChanges();
        break;

      //
      case XActionBarAction.FireAction:
        await this.fireAction(model.id, model.event);
        break;

      //
      case XActionBarAction.EnableAction:
        //
        if (this.ACTIONS_DISABLED.has(actionId)) {
          //
          this.ACTIONS_DISABLED.set(actionId, false);
          this.detectChanges();
        } else {
          if (!this.hasChild(this.ACTIONS)) {
            setTimeout(() => this.handleActionProviderAction(model), 500);
          }
        }
        break;

      //
      case XActionBarAction.DisbaleAction:
        //
        if (this.ACTIONS_DISABLED.has(actionId)) {
          //
          this.ACTIONS_DISABLED.set(actionId, true);
          this.detectChanges();
        } else {
          if (!this.hasChild(this.ACTIONS)) {
            setTimeout(() => this.handleActionProviderAction(model), 500);
          }
        }
        break;

      //
      case XActionBarAction.ShowAction:
        //
        if (this.ACTIONS_HIDDEN.has(actionId)) {
          //
          this.ACTIONS_HIDDEN.set(actionId, false);
          this.detectChanges();
        } else {
          if (!this.hasChild(this.ACTIONS)) {
            setTimeout(() => this.handleActionProviderAction(model), 500);
          }
        }
        break;

      //
      case XActionBarAction.HideAction:
        //
        if (this.ACTIONS_HIDDEN.has(actionId)) {
          //
          this.ACTIONS_HIDDEN.set(actionId, true);
          this.detectChanges();
        } else {
          if (!this.hasChild(this.ACTIONS)) {
            setTimeout(() => this.handleActionProviderAction(model), 500);
          }
        }
        break;
    }
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-action-bar\x-action-bar.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XButtonModule } from '../x-button/x-button.module';
import { XActionBarComponent } from './x-action-bar.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XActionBarComponent],
  imports: [XIconModule, CommonModule, XButtonModule, XBaseComponentModule],
  exports: [
    XIconModule,
    CommonModule,
    XButtonModule,
    XActionBarComponent,
    XBaseComponentModule,
  ],
})
export class XActionBarModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-action-bar\x-action-bar.typings.ts

import {
  XColor,
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  Input,
  Inject,
  NgZone,
  Output,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseComponentContainer,
  XBaseContainerComponent,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../classes/i-x-base.container';
import { map } from 'rxjs/operators';
import { Observable, forkJoin, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XIconNames, XIconIdentifier } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region ActionBar Item ...
export enum XActionBarItemSlot {
  START = 'start',
  END = 'end',
}

export type XActionBarItemSlotIdentifier = XActionBarItemSlot | string;

export interface XActionBarItem {
  index?: number;
  id: XStandardType<string>;
  hidden?: XStandardType<boolean>;
  onlyIcon?: XStandardType<boolean>;
  disabled?: XStandardType<boolean>;
  icon: XStandardType<XIconIdentifier>;
  color?: XStandardType<XColorIdentifier>;
  title?: XStandardType<XResourceIdentifier>;
  iconColor?: XStandardType<XColorIdentifier>;
  titleColor?: XStandardType<XColorIdentifier>;
  cssClass?: XStandardType<XOneOrManyType<string>>;
  slot: XStandardType<XActionBarItemSlotIdentifier>;
  handler?: (model?: XActionBarActionFiredModel) => XStandardType<any>;
}

export interface XActionBarActionFiredModel {
  action: string;
  event?: Event;
}
//#endregion

//
//#region Action Provider ...
export enum XActionBarAction {
  Refresh = 'refresh',
  FireAction = 'fire_action',
  HideAction = 'hide_action',
  ShowAction = 'show_action',
  EnableAction = 'enable_action',
  DisbaleAction = 'disable_action',
}

export type XActionBarActionIdentifier = XActionBarAction | string;

export interface XActionBarActionModel
  extends XBaseActionModel<XActionBarActionIdentifier> {
  id?: string;
  event?: Event;
}
//#endregion

//
//#region Default Actionbar Item IDs ...
export enum DefaultActionBarItemIds {
  Add = 'add',
  Set = 'set',
  Edit = 'edit',
  Remove = 'remove',
  Upload = 'upload',
  Download = 'download',
  SelectAll = 'select_all',
  DeSelectAll = 'de_select_all',
  SelectAllGroup = 'select_all_group',
  InverseSelection = 'inverse_selection',
  DeSelectAllGroup = 'de_select_all_group',
}

export type DefaultActionBarItemIdsIdentifier =
  | DefaultActionBarItemIds
  | string;
//#endregion

//
//#region Default Items ...
export const defaultActionBarRemoveItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Danger,
  icon: XIconNames.remove,
  iconColor: XColor.Danger,
  title: XResourceIDs.delete,
  slot: XActionBarItemSlot.START,
  id: DefaultActionBarItemIds.Remove,
};

export const defaultActionBarUploadItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  icon: XIconNames.upload,
  iconColor: XColor.Tertiary,
  title: XResourceIDs.upload,
  slot: XActionBarItemSlot.START,
  id: DefaultActionBarItemIds.Upload,
};

export const defaultActionBarDownloadItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  icon: XIconNames.download,
  iconColor: XColor.Tertiary,
  title: XResourceIDs.download,
  slot: XActionBarItemSlot.START,
  id: DefaultActionBarItemIds.Download,
};

export const defaultActionBarSelectAllItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  iconColor: XColor.Tertiary,
  icon: XIconNames.select_all,
  slot: XActionBarItemSlot.END,
  title: XResourceIDs.select_all,
  id: DefaultActionBarItemIds.SelectAll,
};

export const defaultActionBarDeSelectAllItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  iconColor: XColor.Tertiary,
  slot: XActionBarItemSlot.END,
  icon: XIconNames.deselect_all,
  title: XResourceIDs.deselect_all,
  id: DefaultActionBarItemIds.DeSelectAll,
};

export const defaultActionBarSelectAllGroupItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  iconColor: XColor.Tertiary,
  slot: XActionBarItemSlot.END,
  icon: XIconNames.select_all_group,
  title: XResourceIDs.select_all_group,
  id: DefaultActionBarItemIds.SelectAllGroup,
};

export const defaultActionBarDeSelectAllGroupItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  iconColor: XColor.Tertiary,
  slot: XActionBarItemSlot.END,
  icon: XIconNames.deselect_all_group,
  title: XResourceIDs.deselect_all_group,
  id: DefaultActionBarItemIds.DeSelectAllGroup,
};

export const defaultActionBarInverseSelectionItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Tertiary,
  iconColor: XColor.Tertiary,
  slot: XActionBarItemSlot.END,
  icon: XIconNames.inverse_selection,
  title: XResourceIDs.inverse_selection,
  id: DefaultActionBarItemIds.InverseSelection,
};

export const defaultActionBarAddItem: XActionBarItem = {
  onlyIcon: true,
  icon: XIconNames.add,
  color: XColor.Success,
  title: XResourceIDs.add,
  iconColor: XColor.Success,
  slot: XActionBarItemSlot.START,
  id: DefaultActionBarItemIds.Add,
};

export const defaultActionBarSetItem: XActionBarItem = {
  onlyIcon: true,
  color: XColor.Success,
  icon: XIconNames.done,
  title: XResourceIDs.set,
  iconColor: XColor.Success,
  slot: XActionBarItemSlot.START,
  id: DefaultActionBarItemIds.Set,
};

export const defaultActionBarEditItem: XActionBarItem = {
  onlyIcon: true,
  icon: XIconNames.edit,
  color: XColor.Warning,
  title: XResourceIDs.edit,
  iconColor: XColor.Warning,
  slot: XActionBarItemSlot.START,
  id: DefaultActionBarItemIds.Edit,
};
//#endregion

//
//#region Container ...
export interface IXActionBarContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XActionBarActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  actions: XStandardType<XOneOrManyType<XActionBarItem>>;

  //
  actionFired: EventEmitter<XActionBarActionFiredModel>;
}

@Injectable({
  providedIn: 'root',
})
export class XActionBarContainer
  extends XBaseContainer
  implements Readonly<IXActionBarContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider = new Subject<XActionBarActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly actions: XStandardType<XOneOrManyType<XActionBarItem>>;

  //
  readonly actionFired = new EventEmitter<XActionBarActionFiredModel>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XActionBarContainerComponent
  extends XBaseContainerComponent
  implements IXActionBarContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  actions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actions;

  //
  @Output()
  actionFired: EventEmitter<XActionBarActionFiredModel> = new EventEmitter<XActionBarActionFiredModel>();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XActionBarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXActionBarContainer
  ): Observable<IXActionBarContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),

      //
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),

      //
      actions: this.getValue(container ? container.actions : this.actions),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          actionFired: this.actionFired,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentActionBarContainer = {
  [TKey in keyof IXActionBarContainer as `actionBar${Capitalize<
    string & TKey
  >}`]: IXActionBarContainer[TKey];
};

export interface IXComponentActionBarContainer
  extends TXComponentActionBarContainer,
    IXBaseComponentContainer {
  showActionBar: XStandardType<boolean>;
  actionBarColor: XStandardType<XColorIdentifier>;
  actionBarForegroundColor: XStandardType<boolean>;
  actionBarActionProvider: Subject<XActionBarActionModel>;
  actionBarCssClass: XStandardType<XOneOrManyType<string>>;
  actionBarActionFired: EventEmitter<XActionBarActionFiredModel>;
  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentActionBarContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentActionBarContainer>
{
  //
  //#region Props ...
  //
  readonly showActionBar: XStandardType<boolean> = true;

  //
  readonly actionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionProvider;
  readonly actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;
  readonly actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  readonly actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actions;

  //
  readonly actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.propertyProvider.actionFired;
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XActionBarContainer) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XComponentActionBarContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentActionBarContainer
{
  //
  //#region Props ...
  //
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  //
  @Input()
  actionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionBarActionProvider;

  @Input()
  actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  //
  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;

  //
  @Input()
  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actionBarActions;

  //
  @Output()
  actionBarActionFired: EventEmitter<XActionBarActionFiredModel> = new EventEmitter<XActionBarActionFiredModel>();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentActionBarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentActionBarContainer
  ): Observable<IXComponentActionBarContainer> {
    return forkJoin({
      //
      showActionBar: this.getValue(
        container ? container.showActionBar : this.showActionBar
      ),

      //
      actionBarCssClass: this.getValue(
        container ? container.actionBarCssClass : this.actionBarCssClass
      ),

      //
      actionBarColor: this.getValue(
        container ? container.actionBarColor : this.actionBarColor
      ),
      actionBarForegroundColor: this.getValue(
        container
          ? container.actionBarForegroundColor
          : this.actionBarForegroundColor
      ),

      //
      actionBarActions: this.getValue(
        container ? container.actionBarActions : this.actionBarActions
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionBarActionProvider: container
            ? container.actionBarActionProvider
            : this.actionBarActionProvider,

          //
          actionBarActionFired: this.actionBarActionFired,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-avatar-upload\x-avatar-upload.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <!-- Avatar Container -->
  <div
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    class="x-avatar-container x-wrapper"
    [isForeground]="getValue(foregroundColor) | async"
  >
    <!-- File Upload Input -->
    <input
      hidden
      #fileInput
      type="file"
      multiple="false"
      (change)="handleFilesSelected()"
      [disabled]="loading || uiDisabled"
    />

    <!-- File Drop -->
    <ng-container *ngIf="getValue(showFileDropArea) | async">
      <ng-container *ngTemplateOutlet="fileDragAreaRef"></ng-container>
    </ng-container>

    <!-- Wrapper of Thumbnail -->
    <div class="thumbnail-wrapper">
      <x-slotter
        [hasCenterSlot]="false"
        [layout]="SlotLayout.HORIZONTAL"
        [startTopSlotCssClass]="'ion-text-start'"
        [endBottomSlotCssClass]="['ion-text-center']"
      >
        <x-slot [name]="SlotNames.START" [cssClass]="">
          <!-- Thumbnail -->
          <x-thumbnail
            class="x-thumb"
            [circular]="true"
            [loading]="loading"
            [cssClass]="'thumbnail'"
            [uiDisabled]="uiDisabled"
            [type]="ThumbnailTypes.CUSTOM"
            [identifier]="avatar || (getValue(placeHolderAvatar) | async)"
          >
          </x-thumbnail>
        </x-slot>

        <x-slot [name]="SlotNames.END" cssClass="ion-text-end">
          <div
            class="avatar-actions ion-text-end"
            *ngIf="hasChildValue(actions) | async"
          >
            <!-- Action Buttons -->
            <ng-container *ngFor="let action of getActions() | async">
              <!-- Crop -->
              <ng-container *ngIf="action.name === ActionButtonNames.Crop">
                <ng-container
                  *ngTemplateOutlet="
                    actionButtonRef;
                    context: {
                      $implicit: action,
                      showCondition: isCroppableImage()
                    }
                  "
                ></ng-container>
              </ng-container>

              <!-- Clear -->
              <ng-container *ngIf="action.name === ActionButtonNames.Clear">
                <ng-container
                  *ngTemplateOutlet="
                    actionButtonRef;
                    context: {
                      $implicit: action,
                      showCondition: avatar
                    }
                  "
                ></ng-container>
              </ng-container>

              <!-- Select -->
              <ng-container *ngIf="action.name === ActionButtonNames.Select">
                <ng-container
                  *ngTemplateOutlet="
                    actionButtonRef;
                    context: {
                      $implicit: action,
                      showCondition: !avatar
                    }
                  "
                ></ng-container>
              </ng-container>
            </ng-container>
          </div>
        </x-slot>
      </x-slotter>
    </div>
  </div>
</ng-template>

<!-- File Drop Area Template -->
<ng-template #fileDragAreaRef>
  <div class="x-file-drag-drop-wrapper">
    <x-file-drop-area
      [loading]="loading"
      [uiDisabled]="uiDisabled"
      [supportsMultipleFiles]="false"
      [icon]="getValue(fileDropAreaIcon) | async"
      [actionProvider]="fileDropAreaActionProvider"
      [color]="getValue(fileDropAreaColor) | async"
      (filesChangeEmiter)="handleFileDropped($event)"
      [cssClass]="getValue(fileDropAreaCssClass) | async"
      [isInPage]="getValue(fileDropAreaIsInPage) | async"
      [cardColor]="getValue(fileDropAreaCardColor) | async"
      [errorColor]="getValue(fileDropAreaErrorColor) | async"
      [accentColor]="getValue(fileDropAreaAccentColor) | async"
      [message]="getResourceValue(fileDropAreaMessage) | async"
      [wrapWithCard]="getValue(fileDropAreaWrapWithCard) | async"
      [successColor]="getValue(fileDropAreaSuccessColor) | async"
      [dragDisabled]="getValue(fileDropAreaDragDisabled) | async"
      [cardTitle]="getResourceValue(fileDropAreaCardTitle) | async"
      [allowedExtensions]="getArrayValue(allowedExtensions) | async"
      [cardShowFooter]="getValue(fileDropAreaCardShowFooter) | async"
      [foregroundColor]="getValue(fileDropAreaForegroundColor) | async"
      [contentTemplate]="getValue(fileDropAreaContentTemplate) | async"
      [cardSubTitle]="getResourceValue(fileDropAreaCardSubTitle) | async"
      [disableFileCheck]="getValue(fileDropAreaDisableFileCheck) | async"
      [cardForegroundColor]="getValue(fileDropAreaCardForegroundColor) | async"
    ></x-file-drop-area>
  </div>
</ng-template>

<!-- Action Button Template -->
<ng-template #actionButtonRef let-descriptor let-showCondition="showCondition">
  <x-button
    *ngIf="showCondition"
    [loading]="loading"
    [uiDisabled]="uiDisabled"
    [type]="ButtonTypes.Icon"
    [color]="getValue(descriptor.color) | async"
    (clicked)="handleActionButtonClicked(descriptor.name)"
    [matTooltip]="getResourceValue(descriptor.tooltip) | async"
  >
    <ng-container
      *ngTemplateOutlet="iconRef; context: { $implicit: descriptor }"
    ></ng-container>
  </x-button>
</ng-template>

<!-- Icon Template -->
<ng-template #iconRef let-descriptor>
  <x-icon
    [name]="getValue(descriptor.icon) | async"
    [color]="getValue(descriptor.color) | async"
    [foregroundColor]="getValue(foregroundColor) | async"
  ></x-icon>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-avatar-upload\x-avatar-upload.component.scss

//
:host {
  --avatar-size: 64px;
  --top-radius: 5px;
  --padding: 6px;
}

.x-file-drag-drop-wrapper {
  padding-bottom: var(--padding);
  border-top-left-radius: var(--top-radius);
  border-top-right-radius: var(--top-radius);
}

.x-avatar-container {
  padding: 6px;
  border-radius: 5px;
}

.thumbnail-wrapper {
  height: var(--avatar-size, 64px) !important;
}

x-thumbnail {
  --thumbnail-size: var(--avatar-size, 64px);
}

x-file-drop-area {
  --padding: 0px;
}

.avatar-actions {
  //
  @extend .thumbnail-wrapper;

  //
  display: flex;
  flex-direction: row;
  align-items: stretch;
  justify-content: flex-end;

  //
  * {
    flex: 0 0 auto;
    margin: {
      top: auto;
      bottom: auto;
    }
  }

  //
  * {
    margin: {
      left: 3px;
      right: 3px;
    }
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-avatar-upload\x-avatar-upload.component.ts

import {
  Component,
  ViewChild,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  sort,
  nameof,
  isArray,
  toArray,
  hasChild,
  toBoolean,
  getArrayOf,
  XDialogResult,
  isExtensionOk,
  getErrorException,
  getImageDimensions,
  XColorWithBrightness,
  XExceptionIdentifier,
} from 'x-framework-core';
import {
  XAvatarStatus,
  XAvatarUploadAction,
  XAvatarUploadActionModel,
  XAvatarUploadActionButtonName,
  XAvatarUploadContainerComponent,
} from './x-avatar-upload.typings';
import { map } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import { XIconNames } from '../x-icon/x-icon.typings';
import { XButtonType } from '../x-button/x-button.typings';
import { XThumbnailType } from '../x-thumbnail/x-thumbnail.typings';
import { XSlotName, XSlotLayout } from '../x-slotter/x-slotter.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-avatar-upload',
  templateUrl: './x-avatar-upload.component.html',
  styleUrls: ['./x-avatar-upload.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XAvatarUploadComponent extends XAvatarUploadContainerComponent {
  //
  //#region Props ...
  //
  private SELECTED_AVATAR: File;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly IconNames = Object.assign({}, XIconNames);
  readonly SlotLayout = Object.assign({}, XSlotLayout);
  readonly ButtonTypes = Object.assign({}, XButtonType);
  readonly ThumbnailTypes = Object.assign({}, XThumbnailType);
  readonly ColorNames = Object.assign({}, XColorWithBrightness);
  readonly ActionButtonNames = Object.assign({}, XAvatarUploadActionButtonName);

  //
  selectedFileWidth = 0;
  selectedFileHeight = 0;

  //
  @ViewChild('fileInput', { static: false })
  private fileInputRef!: ElementRef;

  set avatar(v: File) {
    //
    this.SELECTED_AVATAR = v;

    //
    if (!v) {
      return;
    }

    //
    getImageDimensions(v).then((dimension) => {
      if (dimension) {
        //
        this.selectedFileWidth = dimension.width;
        this.selectedFileHeight = dimension.height;

        //
        this.detectChanges();
      }
    });
  }

  get avatar() {
    return this.SELECTED_AVATAR;
  }
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Action Provider ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XAvatarUploadComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Task Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  getActions() {
    return this.getArrayValue(this.actions).pipe(
      map((actions) => sort(actions, (a1, a2) => a1.index - a2.index))
    );
  }

  isCroppableImage() {
    return (
      this.avatar &&
      this.selectedFileWidth > 0 &&
      this.selectedFileHeight > 0 &&
      this.selectedFileWidth >= this.config.minAllowedImageSize &&
      this.selectedFileHeight >= this.config.minAllowedImageSize
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  //
  handleFilesSelected() {
    //
    if (!this.fileInputRef) {
      return;
    }

    //
    const fileInput = this.fileInputRef.nativeElement;
    if (!fileInput || !fileInput.files) {
      return;
    }

    //
    const files = getArrayOf<File>(fileInput.files);

    //
    if (!files || files.length === 0) {
      return;
    }

    //
    const file = files[0];
    if (!file) {
      return;
    }

    //
    fileInput.value = null;

    //
    this.handleFileDropped(files);
  }

  //
  async handleFileDropped(files: File | File[]) {
    //
    if (!files) {
      return;
    }

    //
    this.loading = true;

    //
    let file: File;
    if (!isArray(files) && files instanceof File) {
      file = files as File;
    } else {
      //
      const filesArr = files as File[];
      if (hasChild(filesArr)) {
        file = filesArr[0];
      }
    }

    //
    this.loading = false;

    //
    if (!file) {
      return;
    }

    //
    const status = await this.getFileStatus(file);
    if (status !== XAvatarStatus.OK) {
      this.loading = false;
      await this.handleFileStatus(status);
      return;
    }

    //
    this.avatar = file;
    this.avatarChange.emit(file);

    //
    this.detectChanges();
  }

  //
  async handleActionButtonClicked(name: XAvatarUploadActionButtonName) {
    switch (name) {
      //
      case XAvatarUploadActionButtonName.Crop:
        this.loadCropper();
        break;

      //
      case XAvatarUploadActionButtonName.Clear:
        await this.clear();
        break;

      //
      case XAvatarUploadActionButtonName.Select:
        this.select();
        break;
    }
  }
  //#endregion

  //
  //#region Actions ...
  //
  select() {
    //
    if (!this.fileInputRef || !this.fileInputRef.nativeElement) {
      return;
    }

    //
    this.fileInputRef.nativeElement.click();
  }

  //
  async clear(force = false) {
    //
    if (!this.avatar) {
      return;
    }

    //
    if (force) {
      this.clearImage();
      return;
    }

    //
    await this.managerService.dialogService.presentAreYouSureYesNoDialog(() => {
      this.clearImage();
    });
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XAvatarUploadActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XAvatarUploadAction.Clear:
            await this.clear(toBoolean(model.payload));
            break;

          //
          case XAvatarUploadAction.Select:
            this.select();
            break;

          //
          case XAvatarUploadAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private loadCropper() {
    this.imageCropperService.showCropper(this.avatar);

    //
    const s = this.applyTakeUntilWrapper(
      this.imageCropperService.onResult.asObservable()
    ).subscribe((res) => {
      //
      if (res.result === XDialogResult.Ok && res.file) {
        //
        this.avatar = null;
        setTimeout(() => {
          //
          this.avatar = res.file;
          this.avatarChange.emit(res.file);

          //
          this.detectChanges();
        }, 100);
      }

      //
      s.unsubscribe();
    });
  }

  private clearImage() {
    //
    if (!this.avatar) {
      return;
    }

    //
    this.avatar = null;
    this.selectedFileWidth = 0;
    this.selectedFileHeight = 0;
    this.avatarChange.emit(null);

    //
    this.detectChanges();
  }

  private async getFileStatus(file: File) {
    //
    // Check Not Empty File ...
    if (!file || file.size === 0) {
      return XAvatarStatus.EMPTY;
    }

    //
    // Check File Extension Validation ...
    const isExtensionIsOk = await this.isExtensionOk(file);
    if (!isExtensionIsOk) {
      return XAvatarStatus.NOT_ALLOWED_TYPE;
    }

    //
    // Check File Min Size Ok ...
    const isMinAllowedFileSizeOk = await this.isMinAllowedFileSizeOk(file);
    if (!isMinAllowedFileSizeOk) {
      return XAvatarStatus.MIN_SIZE_NOT_REACHED;
    }

    //
    // Check File Max Size Ok ...
    const isMaxAllowedFileSizeOk = await this.isMaxAllowedFileSizeOk(file);
    if (!isMaxAllowedFileSizeOk) {
      return XAvatarStatus.SIZE_EXCEEDED;
    }

    //
    // Everythings Ok ...
    return XAvatarStatus.OK;
  }

  private async handleFileStatus(status: XAvatarStatus) {
    //
    let identifier: XExceptionIdentifier = null;
    switch (status) {
      //
      case XAvatarStatus.EMPTY:
        identifier = await this.getValueAsync(this.emptyFileException);
        break;

      //
      case XAvatarStatus.NOT_ALLOWED_TYPE:
        identifier = await this.getValueAsync(this.notAllowedFileException);
        break;

      //
      case XAvatarStatus.SIZE_EXCEEDED:
        identifier = await this.getValueAsync(this.maxAllowedFileSizeException);
        break;

      //
      case XAvatarStatus.MIN_SIZE_NOT_REACHED:
        identifier = await this.getValueAsync(this.minAllowedFileSizeException);
        break;
    }

    //
    const exception = getErrorException(identifier);
    if (exception) {
      this.managerService.notificationService.presentErrorNotification({
        exception,
        dissmissable: true,
      });
    }
  }

  //
  //#region File Selection Policies ...
  /**
   * Check File Extension ...
   */
  private async isExtensionOk(file: File) {
    //
    if (!file) {
      return false;
    }

    //
    const allowedExtensionsType = await this.getValueAsync(
      this.allowedExtensions
    );
    if (!allowedExtensionsType) {
      return true;
    }

    //
    const allowedExtensions = toArray(allowedExtensionsType);
    if (!hasChild(allowedExtensions)) {
      return true;
    }

    //
    const result = isExtensionOk(file, allowedExtensions);
    return result;
  }

  /**
   * Check File Size less than or Equals Max Required Size ...
   */
  private async isMaxAllowedFileSizeOk(file: File) {
    //
    const maxAllowedFileSize = await this.getValueAsync(
      this.maxAllowedFileSize
    );

    //
    const result =
      maxAllowedFileSize && maxAllowedFileSize > 0
        ? file.size <= maxAllowedFileSize
        : true;

    //
    return result;
  }

  /**
   * Check File Size satisfied Min Required Size ...
   */
  private async isMinAllowedFileSizeOk(file: File) {
    //
    const minAllowedFileSize = await this.getValueAsync(
      this.minAllowedFileSize
    );

    //
    const result =
      minAllowedFileSize && minAllowedFileSize > 0
        ? file.size >= minAllowedFileSize
        : true;

    //
    return result;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-avatar-upload\x-avatar-upload.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XButtonModule } from '../x-button/x-button.module';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XAvatarUploadComponent } from './x-avatar-upload.component';
import { XThumbnailModule } from '../x-thumbnail/x-thumbnail.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XFileDropAreaModule } from '../x-file-drop-area/x-file-drop-area.module';

@NgModule({
  declarations: [XAvatarUploadComponent],
  imports: [
    XIconModule,
    XCardModule,
    CommonModule,
    XButtonModule,
    XSlotterModule,
    XThumbnailModule,
    XFileDropAreaModule,
    XBaseComponentModule,
  ],
  exports: [
    XIconModule,
    XCardModule,
    XButtonModule,
    XSlotterModule,
    XThumbnailModule,
    XFileDropAreaModule,
    XBaseComponentModule,
    XAvatarUploadComponent,
  ],
})
export class XAvatarUploadModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-avatar-upload\x-avatar-upload.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XColor,
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XCardFileFileDropContainer,
  IXCardFileFileDropContainer,
  XCardFileFileDropContainerComponent,
} from '../classes/i-x-file.container';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XIconIdentifier, XIconNames } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XImageCropperService } from '../x-image-cropper/x-image-cropper.service';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XAvatarUploadAction {
  //
  Clear = 'clear',
  Select = 'select',
  Refresh = 'refresh',
}

export type XAvatarUploadActionIdentifier = XAvatarUploadAction | string;

export interface XAvatarUploadActionModel
  extends XBaseActionModel<XAvatarUploadActionIdentifier> {}

export enum XAvatarStatus {
  OK = 'ok',
  EMPTY = 'empty',
  SIZE_EXCEEDED = 'size_exceeded',
  NOT_ALLOWED_TYPE = 'not_allowed_type',
  MIN_SIZE_NOT_REACHED = 'min_size_not_reached',
}

export type XAvatarStatusIdentifier = XAvatarStatus | string;

export enum XAvatarUploadActionButtonName {
  Crop,
  Clear,
  Select,
}

export interface XAvatarUploadActionButton {
  index?: number;
  name: XAvatarUploadActionButtonName;
  icon?: XStandardType<XIconIdentifier>;
  color?: XStandardType<XColorIdentifier>;
  tooltip?: XStandardType<XResourceIdentifier>;
}

export const defaultAvatarUploadActionButtons: XAvatarUploadActionButton[] = [
  //
  // Select ...
  {
    index: 0,
    color: XColor.Success,
    icon: XIconNames.touch,
    tooltip: XResourceIDs.select,
    name: XAvatarUploadActionButtonName.Select,
  },
  //
  // Crop ...
  {
    index: 1,
    icon: XIconNames.crop,
    color: XColor.Tertiary,
    tooltip: XResourceIDs.crop,
    name: XAvatarUploadActionButtonName.Crop,
  },
  //
  // Clear ...
  {
    index: 2,
    color: XColor.Warning,
    icon: XIconNames.clear,
    tooltip: XResourceIDs.clear,
    name: XAvatarUploadActionButtonName.Clear,
  },
];

//
//#region Inside Card Container ...
export interface IXAvatarUploadContainer extends IXCardFileFileDropContainer {
  //
  actionProvider: Subject<XAvatarUploadActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  placeHolderAvatar: XStandardType<string>;

  //
  actions: XStandardType<XOneOrManyType<XAvatarUploadActionButton>>;

  //
  avatarChange: EventEmitter<File>;
}

@Injectable({
  providedIn: 'root',
})
export class XAvatarUploadContainer
  extends XCardFileFileDropContainer
  implements Readonly<IXAvatarUploadContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XAvatarUploadActionModel> =
    new Subject<XAvatarUploadActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly placeHolderAvatar: XStandardType<string>;

  //
  readonly actions: XStandardType<XOneOrManyType<XAvatarUploadActionButton>> =
    defaultAvatarUploadActionButtons;

  //
  readonly avatarChange: EventEmitter<File> = new EventEmitter<File>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XAvatarUploadContainerComponent
  extends XCardFileFileDropContainerComponent
  implements IXAvatarUploadContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XAvatarUploadActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  placeHolderAvatar: XStandardType<string> =
    this.propertyProvider.placeHolderAvatar;

  //
  @Input()
  actions: XStandardType<XOneOrManyType<XAvatarUploadActionButton>> =
    this.propertyProvider.actions;

  //
  @Output()
  avatarChange: EventEmitter<File> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    public imageCropperService: XImageCropperService,
    protected propertyProvider: XAvatarUploadContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXAvatarUploadContainer
  ): Observable<IXAvatarUploadContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            placeHolderAvatar: this.getValue(
              container ? container.placeHolderAvatar : this.placeHolderAvatar
            ),
            actions: this.getValue(
              container ? container.actions : this.actions
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            avatarChange: this.avatarChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-button\x-button.component.html

<ng-container
  *ngTemplateOutlet="
    buttonRef;
    context: { $implicit: getValue(this.type) | async }
  "
></ng-container>

<ng-template #buttonRef let-type>
  <!-- Raised -->
  <button
    mat-raised-button
    class="x-button x-wrapper"
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    *ngIf="type === XButtonTypes.Raised"
    [disabled]="uiDisabled || loading"
    (click)="handleButtonClicked($event)"
    [ngClass]="getValue(cssClass) | async"
    [disableRipple]="uiDisabled || loading"
    [isForeground]="getValue(foregroundColor) | async"
  >
    <!-- Has Icon -->
    <ng-container *ngIf="hasIcon() | async">
      <ng-container *ngTemplateOutlet="bothRef"></ng-container>
    </ng-container>

    <!-- Hasn't Icon -->
    <ng-container *ngIf="!(hasIcon() | async)">
      <ng-container
        *ngTemplateOutlet="contentRef; context: { $implicit: true }"
      ></ng-container>
    </ng-container>
  </button>

  <!-- Icon -->
  <button
    mat-icon-button
    class="x-button x-wrapper"
    (click)="handleButtonClicked()"
    *ngIf="type === XButtonTypes.Icon"
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    [disabled]="uiDisabled || loading"
    [ngClass]="getValue(cssClass) | async"
    [disableRipple]="uiDisabled || loading"
    [matTooltip]="getResourceValue(title) | async"
    [isForeground]="getValue(foregroundColor) | async"
  >
    <ng-container
      *ngTemplateOutlet="contentRef; context: { $implicit: false }"
    ></ng-container>
  </button>
</ng-template>

<ng-template #bothRef>
  <x-slotter
    [hasCenterSlot]="false"
    [startTopSlotCssClass]="'ion-text-center'"
    [endBottomSlotCssClass]="'ion-text-center'"
    [layout]="
      (isVerticalLayout() | async)
        ? XSlotLayouts.VERTICAL
        : XSlotLayouts.HORIZONTAL
    "
  >
    <!-- icon slot -->
    <x-slot
      [name]="iconSlotName() | async"
      [cssClass]="'x-button-icon-wrapper'"
    >
      <ng-container
        *ngTemplateOutlet="contentRef; context: { $implicit: false }"
      ></ng-container>
    </x-slot>

    <!-- title slot -->
    <x-slot [name]="titleSlotName() | async">
      <ng-container
        *ngTemplateOutlet="contentRef; context: { $implicit: true }"
      ></ng-container>
    </x-slot>
  </x-slotter>
</ng-template>

<ng-template #contentRef let-showTitle>
  <!-- if Title Not Provided -->
  <ng-container *ngIf="!showTitle || !title || !(getValue(title) | async)">
    <!-- Has Icon -->
    <ng-container *ngIf="hasIcon() | async">
      <x-icon
        [name]="getValue(icon) | async"
        [color]="getValue(iconColor) | async"
        [foregroundColor]="getValue(foregroundColor) | async"
      ></x-icon>
    </ng-container>

    <!-- Hasn't Icon -->
    <ng-container *ngIf="!(hasIcon() | async)">
      <ng-content></ng-content>
    </ng-container>
  </ng-container>

  <!-- if Title Provided -->
  <ng-container *ngIf="showTitle && title && (getValue(title) | async)">
    {{ getResourceValue(title) | async }}
  </ng-container>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-button\x-button.component.scss

:host ::ng-deep .x-button-icon-wrapper {
  vertical-align: middle;
  display: inline-grid;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-button\x-button.component.ts

import {
  nameof,
  isNullOrUndefined,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XButtonType,
  XButtonAction,
  XButtonActionModel,
  XButtonIconPosition,
  XButtonContainerComponent,
} from './x-button.typings';
import { concatMap, map, switchMap } from 'rxjs/operators';
import { of, Observable, Subscription, Subject } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XSlotLayout, XSlotName } from '../x-slotter/x-slotter.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-button',
  templateUrl: './x-button.component.html',
  styleUrls: ['./x-button.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XButtonComponent extends XButtonContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly XSlotNames = Object.assign({}, XSlotName);
  readonly XButtonTypes = Object.assign({}, XButtonType);
  readonly XSlotLayouts = Object.assign({}, XSlotLayout);
  readonly XButtonIconPositions = Object.assign({}, XButtonIconPosition);
  //#endregion

  //
  //#region LifeCycle ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Action Provider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XButtonComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region is IconPositionChanged ...
    const isIconPositionChanged = changeKeys.includes(
      nameof<XButtonComponent>('iconPosition')
    );
    if (isIconPositionChanged && isNullOrUndefined(this.iconPosition)) {
      //
      this.iconPosition = XButtonIconPosition.Start;
      this.detectChanges();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  hasIcon() {
    return this.notNullOrUndefinedValue(this.icon);
  }

  hasTitle() {
    return this.notNullOrUndefinedValue(this.title);
  }

  useSlot() {
    return this.hasIcon().pipe(
      concatMap((hasIcon) =>
        this.hasTitle().pipe(map((hasTitle) => hasIcon && hasTitle))
      )
    );
  }

  isVerticalLayout() {
    return this.useSlot().pipe(
      concatMap((useSlot) => {
        //
        if (!useSlot) {
          return of(false);
        }

        //
        return this.getValue(this.iconPosition).pipe(
          map(
            (iPos) =>
              iPos === this.XButtonIconPositions.Above ||
              iPos === this.XButtonIconPositions.Below
          )
        );
      })
    );
  }

  iconSlotName(): Observable<XSlotName> {
    return this.getValue(this.iconPosition).pipe(
      map((iPos) => {
        //
        switch (iPos) {
          //
          case this.XButtonIconPositions.Start:
            return this.XSlotNames.START;

          //
          case this.XButtonIconPositions.End:
            return this.XSlotNames.END;

          //
          case this.XButtonIconPositions.Above:
            return this.XSlotNames.TOP;

          //
          case this.XButtonIconPositions.Below:
            return this.XSlotNames.BOTTOM;

          //
          default:
            return this.XSlotNames.START;
        }
      })
    );
  }

  titleSlotName(): Observable<XSlotName> {
    return this.iconSlotName().pipe(
      map((iSlot) => {
        //
        switch (iSlot) {
          //
          case this.XSlotNames.START:
            return this.XSlotNames.END;

          //
          case this.XSlotNames.END:
            return this.XSlotNames.START;

          //
          case this.XSlotNames.TOP:
            return this.XSlotNames.BOTTOM;

          //
          case this.XSlotNames.BOTTOM:
            return this.XSlotNames.TOP;

          //
          default:
            return this.XSlotNames.CENTER;
        }
      })
    );
  }

  getButtonColor() {
    return this.hasIcon().pipe(
      switchMap((hasIcon) =>
        this.getValue(hasIcon ? XColorWithBrightness.Transparent : this.color)
      )
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  handleButtonClicked(event?: Event) {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    this.clicked.emit(event);
  }
  //#endregion

  //
  //#region Actions ...
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XButtonActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XButtonAction.Click:
            this.clicked.emit();
            break;

          //
          case XButtonAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-button\x-button.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XButtonComponent } from './x-button.component';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XButtonComponent],
  imports: [CommonModule, XIconModule, XSlotterModule, XBaseComponentModule],
  exports: [
    CommonModule,
    XIconModule,
    XSlotterModule,
    XButtonComponent,
    XBaseComponentModule,
  ],
})
export class XButtonModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-button\x-button.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Renderer2,
  Component,
  Injectable,
  ElementRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseContainer } from './../classes/i-x-base.container';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XButtonType {
  Raised = 'raised',
  Icon = 'icon',
}

export type XButtonTypeIdentifier = XButtonType | string;

export enum XButtonIconPosition {
  Above = 'above',
  Start = 'strat',
  Below = 'below',
  End = 'end',
}

export type XButtonIconPositionIdentifier = XButtonIconPosition | string;

export enum XButtonAction {
  Click = 'click',
  Refresh = 'refresh',
}

export type XButtonActionIdentifier = XButtonAction | string;

export interface XButtonActionModel
  extends XBaseActionModel<XButtonActionIdentifier> {}

//
//#region Container ...
export interface IXButtonContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XButtonActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  type: XStandardType<XButtonTypeIdentifier>;
  title: XStandardType<XResourceIdentifier>;
  icon: XStandardType<XIconIdentifier>;
  iconPosition: XStandardType<XButtonIconPositionIdentifier>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;
  iconColor: XStandardType<XColorIdentifier>;

  //
  clicked: EventEmitter<Event>;
}

@Injectable({
  providedIn: 'root',
})
export class XButtonContainer
  extends XBaseContainer
  implements Readonly<IXButtonContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XButtonActionModel> =
    new Subject<XButtonActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly type: XStandardType<XButtonTypeIdentifier> = XButtonType.Raised;
  readonly title: XStandardType<XResourceIdentifier>;
  readonly icon: XStandardType<XIconIdentifier>;
  readonly iconPosition: XStandardType<XButtonIconPositionIdentifier> =
    XButtonIconPosition.Start;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;
  readonly iconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;

  //
  readonly clicked: EventEmitter<Event> = new EventEmitter<Event>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XButtonContainerComponent
  extends XBaseContainerComponent
  implements IXButtonContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XButtonActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  type: XStandardType<XButtonTypeIdentifier> = this.propertyProvider.type;

  @Input()
  title: XStandardType<XResourceIdentifier> = this.propertyProvider.title;

  @Input()
  icon: XStandardType<XIconIdentifier> = this.propertyProvider.icon;

  @Input()
  iconPosition: XStandardType<XButtonIconPositionIdentifier> =
    this.propertyProvider.iconPosition;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  @Input()
  iconColor: XStandardType<XColorIdentifier> = this.propertyProvider.iconColor;

  //
  @Output()
  clicked: EventEmitter<Event> = new EventEmitter<Event>();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XButtonContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXButtonContainer
  ): Observable<IXButtonContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),

      //
      type: this.getValue(container ? container.type : this.type),
      title: this.getValue(container ? container.title : this.title),
      icon: this.getValue(container ? container.icon : this.icon),
      iconPosition: this.getValue(
        container ? container.iconPosition : this.iconPosition
      ),

      //
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),
      iconColor: this.getValue(
        container ? container.iconColor : this.iconColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          clicked: this.clicked,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-card\x-card.component.html

<mat-card
  class="x-card x-wrapper"
  [xColor]="getValue(color) | async"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
  [class.no-text]="isNoTextCard() | async"
  [class.page-card]="getValue(isInPage) | async"
  [isForeground]="getValue(foregroundColor) | async"
>
  <!-- Card Header -->
  <mat-card-header class="x-header" *ngIf="getValue(showHeader) | async">
    <!-- Avatar Image -->
    <section
      *ngIf="notEmptyValue(avatarUrl) | async"
      mat-card-avatar
      class="x-avatar"
    >
      <img
        mat-card-avatar
        class="x-avatar"
        [src]="getValue(avatarUrl) | async"
        *ngIf="
          (notEmptyValue(avatarUrl) | async) &&
          !(notNullOrUndefinedValue(avatarTemplate) | async)
        "
      />
      <ng-container *ngIf="notNullOrUndefinedValue(avatarTemplate) | async">
        <ng-container
          *ngTemplateOutlet="
            avatarTemplate;
            context: {
              avatarUrl: getValue(avatarUrl) | async
            }
          "
        ></ng-container>
      </ng-container>
    </section>

    <!-- Card Title -->
    <mat-card-title class="x-title" *ngIf="notEmptyValue(title) | async">
      {{ getResourceValue(title) | async }}
    </mat-card-title>

    <!-- Card Sub Title -->
    <mat-card-subtitle
      class="x-sub-title"
      *ngIf="notEmptyValue(subTitle) | async"
    >
      {{ getResourceValue(subTitle) | async }}</mat-card-subtitle
    >

    <!-- Header Content -->
    <ng-content select="[header]"></ng-content>
  </mat-card-header>

  <!-- Card Image -->
  <img
    mat-card-image
    class="x-image"
    [src]="getValue(imageUrl) | async"
    *ngIf="notEmptyValue(imageUrl) | async"
    [alt]="getResourceValue(imageDescription) | async"
  />

  <!-- Card Content -->
  <mat-card-content class="x-content" *ngIf="getValue(showContent) | async">
    <ng-content></ng-content>
  </mat-card-content>

  <!-- Card Actions -->
  <mat-card-actions class="x-actions" *ngIf="getValue(showActions) | async">
    <ng-content select="[actions]"></ng-content>
  </mat-card-actions>

  <!-- Card Footer -->
  <mat-card-footer
    #footer
    class="x-footer"
    *ngIf="getValue(showFooter) | async"
  >
    <ng-content select="[footer]"></ng-content>
  </mat-card-footer>
</mat-card>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-card\x-card.component.scss

//
@import '../theme/theme.scss';

//
:host::ng-deep.x-card.no-text {
  .mat-card-header-text {
    display: none;
    visibility: hidden;
  }
}

//
.x-card {
  @extend .p-0;
  width: inherit;
  height: var(--height, inherit);
}

.x-card.page-card {
  @include push-auto();
  @include applyWidthHeight();
  @include m-tb($d-l);
  @include animation('fadeInUp');

  &.mobile {
    box-sizing: border-box;
    width: $d-100p;
    max-width: $d-100p;
    min-width: $d-100p;
    @include m(0);
    margin-bottom: $d-l;
  }
}

.x-header,
.x-content,
.x-actions,
.x-footer {
  @include p($d-md);
}

.x-footer {
  margin: 0px !important;
}

.x-image {
  width: 100% !important;
  @extend .m-0;
}

.x-actions {
  margin: 0 !important;
}

.x-header:empty,
.x-footer:empty,
.x-actions:empty {
  display: none;
  visibility: hidden;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-card\x-card.component.ts

import {
  XCardAction,
  XCardActionModel,
  XCardContainerComponent,
} from './x-card.typings';
import { Subject, Subscription } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { nameof, isNullOrEmptyString } from 'x-framework-core';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-card',
  templateUrl: './x-card.component.html',
  styleUrls: ['./x-card.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XCardComponent extends XCardContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Action Provider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XCardComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  isNoTextCard() {
    return this.getResourceValue(this.title).pipe(
      concatMap((title) =>
        this.getResourceValue(this.subTitle).pipe(
          map((subTitle) => {
            return {
              title,
              subTitle,
            };
          }),
          map((object) => {
            return (
              isNullOrEmptyString(object.title) &&
              isNullOrEmptyString(object.subTitle)
            );
          })
        )
      )
    );
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XCardActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.actionProvider.asObservable()
    ).subscribe(async (model) => {
      //
      if (!model) {
        return;
      }

      //
      switch (model.action) {
        //
        case XCardAction.Refresh:
          this.detectChanges();
          break;
      }
    });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-card\x-card.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardComponent } from './x-card.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [
    XCardComponent
  ],
  imports: [CommonModule, XBaseComponentModule],
  exports: [XBaseComponentModule, XCardComponent],
})
export class XCardModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-card\x-card.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseComponentContainer,
  XBaseContainerComponent,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../classes/i-x-base.container';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XCardAction {
  Refresh = 'refresh',
}

export type XCardActionIdentifier = XCardAction | string;

export interface XCardActionModel
  extends XBaseActionModel<XCardActionIdentifier> {}

//
//#region Container ...
export interface IXCardContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XCardActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  showHeader: XStandardType<boolean>;
  title: XStandardType<XResourceIdentifier>;
  subTitle: XStandardType<XResourceIdentifier>;
  avatarUrl: XStandardType<string>;
  avatarTemplate: TemplateRef<any>;

  //
  imageUrl: XStandardType<string>;
  imageDescription: XStandardType<XResourceIdentifier>;

  //
  isInPage: XStandardType<boolean>;

  //
  showContent: XStandardType<boolean>;
  showActions: XStandardType<boolean>;
  showFooter: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XCardContainer
  extends XBaseContainer
  implements Readonly<IXCardContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XCardActionModel> =
    new Subject<XCardActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly showHeader: XStandardType<boolean> = false;
  readonly title: XStandardType<XResourceIdentifier>;
  readonly subTitle: XStandardType<XResourceIdentifier>;
  readonly avatarUrl: XStandardType<string>;
  readonly avatarTemplate: TemplateRef<any>;

  //
  readonly imageUrl: XStandardType<string>;
  readonly imageDescription: XStandardType<XResourceIdentifier>;

  //
  readonly isInPage: XStandardType<boolean> = false;

  //
  readonly showContent: XStandardType<boolean> = true;
  readonly showActions: XStandardType<boolean> = false;
  readonly showFooter: XStandardType<boolean> = false;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XCardContainerComponent
  extends XBaseContainerComponent
  implements IXCardContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XCardActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  showHeader: XStandardType<boolean> = this.propertyProvider.showHeader;

  @Input()
  title: XStandardType<XResourceIdentifier> = this.propertyProvider.title;

  @Input()
  subTitle: XStandardType<XResourceIdentifier> = this.propertyProvider.subTitle;

  @Input()
  avatarUrl: XStandardType<string> = this.propertyProvider.avatarUrl;

  @Input()
  avatarTemplate: TemplateRef<any> = this.propertyProvider.avatarTemplate;

  //
  @Input()
  imageUrl: XStandardType<string> = this.propertyProvider.imageUrl;

  @Input()
  imageDescription: XStandardType<XResourceIdentifier> =
    this.propertyProvider.imageDescription;

  //
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  //
  @Input()
  showContent: XStandardType<boolean> = this.propertyProvider.showContent;

  @Input()
  showActions: XStandardType<boolean> = this.propertyProvider.showActions;

  @Input()
  showFooter: XStandardType<boolean> = this.propertyProvider.showFooter;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCardContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCardContainer
  ): Observable<IXCardContainer> {
    return forkJoin({
      //
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),

      //
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),

      //
      showHeader: this.getValue(
        container ? container.showHeader : this.showHeader
      ),
      title: this.getValue(container ? container.title : this.title),
      subTitle: this.getValue(container ? container.subTitle : this.subTitle),
      avatarUrl: this.getValue(
        container ? container.avatarUrl : this.avatarUrl
      ),
      avatarTemplate: this.getValue(
        container ? container.avatarTemplate : this.avatarTemplate
      ),

      //
      imageUrl: this.getValue(container ? container.imageUrl : this.imageUrl),
      imageDescription: this.getValue(
        container ? container.imageDescription : this.imageDescription
      ),

      //
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),

      //
      showContent: this.getValue(
        container ? container.showContent : this.showContent
      ),
      showActions: this.getValue(
        container ? container.showActions : this.showActions
      ),
      showFooter: this.getValue(
        container ? container.showFooter : this.showFooter
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentCardContainer = {
  [TKey in keyof Pick<
    IXCardContainer,
    | 'actionProvider'
    | 'cssClass'
    | 'color'
    | 'foregroundColor'
    | 'title'
    | 'subTitle'
    | 'showFooter'
    | 'showActions'
  > as `card${Capitalize<string & TKey>}`]: IXCardContainer[TKey];
};

export interface IXComponentCardContainer
  extends TXComponentCardContainer,
    IXBaseComponentContainer {
  isInPage: XStandardType<boolean>;
  wrapWithCard: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentCardContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentCardContainer>
{
  //
  //#region Props ...
  //
  readonly isInPage: XStandardType<boolean> = false;
  readonly wrapWithCard: XStandardType<boolean> = false;

  //
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.actionProvider;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.title;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.subTitle;

  //
  readonly cardShowFooter: XStandardType<boolean> =
    this.propertyProvider.showFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.propertyProvider.showActions;
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XCardContainer) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XComponentCardContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentCardContainer
{
  //
  //#region Props ...
  //
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  //
  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;

  //
  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  //
  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  //
  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentCardContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentCardContainer
  ): Observable<IXComponentCardContainer> {
    return forkJoin({
      //
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardCssClass: this.getValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-chart\x-chart.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <!-- Component Box ... -->
  <div
    class="x-chart x-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <div #chartContainer class="x-chart-container"></div>
  </div>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-chart\x-chart.component.scss

:host {
  display: block;
  width: 100%;
  height: 100%;
}

.x-chart-wrapper {
  width: 100%;
  height: 100%;
  min-height: 300px; /* ارتفاع پیش‌فرض برای جلوگیری از فروپاشی layout */
}

.x-chart-container {
  width: 100%;
  height: 100%;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-chart\x-chart.component.ts

import {
  Component,
  ViewChild,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XChartAction,
  XChartOptions,
  XChartSeriesType,
  XChartContainerComponent,
} from './x-chart.typings';
import { Subscription } from 'rxjs';
import ApexCharts from 'apexcharts';
import { isNullOrUndefined, nameof } from 'x-framework-core';

@Component({
  selector: 'x-chart',
  templateUrl: './x-chart.component.html',
  styleUrls: ['./x-chart.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XChartComponent extends XChartContainerComponent {
  //
  //#region Props ...
  //
  private CHART_CONTAINER: ElementRef | undefined;

  @ViewChild('chartContainer', { static: false })
  set chartContainer(el: ElementRef | undefined) {
    //
    if (isNullOrUndefined(el)) {
      //
      this.destroyComponent();
      return;
    }

    //
    this.CHART_CONTAINER = el;
    this.initializeComponent();
  }

  get chartContainer() {
    return this.CHART_CONTAINER;
  }

  //
  private chartInstance: ApexCharts = null;

  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  async afterViewInit() {
    super.afterViewInit();

    //
    await this.initializeComponent();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    // Auto Update ...
    const isAutoUpdateChanged = changeKeys.includes(
      nameof<XChartComponent>('autoUpdate'),
    );
    if (isAutoUpdateChanged) {
      //
      if (isNullOrUndefined(this.autoUpdate)) {
        this.autoUpdate = this.propertyProvider.autoUpdate;
      }
    }

    //
    // Action Provider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XChartComponent>('actionProvider'),
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }

    //
    // Check Auto Update ...
    var autoUpdate = await this.getValueAsync(this.autoUpdate);
    if (
      autoUpdate &&
      !isNullOrUndefined(this.chartInstance) &&
      !isNullOrUndefined(this.chartContainer) &&
      !isNullOrUndefined(this.chartContainer.nativeElement)
    ) {
      //
      // Options Changed ...
      const isOptionsChanged = changeKeys.includes(
        nameof<XChartComponent>('options'),
      );
      if (isOptionsChanged) {
        //
        if (isNullOrUndefined(this.options)) {
          this.options = this.propertyProvider.options;
        }

        //
        const options = await this.getValueAsync(this.options);
        await this.chartInstance.updateOptions(options);
      }

      //
      // Series Changed ...
      const isSeriesChanged = changeKeys.includes(
        nameof<XChartComponent>('series'),
      );
      if (isSeriesChanged) {
        //
        if (isNullOrUndefined(this.series)) {
          this.series = this.propertyProvider.series;
        }

        //
        const series = await this.getValueAsync(this.series);
        await this.chartInstance.updateSeries(series);
      }
    }

    //
    this.detectChanges();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.destroyComponent();
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  private async initializeComponent() {
    //
    if (isNullOrUndefined(this.CHART_CONTAINER)) {
      return;
    }

    //
    // Reading Requirement Properties ...
    const chartId = await this.getValueAsync(this.uuid);
    const series = await this.getValueAsync(this.series);
    const options = await this.getValueAsync(this.options);

    //
    // Validate Options ...
    if (isNullOrUndefined(options)) {
      return;
    }

    //
    const computedStyle = getComputedStyle(document.documentElement);
    const primaryColor =
      computedStyle.getPropertyValue('--x-color-primary').trim() || '#008ffb';
    const textColor =
      computedStyle.getPropertyValue('--x-color-dark-contrast').trim() ||
      '#373d3f';
    const gridColor =
      computedStyle.getPropertyValue('--x-color-light-shade').trim() ||
      '#e0e0e0';

    //
    const finalOptions: XChartOptions = {
      ...options,
      series: series || options.series,
      chart: {
        ...options.chart,
        id: chartId,
        type: options.chart?.type || 'line',
        foreColor: textColor,
        events: {
          ...options.chart?.events,
          mounted: (chartContext, config) => {
            this.chartRendered.emit();
            options.chart?.events?.mounted?.(chartContext, config);
          },
          dataPointSelection: (event, chartContext, config) => {
            this.dataPointSelected.emit({ event, chartContext, config });
            options.chart?.events?.dataPointSelection?.(
              event,
              chartContext,
              config,
            );
          },
          zoomed: (chartContext, { xaxis, yaxis }) => {
            this.zoomed.emit({ xaxis, yaxis });
            options.chart?.events?.zoomed?.(chartContext, { xaxis, yaxis });
          },
        },
      },
      theme: {
        ...options.theme,
        palette: options.theme?.palette || 'palette1',
      },
      colors: options.colors || [primaryColor],
      grid: {
        ...options.grid,
        borderColor: gridColor,
      },
    };

    //
    this.chartInstance = new ApexCharts(
      this.chartContainer.nativeElement,
      finalOptions,
    );

    //
    await this.chartInstance.render();
  }

  private destroyComponent() {
    //
    if (isNullOrUndefined(this.chartInstance)) {
      return;
    }

    //
    this.chartInstance.destroy();
    this.chartInstance = null;
  }

  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = this.propertyProvider.actionProvider;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XChartAction.Refresh:
            this.detectChanges();
            break;

          //
          case XChartAction.Destroy:
            this.onDestroy();
            break;

          //
          case XChartAction.UpdateSeries:
            //
            if (
              !isNullOrUndefined(model.payload) ||
              !isNullOrUndefined(this.chartInstance)
            ) {
              //
              this.series = await this.getValueAsync(
                model.payload as XChartSeriesType,
              );
              await this.chartInstance.updateSeries(this.series);
              this.detectChanges();
            }
            break;

          //
          case XChartAction.UpdateOptions:
            //
            if (
              !isNullOrUndefined(model.payload) ||
              !isNullOrUndefined(this.chartInstance)
            ) {
              //
              this.options = await this.getValueAsync(
                model.payload as XChartOptions,
              );
              await this.chartInstance.updateOptions(this.options);
              this.detectChanges();
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-chart\x-chart.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XChartComponent } from './x-chart.component';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  providers: [],
  entryComponents: [],
  declarations: [XChartComponent],
  imports: [
    XCardModule,
    XIconModule,
    CommonModule,
    XSlotterModule,
    XBaseComponentModule,
  ],
  exports: [
    XCardModule,
    XIconModule,
    CommonModule,
    XSlotterModule,
    XChartComponent,
    XBaseComponentModule,
  ],
})
export class XChartModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-chart\x-chart.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
} from 'x-framework-core';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Chart ...
export type XChartAnnotationStyle = {
  background?: string;
  color?: string;
  fontFamily?: string;
  fontWeight?: string | number;
  fontSize?: string;
  cssClass?: string;
  padding?: {
    left?: number;
    right?: number;
    top?: number;
    bottom?: number;
  };
};

export type XChartAnnotationLabel = {
  borderColor?: string;
  borderWidth?: number;
  borderRadius?: number;
  text?: string | string[];
  textAnchor?: string;
  offsetX?: number;
  offsetY?: number;
  style?: XChartAnnotationStyle;
  position?: string;
  orientation?: string;
  mouseEnter?: (annotation: XChartAnnotationLabel, e: MouseEvent) => void;
  mouseLeave?: (annotation: XChartAnnotationLabel, e: MouseEvent) => void;
  click?: (annotation: XChartAnnotationLabel, e: MouseEvent) => void;
};

export type XCahrtAxisBaseAnnotations = {
  id?: number | string;
  strokeDashArray?: number;
  fillColor?: string;
  borderColor?: string;
  borderWidth?: number;
  opacity?: number;
  offsetX?: number;
  offsetY?: number;
  label?: XChartAnnotationLabel;
  draggable?: boolean;
};

export type XChartYAxisAnnotations = XCahrtAxisBaseAnnotations & {
  y?: null | number | string;
  y2?: null | number | string;
  width?: number | string;
  yAxisIndex?: number;
};

export type XChartXAxisAnnotations = XCahrtAxisBaseAnnotations & {
  x?: null | number | string;
  x2?: null | number | string;
};

export type XChartPointAnnotations = {
  id?: number | string;
  x?: number | string;
  y?: null | number;
  yAxisIndex?: number;
  seriesIndex?: number;
  /**
   * Ink Layer (#7): make this point annotation draggable. Overrides
   * `chart.ink.enabled`. Requires the `ink` feature.
   */
  draggable?: boolean;
  mouseEnter?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
  mouseLeave?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
  click?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
  marker?: {
    size?: number;
    fillColor?: string;
    strokeColor?: string;
    strokeWidth?: number;
    shape?: string;
    offsetX?: number;
    offsetY?: number;
    cssClass?: string;
  };
  label?: XChartAnnotationLabel;
  image?: {
    path?: string;
    width?: number;
    height?: number;
    offsetX?: number;
    offsetY?: number;
  };
  /**
   * Show a hover tooltip over the annotation marker, like a regular data
   * point. Useful for surfacing more detail than fits in the label.
   */
  tooltip?: {
    enabled?: boolean;
    /**
     * Static tooltip content (HTML allowed; an array is joined with line
     * breaks). Falls back to `label.text` when omitted.
     */
    text?: string | string[];
    /**
     * Returns the tooltip markup (HTML). Takes precedence over `text`.
     */
    formatter?: (opts: {
      annotation: XChartPointAnnotations;
      seriesIndex?: number;
      id?: number | string;
      w: any;
    }) => string;
    /**
     * Tooltip theme. Falls back to the global `tooltip.theme`.
     */
    theme?: 'light' | 'dark';
    offsetX?: number;
    offsetY?: number;
  };
  /**
   * Render arbitrary SVG markup at the annotation's position. Deprecated in
   * favor of `image`/`marker`, but still supported.
   */
  customSVG?: {
    SVG?: string;
    cssClass?: string;
    offsetX?: number;
    offsetY?: number;
  };
};

export type XChartTextAnnotations = {
  x?: number;
  y?: number;
  text?: string;
  textAnchor?: string;
  foreColor?: string;
  fontSize?: string | number;
  fontFamily?: undefined | string;
  fontWeight?: string | number;
  /** CSS selector for the parent element the text is appended to. */
  appendTo?: string;
  backgroundColor?: string;
  borderColor?: string;
  borderRadius?: number;
  borderWidth?: number;
  paddingLeft?: number;
  paddingRight?: number;
  paddingTop?: number;
  paddingBottom?: number;
};

export type XChartImageAnnotations = {
  path?: string;
  x?: number;
  y?: number;
  width?: number;
  height?: number;
};

export type XChartAnnotations = {
  texts?: XChartTextAnnotations[];
  yaxis?: XChartYAxisAnnotations[];
  xaxis?: XChartXAxisAnnotations[];
  points?: XChartPointAnnotations[];
  images?: XChartImageAnnotations[];
};

export type XChartContext = {
  config: XChartOptions;
  globals: any;
  [key: string]: any;
};

export type XChartColorFormatterOpts = {
  value: number;
  seriesIndex: number;
  dataPointIndex: number;
  w: XChartContext;
};

export type XChartFormatterOpts = {
  seriesIndex: number;
  dataPointIndex: number;
  series?: any[][];
  w: XChartContext;
  [key: string]: any;
};

export type XChartDropShadow = {
  enabled?: boolean;
  top?: number;
  left?: number;
  blur?: number;
  opacity?: number;
  /**
   * Shadow color. A single string applies to all series; an array applies
   * per-series (only respected by `chart.dropShadow`).
   */
  color?: string | string[];
};

export type XChartDataLabels = {
  enabled?: boolean;
  enabledOnSeries?: undefined | number[];
  textAnchor?: 'start' | 'middle' | 'end';
  distributed?: boolean;
  /**
   * Horizontal offset of the label. Pass a function to vary the offset per
   * data point, e.g. to separate labels that would otherwise overlap.
   * The function must be pure, as it may be called more than once per label.
   */
  offsetX?: number | ((opts: XChartFormatterOpts) => number);
  /**
   * Vertical offset of the label. Pass a function to vary the offset per
   * data point, e.g. to separate labels that would otherwise overlap.
   * The function must be pure, as it may be called more than once per label.
   */
  offsetY?: number | ((opts: XChartFormatterOpts) => number);
  style?: {
    fontSize?: string;
    fontFamily?: string;
    fontWeight?: string | number;
    colors?: string[];
  };
  background?: {
    enabled?: boolean;
    foreColor?: string;
    backgroundColor?: string;
    borderRadius?: number;
    padding?: number;
    opacity?: number;
    borderWidth?: number;
    borderColor?: string;
    dropShadow?: XChartDropShadow;
  };
  dropShadow?: XChartDropShadow;
  /**
   * Ride data labels to their new position on a data-change update (e.g. a bar
   * chart race) instead of snapping. Off by default. Bar/column only; speed and
   * easing follow chart.animations.dynamicAnimation.
   */
  animate?: {
    enabled?: boolean;
  };
  /**
   * Count the numeric value up/down from its previous value on update, like
   * countUp.js. Off by default. The formatter runs each frame so number
   * formatting is preserved. Bar/column only.
   */
  countUp?: {
    enabled?: boolean;
  };
  formatter?(
    val: string | number | number[],
    opts?: XChartFormatterOpts,
  ): string | number | (string | number)[];
};

export type XChartHierarchyNode = {
  /** The node's label. `name` is accepted as an alias. */
  x?: string | number;
  name?: string;
  /** The node's value. `value` is accepted as an alias. */
  y?: number | null;
  value?: number | null;
  color?: string;
  fillColor?: string;
  /** A second metric driving colour rather than size (`treemap`). */
  colorValue?: number;
  meta?: unknown;
  children?: XChartHierarchyNode[];
  /**
   * The `id` of a `drilldown.series` entry, read as another level by the
   * sunburst (and by the treemap with
   * `plotOptions.treemap.nested.drilldownAsLevels`).
   */
  drilldown?: string | number;
};

export type XChartParsing = {
  x?: string;
  y?: string | string[];
  z?: string;
};

export type XChartColorStop = {
  offset: number;
  color: string;
  opacity: number;
};

export type XChartFill = {
  colors?: string[];
  opacity?: number | number[];
  type?: string | string[];
  gradient?: {
    shade?: string;
    type?: string;
    shadeIntensity?: number;
    gradientToColors?: string[];
    inverseColors?: boolean;
    opacityFrom?: number | number[];
    opacityTo?: number | number[];
    stops?: number[];
    colorStops?: XChartColorStop[][] | XChartColorStop[];
  };
  image?: {
    src?: string | string[];
    width?: number;
    height?: number;
  };
  pattern?: {
    style?: string | string[];
    width?: number;
    height?: number;
    strokeWidth?: number;
  };
};

export type XChartAxisChartSeries = {
  name?: string;
  type?: string;
  color?: string;
  group?: string;
  hidden?: boolean;
  zIndex?: number;
  parsing?: XChartParsing;
  data:
    | (number | null)[]
    | {
        /**
         * A category label, a timestamp, or a `Date`. On a `datetime` axis all
         * three keep millisecond resolution.
         */
        x: string | number | Date;
        /**
         * A plain value for most charts. For `candlestick`/`boxPlot`, the
         * summary array (`[O,H,L,C]` / `[min,Q1,median,Q3,max]`). For `violin`, an
         * object carrying the precomputed density profile (`[value, weight]` pairs)
         * plus the raw observations rendered as jitter. For a `scatter` strip plot
         * (`plotOptions.scatter.jitter`), the array of observations in this category.
         */
        y:
          | number
          | null
          | number[]
          | { density: [number, number][]; points?: number[] };
        /**
         * Optional raw observations for a `boxPlot` data point, rendered as jitter
         * dots when `plotOptions.boxPlot.points.show` is enabled.
         */
        points?: number[];
        fill?: XChartFill;
        fillColor?: string;
        strokeColor?: string;
        meta?: unknown;
        /**
         * A second metric that drives this point's COLOUR, independent of `y`
         * which drives its size (`treemap`). See
         * `plotOptions.treemap.colorScale.colorValue` to read a different key.
         */
        colorValue?: number;
        /**
         * Nested hierarchy: this point is a branch containing these children,
         * to any depth (`treemap`, `sunburst`). A branch normally omits `y` and
         * takes the sum of its children instead.
         */
        children?: XChartHierarchyNode[];
        /**
         * Drilldown target: the `id` of a `drilldown.series` entry. Clicking this
         * point drills into that level. Requires the Drilldown feature.
         */
        drilldown?: string | number;
        goals?: {
          name?: string;
          value: number;
          strokeHeight?: number;
          strokeWidth?: number;
          strokeColor?: string;
          strokeDashArray?: number;
          strokeLineCap?: 'butt' | 'square' | 'round';
        }[];
        barHeightOffset?: number;
        columnWidthOffset?: number;
      }[]
    | [number, number | null][]
    | [number, (number | null)[]][]
    | number[][]
    // A `children` hierarchy for the partition charts, where a branch carries no
    // value of its own. Listed before the catch-all so authors get completion on
    // the node shape instead of falling through to `Record<string, any>`.
    | XChartHierarchyNode[]
    | Record<string, any>[];
}[];

export type XChartNonAxisChartSeries = number[] | XChartAxisChartSeries;

export type XChartGradientLegend = {
  enabled?: boolean;
  /**
   * Strip length for horizontal placements (top/bottom). Accepts a number
   * (pixels) or percentage string (e.g. `'70%'`, resolved against the chart's
   * SVG width). Default `'70%'`.
   */
  width?: number | string;
  /**
   * Strip length for vertical placements (left/right). Accepts a number
   * (pixels) or percentage string (e.g. `'70%'`, resolved against the chart's
   * SVG height). Default `'70%'`.
   */
  height?: number | string;
  /** Strip thickness (short axis) in pixels. Default 12. */
  thickness?: number;
  /**
   * Strip alignment within the legend area.
   * - top/bottom: 'start' = left, 'center', 'end' = right
   * - left/right: 'start' = top,  'center', 'end' = bottom
   * Default `'center'`.
   */
  align?: 'start' | 'center' | 'end';
  /**
   * Number of color stops sampled from the shade function when no explicit
   * `ranges` (or continuous scale) supply their own. Default 16.
   */
  stops?: number;
  /** Show min/max labels at the ends of the strip. Default true. */
  showLabels?: boolean;
  /** Show a value tooltip next to the arrow on mark hover. Default true. */
  showHoverValue?: boolean;
  labelStyle?: {
    fontSize?: string;
    fontFamily?: string;
    colors?: string;
  };
  arrow?: {
    size?: number;
    color?: string;
  };
  /** Formatter for min/max labels and the hover value tooltip. */
  formatter?(value: number): string;
};

export type XChartTreemapLevel = {
  /** Inset between a parent's edge and the children inside it. Default 4. */
  padding?: number;
  /** Container fill. Defaults to a neutral tint that deepens with each level. */
  fill?: string;
  fillOpacity?: number;
  borderColor?: string;
  borderWidth?: number;
  /** Falls back to `plotOptions.treemap.borderRadius`. */
  borderRadius?: number;
  hover?: {
    /** Outline the container on hover. Default true. */
    show?: boolean;
    color?: string;
    width?: number;
  };
  header?: {
    show?: boolean;
    /** Height of the strip reserved at the top of the container. Default 22. */
    height?: number;
    /**
     * Skip the strip on tiles narrower than this, where no name could be read
     * anyway. Default 40.
     */
    minWidth?: number;
    align?: 'left' | 'center' | 'right';
    offsetX?: number;
    offsetY?: number;
    /** Append the branch's aggregate to its name. Default false. */
    showValue?: boolean;
    formatter?(
      name: string,
      opts: {
        value: number;
        depth: number;
        seriesIndex: number;
        node: any;
        w: any;
      },
    ): string;
    style?: {
      fontSize?: string;
      fontFamily?: string;
      fontWeight?: number | string;
      color?: string;
      background?: string;
      cssClass?: string;
    };
  };
};

export type XChartBreadcrumb = {
  show?: boolean;
  position?: 'top-left' | 'top-right';
  separator?: string;
  /** Label of the leftmost crumb, the "everything" level. Default 'All'. */
  rootLabel?: string;
  offsetX?: number;
  offsetY?: number;
  formatter?(
    label: string,
    opts: { index: number; depth: number; data?: any },
  ): string;
};

export interface XChartUnitObject {
  /**
   * Stable identity. The datum's own `id`/`name` when the per-unit object form
   * supplies one, so a provider can address a specific unit ("Texas",
   * "employee 41") rather than a positional slot; otherwise
   * `"<seriesIndex>:<dataPointIndex>"`.
   */
  id: string;
  /** Global draw order across every category. */
  index: number;
  /** Category this mark belongs to. */
  seriesIndex: number;
  /** Index within its category. */
  dataPointIndex: number;
  /** Category label. */
  label: string;
  /** The datum's value, when the per-unit object form supplies one. */
  value?: number;
  /** The raw per-unit datum, when supplied. */
  datum?: any;
  /** The radius the engine would use, so a size-aware provider need not redo it. */
  r: number;
}

export interface XChartUnitPosition {
  /** Must match an `ApexUnitObject.id`; unknown ids are ignored. */
  id: string;
  x: number;
  y: number;
  /** Overrides the engine's radius for this mark. */
  r?: number;
}

export type XChartUnitLayout = (
  objects: XChartUnitObject[],
  rect: { x: number; y: number; width: number; height: number },
) => XChartUnitPosition[];

export type XChartPlotOptions = {
  line?: {
    isSlopeChart?: boolean;
    colors?: {
      threshold?: number;
      colorAboveThreshold?: string;
      colorBelowThreshold?: string;
    };
  };
  area?: {
    fillTo?: 'origin' | 'end';
  };
  bar?: {
    horizontal?: boolean;
    columnWidth?: string | number;
    barHeight?: string | number;
    distributed?: boolean;
    borderRadius?: number;
    borderRadiusApplication?: 'around' | 'end';
    borderRadiusWhenStacked?: 'all' | 'last';
    hideZeroBarsWhenGrouped?: boolean;
    rangeBarOverlap?: boolean;
    rangeBarGroupRows?: boolean;
    isDumbbell?: boolean;
    dumbbellColors?: string[][];
    isFunnel?: boolean;
    isFunnel3d?: boolean;
    colors?: {
      ranges?: {
        from?: number;
        to?: number;
        color?: string;
      }[];
      backgroundBarColors?: string[];
      backgroundBarOpacity?: number;
      backgroundBarRadius?: number;
    };
    dataLabels?: {
      maxItems?: number;
      hideOverflowingLabels?: boolean;
      position?: string;
      orientation?: 'horizontal' | 'vertical';
      total?: {
        enabled?: boolean;
        formatter?(val?: string, opts?: XChartFormatterOpts): string;
        offsetX?: number;
        offsetY?: number;
        style?: {
          color?: string;
          fontSize?: string;
          fontFamily?: string;
          fontWeight?: number | string;
        };
      };
    };
  };
  bubble?: {
    zScaling?: boolean;
    minBubbleRadius?: number;
    maxBubbleRadius?: number;
  };
  scatter?: {
    /**
     * Spread overlapping points apart ("jitter"). Two uses, one engine:
     *  - Strip plot: supply data as `{ x: 'Category', y: [v1, v2, ...] }`. Each
     *    category becomes a band and the values scatter horizontally within it.
     *  - Overplotting: ordinary `{ x, y }` points get a small random offset so
     *    dense clusters fan out. The underlying data (and tooltip values) stay
     *    exact; only the drawn position moves.
     * Offsets are in axis units and deterministic (stable across re-renders).
     */
    jitter?: {
      enabled?: boolean;
      /** Max ± horizontal offset, in x-axis units (1 = one category step). */
      x?: number;
      /** Max ± vertical offset, in y-axis units. */
      y?: number;
      /** Single series: colour each band differently (by its position). */
      distributed?: boolean;
      /** Per-band cap; values beyond this are stride-thinned. */
      maxPoints?: number;
    };
  };
  candlestick?: {
    type?: string;
    colors?: {
      upward?: string | string[];
      downward?: string | string[];
    };
    wick?: {
      useFillColor?: boolean;
    };
  };
  boxPlot?: {
    colors?: {
      upper?: string | string[];
      lower?: string | string[];
    };
    /**
     * Where the whiskers reach when the summary is DERIVED from raw
     * observations: a datum supplying `points` instead of a five-number `y`,
     * which requires `import 'apexcharts/features/stats'`. A precomputed
     * summary is drawn exactly as given and ignores this.
     *
     * `'minmax'` (default) reaches the extremes, so nothing is hidden.
     * `'tukey'` stops at the last observation within 1.5 * IQR of each
     * quartile; points beyond the fence then fall outside the whisker, so pair
     * it with `points.show` or they become invisible.
     */
    whiskers?: 'minmax' | 'tukey';
    /**
     * Individual observations ("jitter") overlaid on each box. Inert unless a
     * data point supplies a `points: number[]` array; `show` is false by
     * default so existing boxPlot charts are unchanged.
     *
     * `points` is also the sample the five-number summary is derived from when
     * a datum has no `y` (see `whiskers`), so the observations live in one
     * place whether the library summarises them or you do.
     */
    points?: {
      show?: boolean;
      shape?: 'circle' | 'square';
      /** Marker radius in pixels. */
      size?: number;
      /** 0..1 fraction of the box half-width to scatter within. */
      jitter?: number;
      /** Cap per box; observations beyond this are stride-thinned. */
      maxPoints?: number;
      opacity?: number;
      /**
       * Dot fill colour. Defaults to 'series-dark' (a darker shade of the
       * series colour). Use 'series' for the series colour, or any literal
       * colour string.
       */
      fillColor?: string;
      /** Colour of the outline around each dot. Defaults to '#fff'. */
      strokeColor?: string;
      /** Width of the dot's outline in pixels. Defaults to 1; 0 disables it. */
      strokeWidth?: number;
      /**
       * Colour each dot by its value along a colour ramp (overrides fillColor).
       * Points are bucketed into `steps` shades to keep rendering performant.
       */
      colorScale?: {
        colors: string[];
        min?: number;
        max?: number;
        steps?: number;
      };
    };
  };
  violin?: {
    /**
     * Multiplies the density-derived half-width. 1 maps the density's own
     * maxWeight to half the category slot.
     */
    bandwidthScale?: number;
    /**
     * Kernel density estimation, used only when the density is DERIVED from raw
     * observations: a datum supplying `points`, or a flat number array as `y`,
     * which requires `import 'apexcharts/features/stats'`. A precomputed
     * density profile is drawn exactly as given.
     */
    kde?: {
      /**
       * Kernel width in value units. Unset uses Silverman's rule of thumb.
       * This is the statistical parameter; `bandwidthScale` above only scales
       * the drawn width.
       */
      bandwidth?: number;
      /** Density samples per violin (default 64). */
      resolution?: number;
    };
    /**
     * 'individual' (default): each violin is scaled to its own peak density, so
     * all violins reach the full slot width. 'group': all violins share the
     * densest violin's scale, keeping widths proportional to density across
     * categories.
     */
    normalize?: 'individual' | 'group';
    /** Individual observations ("jitter") overlaid on the violin shape. */
    points?: {
      show?: boolean;
      shape?: 'circle' | 'square';
      /** Marker radius in pixels. */
      size?: number;
      /** 0..1 fraction of the half-width to scatter within. */
      jitter?: number;
      /** Clamp jitter to the density width at each value so points stay inside. */
      constrainToViolin?: boolean;
      /** Cap per violin; observations beyond this are stride-thinned. */
      maxPoints?: number;
      opacity?: number;
      /**
       * Dot fill colour. Defaults to 'series-dark' (a darker shade of each
       * violin's own colour). Use 'series' for the violin's colour as-is, or
       * any literal colour string (e.g. '#fff').
       */
      fillColor?: string;
      /** Colour of the ring/outline around each dot. Defaults to '#fff'. */
      strokeColor?: string;
      /** Width of the dot's outline in pixels. Defaults to 1; 0 disables it. */
      strokeWidth?: number;
      /**
       * Colour each dot by its value along a colour ramp (overrides fillColor).
       * Points are bucketed into `steps` shades to keep rendering performant.
       */
      colorScale?: {
        /** Hex colour stops, low → high (a sequential colour ramp). */
        colors: string[];
        /** Value mapped to the first stop. Defaults to the data minimum. */
        min?: number;
        /** Value mapped to the last stop. Defaults to the data maximum. */
        max?: number;
        /** Number of shade buckets. Defaults to 24. */
        steps?: number;
      };
    };
  };
  /**
   * `chart.type: 'histogram'`. The series carry raw observations (a flat
   * number array, or `{ y }` objects) and are binned into one column per bin;
   * all series share one set of edges so overlaid distributions stay
   * comparable.
   *
   * Requires the optional stats feature. Import from `apexcharts/histogram`,
   * or add `import 'apexcharts/features/stats'` alongside `apexcharts/bar`.
   * The default `apexcharts` bundle already includes it. Without it the chart
   * warns and draws nothing, rather than rendering one bar per observation.
   */
  histogram?: {
    /**
     * How the bin width is chosen: a rule name, or a fixed bin count.
     * `'auto'` takes the narrower of Freedman-Diaconis and Sturges, falling
     * back to Sturges when the IQR is 0.
     */
    bins?: 'auto' | 'fd' | 'sturges' | 'scott' | 'rice' | 'sqrt' | number;
    /**
     * Explicit bin width in value units. Wins over `bins`, for when the
     * boundaries carry meaning (decades, 5-minute buckets) rather than being
     * a statistical choice.
     */
    binWidth?: number;
    /** `[min, max]` to bin over instead of the data's own extent. */
    range?: number[];
    /**
     * y units: observations per bin, percent of the series total, or
     * `count / (n * binWidth)` so the total area is 1.
     */
    normalize?: 'count' | 'relative' | 'density';
    /** Running total across bins, i.e. a cumulative distribution. */
    cumulative?: boolean;
    /**
     * With more than one series, draw every distribution across the full bin
     * so they overlay, instead of dividing the bin between them. Defaults to
     * `true`: all series already share one set of edges, and comparing two
     * shapes is the reason to put them on one axis. Set `false` for
     * side-by-side bars.
     *
     * An overlay also softens the fill and drops the bin separator stroke, so
     * the overlapping region reads. Both remain overridable.
     */
    overlap?: boolean;
  };
  heatmap?: {
    radius?: number;
    enableShades?: boolean;
    shadeIntensity?: number;
    reverseNegativeShade?: boolean;
    distributed?: boolean;
    useFillColorAsStroke?: boolean;
    colorScale?: {
      ranges?: {
        from?: number;
        to?: number;
        color?: string;
        foreColor?: string;
        name?: string;
      }[];
      inverse?: boolean;
      min?: number;
      max?: number;
      /**
       * When enabled, replaces the default categorical legend with a
       * continuous color gradient strip and a hover indicator arrow that
       * tracks the currently hovered mark's value along the spectrum.
       * Follows `legend.position` (top / right / bottom / left); the arrow
       * orientation flips to point at the strip from the chart-facing side.
       */
      gradientLegend?: XChartGradientLegend;
    };
  };
  funnel?: {
    /**
     * 'rectangle' (default) preserves the existing centered-rectangle funnel
     * geometry. 'trapezoid' produces continuous sloped sides between
     * consecutive stages (each stage's bottom width matches the next stage's
     * top width).
     */
    shape?: 'rectangle' | 'trapezoid';
    /**
     * For `shape: 'trapezoid'` only — last stage's bottom edge:
     * 'flat' (default, parallel sides) or 'taper' (taper to a point).
     */
    lastShape?: 'flat' | 'taper';
  };
  treemap?: {
    enableShades?: boolean;
    shadeIntensity?: number;
    distributed?: boolean;
    reverseNegativeShade?: boolean;
    useFillColorAsStroke?: boolean;
    dataLabels?: {
      format?: 'scale' | 'truncate';
      /**
       * Skip a tile's label when it would render below this size in px.
       *
       * With `format: 'scale'` the font size follows the tile's area, so a
       * dense treemap asks for a lot of text only a few pixels tall. Each such
       * label still has to be built and measured against the DOM, which on a
       * large chart dominates the render. Default 4, below the smallest label
       * any bundled sample draws. Set 0 to label every tile regardless.
       */
      minFontSize?: number;
    };
    borderRadius?: number;
    colorScale?: {
      inverse?: boolean;
      ranges?: {
        from?: number;
        to?: number;
        color?: string;
        foreColor?: string;
        name?: string;
      }[];
      min?: number;
      max?: number;
      /**
       * Colour a tile by a SECOND metric, independent of the value that sizes
       * it: area is how big something is, colour is how it did. Reads
       * `datum.colorValue` by default; pass a key name to read a different
       * property, or an accessor to compute one.
       */
      colorValue?:
        | string
        | ((
            datum: any,
            opts: { seriesIndex: number; dataPointIndex: number; w: any },
          ) => number);
      /**
       * Continuous interpolation between colour stops, for the metric above.
       * Active as soon as any datum carries a colour metric; `enabled: false`
       * opts out and `true` forces it on. `ranges` is unaffected and still
       * applies wherever it is set.
       */
      gradient?: {
        enabled?: boolean;
        /** Domain low. Defaults to the extent of the colour metric. */
        min?: number;
        /** Domain high. Defaults to the extent of the colour metric. */
        max?: number;
        /**
         * The value the middle colour is pinned to. Defaults to 0 when the
         * domain straddles zero (a diverging metric), otherwise none. Pass
         * `null` to force a plain sequential ramp.
         */
        midpoint?: number | null;
        /**
         * With a midpoint, balance the domain around it so equal moves in
         * either direction read as equally saturated. Default true.
         */
        symmetric?: boolean;
        /** Low -> mid -> high. Two colours make a sequential ramp. */
        colors?: string[];
        /** Explicit stops; overrides `colors` and `midpoint`. */
        stops?: { value: number; color: string }[];
      };
      /**
       * Continuous colour legend for the metric above: a gradient strip with
       * end labels and a hover indicator, in place of the categorical legend.
       */
      gradientLegend?: XChartGradientLegend;
    };
    /**
     * Arbitrary-depth treemap. A datum may carry `children` to any depth;
     * every branch is drawn as a real container with a header strip and its
     * children inset below it.
     */
    nested?: {
      /**
       * Parent containers appear on their own as soon as the data is nested.
       * `false` forces the flat two-level layout.
       */
      enabled?: boolean;
      /**
       * Read `drilldown: '<id>'` ids as extra levels instead of as a click
       * target for the drilldown feature. Default false, because on a treemap
       * that id has always meant "descend on click".
       */
      drilldownAsLevels?: boolean;
    };
    /**
     * How a branch is drawn once the data is nested. Per-level overrides go in
     * `levels`.
     */
    parents?: XChartTreemapLevel & {
      /** `'auto'` (default): on when the data carries `children`. */
      show?: boolean | 'auto';
      tooltip?: {
        formatter?(opts: {
          name: string;
          value: number;
          depth: number;
          leafCount: number;
          percentOfParent: number;
          percentOfTotal: number;
          node: any;
          w: any;
        }): string;
      };
    };
    /**
     * Per-depth overrides of `parents`, indexed from the outermost group
     * actually drawn (0 = the series, or the first authored level when a
     * single series is unwrapped).
     */
    levels?: XChartTreemapLevel[];
    /**
     * Click a group to fill the canvas with it; a breadcrumb goes back.
     *
     * Ignored when the drilldown feature is active on the same chart: both
     * navigate the hierarchy, and drilldown owns the click there.
     */
    zoom?: {
      enabled?: boolean;
      /**
       * Overrides `drilldown.breadcrumb` for this chart only, so a zoomed
       * treemap and a drilled-in chart present the same affordance without
       * importing the drilldown feature.
       */
      breadcrumb?: XChartBreadcrumb;
    };
    seriesTitle?: {
      show?: boolean;
      offsetY?: number;
      offsetX?: number;
      borderColor?: string;
      borderWidth?: number;
      borderRadius?: number;
      style?: {
        background?: string;
        color?: string;
        fontSize?: string;
        fontFamily?: string;
        fontWeight?: number | string;
        cssClass?: string;
        padding?: {
          left?: number;
          right?: number;
          top?: number;
          bottom?: number;
        };
      };
    };
  };
  unit?: {
    /**
     * 'grouped' (default): each category is its own cluster, laid out in a row.
     * 'packed': one blob; categories are coloured and (with sortByGroup) ordered
     * smallest-first so the minority group nests in the centre.
     * 'columns': each category is a vertical bar built from stacked dots (a unit
     * / waffle column) whose height encodes the count.
     * 'grid': one lattice of cells filled in category order - a waffle /
     * part-to-whole square "pie" (`chart.type: 'waffle'` presets this layout).
     * 'scatter': beeswarm - each unit placed on a real numeric X value axis by
     * its per-unit value, laned by category on Y (draws its own axis + lanes).
     * 'arc': parliament / hemicycle - seats in concentric arced rows, filled in
     * category order so each category is a contiguous wedge (see `arc`).
     * 'custom': positions come from `positions`.
     */
    layout?:
      | 'grouped'
      | 'packed'
      | 'columns'
      | 'grid'
      | 'scatter'
      | 'arc'
      | 'custom';
    /**
     * `layout: 'custom'` only. The layout provider: either a function returning
     * plot-pixel positions, or the name of one registered with
     * `ApexCharts.registerUnitLayout`.
     *
     * A layout is objects in, positions out. It knows nothing about animation,
     * because the engine already tweens position, radius and colour and already
     * keeps each mark's identity across a relayout, so an arrangement the
     * built-in layouts cannot express needs no new transition code.
     *
     * A mark whose id the provider omits animates out; ids matching no mark are
     * ignored.
     */
    positions?: string | XChartUnitLayout;
    /**
     * How dots are matched between renders on an update (which previous dot a
     * new dot tweens from).
     * 'group' (default): keyed per category, so a dot stays in its group and
     * category-level enters/exits fade in and out.
     * 'flow': keyed by global draw order, so the anonymous crowd migrates (and
     * recolours) across a regroup - the circles-to-bars transition.
     * 'identity': keyed by each datum's `id`/`name`, so a SPECIFIC unit migrates
     * across any regroup or relayout keeping its colour and size. Requires the
     * per-unit object form with unique ids/names.
     */
    transition?: 'group' | 'flow' | 'identity';
    /** Mark shape for each unit. `'image'` renders an icon (isotype pictogram). */
    shape?: 'circle' | 'square' | 'image';
    /** Icon used when `shape: 'image'`. */
    image?: {
      /** Icon URL or data URI. */
      src?: string;
      width?: number;
      height?: number;
      /**
       * Recolour a monochrome icon to the category colour (or a per-unit
       * `fillColor`) so the pictogram matches the legend. Leave off (default)
       * for multi-colour icons that should keep their own colours.
       */
      tint?: boolean;
    };
    /** Dot radius in px, or 'auto' to size dots so the largest cluster fits. */
    size?: number | 'auto';
    /**
     * The 'columns' layout can size its dots independently of `size` (which the
     * circle layouts / storyboard beats often pin to a constant so dots do not
     * resize while migrating).
     */
    columns?: {
      /**
       * 'inherit' (default) uses `size`; 'auto' sizes dots to fill the plot
       * height; a number pins a columns-only size. Circle / square only (image
       * icons keep their intrinsic size).
       */
      size?: 'inherit' | 'auto' | number;
    };
    /**
     * The 'grid' (waffle) layout: one lattice of cells filled in category order.
     */
    grid?: {
      /** Cells per row. Defaults to 10. */
      columns?: number;
      /**
       * Fixed cell budget (e.g. 100 for a percentage waffle); largest-remainder
       * allocates the cells to categories. Leave undefined for one cell per unit
       * (respects unitValue / maxUnits).
       */
      total?: number;
      /** First row of the fill: 'bottom' (default) or 'top'. */
      fillFrom?: 'bottom' | 'top';
      /**
       * Small multiples: render ONE mini-waffle per category in a trellis
       * instead of a single shared lattice. Each tile has `total` cells
       * (default 100) and fills value/`max` of them; the rest show as a faint
       * `trackColor` backdrop, and each tile carries its own label.
       */
      split?: boolean;
      /** Small-multiple tiles per row; undefined = auto (near-square). */
      tileColumns?: number;
      /**
       * Small-multiple value -> filled-cell denominator; undefined = the largest
       * count (leader fills its tile). Set to 100 for true "of 100" percentage tiles.
       */
      max?: number;
      /** Small-multiple empty ("track") cell colour; undefined = neutral grey. */
      trackColor?: string;
    };
    /**
     * The 'scatter' layout places units on real value axes (needs the object-form
     * data). `y:'lanes'` (default) is a beeswarm (X value axis, Y category lane);
     * `y:'value'` is a 2D value-value scatter (each datum's `x`/`y` on two numeric
     * axes, category = colour). `sizeRange` turns dots into bubbles.
     */
    scatter?: {
      /** 'lanes' (beeswarm, default) or 'value' (2D value-value scatter). */
      y?: 'lanes' | 'value';
      /** 'swarm' (anti-overlap pack, default) or 'jitter' (random lane spread). */
      spread?: 'swarm' | 'jitter';
      /**
       * Beeswarm orientation (1D `y:'lanes'` mode only). 'horizontal' (default):
       * value on X, category lanes stacked on Y. 'vertical': value on Y,
       * category lanes as columns across X. The value-axis config keys
       * (`xMin`/`xMax`/`xTitle`/`xFormatter`/`tickAmount`) describe the value
       * axis in both orientations.
       */
      orientation?: 'horizontal' | 'vertical';
      /** Approximate number of value-axis ticks. Defaults to 5. */
      tickAmount?: number;
      /** Fixed X-axis min / max; undefined = derived (nice-numbered) from data. */
      xMin?: number;
      xMax?: number;
      /** X-axis title drawn under the tick labels. */
      xTitle?: string;
      /** X tick-label formatter, `(value) => string`. */
      xFormatter?: (value: number) => string;
      /** Approximate number of Y-axis ticks (2D mode). Defaults to 5. */
      yTickAmount?: number;
      /** Fixed Y-axis min / max (2D mode); undefined = nice-numbered from data. */
      yMin?: number;
      yMax?: number;
      /** Y-axis title (2D mode), drawn rotated at the left. */
      yTitle?: string;
      /** Y tick-label formatter, `(value) => string`. */
      yFormatter?: (value: number) => string;
      /** Datum key holding the bubble size value. Defaults to 'z'. */
      sizeField?: string;
      /** `[minRadius, maxRadius]` in px: turns dots into area-scaled bubbles. */
      sizeRange?: [number, number];
      /** Left-gutter width reserved for lane (category) labels (lanes mode). */
      laneLabelWidth?: number;
      /** Draw the faint gridlines. Defaults to true. */
      gridlines?: boolean;
    };
    /**
     * Opt-in bubble sizing: scale each dot's radius by its per-unit value
     * (requires the object-form data, `series: [{ data: [{ value }] }]`).
     * Circle shape only; the lattice is spaced for the largest bubble so dots
     * never overlap. Ignored when there are no per-unit values.
     */
    sizeByValue?: {
      enabled?: boolean;
      /** Radius (px) for the largest value, or 'auto' to fit it to the plot. */
      maxRadius?: number | 'auto';
      /** Radius (px) for the smallest value; defaults to ~35% of maxRadius. */
      minRadius?: number;
      /** 'area' (bubble area proportional to value) or 'linear'. */
      scale?: 'area' | 'linear';
    };
    /** Packing gap factor between spiral shells (1 = dots touch). */
    spacing?: number;
    /**
     * How marks move between layouts on an update, and where entering marks
     * come from.
     */
    gather?: {
      /**
       * 'spring' settles each mark on a damped spring, so a gather interrupted
       * by the next update carries the marks' velocity into it instead of
       * restarting them from a standstill. 'tween' runs the fixed-duration
       * `easing` below. 'auto' (the default) is spring, unless `easing` was set
       * to something other than the default.
       */
      motion?: 'auto' | 'spring' | 'tween';
      /**
       * Spring character (`motion: 'spring'` only): 'crisp' (default),
       * 'gentle' (softer, for large reflows) or 'snappy' (faster, a hint of
       * settle). Scaled by `chart.animations.speed`.
       */
      spring?: 'crisp' | 'gentle' | 'snappy';
      /** Tween curve: 'outCubic' (default: decelerate and stop), 'inOutCubic' (weighted start), or 'outBack' (overshoot + settle). Setting this implies `motion: 'tween'`. */
      easing?: 'outCubic' | 'inOutCubic' | 'outBack';
      /** Overshoot strength for `easing: 'outBack'`. Defaults to 1.70158 (~10% overshoot). */
      overshoot?: number;
      /**
       * Where an ENTERING mark animates from (fresh mount, or a category
       * appearing): 'burst' (default) flies out from the cluster centre,
       * 'fade' materialises in place, 'rise' fades in while drifting gently
       * up into its slot.
       */
      enter?: 'burst' | 'fade' | 'rise';
    };
    /**
     * Options for `layout: 'arc'` (parliament / hemicycle). Angles use the
     * radialBar convention: 0 = top, clockwise. The default sweep is a top
     * semicircle; a full circle is `startAngle: 0, endAngle: 360`.
     */
    arc?: {
      /** Sweep start angle in degrees (0 = top, clockwise). Default -90. */
      startAngle?: number;
      /** Sweep end angle in degrees. Default 90 (a top semicircle). */
      endAngle?: number;
      /** Donut hole: inner radius as a fraction of the outer radius. Default 0.4. */
      innerRadiusRatio?: number;
      /** Number of concentric seat rows, or 'auto' to size dots as large as fit. */
      rows?: number | 'auto';
    };
    /** Corner radius for shape:'square'. */
    borderRadius?: number;
    /** 1 dot represents this many units of value (waffle scaling). */
    unitValue?: number;
    /** Safety cap on total dots; counts scale down proportionally above it. */
    maxUnits?: number;
    /** Packed layout: order categories smallest-first (minority centred). */
    sortByGroup?: boolean;
    clusterLabels?: {
      show?: boolean;
      /** Label placement relative to the cluster/bar. Defaults to 'top'. A 'bottom' label is always straight (the curved arc rides the top crown only). */
      position?: 'top' | 'bottom';
      curved?: boolean;
      fontSize?: string;
      fontFamily?: string;
      fontWeight?: number | string;
      /** Defaults to the cluster's own colour when undefined. */
      color?: string;
      offsetY?: number;
      /** Return "\n"-separated text to split an outer label over several lines. */
      formatter?(
        name: string,
        opts: { seriesIndex: number; value: number; percent: number; w: any },
      ): string;
      /**
       * Outer (name) labels, as pie/donut draw them: the label sits in the margin
       * beside the shape and a leader line joins it to the colour band it names,
       * so the crowd can be read without a legend.
       *
       * `layout: 'custom'` only, and best on a silhouette whose categories stack
       * vertically (the default row ordering): those alternate down the left and
       * right gutters. A column-ordered shape sends each label to the side its own
       * band sits on. The margin is taken off both sides so the shape stays
       * centred, so turning this on makes the silhouette a little smaller.
       */
      external?: {
        show?: boolean;
        connector?: {
          show?: boolean;
          width?: number;
          /** Defaults to the band's own colour when undefined. */
          color?: string;
          /** Air between the band's outermost dot and the leader line's bend. */
          gap?: number;
          /** Length of the run out to the label. */
          length?: number;
        };
        offsetX?: number;
        offsetY?: number;
      };
    };
    /** Per-unit (per-dot) tooltip. */
    tooltip?: {
      /**
       * Return the tooltip body for a single hovered dot. The dot's category is
       * `seriesIndex` and its index within that category is `dataPointIndex`, so
       * the formatter can index into per-unit data. Return a string or HTML.
       * Defaults to `"#<dataPointIndex+1> of <count>"`.
       */
      formatter?(opts: {
        seriesName: string;
        seriesIndex: number;
        dataPointIndex: number;
        /** Number of dots drawn for this category (after unitValue + maxUnits). */
        count: number;
        /** Raw category value (before unitValue scaling). */
        value: number;
        unitValue: number;
        /**
         * This dot's own datum when the per-unit object form was supplied
         * (`series: [{ name, data: [...] }]`); otherwise undefined.
         */
        datum: any;
        color: string;
        w: any;
      }): string;
    };
  };
  pie?: {
    startAngle?: number;
    endAngle?: number;
    customScale?: number;
    offsetX?: number;
    offsetY?: number;
    expandOnClick?: boolean;
    /**
     * How far a clicked slice slides out of the pie (px), measured along its
     * own mid-angle. The slice is translated, not redrawn at a bigger radius,
     * so its shape is unchanged and a gap opens between it and the rest of the
     * pie. Defaults to 10. Ignored for polarArea, and in a drilldown pie/donut
     * where a slice click navigates instead. Set 0 to keep the slice in place
     * on click.
     */
    expandOffset?: number;
    /**
     * Hover outline: a translucent band traced just outside the rim of the
     * hovered slice, so the slice keeps its own colour instead of being
     * lightened. Takes the place of the `states.hover` filter for pie, donut
     * and polarArea, and is skipped when `states.hover.filter.type` is
     * `'none'`.
     */
    hoverOutline?: {
      show?: boolean;
      /** Band thickness in px. Defaults to 8. */
      size?: number;
      /**
       * Extra clearance between the slice rim and the band, in px, on top of
       * the slice stroke (the band always starts at the outer edge of the
       * stroke, never under it). Defaults to 0, since a stroke is normally
       * present and already reads as the separation.
       */
      gap?: number;
      /** Band opacity over the slice colour. Defaults to 0.3. */
      opacity?: number;
      /** Band colour. Defaults to the hovered slice's colour. */
      color?: string;
    };
    /**
     * Rounds the corners of each slice (in px). Applies to pie, donut and
     * polarArea. Defaults to 0 (sharp corners). The value is clamped per
     * slice so corner fillets never cross on thin or narrow slices.
     */
    borderRadius?: number;
    /**
     * Gap between adjacent slices (in px). Applies to pie, donut and
     * polarArea. Defaults to 0 (slices touch). Each slice is inset
     * symmetrically, so its mid-angle (data label and hit region) is kept.
     */
    spacing?: number;
    dataLabels?: {
      offset?: number;
      minAngleToShowLabel?: number;
      /**
       * External (outer) labels: render the category/series name outside the
       * slice, joined by a leader (connector) line, so the chart is readable
       * without the legend. The percentage keeps rendering inside the slice.
       * Applies to pie and donut only (ignored for polarArea, where the radial
       * length already encodes the value).
       */
      external?: {
        show?: boolean;
        offsetX?: number;
        offsetY?: number;
        fontSize?: string;
        fontFamily?: string;
        fontWeight?: string | number;
        color?: string;
        /**
         * Return a string for a single-line label, or an array of strings to
         * stack multiple lines (e.g. `[name, percent + '%']`).
         */
        formatter?(
          name: string,
          opts: {
            seriesIndex: number;
            percent: number;
            value: number;
            w: XChartContext;
          },
        ): string | string[];
        /** Leader line from the slice edge to the label. */
        connector?: {
          show?: boolean;
          width?: number;
          color?: string;
          length?: number;
          gap?: number;
        };
      };
    };
    donut?: {
      size?: string;
      background?: string;
      labels?: {
        show?: boolean;
        name?: {
          show?: boolean;
          fontSize?: string;
          fontFamily?: string;
          fontWeight?: string | number;
          color?: string;
          offsetY?: number;
          formatter?(val: string): string;
        };
        value?: {
          show?: boolean;
          fontSize?: string;
          fontFamily?: string;
          fontWeight?: string | number;
          color?: string;
          offsetY?: number;
          formatter?(val: number | string): string;
        };
        total?: {
          show?: boolean;
          showAlways?: boolean;
          fontFamily?: string;
          fontWeight?: string | number;
          fontSize?: string;
          label?: string;
          color?: string;
          formatter?(w: XChartContext): string;
        };
      };
    };
  };
  polarArea?: {
    rings?: {
      strokeWidth?: number;
      strokeColor?: string;
    };
    spokes?: {
      strokeWidth?: number;
      connectorColors?: string | string[];
    };
  };
  /**
   * Sunburst / nested pie-donut (hierarchical radial). Rings go from the centre
   * hole outward, one per hierarchy level; each child arc is nested inside its
   * parent's angular wedge. Accepts a native `children` hierarchy or an existing
   * `drilldown` config (adapter).
   */
  sunburst?: {
    offsetX?: number;
    offsetY?: number;
    startAngle?: number;
    endAngle?: number;
    /** Centre hole radius as a % of the max radius (e.g. '15%'). */
    innerSize?: string;
    /** Corner rounding of each arc (px), same semantics as pie borderRadius. */
    borderRadius?: number;
    /** Gap between adjacent arcs (px), same semantics as pie spacing. */
    spacing?: number;
    /** Draw a shallow branch's leaf to the rim ('extend') or stop it ('stop'). */
    leaf?: 'extend' | 'stop';
    /** Angular partition of a parent's wedge among its children. */
    partition?: 'normalize' | 'strict';
    /** Per-depth lightening of the parent colour (0 = same, 1 = white). */
    tint?: number;
    /** Click a wedge to zoom into its branch (breadcrumb to go back). Default true. */
    zoomOnClick?: boolean;
    dataLabels?: {
      show?: boolean;
      /** Hide the label on any arc narrower than this (degrees). */
      minAngleToShow?: number;
      style?: {
        fontSize?: string;
        fontFamily?: string;
        fontWeight?: string | number;
        colors?: string[];
      };
    };
  };
  radar?: {
    size?: number;
    offsetX?: number;
    offsetY?: number;
    polygons?: {
      strokeColors?: string | string[];
      strokeWidth?: number | number[] | string | string[];
      connectorColors?: string | string[];
      fill?: {
        colors?: string[];
      };
    };
  };
  radialBar?: {
    inverseOrder?: boolean;
    startAngle?: number;
    endAngle?: number;
    offsetX?: number;
    offsetY?: number;
    /**
     * Gauge sub-shape. 'arc' (default) renders the existing filled value-arc
     * gauge; 'needle' replaces the value-arc with a rotating pointer/needle.
     * Bands and ticks are independent and work for both shapes.
     */
    shape?: 'arc' | 'needle';
    /**
     * Value-to-angle mapping (gauge). Defaults: min: 0, max: 100. Override
     * for gauges with a custom domain (e.g. min: 0, max: 240 speedometer).
     */
    min?: number;
    max?: number;
    /**
     * Threshold bands rendered as colored arc segments along the gauge arc.
     * Each band spans [`from`, `to`] in the gauge's `min..max` domain and is
     * filled with `color`.
     */
    bands?: Array<{
      from: number;
      to: number;
      color: string;
      label?: string;
    }>;
    bandsStyle?: {
      strokeWidth?: string;
      gap?: number;
      hideTrackWhenPresent?: boolean;
      linecap?: 'butt' | 'round' | 'square';
    };
    ticks?: {
      show?: boolean;
      major?: {
        count?: number;
        length?: number;
        width?: number;
        color?: string;
        placement?: 'inside' | 'outside';
      };
      minor?: {
        count?: number;
        length?: number;
        width?: number;
        color?: string;
        placement?: 'inside' | 'outside';
      };
      labels?: {
        show?: boolean;
        offset?: number;
        fontSize?: string;
        fontFamily?: string;
        fontWeight?: string | number;
        color?: string;
        formatter?: (value: number) => string;
      };
    };
    needle?: {
      color?: string;
      length?: string | number;
      baseWidth?: number;
      tipWidth?: number;
      /**
       * When true, also render the filled value-arc alongside the needle.
       * Default false preserves needle-only behavior.
       */
      showValueArc?: boolean;
      /**
       * px offset from the geometric arc center on Y. Positive values push
       * the needle base down (toward the chord midpoint of a ∩-shape
       * gauge); negative pushes up. The needle rotates around this shifted
       * point.
       */
      offsetY?: number;
      animation?: {
        enabled?: boolean;
        duration?: number;
        easing?: string;
      };
    };
    hollow?: {
      margin?: number;
      size?: string;
      background?: string;
      image?: string;
      imageWidth?: number;
      imageHeight?: number;
      imageOffsetX?: number;
      imageOffsetY?: number;
      imageClipped?: boolean;
      position?: 'front' | 'back';
      /**
       * Optional stroke color around the hollow ring. Combined with
       * `strokeDasharray` this produces a dashed indicator circle around
       * the value text.
       */
      stroke?: string;
      strokeWidth?: number;
      strokeDasharray?: string | number;
      dropShadow?: XChartDropShadow;
    };
    track?: {
      show?: boolean;
      startAngle?: number;
      endAngle?: number;
      background?: string | string[];
      strokeWidth?: string;
      opacity?: number;
      margin?: number;
      dropShadow?: XChartDropShadow;
    };
    dataLabels?: {
      show?: boolean;
      name?: {
        show?: boolean;
        fontFamily?: string;
        fontWeight?: string | number;
        fontSize?: string;
        color?: string;
        offsetY?: number;
        formatter?(seriesName: string): string;
      };
      value?: {
        show?: boolean;
        fontFamily?: string;
        fontSize?: string;
        fontWeight?: string | number;
        color?: string;
        offsetY?: number;
        formatter?(val: number): string;
      };
      total?: {
        show?: boolean;
        label?: string;
        color?: string;
        fontFamily?: string;
        fontWeight?: string | number;
        fontSize?: string;
        formatter?(w: XChartContext): string;
      };
    };
    barLabels?: {
      enabled?: boolean;
      offsetX?: number;
      offsetY?: number;
      useSeriesColors?: boolean;
      fontFamily?: string;
      fontWeight?: string | number;
      fontSize?: string;
      formatter?: (barName: string, opts?: XChartFormatterOpts) => string;
      onClick?: (barName: string, opts?: XChartFormatterOpts) => void;
    };
  };
};

export type XChartXAxis = {
  type?: 'category' | 'datetime' | 'numeric';
  /**
   * X-axis category labels. Pass a flat array for a single row of labels,
   * or a 2-D array (`[group, label][]`) to render grouped category axes.
   */
  categories?: Array<string | number> | Array<Array<string | number>>;
  overwriteCategories?: number[] | string[] | undefined;
  offsetX?: number;
  offsetY?: number;
  sorted?: boolean;
  labels?: {
    show?: boolean;
    rotate?: number;
    rotateAlways?: boolean;
    hideOverlappingLabels?: boolean;
    showDuplicates?: boolean;
    trim?: boolean;
    minHeight?: number;
    maxHeight?: number;
    style?: {
      colors?: string | string[];
      fontSize?: string;
      fontFamily?: string;
      fontWeight?: string | number;
      cssClass?: string;
    };
    offsetX?: number;
    offsetY?: number;
    format?: string;
    formatter?(
      value: string | number,
      timestamp?: number,
      opts?: XChartFormatterOpts,
    ): string | string[];
    datetimeUTC?: boolean;
    datetimeFormatter?: {
      year?: string;
      month?: string;
      day?: string;
      hour?: string;
      minute?: string;
      second?: string;
    };
  };
  group?: {
    groups?: { title: string; cols: number }[];
    style?: {
      colors?: string | string[];
      fontSize?: string;
      fontFamily?: string;
      fontWeight?: string | number;
      cssClass?: string;
    };
  };
  axisBorder?: {
    show?: boolean;
    color?: string;
    width?: string | number;
    height?: number;
    offsetX?: number;
    offsetY?: number;
  };
  axisTicks?: {
    show?: boolean;
    borderType?: 'solid' | 'dotted' | 'dashed';
    color?: string;
    height?: number;
    offsetX?: number;
    offsetY?: number;
  };
  tickPlacement?: string;
  tickAmount?: number | 'dataPoints';
  stepSize?: number;
  min?: number;
  max?: number;
  range?: number;
  floating?: boolean;
  decimalsInFloat?: number;
  position?: string;
  title?: {
    text?: string;
    offsetX?: number;
    offsetY?: number;
    style?: {
      color?: string;
      fontFamily?: string;
      fontWeight?: string | number;
      fontSize?: string;
      cssClass?: string;
    };
  };
  crosshairs?: {
    show?: boolean;
    width?: number | string;
    position?: string;
    opacity?: number;
    stroke?: {
      color?: string;
      width?: number;
      dashArray?: number;
    };
    fill?: {
      type?: string;
      color?: string;
      gradient?: {
        colorFrom?: string;
        colorTo?: string;
        stops?: number[];
        opacityFrom?: number;
        opacityTo?: number;
      };
    };
    dropShadow?: XChartDropShadow;
  };
  tooltip?: {
    enabled?: boolean;
    offsetY?: number;
    formatter?(value: string | number, opts?: XChartFormatterOpts): string;
    style?: {
      fontSize?: string;
      fontFamily?: string;
    };
  };
};

export type XChartYAxis = {
  show?: boolean;
  showAlways?: boolean;
  showForNullSeries?: boolean;
  seriesName?: string | string[];
  opposite?: boolean;
  reversed?: boolean;
  logarithmic?: boolean;
  logBase?: number;
  tickAmount?: number;
  stepSize?: number;
  forceNiceScale?: boolean;
  alignZero?: boolean;
  min?: number | ((min: number) => number);
  max?: number | ((max: number) => number);
  floating?: boolean;
  decimalsInFloat?: number;
  labels?: {
    show?: boolean;
    showDuplicates?: boolean;
    minWidth?: number;
    maxWidth?: number;
    offsetX?: number;
    offsetY?: number;
    rotate?: number;
    align?: 'left' | 'center' | 'right';
    padding?: number;
    style?: {
      colors?: string | string[];
      fontSize?: string;
      fontWeight?: string | number;
      fontFamily?: string;
      cssClass?: string;
    };
    formatter?(val: number, opts?: XChartFormatterOpts): string | string[];
  };
  axisBorder?: {
    show?: boolean;
    color?: string;
    width?: number;
    offsetX?: number;
    offsetY?: number;
  };
  axisTicks?: {
    show?: boolean;
    color?: string;
    width?: number;
    offsetX?: number;
    offsetY?: number;
  };
  title?: {
    text?: string;
    rotate?: number;
    offsetX?: number;
    offsetY?: number;
    style?: {
      color?: string;
      fontSize?: string;
      fontWeight?: string | number;
      fontFamily?: string;
      cssClass?: string;
    };
  };
  crosshairs?: {
    show?: boolean;
    position?: string;
    stroke?: {
      color?: string;
      width?: number;
      dashArray?: number;
    };
  };
  tooltip?: {
    enabled?: boolean;
    offsetX?: number;
  };
};

export type XChartLegendFormatterOpts = {
  seriesIndex: number;
  w: XChartContext;
};

export type XChartMarkerShapeOptions =
  | 'circle'
  | 'square'
  | 'rect'
  | 'line'
  | 'cross'
  | 'plus'
  | 'star'
  | 'sparkle'
  | 'diamond'
  | 'triangle';

export type XChartMarkerShape =
  | XChartMarkerShapeOptions
  | XChartMarkerShapeOptions[];

export type XChartDiscretePoint = {
  seriesIndex?: number;
  dataPointIndex?: number;
  fillColor?: string;
  strokeColor?: string;
  size?: number;
  shape?: XChartMarkerShape;
};

export type XChartMarkers = {
  size?: number | number[];
  colors?: string | string[];
  strokeColors?: string | string[];
  strokeWidth?: number | number[];
  strokeOpacity?: number | number[];
  strokeDashArray?: number | number[];
  fillOpacity?: number | number[];
  discrete?: XChartDiscretePoint[];
  shape?: XChartMarkerShape;
  offsetX?: number;
  offsetY?: number;
  showNullDataPoints?: boolean;
  onClick?(e?: MouseEvent): void;
  onDblClick?(e?: MouseEvent): void;
  hover?: {
    size?: number;
    sizeOffset?: number;
  };
};

export type XChartLegend = {
  show?: boolean;
  showForSingleSeries?: boolean;
  showForNullSeries?: boolean;
  showForZeroSeries?: boolean;
  floating?: boolean;
  inverseOrder?: boolean;
  position?: 'top' | 'right' | 'bottom' | 'left';
  horizontalAlign?: 'left' | 'center' | 'right';
  fontSize?: string;
  fontFamily?: string;
  fontWeight?: string | number;
  width?: number;
  height?: number;
  offsetX?: number;
  offsetY?: number;
  formatter?(legendName: string, opts?: XChartLegendFormatterOpts): string;
  tooltipHoverFormatter?(
    legendName: string,
    opts?: XChartLegendFormatterOpts,
  ): string;
  customLegendItems?: string[];
  clusterGroupedSeries?: boolean;
  clusterGroupedSeriesOrientation?: 'vertical' | 'horizontal';
  labels?: {
    colors?: string | string[];
    useSeriesColors?: boolean;
  };
  markers?: {
    size?: number;
    strokeWidth?: number;
    fillColors?: string[];
    shape?: XChartMarkerShape;
    offsetX?: number;
    offsetY?: number;
    customHTML?(): string;
    onClick?(e: MouseEvent): void;
  };
  itemMargin?: {
    horizontal?: number;
    vertical?: number;
  };
  onItemClick?: {
    toggleDataSeries?: boolean;
  };
  onItemHover?: {
    highlightDataSeries?: boolean;
  };
};

export type XChartEasing =
  | 'linear'
  | 'easeInSine'
  | 'easeOutSine'
  | 'easeInOutSine'
  | 'easeInQuad'
  | 'easeOutQuad'
  | 'easeInOutQuad'
  | 'easeInCubic'
  | 'easeOutCubic'
  | 'easeInOutCubic'
  | 'easeOutBack'
  | 'easeInOutBack'
  | (string & {})
  | [number, number, number, number]
  | ((t: number) => number);

export type XChartEventOpts = {
  seriesIndex: number;
  dataPointIndex: number;
  w: XChartContext;
  [key: string]: any;
};

export type XChartLocale = {
  name?: string;
  options?: {
    months?: string[];
    shortMonths?: string[];
    days?: string[];
    shortDays?: string[];
    toolbar?: {
      download?: string;
      selection?: string;
      selectionZoom?: string;
      zoomIn?: string;
      zoomOut?: string;
      pan?: string;
      reset?: string;
      measure?: string;
      menu?: string;
      exportToSVG?: string;
      exportToPNG?: string;
      exportToCSV?: string;
    };
  };
};

export interface XChartStoryboardBeatInfo {
  index: number;
  key: string | null;
  el: Element;
  direction: 'up' | 'down';
}

export interface XChartDrilldownSeries {
  /** Unique id referenced by a data point's `drilldown` field. */
  id: string | number;
  /** Display name used by the breadcrumb and as the (single-series) child series name. */
  name?: string;
  /** Child data points for a single-series level. Use this OR `series`. */
  data?: any[];
  /** Full multi-series array for a grouped/stacked drilldown level. Use this OR `data`. */
  series?: XChartAxisChartSeries;
  /** Optional chart-type override applied when this level is shown. */
  chart?: Pick<XChart, 'type' | 'stacked' | 'stackType'>;
  plotOptions?: XChartPlotOptions;
  xaxis?: XChartXAxis;
  yaxis?: XChartYAxis | XChartYAxis[];
  colors?: Array<string | ((opts: XChartColorFormatterOpts) => string)>;
  /** Optional fill override (e.g. a pattern fill to visually distinguish drilled levels). */
  fill?: XChartFill;
  /** Optional legend override (e.g. show a legend when a level is a pie/donut). */
  legend?: XChartLegend;
}

export interface XChartDrilldownEvent {
  /** The level id navigated away from. */
  from: string | number;
  /** The level id navigated to (`'root'` at the top). */
  to: string | number;
  /** The clicked data point (drill-down only). */
  point?: any;
  seriesIndex?: number;
  dataPointIndex?: number;
}

export interface XChartDrilldownContext {
  /** The requested level id, i.e. the clicked point's `drilldown` value. */
  id: string | number | null;
  point: any;
  seriesIndex?: number;
  dataPointIndex?: number;
}

export interface XChartDrilldown {
  /** Master switch. When false the feature stays inert even if imported. */
  enabled?: boolean;
  /** Inline child levels referenced by data-point `drilldown` ids. */
  series?: XChartDrilldownSeries[];
  breadcrumb?:
    | false
    | {
        show?: boolean;
        position?: 'top-left' | 'top-right';
        separator?: string;
        rootLabel?: string;
        offsetX?: number;
        offsetY?: number;
        formatter?(
          label: string,
          opts: { index: number; depth: number },
        ): string;
      };
  animation?: {
    enabled?: boolean;
    /**
     * Anchor the drill transition at the clicked point: the child unfolds
     * outward from it (and settles back on drill-up) instead of the chart
     * simply re-rendering. A gentle scale layered on the SVG. Opt-in.
     * Defaults to false.
     */
    zoomFromPoint?: boolean;
    /** Base transition duration in ms when `zoomFromPoint` is true. Default 260. */
    speed?: number;
  };
  /**
   * The dot marking a drillable point on a line/area chart drawn without
   * markers. A bar, slice, tile or cell is already a visible, clickable mark;
   * a line point is not, so without this nothing would show that a point can
   * be opened. Only drillable points get one. Set `show: false` to supply your
   * own affordance. Omitted colours inherit the series marker defaults.
   */
  marker?: {
    /** Default true. */
    show?: boolean;
    /** Radius in px. Default 6. */
    size?: number;
    /** Defaults to the series marker shape. */
    shape?: 'circle' | 'square' | 'rect';
    /** Defaults to the series colour. */
    fillColor?: string;
    /** Default '#fff'. */
    strokeColor?: string;
  };
  /**
   * Async resolver called when a drillable point has no inline `series` match.
   *
   * Failure never changes state: a throw, a rejection, or a resolved value
   * without a `data` array leaves the chart where it was and fires
   * `drillDownError`. A second click while one is in flight is ignored rather
   * than starting a second request.
   */
  onDrillDown?(
    ctx: XChartDrilldownContext,
  ): XChartDrilldownSeries | Promise<XChartDrilldownSeries>;
  /**
   * Overlay shown while an async level resolves. `text` is optional; with
   * none, the spinner shows alone and carries "Loading" as its accessible
   * name, so the default ships no user-visible English.
   */
  loading?:
    | false
    | {
        show?: boolean;
        text?: string;
      };
  /**
   * Cache levels resolved by `onDrillDown`, keyed by id, so drilling back down
   * a branch does not re-fetch. Default true. Clear it with the drilldown
   * module's `clearCache()` when the underlying data changes.
   */
  cache?: boolean;
}

export type XChartForecastDataPoints = {
  count?: number;
  fillOpacity?: number;
  strokeWidth?: undefined | number;
  dashArray?: number;
};

export type XChartGrid = {
  show?: boolean;
  borderColor?: string;
  strokeDashArray?: number;
  position?: 'front' | 'back';
  xaxis?: {
    lines?: {
      show?: boolean;
      offsetX?: number;
      offsetY?: number;
    };
  };
  yaxis?: {
    lines?: {
      show?: boolean;
      offsetX?: number;
      offsetY?: number;
    };
  };
  row?: {
    colors?: string[];
    opacity?: number;
  };
  column?: {
    colors?: string[];
    opacity?: number;
  };
  padding?: {
    top?: number;
    right?: number;
    bottom?: number;
    left?: number;
  };
};

export type XChartNoData = {
  text?: string;
  align?: 'left' | 'right' | 'center';
  verticalAlign?: 'top' | 'middle' | 'bottom';
  offsetX?: number;
  offsetY?: number;
  style?: {
    color?: string;
    fontSize?: string;
    fontFamily?: string;
  };
};

export interface XChartPluginActivation {
  name: string;
  options?: Record<string, any>;
  order?: number;
}

export type XChartResponsive = {
  breakpoint?: number;
  options?: XChartOptions;
};

export type XChartStates = {
  hover?: {
    filter?: {
      type?: 'none' | 'lighten' | 'darken';
      /**
       * Blend strength toward white (lighten) or black (darken), from 0 to 1.
       * Higher means a stronger effect. The shift is proportional to the base
       * color's head-room, so already-light colors are lightened only slightly
       * (and already-dark colors darkened only slightly) and never wash out.
       * @default 0.15
       */
      value?: number;
    };
  };
  active?: {
    allowMultipleDataPointsSelection?: boolean;
    filter?: {
      type?: 'none' | 'lighten' | 'darken';
      /**
       * Blend strength toward white (lighten) or black (darken), from 0 to 1.
       * Higher means a stronger effect.
       * @default 0.35
       */
      value?: number;
    };
  };
};

export type XChartStroke = {
  show?: boolean;
  curve?:
    | 'smooth'
    | 'straight'
    | 'stepline'
    | 'linestep'
    | 'monotoneCubic'
    | ('smooth' | 'straight' | 'stepline' | 'linestep' | 'monotoneCubic')[];
  lineCap?: 'butt' | 'square' | 'round';
  colors?: string[];
  width?: number | number[];
  dashArray?: number | number[];
  fill?: XChartFill;
};

export type XChartTitleSubtitle = {
  text?: string;
  align?: 'left' | 'center' | 'right';
  margin?: number;
  offsetX?: number;
  offsetY?: number;
  floating?: boolean;
  style?: {
    fontSize?: string;
    fontFamily?: string;
    fontWeight?: string | number;
    color?: string;
  };
};

export type XChartTheme = {
  /** '' (the default) inherits / auto-resolves; 'light' | 'dark' force a mode. */
  mode?: 'light' | 'dark' | '';
  palette?: string;
  /**
   * Facet (#13): read `--apx-*` CSS design tokens from the cascade
   * (`--apx-accent`, `--apx-fore`, `--apx-grid`, `--apx-surface`,
   * `--apx-series-1..N`). They top the resolution chain, below explicit config.
   * true (default) reads any present (absence is a no-op); false disables.
   * Tokens are re-read on each render; use `chart.refreshTokens()` after a
   * runtime CSS change that does not itself trigger a render.
   */
  tokens?: boolean;
  /**
   * Facet (#13): 'os' follows the operating system's `prefers-color-scheme`
   * (light/dark) and `prefers-contrast` reactively, with no JS. SSR-safe.
   */
  follow?: 'os' | false;
  /** Facet (#13): a theme registered via `ApexCharts.registerTheme(name, def)`. */
  name?: string;
  monochrome?: {
    enabled?: boolean;
    color?: string;
    shadeTo?: 'light' | 'dark';
    shadeIntensity?: number;
  };
  accessibility?: {
    colorBlindMode?:
      | 'deuteranopia'
      | 'protanopia'
      | 'tritanopia'
      | 'highContrast'
      | '';
  };
};

export type XChartTooltipCustomOpts = {
  series: number[][];
  seriesIndex: number;
  dataPointIndex: number;
  y1?: number;
  y2?: number;
  w: XChartContext;
};

export type XChartTooltipY = {
  title?: {
    formatter?(seriesName: string, opts?: XChartFormatterOpts): string;
  };
  formatter?(val: number, opts?: XChartFormatterOpts): string;
};

export type XChartTooltip = {
  enabled?: boolean;
  enabledOnSeries?: undefined | number[];
  shared?: boolean;
  followCursor?: boolean;
  intersect?: boolean;
  inverseOrder?: boolean;
  arrow?: boolean;
  custom?:
    | ((
        opts: XChartTooltipCustomOpts,
      ) => string | number | Element | { nodeName: string })
    | Array<
        (
          opts: XChartTooltipCustomOpts,
        ) => string | number | Element | { nodeName: string }
      >;
  fillSeriesColor?: boolean;
  theme?: 'light' | 'dark';
  cssClass?: string;
  hideEmptySeries?: boolean;
  style?: {
    fontSize?: string;
    fontFamily?: string;
    background?: string;
  };
  onDatasetHover?: {
    highlightDataSeries?: boolean;
  };
  x?: {
    show?: boolean;
    format?: string;
    formatter?(val: string | number, opts?: XChartFormatterOpts): string;
  };
  y?: XChartTooltipY | XChartTooltipY[];
  z?: {
    title?: string;
    formatter?(val: number): string;
  };
  marker?: {
    show?: boolean;
    fillColors?: string[];
  };
  items?: {
    display?: string;
  };
  fixed?: {
    enabled?: boolean;
    position?: string; // topRight; topLeft; bottomRight; bottomLeft
    offsetX?: number;
    offsetY?: number;
  };
};

export interface XChartOptions {
  annotations?: XChartAnnotations;
  chart?: XChart;
  /**
   * Series colors. Each entry is either a CSS color string (hex, rgb, hsl,
   * named) or a function returning one per-datapoint. The list cycles when
   * there are more series than colors.
   */
  colors?: Array<string | ((opts: XChartColorFormatterOpts) => string)>;
  dataLabels?: XChartDataLabels;
  /** Opt-in drilldown navigation. Requires `import 'apexcharts/features/drilldown'`. */
  drilldown?: XChartDrilldown;
  fill?: XChartFill;
  forecastDataPoints?: XChartForecastDataPoints;
  grid?: XChartGrid;
  labels?: string[];
  legend?: XChartLegend;
  markers?: XChartMarkers;
  noData?: XChartNoData;
  /** Weave (#1) plugin activation list. Requires `import 'apexcharts/features/weave'`. */
  plugins?: XChartPluginActivation[];
  plotOptions?: XChartPlotOptions;
  responsive?: XChartResponsive[];
  parsing?: XChartParsing;
  series?: XChartSeriesType;
  states?: XChartStates;
  stroke?: XChartStroke;
  subtitle?: XChartTitleSubtitle;
  theme?: XChartTheme;
  title?: XChartTitleSubtitle;
  tooltip?: XChartTooltip;
  xaxis?: XChartXAxis;
  yaxis?: XChartYAxis | XChartYAxis[];
}

export type XChart = {
  width?: string | number;
  height?: string | number;
  type?:
    | 'line'
    | 'area'
    | 'bar'
    | 'pie'
    | 'donut'
    | 'radialBar'
    | 'scatter'
    | 'bubble'
    | 'heatmap'
    | 'candlestick'
    | 'boxPlot'
    | 'violin'
    | 'histogram'
    | 'radar'
    | 'polarArea'
    | 'rangeBar'
    | 'rangeArea'
    | 'treemap'
    | 'unit'
    | 'waffle'
    | 'sunburst'
    | 'funnel'
    | 'pyramid'
    | 'gauge';
  /**
   * Internal — populated when `type` is a first-class alias (`'funnel'`,
   * `'pyramid'`, `'gauge'`, `'waffle'`, `'histogram'`). The original requested
   * type is preserved here while `type` is normalized to the underlying
   * renderer (`'bar'`, `'radialBar'` or `'unit'`). Read-only for consumers.
   */
  requestedType?: 'funnel' | 'pyramid' | 'gauge' | 'waffle' | 'histogram';
  foreColor?: string;
  fontFamily?: string;
  background?: string;
  offsetX?: number;
  offsetY?: number;
  dropShadow?: XChartDropShadow & {
    enabledOnSeries?: undefined | number[];
  };
  nonce?: string;
  events?: {
    animationEnd?(chart: ApexCharts, options?: XChartEventOpts): void;
    beforeMount?(chart: ApexCharts, options?: XChartEventOpts): void;
    mounted?(chart: ApexCharts, options?: XChartEventOpts): void;
    updated?(chart: ApexCharts, options?: XChartEventOpts): void;
    mouseMove?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    mouseLeave?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    click?(e: MouseEvent, chart?: ApexCharts, options?: XChartEventOpts): void;
    xAxisLabelClick?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    legendClick?(
      chart: ApexCharts,
      seriesIndex?: number,
      options?: XChartEventOpts,
    ): void;
    markerClick?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    selection?(
      chart: ApexCharts,
      options?: {
        xaxis?: { min: number; max: number };
        yaxis?: { min: number; max: number };
      },
    ): void;
    dataPointSelection?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    dataPointMouseEnter?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    dataPointMouseLeave?(
      e: MouseEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    beforeZoom?(
      chart: ApexCharts,
      options?: { xaxis: { min: number; max: number } },
    ): boolean | void;
    beforeResetZoom?(
      chart: ApexCharts,
      options?: XChartEventOpts,
    ): boolean | void;
    zoomed?(
      chart: ApexCharts,
      options?: {
        xaxis: { min: number; max: number };
        yaxis?: { min: number; max: number }[];
      },
    ): void;
    scrolled?(
      chart: ApexCharts,
      options?: { xaxis: { min: number; max: number } },
    ): void;
    brushScrolled?(
      chart: ApexCharts,
      options?: {
        xaxis: { min: number; max: number };
        yaxis?: { min: number; max: number }[];
      },
    ): void;
    /**
     * Linked Views (#4): fired on the source chart when a brush range drives a
     * crossfilter across the group.
     */
    crossFilter?(
      chart: ApexCharts,
      options?: { xaxis: { min: number; max: number }; sourceChartID?: string },
    ): void;
    /**
     * Linked Views (#4) FILTER mode: fired on the source chart when a click
     * toggles a crossfilter bucket. `options` carries the coordinator state
     * (active filters, filtered/total counts), the source chartID, and the key.
     */
    filterChange?(
      chart: ApexCharts,
      options?: {
        filters: Record<string, any>;
        filteredCount: number;
        total: number;
        sourceChartID?: string;
        key?: any;
      },
    ): void;
    /**
     * Ink Layer (#7): fired after an annotation is dragged or resized. `options`
     * carries the annotation type ('point' | 'xaxis' | 'yaxis'), id/index, and
     * the new data coordinates (x/y, plus x2/y2 for range annotations).
     */
    annotationDragged?(
      chart: ApexCharts,
      options?: {
        type?: 'point' | 'xaxis' | 'yaxis';
        id?: string;
        index: number;
        x: any;
        y: any;
        x2?: any;
        y2?: any;
      },
    ): void;
    /**
     * Ink Layer (#7): fired after a point annotation's label is edited inline.
     * `options` carries the annotation id/index and the new label text.
     */
    annotationEdited?(
      chart: ApexCharts,
      options?: {
        type?: 'point' | 'xaxis' | 'yaxis';
        id?: string;
        index: number;
        text: string;
      },
    ): void;
    /**
     * Ink Layer (#7): fired after an annotation is created by clicking the
     * plot in create mode or from the context menu (note or dashed line).
     * `options` carries the new annotation type/id/index and its x and/or y.
     */
    annotationCreated?(
      chart: ApexCharts,
      options?: {
        type?: 'point' | 'xaxis' | 'yaxis';
        id?: string;
        index: number;
        x?: any;
        y?: any;
      },
    ): void;
    /**
     * Ink Layer (#7): fired after an annotation is restyled from the floating
     * note editor (accent color, bold, font size, marker size/shape). `options`
     * carries the annotation type/id/index and its current label + marker config.
     */
    annotationStyled?(
      chart: ApexCharts,
      options?: {
        type?: 'point' | 'xaxis' | 'yaxis';
        id?: string;
        index: number;
        label?: any;
        marker?: any;
      },
    ): void;
    /**
     * Ink Layer (#7): fired after an annotation is deleted from the floating
     * note editor. `options` carries the annotation type/id and the index it
     * occupied before removal.
     */
    annotationDeleted?(
      chart: ApexCharts,
      options?: {
        type?: 'point' | 'xaxis' | 'yaxis';
        id?: string;
        index: number;
      },
    ): void;
    /**
     * Measure ruler (#18): fired when a measure ruler is drawn. Requires the
     * `measure` feature. `options` carries the endpoints and the deltas.
     */
    measured?(
      chart: ApexCharts,
      options?: {
        from: { x: any; y: any };
        to: { x: any; y: any };
        dx: number;
        dy: number;
        percentChange: number;
        slope: number;
      },
    ): void;
    /**
     * Storyboard: fired when scrolling (or goTo) activates a beat. Requires
     * the `storyboard` feature and an active chart.storyboard.bind().
     */
    beatChange?(chart: ApexCharts, options?: XChartStoryboardBeatInfo): void;
    keyDown?(
      e: KeyboardEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    keyUp?(
      e: KeyboardEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    /** Fired before a drill-down transition begins. Requires the Drilldown feature. */
    drillDownStart?(
      info: ApexCharts.ApexDrilldownEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    /** Fired after a drill-down transition completes. Requires the Drilldown feature. */
    drillDownEnd?(
      info: ApexCharts.ApexDrilldownEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    /** Fired after navigating back up a drilldown level. Requires the Drilldown feature. */
    drillUp?(
      info: ApexCharts.ApexDrilldownEvent,
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
    /** Fired when an async onDrillDown resolver throws or rejects. Requires the Drilldown feature. */
    drillDownError?(
      info: { id: string | number | null; error: any },
      chart?: ApexCharts,
      options?: XChartEventOpts,
    ): void;
  };
  brush?: {
    enabled?: boolean;
    autoScaleYaxis?: boolean;
    target?: string;
    targets?: string[];
  };
  /**
   * Linked Views (#4): crossfilter / linked highlighting. Requires the `link`
   * feature (`import 'apexcharts/features/link'`). Two modes:
   *
   * HIGHLIGHT (P1): `enabled` with no `dimension`. Charts sharing a
   * `chart.group` form a set; brushing a range (needs `chart.selection.enabled`)
   * on any member dims every member's marks whose x is outside the range, in
   * place (no re-render).
   *
   * FILTER (P2): set `dimension` (its presence selects this path). Each chart
   * declares a dimension + reduction over a shared record set registered with
   * `ApexCharts.crossfilter({ id, records })`; clicking a bucket re-aggregates
   * every other participating chart over the filtered subset.
   */
  link?: {
    /** @default false */
    enabled?: boolean;
    /** Highlight mode (P1) label; filter mode is selected by `dimension`. @default 'highlight' */
    mode?: 'highlight' | 'filter';
    /** Opacity applied to dimmed (unselected / out-of-range) marks. @default 0.2 */
    dimOpacity?: number;
    /** FILTER mode: crossfilter coordinator id (defaults to `chart.group`). */
    id?: string;
    /**
     * FILTER mode: `(row) => key`. Its presence selects filter mode. For a
     * heatmap (matrix) dimension it returns `[xKey, yKey]`.
     */
    dimension?: (row: any) => any;
    /** FILTER mode: reduction over a bucket's rows. @default 'count' */
    reduce?:
      | 'count'
      | { sum?: string; avg?: string; min?: string; max?: string }
      | ((rows: any[]) => number);
    /**
     * FILTER mode: bucket kind. Else inferred: `bins` present => 'range', a
     * heatmap chart => 'matrix' (2D), otherwise 'category'.
     */
    type?: 'category' | 'range' | 'matrix';
    /** FILTER mode (range dims): binning spec. */
    bins?: { width?: number; count?: number; thresholds?: number[] };
    /** FILTER mode (category dims): key ordering. @default 'first-seen' */
    order?: 'first-seen' | 'asc' | 'desc' | ((a: any, b: any) => number);
    /** FILTER mode (axis charts): the derived series name. @default 'Count' */
    seriesName?: string;
  };
  /**
   * Ink Layer (#7): direct-manipulation annotations. When enabled, every point
   * annotation is draggable (unless it sets `draggable:false`); or opt in per
   * annotation with `annotations.points[].draggable`. Clicking an ink-managed
   * annotation opens a floating editor card anchored to it: rename inline,
   * recolor via accent swatches, toggle bold, step the font size, size/reshape
   * the marker, or delete the note. Axis-line annotations get separate Label
   * and Line color rows, so restyling the label chip never touches the stroke.
   * Requires the `ink` feature (`import 'apexcharts/features/ink'`). Fires the
   * `annotationDragged`, `annotationEdited`, `annotationStyled` and
   * `annotationDeleted` events.
   */
  ink?: {
    /** @default false */
    enabled?: boolean;
    /**
     * Show a minimal "add note" tool palette; clicking it arms create mode (the
     * next plot click drops an editable, draggable annotation). @default false
     */
    palette?: boolean;
    /**
     * Snap a dragged point / axis-line annotation to the nearest gridline
     * (numeric x + linear y). @default false
     */
    snap?: boolean;
    /**
     * Accent swatches offered by the floating note editor. Defaults to a
     * built-in 6-color palette when omitted.
     */
    noteColors?: string[];
  };
  /**
   * Measure ruler (#18): a measure/delta ruler. Requires the `measure`
   * feature (`import 'apexcharts/features/measure'`). Hold `key` and drag
   * A->B on the plot, or call `chart.startMeasure()`, to read
   * dx/dy/%change/slope in data space; on release the ruler pins as a
   * data-anchored overlay that re-projects on zoom/resize. Fires `measured`.
   */
  measure?: {
    /** @default false */
    enabled?: boolean;
    /**
     * 'span': finance-style vertical band between two x-positions with a
     * change/%/range readout, endpoints snapped to the first series. 'free':
     * a diagonal ruler between two arbitrary points. @default 'span'
     */
    mode?: 'span' | 'free';
    /** Key held to arm a drag when not in sticky mode. @default 'm' */
    key?: string;
    /** Pin the ruler as a data-anchored overlay on release. @default true */
    pinOnRelease?: boolean;
    /**
     * Semantic colors. Every element also has a stable CSS class and a
     * direction class (apexcharts-measure-up|down|flat) for stylesheet theming.
     */
    colors?: { up?: string; down?: string; neutral?: string; guide?: string };
    /** Span mode: draw the shaded band between the two x-positions. @default true */
    band?: boolean;
    /** Span mode: draw the vertical dashed reference lines. @default true */
    guides?: boolean;
    /** Draw the endpoint dots on the series line. @default true */
    markers?: boolean;
    /** Value formatters for the readout. */
    format?: {
      x?: (x: number) => string;
      y?: (y: number) => string;
      percent?: (pct: number) => string;
    };
    /**
     * Full readout override. Receives the measure info and returns a string or
     * an array of lines. Overrides the default readout text.
     */
    label?: (info: {
      from: { x: any; y: any };
      to: { x: any; y: any };
      dx: number;
      dy: number;
      percentChange: number;
      slope: number;
      mode: 'span' | 'free';
    }) => string | string[];
  };
  /**
   * Radial Actions (#chrome): right-click / long-press context menu. Requires
   * the `contextMenu` feature (`import 'apexcharts/features/context-menu'`).
   * Each action receives the clicked data coordinates, so verbs act at that
   * point rather than chart-wide. 'measure' is shown only when the measure tool
   * is enabled. When the ink feature is bundled, 'annotate' drops an
   * ink-managed note that opens its floating editor (rename, restyle, delete),
   * and 'xline' / 'yline' drop ink-managed dashed lines the same way ('xline'
   * vertical at the clicked x, 'yline' horizontal at the clicked y).
   */
  contextMenu?: {
    /** @default false */
    enabled?: boolean;
    /**
     * Ordered menu items: built-in ids and/or custom entries. @default
     * ['annotate','xline','yline','measure']
     */
    items?: Array<
      | 'annotate'
      | 'xline'
      | 'yline'
      | 'measure'
      | {
          id?: string;
          label?: string;
          icon?: string;
          onClick?: (
            chart: ApexCharts,
            context: {
              x: any;
              y: any;
              seriesIndex: number | null;
              dataPointIndex: number | null;
              clientX: number;
              clientY: number;
            },
          ) => void;
        }
    >;
    /** Override the built-in item labels. */
    labels?: {
      annotate?: string;
      xline?: string;
      yline?: string;
      measure?: string;
    };
    /** Text of the annotation dropped by the built-in 'annotate' item. @default 'Note' */
    noteText?: string;
    /**
     * Shared styling for the built-in 'xline' ("Annotate here", vertical at
     * the clicked x) and 'yline' ("Mark this level", horizontal at the
     * clicked y) items. Lines only, never a range rectangle. With the ink
     * feature bundled the line opens the floating editor, whose Label and
     * Line color rows restyle the chip and the stroke independently, and is
     * draggable and undoable, like the note.
     */
    line?: {
      /** Label drawn on the line. @default '' (no label) */
      text?: string;
      /** @default 4 */
      strokeDashArray?: number;
      /** Line color; omit to keep the annotation default. */
      color?: string;
    };
  };
  id?: string;
  injectStyleSheet?: boolean;
  group?: string;
  /**
   * Per-chart license key for the gated premium features (storyboard, link /
   * crossfilter, ink, measure, contextMenu, perspectives, history). Overrides
   * ApexCharts.setLicense() and window.Apex.license for this chart. Without a
   * valid key those features still work but show an "APEXCHARTS" trial
   * watermark. Shared across the ApexCharts family.
   */
  license?: string;
  locales?: XChartLocale[];
  defaultLocale?: string;
  perspectives?: {
    serializeOptions?: string[];
  };
  history?: {
    enabled?: boolean;
    maxDepth?: number;
    coalesceMs?: number;
    keyboard?: boolean;
  };
  /** Strata (#2) series renderer. Requires `import 'apexcharts/features/renderer-canvas'` for non-SVG. */
  renderer?: 'svg' | 'canvas' | 'auto';
  rendererThreshold?: number;
  layers?: {
    series?: 'svg' | 'canvas' | 'auto';
    grid?: 'svg';
    annotations?: 'svg';
    dataLabels?: 'svg';
  };
  parentHeightOffset?: number;
  redrawOnParentResize?: boolean;
  redrawOnWindowResize?: boolean | ((...args: any[]) => boolean);
  sparkline?: {
    enabled?: boolean;
  };
  stacked?: boolean;
  stackType?: 'normal' | '100%';
  stackOnlyBar?: boolean;
  /**
   * Real-time streaming mode. When enabled, appendData() bounds memory
   * automatically: each series is trimmed to `maxPoints` (when set) or to the
   * visible `xaxis.range` window plus a small off-screen runway. The
   * constant-velocity scroll animation for windowed updates needs no opt-in.
   */
  streaming?: {
    enabled?: boolean;
    /** Maximum points kept per series by appendData(). Unset: derived from
     *  `xaxis.range` when that is set; otherwise no trimming occurs. */
    maxPoints?: number;
  };
  toolbar?: {
    show?: boolean;
    offsetX?: number;
    offsetY?: number;
    tools?: {
      download?: boolean | string;
      selection?: boolean | string;
      zoom?: boolean | string;
      zoomin?: boolean | string;
      zoomout?: boolean | string;
      pan?: boolean | string;
      reset?: boolean | string;
      /**
       * Measure ruler toggle. Shown only when `chart.measure.enabled` is true
       * and the `measure` feature is bundled. `false` keeps the ruler
       * key-driven only; a string supplies a custom SVG icon.
       */
      measure?: boolean | string;
      customIcons?: {
        icon?: string;
        title?: string;
        index?: number;
        class?: string;
        click?(
          chart: ApexCharts,
          options?: XChartEventOpts,
          e?: MouseEvent,
        ): void;
      }[];
    };
    export?: {
      csv?: {
        filename?: undefined | string;
        columnDelimiter?: string;
        headerCategory?: string;
        headerValue?: string;
        categoryFormatter?(value?: string | number): string;
        valueFormatter?(value?: string | number): string;
      };
      svg?: {
        filename?: undefined | string;
      };
      png?: {
        filename?: undefined | string;
      };
      width?: number;
      scale?: number;
      /**
       * Inline the `@font-face` rules for the fonts the chart actually uses
       * into the exported SVG/PNG as base64 data URIs.
       *
       * An exported SVG is a standalone document and cannot reach the page's
       * `@font-face` rules, so without this a custom font is replaced by a
       * generic fallback in the export. Cross-origin font files that deny CORS
       * are skipped and fall back as before.
       *
       * @default true
       */
      embedFonts?: boolean;
    };
    autoSelected?: 'zoom' | 'selection' | 'pan' | 'measure';
  };
  zoom?: {
    enabled?: boolean;
    type?: 'x' | 'y' | 'xy';
    autoScaleYaxis?: boolean;
    /**
     * Cursor-anchored zoom on mouse wheel / trackpad. `'auto'` enables it only
     * when the toolbar's reset button is present, so an unintended scroll-zoom
     * is always undoable; `true` forces it on even with the toolbar hidden.
     * Requires `enabled: true`.
     * @default 'auto'
     */
    allowMouseWheelZoom?: boolean | 'auto';
    /**
     * Momentum: enable two-finger pinch-zoom on touch devices. Zooms the x-axis
     * around the pinch centroid, frame-by-frame. `'auto'` enables it only when
     * the toolbar's reset button is present; `true` forces it on even with the
     * toolbar hidden. Requires `enabled: true`.
     * @default 'auto'
     */
    pinch?: boolean | 'auto';
    zoomedArea?: {
      fill?: {
        color?: string;
        opacity?: number;
      };
      stroke?: {
        color?: string;
        opacity?: number;
        width?: number;
      };
    };
  };
  /**
   * Momentum: kinetic panning on touch. A one-finger pan released with velocity
   * keeps gliding and decelerates, clamping at the data edges.
   */
  pan?: {
    /** @default true */
    inertia?: boolean;
    /** Velocity decay applied each animation frame (0-1). @default 0.92 */
    friction?: number;
  };
  selection?: {
    enabled?: boolean;
    type?: string;
    fill?: {
      color?: string;
      opacity?: number;
    };
    stroke?: {
      width?: number;
      color?: string;
      opacity?: number;
      dashArray?: number;
    };
    xaxis?: {
      min?: number;
      max?: number;
    };
    yaxis?: {
      min?: number;
      max?: number;
    };
  };
  animations?: {
    /**
     * Master switch. Each chart type gets a tailored initial-mount animation
     * by default (line/area pen-stroke draw, bar grow, scatter pop, heatmap
     * diagonal wave, treemap largest-first cascade, pie/donut/gauge sweep).
     * Set false to render charts without any animation.
     */
    enabled?: boolean;
    /** Animation duration in ms (default 800). */
    speed?: number;
    /**
     * Cadence (#6): easing for the generic tweens. See `ApexEasing` for the
     * complete built-in curve list and the accepted forms; register custom
     * names with `ApexCharts.registerEasing`.
     * @default 'easeInOutSine'
     */
    easing?: XChartEasing;
    /**
     * Drives per-element stagger across all chart types. When enabled, bars,
     * heatmap cells, scatter points, and treemap tiles reveal in sequence;
     * line/area markers fade in progressively as the line draws.
     */
    animateGradually?: {
      enabled?: boolean;
      /** Requested stagger step in ms; auto-capped per chart so total
       *  stagger ≤ ~half the animation speed. */
      delay?: number;
    };
    /** Data-change (updateSeries) animation. Independent from initial mount. */
    dynamicAnimation?: {
      enabled?: boolean;
      speed?: number;
      /**
       * Easing for data-change morphs only (same accepted forms as
       * `animations.easing`; see `ApexEasing`). Unset: inherits the
       * chart-wide easing, except detected streaming scrolls (appendData or
       * a shifted fixed-length window under `xaxis.range`) which default to
       * 'linear' so the window slides at constant velocity.
       */
      easing?: XChartEasing;
    };
    /**
     * Cross-type morph (updateOptions changing chart.type). Requires the
     * optional `apexcharts/features/morph` feature to be registered; without
     * that import these settings have no effect. Supported pairs include
     * bar ↔ pie/donut/radialBar/polarArea (and the trivial pie↔donut↔polarArea
     * cases). Falls back to instant snap when types or data shape are
     * incompatible.
     */
    chartTypeMorph?: {
      enabled?: boolean;
      speed?: number;
    };
    /**
     * When true (default), honors the OS-level prefers-reduced-motion media
     * query — all initial-mount animations are skipped and the chart renders
     * instantly. Set to false to override (e.g. for QA / demo screens).
     */
    respectReducedMotion?: boolean;
    /**
     * Above this many data points (default 1000), the per-element morph +
     * stagger — which spins up one JS-driven animation timeline per path — is
     * replaced by a single GPU-composited opacity fade of the whole series.
     * Keeps initial render and zoom transitions smooth on large datasets
     * (e.g. thousands of candlesticks/bars). Set to 0 to always animate
     * per-element regardless of dataset size.
     */
    largeDatasetThreshold?: number;
  };
  accessibility?: {
    enabled?: boolean;
    description?: string;
    announcements?: {
      enabled?: boolean;
    };
    keyboard?: {
      enabled?: boolean;
      navigation?: {
        enabled?: boolean;
        wrapAround?: boolean;
      };
    };
  };
  dataReducer?: {
    enabled?: boolean;
    algorithm?: 'lttb';
    targetPoints?: number;
    threshold?: number;
  };
};
//#endregion

export enum XChartAction {
  Refresh = 'refresh',
  Destroy = 'destroy',
  UpdateSeries = 'update_series',
  UpdateOptions = 'update_options',
}

export type XChartActionIdentifier = XChartAction | string;

export interface XChartActionModel extends XBaseActionModel<XChartActionIdentifier> {}

export type XChartSeriesType = XChartAxisChartSeries | XChartNonAxisChartSeries;
export interface IXChartContainer extends IXComponentCardContainer {
  //
  // Commons ...
  actionProvider: Subject<XChartActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  // Chart Props ...
  autoUpdate: XStandardType<boolean>;
  options: XStandardType<XChartOptions>;
  series: XStandardType<XChartSeriesType>;

  //
  // Events ...
  chartRendered: EventEmitter<void>;
  dataPointSelected: EventEmitter<any>;
  zoomed: EventEmitter<any>;
}

@Injectable({
  providedIn: 'root',
})
export class XChartContainer
  extends XComponentCardContainer
  implements Readonly<IXChartContainer>
{
  //
  readonly chartId: XStandardType<string>;
  readonly options: XStandardType<XChartOptions>;
  readonly autoUpdate: XStandardType<boolean> = true;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;
  readonly actionProvider = new Subject<XChartActionModel>();
  readonly series: XStandardType<XChartSeriesType>;

  //
  readonly zoomed = new EventEmitter<any>();
  readonly chartRendered = new EventEmitter<void>();
  readonly dataPointSelected = new EventEmitter<any>();
}

@Injectable()
@Component({ template: `` })
export abstract class XChartContainerComponent
  extends XComponentCardContainerComponent
  implements IXChartContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XChartActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  options: XStandardType<XChartOptions> = this.propertyProvider.options;

  @Input()
  series: XStandardType<XChartSeriesType> =
    this.propertyProvider.series;

  @Input()
  autoUpdate: XStandardType<boolean> = this.propertyProvider.autoUpdate;

  //
  // Events ...
  @Output()
  chartRendered: EventEmitter<void> = new EventEmitter();

  @Output()
  dataPointSelected: EventEmitter<any> = new EventEmitter();

  @Output()
  zoomed: EventEmitter<any> = new EventEmitter();

  //
  //#region ComponentCard Props ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XChartContainer,
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider,
    );
  }
  //#endregionّ

  //
  //#region Abstract ...
  public extractProps(
    container?: IXChartContainer,
  ): Observable<IXChartContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass,
            ),

            //
            options: this.getValue(
              container ? container.options : this.options,
            ),
            series: this.getValue(container ? container.series : this.series),
            autoUpdate: this.getValue(
              container ? container.autoUpdate : this.autoUpdate,
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            }),
          );
        }),
      )
      .pipe(
        map((info) => ({
          ...info.data,
          ...info.superData,
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
          chartRendered: this.chartRendered,
          dataPointSelected: this.dataPointSelected,
          zoomed: this.zoomed,
        })),
      );
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color\x-color.component.html

<div
  class="x-color x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  *ngIf="getValue(value) | async as item"
>
  <!-- Color Title -->
  <div
    class="x-color-title text-truncate"
    *ngIf="item.key && (getValue(showLabel) | async)"
  >
    <span [xColor]="getValue(labelColor) | async" [isForeground]="true">{{
      item.key
    }}</span>
  </div>

  <!-- Spacer -->
  <div class="x-spacer"></div>

  <!-- Color Value -->
  <button
    *ngIf="item.value"
    [matTooltip]="item.value"
    class="x-color-value-button"
    (click)="handleCopyToClipboard(item.value)"
  >
    <div class="x-color-value" [style.backgroundColor]="item.value"></div>
  </button>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color\x-color.component.scss

:host {
  --size: 32px;
}

.x-color {
  direction: ltr !important;
  width: 100%;
  display: flex;
}

.x-color-title {
  flex: 1 1 auto;

  margin: {
    top: auto;
    bottom: auto;
  }

  max-width: 230px;
}

.x-spacer {
  flex: 1 1 auto;
}

.x-color-value-button {
  background-color: transparent;
}

.x-color-value {
  opacity: 0.7;
  width: var(--size);
  border-radius: 50%;
  height: var(--size);
  border-style: var(--border-style);
  border-color: var(--border-color);
  border-width: var(--border-width);
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color\x-color.component.ts

import {
  nameof,
  parseXColor,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XColorAction,
  XColorCssVars,
  XColorActionModel,
  XColorContainerComponent,
} from './x-color.typings';
import { Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'x-color',
  templateUrl: './x-color.component.html',
  styleUrls: ['./x-color.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XColorComponent extends XColorContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.applyDefaults();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Size Changed ...
    const isSizeChanged = changeKeys.includes(nameof<XColorComponent>('size'));
    if (isSizeChanged) {
      await this.applySize();
    }
    //#endregion

    //
    //#region borderWidth Changed ...
    const isBorderWidthChanged = changeKeys.includes(
      nameof<XColorComponent>('borderWidth')
    );
    if (isBorderWidthChanged) {
      await this.applyBorderWith();
    }
    //#endregion

    //
    //#region borderStyle Changed ...
    const isBorderStyleChanged = changeKeys.includes(
      nameof<XColorComponent>('borderStyle')
    );
    if (isBorderStyleChanged) {
      await this.applyBorderStyle();
    }
    //#endregion

    //
    //#region borderColor Changed ...
    const isBorderColorChanged = changeKeys.includes(
      nameof<XColorComponent>('borderColor')
    );
    if (isBorderColorChanged) {
      await this.applyBorderColor();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XColorComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleCopyToClipboard(value: string) {
    this.managerService.saveToClipboard(value);
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XColorActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XColorAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region Appliers ...
  private async applyDefaults() {
    //
    await this.applySize();
    await this.applyBorderWith();
    await this.applyBorderColor();
    await this.applyBorderStyle();
  }

  private async applySize() {
    //
    let size = await this.getValueAsync(this.size);
    size = !size || size === 0 ? 32 : size;

    //
    this.setCssVar(XColorCssVars.Size, `${size}px`);
    this.detectChanges();
  }

  private async applyBorderWith() {
    //
    let borderWidth = await this.getValueAsync(this.borderWidth);

    //
    this.setCssVar(XColorCssVars.BorderWidth, `${borderWidth}px`);
    this.detectChanges();
  }

  private async applyBorderStyle() {
    //
    let borderStyle = await this.getValueAsync(this.borderStyle);

    //
    this.setCssVar(XColorCssVars.BorderStyle, `${borderStyle}`);
    this.detectChanges();
  }

  private async applyBorderColor() {
    //
    const color = await this.getValueAsync(this.borderColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XColorCssVars.BorderColor, `var(${bgColor})`);
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color\x-color.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XColorComponent } from './x-color.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XColorComponent],
  imports: [CommonModule, XBaseComponentModule],
  exports: [CommonModule, XColorComponent, XBaseComponentModule],
})
export class XColorModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color\x-color.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XBorderStyle,
  XStandardType,
  XColorKeyValue,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
  XBorderStyleIdentifier,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XColorAction {
  Refresh = 'refresh',
  SetSelectedColor = 'set_selected_color',
}

export type XColorActionIdentifier = XColorAction | string;

export interface XColorActionModel
  extends XBaseActionModel<XColorActionIdentifier> {}

export enum XColorCssVars {
  Size = 'size',
  BorderWidth = 'border-width',
  BorderColor = 'border-color',
  BorderStyle = 'border-style',
}

//
//#region Container ...
export interface IXColorContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XColorActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  showLabel: XStandardType<boolean>;
  labelColor: XStandardType<XColorIdentifier>;

  //
  size: XStandardType<number>;
  value: XStandardType<XColorKeyValue>;

  //
  borderWidth: XStandardType<number>;
  borderStyle: XStandardType<XBorderStyleIdentifier>;
  borderColor: XStandardType<XColorIdentifier>;
}

@Injectable({
  providedIn: 'root'
})
export class XColorContainer
  extends XBaseContainer
  implements Readonly<IXColorContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XColorActionModel> =
    new Subject<XColorActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly showLabel: XStandardType<boolean> = true;
  readonly labelColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  //
  readonly size: XStandardType<number> = 32;

  //
  readonly value: XStandardType<XColorKeyValue>;

  //
  readonly borderWidth: XStandardType<number> = 1;
  readonly borderStyle: XStandardType<XBorderStyleIdentifier> =
    XBorderStyle.Solid;
  readonly borderColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightShade;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XColorContainerComponent
  extends XBaseContainerComponent
  implements IXColorContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XColorActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  showLabel: XStandardType<boolean> = this.propertyProvider.showLabel;

  @Input()
  labelColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.labelColor;

  //
  @Input()
  size: XStandardType<number> = this.propertyProvider.size;

  @Input()
  value: XStandardType<XColorKeyValue> = this.propertyProvider.value;

  //
  @Input()
  borderWidth: XStandardType<number> = this.propertyProvider.borderWidth;

  @Input()
  borderStyle: XStandardType<XBorderStyleIdentifier> =
    this.propertyProvider.borderStyle;

  @Input()
  borderColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.borderColor;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XColorContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXColorContainer
  ): Observable<IXColorContainer> {
    return forkJoin({
      //
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),

      //
      showLabel: this.getValue(
        container ? container.showLabel : this.showLabel
      ),
      labelColor: this.getValue(
        container ? container.labelColor : this.labelColor
      ),

      //
      size: this.getValue(container ? container.size : this.size),

      //
      value: this.getValue(container ? container.value : this.value),

      //
      borderWidth: this.getValue(
        container ? container.borderWidth : this.borderWidth
      ),
      borderStyle: this.getValue(
        container ? container.borderStyle : this.borderStyle
      ),
      borderColor: this.getValue(
        container ? container.borderColor : this.borderColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <!-- Color Palette/Slider Box ... -->
  <div
    #colorPicker
    class="x-color-picker x-wrapper"
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [isForeground]="getValue(foregroundColor) | async"
  >
    <!-- Force LTR Direction -->
    <div class="x-force-ltr">
      <!-- Color Pallete -->
      <div
        xSlider
        [rgX]="1"
        [rgY]="1"
        #paletteSlider
        class="x-color-palette"
        [style.background-color]="hueSliderColor"
        *ngIf="getValue(showPaletteChannel) | async"
        (newValue)="setSaturationAndBrightness($event)"
      >
        <!-- Cursor -->
        <div
          *ngIf="slider"
          class="x-cursor"
          [style.top.px]="slider.v"
          [style.left.px]="slider.s"
        ></div>
      </div>

      <!-- Color Sliders -->
      <div class="x-info-box">
        <!-- Start -->
        <div class="x-start" *ngIf="getValue(showSelectedColorPreview) | async">
          <!-- Background -->
          <div class="x-selected-color-background"></div>

          <!-- Selected Color -->
          <div
            class="x-selected-color"
            [style.background-color]="selectedColor"
          ></div>
        </div>

        <!-- Margin -->
        <div class="x-margin"></div>

        <!-- End -->
        <div class="x-end">
          <!-- Hue Slider -->
          <div
            xSlider
            [rgX]="1"
            #hueSlider
            class="x-color-hue"
            (newValue)="setHue($event)"
            *ngIf="getValue(showHueChannel) | async"
          >
            <!-- Cursor -->
            <div
              *ngIf="slider"
              class="x-cursor"
              [style.left.px]="slider.h"
            ></div>
          </div>

          <!-- Alpha Slider -->
          <div
            xSlider
            [rgX]="1"
            #alphaSlider
            class="x-color-alpha"
            (newValue)="setAlpha($event)"
            *ngIf="getValue(showAlphaChannel) | async"
            [style.background-color]="alphaSliderColor"
          >
            <!-- Cursor -->
            <div
              *ngIf="slider"
              class="x-cursor"
              [style.left.px]="slider.a"
            ></div>
          </div>
        </div>
      </div>

      <!-- Color Value -->
      <div
        class="x-color-value"
        *ngIf="
          atLeastHasOneValue(true, showColorValue, showCopyToClipboard) | async
        "
      >
        <ng-container
          *ngIf="getArrayValue(representColorFormats) | async as outputFormats;"
        >
          <!-- Hex Value -->
          <div class="x-hex" *ngIf="outputFormats.includes(ColorFormats.XHEX)">
            <!-- Color Text -->
            <span
              class="x-color-text"
              *ngIf="getValue(showColorValue) | async"
              >{{ hexValue }}</span
            >

            <!-- Clipboard Icon ... -->
            <div class="x-icon-wrapper">
              <ng-container
                *ngTemplateOutlet="
                  iconActionsRef;
                  context: { $implicit: hexValue }
                "
              ></ng-container>
            </div>
          </div>

          <!-- RGBA Value -->
          <div
            class="x-rgba"
            *ngIf="outputFormats.includes(ColorFormats.XRGBA)"
          >
            <!-- Color Text -->
            <span
              class="x-color-text"
              *ngIf="getValue(showColorValue) | async"
              >{{ rgbaValue }}</span
            >

            <!-- Clipboard Icon ... -->
            <div class="x-icon-wrapper">
              <ng-container
                *ngTemplateOutlet="
                  iconActionsRef;
                  context: { $implicit: rgbaValue }
                "
              ></ng-container>
            </div>
          </div>

          <!-- HSLA Value -->
          <div
            class="x-hsla"
            *ngIf="outputFormats.includes(ColorFormats.XHSLA)"
          >
            <!-- Color Text -->
            <span
              class="x-color-text"
              *ngIf="getValue(showColorValue) | async"
              >{{ hslaValue }}</span
            >

            <!-- Clipboard Icon ... -->
            <div class="x-icon-wrapper">
              <ng-container
                *ngTemplateOutlet="
                  iconActionsRef;
                  context: { $implicit: hslaValue }
                "
              ></ng-container>
            </div>
          </div>
        </ng-container>
      </div>
    </div>

    <!-- Color Input -->
    <div class="x-color-input-box" *ngIf="getValue(showColorInput) | async">
      <div fxLayout="row" fxLayoutAlign="none">
        <!-- Input Icon -->
        <div>
          <x-icon
            name="input"
            [clickable]="true"
            (clicked)="handleInputColor()"
            [color]="getValue(colorInputIconColor) | async"
            [matTooltip]="getResourceValue(colorInputIconLabel) | async"
          ></x-icon>
        </div>

        <!-- Input Box -->
        <div fxFlex>
          <input
            class="x-color-input"
            [isForeground]="true"
            [(ngModel)]="inputColor"
            (xOnEnter)="handleInputColor($event)"
            [xColor]="getValue(colorTextColor) | async"
            [placeholder]="
              getResourceValue(ResourceIDs.input_color_description) | async
            "
          />
        </div>
      </div>
    </div>

    <!-- Preset Colors -->
    <div
      class="x-preset-colors-wrapper"
      *ngIf="
        (getValue(showPresetColors) | async) &&
        (hasChildValue(presetColors) | async)
      "
    >
      <hr class="x-hr" />

      <!-- Lable -->
      <span
        class="x-preset-label"
        *ngIf="notEmptyValue(presetColorsLable) | async"
        >{{ getResourceValue(presetColorsLable) | async }}</span
      >

      <!-- Preset Colors Boxes -->
      <div
        class="x-preset-colors-box"
        *ngIf="getArrayValue(presetColors) | async as pColors;"
      >
        <div
          class="x-preset-color"
          (click)="setHexColor(color)"
          *ngFor="let color of pColors"
          [style.backgroundColor]="color"
        ></div>
      </div>
    </div>
  </div>
</ng-template>

<!-- Icon Action Template -->
<ng-template #iconActionsRef let-value>
  <!-- Copy To Clipboard -->
  <x-icon
    name="clipboard"
    [clickable]="true"
    *ngIf="getValue(showCopyToClipboard) | async"
    [color]="getValue(copyToClipboardIconColor) | async"
    (clicked)="handleSaveSelectedColorInClipboard(value)"
    [matTooltip]="getResourceValue(copyToClipboardIconLabel) | async"
  ></x-icon>

  <!-- Clear Color -->
  <x-icon
    name="clear"
    [clickable]="true"
    (clicked)="handleClearSelectedColor()"
    *ngIf="notEmptyValue(selectedColor) | async"
    [color]="getValue(clearColorIconColor) | async"
    [matTooltip]="getResourceValue(clearColorIconLabel) | async"
  ></x-icon>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker.component.scss

:host {
  display: block;
  width: inherit;
  --cursor-color: val(--x-color-dark);
}

:host ::ng-deep .x-on-top {
  z-index: 10000;
}

@mixin boder-radius($radius) {
  -moz-border-radius: $radius;
  -webkit-border-radius: $radius;
  border-radius: $radius;
  -khtml-border-radius: $radius;
}

.x-force-ltr {
  direction: ltr !important;
}

.x-color-picker {
  //
  margin: 0;
  font-size: 11px;

  //
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;

  //
  height: auto;
  width: inherit;
  cursor: default;
  min-width: 200px;

  //
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.x-color-palette {
  //
  width: 100%;
  border: none;
  cursor: pointer;
  min-height: 130px;
  margin-bottom: 8px;
  background-size: 100% 100%;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAACCCAYAAABSD7T3AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AIWDwksPWR6lgAAIABJREFUeNrtnVuT47gRrAHN+P//Or/61Y5wONZ7mZ1u3XAeLMjJZGZVgdKsfc5xR3S0RIIUW+CHzCpc2McYo7XGv3ex7UiZd57rjyzzv+v+33X/R/+3r/f7vR386Y+TvKNcf/wdhTLPcv9qU2wZd74uth0t1821jkIZLPcsI/6nWa4XvutquU0Z85mnx80S/ZzgpnLnOtHNt7/ofx1TKXcSNzN/7qbMQ3ju7rNQmMYYd/4s2j9aa+P+gGaMcZrb1M/tdrvf7/d2v99P9/t93O/3cbvdxu12G9frdVwul3E+n8c///nP+2+//Xb66aefxl//+tfx5z//2YK5Al2rgvf4UsbpdGrB52bAvArXpuzjmiqAVSGz5eDmGYXzhbAZmCrnmzddpUU+8Y1dAOYeXCtDUwVwV7YCGH6uAmyMcZ9l5vkUaBPGMUZ7/J5w/792/fvv9Xq93263dr/fTxPECeME8nK5jM/Pz/HTTz/dv337dvrll1/GP/7xj/G3v/1t/OUvfwkVswongjdOp9PzH3U3D3zmWGnZVXn4jCqs7wC2BKP4/8tAzkZsoWx6XrqeHZymvp4ABCBJhTQwKfDT8gzrZCIqi5AhiACjBfEB2rP8/X63MM7f6/V6v9/v7Xa7bYC83W7jcrlsVHIq5ffv30+//fbb+OWXX8ZPP/00/v73v4+ff/75JSvbeu+bL2WMMaFbAlpBNM85QX+ct6qoSqkPAwuQlBVKqGNFSUOAA3Bmu7gC5hNOd15nSwvAOUW7C4giUCV8Sgn5L9hNFIqTsp0GxI0ysioyjAjkY/tGJVEpz+fz+OWXX+7fv38//f777+Pbt2/j119/HT///PP49ddfx8fHRwrmTjV779EXu2px2xhjwtdJZQcAWQIPLPISsMJaSwiD8gzIKrwSyATE5j5nAbR5c1dBUwBlsEWW0h6LqiYsqFPAQxCyRZ3wOSARxmlXMX5k64pQfvv27f75+dk+Pj5OHx8f4/v37+Pbt2/jt99+G9++fRsfHx/jcrmUFLO31gYDWblxRIs/TqfT7ousxJsAxXA2Gc7TA9XdgfdoHbFsj76X2+1WArgI1ageGwA3qupqoHsmcbI6Fu93quggFa9d7LeDtgKfAFHBJ+NEByIkcJ5KervdTmhhGcgJJSZ5vn//fj+fz+18Pp8+Pz/H5+fnmGD+/vvv4/v37+Pj42N8fn6O2+1Ws7JjjP6wraMI5E4RZ8x2vV5TSwkquotV7/d7Tz6HFWsD/qNcdw0CQ3q/321c686TwDVIdbuy73zNldhSHb8I2klZznm+InBS4U6n0302aBFsLhHDAKJVJVglfI9jhvu53W53sLANYNxAiDA6MCeUHx8f9+v12i6XS7tcLqcZW57P5yeY8/fz83Ocz+fnsSmYUyknWEG85WBst9stzSLyMdfr9Qi08iY15UZ0LlDGLhR3o5zK2j7OPUTD0E+nU3tk7Xb/16NFbhloAMuY1zjLUOO3BKeIDe+Z8s3/J4gFo4TM5jPmuRg28foUKKVSwo16TgA5npywcWLHgYl/Pz8/73/605/ab7/91m63W7tcLie0sZj4mao5gTyfz88E0f1+j8EcYzwTPEG2cqjyfHNF0M8fuqEiaOVnRzZZQNh5fwQyHg/HDGfJo89Q1zb/quu5XC6773I2XKfTqd/v9+d3wuqWva/YTdUdEV3fhIv/Viyps6YE3x3r43K5bJQS66zaxVGFsvd+//j4aF+/fm3fv39vt9utff36tf3+++/tdrudvn37ZuNLBaaCMgUzC+rZRiFowxUuJI8YMqcCp9Opq5vagaYU6lGJA1XQqejchw6Cj0Gw5nYBrGw01A2O206n04BGouNNyTfp/FwElhUey6nXrIKw7QQWddxuN2ldL5fL839gSPF8ahu/JvBO48CPSuqMf8Vp9/P53L58+dLu93s7n8/tfr8/39/v9/b5+TkhPJ3P56mQ436/j+/fv+/iSgbzer0+AZx/5+88bv6OMda6S5z6kd21fYC9dxv7cIJJ2d9AOS30fPMzyHiTM8B4DF6XUlYHp4KQW3W+1t77MNB1vGHxWq7Xa7vf78+y5/N5A+H1et29xuP5dbYtyaRu4AksbPq6936fjRzXRxBbPr/b+b18+fKljTHaBBBfn8/n0/1+H1++fBnn8zm0sB8fH5u4cr5GuBhMVk0EEn9RsctgVhM+ixlJtMA23R8B6yysAstBOgFXIKKCMIgToMqNEu2fYMH7ztc732dQKkCj1ytAZtY0Kx8pIr8GGJ+AT3V+2Hirhl++fBmXy2Wz73w+b17P8p+fn8/tUwGVleVkTyUb68DkfayWY4zxNRihU4EpLJPZVrK+u7J4/mgfKqeLW9X2REWlItL1diynbDDb3+jXgYjQqn0rrxWc+NkILP7F7xIbMvx7vV53x40xnlbWJF12ZSag/N0pW6t+ZzmOMzHjajKwDfond78zYTdfq18up97zr2q8v3IioBprRtBl0EZ9og5WBRGOdOHjIjXF7UotFbgOWnXzIJyzYvjG5IYgsmMOxHkz8OsMSrVNWeq5T8DaOcbEv1Od5rbs9aO7YvMet63EkF++fMExq+MRl4/L5bLZN/+ez+fnZ6KazuMqXSQVO5spJXflHAIzes/xJseckRJiDMog9d6VfRrqXMr6KpVV27jRwJacGovOAM1zMdQMnwK1AubK63kdCChvI1C7g0z9nf/D+Xze2Vj8H7Gx4P9duQlsYCrqyN8XqG3Hm/10Oj3jw/n+crlstuM+jPmmxT2dTuPz83Pzt2pn1XsEHX/bnPaVqVmh0xwOt0o6XLLAHePUU203wHfcrspCwmV3TryB5s0Mseeg97x/BwzCjBlbB+pRAPla0BVQuT6V6QHdBlj3d0KG147b+DqxQeUymDO43W4dQar+TIjwmAd0z8/h65vf0/yLv3Pb5XLpru/ydDo9s7ET0I+Pj6dKK9VUEIeKWQWPAOrJ8LKd4vE+t91Y3e7UFlWatg2VwJnb+HPmtvm/sfK59/OaWF3x/eP1UPHvA5DDYDpYXfb0drv1V2DkBkxtw/tEWVVlXWdC9pFYs5/jfh9dS/16vW7s6lTG+TfqsxSJHxkXXq/Xdr1eu4LsfD6P3vsT3N77DkL+zPm5jSdKL4zR3AxQd6rHkLkYlSowsrq7znzu6wSwdsMJOXmA5fBcjxtgMGBYHlr5zokhtsMCTgXLQOW4XC6dEyEMprL8mAQzXRgduix2yZzorxkYsDn3hB1VeMLGsXsVtgl2pW8S3svk0vw7R4hNaHvv4cACl5HFzwIH0Kc6zu4XjDPR/jpAVxWzO1Xk2DDb3vTcxeGU1iWZHkmIDWziWKvirCJ4Dravs6IJ/GG6cTqWdXDy+fArQDVVkLqkVjAoZIITdmmIqXwqa95N3+MGYoZQdRVNO53Y1xRkhO16vY7eu507Ca9lJnbGpxOemQhSw/AQsmmp5zU9BiU8G6wvX76M6/U6Pj4+do0Bz4CpgiknTUeDqwlKBmg3u4OVjrZ1A+rAcgaejWq6eJCvCYFDONSwOgHX4EQRw8lxbzDOdEK6gZ3Hk1b+8g2o1JFtKXyv/fEdTXuWjWXdAZiBp6ADeDrCFiim7B6ZFneeI7Gvm/PMkUDX67W7xI8b0D7/v8dA9qfN5oaCf74WZjH0mf1cmfY1Y0JUFmVrTWu8uzkNcLtEj7u5FXBTkfC6GOA5q8YMxO8KVvF6sAVGdcrUbsKODcQKkLMOMdmlxum642YrPm26AlhZW1YB1R+rrGswE8TaYAWeUMxdf+WjwSvZ2Ef3ytOyfn5+PpVPAaqOn43MtNBqvmjjxbjM4lZjZY4gqNMI5ktaW/sYKNwS+9lFQzGihmMCKPa7+Z0V6Eb0GRmobtpX8JljWu5FMLN5ja6hG9kwQgZqf5+1NH5UxzkFReCdWhJ8XdlGUkxO7HRlYRm4mVO43W7ter12TPJEw/rmEN3L5SKHIWZg9mz+pUoKOYq5bJTJdX2gme1UcxMZQFaEQIlHct32M+Y1BzGkGuzfiyAN9z+ugplZ1symCrDCYYkGxDTpI9RzBy0rHyeDUC1nWaeUaD9n4xkNyYMBDZtzZ3B++fJlY21XFDOcARJlabOyiS3uCpLI9jrZjCDkaVvcCCjwognKShWdzXZWlZMvVTgD8LpqlCLrqgbcB+qYwrgKYpT0ccCqbKyCValkEabn/FynogCrPKfqf51xJ7sGB2ZXcZmxoSOztjx300DZi7a0/2AIR0UlBag9SuDw6KcAzlaB7vHZvWpjK90dyrq6bKyDUZQbR0B05biLQkHIcSUmgIK+SwuqgHCnoio2RQU1yj+BnBy9pphVKLGyC7ZzFK1pxWK+E8IhVCWLN/uLtnUU4ayoYLoaANz8FdtaSvY4pV0BEW2ls61czqllBKpTyKgMAhrZ1cdc1RROtPmvWNkdcKZ7ZKxaWjiPLJMpp7OZKxA+rqG/oJLjxf0pnJlqLoDZo3gyU0mKGys2taKecj/d1C+rJSplBqlTyAqgR+D8KjKlmRL2gtUcAdCtsL+ijCNT1oqqqkH2OHEbG5sDFnUg5Aa+yLou2VU1ptj1S2ZQqv1ORZN9IWzRfgaRBxKoBE8UWyqlJFtrIc0AxNjSjed99CTY/XDfSzCz5M0IZoVEsWnPFNTsl8ooVC1TzbGgqFZNDSgVwKK+1sGDMKqxZCWGVMDysiEr1jVSQJUYwj5iHOlThdHt44SQg9CN+nl8D90NMIgAdgr46JqRiR9I8vRdFvbr17m/yxUMKjNLMiVUADwu2CWGhhi+F55TWM9M9cogzms1dnM4uOF/LAEYWdcqnM7yFmyq3IfwmOROd7Y1iFWtOjoY8To41mTV5IysgFFuRzsbWFGbNIIJCDv1dOo4lZG7jWBwRFtVTKuWyeCByJKOan8oZ3ep9XddNl0tDuaywLz9cXPYeDAA0SpkBO9sbVcTOVWldPv4uyzEkzxHtjvonHoSkFEWNoo1d8DhcQputd2ppNon4BzoAiJ1hBFQg0dVtdbGHHDQWushmNEQukLM2QO1G2Y8bgTXqFhcBJj7EjPgcPts8US8qPpPB/dXznOh5Z438tzH5ec6QgrOKrRRfKmysBmUDB+PhYabMlVPER+GCSITTzr7am2tArH3bgcEzPJm+cr5jJ4NnHNFDVrFXcI5Le9k5Jnw+bedbV+FfRzZIHaOOaOsLY0/7UGs58DjrGwKMIMFIGzOEW1/jGsdAtCN6hEAI4hBe9YXeRROBSVPAVPAqvIM5bx5hVKWAMP6zBRy3iescridVdFBinBxXDnG2GRY2XbCvp1lhvGtO9Bxu5h908XQu42lnSArMFdizMim8uwRCxPGnnOS8lwpnbOiDqTAjsrRN/PcoAScCbaACqVM40ylnjjTBs+bwWlAG23/UKbdkiwKWIQPGzWaczpoSlxPEj822cNWkpS7FyzsDrqpfgpG3jahw2vgbaSQAxuLWZYt7JzyNe8JoZpNAcvDFOdw0wqYT9AK1rZz/DdbSlLPp0ryIxgQJlK9AZlEq7IOXpohg9PIhrCng88JsOxiV4ZWAYfg4sikx/8ky2Z9l862uqwrfscIH8+ugTmVGyiddeVYUgEMn4GZzg14EwIsh9sx2cKKiWXReuOE5gzGOQgdlRKVVdlevqb279Xq0Qnsts2VDaBO0coezsruWtHApu6sKG4IBhN0aGU2kLrMKGRTN3HmbCDwKV14zvkMEDG4QfZVspVlaNU2mhc5TEZ3N1h/zqTheuLpW05ZWTGVjb3dbnNmxKZBnN8JqidaVLKAOyARNLS+MB54Z2+VaqoMLKroVBlngefnTPAcoHNWCSvlfA8CI0HEmBNBnBlXyMrzU7A7WVm94PPqQ2gmqKx+WDGsnvilmcSOBJqOK1nYyAIzuAyesq3UdSK3KfWcYKD95HmfYOU3qser2CtYEUA+FpfqdNvgPBZUBhDrGONRVlQsh8rLcaUCykHG0OOUwTlLBrsh5soEMGezi1E4HRVt1icp5wZEFXdibCkG8Y8vX75sbO4E0iom9z+hjSiOfy3DhpXItpVhE+UGQdvoWjtChmrGHf4YAzKgBNnGtuJxFCeGdhUAfQLLK8kBYAP6gvFJZajMG3Xkycy8KuC0q4Eyymwtwdxdv2M0mIBtK0LKnf640j00Auq4gUkdWGlhs22qJc6dZCsL19oxnlTJG4SYVRIGpD8TPFBuM6OElbS1pldid4mGAyN6ZIupbC5bXJN9fdpbThSxLUaI8IG1XIYBxW3Tjs6KQosKcxfxcQmdnwRGM10GnFcCy2XYunLMyAkdgk4mePiczsLygthcBut6goOqS7YVFXADLjaosB6s6ofcZWAZSIRYqSUkizYwttYab3vUOQ9w2HRxIIg8WwRVeE68xi4UtL3zRphxplzwuZrcqYCq1I3jPI5dnJIygEohMbPqVJSzrwzxBJTs5zN+ReUSgxikPQVF3JVBeNQxbHENrEMNvEdFZVV9lH9+ORGEsNZQpyTNc4C3AG7XF4ngzq+DrO2zbuaaOXgdaFcdkEotoSFBVX2qJ0C8OWZeG4KGlpghA0XfTOPCqV2qqwQ26QWfF2PMLhI2w1lVAa2aPsYd0za25MQRwgcZN6uQDCi+ZxiD4XEM2kZxOT41FnZnaRlcpZouzlRqqdbQVWopQoSB58RV50lBNrHi/AwXS5LrwDVlpY3Fc3ByiYGc52Trist6kOXdwInAQtJpp5QchyaquYOV7Su+fxVMaV3dc0RE2S6mUY0gLt2pMcYqrKIQ9w2l1gpQUMtQYcmmbt5DTNxdhnUCjQqtbK9SUSzvrC0mmhhE1e2FS2+oxypy/ZASutkmtjx3vcBC24PX65nbqkBCRhfjS9kIYPnee8cMagVOhI/3T1fAmdtAWZsCswTJCkQVNa0qWKSKPOpHAUhD9DrbVcyoYkwqhvh17vYAayXLQyKGYdxlUDFp494rBXRjYgO17DDYetNIUj/ezp6S0lnlpEwsWmJMkOwsKXeZKEAjIHn0EQJISaRBcO6UMINz7p/bEjjnw4ft+xmDvksxX4G2rIris7qaeKwAFMP2Oi7n4criuZwtpSUwpfLxSnORSrIqusc5ZFaXysqRWjiZ2DyAWEIL35tVSoQElFACjOeGGSE7AHEQgdo/LSvCOgGBvkxsmDbvlS3Fp5vhaB2TAGqRKrKKMrhLVpaGzEVjZ0OQxDhaCTA+QyRR1d15aQzrJntL3RibsipjG6jlgL4yqbS0sNYg1e84vhbBVrElK64CUcWYXDfKxhpIuxiVJZUxsbMy/uRBKTNRQ4kQ3LdRYLS0rJjRPlTPqY6gdJsEDc+aQXAn+HgsNUCbRuF0Oj0zwnA7bWDkbhO5Ens00qeQhS1laBMl5M/cAaxsLF8rKyql+Tf7ELLEGu/ixiimdCvo0TjfpjKwaggen4eh5v7LokLKbLuyvHhcZG8dhGrEDx7Hg93ZppJF7qBqO3iVveXEDQNInzeoe8Yq6ePaZBZ2JviM3W2UAGotekRCAGq4EkF1X3DOnR11yRsBL1tRa0PVcZiNFXZ2c34FskvomInQQ6lzpJoZbJxk43NwKJFBquJSsrByHydxKOnTxQASBmS3j+JMnsHSla3Ec6K9VWoJVn9zfjwOM7hqYAAqJQwE2a3nA48J2QGegRkpZNivSY+ys3EkKd4oJIwsvIHl3cWgLt5k4NH6OmtLWdpurOkwEMupYc7eMtDRhOcI2ui5JhVIzXzLyto/GAPuZoyo8wkoduVgJglCt7OhGbgID4Mq4si+63zUS1FuFFXFlqyaj2emHlLMcBqYu0FMuR28BbB7lOxRMSiCQXFhCKuwkhZ+pYDiGSgbsKKV8MiSRsuHSIWM9rklRiIlZZuqXjsQK8ooYJMgq3JKWVkhHbhsVxFUzthOWPkYijcbx54IKsSdT+uLr3crGKyoYgFiGR9iBk4kfloUX+JIlQRQqabmpgnhqtpQpb6RVQ1WH5DnrS4hEoGZqaerQ2dhFbz8XePxShmDbo70eISjoorO2vK8SJXI4SUmEU4zWKDzUDtWTYw7xXlbSTEj4FRg7zKnKoGRALv0Gs9Tgc1BpCywGZRQAtqVz2xrBcAMzEpfZwFSa2G5W0QBFjSMapWAEFa3HcGN7CxDzECyIkJ97qwrqWNTWVo876PPsjPkj2wvgroM5lLZKMETKVql/CvnWVFiFa/SzJUQwkoZsr67Y6vlSRV3/2tmNTOY3vnaxYwMuoPKqdzR1w7IqHymlPxaAThfU7Ko2ZXYj4AYJHL+kNdKwRQYESTRa5fsUZ/rVC1TMTyWVyYoqNtuzaHsMyv2tvoarxdfqwYgU1axFo/cnql1FGsqK+uAROV8BX4GU8WcZTATi2q7Qcyi0O0V+GhWBMNRUkn8H1SsWVE5By3Gi0ECqUeJoBfAtDa4amkdXG37AGP5Ggeb84p7UazpoKRzdFzeQ8HkoHGxprKy/Hpm5t12p47J6xTYDEz7uINEXSuxYXvFskYAc+ySxH9sf5ftKzU6IbwVBcUGg5e5FMCEXSErZR0wGayV19woM9guPjTqJdVTqR4uE4nJnLldWVkECCZLd2VLF+xtamex7IpiriSDUpvrpn9lrwGMCHyppMH+ps6LILsuFGUj1XEOXiqbqSHPUKnClpWV68kqtURVNDY4TNaocykoYeTU5ngGEQa/S1DnnE4AeXMcKjHPAmFVjCBENaeyLVNHfr3px8xUstJ94hIpfH4HKE/eDaArK6lSyVVFbdt1gxTIVk3pppVlFXi4pEhVBTObquohU85MLXn1iahvUkHJjSCMc01tLFveVVBx0DodM6jftCu7DOtIzYxrc0qp1JGP2ayYFz2Gb6HvMrO8cnGtV6Gjm3uImSfD2GpWK6uowbZGMxFKQCo1pOMtcMXFpRst+hXGoAomF3sSTBGgTglbBKWwsQ3tZqaYSp0Z1CimRDWFcCJUPYJ00BI5FkKYNoifuQxmN88SWVXWLMaUqqqgC0BmQJR6sk3u9NCf6jYLXxAfqsYEgVLAhRY2AtgtflZNFmFyhxdrLkAdWlk4D88M2ixHyepIdhMHrG/iR1ZGtq0MGpbDbRPYOXeSY1M6Ny4ZstvGSktK+XbFPATj2D371saPEsAMXhXrsZ0km/XStkhhMyBfsa6uXFZe2VCe+YMr1+GKgwrQyNYq1VRrB+EizAow6NsdNKcyVEkYeM73ys6q4kAHp6BiFklTkIrVC5oYV7uzwOGCz4UJ0Stq2lWMJy4wtb+RetL6tZFicnJmBw5UjCvXXMZVJX2MQkbf+XN5EWd78Vz8/JEsMZTBiKNzsm1inLRUQ74H4NidaqI68j5sAFgxcRveC7ieLJXfQYxjZZ2CsiWFewZXJmBIlZ1tdtrX4hSuateKso/RZOtOKW2nmq1oTzeK6dRWAWu2NRVb4hq0SXm1GvtugHrbr5IXqmSktg5CuDE2MSlPwsY5kNE2Wp3AqiZbWVLAxiBF+2iBZbuNj6MB6rsMLC7FyasaYDyo7KkoPyEtw3pEMXfPvxAJi2jAQQgjrz0rLIZSWZlIoNhwd5xK4AR9mYNjWAaLrnuImJeBVN9zBORObVvbr+mTTfFSEJLSRnHo7hEJoIi8MFqjxmvgmF5URZz4zLFgZZ8Ctu2X7ggVccKm9gVxIsOHqxXgNMKnFWZYnf1dBnOhayXq17QwFlWW09eNKyVJFmXqaONGA5aCegMbJ3UUkGY1ic3nKWgjq8qfVYGQG1gRt6rs62a6HiqqUOqdesK5NmX4nGofJoiE1d0dF9lVVkvT1/kEEaaCoYOwFpcVcoLM+7669PxC9rWqktH0sWUYld0VCpuBZ/stVRcGgy9WX2+U1Qthi9SzAqSxzZsy+OiFzBYnySGV6Gku44rD8BCOZBV3BvD5+AKRHNwMEsB6EzHnJpkTAeiUlEGkcECeB6GDZTp5YEJTlvdrknxYjTllMkfNtXwDjM7uVjK5JXUUn43rrqpK2jytaxHW0M5G8DC8rtHMYs7KSgduVQMGTYFqFvVS6rkD3sDJ46afdYFwoq11AOKCBLhvwoUgc8IGANycR6knZrdJPdsuxnyjfd3FovTlRMdEdtOl5CMV5EHsXQBis7TOwvIDZaGj2Vnpbh7cpK63VwYEMLwqbjzyl699sawFFkF1yqjUU31HfC6sW1ZFVFuXVXVgz9keEaw0ys1lWfm+azQAQSWA+hKYVfsZjPncAcUB9oIayy/UZXRNckDGji77GsWbvBo6tPrWPqOyVkBUq+INeqpzNdYs/u0ifh5qmpqIW+33JVSUcwY70KL4U9lYdU6ljtSls7lmfi9g3YzeQfVkaGFaV3ODCnaD2N8wsEDFklE3RzM3ZghdYkWHsszq70FIecnKkVkt8ezMzRq9bkGuKojRLBVSod3Y1yPqKgYW7JRQTPVyy5xIYLjOgxgT52RKJUY1dOrIiRd4futQx/A5AcSmEjz0vFWrkLzvbWAu9HOWbGgxFk1VNTpnBKk6TgwisI/HcxYXP1uAWO72ULFlBTq+aSu2VTUs6hrxM2CF+hEor1VIA9ZmFUaab1lSSgZsVs4sxzHlVLoJHr9H4DhONTkI1XC0/wiY2NoWAG5RlnHFnq6oLccpQddMuJ/O17JVA5OHLi0BqCztq7Y1++ucCd98qLI8MIHBV/cKjxQTme3hFBS3MyCqnDsuym2o80HjvFFTtrURmNaGJsmVahImjTsUXKtQZTAVs7Mvv8/+fzUrZAXcLJ6M4koe6XP0b6SmWWNDzyUpQ8bl+LtWx4tuqZ36cRYV3yuVxPNwvIiqiQCSmu7srgTzR6nkyhpCarXwFy1vGd5iP2cY06lFr5Njhhg1Y6+NB28ftbK83s8rf7kLJbKwDFPbLg25a0AdZJEiqr5phixKMDlRUtcssq1hriLqGoH+zeNgVm9OemjsETV8JdF0NHnkIFxWY1OB4Yrp7rtWJ7NgAAAPXklEQVQ3oNs5nplyVf8u2FoLu1JrHveaZWQjqAkshtFa2gzsSG3Zpkbvg3HafF9slPPlldjFlK80Gysm8Mr4MPhneNWENPGjAIpmilTPATdTRTXlCBYHYAQuPwA36xIpWtGN4q3Y2MhiGsUpuSSnlEJRD8PorC7CFYVw+F51qThgabxsTxWzCGY0ZSsb3lfqAy0OPNjNy8xiQQKsHYFQ2HBZVvVbBuq3m1oWKajqaonsM6uZUr6CjXWNZ0l5E3h3jURma6kP3MJIiy1Lm+kahQq41N2iZja5sjtlLYNZHZrH6qUGm4vMbDp6Rw2CFmvuyFkrBcCyMtFqBaECmsHoK9BZ2LA/lJcRqSaDqnaWbrZdGaz3DLgIvBln4woGztbyJGqslwxkhhHrTjTYFXCtOoKS8uLdofVdAbOylGU6nlYpXWZts4nXBq6WxJitMNokHUJnbnJplQm+aGpY2a5GMV2QD1hRubBPFKdumf5OHkLHz0F9luE5kjBjRa0nFE5CUGqHw32MmjZ6xkgINVnSnZ1VZStK2qKlRaLlQgK7uTq7JFXJwM+3SOEKyhZNI+tJ0I5qMYy9k2qJD7dVWdqKXa0CKNR0Ccjg+B2IYu2fcBZJZkMFgM11r0X92wilghFGgzVnexlqB7xL9mS29SiYUVY2nXOZjNBRsyDsQPRWW5hrZ4XcdC4HVWRbjgJr4sFofK5SzjQ7rhI1UebdPdEbj6sqIvTZQZ5va08rABsAW0UxeWytAk7A2KJ9ZpxzCioB24XFtYAeXYxr6anSqhLgppEqWbGwLunTgrV+IjWlL29ljaAl4EQMGsErp4apeZiquwRXLXAqOCeru32mmydc6oWTSWpFAGdzeTB8RTHVMEtlM90CbbQCYhPjq3egYr1FGdYIQjiuDGZ5zZ/AzobKGOyLxti6c4Rwtv2anyWlLICnlLhxJRXt6A5ebDBWFNONbxWZ2d02mnu4S9YECpeppV1zSWRBWxHYzVIv1CXSouwqqX3jBBBDZdYQbpTQW4ZQlS8r5kH4suSRmg2++3JN10x1PaAmEkmtYlEdeGpJEM6kOuCqCR22oSujj5IV2HdT0zj5prLKTjXFAPjdQlyq7xIBxAQP5yMczG4VxAKw0n6ilZ2QBce2pLulkuxxqnoIzFfgqyqjil9S1VNwBrFmeyeops8yOjZUybZdfS8CuaTIJumzs5tODaNtLpFDQ/PcJGweLhmeL1nB0KqiUDScsiUVD89Di3HtrKtSULw3RLiygZD+7sF8JTObgYsrGvDNUFRGl1iy0Ll1YkUc2aJYMog920I8qW6YDCg1Mqk0JHJFKXkbgbRreI+qpYNOZHrVcDUba7pjsphSJNtK6upgRNAVoOS0mugBeN4bIZgHhuPZ/s1ENaX6KsVr+YNrh1Nb7ipR0PE5zbNRegCbrHRUw6Yf07dLBJl1f8KB9as2V1nNqAsl62LBBhehwalerkHmB1JFIEZKSEusdl5JQj1nJlHXSCF342gJ9CYGrXelknJIXqVP8sD+qtplCR3XH2qfKq0ygMp+KnVkKxNlZ8m2YkIlVMiCnXUwl7qznBKSvQz3m3Pt6oQbXO5b5FixCh/fHxUQW/AEcK6zCNqKQnL9sywqmKuwvqSYzT/aPVNNpVyhvRW21aqciCsjdWvBwILUvh5VyCzbWoC1pJjJ680CWsl+udKB6T5RwG1mlohnlpbg47iz5U9ha0FGtmRLFYBtO99y97Ap0z+ZDTAog6kSLZsMHg/IFkkgp6CpvU2U0cYVSdnmkjwBdOmXbxTWNWzuIbipMioVxEckZEoahSOiy2M3K0jcC1LhVDwaqG0ZvkcWqCnrG4GIxykrqlbWdw6LQyBaZR8HmLRIhQWsHswD42ZXVLNkf9l+FlW0HVQ2lwFsC/Z1FdzlQR0KaPfo+Fdfu+/dwVRICu1CGR7AEIiAhc+AZUF0kOBaPxmUqg4i64vQnU4nFDYJ9Nz+1fVXveH9qmr+kPILx8oKcRV/BFbxbE0JMT0kSD4w6L/lNY8ocsqagVdU3A3MjxhxcGuqzsPH4irpaow1q6OyrVjvp9Npc59E91LldboYVzJWdimWfAW2SNEKcDaX2FmBLLA/uKxlmhh613Is1URQApbKfttwxL02q6Onx5pQxSbPojAg+v5hAnN6LHVRDXIsvKtRjiS0qJUyZTAXVbAK82ElFJWaQdVoqUC1Unt7BVaTQudM6SuqexjQJN4+0icaxv/utbKv83ETbT8H8gjcOKxOJmbUa6OOVXht3dFY6rHv9XoNzFLceEA1o8+pKm0LAHPHZ2rYKjFq0hfZFixsqHJgD3eD5n+U0kb1mFjXkn2lvMSSOsNE/CdIAKF0Sytq6urOHUN5gwg4GZosgbmggM5ucra2qrS2Ig1cbiBBcxYzgzUDNLCvL8GbZXNp6ORy3LmS+Kk83zRIAK6A1ioKa2I9NapIuiUFdfC9766PFZUtqUr6KbWk+zZU1a/ZrIXEztrjTOfz7hwKziCeXIaraHtbZIMz+2pGgazCmw4qWAFvEdhodYp0Xq0pV7G1YWYWbO4qhGq42+Z8BYtrLWvluNPpZAeaFFS1vubPgbgxsqcpnAaszBovKaFoDQ8BGtjfUOl4NAG2nmQV04feJgumvX2fsrQEWZghL0JnVdYkn3DOZIeRN86RqPWCmsvGVqEMRnwxQAxwS8EMYo3IzmY2+BCcLp4MKiuyuhImamlbZFcNoNl7tp+RHd18ZjQIRKyXdFRhN98/hyKqwXWNo7O1wiaXoHN108REZZWEq6grnIfjzeg8jdRf1XEL4kkXa5bBjKxoKaljBjeHlVxQ4GaycpW4lDOAKtnTxHAtOfzOtZwHAM7sqVXkV6yu6kap1nHkXKqWF/4XHqjenNKqBjpR3l1ch3Ejg1+EsgdQhsdG0B4FM9sWAVWpuAyiwTPleZxt9VyZVS2qXfReWqTAilpr9ApoWTjxymit7NwV4JTriZyOA9B0k7HFfULourmKYHVnRQvqGL5HMHdqFcR2qWpmcK6eTwx2dipWrviDilr+fKWq3OWRWdHKwA4eu8wjchbeRzFilqjjZN3ufCpfkJ0/scVpnYk6L0PI77lxdWCZ87WiWm7B/AGquQSnujGKsB8CJmiJq8q1pKIVWyqOiTK66r18BN8r74/AE71fdC3yPS2MxdOpnE1tlVxD9JmVOoggN+r4PjAXVFPa3Eg5jVJGFVUGNolH20GVrUB7BOySWq6WqYQdWR92pcFMYMwckbSgCKCqD67DiiWu1g8MQC9ByfcFqW1L+jL714qNCuznoSxt0da2gtWN1G8F0BK0NN0nuimelUF9dIdAfjO44UT3CjQLoUeLHJFTO3gmpRuIIOvwBQCbqNeo3qtZ9iF6xVK13GRlo4zqimq+CGdTiR1uRY8oqgE02hZBa79kZXPMquxRHKla2saZWN4mRqZUj0vLCKhkjKnqOQHNuSZVJoKvAqS1wpEquvWDC1B2ypwrCPsRMEPVTODMLJMDv6qeKXwi2JYV5Sq4qKyvgGsHCLiuj2jR59V8gMqSJ2FJZRXEHVRHj3sFPrct6OpqlW1GpatQdt0GvwfM6n63InsGVFhJGaBqgqqIV6IsXllZgySPq4R3bnt3wi5cv+cN2yqQLW1T95KYVsWWtKk4cB9W53WQQflQYR6Wl4HaJZjvVE0D5yvq+RKgZCs5qdBEP5sD94cAvQLlSgNaSMAtHx88BuNQ41zdFsX30zKbcs0MLD/ihkpQzl0wiTqKLTfbKmCmyYICnK0IbaieC4CG9iSyLQ7cIMGQwau6TKoq60Apl3WN40LZpca1CKKK9VQyyIEn8w0F8F6CL2h8o3ixGwC7s7EWzCOqmcApYxYD4jsAzVS0sl2t98pA7vrKophCVSonbYpgH6mvSn24pTBV4sdtV3BtMq5k82y+IADvUJ0uAlkCVTxIaPm+UNu/qkV4F1TzHXCGrXIAqItBKypqK99VtAOVs64O4ObX7pHLVCpYHcRmwvLR7TvYAKBBN58LGVzDuFz+hQbWgncQyCZAk+VbsPSouf93261iZgmfCpwRbAvqmSqriU2PwhjaoOyYqtIegVXViTsmyta6bGySpY3gyRrpIyAeaWDDxtpsXwKyalMDKNP7YBXMqEskUsi2uC8FNAPxAKTVfT1o6VzM0E0jF+1rWcUuHvdyg7vgoFplX8HpvHpMCOMRUPHzZkInsqlFKNX/EIO52E0SxSzOwob2VmRLW5D1XIU0rbgM1AzWgyC7fe8G7xUAK/taEBat7luqtyP7EmsaJQOj5F+mrnZfCuYCfBUAWwShyd6pMY/vAHG1UqOYpbI/gy5T0CMKm+UO3gFuC85dgfDVeguPDfITrIBLsLrcgdh3CFgFZjaKJ4Iv3F8ANEqvuxR1tVKOgLoCa1jxboBAkj6v7j/icFbA7f4rfRnQDLRViG13i0vqBQrYVqBbADZT0ZpiHoSzvQpopKIFS3sE1HfBWlHXd0H7LnArqvougMtljHBgZnh3Eoz/BKjLML4Z2Aq0+hEJr9jaVUBbvNzCIUiroC7AWmmFw4o5AK3MtB5VypZMSFgs05JyGVwlwBqsEGAAa2ZU1CjUexXGsE4rKriilBvFzOKKo3AuAroE6QFQU3u8YpNXwS5k+1TZt5UrwouN4KiUEw+k3ZWDp1RXHNRqXb21Ts39945yZSg3VnZFNQ9CF3XeZyr5DgBXKiwCMa2MxeTDYXgP1Fsf9QNKZc0k81RJk3r6EQ3rCmBVyLL75EjZ1pIVDHoFtiOAHoB0BdTVylqBsKKKS+AeBXJVLY+CXASuGvO/Auq7GuEjDfGKg1oKa1z/dmmi9I9SUGNhl0AtfulHAawoYrnSkmNXAVuGEhrEVXvUF+A5Ct2PqNOjDetyna4CmeUolmeXLN4Aq7C5Sj10Q7yjgl+t6CNxSRHmI5X+CpwreYB3Qfdqna4q21KdBuc4GoZsn49ZOOiVinwHqK9WzjvgeweEh2AU5+vtxZ9Cd9Wqkh49V18E5oj6vVyn0RStAyGIO5edXRKd5B0VGVXq2yr3xYp+5Ut+C4QJ4P1N339pQMjRejj4vb/Dcr6rQc3O/0rjmtZpeYCBiCHfCemRbNhbK/pNUPc3wfKy5f2D7OlL3/uPhve/oU4T0F8f+VNM2vyoiv0jK+KHQfdHq+0bncz4oz73/+Y6LbKw1o/5B7eOf1Rl/0du9B9tn/9bvrf/j+v0h6ttn2tp/r/4819y4/zv5391uvzzfwDifz6phT1MPgAAAABJRU5ErkJggg==');
}

.x-cursor {
  cursor: default;
  width: 16px;
  height: 16px;
  border-width: 2px;
  position: relative;
  border-style: solid;
  @include boder-radius(50%);
  border-color: var(--cursor-color);
}

.x-info-box {
  //
  display: flex;
  margin-bottom: 8px;

  //
  .x-start {
    margin: {
      top: auto;
      bottom: auto;
    }

    position: relative;
  }

  //
  .x-margin {
    width: 2px;
    height: auto;

    margin: {
      left: 4px;
      right: 4px;
    }
  }

  //
  .x-end {
    flex: 1 1 auto;
  }
}

.x-selected-color {
  top: 0;
  left: 0;
  width: 40px;
  height: 40px;
  position: absolute;
  @include boder-radius(50%);
}

.x-selected-color-background {
  width: 40px;
  height: 40px;
  @include boder-radius(50%);
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAh0lEQVRYR+2W0QlAMQgD60zdfwOdqa8TmI/wQMr5K0I5bZLIzLOa2nt37VVVbd+dDx5obgCC3KBLwJ2ff4PnVidkf+ucIhw80HQaCLo3DMH3CRK3iFsmAWVl6hPNDwt8EvNE5q+YuEXcMgkonVM6SdyCoEvAnZ8v1Hjx817MilmxSUB5rdLJDycZgUAZUch/AAAAAElFTkSuQmCC');
}

.x-color-hue {
  width: 100%;
  height: 16px;
  border: none;
  cursor: pointer;
  margin-bottom: 8px;
  background-size: 100% 100%;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAAAQCAYAAAD06IYnAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AIWDwkUFWbCCAAAAFxJREFUaN7t0kEKg0AQAME2x83/n2qu5qCgD1iDhCoYdpnbQC9bbY1qVO/jvc6k3ad91s7/7F1/csgPrujuQ17BDYSFsBAWwgJhISyEBcJCWAgLhIWwEBYIi2f7Ar/1TCgFH2X9AAAAAElFTkSuQmCC');
}

.x-color-alpha {
  width: 100%;
  height: 16px;
  border: none;
  cursor: pointer;
  background-size: 100% 100%;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAAAQCAYAAAD06IYnAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AIWDwYQlZMa3gAAAWVJREFUaN7tmEGO6jAQRCsOArHgBpyAJYGjcGocxAm4A2IHpmoWE0eBH+ezmFlNvU06shJ3W6VEelWMUQAIIF9f6qZpimsA1LYtS2uF51/u27YVAFZVRUkEoGHdPV/sIcbIEIIkUdI/9Xa7neyv61+SWFUVAVCSct00TWn2fv6u3+Ecfd3tXzy/0+nEUu+SPjo/kqzrmiQpScN6v98XewfA8/lMkiLJ2WxGSUopcT6fM6U0NX9/frfbjev1WtfrlZfLhYfDQQHG/AIOlnGwjINlHCxjHCzjYJm/TJWdCwquJXseFFzGwDNNeiKMOJTO8xQdDQaeB29+K9efeLaBo9J7vdvtJj1RjFFjfiv7qv95tjx/7leSQgh93e1ffMeIp6O+YQjho/N791t1XVOSSI7N//K+4/GoxWLBx+PB5/Op5XLJ+/3OlJJWqxU3m83ovv5iGf8KjYNlHCxjHCzjYBkHy5gf5gusvQU7U37jTAAAAABJRU5ErkJggg==');
}

.x-color-value {
  margin-bottom: 8px;
}

.x-hex,
.x-rgba,
.x-hsla {
  display: flex;
  margin-bottom: 8px;
}

.x-color-text {
  display: block;
  flex: 1 1 auto;
  margin: {
    top: auto;
    bottom: auto;
  }
}

.x-color-input {
  //
  width: 100%;
  background-color: transparent !important;

  //
  border: {
    top: unset;
    left: unset;
    right: unset;
  }

  &:focus {
    outline: unset;
  }
}

.x-preset-colors-wrapper {
  * {
    text-align: start;
  }
}

.x-preset-label {
  display: block;
}

.x-preset-colors-box {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.x-preset-color {
  width: 18px;
  height: 18px;
  cursor: pointer;
  display: inline-block;
  margin: 6px 6px 8px 6px;
  @include boder-radius(25%);
  border: #a9a9a9 solid 1px;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker.component.ts

import {
  Component,
  ViewChild,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  rgbString,
  XHSVAColor,
  hslaToHsva,
  hsvaToHsla,
  rgbaString,
  rgbaToHsva,
  hsvaToRgba,
  rgbaToHex1,
  hslaString,
  parseXColor,
  stringToHsva,
  toNormalString,
  denormalizeRGBA,
  isNullOrEmptyString,
  XColorWithBrightness,
  XSliderValueEventModel,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XSliderPosition,
  XSliderDimension,
  XColorPickerAction,
  XColorPickerCssVar,
  XColorPickerActionModel,
  XColorPickerOutputFormat,
  DEFAULT_COLOR_PICKER_COLOR,
  XColorPickerContainerComponent,
} from './x-color-picker.typings';
import { Subscription, Subject } from 'rxjs';

@Component({
  selector: 'x-color-picker',
  templateUrl: './x-color-picker.component.html',
  styleUrls: ['./x-color-picker.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XColorPickerComponent extends XColorPickerContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private LAST_OUTPUT_COLOR: string;

  //
  readonly ColorFormats = Object.assign({}, XColorPickerOutputFormat);

  //
  hexValue: string;
  hslaValue: string;
  rgbaValue: string;

  //
  hsva: XHSVAColor = {
    h: 0,
    s: 0,
    v: 0,
    a: 1,
  };
  hueSliderColor: string;
  slider: XSliderPosition;
  alphaSliderColor: string;
  sliderDimMax: XSliderDimension;

  //
  inputColor: string;

  //
  //#region View Childs ...
  //
  //#region ColorPicker ViewChild ...
  private COLOR_PICKER: ElementRef<any>;

  @ViewChild('colorPicker', { static: false })
  set colorPicker(v: ElementRef<any>) {
    //
    this.COLOR_PICKER = v;
    this.initializeComponent();
  }

  get colorPicker() {
    return this.COLOR_PICKER;
  }
  //#endregion

  //
  //#region PaletteSlider ViewChild ...
  private PALETTE_SLIDER: ElementRef<any>;

  @ViewChild('paletteSlider', { static: false })
  set paletteSlider(v: ElementRef<any>) {
    //
    this.PALETTE_SLIDER = v;
    this.initializeComponent();
  }

  get paletteSlider() {
    return this.PALETTE_SLIDER;
  }
  //#endregion

  //
  //#region HueSlider ViewChild ...
  private HUE_SLIDER: ElementRef<any>;

  @ViewChild('hueSlider', { static: false })
  set hueSlider(v: ElementRef<any>) {
    //
    this.HUE_SLIDER = v;
    this.initializeComponent();
  }

  get hueSlider() {
    return this.HUE_SLIDER;
  }
  //#endregion

  //
  //#region AlphaSlider ViewChild ...
  private ALPHA_SLIDER: ElementRef<any>;

  @ViewChild('alphaSlider', { static: false })
  set alphaSlider(v: ElementRef<any>) {
    //
    this.ALPHA_SLIDER = v;
    this.initializeComponent();
  }

  get alphaSlider() {
    return this.ALPHA_SLIDER;
  }
  //#endregion
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.applyColors();
    await this.initializeComponent();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Colors Changed ...
    const isCursorColorChanged = changeKeys.includes(
      nameof<XColorPickerComponent>('cursorColor')
    );
    if (isCursorColorChanged) {
      await this.applyColors();
    }
    //#endregion

    //
    //#region SelectedColor Changed ...
    const isSelectedColorChanged = changeKeys.includes(
      nameof<XColorPickerComponent>('selectedColor')
    );
    if (isSelectedColorChanged) {
      //
      const selectedColor = await this.getValueAsync(this.selectedColor);
      if (isNullOrEmptyString(selectedColor)) {
        this.LAST_OUTPUT_COLOR = DEFAULT_COLOR_PICKER_COLOR;
      } else {
        this.LAST_OUTPUT_COLOR = selectedColor;
      }

      //
      await this.initializeComponent();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XColorPickerComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  async afterViewInit() {
    super.afterViewInit();
  }

  async onResize() {
    super.onResize();

    //
    await this.initializeComponent();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //#endregion

  //
  //#region UI Handlers ...
  async handleSaveSelectedColorInClipboard(color: string) {
    //
    if (isNullOrEmptyString(color)) {
      return;
    }

    //
    const isSaved = this.managerService.saveToClipboard(color);
    if (!isSaved) {
      return;
    }

    //
    await this.managerService.notificationService.presentDefaultSuccessNotification();
  }

  async handleClearSelectedColor() {
    //
    this.selectedColor = DEFAULT_COLOR_PICKER_COLOR;
    this.LAST_OUTPUT_COLOR = DEFAULT_COLOR_PICKER_COLOR;

    //
    this.detectChanges();
    this.colorSelected.emit('');
  }

  async handleInputColor(event?: any) {
    //
    if (isNullOrEmptyString(this.inputColor)) {
      return;
    }

    //
    const color = toNormalString(this.inputColor);
    this.selectedColor = color;
    await this.initializeComponent();
  }
  //#endregion

  //
  //#region Actions ...
  async setR(model: XSliderValueEventModel) {
    //
    const rgba = hsvaToRgba(this.hsva);
    rgba.r = model.v / model.rg;
    this.hsva = rgbaToHsva(rgba);

    //
    await this.update();
  }

  async setG(model: XSliderValueEventModel) {
    //
    const rgba = hsvaToRgba(this.hsva);
    rgba.g = model.v / model.rg;
    this.hsva = rgbaToHsva(rgba);

    //
    await this.update();
  }

  async setB(model: XSliderValueEventModel) {
    //
    const rgba = hsvaToRgba(this.hsva);
    rgba.b = model.v / model.rg;
    this.hsva = rgbaToHsva(rgba);

    //
    await this.update();
  }

  async setHue(model: XSliderValueEventModel) {
    //
    this.hsva.h = model.v / model.rg;

    //
    await this.update();
  }

  async setHexColor(model: string) {
    //
    let hsva = stringToHsva(model);
    if (!hsva) {
      hsva = stringToHsva(model);
    }

    //
    if (hsva) {
      //
      this.hsva = hsva;
      await this.update();
    }
  }

  async setAlpha(model: XSliderValueEventModel) {
    //
    this.hsva.a = model.v / model.rg;

    //
    await this.update();
  }

  async setSaturation(model: XSliderValueEventModel) {
    //
    const hsla = hsvaToHsla(this.hsva);
    hsla.s = model.v / model.rg;
    this.hsva = hslaToHsva(hsla);

    //
    await this.update();
  }

  async setLightness(model: XSliderValueEventModel) {
    //
    const hsla = hsvaToHsla(this.hsva);
    hsla.l = model.v / model.rg;
    this.hsva = hslaToHsva(hsla);

    //
    await this.update();
  }

  async setSaturationAndBrightness(model: XSliderValueEventModel) {
    //
    this.hsva.s = model.s / model.rgX;
    this.hsva.v = model.v / model.rgY;

    //
    await this.update();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XColorPickerActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XColorPickerAction.Refresh:
            this.detectChanges();
            break;

          //
          case XColorPickerAction.SetSelectedColor:
            //
            if (model.payload) {
              //
              this.selectedColor = model.payload as string;
              await this.initializeComponent();
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async applyColors() {
    //
    let cursorColor = await this.getValueAsync(this.cursorColor);
    if (!cursorColor) {
      cursorColor = XColorWithBrightness.Dark;
    }

    //
    const colorDescriptor = parseXColor(cursorColor);
    if (!colorDescriptor) {
      this.detectChanges();
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XColorPickerCssVar.CursorColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async initializeComponent() {
    //
    if (!this.colorPicker) {
      return;
    }

    //
    const colorPickerElement = this.colorPicker.nativeElement as HTMLElement;
    if (!colorPickerElement) {
      return;
    }

    //
    let paletteWidth = 0;
    let paletteHeight = 0;
    let hueOffsetWith = 0;
    let alphaOffsetWith = 0;

    //
    //#region Check Palette ...
    const hasPaletteChannel = await this.getValueAsync(this.showPaletteChannel);
    if (hasPaletteChannel) {
      //
      if (!this.paletteSlider) {
        return;
      }

      //
      const paletteElement = this.paletteSlider.nativeElement as HTMLElement;
      if (!paletteElement) {
        return;
      }

      //
      paletteWidth = paletteElement.offsetWidth;
      if (paletteWidth === 0) {
        setTimeout(() => this.initializeComponent(), 100);
        return;
      }

      //
      paletteHeight = paletteElement.offsetHeight;
      if (paletteHeight === 0) {
        setTimeout(() => this.initializeComponent(), 100);
        return;
      }
    }
    //#endregion

    //
    //#region Check Hue ...
    const hasHueChannel = await this.getValueAsync(this.showHueChannel);
    if (hasHueChannel) {
      //
      if (!this.hueSlider) {
        return;
      }

      //
      const hueElement = this.hueSlider.nativeElement as HTMLElement;
      if (!hueElement) {
        return;
      }

      //
      hueOffsetWith = hueElement.offsetWidth;
      if (hueOffsetWith === 0) {
        setTimeout(() => this.initializeComponent(), 100);
        return;
      }
    }
    //#endregion

    //
    //#region Check Alpha ...
    const hasAlphaChannel = await this.getValueAsync(this.showAlphaChannel);
    if (hasAlphaChannel) {
      //
      if (!this.alphaSlider) {
        return;
      }

      //
      const alphaElement = this.alphaSlider.nativeElement as HTMLElement;
      if (!alphaElement) {
        return;
      }

      //
      alphaOffsetWith = alphaElement.offsetWidth;
      if (alphaOffsetWith === 0) {
        setTimeout(() => this.initializeComponent(), 100);
        return;
      }
    }
    //#endregion

    //
    this.sliderDimMax = {
      h: hueOffsetWith,
      s: paletteWidth,
      v: paletteHeight,
      a: alphaOffsetWith,
    };

    //
    this.slider = {
      h: 0,
      s: 0,
      v: 0,
      a: 0,
    };

    //
    let selectedColor = await this.getValueAsync(this.selectedColor);
    if (isNullOrEmptyString(selectedColor)) {
      selectedColor = DEFAULT_COLOR_PICKER_COLOR;
    }

    //
    let hsva = stringToHsva(selectedColor);
    if (hsva) {
      //
      this.hsva = hsva;

      //
      await this.update();
    }

    //
    this.detectChanges();
  }

  private async getSelectedColor() {
    //
    const rgba = denormalizeRGBA(hsvaToRgba(this.hsva));

    //
    let outputFormat = await this.getValueAsync(this.outputFormat);
    if (isNullOrEmptyString(outputFormat)) {
      outputFormat = XColorPickerOutputFormat.XHEX;
    }

    //
    let result = '';
    switch (outputFormat) {
      //
      case XColorPickerOutputFormat.XHEX:
        result = rgbaToHex1(rgba, true);
        break;

      //
      case XColorPickerOutputFormat.XHSLA:
        //
        const hsla = hsvaToHsla(this.hsva);
        result = hslaString(hsla);
        break;

      //
      case XColorPickerOutputFormat.XRGBA:
        result = rgbaString(rgba);
        break;
    }

    //
    return result;
  }

  private async update() {
    //
    const rgba = denormalizeRGBA(hsvaToRgba(this.hsva));
    const hsla = hsvaToHsla(this.hsva);

    //
    const hHsva: XHSVAColor = {
      h: this.hsva.h,
      s: 1,
      v: 1,
      a: 1,
    };
    const hueRgba = denormalizeRGBA(hsvaToRgba(hHsva));

    //
    this.hexValue = rgbaToHex1(rgba, true);
    this.rgbaValue = rgbaString(rgba);
    this.hslaValue = hslaString(hsla);

    //
    this.alphaSliderColor = rgbString(rgba);
    this.hueSliderColor = rgbString(hueRgba);

    //
    this.selectedColor = await this.getSelectedColor();
    const selectedColor = await this.getValueAsync(this.selectedColor);

    //
    this.slider = {
      h: this.hsva.h * this.sliderDimMax.h - 8,
      s: this.hsva.s * this.sliderDimMax.s - 8,
      v: (1 - this.hsva.v) * this.sliderDimMax.v - 8,
      a: this.hsva.a * this.sliderDimMax.a - 8,
    };

    //
    if (
      !isNullOrEmptyString(this.LAST_OUTPUT_COLOR) &&
      this.LAST_OUTPUT_COLOR !== selectedColor
    ) {
      //
      this.LAST_OUTPUT_COLOR = selectedColor;
      this.colorSelected.emit(selectedColor);
    }

    //
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XColorPickerComponent } from './x-color-picker.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XColorTextDirective } from './directives/x-color-text.directive';
import { XColorPickerDialogService } from './x-color-picker-dialog/x-color-picker-dialog.service';
import { XColorPickerDialogComponent } from './x-color-picker-dialog/x-color-picker-dialog.component';

@NgModule({
  providers: [XColorPickerDialogService],
  entryComponents: [XColorPickerDialogComponent],
  declarations: [
    XColorTextDirective,
    XColorPickerComponent,
    XColorPickerDialogComponent,
  ],
  imports: [
    XCardModule,
    XIconModule,
    CommonModule,
    XSlotterModule,
    XBaseComponentModule,
  ],
  exports: [
    XCardModule,
    XIconModule,
    CommonModule,
    XSlotterModule,
    XColorTextDirective,
    XBaseComponentModule,
    XColorPickerComponent,
    XColorPickerDialogComponent,
  ],
})
export class XColorPickerModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XHSVAColor,
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { map, concatMap } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XColorPickerAction {
  Refresh = 'refresh',
  SetSelectedColor = 'set_selected_color',
}

export type XColorPickerActionIdentifier = XColorPickerAction | string;

export interface XColorPickerActionModel
  extends XBaseActionModel<XColorPickerActionIdentifier> {}

export interface XSliderPosition extends Required<XHSVAColor> {}

export interface XSliderDimension extends Required<XHSVAColor> {}

export enum XColorPickerOutputFormat {
  XHEX = 'hex',
  XRGBA = 'rgba',
  XHSLA = 'hsla',
}

export type XColorPickerOutputFormatIdentifier =
  | XColorPickerOutputFormat
  | string;

export enum XColorPickerCssVar {
  CursorColor = 'cursor-color',
}

export const DEFAULT_COLOR_PICKER_COLOR = '#ffffffff';

//
//#region Container ...
export interface IXColorPickerContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XColorPickerActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  colorSelected: EventEmitter<string>;

  //
  //#region Colors ...
  selectedColor: XStandardType<string>;
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;
  copyToClipboardIconColor: XStandardType<XColorIdentifier>;
  colorInputIconColor: XStandardType<XColorIdentifier>;
  clearColorIconColor: XStandardType<XColorIdentifier>;
  colorTextColor: XStandardType<XColorIdentifier>;
  presetColorsLableColor: XStandardType<XColorIdentifier>;
  cursorColor: XStandardType<XColorIdentifier>;
  //#endregion

  //
  //#region Labels ...
  colorInputIconLabel: XStandardType<XResourceIdentifier>;
  copyToClipboardIconLabel: XStandardType<XResourceIdentifier>;
  clearColorIconLabel: XStandardType<XResourceIdentifier>;
  //#endregion

  //
  //#region PresetColors ...
  presetColorsLable: XStandardType<XResourceIdentifier>;
  presetColors: XStandardType<XOneOrManyType<string>>;
  //#endregion

  //
  //#region Represent Color Value ...
  representColorFormats: XStandardType<
    XOneOrManyType<XColorPickerOutputFormatIdentifier>
  >;
  outputFormat: XStandardType<XColorPickerOutputFormatIdentifier>;
  //#endregion

  //
  //#region Show Parts Props ...
  showColorInput: XStandardType<boolean>;
  showCopyToClipboard: XStandardType<boolean>;
  showSelectedColorPreview: XStandardType<boolean>;
  showPaletteChannel: XStandardType<boolean>;
  showHueChannel: XStandardType<boolean>;
  showAlphaChannel: XStandardType<boolean>;
  showColorValue: XStandardType<boolean>;
  showPresetColors: XStandardType<boolean>;
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
export class XColorPickerContainer
  extends XComponentCardContainer
  implements Readonly<IXColorPickerContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XColorPickerActionModel> =
    new Subject<XColorPickerActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly colorSelected: EventEmitter<string> = new EventEmitter();

  //
  //#region Colors ...
  readonly selectedColor: XStandardType<string> = DEFAULT_COLOR_PICKER_COLOR;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly copyToClipboardIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.TertiaryShade;

  readonly colorInputIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.SecondaryShade;

  readonly clearColorIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Danger;

  readonly colorTextColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  readonly presetColorsLableColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  readonly cursorColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  //#endregion

  //
  //#region Labels ...
  readonly colorInputIconLabel: XStandardType<XResourceIdentifier> =
    XResourceIDs.input;

  readonly copyToClipboardIconLabel: XStandardType<XResourceIdentifier> =
    XResourceIDs.copy_to_clipboard;

  readonly clearColorIconLabel: XStandardType<XResourceIdentifier> =
    XResourceIDs.clear;
  //#endregion

  //
  //#region PresetColors ...
  readonly presetColorsLable: XStandardType<XResourceIdentifier>;
  readonly presetColors: XStandardType<XOneOrManyType<string>>;
  //#endregion

  //
  //#region Represent Color Value ...
  readonly representColorFormats: XStandardType<
    XOneOrManyType<XColorPickerOutputFormatIdentifier>
  > = [XColorPickerOutputFormat.XHEX];

  readonly outputFormat: XStandardType<XColorPickerOutputFormatIdentifier> =
    XColorPickerOutputFormat.XHEX;
  //#endregion

  //
  //#region Show Parts Props ...
  readonly showColorInput: XStandardType<boolean> = true;
  readonly showCopyToClipboard: XStandardType<boolean> = false;
  readonly showSelectedColorPreview: XStandardType<boolean> = true;
  readonly showPaletteChannel: XStandardType<boolean> = true;
  readonly showHueChannel: XStandardType<boolean> = true;
  readonly showAlphaChannel: XStandardType<boolean> = true;
  readonly showColorValue: XStandardType<boolean> = true;
  readonly showPresetColors: XStandardType<boolean> = true;
  //#endregion
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XColorPickerContainerComponent
  extends XComponentCardContainerComponent
  implements IXColorPickerContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XColorPickerActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Output()
  colorSelected: EventEmitter<string> = new EventEmitter();

  //
  //#region Colors ...
  @Input()
  selectedColor: XStandardType<string> = this.propertyProvider.selectedColor;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  @Input()
  copyToClipboardIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.copyToClipboardIconColor;

  @Input()
  colorInputIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.colorInputIconColor;

  @Input()
  clearColorIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.clearColorIconColor;

  @Input()
  colorTextColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.colorTextColor;

  @Input()
  presetColorsLableColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.presetColorsLableColor;

  @Input()
  cursorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.cursorColor;
  //#endregion

  //
  //#region Labels ...
  @Input()
  colorInputIconLabel: XStandardType<XResourceIdentifier> =
    this.propertyProvider.colorInputIconLabel;

  @Input()
  copyToClipboardIconLabel: XStandardType<XResourceIdentifier> =
    this.propertyProvider.copyToClipboardIconLabel;

  @Input()
  clearColorIconLabel: XStandardType<XResourceIdentifier> =
    this.propertyProvider.clearColorIconLabel;
  //#endregion

  //
  //#region PresetColors ...
  @Input()
  presetColorsLable: XStandardType<XResourceIdentifier> =
    this.propertyProvider.presetColorsLable;

  @Input()
  presetColors: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.presetColors;
  //#endregion

  //
  //#region Represent Color Value ...
  @Input()
  representColorFormats: XStandardType<
    XOneOrManyType<XColorPickerOutputFormatIdentifier>
  > = this.propertyProvider.representColorFormats;

  @Input()
  outputFormat: XStandardType<XColorPickerOutputFormatIdentifier> =
    this.propertyProvider.outputFormat;
  //#endregion

  //
  //#region Show Parts Props ...
  @Input()
  showColorInput: XStandardType<boolean> = this.propertyProvider.showColorInput;

  @Input()
  showCopyToClipboard: XStandardType<boolean> =
    this.propertyProvider.showCopyToClipboard;

  @Input()
  showSelectedColorPreview: XStandardType<boolean> =
    this.propertyProvider.showSelectedColorPreview;

  @Input()
  showPaletteChannel: XStandardType<boolean> =
    this.propertyProvider.showPaletteChannel;

  @Input()
  showHueChannel: XStandardType<boolean> = this.propertyProvider.showHueChannel;

  @Input()
  showAlphaChannel: XStandardType<boolean> =
    this.propertyProvider.showAlphaChannel;

  @Input()
  showColorValue: XStandardType<boolean> = this.propertyProvider.showColorValue;

  @Input()
  showPresetColors: XStandardType<boolean> =
    this.propertyProvider.showPresetColors;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XColorPickerContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXColorPickerContainer
  ): Observable<IXColorPickerContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            //#region Colors ...
            selectedColor: this.getValue(
              container ? container.selectedColor : this.selectedColor
            ),
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            copyToClipboardIconColor: this.getValue(
              container
                ? container.copyToClipboardIconColor
                : this.copyToClipboardIconColor
            ),
            colorInputIconColor: this.getValue(
              container
                ? container.colorInputIconColor
                : this.colorInputIconColor
            ),
            clearColorIconColor: this.getValue(
              container
                ? container.clearColorIconColor
                : this.clearColorIconColor
            ),
            colorTextColor: this.getValue(
              container ? container.colorTextColor : this.colorTextColor
            ),
            presetColorsLableColor: this.getValue(
              container
                ? container.presetColorsLableColor
                : this.presetColorsLableColor
            ),
            cursorColor: this.getValue(
              container ? container.cursorColor : this.cursorColor
            ),
            //#endregion

            //
            //#region Labels ...
            colorInputIconLabel: this.getValue(
              container
                ? container.colorInputIconLabel
                : this.colorInputIconLabel
            ),
            copyToClipboardIconLabel: this.getValue(
              container
                ? container.copyToClipboardIconLabel
                : this.copyToClipboardIconLabel
            ),
            clearColorIconLabel: this.getValue(
              container
                ? container.clearColorIconLabel
                : this.clearColorIconLabel
            ),
            //#endregion

            //
            //#region PresetColors ...
            presetColorsLable: this.getValue(
              container ? container.presetColorsLable : this.presetColorsLable
            ),
            presetColors: this.getValue(
              container ? container.presetColors : this.presetColors
            ),
            //#endregion

            //
            //#region Represent Color Value ...
            representColorFormats: this.getValue(
              container
                ? container.representColorFormats
                : this.representColorFormats
            ),
            outputFormat: this.getValue(
              container ? container.outputFormat : this.outputFormat
            ),
            //#endregion

            //
            //#region Show Parts Props ...
            showColorInput: this.getValue(
              container ? container.showColorInput : this.showColorInput
            ),
            showCopyToClipboard: this.getValue(
              container
                ? container.showCopyToClipboard
                : this.showCopyToClipboard
            ),
            showSelectedColorPreview: this.getValue(
              container
                ? container.showSelectedColorPreview
                : this.showSelectedColorPreview
            ),
            showPaletteChannel: this.getValue(
              container ? container.showPaletteChannel : this.showPaletteChannel
            ),
            showHueChannel: this.getValue(
              container ? container.showHueChannel : this.showHueChannel
            ),
            showAlphaChannel: this.getValue(
              container ? container.showAlphaChannel : this.showAlphaChannel
            ),
            showColorValue: this.getValue(
              container ? container.showColorValue : this.showColorValue
            ),
            showPresetColors: this.getValue(
              container ? container.showPresetColors : this.showPresetColors
            ),
            //#endregion
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            colorSelected: this.colorSelected,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\directives\x-color-text.directive.ts

import {
  Input,
  Output,
  Directive,
  EventEmitter,
  HostListener,
} from '@angular/core';

@Directive({
  selector: '[xColorText]',
})
export class XColorTextDirective {
  //
  @Input()
  text: any;

  @Input()
  rg: number;

  //
  @Output()
  newValue = new EventEmitter<any>();

  //
  @HostListener('input', ['$event.target.value'])
  changeInput(value: string) {
    //
    if (this.rg === undefined) {
      this.newValue.emit(value);
    } else {
      //
      const numeric = parseFloat(value);

      //
      if (!isNaN(numeric) && numeric >= 0 && numeric <= this.rg) {
        this.newValue.emit({ v: numeric, rg: this.rg });
      }
    }
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker-dialog\x-color-picker-dialog.component.html

<!-- Title Toolbar -->
<ion-toolbar class="v-toolbar" [xColor]="'primary'">
  <!-- Slots -->
  <div class="toolbar-area">
    <x-slotter>
      <!-- Start -->
      <x-slot
        [name]="SlotNames.START"
        [cssClass]="['ion-text-start', 'ion-float-start']"
      >
        <div
          matRipple
          (click)="handleBack()"
          class="toolbar-item toolbar-button center-vertical-flex"
        >
          <x-icon
            [name]="backIcon"
            [loading]="loading"
            [color]="'cb-inherit'"
            [uiDisabled]="uiDisabled"
          ></x-icon>
        </div>
      </x-slot>

      <!-- Center -->
      <x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
        <ion-title class="toolbar-item center-vertical-flex">
          <h3>
            {{ getTitle() | async }}
          </h3>
        </ion-title>
      </x-slot>

      <!-- End -->
      <x-slot
        [name]="SlotNames.END"
        [cssClass]="['ion-text-end', 'ion-float-end']"
      >
        <div
          matRipple
          *ngIf="canSelect()"
          (click)="handleSelect()"
          class="toolbar-item toolbar-button center-vertical-flex"
        >
          <x-icon
            [name]="selectIcon"
            [loading]="loading"
            [color]="'cb-inherit'"
            [uiDisabled]="uiDisabled"
          ></x-icon>
        </div>
      </x-slot>
    </x-slotter>
  </div>
</ion-toolbar>

<!-- ColorPicker Component -->
<x-color-picker
  [isInPage]="true"
  [wrapWithCard]="true"
  [uiDisabled]="uiDisabled"
  [color]="getColor() | async"
  [actionProvider]="actionProvider"
  [cssClass]="getCssClass() | async"
  [cardColor]="getCardColor() | async"
  [cardTitle]="getCardTitle() | async"
  [cursorColor]="getCursorColor() | async"
  [cardSubTitle]="getCardSubTitle() | async"
  [presetColors]="getPresetColors() | async"
  [outputFormat]="getOutputFormat() | async"
  [showHueChannel]="showHueChannel() | async"
  [showColorValue]="showColorValue() | async"
  [showColorInput]="showColorInput() | async"
  [selectedColor]="getSelectedColor() | async"
  (colorSelected)="handleColorSelected($event)"
  [colorTextColor]="getColorTextColor() | async"
  [foregroundColor]="isForegroundColor() | async"
  [showPresetColors]="showPresetColors() | async"
  [showAlphaChannel]="showAlphaChannel() | async"
  [showPaletteChannel]="showPaletteChannel() | async"
  [presetColorsLable]="getPresetColorsLable() | async"
  [showCopyToClipboard]="showCopyToClipboard() | async"
  [cardForegroundColor]="isCardForegroundColor() | async"
  [clearColorIconColor]="getClearColorIconColor() | async"
  [clearColorIconLabel]="getClearColorIconLabel() | async"
  [representColorFormats]="getRepresentColorFormats() | async"
  [presetColorsLableColor]="getPresetColorsLableColor() | async"
  [colorInputIconColor]="getColorInputIconColor() | async"
  [colorInputIconLabel]="getColorInputIconLabel() | async"
  [showSelectedColorPreview]="showSelectedColorPreview() | async"
  [copyToClipboardIconColor]="getCopyToClipboardIconColor() | async"
  [copyToClipboardIconLabel]="getCopyToClipboardIconLabel() | async"
>
</x-color-picker>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker-dialog\x-color-picker-dialog.component.scss

::ng-deep.fullscreen-dialog {
  //
  width: 80vw !important;
  max-width: 80vw !important;

  //
  height: 80vh !important;
  max-height: 80vh !important;

  //
  &.mobile {
    //
    width: 100vw !important;
    max-width: 100vw !important;

    //
    height: 100vh !important;
    max-height: 100vh !important;
  }

  //
  .mat-dialog-container {
    padding: 0 !important;
  }
}

x-icon {
  --size: 24px;
}

.toolbar-area {
  margin: 8px;
}

.toolbar-item {
  width: auto;
  height: 48px;
}

.toolbar-button {
  width: 32px;
  margin: {
    left: 4px;
    right: 4px;
  }
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker-dialog\x-color-picker-dialog.component.ts

import {
  Inject,
  NgZone,
  Renderer2,
  Component,
  ElementRef,
  ChangeDetectorRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XColor,
  hasChild,
  isFunction,
  XResourceIDs,
  XDialogResult,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XColorPickerActionModel,
  XColorPickerOutputFormat,
} from '../x-color-picker.typings';
import { Subject, of } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XBasePage } from '../../classes/x-base-page';
import { XManagerService } from 'x-framework-services';
import { XIconNames } from '../../x-icon/x-icon.typings';
import { XSlotName } from '../../x-slotter/x-slotter.typings';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { XColorPickerDialogConfig } from './x-color-picker-dialog.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from './../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from './../../config/x-framework-components.config';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-color-picker-dialog',
  templateUrl: './x-color-picker-dialog.component.html',
  styleUrls: ['./x-color-picker-dialog.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XColorPickerDialogComponent extends XBasePage {
  //
  //#region Props ...
  //
  selectIcon = XIconNames.checked;
  backIcon = XIconNames.arrow_back;
  SlotNames = Object.assign({}, XSlotName);

  //
  isMobileUi$ = this.managerService.isMobileUi$;
  isNotMobile$ = this.managerService.isNotMobileUi$;

  //
  actionProvider = new Subject<XColorPickerActionModel>();

  //
  selectedColor: string;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    @Inject(MAT_DIALOG_DATA) public data: any,
    public dialogRef: MatDialogRef<XColorPickerDialogComponent>
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    this.preventBack = true;
  }

  async afterViewInit() {
    super.afterViewInit();
  }
  //#endregion

  //
  //#region UI Providers ...
  getData() {
    return this.getValue(this.data as XColorPickerDialogConfig);
  }

  hasData() {
    return !isNullOrUndefined(this.getData());
  }

  canSelect() {
    return !isNullOrEmptyString(this.selectedColor);
  }

  getTitle() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.title))
    );
  }

  /**
   * retrieve selectedColor value ...
   */
  getSelectedColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.selectedColor))
    );
  }

  //
  //#region Colors ...
  /**
   * retrieve color value ...
   */
  getColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.color)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Light))
    );
  }

  /**
   * determines is Foureground color or not ...
   */
  isForegroundColor() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.foregroundColor))
    );
  }

  /**
   * retrieve cursorColor value ...
   */
  getCursorColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.cursorColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Dark))
    );
  }

  /**
   * retrieve colorTextColor value ...
   */
  getColorTextColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.colorTextColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Dark))
    );
  }

  /**
   * retrieve clearColorIconColor value ...
   */
  getClearColorIconColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.clearColorIconColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Danger))
    );
  }

  /**
   * retrieve colorInputIconColor value ...
   */
  getColorInputIconColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.colorInputIconColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Secondary))
    );
  }

  /**
   * retrieve presetColorsLableColor value ...
   */
  getPresetColorsLableColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.presetColorsLableColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Dark))
    );
  }

  /**
   * retrieve copyToClipboardIconColor value ...
   */
  getCopyToClipboardIconColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.copyToClipboardIconColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Tertiary))
    );
  }
  //#endregion

  //
  //#region Labels ...
  /**
   * retrieve presetColorsLable value ...
   */
  getPresetColorsLable() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.presetColorsLable)),
      map((value) =>
        !isNullOrEmptyString(value) ? value : XResourceIDs.preset_colors
      )
    );
  }

  /**
   * retrieve clearColorIconLabel value ...
   */
  getClearColorIconLabel() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.clearColorIconLabel)),
      map((value) => (!isNullOrEmptyString(value) ? value : XResourceIDs.clear))
    );
  }

  /**
   * retrieve colorInputIconLabel value ...
   */
  getColorInputIconLabel() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.colorInputIconLabel)),
      map((value) => (!isNullOrEmptyString(value) ? value : XResourceIDs.input))
    );
  }

  /**
   * retrieve copyToClipboardIconLabel value ...
   */
  getCopyToClipboardIconLabel() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getResourceValue(config.copyToClipboardIconLabel)
      ),
      map((value) =>
        !isNullOrEmptyString(value) ? value : XResourceIDs.copy_to_clipboard
      )
    );
  }
  //#endregion

  //
  //#region Preset Colors ...
  getPresetColors() {
    return this.getData().pipe(
      concatMap((config) => this.getArrayValue(config.presetColors))
    );
  }
  //#endregion

  //
  //#region Represent Format ...
  /**
   * retrieve representColorFormats value ...
   */
  getRepresentColorFormats() {
    return this.getData().pipe(
      concatMap((config) => this.getArrayValue(config.representColorFormats)),
      map((formats) =>
        hasChild(formats) ? formats : [XColorPickerOutputFormat.XHEX]
      )
    );
  }

  /**
   * retrieve outputFormat value ...
   */
  getOutputFormat() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.outputFormat)),
      map((format) =>
        !isNullOrEmptyString(format) ? format : XColorPickerOutputFormat.XHEX
      )
    );
  }
  //#endregion

  //
  //#region Show Parts ...
  /**
   * determines showCopyToClipboard or not ...
   */
  showCopyToClipboard() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showCopyToClipboard))
    );
  }

  /**
   * determines showColorInput or not ...
   */
  showColorInput() {
    return this.getData().pipe(
      concatMap((config) =>
        !isNullOrUndefined(config.showColorInput)
          ? this.getOptionalValue(config.showColorInput)
          : of(true)
      )
    );
  }

  /**
   * determines showSelectedColorPreview or not ...
   */
  showSelectedColorPreview() {
    return this.getData().pipe(
      concatMap((config) =>
        !isNullOrUndefined(config.showSelectedColorPreview)
          ? this.getOptionalValue(config.showSelectedColorPreview)
          : of(true)
      )
    );
  }

  /**
   * determines showPaletteChannel or not ...
   */
  showPaletteChannel() {
    return this.getData().pipe(
      concatMap((config) =>
        !isNullOrUndefined(config.showPaletteChannel)
          ? this.getOptionalValue(config.showPaletteChannel)
          : of(true)
      )
    );
  }

  /**
   * determines showHueChannel or not ...
   */
  showHueChannel() {
    return this.getData().pipe(
      concatMap((config) =>
        !isNullOrUndefined(config.showHueChannel)
          ? this.getOptionalValue(config.showHueChannel)
          : of(true)
      )
    );
  }

  /**
   * determines showAlphaChannel or not ...
   */
  showAlphaChannel() {
    return this.getData().pipe(
      concatMap((config) =>
        !isNullOrUndefined(config.showAlphaChannel)
          ? this.getOptionalValue(config.showAlphaChannel)
          : of(true)
      )
    );
  }

  /**
   * determines showColorValue or not ...
   */
  showColorValue() {
    return this.getData().pipe(
      concatMap((config) =>
        !isNullOrUndefined(config.showColorValue)
          ? this.getOptionalValue(config.showColorValue)
          : of(true)
      )
    );
  }

  /**
   * determines showPresetColors or not ...
   */
  showPresetColors() {
    return this.getData().pipe(
      concatMap((config) =>
        config.showPresetColors
          ? this.getOptionalValue(config.showPresetColors)
          : of(null)
      )
    );
  }
  //#endregion

  //
  /**
   * retrieve css classes ...
   */
  getCssClass() {
    return this.getData().pipe(
      concatMap((config) => this.getArrayValue(config.cssClass))
    );
  }

  //
  //#region
  /**
   * retireve cardColor value ...
   */
  getCardColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.cardColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Light))
    );
  }

  /**
   * determines is cardForegroundColor or not ...
   */
  isCardForegroundColor() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.cardForegroundColor))
    );
  }

  /**
   * retrieve cardTitle value ...
   */
  getCardTitle() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.cardTitle))
    );
  }

  /**
   * retrieve cardSubTitle value ...
   */
  getCardSubTitle() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.cardSubTitle))
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleBack() {
    this.dialogRef.close();
  }

  async handleColorSelected(event: string) {
    //
    this.selectedColor = event;

    //
    const config = await this.getValueAsync(this.getData());
    if (!config || !config.colorSelected || !isFunction(config.colorSelected)) {
      return;
    }

    //
    config.colorSelected.emit(event);

    //
    this.detectChanges();
  }

  async handleSelect() {
    //
    if (!this.canSelect()) {
      return;
    }

    //
    this.dialogRef.close({
      result: XDialogResult.Ok,
      payload: this.selectedColor,
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker-dialog\x-color-picker-dialog.service.ts

import {
  XResourceIDs,
  XBaseService,
  XDialogResult,
  isNullOrUndefined,
} from 'x-framework-core';
import {
  MatDialog,
  MatDialogRef,
  MatDialogConfig,
} from '@angular/material/dialog';
import {
  XColorPickerDialogResult,
  XColorPickerDialogConfig,
} from './x-color-picker-dialog.typings';
import { XManagerService } from 'x-framework-services';
import { Injectable, Inject, Output, EventEmitter } from '@angular/core';
import { XColorPickerDialogComponent } from './x-color-picker-dialog.component';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from './../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from './../../config/x-framework-components.config';

@Injectable({
  providedIn: 'root',
})
export class XColorPickerDialogService extends XBaseService {
  //
  //#region Props ...
  private MOBILE_CLASS_NAME = 'mobile';
  private DIALOG_REF: MatDialogRef<XColorPickerDialogComponent>;

  //
  isMobile$ = this.managerService.isMobileUi$;

  // tslint:disable-next-line:no-output-on-prefix
  @Output()
  onResult = new EventEmitter<XColorPickerDialogResult>();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    private dialog: MatDialog,
    private managerService: XManagerService
  ) {
    super(config);
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * Open Select Map Dialog ...
   */
  async openDialog(config?: XColorPickerDialogConfig) {
    //
    // Prevent Double Bubble ...
    if (this.DIALOG_REF) {
      return;
    }

    //
    // Prepare Config ...
    if (!config) {
      config = {};
    }

    //
    // Set Default Title ...
    if (config && !config.title) {
      config.title = XResourceIDs.color_picker_dialog_title;
    }

    //
    // Prepare Dialog Config ...
    const data: MatDialogConfig = {
      data: config,
    };

    //
    data.panelClass = 'fullscreen-dialog';

    //
    // Show Cropper inside a Dialog ...
    this.DIALOG_REF = this.dialog.open(XColorPickerDialogComponent, data);
    this.DIALOG_REF.disableClose = true;

    //
    // Attach Mobile Class ...
    this.isMobile$.subscribe((isMobile) => {
      //
      if (isMobile) {
        if (this.DIALOG_REF) {
          this.DIALOG_REF.addPanelClass(this.MOBILE_CLASS_NAME);
        }
      } else {
        if (this.DIALOG_REF) {
          this.DIALOG_REF.removePanelClass(this.MOBILE_CLASS_NAME);
        }
      }
    });

    //
    // Handle Backdrop ...
    this.DIALOG_REF.backdropClick().subscribe(() => {
      //
      // Cancel Dialog ...
      this.onResult.emit({ result: XDialogResult.Cancel });

      //
      this.closeDialog();
    });

    //
    // Handle Dialog Result ...
    this.DIALOG_REF.afterClosed().subscribe(
      (model: XColorPickerDialogResult) => {
        //
        if (isNullOrUndefined(model) || model.result !== XDialogResult.Ok) {
          //
          // Cancel Dialog ...
          this.onResult.emit({ result: XDialogResult.Cancel });
        } else {
          //
          // Handle Results ...
          this.onResult.emit(model);
        }

        //
        this.closeDialog();
      }
    );
  }

  /**
   * Close Select Map Dialog ...
   */
  closeDialog() {
    //
    // Prevent Go Forward ...
    if (!this.DIALOG_REF) {
      return;
    }

    //
    // Close Dialog ...
    this.DIALOG_REF.close();
    this.DIALOG_REF = null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-color-picker\x-color-picker-dialog\x-color-picker-dialog.typings.ts

import { XDialogResult } from 'x-framework-core';
import { IXColorPickerContainer } from '../x-color-picker.typings';
import { XBaseDialogConfig } from '../../classes/i-x-base-dialog.container';

export interface XColorPickerDialogResult {
  payload?: string;
  result: XDialogResult;
}

export interface XColorPickerDialogConfig
  extends XBaseDialogConfig,
    Partial<IXColorPickerContainer> {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-counter\x-counter.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <h4
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    class="
      x-wrapper x-counter
      display-message-container
      ion-no-padding ion-no-margin
    "
  >
    {{ displayMessage }}
  </h4>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-counter\x-counter.component.scss

.display-message-container {
  margin: 2px;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-counter\x-counter.component.ts

import {
  XCounterTick,
  XCounterType,
  XCounterState,
  XCounterAction,
  XCounterActionModel,
  XCounterTypeIdentifier,
  XCounterContainerComponent,
} from './x-counter.typings';
import { nameof } from 'x-framework-core';
import { Subscription, interval, Subject } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'x-counter',
  templateUrl: './x-counter.component.html',
  styleUrls: ['./x-counter.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XCounterComponent extends XCounterContainerComponent {
  //
  //#region Props ...
  private StepCount: number;
  private IsCounting: boolean;
  private TYPE: XCounterTypeIdentifier;
  private TimerSubscription: Subscription;

  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  displayMessage = '';
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Handle Values ...
    const isStopValueChanged = changeKeys.includes(
      nameof<XCounterComponent>('stopValue')
    );
    const isStartValueChanged = changeKeys.includes(
      nameof<XCounterComponent>('startValue')
    );
    const isCurrentValueChanged = changeKeys.includes(
      nameof<XCounterComponent>('currentValue')
    );
    if (isStopValueChanged || isStartValueChanged || isCurrentValueChanged) {
      this.setBoundaries(this.startValue, this.stopValue, this.currentValue);
    }
    //#endregion

    //
    //#region Handle Type Changed ...
    const isTypeChanged = changeKeys.includes(
      nameof<XCounterComponent>('type')
    );
    if (isTypeChanged) {
      await this.prepareType();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XCounterComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  async onInit() {
    super.onInit();

    //
    await this.prepareType();

    //
    // Try to Show Start Value ...
    this.updateDisplayMessage();

    //
    await this.handleAutoStart();
  }

  onDestroy() {
    super.onDestroy();

    //
    if (this.TimerSubscription) {
      this.TimerSubscription.unsubscribe();
      this.TimerSubscription = null;
    }

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //#endregion

  //
  //#region UI Handlers ...
  //#endregion

  //
  //#region Actions ...
  setBoundaries(start: number, stop: number, current: number) {
    //
    this.startValue = start;
    this.stopValue = stop;
    this.currentValue = current;
  }

  async start() {
    //
    const isUpdated = await this.updateValues();

    //
    if (isUpdated && !this.TimerSubscription && this.getStepsCount() > 0) {
      this.startCounter();
    }
  }

  pause() {
    //
    if (this.TimerSubscription) {
      this.IsCounting = false;
      this.TimerSubscription.unsubscribe();
      this.TimerSubscription = null;
    }
  }

  cancel() {
    this.currentValue = this.stopValue;
  }

  getCounterState(): XCounterState {
    //
    if (
      this.TYPE === XCounterType.CountDown &&
      this.currentValue === this.startValue
    ) {
      return XCounterState.Start;
    }

    //
    if (
      this.TYPE === XCounterType.CountUp &&
      this.currentValue === this.stopValue
    ) {
      return XCounterState.Start;
    }

    //
    if (this.isLastStep()) {
      return XCounterState.Stop;
    }

    //
    if (
      this.TYPE === XCounterType.CountDown &&
      this.currentValue > this.startValue
    ) {
      return XCounterState.Tick;
    }

    //
    if (
      this.TYPE === XCounterType.CountUp &&
      this.currentValue < this.startValue
    ) {
      return XCounterState.Tick;
    }

    //
    return XCounterState.Stop;
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XCounterActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XCounterAction.START:
            await this.start();
            break;

          //
          case XCounterAction.PAUSE:
            this.pause();
            break;

          //
          case XCounterAction.CANCEL:
            this.cancel();
            break;

          //
          case XCounterAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private startCounter() {
    //
    this.StepCount = this.getStepsCount();

    //
    if (!this.IsCounting && this.StepCount && this.StepCount > 0) {
      //
      this.IsCounting = true;
      this.TimerSubscription = interval(this.delay).subscribe(() => {
        //
        this.updateDisplayMessage();

        //
        const eventModel = this.getTickModel();

        //
        if (eventModel.state === XCounterState.Start) {
          this.counterStart.emit(eventModel);
        }

        //
        if (eventModel.state === XCounterState.Stop) {
          this.counterStop.emit(eventModel);
        }

        //
        this.counterTtick.emit(eventModel);

        //
        if (this.isLastStep()) {
          this.pause();
        } else if (this.TYPE === XCounterType.CountDown) {
          this.currentValue--;
        } else if (this.TYPE === XCounterType.CountUp) {
          this.currentValue++;
        }
      });
    }
  }

  private getTickModel(): XCounterTick {
    return {
      type: this.TYPE,
      start: this.startValue,
      stop: this.stopValue,
      current: this.currentValue,
      delay: this.delay,
      state: this.getCounterState(),
    } as XCounterTick;
  }

  private getStepsCount() {
    //
    let count = -1;

    //
    switch (this.TYPE) {
      case XCounterType.CountDown:
        count = this.startValue - this.stopValue;
        break;

      case XCounterType.CountUp:
        count = this.stopValue - this.startValue;
        break;
    }

    //
    if (isNaN(count)) {
      count = -1;
    }

    //
    return count;
  }

  private isLastStep() {
    //
    switch (this.TYPE) {
      //
      case XCounterType.CountDown:
        return this.currentValue <= this.stopValue;

      //
      default:
      case XCounterType.CountUp:
        return this.currentValue >= this.startValue;
    }
  }

  private updateValues() {
    return new Promise<boolean>((res) => {
      //
      switch (this.TYPE) {
        case XCounterType.CountDown:
          //
          if (this.startValue < this.stopValue) {
            res(false);
            return;
          }

          //
          if (!this.currentValue) {
            this.currentValue = this.startValue;
          }
          break;

        case XCounterType.CountUp:
          if (this.startValue > this.stopValue) {
            res(false);
            return;
          }

          //
          if (!this.currentValue) {
            this.currentValue = this.stopValue;
          }
          break;
      }

      //
      res(true);
    });
  }

  private updateDisplayMessage() {
    //
    this.displayMessage = this.displayPattern(
      this.stopValue,
      this.startValue,
      this.currentValue
    );
  }

  //
  private async prepareType() {
    this.TYPE = await this.getValueAsync(this.type);
  }

  //
  private async handleAutoStart() {
    //
    const isAutoStart = await this.getValueAsync(this.autoStart);
    if (!isAutoStart) {
      return;
    }

    //
    await this.start();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-counter\x-counter.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XCounterComponent } from './x-counter.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XCounterComponent],
  imports: [XCardModule, CommonModule, XBaseComponentModule],
  exports: [XCardModule, CommonModule, XCounterComponent, XBaseComponentModule],
})
export class XCounterModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-counter\x-counter.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XOneOrManyType, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XCounterType {
  CountUp = 'count_up',
  CountDown = 'count_down',
}

export type XCounterTypeIdentifier = XCounterType | string;

export enum XCounterState {
  Start,
  Tick,
  Stop,
}

export interface XCounterTick {
  type: XCounterType;
  start: number;
  stop: number;
  current: number;
  delay: number;
  state: XCounterState;
}

export const defaultStringPatternMaker = (
  max: number,
  min: number,
  current: number
) => {
  return `${current}`;
};

export enum XCounterAction {
  START = 'start',
  PAUSE = 'pause',
  CANCEL = 'cancel',

  //
  Refresh = 'refresh',
}

export type XCounterActionIdentifier = XCounterAction | string;

export interface XCounterActionModel
  extends XBaseActionModel<XCounterActionIdentifier> {}

//
//#region Container ...
export interface IXCounterContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XCounterActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  type: XStandardType<XCounterTypeIdentifier>;
  autoStart: XStandardType<boolean>;
  stopValue: number;
  startValue: number;
  currentValue: number;
  delay: number;

  //
  displayPattern: (min: number, max: number, current: number) => string;

  //
  counterStart: EventEmitter<XCounterTick>;
  counterTtick: EventEmitter<XCounterTick>;
  counterStop: EventEmitter<XCounterTick>;
}

@Injectable({
  providedIn: 'root',
})
export class XCounterContainer
  extends XComponentCardContainer
  implements Readonly<IXCounterContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XCounterActionModel> =
    new Subject<XCounterActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly type: XStandardType<XCounterTypeIdentifier> = XCounterType.CountDown;
  readonly autoStart: XStandardType<boolean> = false;
  readonly stopValue: number;
  readonly startValue: number;
  readonly currentValue: number;
  readonly delay: number = 1000;

  //
  readonly displayPattern: (
    min: number,
    max: number,
    current: number
  ) => string = defaultStringPatternMaker;

  //
  readonly counterStart: EventEmitter<XCounterTick> =
    new EventEmitter<XCounterTick>();
  readonly counterTtick: EventEmitter<XCounterTick> =
    new EventEmitter<XCounterTick>();
  readonly counterStop: EventEmitter<XCounterTick> =
    new EventEmitter<XCounterTick>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XCounterContainerComponent
  extends XComponentCardContainerComponent
  implements IXCounterContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XCounterActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  type: XStandardType<XCounterTypeIdentifier> = this.propertyProvider.type;

  @Input()
  autoStart: XStandardType<boolean> = false;

  @Input()
  stopValue: number = this.propertyProvider.stopValue;

  @Input()
  startValue: number = this.propertyProvider.startValue;

  @Input()
  currentValue: number = this.propertyProvider.currentValue;

  @Input()
  delay = this.propertyProvider.delay;

  @Input()
  displayPattern: (min: number, max: number, current: number) => string =
    this.propertyProvider.displayPattern;

  //
  @Output()
  counterStart: EventEmitter<XCounterTick> = new EventEmitter();

  @Output()
  counterTtick: EventEmitter<XCounterTick> = new EventEmitter();

  @Output()
  counterStop: EventEmitter<XCounterTick> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XCounterContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXCounterContainer
  ): Observable<IXCounterContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            type: this.getValue(container ? container.type : this.type),
            autoStart: this.getValue(
              container ? container.autoStart : this.autoStart
            ),
            stopValue: this.getValue(
              container ? container.stopValue : this.stopValue
            ),
            startValue: this.getValue(
              container ? container.startValue : this.startValue
            ),
            currentValue: this.getValue(
              container ? container.currentValue : this.currentValue
            ),
            delay: this.getValue(container ? container.delay : this.delay),
            displayPattern: this.getValue(
              container ? container.displayPattern : this.displayPattern
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            counterStart: this.counterStart,
            counterTtick: this.counterTtick,
            counterStop: this.counterStop,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-pager\x-data-pager.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <div
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    class="x-wrapper x-data-pager ion-no-padding"
  >
    <!-- Above Pager -->
    <ng-container
      *ngIf="
        (getValue(paginatorPosition) | async) ===
          DataPagerPaginatorPositions.Above &&
        result &&
        hasChildValue(result.items)
      "
    >
      <ng-container *ngTemplateOutlet="paginatorRef"></ng-container>
    </ng-container>

    <!-- Custom Contet -->
    <ng-content select="[content]"> </ng-content>

    <!-- Items or Empty -->
    <ng-container
      *ngTemplateOutlet="
        result && (hasChildValue(result.items) | async)
          ? itemsRef
          : emptyListTemplate
          ? emptyListTemplate
          : defaultEmptyListRef
      "
    >
    </ng-container>

    <!-- Below Pager -->
    <ng-container
      *ngIf="
        (getValue(paginatorPosition) | async) ===
          DataPagerPaginatorPositions.Below &&
        result &&
        hasChildValue(result.items)
      "
    >
      <ng-container *ngTemplateOutlet="paginatorRef"></ng-container>
    </ng-container>
  </div>
</ng-template>

<!-- Items Presenting -->
<ng-template #itemsRef>
  <ng-container *ngIf="itemTemplate">
    <!-- Item Type Handler -->
    <ng-container
      *ngIf="(getValue(type) | async) === DataPagerPresetTypes.Item"
    >
      <ng-container *ngFor="let item of result.items">
        <ng-container
          *ngTemplateOutlet="itemTemplate; context: { $implicit: item }"
        ></ng-container>
      </ng-container>
    </ng-container>

    <!-- Page Type Handler -->
    <ng-container
      *ngIf="(getValue(type) | async) === DataPagerPresetTypes.Page"
    >
      <ng-container
        *ngTemplateOutlet="itemTemplate; context: { $implicit: result.items }"
      ></ng-container>
    </ng-container>
  </ng-container>
</ng-template>

<ng-template #paginatorRef>
  <ion-toolbar
    class="x-paginator-bar"
    *ngIf="
      (getValue(showPaginator) | async) &&
      (notNullOrUndefinedValue(preparedPaginatorOptions) | async)
    "
    [xColor]="getValue(paginatorColor) | async"
  >
    <!-- Mat Paginator Component -->
    <mat-paginator
      #matPaginator
      [hidden]="true"
      [disabled]="loading || uiDisabled"
      [pageSize]="getValue(preparedPaginatorOptions.pageSize) | async"
    ></mat-paginator>

    <!-- Start Slot Contents -->
    <ion-buttons slot="start" *ngIf="preparedPaginatorOptions">
      <!-- First -->
      <ion-button
        #first
        (click)="firstPage()"
        *ngIf="preparedPaginatorOptions.first"
        [hidden]="getValue(preparedPaginatorOptions.first.hidden) | async"
        [disabled]="
          loading ||
          uiDisabled ||
          (getValue(preparedPaginatorOptions.first.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.first.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.first.title) | async,
              color: getValue(preparedPaginatorOptions.first.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>

      <!-- Previous -->
      <ion-button
        #previous
        (click)="previousPage()"
        *ngIf="preparedPaginatorOptions.prev"
        [hidden]="getValue(preparedPaginatorOptions.prev.hidden) | async"
        [disabled]="
          loading ||
          uiDisabled ||
          (getValue(preparedPaginatorOptions.prev.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.prev.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.prev.title) | async,
              color: getValue(preparedPaginatorOptions.prev.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>
    </ion-buttons>

    <!-- Pagination Info -->
    <div class="x-paginator-wrapper">
      <div fxLayout="row" fxLayoutAlign="center stretch">
        <!-- Current Page -->
        <div fxFlex="auto">
          <ion-button
            fill="clear"
            expand="full"
            class="cb-inherit"
            *ngIf="!showPageNumberInput"
            (click)="showPageNumberInput = true"
            [disabled]="loading || uiDisabled"
          >
            {{
              preparedPaginatorOptions.pageInfoProvider(
                managerService.currentLocale,
                currentPage,
                pageSize,
                totalPages,
                totalItems
              )
            }}
          </ion-button>

          <!-- Change Current Page -->
          <ion-input
            step="1"
            [min]="1"
            type="number"
            debounce="500"
            required="true"
            autofocus="true"
            [max]="totalPages"
            class="cb-inherit"
            inputmode="numeric"
            [value]="currentPage"
            *ngIf="showPageNumberInput"
            (ionBlur)="showPageNumberInput = false"
            [disabled]="loading || uiDisabled"
            (ionChange)="handlePageChange($event)"
          ></ion-input>
        </div>

        <!-- Page Size -->
        <div fxFlex="auto">
          <ion-button
            fill="clear"
            expand="full"
            *ngIf="pageSize"
            class="cb-inherit"
            [disabled]="loading || uiDisabled"
            (click)="handlePageSizeChange($event)"
          >
            {{ applyLocale(pageSize) }}
          </ion-button>
        </div>
      </div>
    </div>

    <!-- End Slot Content -->
    <ion-buttons slot="end">
      <!-- Noxt -->
      <ion-button
        #next
        (click)="nextPage()"
        *ngIf="preparedPaginatorOptions.next"
        [hidden]="getValue(preparedPaginatorOptions.next.hidden) | async"
        [disabled]="
          loading ||
          uiDisabled ||
          (getValue(preparedPaginatorOptions.next.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.next.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.next.title) | async,
              color: getValue(preparedPaginatorOptions.next.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>

      <!-- Last -->
      <ion-button
        #last
        (click)="lastPage()"
        *ngIf="preparedPaginatorOptions.last"
        [hidden]="getValue(preparedPaginatorOptions.last.hidden) | async"
        [disabled]="
          loading ||
          uiDisabled ||
          (getValue(preparedPaginatorOptions.last.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.last.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.last.title) | async,
              color: getValue(preparedPaginatorOptions.prev.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ng-template>

<!-- Default Empty Content -->
<ng-template #defaultEmptyListRef>
  <!-- Empty Handler -->
  <ng-container
    *ngIf="
      (getValue(showEmpty) | async) &&
      (!result || !(hasChildValue(result.items) | async))
    "
  >
    <x-empty
      (refresh)="emptyRefresh.emit($event)"
      [color]="getValue(emptyColor) | async"
      [message]="getResourceValue(emptyMessage) | async"
      [refreshIcon]="getValue(emptyRefreshIcon) | async"
      [messageColor]="getValue(emptyMessageColor) | async"
      [hasRefresh]="getValue(emptyHasRefresh) | async"
      [foregroundRefreshButtonColor]="
        getValue(emptyForegroundRefreshButtonColor) | async
      "
      [foregroundColor]="getValue(emptyForegroundColor) | async"
      [refreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
      [foregroundMessageColor]="getValue(emptyForegroundMessageColor) | async"
    ></x-empty> </ng-container
></ng-template>

<!-- Icon Template Ref -->
<ng-template
  #iconRef
  let-name="name"
  let-tooltip="tooltip"
  let-color="color"
  let-applyDirection="applyDirection"
>
  <x-icon
    [name]="name"
    [color]="color"
    [loading]="loading"
    [matTooltip]="tooltip"
    [foregroundColor]="true"
    [uiDisabled]="uiDisabled"
    [applyDirection]="applyDirection"
    [cssClass]="!color ? 'cb-inherit' : null"
  ></x-icon>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-pager\x-data-pager.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-pager\x-data-pager.component.ts

import {
  nameof,
  XLocale,
  toNumber,
  hasChild,
  applyLocale,
  XModalButtonRole,
  XPickerColumnOption,
  XBaseDataPagerQuery,
} from 'x-framework-core';
import {
  XDataPagerAction,
  XDataPagerActionModel,
  XDataPagerPresentType,
  XDataPagerPaginatorOptions,
  XDataPagerPaginatorPosition,
  XDataPagerContainerComponent,
} from './x-data-pager.typings';
import { Subject, Subscription } from 'rxjs';
import { Component, ViewChild } from '@angular/core';
import { XIconNames } from '../x-icon/x-icon.typings';
import { ChangeDetectionStrategy } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';

@Component({
  selector: 'x-data-pager',
  templateUrl: './x-data-pager.component.html',
  styleUrls: ['./x-data-pager.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XDataPagerComponent extends XDataPagerContainerComponent {
  //
  //#region Props ...
  //
  private MODAL: any;
  private PAGE_SIZE: number;
  private TOTAL_PAGES: number;
  private TOTAL_ITEMS: number;
  private CURRENT_PAGE: number;
  private PAGINATOR: MatPaginator;
  private PAGINATOR_PAGE_SUBSCRIPTION: Subscription;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private PAGINATOR_OPTIONS: XDataPagerPaginatorOptions;

  //
  readonly DataPagerPresetTypes = Object.assign({}, XDataPagerPresentType);
  readonly DataPagerPaginatorPositions = Object.assign(
    {},
    XDataPagerPaginatorPosition
  );

  //
  showPageNumberInput: boolean;

  //
  @ViewChild(MatPaginator, { static: false })
  set paginator(v: MatPaginator) {
    if (!v) {
      return;
    }

    //
    this.PAGINATOR = v;
    this.setPaginatorData();
  }

  get paginator() {
    return this.PAGINATOR;
  }

  get pageSize() {
    return this.PAGE_SIZE;
  }

  get currentPage() {
    return this.CURRENT_PAGE;
  }

  get totalPages() {
    return this.TOTAL_PAGES;
  }

  get totalItems() {
    return this.TOTAL_ITEMS;
  }

  get preparedPaginatorOptions() {
    return this.PAGINATOR_OPTIONS;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    await this.preparePaginatorOptions();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XDataPagerComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region PaginatorOptionChanged ...
    const isPaginatorOptionsChanged = changeKeys.includes(
      nameof<XDataPagerComponent>('paginatorOptions')
    );
    if (isPaginatorOptionsChanged) {
      await this.preparePaginatorOptions();
    }
    //#endregion

    //
    //#region Query Changed ...
    const isTaskChanged = changeKeys.includes(
      nameof<XDataPagerComponent>('task')
    );
    const isQueryChanged = changeKeys.includes(
      nameof<XDataPagerComponent>('query')
    );
    if (isQueryChanged || isTaskChanged) {
      await this.handleQueryChanged();
    }
    //#endregion

    //
    //#region Result Changed ...
    const isResultChanged = changeKeys.includes(
      nameof<XDataPagerComponent>('result')
    );
    if (isResultChanged) {
      await this.handleResultChanged();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handlePageChange(event: CustomEvent | any) {
    //
    const selectedPage = (event.detail.value as number) || undefined;
    if (!selectedPage) {
      return;
    }

    //
    await this.toPage(selectedPage);
  }

  async handlePageSizeChange(event?: any) {
    //
    if (!this.preparedPaginatorOptions || !this.pageSize) {
      return;
    }

    //
    const pageSizes = await this.getValueAsync(
      this.preparedPaginatorOptions.pageSizes
    );
    if (!hasChild(pageSizes)) {
      return;
    }

    //
    const options: XPickerColumnOption[] = pageSizes.map((ps) => {
      //
      const opt: XPickerColumnOption = {
        text: this.applyLocale(ps.toString()),
        value: ps,
        disabled: ps === this.pageSize,
      };

      //
      return opt;
    });

    //
    if (this.MODAL) {
      await this.managerService.dialogService.pickerController.dismiss();
      this.MODAL = null;
    }

    //
    this.MODAL = await this.managerService.dialogService.presentPicker({
      buttons: [
        {
          text: this.resourceProvider(this.ResourceIDs.accept),
          role: XModalButtonRole.Selected,
          handler: (value) => {
            if (!value) {
              return;
            }

            //
            const pageSize = value.pageSize.value;
            this.changePageSize(pageSize);
          },
        },
        {
          text: this.resourceProvider(this.ResourceIDs.cancel),
          role: XModalButtonRole.Cancel,
        },
      ],
      columns: [{ name: 'pageSize', options }],
    });

    //
    await this.MODAL.onWillDismiss();
    this.MODAL = null;
  }
  //#endregion

  //
  //#region Actions ...
  //
  //#region Pager Actions ...
  async firstPage() {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.firstPage();
  }

  async lastPage() {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.lastPage();
  }

  async nextPage() {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.nextPage();
  }

  async previousPage() {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.previousPage();
  }

  async toPage(index: number) {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    index = index === 0 ? 1 : index;

    //
    this.paginator.pageIndex = index - 1;
    this.paginator.page.next({
      pageIndex: index - 1,
      pageSize: this.pageSize,
      length: this.totalItems,
    });
  }

  async changePageSize(size: number) {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    if (!size || !this.paginator) {
      return;
    }

    //
    this.paginator.pageSize = size;
    this.paginator.page.next({
      pageIndex: 0,
      pageSize: size,
      length: this.totalItems,
    });
  }
  //#endregion
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XDataPagerActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XDataPagerAction.Refresh:
            this.detectChanges();
            break;

          //
          case XDataPagerAction.GoFirst:
            await this.firstPage();
            break;

          //
          case XDataPagerAction.GoLast:
            await this.lastPage();
            break;

          //
          case XDataPagerAction.GoNext:
            await this.nextPage();
            break;

          //
          case XDataPagerAction.GoPrev:
            await this.previousPage();
            break;

          //
          case XDataPagerAction.ToPage:
            if (model.payload) {
              await this.toPage(model.payload);
            }
            break;

          //
          case XDataPagerAction.ChangePageSize:
            if (model.payload) {
              await this.changePageSize(model.payload);
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  private async handleQueryChanged() {
    //
    this.result = null;

    //
    if (!this.query || !this.task) {
      return;
    }

    //
    const mTask = this.task(this.query);

    //
    this.taskReady.emit(mTask);
  }

  private async handleResultChanged() {
    //
    if (!this.result) {
      return;
    }

    //
    //#region Extract Required Options ...
    this.CURRENT_PAGE = this.query.pageNumber;
    this.PAGE_SIZE = this.query.rowsInPage;
    this.TOTAL_PAGES = this.calculatePageCount(
      this.result.count,
      this.query.rowsInPage
    );
    this.TOTAL_ITEMS = this.result.count;
    //#endregion

    //
    if (!this.preparedPaginatorOptions) {
      await this.preparePaginatorOptions();
    }

    //
    await this.setPaginatorData();
  }

  private async setPaginatorData() {
    //
    if (!this.PAGINATOR) {
      return;
    }

    //
    this.PAGINATOR.pageSize = this.PAGE_SIZE;
    this.PAGINATOR.pageIndex = this.CURRENT_PAGE - 1;
    this.PAGINATOR.length = this.TOTAL_ITEMS;

    //
    // Prepare Paginator Options ...
    await this.preparePaginatorOptions();
    await this.registerPaginatorHandlers();
  }

  private async preparePaginatorOptions() {
    //
    if (!this.result) {
      return;
    }

    //
    //#region Preparing Options ...
    //
    const defaultOptions = this.defaultPaginatorOptions();
    const options = await this.getValueAsync(this.paginatorOptions);
    const preparedOptions: XDataPagerPaginatorOptions = {
      ...defaultOptions,
      ...options,
      pageSize: this.query.rowsInPage,
    };

    //
    if (options) {
      //
      if (!options.first) {
        preparedOptions.first = undefined;
      } else {
        //
        const first = options.first;

        //
        if (first.disabled) {
          const firstDisabled = await this.getValueAsync(first.disabled);
          preparedOptions.first.disabled =
            defaultOptions.first.disabled && firstDisabled;
        } else {
          preparedOptions.first.disabled = defaultOptions.first.disabled;
        }

        //
        if (!first.icon) {
          preparedOptions.first.icon = defaultOptions.first.icon;
        }

        //
        if (!first.title) {
          preparedOptions.first.title = defaultOptions.first.title;
        }
      }

      //
      if (!options.last) {
        preparedOptions.last = undefined;
      } else {
        //
        const last = options.last;

        //
        if (last.disabled) {
          const lastDisabled = await this.getValueAsync(last.disabled);
          preparedOptions.last.disabled =
            defaultOptions.last.disabled && lastDisabled;
        } else {
          preparedOptions.last.disabled = defaultOptions.last.disabled;
        }

        //
        if (!last.icon) {
          preparedOptions.last.icon = defaultOptions.last.icon;
        }

        //
        if (!last.title) {
          preparedOptions.last.title = defaultOptions.last.title;
        }
      }

      //
      if (!options.next) {
        preparedOptions.next = undefined;
      } else {
        //
        const next = options.next;

        //
        if (next.disabled) {
          const nextDisabled = await this.getValueAsync(next.disabled);
          preparedOptions.next.disabled =
            defaultOptions.next.disabled && nextDisabled;
        } else {
          preparedOptions.next.disabled = defaultOptions.next.disabled;
        }

        //
        if (!next.icon) {
          preparedOptions.next.icon = defaultOptions.next.icon;
        }

        //
        if (!next.title) {
          preparedOptions.next.title = defaultOptions.next.title;
        }
      }

      //
      if (!options.prev) {
        preparedOptions.prev = undefined;
      } else {
        //
        const prev = options.prev;

        //
        if (prev.disabled) {
          const prevDisabled = await this.getValueAsync(prev.disabled);
          preparedOptions.prev.disabled =
            defaultOptions.prev.disabled && prevDisabled;
        } else {
          preparedOptions.prev.disabled = defaultOptions.prev.disabled;
        }

        //
        if (!prev.icon) {
          preparedOptions.prev.icon = defaultOptions.prev.icon;
        }

        //
        if (!prev.title) {
          preparedOptions.prev.title = defaultOptions.prev.title;
        }
      }
    }

    //
    this.PAGINATOR_OPTIONS = preparedOptions;

    //
    this.detectChanges();
    //#endregion
  }

  private async registerPaginatorHandlers() {
    //
    //#region PageHandler Actions ...
    //
    if (!this.PAGINATOR) {
      return;
    }

    //
    // Unsubscribe last ...
    if (this.PAGINATOR_PAGE_SUBSCRIPTION) {
      this.PAGINATOR_PAGE_SUBSCRIPTION.unsubscribe();
      this.PAGINATOR_PAGE_SUBSCRIPTION = null;
    }

    //
    // Subscribe New ...
    this.PAGINATOR_PAGE_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.paginator.page.asObservable()
    ).subscribe(async (res) => {
      //
      // Prepare New Query ...
      const query: XBaseDataPagerQuery = {
        ...this.query,
        //
        pageNumber: res.pageIndex + 1,
        rowsInPage: res.pageSize,
      };

      //
      // Notify Query Changed ...
      this.queryChange.emit(query);
    });
    //#endregion
  }

  private defaultPaginatorOptions() {
    //
    const options: XDataPagerPaginatorOptions = {
      pageSizes: this.config.pageSizes,
      pageSize: this.config.defaultPageSize,
      first: {
        title: 'first',
        icon: XIconNames.first,
        disabled: !this.paginator || !this.paginator.hasPreviousPage(),
      },
      last: {
        title: 'last',
        icon: XIconNames.last,
        disabled: !this.paginator || !this.paginator.hasNextPage(),
      },
      prev: {
        title: 'previous',
        icon: XIconNames.previous,
        disabled: !this.paginator || !this.paginator.hasPreviousPage(),
      },
      next: {
        title: 'next',
        icon: XIconNames.next,
        disabled: !this.paginator || !this.paginator.hasNextPage(),
      },
      pageInfoProvider: (
        locale: XLocale,
        currentPage: number,
        pageSize: number,
        totalPages: number,
        totalItems: number
      ) => {
        //
        const info = `${currentPage}/${totalPages}`;
        return applyLocale(info, locale);
      },
    };

    //
    return options;
  }

  private calculatePageCount(itemsCount: number, pageSize: number) {
    //
    // Data Correction ...
    pageSize = toNumber(pageSize);
    itemsCount = toNumber(itemsCount);

    //
    // Result ...
    return Math.floor((itemsCount + (pageSize - 1)) / pageSize);
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-pager\x-data-pager.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XEmptyModule } from '../x-empty/x-empty.module';
import { XDataPagerComponent } from './x-data-pager.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XDataPagerComponent],
  imports: [
    XCardModule,
    XIconModule,
    XEmptyModule,
    CommonModule,
    XBaseComponentModule,
  ],
  exports: [
    XIconModule,
    XCardModule,
    XEmptyModule,
    CommonModule,
    XDataPagerComponent,
    XBaseComponentModule,
  ],
})
export class XDataPagerModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-pager\x-data-pager.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XBaseDataPagerQuery,
  XColorWithBrightness,
  XBaseDataPagerQueryResult,
} from 'x-framework-core';
import {
  XCardEmptyContainer,
  XCardEmptyContainerComponent,
} from '../classes/i-x-card-empty.container';
import { map, concatMap } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XTablePaginatorOptions } from '../x-table/x-table.typings';
import { IXCardEmptyContainer } from '../classes/i-x-card-empty.container';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XDataPagerPaginatorPosition {
  Above = 'above',
  Below = 'below',
}

export type XDataPagerPaginatorPositionIdentifier =
  | XDataPagerPaginatorPosition
  | string;

// tslint:disable-next-line:no-empty-interface
export interface XDataPagerPaginatorOptions extends XTablePaginatorOptions {}

export enum XDataPagerPresentType {
  Item = 'item',
  Page = 'page',
}

export type XDataPagerPresentTypeIdentifier = XDataPagerPresentType | string;

export enum XDataPagerAction {
  GoFirst = 'go_first',
  GoLast = 'go_last',
  GoNext = 'go_next',
  GoPrev = 'go_prev',
  ToPage = 'to_page',
  ChangePageSize = 'change_page_size',
  Refresh = 'refresh',
}

export type XDataPagerActionIdentifier = XDataPagerAction | string;

export interface XDataPagerActionModel
  extends XBaseActionModel<XDataPagerActionIdentifier> {}

//
//#region Inside Card/Empty Container ...
export interface IXDataPagerContainer extends IXCardEmptyContainer {
  //
  actionProvider: Subject<XDataPagerActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  pageChanged: EventEmitter<number>;
  type: XStandardType<XDataPagerPresentTypeIdentifier>;
  itemTemplate: TemplateRef<any>;

  //
  result: XBaseDataPagerQueryResult<any>;
  task: (
    query: XBaseDataPagerQuery
  ) => Observable<XBaseDataPagerQueryResult<any>>;
  taskReady: EventEmitter<Observable<XBaseDataPagerQueryResult<any>>>;
  query: XBaseDataPagerQuery;
  queryChange: EventEmitter<XBaseDataPagerQuery>;

  //
  showPaginator: XStandardType<boolean>;
  paginatorPosition: XStandardType<XDataPagerPaginatorPositionIdentifier>;
  paginatorColor: XStandardType<XColorIdentifier>;
  paginatorOptions: XStandardType<XDataPagerPaginatorOptions>;

  //
  emptyListTemplate: TemplateRef<any>;
}

@Injectable({
  providedIn: 'root',
})
export class XDataPagerContainer
  extends XCardEmptyContainer
  implements Readonly<IXDataPagerContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider = new Subject<XDataPagerActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly pageChanged: EventEmitter<number> = new EventEmitter<number>();
  readonly type: XStandardType<XDataPagerPresentTypeIdentifier> =
    XDataPagerPresentType.Item;
  readonly itemTemplate: TemplateRef<any>;

  //
  readonly result: XBaseDataPagerQueryResult<any>;
  readonly task: (
    query: XBaseDataPagerQuery
  ) => Observable<XBaseDataPagerQueryResult<any>>;
  readonly taskReady: EventEmitter<Observable<XBaseDataPagerQueryResult<any>>> =
    new EventEmitter<Observable<XBaseDataPagerQueryResult<any>>>();
  readonly query: XBaseDataPagerQuery;
  readonly queryChange: EventEmitter<XBaseDataPagerQuery> =
    new EventEmitter<XBaseDataPagerQuery>();

  //
  readonly showPaginator: XStandardType<boolean>;
  readonly paginatorPosition: XStandardType<XDataPagerPaginatorPositionIdentifier> =
    XDataPagerPaginatorPosition.Below;
  readonly paginatorColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly paginatorOptions: XStandardType<XDataPagerPaginatorOptions>;

  //
  readonly emptyListTemplate: TemplateRef<any>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XDataPagerContainerComponent
  extends XCardEmptyContainerComponent
  implements IXDataPagerContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XDataPagerActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Output()
  pageChanged: EventEmitter<number> = new EventEmitter();

  @Input()
  type: XStandardType<XDataPagerPresentTypeIdentifier> =
    this.propertyProvider.type;

  @Input()
  itemTemplate: TemplateRef<any> = this.propertyProvider.itemTemplate;

  //
  @Input()
  result: XBaseDataPagerQueryResult<any> = this.propertyProvider.result;

  @Input()
  task: (
    query: XBaseDataPagerQuery
  ) => Observable<XBaseDataPagerQueryResult<any>> = this.propertyProvider.task;

  @Output()
  taskReady: EventEmitter<Observable<XBaseDataPagerQueryResult<any>>> =
    new EventEmitter();

  @Input()
  query: XBaseDataPagerQuery = this.propertyProvider.query;

  @Output()
  queryChange: EventEmitter<XBaseDataPagerQuery> = new EventEmitter();

  //
  @Input()
  showPaginator: XStandardType<boolean> = this.propertyProvider.showPaginator;

  @Input()
  paginatorPosition: XStandardType<XDataPagerPaginatorPositionIdentifier> =
    this.propertyProvider.paginatorPosition;

  @Input()
  paginatorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.paginatorColor;

  @Input()
  paginatorOptions: XStandardType<XDataPagerPaginatorOptions> =
    this.propertyProvider.paginatorOptions;

  //
  @Input()
  emptyListTemplate: TemplateRef<any> = this.propertyProvider.emptyListTemplate;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XDataPagerContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXDataPagerContainer
  ): Observable<IXDataPagerContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            type: this.getValue(container ? container.type : this.type),
            itemTemplate: this.getValue(
              container ? container.itemTemplate : this.itemTemplate
            ),
            result: this.getValue(container ? container.result : this.result),
            task: this.getValue(container ? container.task : this.task),
            query: this.getValue(container ? container.query : this.query),
            showPaginator: this.getValue(
              container ? container.showPaginator : this.showPaginator
            ),
            paginatorPosition: this.getValue(
              container ? container.paginatorPosition : this.paginatorPosition
            ),
            paginatorColor: this.getValue(
              container ? container.paginatorColor : this.paginatorColor
            ),
            paginatorOptions: this.getValue(
              container ? container.paginatorOptions : this.paginatorOptions
            ),

            //
            emptyListTemplate: this.getValue(
              container ? container.emptyListTemplate : this.emptyListTemplate
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            pageChanged: this.pageChanged,
            taskReady: this.taskReady,
            queryChange: this.queryChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-scroller\x-data-scroller.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <div
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    class="x-data-scroller x-wrapper ion-no-padding"
  >
    <!-- Items or Empty -->
    <ng-container
      *ngTemplateOutlet="
        items && (hasChildValue(items) | async)
          ? contentsRef
          : emptyListTemplate
          ? emptyListTemplate
          : defaultEmptyListRef
      "
    >
    </ng-container>
  </div>
</ng-template>

<!-- Default Empty Content -->
<ng-template #defaultEmptyListRef>
  <!-- Empty Handler -->
  <ng-container
    *ngIf="
      (getValue(showEmpty) | async) &&
      (!result || !(hasChildValue(result.items) | async))
    "
  >
    <x-empty
      (refresh)="emptyRefresh.emit($event)"
      [color]="getValue(emptyColor) | async"
      [hasRefresh]="getValue(emptyHasRefresh) | async"
      [message]="getResourceValue(emptyMessage) | async"
      [refreshIcon]="getValue(emptyRefreshIcon) | async"
      [messageColor]="getValue(emptyMessageColor) | async"
      [foregroundRefreshButtonColor]="
        getValue(emptyForegroundRefreshButtonColor) | async
      "
      [foregroundColor]="getValue(emptyForegroundColor) | async"
      [refreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
      [foregroundMessageColor]="getValue(emptyForegroundMessageColor) | async"
    ></x-empty> </ng-container
></ng-template>

<!-- Contents Template -->
<ng-template #contentsRef>
  <ng-content></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-scroller\x-data-scroller.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-scroller\x-data-scroller.component.ts

import {
  XDataScrollerAction,
  XDataScrollerActionModel,
  XDataScrollerContainerComponent,
} from './x-data-scroller.typings';
import { Component } from '@angular/core';
import { of, Subject, Subscription } from 'rxjs';
import { ChangeDetectionStrategy } from '@angular/core';
import { nameof, hasChild, XBaseDataPagerQuery } from 'x-framework-core';

@Component({
  selector: 'x-data-scroller',
  templateUrl: './x-data-scroller.component.html',
  styleUrls: ['./x-data-scroller.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XDataScrollerComponent extends XDataScrollerContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  canNext: boolean = true;
  items: any[] = [];
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XDataScrollerComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Query Changed ...
    const isTaskChanged = changeKeys.includes(
      nameof<XDataScrollerComponent>('task')
    );
    const isQueryChanged = changeKeys.includes(
      nameof<XDataScrollerComponent>('query')
    );
    if (isQueryChanged || isTaskChanged) {
      await this.handleQueryChanged();
    }
    //#endregion

    //
    //#region Result Changed ...
    const isResultChanged = changeKeys.includes(
      nameof<XDataScrollerComponent>('result')
    );
    if (isResultChanged) {
      await this.handleResultChanged();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Handlers ...
  //#endregion

  //
  //#region Actions ...
  async refreshItems() {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    this.items = [];
    this.result = null;
    this.canNext = true;
    this.notifyCanNext();

    //
    const query: XBaseDataPagerQuery = {
      pageNumber: 1,
      rowsInPage: this.query.rowsInPage,
    };

    //
    // Notify Query Changed ...
    this.queryChange.emit(query);
  }

  async loadMore() {
    //
    if (!this.canNext || this.uiDisabled || this.loading) {
      this.notifyCanNext();
      return;
    }

    //
    // Prepare New Query ...
    const query: XBaseDataPagerQuery = {
      ...this.query,
      //
      pageNumber: this.query.pageNumber + 1,
      rowsInPage: this.query.rowsInPage,
    };

    //
    // Notify Query Changed ...
    this.queryChange.emit(query);
  }

  async clearItems() {
    //
    this.items = [];
    this.result = null;
    this.canNext = true;
    this.notifyCanNext();

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XDataScrollerActionModel>();
    }

    //
    this.notifyCanNext();

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XDataScrollerAction.Refresh:
            this.detectChanges();
            break;

          //
          case XDataScrollerAction.LoadMore:
            await this.loadMore();
            break;

          //
          case XDataScrollerAction.RefreshItems:
            await this.refreshItems();
            break;

          //
          case XDataScrollerAction.ClearItems:
            await this.clearItems();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  private async handleQueryChanged() {
    //
    this.result = null;

    //
    if (!this.query || !this.task) {
      return;
    }

    //
    const mTask = this.task(this.query);

    //
    this.taskReady.emit(mTask);
  }

  private async handleResultChanged() {
    //
    if (!this.result || !hasChild(this.result.items)) {
      return;
    }

    //
    of(...this.result.items).subscribe({
      next: item => {
        setTimeout(() => {
          //
          this.items.push(item);
          this.detectChanges();                
        }, 100);
      },
      error: (err) => {},
      complete: () => {
        this.canNext = this.items.length < this.result.count;
        this.notifyCanNext();    
      }
    })
  }

  private notifyCanNext() {
    //
    if (!this.actionProvider) {
      return;
    }

    //
    this.actionProvider.next({
      action: XDataScrollerAction.CanNext,
      payload: this.canNext,
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-scroller\x-data-scroller.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XEmptyModule } from '../x-empty/x-empty.module';
import { XDataScrollerComponent } from './x-data-scroller.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XDataScrollerComponent],
  imports: [XCardModule, CommonModule, XEmptyModule, XBaseComponentModule],
  exports: [
    XCardModule,
    CommonModule,
    XEmptyModule,
    XBaseComponentModule,
    XDataScrollerComponent,
  ],
})
export class XDataScrollerModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-data-scroller\x-data-scroller.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XBaseDataPagerQuery,
  XBaseDataPagerQueryResult,
} from 'x-framework-core';
import {
  IXCardEmptyContainer,
  XCardEmptyContainer,
  XCardEmptyContainerComponent,
} from '../classes/i-x-card-empty.container';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XDataScrollerAction {
  Refresh = 'refresh',
  RefreshItems = 'refresh_items',
  LoadMore = 'load_more',
  ClearItems = 'clear_items',
  CanNext = 'can_next',
}

export type XDataScrollerActionIdentifier = XDataScrollerAction | string;

export interface XDataScrollerActionModel
  extends XBaseActionModel<XDataScrollerActionIdentifier> {}

//
//#region Inside Card/Empty Container ...
export interface IXDataScrollerContainer extends IXCardEmptyContainer {
  //
  actionProvider: Subject<XDataScrollerActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  result: XBaseDataPagerQueryResult<any>;
  task: (query: XBaseDataPagerQuery) => Observable<any>;
  taskReady: EventEmitter<Observable<any>>;
  query: XBaseDataPagerQuery;
  queryChange: EventEmitter<XBaseDataPagerQuery>;

  //
  emptyListTemplate: TemplateRef<any>;
}

@Injectable({
  providedIn: 'root',
})
export class XDataScrollerContainer
  extends XCardEmptyContainer
  implements Readonly<IXDataScrollerContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider = new Subject<XDataScrollerActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly result: XBaseDataPagerQueryResult<any>;
  readonly task: (query: XBaseDataPagerQuery) => Observable<any>;
  readonly taskReady = new EventEmitter<Observable<any>>();
  readonly query: XBaseDataPagerQuery;
  readonly queryChange = new EventEmitter<XBaseDataPagerQuery>();

  //
  readonly emptyListTemplate: TemplateRef<any>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XDataScrollerContainerComponent
  extends XCardEmptyContainerComponent
  implements IXDataScrollerContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XDataScrollerActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  result: XBaseDataPagerQueryResult<any> = this.propertyProvider.result;

  @Input()
  task: (query: XBaseDataPagerQuery) => Observable<any> =
    this.propertyProvider.task;

  @Output()
  taskReady: EventEmitter<Observable<any>> = new EventEmitter();

  @Input()
  query: XBaseDataPagerQuery = this.propertyProvider.query;

  @Output()
  queryChange: EventEmitter<XBaseDataPagerQuery> = new EventEmitter();

  //
  @Input()
  emptyListTemplate: TemplateRef<any>;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XDataScrollerContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXDataScrollerContainer
  ): Observable<IXDataScrollerContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            result: this.getValue(container ? container.result : this.result),
            task: this.getValue(container ? container.task : this.task),
            query: this.getValue(container ? container.query : this.query),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            emptyListTemplate: container
              ? container.emptyListTemplate
              : this.emptyListTemplate,

            //
            taskReady: this.taskReady,
            queryChange: this.queryChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <!-- Check Prepared Model Exists -->
  <div
    class="x-wrapper x-description-list"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [class.isLegacy]="getValue(isLegacy) | async"
    [class.isNotLegacy]="!(getValue(isLegacy) | async)"
    *ngIf="notNullOrUndefinedValue(preparedModel) | async"
  >
    <!-- New -->
    <ng-container *ngIf="!(getValue(isLegacy) | async)">
      <dl
        class="x-description-list-innner"
        [xColor]="getValue(color) | async"
        [isForeground]="getValue(foregroundColor) | async"
      >
        <!-- Loop of Model Keys -->
        <ng-container *ngFor="let key of keys(preparedModel)">
          <!-- Check Can Show Key -->
          <section
            class="x-description-list-item-container"
            *ngIf="canShowKey(key) | async as canShowTheKey"
          >
            <!-- Title (Key) -->
            <dt
              [isForeground]="true"
              class="x-description-list-title"
              *ngIf="
                canShowTheKey &&
                (getValue(showLabels) | async) &&
                (canShowLabelKey(key) | async)
              "
              [xColor]="getValue(labelColor) | async"
              [ngClass]="getDescriptionTitleClasses(key)"
            >
              <span class="x-title-span text-truncate">
                {{ getTitle(key) | async }}
              </span>
            </dt>

            <!-- Content (Value) -->
            <!-- if Has Not ContentItem and Descibeable -->
            <dd
              [isForeground]="true"
              class="x-description-list-description"
              [xColor]="getValue(valueColor) | async"
              [ngClass]="getDescriptionContentClasses(key)"
              *ngIf="
                !(hasContentItem(key) | async) &&
                (canDescribe(key, preparedModel[key]) | async)
              "
            >
              <x-slotter
                [hasCenterSlot]="false"
                [layout]="SlotLayouts.HORIZONTAL"
                [hasEndOrBottomSlot]="
                  notNullOrUndefinedValue(propertyActionTemplate) | async
                "
              >
                <!-- Start -->
                <x-slot
                  [name]="SlotNames.START"
                  style="overflow: hidden"
                  [cssClass]="[
                    'ion-text-start',
                    'text-truncate',
                    'x-decription-span'
                  ]"
                >
                  {{ getContent(key, preparedModel[key]) | async }}
                </x-slot>

                <!-- End -->
                <x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
                  <!-- propertyActionTemplate -->
                  <ng-container
                    *ngIf="
                      notNullOrUndefinedValue(propertyActionTemplate) | async
                    "
                  >
                    <ng-container
                      *ngTemplateOutlet="
                        propertyActionTemplate;
                        context: {
                          key: key,
                          prefix: prefix,
                          value: preparedModel[key]
                        }
                      "
                    >
                    </ng-container>
                  </ng-container>
                </x-slot>
              </x-slotter>
            </dd>

            <!-- if Has not ContentItem && not Describeable -->
            <dd
              [isForeground]="true"
              class="x-description-list-description"
              [xColor]="getValue(valueColor) | async"
              [ngClass]="getDescriptionContentClasses(key)"
              *ngIf="
                !(hasContentItem(key) | async) &&
                !(canDescribe(key, preparedModel[key]) | async)
              "
            >
              <section class="nested-container ion-margin-start">
                <ng-container
                  *ngTemplateOutlet="
                    nestedRef;
                    context: {
                      key: key,
                      value: preparedModel[key],
                      prefix: getKeyPrefix(key),
                      hiddenModelKeys: (getHiddenModelKeys(key) | async),
                      availableModelKeys: (getAvailableModelKeys(key) | async),
                      sortOrderModelKeys: (getSortOrderModelKeys(key) | async),
                      hiddenLabelModelKeys:
                        (getHiddenLabelModelKeys(key) | async)
                    }
                  "
                >
                </ng-container>
              </section>
            </dd>

            <!-- if Has ContentItem -->
            <dd
              [isForeground]="true"
              *ngIf="hasContentItem(key) | async"
              class="x-description-list-description"
              [xColor]="getValue(valueColor) | async"
              [ngClass]="getDescriptionContentClasses(key)"
            >
              <ng-container
                *ngTemplateOutlet="
                  (getContentItem(key, preparedModel[key]) | async).content
                "
              ></ng-container>
            </dd>
          </section>
        </ng-container>
      </dl>
    </ng-container>

    <!-- Legacy -->
    <ng-container *ngIf="getValue(isLegacy) | async">
      <dl
        class="x-description-list"
        [xColor]="getValue(color) | async"
        [class.inline]="isNotMobileUi$ | async"
        [isForeground]="getValue(foregroundColor) | async"
      >
        <!-- Loop of Model Keys -->
        <ng-container *ngFor="let key of keys(preparedModel)">
          <!-- Check Can Show Key -->
          <ng-container *ngIf="canShowKey(key) | async">
            <!-- Title -->
            <dt
              [isForeground]="true"
              class="x-description-list-title"
              [xColor]="getValue(labelColor) | async"
              [ngClass]="getDescriptionTitleClasses(key)"
              *ngIf="
                (getValue(showLabels) | async) && (canShowLabelKey(key) | async)
              "
            >
              {{ getTitle(key) | async }}
            </dt>

            <!-- Description -->
            <dd
              [isForeground]="true"
              class="x-description-list-description"
              [xColor]="getValue(valueColor) | async"
              [ngClass]="getDescriptionContentClasses(key)"
              [class.full-width]="hasContentItem(key) | async"
            >
              <ng-container
                *ngTemplateOutlet="
                  (hasContentItem(key) | async) ? contentItemRef : contentRef;
                  context: {
                    $implicit: key,
                    value: getContent(key, preparedModel[key]) | async
                  }
                "
              >
              </ng-container>
            </dd>
          </ng-container>
        </ng-container>
      </dl>
    </ng-container>
  </div>
</ng-template>

<!-- NestedRef -->
<ng-template
  #nestedRef
  let-key="key"
  let-value="value"
  let-prefix="prefix"
  let-hiddenModelKeys="hiddenModelKeys"
  let-availableModelKeys="availableModelKeys"
  let-sortOrderModelKeys="sortOrderModelKeys"
  let-hiddenLabelModelKeys="hiddenLabelModelKeys"
>
  <x-description-list
    [model]="value"
    [attr.keyPrefix]="prefix"
    [actionProvider]="actionProvider"
    [color]="getValue(color) | async"
    [hiddenModelKeys]="hiddenModelKeys"
    [modelKeyProvider]="modelKeyProvider"
    [availableModelKeys]="availableModelKeys"
    [sortOrderModelKeys]="sortOrderModelKeys"
    [modelValueProvider]="modelValueProvider"
    [showLabels]="getValue(showLabels) | async"
    [labelColor]="getValue(labelColor) | async"
    [valueColor]="getValue(valueColor) | async"
    [cssClass]="getArrayValue(cssClass) | async"
    [hiddenLabelModelKeys]="hiddenLabelModelKeys"
    (onModelChanged)="onModelChanged.emit($event)"
    [propertyActionTemplate]="propertyActionTemplate"
    [foregroundColor]="getValue(foregroundColor) | async"
    [hideOptionalValues]="getValue(hideOptionalValues) | async"
    *ngIf="
      (notEmptyValue(key) | async) && (notNullOrUndefinedValue(value) | async)
    "
  >
  </x-description-list>
</ng-template>

<!-- XDescriptionListItemContent Template -->
<ng-template #contentItemRef let-key let-value="value">
  <ng-container
    *ngTemplateOutlet="(getContentItem(key, value) | async).content"
  ></ng-container>
</ng-template>

<!-- XDescriptionList Default Content Template -->
<ng-template #contentRef let-key let-value="value">
  {{ value }}
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list.component.scss

:host ::ng-deep .x-description-list {
  &.isLegacy {
    .x-card {
      .x-description-list-title {
        padding: {
          left: 0;
          right: 0;
        }
      }

      .x-description-list-description:not(.full-width) {
        padding: {
          left: 0;
          right: 0;
        }
      }
    }

    .x-description-list-title {
      padding: {
        left: 4px;
        right: 4px;
      }
    }

    .x-description-list-description:not(.full-width) {
      padding: {
        left: 4px;
        right: 4px;
      }
    }

    dl {
      margin: {
        top: 0;
        bottom: 0;
      }
    }

    dl.inline dd {
      display: inline;
      margin: 0;
    }

    dl.inline dd::after {
      white-space: pre;
      content: "\a";
    }

    dl.inline dt {
      display: inline-block;
      min-width: 100px;
    }

    dt {
      margin-top: 8px;
      margin-bottom: 8px;
    }

    dt::after {
      content: ": ";
    }

    dd {
      text-align: center;

      //
      &.full-width {
        margin-inline-end: 0 !important;
        margin-inline-start: 0 !important;
      }
    }
  }

  &.isNotLegacy {
    dd {
      width: 100%;
      display: inline;
      margin-inline-start: 8px;
    }

    .x-description-list-item-container {
      width: 100%;
    }

    .x-title-span::after {
      content: ": ";
    }

    x-slotter {
      width: 100%;
      display: inline-block;
    }

    .x-title-span,
    .x-decription-span {
      max-width: calc(90%);
    }

    .x-slotter {
      .x-start-top-slot {
        flex: 1 1 auto !important;
      }

      .x-end-bottom-slot {
        flex: 1 1 0% !important;
      }
    }
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list.component.ts

import {
  Component,
  QueryList,
  ContentChildren,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  tap,
  map,
  filter,
  mergeMap,
  concatMap,
  switchMap,
} from 'rxjs/operators';
import {
  keys,
  nameof,
  isDate,
  isArray,
  toArray,
  isObject,
  isNumber,
  isString,
  toXString,
  isFunction,
  isSameObject,
  XModelProperty,
  XOneOrManyType,
  toNormalString,
  getPropertyValue,
  setPropertyValue,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XDescriptionListAction,
  XDescriptionListActionModel,
  XDescriptionListContainerComponent,
} from './x-description-list.typings';
import { XSlotLayout, XSlotName } from '../x-slotter/x-slotter.typings';
import { of, from, Subject, forkJoin, Observable, Subscription } from 'rxjs';
import { XDescriptionListItemComponent } from './x-description-list-item/x-description-list-item.component';

@Component({
  selector: 'x-description-list',
  templateUrl: './x-description-list.component.html',
  styleUrls: ['./x-description-list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XDescriptionListComponent<
  T = any
> extends XDescriptionListContainerComponent<T> {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private keyPrefix = '';
  private MODEL: T;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly SlotLayouts = Object.assign({}, XSlotLayout);

  //
  get prefix() {
    return this.keyPrefix;
  }

  get preparedModel() {
    return this.MODEL;
  }

  //
  @ContentChildren(XDescriptionListItemComponent)
  items: QueryList<XDescriptionListItemComponent>;
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.prepareModel();
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    // prepare KeyPrefix ...
    const keyPrefix = this.element.nativeElement
      ? (this.element.nativeElement as HTMLElement).getAttribute('keyPrefix')
      : '';
    if (!isNullOrEmptyString(keyPrefix)) {
      this.keyPrefix += `${keyPrefix}`;
    }
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Model Changed ...
    const isModelChanged = changeKeys.includes(
      nameof<XDescriptionListComponent>('model')
    );

    //
    if (isModelChanged) {
      await this.prepareModel();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XDescriptionListComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  keys(model: T) {
    //
    let modelKeys = keys(model);

    //
    if (!this.hasChild(this.sortOrderModelKeys)) {
      return modelKeys;
    }

    //
    const sortOrderArray = toArray(this.sortOrderModelKeys) as string[];
    const result: string[] = [
      ...sortOrderArray.filter((k) => modelKeys.includes(k)),
      ...modelKeys.filter((k) => !sortOrderArray.includes(k)),
    ];

    //
    return result;
  }

  canShowKey(key: string) {
    //
    return forkJoin({
      key: this.getValue(key),
      model: this.getValue(this.model),
      keyPrefix: this.getValue(this.keyPrefix),
      hiddenModelKeys: this.getArrayValue(this.hiddenModelKeys),
      hideOptionalValues: this.getValue(this.hideOptionalValues),
      availableModelKeys: this.getArrayValue(this.availableModelKeys),
    }).pipe(
      map((provider) => {
        //
        const value = provider.model[provider.key];

        //
        const modelHasValue =
          !isNullOrUndefined(provider.model) &&
          !isNullOrUndefined(value) &&
          !isNullOrEmptyString(toXString(provider.model[provider.key]));
        const hasChildHiddens = this.hasChild(provider.hiddenModelKeys);
        const hasChildAvailables = this.hasChild(provider.availableModelKeys);

        //
        if (!hasChildAvailables && !hasChildHiddens) {
          //
          if (!modelHasValue && provider.hideOptionalValues) {
            return false;
          }

          //
          return true;
        }

        //
        const isInHiddens = provider.hiddenModelKeys.includes(
          provider.key as any
        );
        const isInAvailables = provider.availableModelKeys.includes(
          provider.key as any
        );

        //
        if (
          (!hasChildAvailables && !isInHiddens) ||
          (isInAvailables && !hasChildHiddens) ||
          (isInAvailables && !isInHiddens) ||
          (!isInAvailables &&
            !isInHiddens &&
            isNumber(provider.key) &&
            !isNullOrEmptyString(provider.keyPrefix))
        ) {
          //
          if (!modelHasValue && provider.hideOptionalValues) {
            return false;
          }

          //
          return true;
        }

        //
        return false;
      })
    );
  }

  canShowLabelKey(key: string) {
    return this.getArrayValue(this.hiddenLabelModelKeys).pipe(
      map((hiddenLabelsModelKeys) => {
        //
        const hasChildHiddenLabels = this.hasChild(hiddenLabelsModelKeys);

        //
        if (!hasChildHiddenLabels) {
          return true;
        }

        //
        const isInHiddens = hiddenLabelsModelKeys.includes(key as any);
        return hasChildHiddenLabels && !isInHiddens;
      })
    );
  }

  getTitle(key: string): Observable<string> {
    return this.getValue(this.modelKeyProvider).pipe(
      concatMap((provider) => {
        return isNullOrUndefined(provider) || !isFunction(provider)
          ? this.getValue(key)
          : this.getValue(provider(key as any));
      })
    );
  }

  hasContentItem(key: string) {
    return of(this.items).pipe(
      concatMap((items) => {
        return isNullOrUndefined(items)
          ? of(false)
          : this.getArrayValue(items.toArray()).pipe(
              switchMap((items) =>
                from(items).pipe(
                  mergeMap((item) =>
                    this.getValue(item.name).pipe(
                      map((name) => ({ name, item }))
                    )
                  ),
                  filter(
                    (itemData) =>
                      toNormalString(itemData.name) === toNormalString(key)
                  ),
                  map((dataObject) => !isNullOrUndefined(dataObject))
                )
              )
            );
      })
    );
  }

  getContentItem(
    key: string,
    value: any
  ): Observable<XDescriptionListItemComponent> {
    return this.hasContentItem(key).pipe(
      concatMap((isExists) => {
        return !isExists
          ? of(null)
          : of(this.items).pipe(
              concatMap((items) => {
                return isNullOrUndefined(items)
                  ? of(null)
                  : this.getArrayValue(items.toArray()).pipe(
                      switchMap((items) =>
                        from(items).pipe(
                          mergeMap((item) =>
                            this.getValue(item.name).pipe(
                              map((name) => ({ name, item }))
                            )
                          ),
                          filter(
                            (itemData) =>
                              toNormalString(itemData.name) ===
                              toNormalString(key)
                          ),
                          tap((dataObject) => (dataObject.item.value = value)),
                          map((dataObject) => dataObject.item)
                        )
                      )
                    );
              })
            );
      })
    );
  }

  getContent(key: string, value: any): Observable<string> {
    return this.getValue(this.modelValueProvider).pipe(
      concatMap((provider) => {
        return isNullOrUndefined(provider) || !isFunction(provider)
          ? this.getValue(value)
          : this.getValue(provider(key as any, value));
      })
    );
  }

  canDescribe(key: string, value: any) {
    return of(value).pipe(
      map(
        (eVal) =>
          isDate(eVal) ||
          isFunction(eVal) ||
          (!isArray(eVal) && !isObject(eVal))
      )
    );
  }

  getDescriptionTitleClasses(key: string) {
    return [`x-description-${key}-title`];
  }

  getDescriptionContentClasses(key: string) {
    return [`x-description-${key}-title`];
  }

  //
  //#region Neste ...
  getKeyPrefix(key: string) {
    //
    const result = isNullOrEmptyString(this.keyPrefix)
      ? `${key}`
      : `${this.keyPrefix}.${key}`;

    //
    return result;
  }

  getHiddenModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.hiddenModelKeys);
  }

  getSortOrderModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.sortOrderModelKeys);
  }

  getAvailableModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.availableModelKeys);
  }

  getHiddenLabelModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.hiddenLabelModelKeys);
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  //#endregion

  //
  //#region Actions ...
  async setModelProperty(property: XModelProperty) {
    //
    if (!property || isNullOrEmptyString(property.key)) {
      return;
    }

    //
    const model = this.MODEL;
    const nodeValue = getPropertyValue(model, property.prefix);
    const modelKeys = isNullOrUndefined(nodeValue) ? [] : keys(nodeValue);
    if (!this.hasChild(modelKeys)) {
      return;
    }

    //
    const isExists = modelKeys.includes(property.key as string);
    if (!isExists) {
      return;
    }

    //
    // normalize value ...
    const normalValue = isString(property.value)
      ? !isNullOrEmptyString(property.value) &&
        !isNullOrUndefined(property.value)
        ? property.value.toString().trim()
        : undefined
      : isNullOrUndefined(property.value)
      ? undefined
      : property.value;

    //
    if (nodeValue[property.key] === normalValue) {
      return;
    }

    //
    const setterPropertyPath = isNullOrEmptyString(property.prefix)
      ? `${property.key as string}`
      : `${property.prefix}.${property.key as string}`;

    //
    setPropertyValue(model, setterPropertyPath, normalValue);

    //
    this.MODEL = {
      ...model,
    };
    this.detectChanges();
    this.onModelChanged.emit(model);
  }

  async getModelProperty(key: keyof T | string) {
    //
    if (isNullOrEmptyString(key)) {
      return null;
    }

    //
    const model = await this.getValueAsync(this.model);
    const modelKeys = await this.getValueAsync(this.keys(model));
    if (!this.hasChild(modelKeys)) {
      return null;
    }

    //
    const isExists = modelKeys.includes(key);
    if (!isExists) {
      return null;
    }

    //
    const result = model[key as any];
    return result;
  }

  async getModelViewProperty(key: keyof T | string) {
    //
    if (isNullOrEmptyString(key)) {
      return null;
    }

    //
    const model = await this.getValueAsync(this.model);
    const modelKeys = await this.getValueAsync(this.keys(model));
    if (!this.hasChild(modelKeys)) {
      return null;
    }

    //
    const isExists = modelKeys.includes(key);
    if (!isExists) {
      return null;
    }

    //
    const result = await this.getValueAsync(
      this.getContent(key, model[key as any])
    );
    return result;
  }

  async setModel(model: T) {
    //
    if (!model || isSameObject(model as any, this.MODEL as any)) {
      return;
    }

    //
    this.model = {
      ...model,
    };
    this.MODEL = {
      ...model,
    };

    //
    await this.prepareModel();
    this.onModelChanged.emit(model);
  }

  async getModel() {
    //
    const result = await this.getValueAsync(this.model);
    return result;
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XDescriptionListActionModel>();
    }

    //
    let modelProperty: XModelProperty = null;

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XDescriptionListAction.Refresh:
            this.detectChanges();
            break;

          //
          case XDescriptionListAction.ClearProperty:
            //
            modelProperty = model.payload as XModelProperty;
            if (
              !model.payload ||
              isNullOrUndefined(model) ||
              isNullOrUndefined(model.payload) ||
              isNullOrEmptyString(model.action)
            ) {
              return;
            }

            //
            modelProperty.value = null;

            //
            await this.setModelProperty(modelProperty);
            break;

          //
          case XDescriptionListAction.SetProperty:
            //
            modelProperty = model.payload as XModelProperty;
            if (
              !model.payload ||
              isNullOrUndefined(model) ||
              isNullOrUndefined(model.payload) ||
              isNullOrEmptyString(model.action)
            ) {
              return;
            }

            //
            await this.setModelProperty(modelProperty);
            break;

          //
          case XDescriptionListAction.SetModel:
            //
            const resetModel = model.payload as T;
            if (!model || !model.payload || !resetModel) {
              return;
            }

            //
            await this.setModel(resetModel);
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async prepareModel() {
    //
    this.MODEL = null;
    this.detectChanges();

    //
    if (!this.model) {
      return;
    }

    //
    this.MODEL = await this.getValueAsync(this.model);
    this.detectChanges();
  }

  private prepareNestedCollection(
    key: string,
    collection: XOneOrManyType<string>
  ) {
    //
    return forkJoin({
      key: this.getValue(key),
      keyPrefix: this.getValue(this.getKeyPrefix(key)),
      model: this.getValue(this.preparedModel[key]),
      collection: this.getArrayValue(collection),
    }).pipe(
      map((provider) => {
        //
        const allAvailableCollectionItems = provider.collection;

        //
        const keySelector = isNullOrUndefined(provider.keyPrefix)
          ? `${provider.key}.`
          : `${provider.keyPrefix}.`;

        //
        const canFilter =
          !isNumber(provider.key) &&
          !provider.keyPrefix.includes(`${provider.key}.`);
        const result = canFilter
          ? allAvailableCollectionItems
              .filter((k) => k.includes(`${provider.key}.`))
              .map((k) => k.replace(`${provider.key}.`, ''))
          : allAvailableCollectionItems;

        //
        return result;
      })
    );
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XDescriptionListComponent } from './x-description-list.component';
import { XDescriptionListItemComponent } from './x-description-list-item/x-description-list-item.component';

@NgModule({
  declarations: [XDescriptionListComponent, XDescriptionListItemComponent],
  imports: [XCardModule, CommonModule, XSlotterModule, XBaseComponentModule],
  exports: [
    XCardModule,
    CommonModule,
    XSlotterModule,
    XBaseComponentModule,
    XDescriptionListComponent,
    XDescriptionListItemComponent,
  ],
})
export class XDescriptionListModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XModelFieldsType,
  XModelKeyProvider,
  XModelValueProvider,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XDescriptionListAction {
  //
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',

  //
  ClearProperty = 'clear_property',
  SetProperty = 'set_property',

  //
  SetModel = 'set_model',

  //
  Refresh = 'refresh',
}

export type XDescriptionListActionIdentifier = XDescriptionListAction | string;

export interface XDescriptionListActionModel
  extends XBaseActionModel<XDescriptionListActionIdentifier> {}

//
//#region Inside Card Container ...
export interface IXDescriptionListContainer<T = any>
  extends IXComponentCardContainer {
  //
  actionProvider: Subject<XDescriptionListActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  isLegacy: XStandardType<boolean>;
  showLabels: XStandardType<boolean>;
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;
  labelColor: XStandardType<XColorIdentifier>;
  valueColor: XStandardType<XColorIdentifier>;
  propertyActionTemplate: TemplateRef<any>;
  onModelChanged: EventEmitter<T>;

  //
  model: XStandardType<T>;

  //
  //#region Model Providers ...
  hideOptionalValues: XStandardType<boolean>;
  availableModelKeys: XModelFieldsType<T>;
  hiddenModelKeys: XModelFieldsType<T>;
  hiddenLabelModelKeys: XModelFieldsType<T>;
  sortOrderModelKeys: XModelFieldsType<T>;
  modelKeyProvider: XModelKeyProvider<T>;
  modelValueProvider: XModelValueProvider<T>;
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
export class XDescriptionListContainer<T = any>
  extends XComponentCardContainer
  implements Readonly<IXDescriptionListContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XDescriptionListActionModel> =
    new Subject<XDescriptionListActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly isLegacy: XStandardType<boolean> = true;
  readonly showLabels: XStandardType<boolean> = false;
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;
  readonly labelColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.TertiaryShade;
  readonly valueColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly propertyActionTemplate: TemplateRef<any>;

  //
  readonly onModelChanged: EventEmitter<T>;

  //
  readonly model: XStandardType<T>;

  //
  //#region Model Providers ...
  readonly hideOptionalValues: XStandardType<boolean>;
  readonly availableModelKeys: XModelFieldsType<T>;
  readonly hiddenModelKeys: XModelFieldsType<T>;
  readonly hiddenLabelModelKeys: XModelFieldsType<T>;
  readonly sortOrderModelKeys: XModelFieldsType<T>;
  readonly modelKeyProvider: XModelKeyProvider<T>;
  readonly modelValueProvider: XModelValueProvider<T>;
  //#endregion
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XDescriptionListContainerComponent<T = any>
  extends XComponentCardContainerComponent
  implements IXDescriptionListContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XDescriptionListActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  isLegacy: XStandardType<boolean> = this.propertyProvider.isLegacy;

  @Input()
  showLabels: XStandardType<boolean> = this.propertyProvider.showLabels;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  @Input()
  labelColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.labelColor;

  @Input()
  valueColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.valueColor;

  @Input()
  propertyActionTemplate: TemplateRef<any> =
    this.propertyProvider.propertyActionTemplate;

  @Output()
  onModelChanged: EventEmitter<T> = new EventEmitter();

  //
  @Input()
  model: XStandardType<T> = this.propertyProvider.model;

  //
  //#region Model Providers ...
  @Input()
  hideOptionalValues: XStandardType<boolean> =
    this.propertyProvider.hideOptionalValues;

  @Input()
  availableModelKeys: XModelFieldsType<T> =
    this.propertyProvider.availableModelKeys;

  @Input()
  hiddenModelKeys: XModelFieldsType<T> = this.propertyProvider.hiddenModelKeys;

  @Input()
  hiddenLabelModelKeys: XModelFieldsType<T> =
    this.propertyProvider.hiddenLabelModelKeys;

  @Input()
  sortOrderModelKeys: XModelFieldsType<T> =
    this.propertyProvider.sortOrderModelKeys;

  @Input()
  modelKeyProvider: XModelKeyProvider<T> =
    this.propertyProvider.modelKeyProvider;

  @Input()
  modelValueProvider: XModelValueProvider<T> =
    this.propertyProvider.modelValueProvider;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XDescriptionListContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXDescriptionListContainer
  ): Observable<IXDescriptionListContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            isLegacy: this.getValue(
              container ? container.isLegacy : this.isLegacy
            ),
            showLabels: this.getValue(
              container ? container.showLabels : this.showLabels
            ),
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            labelColor: this.getValue(
              container ? container.labelColor : this.labelColor
            ),
            valueColor: this.getValue(
              container ? container.valueColor : this.valueColor
            ),
            propertyActionTemplate: this.getValue(
              container
                ? container.propertyActionTemplate
                : this.propertyActionTemplate
            ),
            model: this.getValue(container ? container.model : this.model),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            //#region Model Providers ...
            hideOptionalValues: container
              ? container.hideOptionalValues
              : this.hideOptionalValues,
            availableModelKeys: container
              ? container.availableModelKeys
              : this.availableModelKeys,
            hiddenModelKeys: container
              ? container.hiddenModelKeys
              : this.hiddenModelKeys,
            hiddenLabelModelKeys: container
              ? container.hiddenLabelModelKeys
              : this.hiddenLabelModelKeys,
            sortOrderModelKeys: container
              ? container.sortOrderModelKeys
              : this.sortOrderModelKeys,
            modelKeyProvider: container
              ? container.modelKeyProvider
              : this.modelKeyProvider,
            modelValueProvider: container
              ? container.modelValueProvider
              : this.modelValueProvider,
            //#endregion

            //
            onModelChanged: this.onModelChanged,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list-item\x-description-list-item.component.html

<ng-template #content>
  <ng-content></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list-item\x-description-list-item.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list-item\x-description-list-item.component.ts

import { nameof } from 'x-framework-core';
import {
  XDescriptionListItemAction,
  XDescriptionListItemActionModel,
  XDescriptionListItemContainerComponent,
} from './x-description-list-item.typings';
import { Subject, Subscription } from 'rxjs';
import { Component, ViewChild, TemplateRef } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-description-list-item',
  templateUrl: './x-description-list-item.component.html',
  styleUrls: ['./x-description-list-item.component.scss'],
})
export class XDescriptionListItemComponent extends XDescriptionListItemContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  @ViewChild('content', { static: false })
  content: TemplateRef<any>;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XDescriptionListItemComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XDescriptionListItemActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XDescriptionListItemAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-description-list\x-description-list-item\x-description-list-item.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../../classes/i-x-base.container';
import { map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XOneOrManyType, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XDescriptionListItemAction {
  Refresh = 'refresh',
}
export type XDescriptionListItemActionIdentifier =
  | XDescriptionListItemAction
  | string;

export interface XDescriptionListItemActionModel
  extends XBaseActionModel<XDescriptionListItemActionIdentifier> {}

//
//#region Container ...
export interface IXDescriptionListItemContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XDescriptionListItemActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  name: XStandardType<string>;
  value: XStandardType<any>;
}

@Injectable({
  providedIn: 'root'
})
export class XDescriptionListItemContainer
  extends XBaseContainer
  implements Readonly<IXDescriptionListItemContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XDescriptionListItemActionModel> =
    new Subject<XDescriptionListItemActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly name: XStandardType<string>;
  readonly value: XStandardType<any>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XDescriptionListItemContainerComponent
  extends XBaseContainerComponent
  implements IXDescriptionListItemContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XDescriptionListItemActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  name: XStandardType<string> = this.propertyProvider.name;

  @Input()
  value: XStandardType<any> = this.propertyProvider.value;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XDescriptionListItemContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXDescriptionListItemContainer
  ): Observable<IXDescriptionListItemContainer> {
    return forkJoin({
      //
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),

      //
      name: this.getValue(container ? container.name : this.name),
      value: this.getValue(container ? container.value : this.value),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-empty\x-empty.component.html

<div
  [xColor]="getValue(color) | async"
  [class.mobile]="isMobileUi$ | async"
  class="x-empty x-wrapper ion-text-center"
  [ngClass]="getArrayValue(cssClass) | async"
  [isForeground]="getValue(foregroundColor) | async"
>
  <h3
    class="x-empty-title"
    [xColor]="getValue(messageColor) | async"
    [isForeground]="getValue(foregroundMessageColor) | async"
  >
    {{ getResourceValue(message) | async }}
  </h3>
  <x-button
    class="x-refresh"
    (clicked)="refresh.emit()"
    [type]="ButtonTypes.Icon"
    *ngIf="getValue(hasRefresh) | async"
    [color]="getValue(refreshButtonColor) | async"
    [foregroundColor]="getValue(foregroundRefreshButtonColor) | async"
  >
    <x-icon
      [foregroundColor]="true"
      [name]="getValue(refreshIcon) | async"
      [color]="getValue(refreshButtonColor) | async"
    ></x-icon>
  </x-button>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-empty\x-empty.component.scss

:host {
--padding: 6px;
}

.x-empty {
  padding: var(--padding);
}

.x-empty-title {
  padding-bottom: 6px;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-empty\x-empty.component.ts

import {
  XEmptyAction,
  XEmptyActionModel,
  XEmptyContainerComponent,
} from './x-empty.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';
import { XButtonType } from '../x-button/x-button.typings';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-empty',
  templateUrl: './x-empty.component.html',
  styleUrls: ['./x-empty.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XEmptyComponent extends XEmptyContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly ButtonTypes = Object.assign({}, XButtonType);
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Action Provider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XEmptyComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XEmptyActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XEmptyAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-empty\x-empty.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XEmptyComponent } from './x-empty.component';
import { XIconModule } from '../x-icon/x-icon.module';
import { XButtonModule } from '../x-button/x-button.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XEmptyComponent],
  imports: [XIconModule, XButtonModule, CommonModule, XBaseComponentModule],
  exports: [XIconModule, XButtonModule, XEmptyComponent, XBaseComponentModule],
})
export class XEmptyModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-empty\x-empty.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseComponentContainer,
  XBaseContainerComponent,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../classes/i-x-base.container';
import { map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XIconIdentifier, XIconNames } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XEmptyAction {
  Refresh = 'refresh',
}

export type XEmptyActionIdentifier = XEmptyAction | string;

export interface XEmptyActionModel
  extends XBaseActionModel<XEmptyActionIdentifier> {}

//
//#region Container ...
export interface IXEmptyContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XEmptyActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  message: XStandardType<XResourceIdentifier>;
  messageColor: XStandardType<XColorIdentifier>;
  foregroundMessageColor: XStandardType<boolean>;

  //
  hasRefresh: XStandardType<boolean>;

  //
  refreshButtonColor: XStandardType<XColorIdentifier>;
  foregroundRefreshButtonColor: XStandardType<boolean>;

  //
  refreshIcon: XStandardType<XIconIdentifier>;

  //
  refresh: EventEmitter<void>;
}

@Injectable({
  providedIn: 'root',
})
export class XEmptyContainer
  extends XBaseContainer
  implements Readonly<IXEmptyContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XEmptyActionModel> =
    new Subject<XEmptyActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly messageColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly foregroundMessageColor: XStandardType<boolean> = true;

  //
  readonly hasRefresh: XStandardType<boolean> = true;

  //
  readonly refreshButtonColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly foregroundRefreshButtonColor: XStandardType<boolean> = true;
  readonly message: XStandardType<XResourceIdentifier> = XResourceIDs.no_item;

  //
  readonly refreshIcon: XStandardType<XIconIdentifier> = XIconNames.refresh;

  //
  readonly refresh: EventEmitter<void> = new EventEmitter<void>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XEmptyContainerComponent
  extends XBaseContainerComponent
  implements IXEmptyContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  message: XStandardType<XResourceIdentifier> = this.propertyProvider.message;

  @Input()
  messageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.messageColor;

  @Input()
  foregroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.foregroundMessageColor;

  //
  @Input()
  hasRefresh: XStandardType<boolean> = this.propertyProvider.hasRefresh;

  //
  @Input()
  refreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.refreshButtonColor;

  @Input()
  foregroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.foregroundRefreshButtonColor;

  //
  @Input()
  refreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.refreshIcon;

  @Output()
  refresh: EventEmitter<void> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XEmptyContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXEmptyContainer
  ): Observable<IXEmptyContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),

      //
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),

      //
      messageColor: this.getValue(
        container ? container.messageColor : this.messageColor
      ),
      foregroundMessageColor: this.getValue(
        container
          ? container.foregroundMessageColor
          : this.foregroundMessageColor
      ),

      //
      hasRefresh: this.getValue(
        container ? container.hasRefresh : this.hasRefresh
      ),
      refreshButtonColor: this.getValue(
        container ? container.refreshButtonColor : this.refreshButtonColor
      ),
      foregroundRefreshButtonColor: this.getValue(
        container
          ? container.foregroundRefreshButtonColor
          : this.foregroundRefreshButtonColor
      ),
      refreshIcon: this.getValue(
        container ? container.refreshIcon : this.refreshIcon
      ),

      //
      message: this.getValue(container ? container.message : this.message),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          refresh: this.refresh,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentEmptyContainer = {
  [TKey in keyof IXEmptyContainer as `empty${Capitalize<
    string & TKey
  >}`]: IXEmptyContainer[TKey];
};

export interface IXComponentEmptyContainer
  extends TXComponentEmptyContainer,
    IXBaseComponentContainer {
  showEmpty: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentEmptyContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentEmptyContainer>
{
  //
  //#region Props ...
  //
  readonly showEmpty: XStandardType<boolean> = false;

  //
  readonly emptyActionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.actionProvider;
  readonly emptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly emptyColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;
  readonly emptyForegroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  readonly emptyMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.message;
  readonly emptyMessageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.messageColor;
  readonly emptyForegroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.foregroundMessageColor;

  //
  readonly emptyHasRefresh: XStandardType<boolean> =
    this.propertyProvider.hasRefresh;

  //
  readonly emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.refreshButtonColor;
  readonly emptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.foregroundRefreshButtonColor;

  //
  readonly emptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.refreshIcon;

  //
  readonly emptyRefresh: EventEmitter<void> = this.propertyProvider.refresh;
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XEmptyContainer) {
    super();
  }
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
@Component({
  template: ``,
})
export abstract class XComponentEmptyContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentEmptyContainer
{
  //
  //#region Props ...
  //
  @Input()
  showEmpty: XStandardType<boolean> = this.propertyProvider.showEmpty;

  //
  @Input()
  emptyActionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.emptyActionProvider;

  @Input()
  emptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.emptyCssClass;

  //
  @Input()
  emptyColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyColor;

  @Input()
  emptyForegroundColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundColor;

  //
  @Input()
  emptyMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.emptyMessage;

  @Input()
  emptyMessageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyMessageColor;

  @Input()
  emptyForegroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundMessageColor;

  //
  @Input()
  emptyHasRefresh: XStandardType<boolean> =
    this.propertyProvider.emptyHasRefresh;

  //
  @Input()
  emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyRefreshButtonColor;

  @Input()
  emptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundRefreshButtonColor;

  //
  @Input()
  emptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.emptyRefreshIcon;

  //
  @Output()
  emptyRefresh: EventEmitter<void> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentEmptyContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentEmptyContainer
  ): Observable<IXComponentEmptyContainer> {
    return forkJoin({
      //
      showEmpty: this.getValue(
        container ? container.showEmpty : this.showEmpty
      ),
      emptyCssClass: this.getValue(
        container ? container.emptyCssClass : this.emptyCssClass
      ),
      emptyColor: this.getValue(
        container ? container.emptyColor : this.emptyColor
      ),
      emptyForegroundColor: this.getValue(
        container ? container.emptyForegroundColor : this.emptyForegroundColor
      ),
      emptyMessage: this.getValue(
        container ? container.emptyMessage : this.emptyMessage
      ),
      emptyMessageColor: this.getValue(
        container ? container.emptyMessageColor : this.emptyMessageColor
      ),
      emptyForegroundMessageColor: this.getValue(
        container
          ? container.emptyForegroundMessageColor
          : this.emptyForegroundMessageColor
      ),
      emptyHasRefresh: this.getValue(
        container ? container.emptyHasRefresh : this.emptyHasRefresh
      ),
      emptyRefreshButtonColor: this.getValue(
        container
          ? container.emptyRefreshButtonColor
          : this.emptyRefreshButtonColor
      ),
      emptyForegroundRefreshButtonColor: this.getValue(
        container
          ? container.emptyForegroundRefreshButtonColor
          : this.emptyForegroundRefreshButtonColor
      ),
      emptyRefreshIcon: this.getValue(
        container ? container.emptyRefreshIcon : this.emptyRefreshIcon
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          emptyActionProvider: container
            ? container.emptyActionProvider
            : this.emptyActionProvider,

          //
          emptyRefresh: this.emptyRefresh,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-list\x-expandable-list.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else listRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="listRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- List Ref -->
<ng-template #listRef>
  <ion-list
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    class="x-expandable-list x-wrapper ion-no-padding"
  >
    <ng-container *ngFor="let item of getArrayValue(items) | async">
      <ng-container
        *ngTemplateOutlet="
          parentPresenterRef;
          context: { $implicit: item, siblings: getArrayValue(items) | async }
        "
      >
      </ng-container>
    </ng-container>
  </ion-list>
</ng-template>

<!-- Parent Ref -->
<ng-template #parentPresenterRef let-item let-siblings="siblings">
  <ion-item-group
    button="true"
    xStopClickPropagation
    class="expandable-item-parent"
    [ngClass]="{ active: isExpanded(item) | async }"
  >
    <ion-item-divider
      (click)="toggle(item, siblings)"
      class="item-parent ion-activatable"
      [class.active-parent]="isParentActivated(item) | async"
      [matTooltip]="getResourceValue(item.description) | async"
    >
      <ion-ripple-effect></ion-ripple-effect>
      <ng-container
        *ngTemplateOutlet="
          parentTemplate ? parentTemplate : defaultParentRef;
          context: { $implicit: item }
        "
      ></ng-container>
      <x-icon
        slot="end"
        [color]="'cb-inherit'"
        [name]="getIconName(item) | async"
        [class.invisible]="
          !(getValue(showParentDetailIcon) | async) ||
          !(hasChildItems(item) | async)
        "
      ></x-icon>
    </ion-item-divider>

    <!-- Childs -->
    <ng-container
      *ngTemplateOutlet="childsPresenterRef; context: { $implicit: item }"
    >
    </ng-container>
  </ion-item-group>
</ng-template>

<!-- Children Ref -->
<ng-template #childsPresenterRef let-item>
  <ng-container *ngFor="let child of getValue(item.childs) | async">
    <ng-container *ngIf="!(hasChildItems(child) | async)">
      <ion-item
        button="true"
        class="item-child"
        xStopClickPropagation
        [class.ion-hide]="isHidden(child) | async"
        [ngClass]="getValue(itemCssClass) | async"
        (click)="handleItemSelectedAction(child, item)"
        [class.collapsed]="!(isExpanded(item) | async)"
        [detail]="getValue(showChildDetailIcon) | async"
        [class.active-child]="isChildActivated(child) | async"
        [matTooltip]="getResourceValue(child.description) | async"
        [disabled]="loading || uiDisabled || (isDisabled(child) | async)"
      >
        <ng-container
          *ngTemplateOutlet="
            childTemplate ? childTemplate : defaultChildRef;
            context: { $implicit: child }
          "
        >
        </ng-container>
      </ion-item>
    </ng-container>

    <ng-container *ngIf="hasChildItems(child) | async">
      <ion-item
        [class.ion-hide]="isHidden(child) | async"
        [class.collapsed]="!(isExpanded(item) | async)"
        class="item-child ion-no-padding ion-no-margin"
      >
        <ng-container
          *ngTemplateOutlet="
            parentPresenterRef;
            context: {
              $implicit: child,
              siblings: getValue(item.childs) | async
            }
          "
        >
        </ng-container>
      </ion-item>
    </ng-container>
  </ng-container>
</ng-template>

<!-- Default Parent Item -->
<ng-template #defaultParentRef let-item>
  <h4>{{ getResourceValue(item.title) | async }}</h4>
</ng-template>

<!-- Default Child Item -->
<ng-template #defaultChildRef let-item>
  <h5>{{ getResourceValue(item.title) | async }}</h5>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-list\x-expandable-list.component.scss

:host ::ng-deep {
  //
  --divider-color: var(--x-color-dark);

  //
  --child-color: var(--x-color-primary-shade-contrast);
  --child-background-color: var(--x-color-primary-shade);

  //
  --child-active-color: var(--x-color-dark-tint-contrast);
  --child-active-background-color: var(--x-color-dark-tint);

  //
  --highlight-color: var(--x-color-warning-contrast);
  --highlight-background-color: var(--x-color-warning);

  //
  --parent-color: var(--x-color-tertiary-tint-contrast);
  --parent-background-color: var(--x-color-tertiary-tint);

  //
  --parent-active-color: var(--x-color-dark-contrast);
  --parent-active-background-color: var(--x-color-dark);
}

:host ::ng-deep .invisible {
  display: none;
  visibility: hidden;
}

ion-list,
ion-item,
ion-item-group {
  width: 100%;
  --background: transparent;
  --inner-padding-end: 0px;
  --inner-padding-start: 0px;
  --color: var(--child-color);
  --border-color: var(--divider-color);
}

ion-item-divider {
  width: 100%;
  position: relative;
  --border-color: var(--divider-color);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
}

.item-parent {
  //
  width: 100%;
  padding-top: 0;
  padding-bottom: 0;
  position: relative;

  //
  color: var(--parent-color);
  background-color: var(--parent-background-color);

  //
  *,
  ion-icon,
  x-icon {
    color: var(--parent-color) !important;
  }
}

.item-child {
  padding-top: 0;
  padding-bottom: 0;
  display: block !important;
  transition: opacity 0.2s ease-in;
  color: var(--child-color) !important;
  background-color: var(--child-background-color) !important;

  //
  *,
  ion-icon,
  x-icon {
    color: var(--child-color) !important;
  }
}

.item-child:hover,
.item-parent:hover {
  color: var(--highlight-color) !important;
  --color: var(--highlight-color) !important;
  background-color: var(--highlight-background-color) !important;

  //
  *,
  ion-icon,
  x-icon {
    color: var(--highlight-color) !important;
  }
}

.item-parent.active-link,
.active-parent {
  //
  color: var(--parent-active-color) !important;
  background-color: var(--parent-active-background-color) !important;

  //
  *,
  ion-icon,
  x-icon {
    color: var(--parent-active-color) !important;
  }
}

.item-child.active-link,
.active-child {
  //
  color: var(--child-active-color) !important;
  background-color: var(--child-active-background-color) !important;

  //
  *,
  ion-icon,
  x-icon {
    color: var(--child-active-color) !important;
  }
}

ion-icon {
  &.invisible {
    visibility: hidden;
  }
}

.collapsed {
  opacity: 0;
  height: 0;
}

.ion-hide {
  display: none !important;
}

x-card {
  width: inherit;
  height: inherit;
}

.item-parent,
.expandable-item-parent {
  //
  cursor: pointer;

  //
  min-height: 48px;

  //
  &.active {
    color: var(--parent-active-color);
    background-color: var(--parent-active-background-color);
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-list\x-expandable-list.component.ts

import {
  nameof,
  toArray,
  hasChild,
  parseXColor,
  XOneOrManyType,
  toNormalString,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  findItemById,
  findItemByRoute,
  parentItemsById,
  parentItemsByRoute,
  containsItemsByRoute,
} from './x-expandable-list.tools';
import {
  XExpandableListItem,
  XExpandableListAction,
  XExpandableListCssVarKeys,
  XExpandableListActionModel,
  XExpandableListStartupState,
  XExpandableListContainerComponent,
} from './x-expandable-list.typings';
import { concatMap, map } from 'rxjs/operators';
import { XIconNames } from '../x-icon/x-icon.typings';
import { forkJoin, of, Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-expandable-list',
  templateUrl: './x-expandable-list.component.html',
  styleUrls: ['./x-expandable-list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XExpandableListComponent extends XExpandableListContainerComponent {
  //
  //#region Props ...
  //
  private ACTIVATED_CHILD: XExpandableListItem<any>;
  private ACTIVATED_PARENTS: XExpandableListItem<any>[];

  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycle ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Item Changed ...
    const isItemsChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('items')
    );
    if (isItemsChanged) {
      await this.handleStartupState();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Color Change Detectors ...
    const isChildColorChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('childColor')
    );
    if (isChildColorChanged) {
      await this.applyChildColor();
    }

    const isChildActiveColorChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('childActiveColor')
    );
    if (isChildActiveColorChanged) {
      await this.applyChildActiveColor();
    }

    const isParentColorChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('parentColor')
    );
    if (isParentColorChanged) {
      await this.applyParentColor();
    }

    const isParentActiveColorChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('parentActiveColor')
    );
    if (isParentActiveColorChanged) {
      await this.applyParentActiveColor();
    }

    const isHighlightColorChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('highlightColor')
    );
    if (isHighlightColorChanged) {
      await this.applyHighlightColor();
    }

    const isDividerColorChanged = changeKeys.includes(
      nameof<XExpandableListComponent>('dividerColor')
    );
    if (isDividerColorChanged) {
      await this.applyDividerColor();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Provider Actions ...
  hasChildItems(item: XExpandableListItem<any>) {
    //
    if (!item || !item.childs) {
      return of(false);
    }

    return this.getValue(item.childs).pipe(map((childs) => hasChild(childs)));
  }

  getIconName(item: XExpandableListItem<any>) {
    return this.isExpanded(item).pipe(
      map((isExpanded) =>
        isExpanded
          ? XIconNames.arrow_dropup.toString()
          : XIconNames.arrow_dropdown.toString()
      )
    );
  }

  isParentActivated(item: XExpandableListItem<any>) {
    //
    return this.getValue(item).pipe(
      concatMap((pItem) =>
        this.getValue(this.supportRouting).pipe(
          map((supportRouting) => {
            return { item: pItem, supportRouting };
          })
        )
      ),
      concatMap((dObject) => {
        //
        if (!dObject || !dObject.item) {
          return this.getValue(false);
        }

        //
        return this.getArrayValue(dObject.item.href).pipe(
          map((hrefArray) => this.managerService.mergeRoutes(hrefArray)),
          concatMap((route) => {
            //
            if (!this.ACTIVATED_PARENTS || !hasChild(this.ACTIVATED_PARENTS)) {
              return this.getValue(false);
            }

            //
            if (dObject.supportRouting) {
              //
              const routes$ = forkJoin(
                this.ACTIVATED_PARENTS.map((i) =>
                  this.getArrayValue(i.href).pipe(
                    map((href) => this.managerService.mergeRoutes(href))
                  )
                )
              );

              //
              return routes$.pipe(
                map(
                  (routes) =>
                    routes.findIndex(
                      (r) => toNormalString(r) === toNormalString(route)
                    ) > -1
                )
              );
            } else {
              //
              const ids$ = forkJoin(this.ACTIVATED_PARENTS.map((i) => i.id));

              //
              return ids$.pipe(
                map(
                  (ids) =>
                    ids.findIndex(
                      (i) =>
                        toNormalString(i) === toNormalString(dObject.item.id)
                    ) > -1
                )
              );
            }
          })
        );
      })
    );
  }

  isChildActivated(item: XExpandableListItem<any>) {
    //
    return this.getValue(item).pipe(
      concatMap((pItem) =>
        this.getValue(this.supportRouting).pipe(
          map((supportRouting) => {
            return { item: pItem, supportRouting };
          })
        )
      ),
      concatMap((dObject) => {
        //
        if (!dObject || !dObject.item || !this.ACTIVATED_CHILD) {
          return this.getValue(false);
        }

        //
        return this.getArrayValue(this.ACTIVATED_CHILD.href)
          .pipe(map((hrefArray) => this.managerService.mergeRoutes(hrefArray)))
          .pipe(
            concatMap((activatedRoute) => {
              return this.getArrayValue(dObject.item.href)
                .pipe(
                  map((hrefArray) => this.managerService.mergeRoutes(hrefArray))
                )
                .pipe(
                  map((itemRoute) => {
                    return { itemRoute, activatedRoute };
                  }),
                  map((routeObject) => {
                    //
                    if (dObject.supportRouting) {
                      return (
                        toNormalString(routeObject.itemRoute) ===
                        toNormalString(routeObject.activatedRoute)
                      );
                    } else {
                      return (
                        toNormalString(this.ACTIVATED_CHILD.id) ===
                        toNormalString(dObject.item.id)
                      );
                    }
                  })
                );
            })
          );
      })
    );
  }

  isHidden(item: XExpandableListItem<any>) {
    return !item || !item.hidden ? of(false) : this.getValue(item.hidden);
  }

  isDisabled(item: XExpandableListItem<any>) {
    return !item || !item.disabled ? of(false) : this.getValue(item.disabled);
  }

  isExpanded(item: XExpandableListItem<any>) {
    return !item || !item.opened ? of(false) : this.getValue(item.opened);
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleItemSelectedAction(
    item: XExpandableListItem<any>,
    parent: XExpandableListItem<any>
  ) {
    //
    // Check Component State ...
    if (this.loading || this.uiDisabled) {
      return;
    }

    //
    // Emit Event Handler ...
    this.itemSelected.emit(item);

    //
    const isSupportRouting = await this.getValueAsync(this.supportRouting);
    if (isSupportRouting) {
      //
      const currUrl: string = this.managerService.getRoute();
      const itemHref = await this.getArrayValueAsync(item.href);
      const itemUrl = this.managerService.mergeRoutes(itemHref);

      //
      if (currUrl === itemUrl) {
        //
        await this.setActivatedParent(parent);
        await this.setActivatedChild(item);
      } else {
        await this.handleItemRoutingActions(item);
      }
    } else {
      //
      await this.setActivatedParent(parent);
      await this.setActivatedChild(item);
    }
  }
  //#endregion

  //
  //#region Actions ...
  async toggle(
    item: XExpandableListItem<any>,
    siblings: XExpandableListItem<any>[]
  ) {
    //
    const isExpanded = await this.toPromise(this.isExpanded(item));
    item.opened = !isExpanded;

    //
    const isCloseSibling = await this.toPromise(
      this.getValue(this.closeSiblings)
    );

    //
    if (isCloseSibling) {
      this.handleCloseSiblings(item, siblings);
    }

    //
    if (this.loading || this.uiDisabled) {
      return;
    }

    //
    this.itemSelected.emit(item);

    //
    await this.handleItemRoutingActions(item, true);
  }

  async expand(item: XExpandableListItem<any>) {
    item.opened = true;
  }

  async expandAll() {
    //
    const items = await this.getArrayValueAsync(this.items);
    await this.handleChangeState(items, true);
  }

  async collapse(item: XExpandableListItem<any>) {
    item.opened = false;
  }

  async collapseAll() {
    //
    const items = await this.getArrayValueAsync(this.items);
    await this.handleChangeState(items, false);
  }
  //#endregion

  //
  //#region Privates ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XExpandableListActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XExpandableListAction.Refresh:
            this.detectChanges();
            break;

          //
          case XExpandableListAction.ExpandAll:
            await this.expandAll();
            break;

          //
          case XExpandableListAction.CollapseAll:
            await this.collapseAll();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region Parent and Child Avtivated ...
  private async handleApplyCurrentItems() {
    //
    if (this.uiDisabled || this.loading) {
      setTimeout(async () => await this.handleApplyCurrentItems(), 100);
    }

    //
    const supportRouting = await this.getValueAsync(this.supportRouting);
    if (supportRouting) {
      await this.handleApplyCurrentItemsByRouting();
    } else {
      await this.handleApplyCurrentItemsById();
    }

    //
    this.detectChanges();
  }

  private async handleApplyCurrentItemsByRouting() {
    //
    const currentRoute = this.managerService.getRoute();
    if (isNullOrEmptyString(currentRoute)) {
      return;
    }

    //
    const foundedItem = await findItemByRoute(this.items, currentRoute);
    const parentItems = await parentItemsByRoute(this.items, currentRoute);

    //
    if (!foundedItem || !hasChild(parentItems)) {
      //
      if (foundedItem) {
        //
        this.ACTIVATED_CHILD = foundedItem;
        this.ACTIVATED_PARENTS = [foundedItem];
      }

      //
      return;
    }

    //
    this.ACTIVATED_PARENTS = parentItems;
    this.ACTIVATED_CHILD = foundedItem;
  }

  private async handleApplyCurrentItemsById() {
    //
    const items = await this.getArrayValueAsync(this.items);
    const currentId = this.ACTIVATED_CHILD
      ? this.ACTIVATED_CHILD.id
      : hasChild(items)
      ? items[0].id
      : '';
    if (isNullOrEmptyString(currentId)) {
      return;
    }

    //
    const foundedItem = await findItemById(this.items, currentId);
    const parentItems = await parentItemsById(this.items, currentId);

    //
    if (!foundedItem || !hasChild(parentItems)) {
      return;
    }

    //
    this.ACTIVATED_PARENTS = parentItems;
    this.ACTIVATED_CHILD = foundedItem;
  }
  //#endregion

  private async handleItemRoutingActions(
    item: XExpandableListItem<any>,
    isParent = false
  ) {
    //
    if (!item || this.uiDisabled || this.loading) {
      return;
    }

    //
    const hidden = await this.getValueAsync(item.hidden);
    const disabled = await this.getValueAsync(item.disabled);
    if (hidden || disabled) {
      return;
    }

    //
    //#region Handle Routing ...
    //
    const href = await this.getValueAsync(item.href);
    const route = this.managerService.mergeRoutes(href);
    const supportRouting = await this.getValueAsync(this.supportRouting);
    const replaceUrl = await this.getOptionalValueAsync(item.replaceUrl);
    const disableParentRouting = await this.getValueAsync(
      this.disableParentRouting
    );

    //
    // When just a not Child Parent with Href provided ...
    const childsArr = !item.childs
      ? []
      : await this.getArrayValueAsync(item.childs);
    if (isParent && !hasChild(childsArr)) {
      isParent = false;
    }

    //
    if (isParent && disableParentRouting) {
      return;
    }

    //
    if (supportRouting && hasChild(href) && !isNullOrEmptyString(route)) {
      await this.managerService.navigateByUrlReplace(route, {}, replaceUrl);
    }
    //#endregion
  }

  private handleCloseSiblings(
    item: XExpandableListItem<any>,
    siblings: XOneOrManyType<XExpandableListItem<any>>
  ) {
    //
    const pSiblings = toArray(siblings);
    const others = pSiblings.filter((i) => item.id !== i.id);
    others.forEach((i) => (i.opened = false));

    //
    this.detectChanges();
  }

  private async handleChangeState(
    items: XOneOrManyType<XExpandableListItem<any>>,
    expand: boolean
  ) {
    //
    const pItems = toArray(items);
    pItems.forEach((i) => {
      //
      i.opened = expand;

      //
      this.getValueAsync(i.childs).then((childs) => {
        if (hasChild(childs)) {
          this.handleChangeState(childs, expand);
        }
      });

      //
      this.detectChanges();
    });
  }

  private async handleStartupState() {
    //
    const items = (await this.getValueAsync(
      this.items
    )) as XExpandableListItem<any>[];

    //
    await this.applyStartupState(items);
    await this.handleApplyCurrentItems();
  }

  private async setActivatedParent(v: XExpandableListItem<any>) {
    //
    const isExists = hasChild(this.ACTIVATED_PARENTS)
      ? this.ACTIVATED_PARENTS.findIndex(
          (p) => toNormalString(p.id) === toNormalString(v.id)
        ) > 1
      : false;

    //
    if (isExists) {
      return;
    }

    //
    this.ACTIVATED_PARENTS = await containsItemsByRoute(this.items, v.href);

    //
    this.detectChanges();
  }

  private async setActivatedChild(v: XExpandableListItem<any>) {
    //
    const isSamed = this.ACTIVATED_CHILD
      ? v.id === this.ACTIVATED_CHILD.id
      : false;

    //
    if (isSamed) {
      return;
    }

    //
    this.ACTIVATED_CHILD = v;

    //
    this.detectChanges();
  }

  private async applyStartupState(
    items: XOneOrManyType<XExpandableListItem<any>>
  ) {
    //
    if (!hasChild(items)) {
      return;
    }

    //
    const startupState = await this.getValueAsync(this.startupState);
    const pItems = await this.getArrayValueAsync(items);

    //
    pItems.forEach((i) => {
      //
      // Handle StartUp State for Items ...
      i.opened =
        startupState === XExpandableListStartupState.CollapseAll
          ? false
          : startupState === XExpandableListStartupState.ExpandAll
          ? true
          : false;

      //
      if (i.childs) {
        this.getValueAsync(i.childs).then((childs) => {
          this.applyStartupState(childs);
        });
      }

      //
      this.detectChanges();
    });

    //
    this.detectChanges();
  }

  //
  //#region Color Appliers ...
  private async applyChildColor() {
    //
    const color = await this.getValueAsync(this.childColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XExpandableListCssVarKeys.ChildColor, `var(${fgColor})`);
    this.setCssVar(XExpandableListCssVarKeys.ChildBgColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyChildActiveColor() {
    //
    const color = await this.getValueAsync(this.childActiveColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XExpandableListCssVarKeys.ChildActiveColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XExpandableListCssVarKeys.ChildActiveBgColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyParentColor() {
    //
    const color = await this.getValueAsync(this.parentColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XExpandableListCssVarKeys.ParentColor, `var(${fgColor})`);
    this.setCssVar(XExpandableListCssVarKeys.ParentBgColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyParentActiveColor() {
    //
    const color = await this.getValueAsync(this.parentActiveColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XExpandableListCssVarKeys.ParentActiveColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XExpandableListCssVarKeys.ParentActiveBgColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyHighlightColor() {
    //
    const color = await this.getValueAsync(this.highlightColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XExpandableListCssVarKeys.HighlightColor, `var(${fgColor})`);
    this.setCssVar(
      XExpandableListCssVarKeys.HighlightBgColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyDividerColor() {
    //
    const color = await this.getValueAsync(this.dividerColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const colorVar = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(colorVar)) {
      return;
    }

    //
    this.setCssVar(XExpandableListCssVarKeys.DividerColor, `var(${colorVar})`);

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-list\x-expandable-list.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XExpandableListComponent } from './x-expandable-list.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XExpandableListComponent],
  imports: [CommonModule, XBaseComponentModule, XCardModule, XIconModule],
  exports: [
    XCardModule,
    XIconModule,
    XBaseComponentModule,
    XExpandableListComponent,
  ],
})
export class XExpandableListModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-list\x-expandable-list.tools.ts

import {
  toArray,
  hasChild,
  toPromise,
  mergeRoutes,
  XStandardType,
  XOneOrManyType,
  toNormalString,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import { XExpandableListItem } from './x-expandable-list.typings';

export async function toFlatItems(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>
) {
  //
  const result: XExpandableListItem<any>[] = [];

  //
  if (!items) {
    return result;
  }

  //
  const pItemsArray = await toPromise(items);
  if (!hasChild(pItemsArray)) {
    return result;
  }

  //
  const pItems = toArray(pItemsArray);
  for (const item of pItems) {
    //
    const childsArray = await toPromise(item.childs);
    const isParent = hasChild(childsArray);

    //
    result.push(item);

    //
    if (isParent) {
      //
      const childsRes = await toFlatItems(item.childs);
      result.push(...childsRes);
    }
  }

  //
  return result;
}

//
//#region By Route ...
export async function findItemByRoute(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>,
  route: XStandardType<XOneOrManyType<string>>
) {
  //
  let result: XExpandableListItem<any> = null;

  //
  if (!items || !route) {
    return result;
  }

  //
  const routeArray = await toPromise(route);
  if (!hasChild(routeArray)) {
    return result;
  }

  //
  const pRoute = mergeRoutes(routeArray);
  if (isNullOrEmptyString(pRoute)) {
    return result;
  }

  //
  const pItems = await toFlatItems(items);
  if (!hasChild(pItems)) {
    return result;
  }

  //
  for (const item of pItems) {
    //
    const itemRouteArray = await toPromise(item.href);
    if (hasChild(itemRouteArray)) {
      //
      const itemRoute = mergeRoutes(itemRouteArray);
      if (
        !isNullOrEmptyString(itemRoute) &&
        toNormalString(itemRoute) === toNormalString(pRoute)
      ) {
        result = item;
      }
    }
  }

  //
  return result;
}

export async function containsItemsByRoute(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>,
  route: XStandardType<XOneOrManyType<string>>
) {
  //
  const result: XExpandableListItem<any>[] = [];

  //
  if (!items || !route) {
    return result;
  }

  //
  const routeArray = await toPromise(route);
  if (!hasChild(routeArray)) {
    return result;
  }

  //
  const pRoute = mergeRoutes(routeArray);
  if (isNullOrEmptyString(pRoute)) {
    return result;
  }

  //
  const pItems = await toFlatItems(items);
  if (!hasChild(pItems)) {
    return result;
  }

  //
  for (const item of pItems) {
    //
    const itemRouteArray = await toPromise(item.href);
    if (hasChild(itemRouteArray)) {
      //
      const itemRoute = mergeRoutes(itemRouteArray);
      if (
        !isNullOrEmptyString(itemRoute) &&
        toNormalString(pRoute).includes(toNormalString(itemRoute))
      ) {
        result.push(item);
      }
    }
  }

  //
  return result;
}

export async function parentItemsByRoute(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>,
  route: XStandardType<XOneOrManyType<string>>
) {
  //
  let result: XExpandableListItem<any>[] = [];

  //
  if (!items || !route) {
    return result;
  }

  //
  const parentItems = await containsItemsByRoute(items, route);
  const foundedItem = await findItemByRoute(items, route);
  if (!hasChild(parentItems) || isNullOrUndefined(foundedItem)) {
    return result;
  }

  //
  result = parentItems.filter(
    (i) => toNormalString(i.id) !== toNormalString(foundedItem.id)
  );

  //
  return result;
}
//#endregion

//
//#region By Id ...
export async function findItemById(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>,
  id: XStandardType<string>
) {
  //
  let result: XExpandableListItem<any> = null;

  //
  if (!items || !id) {
    return result;
  }

  //
  const pId = await toPromise(id);
  if (isNullOrEmptyString(pId)) {
    return result;
  }

  //
  const pItems = await toFlatItems(items);
  if (!hasChild(pItems)) {
    return result;
  }

  //
  for (const item of pItems) {
    //
    const itemRouteArray = await toPromise(item.href);
    if (hasChild(itemRouteArray)) {
      //
      if (
        !isNullOrEmptyString(item.id) &&
        toNormalString(item.id) === toNormalString(pId)
      ) {
        result = item;
      }
    }
  }

  //
  return result;
}

export async function containsItemsById(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>,
  id: XStandardType<string>
) {
  //
  const result: XExpandableListItem<any>[] = [];

  //
  if (!items || !id) {
    return result;
  }

  //
  const pId = await toPromise(id);
  if (isNullOrEmptyString(pId)) {
    return result;
  }

  //
  const pItems = await toFlatItems(items);
  if (!hasChild(pItems)) {
    return result;
  }

  //
  for (const item of pItems) {
    //
    const isSameId = toNormalString(item.id) === toNormalString(pId);
    if (isSameId) {
      result.push(item);
    } else {
      //
      const childsArr = await toPromise(item.childs);
      const childs = toArray(childsArr);
      const isParent = hasChild(childs);

      //
      if (isParent) {
        const pChildsRes = await containsItemsById(childsArr, id);
        result.push(...pChildsRes);
      }
    }
  }

  //
  return result;
}

export async function parentItemsById(
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>,
  id: XStandardType<string>
) {
  //
  let result: XExpandableListItem<any>[] = [];

  //
  if (!items || !id) {
    return result;
  }

  //
  const parentItems = await containsItemsById(items, id);
  const foundedItem = await findItemById(items, id);
  if (!hasChild(parentItems) || isNullOrUndefined(foundedItem)) {
    return result;
  }

  //
  result = parentItems.filter(
    (i) => toNormalString(i.id) !== toNormalString(foundedItem.id)
  );

  //
  return result;
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-list\x-expandable-list.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export interface XExpandableListItem<T> {
  id: string;
  data: XStandardType<T>;
  opened?: XStandardType<boolean>;
  hidden?: XStandardType<boolean>;
  disabled?: XStandardType<boolean>;
  replaceUrl?: XStandardType<boolean>;
  title: XStandardType<XResourceIdentifier>;
  href?: XStandardType<XOneOrManyType<string>>;
  description?: XStandardType<XResourceIdentifier>;
  childs?: XStandardType<XOneOrManyType<XExpandableListItem<T>>>;
}

export enum XExpandableListStartupState {
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',
}

export type XExpandableListStartupStateIdentifier =
  | XExpandableListStartupState
  | string;

export enum XExpandableListAction {
  //
  Refresh = 'refresh',

  //
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',
}

export type XExpandableListActionIdentifier = XExpandableListAction | string;

export interface XExpandableListActionModel
  extends XBaseActionModel<XExpandableListActionIdentifier> {}

export enum XExpandableListCssVarKeys {
  //
  ChildColor = 'child-color',
  ChildBgColor = 'child-background-color',

  //
  ChildActiveColor = 'child-active-color',
  ChildActiveBgColor = 'child-active-background-color',

  //
  ParentColor = 'parent-color',
  ParentBgColor = 'parent-background-color',

  //
  ParentActiveColor = 'parent-active-color',
  ParentActiveBgColor = 'parent-active-background-color',

  //
  HighlightColor = 'highlight-color',
  HighlightBgColor = 'highlight-background-color',

  //
  DividerColor = 'divider-color',
}

//
//#region Inside Card Container ...
export interface IXExpandableListContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XExpandableListActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  itemCssClass: XStandardType<XOneOrManyType<string>>;

  //
  supportRouting: XStandardType<boolean>;
  disableParentRouting: XStandardType<boolean>;
  showParentDetailIcon: XStandardType<boolean>;
  showChildDetailIcon: XStandardType<boolean>;
  closeSiblings: XStandardType<boolean>;
  startupState: XStandardType<XExpandableListStartupStateIdentifier>;
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>;
  parentTemplate: TemplateRef<any>;
  childTemplate: TemplateRef<any>;

  //
  childColor: XStandardType<XColorIdentifier>;
  childActiveColor: XStandardType<XColorIdentifier>;
  parentColor: XStandardType<XColorIdentifier>;
  parentActiveColor: XStandardType<XColorIdentifier>;
  highlightColor: XStandardType<XColorIdentifier>;
  dividerColor: XStandardType<XColorIdentifier>;

  //
  itemSelected: EventEmitter<XExpandableListItem<any>>;
}

@Injectable({
  providedIn: 'root',
})
export class XExpandableListContainer
  extends XComponentCardContainer
  implements Readonly<IXExpandableListContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XExpandableListActionModel>;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly itemCssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly supportRouting: XStandardType<boolean> = false;
  readonly disableParentRouting: XStandardType<boolean> = true;
  readonly showParentDetailIcon: XStandardType<boolean> = true;
  readonly showChildDetailIcon: XStandardType<boolean> = true;
  readonly closeSiblings: XStandardType<boolean> = false;
  readonly startupState: XStandardType<XExpandableListStartupStateIdentifier> =
    XExpandableListStartupState.CollapseAll;
  readonly items: XStandardType<XOneOrManyType<XExpandableListItem<any>>>;
  readonly parentTemplate: TemplateRef<any>;
  readonly childTemplate: TemplateRef<any>;

  //
  readonly childColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.PrimaryShade;
  readonly childActiveColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly parentColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.TertiaryTint;
  readonly parentActiveColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly highlightColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;
  readonly dividerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  //
  readonly itemSelected: EventEmitter<XExpandableListItem<any>> =
    new EventEmitter<XExpandableListItem<any>>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XExpandableListContainerComponent
  extends XComponentCardContainerComponent
  implements IXExpandableListContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XExpandableListActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  itemCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.itemCssClass;

  //
  @Input()
  supportRouting: XStandardType<boolean> = this.propertyProvider.supportRouting;

  @Input()
  disableParentRouting: XStandardType<boolean> =
    this.propertyProvider.disableParentRouting;

  @Input()
  showParentDetailIcon: XStandardType<boolean> =
    this.propertyProvider.showParentDetailIcon;

  @Input()
  showChildDetailIcon: XStandardType<boolean> =
    this.propertyProvider.showChildDetailIcon;

  @Input()
  closeSiblings: XStandardType<boolean> = this.propertyProvider.closeSiblings;

  @Input()
  startupState: XStandardType<XExpandableListStartupStateIdentifier> =
    this.propertyProvider.startupState;

  @Input()
  items: XStandardType<XOneOrManyType<XExpandableListItem<any>>> =
    this.propertyProvider.items;

  @Input()
  parentTemplate: TemplateRef<any> = this.propertyProvider.parentTemplate;

  @Input()
  childTemplate: TemplateRef<any> = this.propertyProvider.childTemplate;

  //
  @Input()
  childColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.childColor;

  @Input()
  childActiveColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.childActiveColor;

  @Input()
  parentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.parentColor;

  @Input()
  parentActiveColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.parentActiveColor;

  @Input()
  highlightColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.highlightColor;

  @Input()
  dividerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.dividerColor;

  //
  @Output()
  itemSelected: EventEmitter<XExpandableListItem<any>> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XExpandableListContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXExpandableListContainer
  ): Observable<IXExpandableListContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            itemCssClass: this.getValue(
              container ? container.itemCssClass : this.itemCssClass
            ),

            //
            supportRouting: this.getValue(
              container ? container.supportRouting : this.supportRouting
            ),
            disableParentRouting: this.getValue(
              container
                ? container.disableParentRouting
                : this.disableParentRouting
            ),
            showParentDetailIcon: this.getValue(
              container
                ? container.showParentDetailIcon
                : this.showParentDetailIcon
            ),
            showChildDetailIcon: this.getValue(
              container
                ? container.showChildDetailIcon
                : this.showChildDetailIcon
            ),
            closeSiblings: this.getValue(
              container ? container.closeSiblings : this.closeSiblings
            ),
            startupState: this.getValue(
              container ? container.startupState : this.startupState
            ),
            items: this.getValue(container ? container.items : this.items),
            parentTemplate: this.getValue(
              container ? container.parentTemplate : this.parentTemplate
            ),
            childTemplate: this.getValue(
              container ? container.childTemplate : this.childTemplate
            ),

            //
            childColor: this.getValue(
              container ? container.childColor : this.childColor
            ),
            childActiveColor: this.getValue(
              container ? container.childActiveColor : this.childActiveColor
            ),
            parentColor: this.getValue(
              container ? container.parentColor : this.parentColor
            ),
            parentActiveColor: this.getValue(
              container ? container.parentActiveColor : this.parentActiveColor
            ),
            highlightColor: this.getValue(
              container ? container.highlightColor : this.highlightColor
            ),
            dividerColor: this.getValue(
              container ? container.dividerColor : this.dividerColor
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            itemSelected: this.itemSelected,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-expandable-panel.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else wrapperRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="wrapperRef"></ng-container>

    <!-- Actions -->
    <div class="x-card-actions" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Components itSelf Template -->
<ng-template #wrapperRef>
  <!-- Wrapper Element -->
  <div
    class="x-expandable-panel x-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [class.applyColors]="getValue(applyColors) | async"
  >
    <ng-container
      *ngTemplateOutlet="getIsAccordion() ? accordionRef : panelRef"
    ></ng-container>
  </div>
</ng-template>

<!-- Accordion Template -->
<ng-template #accordionRef>
  <mat-accordion
    class="x-expandable-accordion"
    [multi]="getValue(multiExpanded) | async"
  >
    <ng-container *ngTemplateOutlet="panelRef"> </ng-container>
  </mat-accordion>
</ng-template>

<!-- Non Accordion Template -->
<ng-template #panelRef>
  <!-- Check Panels Exists -->
  <ng-container *ngIf="hasPanels()">
    <ng-container *ngFor="let panel of panels">
      <!-- Panel -->
      <mat-expansion-panel
        [disabled]="panel.uiDisabled"
        (opened)="handlePanelOpened(panel)"
        [expanded]="getExpanded(panel) | async"
        [ngClass]="getValue(panel.cssClass) | async"
        [hideToggle]="getValue(panel.hideToggle) | async"
      >
        <!-- Header -->
        <mat-expansion-panel-header
          class="x-expandable-panel-header"
          [class.mobile]="isMobileUi$ | async"
        >
          <!-- Title -->
          <mat-panel-title
            *ngIf="panel.title"
            class="x-expandable-panel-header-title"
          >
            <ng-container *ngTemplateOutlet="panel.title"></ng-container>
          </mat-panel-title>

          <!-- Description -->
          <mat-panel-description
            class="x-expandable-panel-header-description"
            *ngIf="
              panel.description &&
              (!(getValue(hideDescriptionOnSmallScreens) | async) ||
                ((getValue(hideDescriptionOnSmallScreens) | async) &&
                  (isNotMobileUi$ | async)))
            "
          >
            <ng-container *ngTemplateOutlet="panel.description"></ng-container>
          </mat-panel-description>
        </mat-expansion-panel-header>

        <!-- Conent -->
        <ng-template matExpansionPanelContent>
          <div class="x-expandable-panel-content">
            <ng-container *ngTemplateOutlet="panel.content"></ng-container>
          </div>
        </ng-template>

        <!-- Actions -->
        <mat-action-row
          class="x-expandable-panel-actions"
          *ngIf="
            (hasActions(panel) | async) ||
            (getValue(panel.hasCustomActions) | async)
          "
        >
          <ng-container
            *ngTemplateOutlet="panelActionsRef; context: { $implicit: panel }"
          ></ng-container>
        </mat-action-row>
      </mat-expansion-panel>
    </ng-container>
  </ng-container>
</ng-template>

<!-- Panel Actions Template -->
<ng-template #panelActionsRef let-panel>
  <!-- Step Buttons -->
  <div class="x-panel-actions">
    <!-- Defined Actions  -->
    <x-slotter *ngIf="hasActions(panel) | async">
      <!-- Next -->
      <x-slot
        [name]="getValue(panel.nextPosition) | async"
        [cssClass]="getValue(panel.nextCssClass) | async"
      >
        <ng-container *ngIf="hasStepAction(panel, NPRButtonTypes.Next) | async">
          <ng-container
            *ngTemplateOutlet="
              buttonRef;
              context: {
                panel: panel,
                type: NPRButtonTypes.Next
              }
            "
          ></ng-container>
        </ng-container>
      </x-slot>

      <!-- Reset -->
      <x-slot
        [name]="getValue(panel.resetPosition) | async"
        [cssClass]="getValue(panel.resetCssClass) | async"
      >
        <ng-container
          *ngIf="hasStepAction(panel, NPRButtonTypes.Reset) | async"
        >
          <ng-container
            *ngTemplateOutlet="
              buttonRef;
              context: {
                panel: panel,
                type: NPRButtonTypes.Reset
              }
            "
          ></ng-container>
        </ng-container>
      </x-slot>

      <!-- Prev -->
      <x-slot
        [name]="getValue(panel.prevPosition) | async"
        [cssClass]="getValue(panel.prevCssClass) | async"
      >
        <ng-container *ngIf="hasStepAction(panel, NPRButtonTypes.Prev) | async">
          <ng-container
            *ngTemplateOutlet="
              buttonRef;
              context: {
                panel: panel,
                type: NPRButtonTypes.Prev
              }
            "
          ></ng-container>
        </ng-container>
      </x-slot>
    </x-slotter>

    <!-- Specified Actions -->
    <div
      class="x-panel-custom-actions-wrapper"
      *ngIf="getValue(panel.hasCustomActions) | async"
    >
      <ng-container *ngTemplateOutlet="panel.actions"></ng-container>
    </div>
  </div>
</ng-template>

<!-- Button Template -->
<ng-template #buttonRef let-panel="panel" let-type="type">
  <x-button
    (clicked)="handleNPRStep(panel, type)"
    [icon]="getButtonIcon(panel, type) | async"
    [type]="getButtonType(panel, type) | async"
    [title]="getButtonTitle(panel, type) | async"
    [color]="getButtonColor(panel, type) | async"
    [cssClass]="getButtonCssClass(panel, type) | async"
    [uiDisabled]="getButtonDisabled(panel, type) | async"
    [class.disabled]="getButtonDisabled(panel, type) | async"
    [iconPosition]="getButtonIconPosition(panel, type) | async"
    [foregroundColor]="getButtonForegroundColor(panel, type) | async"
  ></x-button>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-expandable-panel.component.scss

:host {
  --panel-header-size: 48px;
}

.x-expandable-panel-header {
  height: var(--panel-header-size);
}

.applyColors {
  .mat-expansion-panel-header {
    //
    color: var(--header-foreground-color, var(--x-color-light-contrast));
    background-color: var(--header-background-color, var(--x-color-light));

    * {
      color: var(--header-foreground-color, var(--x-color-light-contrast));
    }

    .mat-expansion-indicator {
      &::after {
        color: var(
          --header-foreground-color,
          var(--x-color-light-contrast)
        ) !important;
      }
    }

    &:focus,
    &:hover,
    &.mat-expanded {
      background: unset;
      color: var(
        --header-highlight-foreground-color,
        var(--x-color-light-contrast)
      );
      background-color: var(
        --header-highlight-background-color,
        var(--x-color-light)
      );

      * {
        color: var(
          --header-highlight-foreground-color,
          var(--x-color-light-contrast)
        );
      }

      .mat-expansion-indicator {
        &::after {
          color: var(
            --header-highlight-foreground-color,
            var(--x-color-light-contrast)
          );
        }
      }
    }
  }
}

:host ::ng-deep .mat-expansion-indicator {
  &::after {
    color: var(
      --header-foreground-color,
      var(--x-color-light-contrast)
    ) !important;
  }
}

.x-panel-actions,
.x-panel-custom-actions-wrapper {
  width: 100%;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-expandable-panel.component.ts

import {
  Component,
  QueryList,
  ContentChildren,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  parseXColor,
  XStandardType,
  XModalButtonRole,
  isNullOrEmptyString,
  XColorWithBrightness,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XExpandablePanelAction,
  XExpandablePanelCssVars,
  XExpandablePanelActionModel,
  XExpandablePanelStartupState,
  XExpandablePandelContainerComponent,
} from './x-expandable-panel.typings';
import { Subject, Subscription } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { XButtonType } from '../x-button/x-button.typings';
import { XPanelComponent } from './x-panel/x-panel.component';
import { XNPRButtonType } from '../classes/i-x-next-prev-reset.container';

@Component({
  selector: 'x-expandable-panel',
  templateUrl: './x-expandable-panel.component.html',
  styleUrls: ['./x-expandable-panel.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XExpandablePanelComponent extends XExpandablePandelContainerComponent {
  //
  //#region Props ...
  //
  private IS_ACCORDION: boolean;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly NPRButtonTypes = Object.assign({}, XNPRButtonType);

  //
  //#region Panels Props ...
  //
  private PANELS: QueryList<XPanelComponent>;

  //
  @ContentChildren(XPanelComponent)
  set panels(p: QueryList<XPanelComponent>) {
    //
    if (!p) {
      return;
    }

    //
    this.PANELS = p;
    this.detectChanges();
  }

  //
  get panels() {
    return this.PANELS;
  }
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  async afterViewInit() {
    super.afterViewInit();

    //
    await this.handleStartupState();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const isAccordionChanged = changeKeys.includes(
      nameof<XExpandablePanelComponent>('isAccordion')
    );
    if (isAccordionChanged) {
      this.IS_ACCORDION = await this.getValueAsync(this.isAccordion);
    }

    //
    const isHeaderColorChanged = changeKeys.includes(
      nameof<XExpandablePanelComponent>('headerColor')
    );
    if (isHeaderColorChanged) {
      await this.applyHeaderColor();
    }

    //
    const isHeaderHighlighColorChanged = changeKeys.includes(
      nameof<XExpandablePanelComponent>('headerHighlightColor')
    );
    if (isHeaderHighlighColorChanged) {
      await this.applyHeaderHighlightColor();
    }

    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XExpandablePanelComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  registerInstanceHandlers() {
    super.registerInstanceHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Provider ...
  hasStepAction(panel: XPanelComponent, type: XNPRButtonType) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        return this.getOptionalValue(panel.hasNext);

      //
      case this.NPRButtonTypes.Prev:
        return this.getOptionalValue(panel.hasPrev);

      //
      case this.NPRButtonTypes.Reset:
        return this.getOptionalValue(panel.hasReset);
    }
  }

  hasPanels() {
    return this.panels && this.panels.length > 0;
  }

  hasActions(panel: XPanelComponent) {
    return this.getValue(panel.hasNext).pipe(
      concatMap((hasNext) =>
        this.getValue(panel.hasPrev).pipe(
          map((hasPrev) => {
            return {
              hasNext,
              hasPrev,
            };
          })
        )
      ),
      concatMap((hasNextPrev) =>
        this.getValue(panel.hasReset).pipe(
          map((hasReset) => {
            return {
              ...hasNextPrev,
              hasReset,
            };
          })
        )
      ),
      map((info) => info.hasNext || info.hasPrev || info.hasReset)
    );
  }

  getIsAccordion() {
    return this.IS_ACCORDION;
  }

  getExpanded(panel: XPanelComponent) {
    return this.getValue(panel.id).pipe(
      concatMap((panelId) =>
        this.getValue(panel.expanded).pipe(
          map((panelExpanded) => {
            return {
              panelId,
              panelExpanded,
            };
          })
        )
      ),
      map((panelInfo) => {
        return {
          ...panelInfo,
          isAccordion: this.IS_ACCORDION,
        };
      }),
      map((info) =>
        info.isAccordion ? this.selected === info.panelId : info.panelExpanded
      )
    );
  }

  //
  //#region Button Providers ...
  getButtonIcon(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) => this.getValue(actionButton.icon))
    );
  }

  getButtonType(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) => this.getValue(actionButton.type)),
      map((btnType) =>
        !isNullOrEmptyString(btnType) ? btnType : XButtonType.Raised
      )
    );
  }

  getButtonTitle(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) => this.getResourceValue(actionButton.title))
    );
  }

  getButtonColor(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) => this.getValue(actionButton.color)),
      map((btnColor) =>
        !isNullOrEmptyString(btnColor) ? btnColor : XColorWithBrightness.Primary
      )
    );
  }

  getButtonDisabled(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButtonDisabled(panel, type);
  }

  getButtonCssClass(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) => this.getArrayValue(actionButton.cssClass))
    );
  }

  getButtonIconPosition(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) => this.getValue(actionButton.iconPosition))
    );
  }

  getButtonForegroundColor(panel: XPanelComponent, type: XNPRButtonType) {
    return this.getActionButton(panel, type).pipe(
      concatMap((actionButton) =>
        this.getOptionalValue(actionButton.foregroundColor)
      )
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleNPRStep(panel: XPanelComponent, type: XNPRButtonType) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        await this.handleNextStep(panel);
        break;

      //
      case this.NPRButtonTypes.Prev:
        await this.handlePrevStep(panel);
        break;

      //
      case this.NPRButtonTypes.Reset:
        await this.handleReset(panel);
        break;
    }
  }

  async handleNextStep(panel: XPanelComponent, force?: boolean) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !panel ||
      panel.loading ||
      panel.uiDisabled ||
      !this.hasPanels()
    ) {
      return;
    }

    //
    let nextId: string;
    const panelId = await this.getValueAsync(panel.id);
    const panelIndex = await this.getPanelIndex(panelId);
    if (panelIndex === -1) {
      return;
    }
    if (panelIndex < this.panels.length - 1) {
      nextId = await this.getValueAsync(
        this.panels.toArray()[panelIndex + 1].id
      );
    }
    if (isNullOrEmptyString(nextId)) {
      return;
    }

    //
    if (!force) {
      //
      const isUiBusy = this.uiDisabled || this.loading;
      const canNextStep = await this.getValueAsync(this.canNext);
      const disableNextStep = await this.getValueAsync(panel.disableNext);

      //
      if (isUiBusy || disableNextStep || !canNextStep) {
        return;
      }

      //
      const isStepNextConfitmedType = panel.nextConfirmer
        ? panel.nextConfirmer()
        : this.getValue(true);
      const isStepNextConfirmed = await this.getValueAsync(
        isStepNextConfitmedType
      );
      if (!isStepNextConfirmed) {
        return;
      }
    }

    //
    this.selected = nextId;
    this.detectChanges();
    this.selectedChange.emit(nextId);
    this.onNext.emit(panelId);
    this.detectChanges();
  }

  async handlePrevStep(panel: XPanelComponent, force?: boolean) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !panel ||
      panel.loading ||
      panel.uiDisabled ||
      !this.hasPanels()
    ) {
      return;
    }

    //
    let prevId: string;
    const panelId = await this.getValueAsync(panel.id);
    const panelIndex = await this.getPanelIndex(panelId);
    if (panelIndex === -1) {
      return;
    }
    if (panelIndex > 0) {
      prevId = await this.getValueAsync(
        this.panels.toArray()[panelIndex - 1].id
      );
    }
    if (isNullOrEmptyString(prevId)) {
      return;
    }

    //
    if (!force) {
      const isUiBusy = this.uiDisabled || this.loading;
      const canPrevStep = await this.getValueAsync(this.canPrev);
      const disablePrevStep = await this.getValueAsync(panel.disablePrev);
      if (isUiBusy || disablePrevStep || !canPrevStep) {
        return;
      }

      //
      const isStepPrevConfitmedType = panel.prevConfirmer
        ? panel.prevConfirmer()
        : this.getValue(true);
      const isStepPrevConfirmed = await this.getValueAsync(
        isStepPrevConfitmedType
      );
      if (!isStepPrevConfirmed) {
        return;
      }
    }

    //
    this.selected = prevId;
    this.detectChanges();
    this.selectedChange.emit(prevId);
    this.onPrev.emit(panelId);
    this.detectChanges();
  }

  async handleReset(panel: XPanelComponent, force?: boolean) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !panel ||
      panel.loading ||
      panel.uiDisabled ||
      !this.hasPanels()
    ) {
      return;
    }

    //
    const stepId = await this.getValueAsync(panel.id);
    const firstId = await this.getValueAsync(this.panels.toArray()[0].id);

    //
    if (!force) {
      const isUiBusy = this.uiDisabled || this.loading;
      const canResetStep = await this.getValueAsync(this.canReset);
      const disableResetStep = await this.getValueAsync(panel.disableReset);
      if (isUiBusy || disableResetStep || !canResetStep) {
        return;
      }

      //
      const isConfirmedType = await this.resetConfirmer();
      const isConfirmed = await this.getValueAsync(isConfirmedType);

      //
      if (!isConfirmed) {
        return;
      }

      //
      const isStepResetConfitmedType = panel.resetConfirmer
        ? panel.resetConfirmer()
        : this.getValue(true);
      const isStepResetConfirmed = await this.getValueAsync(
        isStepResetConfitmedType
      );
      if (!isStepResetConfirmed) {
        return;
      }
    }

    //
    this.selected = firstId;
    this.detectChanges();
    this.selectedChange.emit(firstId);
    this.onReset.emit(stepId);
    this.detectChanges();
  }

  async handleToStep(panel: XPanelComponent) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !panel ||
      panel.loading ||
      panel.uiDisabled ||
      !this.hasPanels() ||
      this.selected === (await this.getValueAsync(panel.id))
    ) {
      return;
    }

    //
    const panelId = await this.getValueAsync(panel.id);

    //
    this.selected = panelId;
    this.detectChanges();
    this.selectedChange.emit(panelId);
    this.detectChanges();
  }

  async handlePanelOpened(panel: XPanelComponent) {
    //
    const panelId = await this.getValueAsync(panel.id);
    this.selected = panelId;
    this.detectChanges();
  }
  //#endregion

  //
  //#region Actions ...
  async next(force?: boolean) {
    //
    const panel = await this.getPanel(this.selected);
    if (!panel) {
      return;
    }

    //
    await this.handleNextStep(panel, force);
  }

  async prev(force?: boolean) {
    //
    const panel = await this.getPanel(this.selected);
    if (!panel) {
      return;
    }

    //
    await this.handlePrevStep(panel, force);
  }

  async reset(force?: boolean) {
    //
    const panel = await this.getPanel(this.selected);
    if (!panel) {
      return;
    }

    //
    await this.handleReset(panel, force);
  }

  async to(id: XStandardType<string>, force?: boolean) {
    //
    if (!id || !this.hasPanels()) {
      return;
    }

    //
    const panelId = await this.getValueAsync(id);
    if (!panelId) {
      return;
    }

    //
    const panelIndex = await this.getPanelIndex(panelId);
    if (panelIndex === -1) {
      return;
    }

    //
    const panel = await this.getPanel(panelId);
    if (!panel) {
      return;
    }

    //
    const selectedIndex = await this.getPanelIndex(this.selected);
    if (selectedIndex === -1 || selectedIndex === panelIndex) {
      return;
    }

    //
    if (panelIndex > selectedIndex) {
      //
      // next confirm ...
      //
      if (!force) {
        //
        const isUiBusy = this.uiDisabled || this.loading;
        const canNextStep = await this.getValueAsync(this.canNext);
        const disableNextStep = await this.getValueAsync(panel.disableNext);

        //
        if (isUiBusy || disableNextStep || !canNextStep) {
          return;
        }

        //
        const isStepNextConfitmedType = panel.nextConfirmer
          ? panel.nextConfirmer()
          : this.getValue(true);
        const isStepNextConfirmed = await this.getValueAsync(
          isStepNextConfitmedType
        );
        if (!isStepNextConfirmed) {
          return;
        }
      }

      //
      this.selected = panelId;
      this.selectedChange.emit(panelId);
    } else if (panelIndex < selectedIndex) {
      //
      // prev Confirm ...
      //
      if (!force) {
        const isUiBusy = this.uiDisabled || this.loading;
        const canPrevStep = await this.getValueAsync(this.canPrev);
        const disablePrevStep = await this.getValueAsync(panel.disablePrev);
        if (isUiBusy || disablePrevStep || !canPrevStep) {
          return;
        }

        //
        const isStepPrevConfitmedType = panel.prevConfirmer
          ? panel.prevConfirmer()
          : this.getValue(true);
        const isStepPrevConfirmed = await this.getValueAsync(
          isStepPrevConfitmedType
        );
        if (!isStepPrevConfirmed) {
          return;
        }
      }

      //
      this.selected = panelId;
      this.selectedChange.emit(panelId);
    }
  }

  async expandAll() {
    //
    if (!this.hasChild(this.PANELS)) {
      return;
    }

    //
    this.PANELS.toArray().forEach((panel) => {
      //
      if (panel) {
        //
        panel.expanded = true;
        panel.detectChanges();
      }
    });

    //
    this.detectChanges();
  }

  async collapseAll() {
    //
    if (!this.hasChild(this.PANELS)) {
      return;
    }

    //
    this.PANELS.toArray().forEach((panel) => {
      //
      if (panel) {
        //
        panel.expanded = false;
        panel.detectChanges();
      }
    });

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  private async handleStartupState() {
    //
    const state = await this.getValueAsync(this.startupState);
    if (isNullOrEmptyString(state)) {
      return;
    }

    //
    switch (state) {
      //
      case XExpandablePanelStartupState.CollapseAll:
        await this.collapseAll();
        break;

      //
      case XExpandablePanelAction.ExpandAll:
        await this.expandAll();
        break;
    }
  }

  private defaultStepperResetConfirmer() {
    //
    return new Promise<boolean>((resolver) => {
      //
      this.managerService.dialogService.presentAreYouSureYesNoDialog(
        () => resolver(true),
        XModalButtonRole.Destructive,
        () => resolver(false)
      );
    });
  }

  private getActionButton(panel: XPanelComponent, type: XNPRButtonType) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        return this.getValue(panel.nextButton);

      //
      case this.NPRButtonTypes.Prev:
        return this.getValue(panel.prevButton);

      //
      case this.NPRButtonTypes.Reset:
        return this.getValue(panel.resetButton);
    }
  }

  private getActionButtonDisabled(
    panel: XPanelComponent,
    type: XNPRButtonType
  ) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        return this.getOptionalValue(panel.disableNext).pipe(
          map((sDisable) => this.uiDisabled || panel.uiDisabled || sDisable)
        );

      //
      case this.NPRButtonTypes.Prev:
        return this.getOptionalValue(panel.disablePrev).pipe(
          map((sDisable) => this.uiDisabled || panel.uiDisabled || sDisable)
        );

      //
      case this.NPRButtonTypes.Reset:
        return this.getOptionalValue(panel.disableReset).pipe(
          map((sDisable) => this.uiDisabled || panel.uiDisabled || sDisable)
        );
    }
  }

  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XExpandablePanelActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XExpandablePanelAction.NEXT:
            await this.next(model.force);
            break;

          //
          case XExpandablePanelAction.PREV:
            await this.prev(model.force);
            break;

          //
          case XExpandablePanelAction.RESET:
            await this.reset(model.force);
            break;

          //
          case XExpandablePanelAction.TO:
            if (model.payload) {
              await this.to(model.payload, model.force);
            }
            break;

          //
          case XExpandablePanelAction.ExpandAll:
            await this.expandAll();
            break;

          //
          case XExpandablePanelAction.CollapseAll:
            await this.collapseAll();
            break;

          //
          case XExpandablePanelAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }

  private async getPanel(id: string) {
    //
    if (!id || !this.hasPanels()) {
      return null;
    }

    //
    return this.panels.toArray().find(async (p) => {
      const pId = await this.getValueAsync(p.id);
      return pId === id;
    });
  }

  private async getPanelIndex(id: string) {
    //
    if (!id || !this.hasPanels()) {
      return -1;
    }

    //
    let result = -1;
    const panels = this.panels.toArray();
    if (!this.hasChild(panels)) {
      return result;
    }

    //
    for (let i = 0; i < panels.length; i++) {
      //
      const panel = panels[i];
      const pId = await this.getValueAsync(panel.id);
      if (pId === id) {
        result = i;
      }
    }

    //
    return result;
  }

  //
  //#region Color Appliers ...
  private async applyHeaderColor() {
    //
    const color = await this.getValueAsync(this.headerColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XExpandablePanelCssVars.HeaderForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XExpandablePanelCssVars.HeaderBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyHeaderHighlightColor() {
    //
    const color = await this.getValueAsync(this.headerHighlightColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XExpandablePanelCssVars.HeaderHighlightForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XExpandablePanelCssVars.HeaderHighlightBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-expandable-panel.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XButtonModule } from '../x-button/x-button.module';
import { XPanelComponent } from './x-panel/x-panel.component';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XExpandablePanelComponent } from './x-expandable-panel.component';

@NgModule({
  declarations: [XPanelComponent, XExpandablePanelComponent],
  imports: [
    XIconModule,
    XCardModule,
    CommonModule,
    XButtonModule,
    XSlotterModule,
    XBaseComponentModule,
  ],
  exports: [
    XIconModule,
    XCardModule,
    CommonModule,
    XButtonModule,
    XSlotterModule,
    XPanelComponent,
    XBaseComponentModule,
    XExpandablePanelComponent,
  ],
})
export class XExpandablePanelModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-expandable-panel.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XModalButtonRole,
  XColorIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { concatMap, map } from 'rxjs/operators';
import {
  XNextPrevResetHostContainer,
  XCardNextPrevResetHostContainer,
  IXCardNextPrevResetHostContainer,
  XCardNextPrevResetHostContainerComponent,
} from '../classes/i-x-next-prev-reset.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XComponentCardContainer } from '../x-card/x-card.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XExpandablePanelStartupState {
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',
}

export type XExpandablePanelStartupStateIdentifier =
  | XExpandablePanelStartupState
  | string;

export enum XExpandablePanelAction {
  NEXT = 'next',
  PREV = 'prev',
  RESET = 'reset',
  TO = 'to',

  //
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',

  //
  Refresh = 'refresh',
}

export type XExpandablePanelActionIdentifier = XExpandablePanelAction | string;

export interface XExpandablePanelActionModel
  extends XBaseActionModel<XExpandablePanelActionIdentifier> {
  force?: boolean;
}

export enum XExpandablePanelCssVars {
  //
  HeaderForegroundColor = 'header-foreground-color',
  HeaderBackgroundColor = 'header-background-color',

  //
  HeaderHighlightForegroundColor = 'header-highlight-foreground-color',
  HeaderHighlightBackgroundColor = 'header-highlight-background-color',
}

export type XExpandablePandelCssVarsIdentifier =
  | XExpandablePanelCssVars
  | string;

//
//#region Inside Host Container ...
export interface IXExpandablePandelContainer
  extends IXCardNextPrevResetHostContainer {
  //
  actionProvider: Subject<XExpandablePanelActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  startupState: XStandardType<XExpandablePanelStartupStateIdentifier>;
  isAccordion: XStandardType<boolean>;
  multiExpanded: XStandardType<boolean>;
  hideDescriptionOnSmallScreens: XStandardType<boolean>;

  //
  applyColors: XStandardType<boolean>;
  headerColor: XStandardType<XColorIdentifier>;
  headerHighlightColor: XStandardType<XColorIdentifier>;

  //
  resetConfirmer: () => XStandardType<boolean>;
  selected: string;

  //
  selectedChange: EventEmitter<string>;
}

@Injectable({
  providedIn: 'root',
})
export class XExpandablePandelContainer
  extends XCardNextPrevResetHostContainer
  implements Readonly<IXExpandablePandelContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider = new Subject<XExpandablePanelActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly startupState: XStandardType<XExpandablePanelStartupStateIdentifier> =
    XExpandablePanelStartupState.ExpandAll;
  readonly isAccordion: XStandardType<boolean>;
  readonly multiExpanded: XStandardType<boolean>;
  readonly hideDescriptionOnSmallScreens: XStandardType<boolean>;

  //
  readonly applyColors: XStandardType<boolean> = true;
  readonly headerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Primary;
  readonly headerHighlightColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;

  //
  readonly resetConfirmer: () => XStandardType<boolean> = () => {
    return new Promise<boolean>((resolver) => {
      //
      this.managerService.dialogService.presentAreYouSureYesNoDialog(
        () => resolver(true),
        XModalButtonRole.Destructive,
        () => resolver(false)
      );
    });
  };
  readonly selected: string;

  //
  readonly selectedChange: EventEmitter<string>;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    public managerService: XManagerService,
    protected cardPropertyProvider: XComponentCardContainer,
    protected nextPrevResetHostPropertyProvider: XNextPrevResetHostContainer
  ) {
    super(cardPropertyProvider, nextPrevResetHostPropertyProvider);
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XExpandablePandelContainerComponent
  extends XCardNextPrevResetHostContainerComponent
  implements IXExpandablePandelContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XExpandablePanelActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  startupState: XStandardType<XExpandablePanelStartupStateIdentifier> =
    this.propertyProvider.startupState;

  @Input()
  isAccordion: XStandardType<boolean> = this.propertyProvider.isAccordion;

  @Input()
  multiExpanded: XStandardType<boolean> = this.propertyProvider.multiExpanded;

  @Input()
  hideDescriptionOnSmallScreens: XStandardType<boolean> =
    this.propertyProvider.hideDescriptionOnSmallScreens;

  //
  @Input()
  applyColors: XStandardType<boolean> = this.propertyProvider.applyColors;

  @Input()
  headerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerColor;

  @Input()
  headerHighlightColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerHighlightColor;

  //
  @Input()
  resetConfirmer: () => XStandardType<boolean> =
    this.propertyProvider.resetConfirmer;

  @Input()
  selected: string = this.propertyProvider.selected;

  //
  @Output()
  selectedChange: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XExpandablePandelContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXExpandablePandelContainer
  ): Observable<IXExpandablePandelContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            startupState: this.getValue(
              container ? container.startupState : this.startupState
            ),
            isAccordion: this.getValue(
              container ? container.isAccordion : this.isAccordion
            ),
            multiExpanded: this.getValue(
              container ? container.multiExpanded : this.multiExpanded
            ),
            hideDescriptionOnSmallScreens: this.getValue(
              container
                ? container.hideDescriptionOnSmallScreens
                : this.hideDescriptionOnSmallScreens
            ),
            applyColors: this.getValue(
              container ? container.applyColors : this.applyColors
            ),
            headerColor: this.getValue(
              container ? container.headerColor : this.headerColor
            ),
            headerHighlightColor: this.getValue(
              container
                ? container.headerHighlightColor
                : this.headerHighlightColor
            ),
            resetConfirmer: this.getValue(
              container ? container.resetConfirmer : this.resetConfirmer
            ),
            selected: this.getValue(
              container ? container.selected : this.selected
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            selectedChange: this.selectedChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-panel\x-panel.component.html

<!-- Title -->
<ng-template #title>
  <ng-content select="[x-panel-title]"></ng-content>
</ng-template>

<!-- Description -->
<ng-template #description>
  <ng-content select="[x-panel-description]"></ng-content>
</ng-template>

<!-- Content -->
<ng-template #content>
  <ng-content></ng-content>
</ng-template>

<!-- Actions -->
<ng-template #actions>
  <ng-content select="[x-panel-actions]"></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-panel\x-panel.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-panel\x-panel.component.ts

import {
  Component,
  ViewChild,
  TemplateRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XPanelAction,
  XPanelActionModel,
  XPanelContainerComponent,
} from './x-panel.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';

@Component({
  selector: 'x-panel',
  templateUrl: './x-panel.component.html',
  styleUrls: ['./x-panel.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XPanelComponent extends XPanelContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  //#region ViewChilds ...
  @ViewChild('title', { static: false })
  title!: TemplateRef<any>;

  @ViewChild('description', { static: false })
  description!: TemplateRef<any>;

  @ViewChild('content', { static: false })
  content!: TemplateRef<any>;

  @ViewChild('actions', { static: false })
  actions!: TemplateRef<any>;
  //#endregion
  //#endregion

  //
  //#region LifeCycle ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);
    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XPanelComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Privates ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XPanelActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XPanelAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-expandable-panel\x-panel\x-panel.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import {
  IXNextPrevResetContainer,
  XNextPrevResetContainer,
  XNextPrevResetContainerComponent,
} from '../../classes/i-x-next-prev-reset.container';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { XStandardType, XOneOrManyType, generateUuid } from 'x-framework-core';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XPanelAction {
  Refresh = 'refresh',
}

export type XPanelActionIdentifier = XPanelAction | string;

export interface XPanelActionModel
  extends XBaseActionModel<XPanelActionIdentifier> {}

//
//#region Inside Card Container ...
export interface IXPanelContainer extends IXNextPrevResetContainer {
  //
  actionProvider: Subject<XPanelActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  id: XStandardType<string>;

  //
  hideToggle: XStandardType<boolean>;
  expanded: XStandardType<boolean>;
  hasCustomActions: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root'
})
export class XPanelContainer
  extends XNextPrevResetContainer
  implements Readonly<IXPanelContainer>
{
  //
  //#region Props ...
  readonly actionProvider = new Subject<XPanelActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly id: XStandardType<string> = generateUuid('XPanelComponent');

  //
  readonly hideToggle: XStandardType<boolean> = false;
  readonly expanded: XStandardType<boolean>;
  readonly hasCustomActions: XStandardType<boolean>;
  //#endregion
}

@Injectable({
  providedIn: 'root'
})
@Component({
  template: `
    <!-- 
    <ng-container *ngIf="extractProps() | async as props">
      <x-panel
        [id]="props.id"
        [hasNext]="props.hasNext"
        [hasPrev]="props.hasPrev"
        [cssClass]="props.cssClass"
        [expanded]="props.expanded"
        [hasReset]="props.hasReset"
        [hideToggle]="props.hideToggle"
        [nextButton]="props.nextButton"
        [prevButton]="props.prevButton"
        [disablePrev]="props.disablePrev"
        [disableNext]="props.disableNext"
        [resetButton]="props.resetButton"
        [nextPosition]="props.nextPosition"
        [nextCssClass]="props.nextCssClass"
        [prevPosition]="props.prevPosition"
        [prevCssClass]="props.prevCssClass"
        [disableReset]="props.disableReset"
        [prevConfirmer]="props.prevConfirmer"
        [nextConfirmer]="props.nextConfirmer"
        [resetPosition]="props.resetPosition"
        [resetCssClass]="props.resetCssClass"
        [actionProvider]="props.actionProvider"
        [resetConfirmer]="props.resetConfirmer"
        [hasCustomActions]="props.hasCustomActions"
      >
      </x-panel>
    </ng-container>
  -->
  `,
})
export abstract class XPanelContainerComponent
  extends XNextPrevResetContainerComponent
  implements IXPanelContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XPanelActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  id: XStandardType<string> = this.propertyProvider.id;

  @Input()
  hideToggle: XStandardType<boolean> = this.propertyProvider.hideToggle;

  @Input()
  expanded: XStandardType<boolean> = this.propertyProvider.expanded;

  @Input()
  hasCustomActions: XStandardType<boolean> =
    this.propertyProvider.hasCustomActions;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XPanelContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXPanelContainer
  ): Observable<IXPanelContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            id: this.getValue(container ? container.id : this.id),
            hideToggle: this.getValue(
              container ? container.hideToggle : this.hideToggle
            ),
            expanded: this.getValue(
              container ? container.expanded : this.expanded
            ),
            hasCustomActions: this.getValue(
              container ? container.hasCustomActions : this.hasCustomActions
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-fab\x-fab.component.html

<section
  class="x-fab x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
  *ngIf="parsedItem && (notNullOrUndefinedValue(item) | async)"
>
  <!-- Fab Main Button -->
  <ion-fab
    #fab
    [slot]="getFixedValue(parsedItem) | async"
    [edge]="getOptionalValue(parsedItem.edged) | async"
    [vertical]="getValue(parsedItem.vertical) | async"
    [horizontal]="getValue(parsedItem.horizontal) | async"
    [activated]="getOptionalValue(parsedItem.activated) | async"
  >
    <!-- Fab Main Button -->
    <ng-container
      *ngTemplateOutlet="fabButtonRef; context: { $implicit: parsedItem }"
    ></ng-container>

    <!-- Fab List Buttons -->
    <section
      class="x-fab-childs-container"
      [ngClass]="getValue(childsCssClass) | async"
    >
      <ng-container *ngTemplateOutlet="fabChildsRef"></ng-container>
    </section>
  </ion-fab>
</section>

<!-- Fab Childs -->
<ng-template #fabChildsRef>
  <!-- Fab Lists -->
  <ng-container *ngIf="hasChildValue(parsedChilds) | async">
    <!-- Top Side -->
    <ion-fab-list side="top" *ngIf="hasTopSideChilds">
      <ng-container *ngFor="let child of parsedChilds">
        <ng-container *ngIf="isTopSideFab(child) | async">
          <ng-container
            *ngTemplateOutlet="fabButtonRef; context: { $implicit: child }"
          ></ng-container>
        </ng-container>
      </ng-container>
    </ion-fab-list>

    <!-- End Side -->
    <ion-fab-list side="end" *ngIf="hasEndSideChilds">
      <ng-container *ngFor="let child of parsedChilds">
        <ng-container *ngIf="isEndSideFab(child) | async">
          <ng-container
            *ngTemplateOutlet="fabButtonRef; context: { $implicit: child }"
          ></ng-container>
        </ng-container>
      </ng-container>
    </ion-fab-list>

    <!-- Start Side -->
    <ion-fab-list side="start" *ngIf="hasStartSideChilds">
      <ng-container *ngFor="let child of parsedChilds">
        <ng-container *ngIf="isStartSideFab(child) | async">
          <ng-container
            *ngTemplateOutlet="fabButtonRef; context: { $implicit: child }"
          ></ng-container>
        </ng-container>
      </ng-container>
    </ion-fab-list>

    <!-- Bottom Side -->
    <ion-fab-list side="bottom" *ngIf="hasBottomSideChilds">
      <ng-container *ngFor="let child of parsedChilds">
        <ng-container *ngIf="isBottomSideFab(child) | async">
          <ng-container
            *ngTemplateOutlet="fabButtonRef; context: { $implicit: child }"
          ></ng-container>
        </ng-container>
      </ng-container>
    </ion-fab-list>
  </ng-container>
</ng-template>

<!-- Represent Single Fab -->
<ng-template #fabButtonRef let-item>
  <ion-fab-button
    [color]="getValue(item.color) | async"
    (click)="handleFabButtonClicked(item)"
    [size]="getOptionalValue(item.size) | async"
    [href]="getOptionalValue(item.href) | async"
    [hidden]="getOptionalValue(item.hidden) | async"
    [download]="getOptionalValue(item.download) | async"
    [matTooltip]="getResourceValue(item.tooltip) | async"
    [target]="getOptionalValue(item.routerTarget) | async"
    [routerDirection]="getOptionalValue(item.routerDirection) | async"
    [disabled]="
      uiDisabled ||
      loading ||
      (applyTakeUntilWrapper(getOptionalValue(item.disabled)) | async)
    "
  >
    <x-icon
      [foregroundColor]="false"
      [name]="getValue(item.icon) | async"
      [color]="getValue(item.color) | async"
    ></x-icon>
  </ion-fab-button>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-fab\x-fab.component.scss

:host ::ng-deep .x-icon {
    background-color: transparent !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-fab\x-fab.component.ts

import {
  XFabItem,
  XFabButton,
  XFabAction,
  XFabButtonSide,
  XFabActionModel,
  XFabContainerComponent,
} from './x-fab.typings';
import {
  nameof,
  hasChild,
  isFunction,
  isNullOrUndefined,
} from 'x-framework-core';
import { IonFab } from '@ionic/angular';
import { concatMap, map } from 'rxjs/operators';
import { of, Observable, Subject, Subscription } from 'rxjs';
import { Component, ViewChild, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-fab',
  templateUrl: './x-fab.component.html',
  styleUrls: ['./x-fab.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XFabComponent extends XFabContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  private FAB: IonFab;
  private PARSED_ITEM: XFabItem;
  private PARSED_CHILDS: XFabButton[] = [];
  private HAS_TOP_SIDE_CHILDS: boolean;
  private HAS_END_SIDE_CHILDS: boolean;
  private HAS_START_SIDE_CHILDS: boolean;
  private HAS_BOTTOM_SIDE_CHILDS: boolean;

  //
  @ViewChild('fab', { static: false })
  set fab(v: IonFab) {
    //
    if (!v) {
      return;
    }

    //
    this.FAB = v;
    this.detectChanges();
  }

  get fab() {
    return this.FAB;
  }

  get parsedItem() {
    return this.PARSED_ITEM;
  }

  get parsedChilds() {
    return this.PARSED_CHILDS;
  }

  get hasTopSideChilds() {
    return this.HAS_TOP_SIDE_CHILDS;
  }

  get hasEndSideChilds() {
    return this.HAS_END_SIDE_CHILDS;
  }

  get hasStartSideChilds() {
    return this.HAS_START_SIDE_CHILDS;
  }

  get hasBottomSideChilds() {
    return this.HAS_BOTTOM_SIDE_CHILDS;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XFabComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Items Changes ...
    //
    const isItemsChanged = changeKeys.includes(nameof<XFabComponent>('item'));
    if (isItemsChanged) {
      //
      const item = await this.getValueAsync(this.item);
      this.detectChanges();
      if (!isNullOrUndefined(item)) {
        //
        this.PARSED_ITEM = item;
        this.detectChanges();

        //
        if (item.childs) {
          //
          const childs = await this.getValueAsync(item.childs);
          if (!hasChild(childs)) {
            return;
          }

          //
          // Sorting Childs ...
          this.PARSED_CHILDS = childs.sort((a, b) => a.index - b.index);
          this.detectChanges();

          //
          // Determine Sides ...
          this.PARSED_CHILDS.forEach(async (ch) => {
            //
            const side = await this.getValueAsync(ch.side);
            switch (side) {
              case XFabButtonSide.TOP:
                if (!this.HAS_TOP_SIDE_CHILDS) {
                  this.HAS_TOP_SIDE_CHILDS = true;
                }
                break;

              case XFabButtonSide.END:
                if (!this.HAS_END_SIDE_CHILDS) {
                  this.HAS_END_SIDE_CHILDS = true;
                }
                break;

              case XFabButtonSide.START:
                if (!this.HAS_START_SIDE_CHILDS) {
                  this.HAS_START_SIDE_CHILDS = true;
                }
                break;

              case XFabButtonSide.BOTTOM:
                if (!this.HAS_BOTTOM_SIDE_CHILDS) {
                  this.HAS_BOTTOM_SIDE_CHILDS = true;
                }
                break;
            }

            //
            this.detectChanges();
          });
        }
      }
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Provider ...
  getFixedValue(items: XFabItem) {
    return of(items).pipe(
      concatMap((rItems) => {
        //
        if (!rItems) {
          return of('');
        }

        //
        const obs = this.getOptionalValue(rItems.fixed) as Observable<boolean>;
        return obs.pipe(map((res) => (res === true ? 'fixed' : '')));
      })
    );
  }

  //
  //#region Fab Button Side Providers ...
  isTopSideFab(child: XFabButton) {
    return this.getValue(child.side).pipe(
      map((side) => side === XFabButtonSide.TOP.toString())
    );
  }

  isEndSideFab(child: XFabButton) {
    return this.getValue(child.side).pipe(
      map((side) => side === XFabButtonSide.END.toString())
    );
  }

  isStartSideFab(child: XFabButton) {
    return this.getValue(child.side).pipe(
      map((side) => side === XFabButtonSide.START.toString())
    );
  }

  isBottomSideFab(child: XFabButton) {
    return this.getValue(child.side).pipe(
      map((side) => side === XFabButtonSide.BOTTOM.toString())
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleFabButtonClicked(item: XFabButton | XFabButton) {
    //
    if (!item) {
      return;
    }

    //
    if (item.handler && isFunction(item.handler)) {
      await this.getValueAsync(item.handler(item.id));
    }
  }
  //#endregion

  //
  //#region Actions ...
  async close() {
    //
    if (!this.fab) {
      return;
    }

    //
    await this.fab.close();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XFabActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.actionProvider.asObservable()
    ).subscribe(async (model) => {
      //
      if (!model) {
        return;
      }

      //
      switch (model.action) {
        //
        case XFabAction.Refresh:
          this.detectChanges();
          break;

        //
        case XFabAction.CLOSE:
          await this.close();
          break;
      }
    });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-fab\x-fab.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XFabComponent } from './x-fab.component';
import { XIconModule } from '../x-icon/x-icon.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XFabComponent],
  imports: [CommonModule, XBaseComponentModule, XIconModule],
  exports: [XBaseComponentModule, XIconModule, XFabComponent],
})
export class XFabModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-fab\x-fab.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XRouterDirection,
  XResourceIdentifier,
  XRouterTargetIdentifier,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XFabVerticalSlot {
  TOP = 'top',
  CENTER = 'center',
  BOTTOM = 'bottom',
}

export type XFabVerticalSlotIdentifier = XFabVerticalSlot | string;

export enum XFabHorizontalSlot {
  START = 'start',
  CENTER = 'center',
  END = 'end',
}

export type XFabHorizontalSlotIdentifier = XFabHorizontalSlot | string;

export enum XFabButtonSide {
  TOP = 'top',
  END = 'end',
  START = 'start',
  BOTTOM = 'bottom',
}

export type XFabButtonSideIdentifier = XFabButtonSide | string;

export enum XFabButtonSize {
  REGULAR = '',
  MINI = 'small',
}

export type XFabButtonSizeIdentifier = XFabButtonSize | string;

export interface XFabButton {
  //
  id: string;
  index: number;

  //
  icon: XStandardType<XIconIdentifier>;
  side: XStandardType<XFabButtonSideIdentifier>;
  size?: XStandardType<XFabButtonSizeIdentifier>;

  //
  disabled?: XStandardType<boolean>;
  hidden?: XStandardType<boolean>;

  //
  color: XStandardType<XColorIdentifier>;

  //
  tooltip?: XStandardType<XResourceIdentifier>;

  //
  href?: XStandardType<string>;
  download?: XStandardType<string>;
  routerDirection?: XStandardType<XRouterDirection>;
  routerTarget?: XStandardType<XRouterTargetIdentifier>;

  //
  handler?: (id?: string) => XStandardType<void>;
}

type FAB_MAIN_BUTTON = Pick<
  XFabButton,
  | 'id'
  | 'icon'
  | 'size'
  | 'color'
  | 'hidden'
  | 'handler'
  | 'tooltip'
  | 'disabled'
  | 'href'
  | 'download'
  | 'routerDirection'
  | 'routerTarget'
>;

export interface XFabItem extends FAB_MAIN_BUTTON {
  //
  vertical: XStandardType<XFabVerticalSlotIdentifier>;
  horizontal: XStandardType<XFabHorizontalSlotIdentifier>;

  //
  fixed?: XStandardType<boolean>;
  edged?: XStandardType<boolean>;

  //
  activated?: XStandardType<boolean>;

  //
  childs?: XStandardType<XFabButton[]>;
}

export enum XFabAction {
  CLOSE = 'close',

  //
  Refresh = 'refresh',
}

export type XFabActionIdentifier = XFabAction | string;

export interface XFabActionModel
  extends XBaseActionModel<XFabActionIdentifier> {}

//
//#region Container ...
export interface IXFabContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XFabActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  childsCssClass: XStandardType<XOneOrManyType<string>>;
  item: XStandardType<XFabItem>;
}

@Injectable({
  providedIn: 'root'
})
export class XFabContainer
  extends XBaseContainer
  implements Readonly<IXFabContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XFabActionModel> =
    new Subject<XFabActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly childsCssClass: XStandardType<XOneOrManyType<string>>;
  readonly item: XStandardType<XFabItem> = null;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XFabContainerComponent
  extends XBaseContainerComponent
  implements IXFabContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XFabActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  childsCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.childsCssClass;

  @Input()
  item: XStandardType<XFabItem> = this.propertyProvider.item;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFabContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(container?: IXFabContainer): Observable<IXFabContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      childsCssClass: this.getValue(
        container ? container.childsCssClass : this.childsCssClass
      ),
      item: this.getValue(container ? container.item : this.item),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-drop-area\x-file-drop-area.component.html

<!-- Card Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component Template -->
<ng-template #componentRef>
  <div
    class="x-file-drop-area x-wrapper"
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [isForeground]="getValue(foregroundColor) | async"
    [class.no-drag]="
      uiDisabled || loading || (getValue(dragDisabled) | async)
    "
  >
    <section
      xDroppable
      class="x-droppable-container"
      [errorColor]="getValue(errorColor) | async"
      [accentColor]="getValue(accentColor) | async"
      [successColor]="getValue(successColor) | async"
      [error]="notNullOrUndefinedValue(error) | async"
      (filesChangeEmiter)="filesChangeEmiter.emit($event)"
      [disableFileCheck]="getValue(disableFileCheck) | async"
      [allowedExtensions]="getValue(allowedExtensions) | async"
      [dragDisabled]="
        uiDisabled || loading || (getValue(dragDisabled) | async)
      "
      [supportsMultipleFiles]="getValue(supportsMultipleFiles) | async"
    >
      <ng-container
        *ngTemplateOutlet="
          contentTemplate ? contentTemplate : defaultContentTemplateRef
        "
      ></ng-container>
    </section>
  </div>
</ng-template>

<!-- Default Content Template -->
<ng-template #defaultContentTemplateRef>
  <div class="x-drop-area">
    <div class="x-icon">
      <x-icon
        [foregroundColor]="false"
        [cssClass]="'transparent'"
        [name]="getValue(icon) | async"
        [color]="getValue(color) | async"
      ></x-icon>
    </div>
    <ion-text class="x-drag-drop-title">
      {{ getResourceValue(message) | async }}
    </ion-text>
  </div>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-drop-area\x-file-drop-area.component.scss

:host {
  width: inherit;
  height: inherit;

  --label-color: var(--x-color-light-contrast);
}

.x-wrapper {
  cursor: grab;
}

.x-wrapper:active {
  cursor: grabbing;
}

.no-drag {
  cursor: unset;
}

.x-droppable-container {
  border-radius: 10px;
}

.x-drop-area {
  width: 100%;
  height: auto;

  box-sizing: border-box;

  border-radius: 10px;
  border: 2px dashed var(--label-color);

  text-align: center;

  padding: {
    top: 16px;
    bottom: 16px;
  }
}

.x-drag-drop-title {
  color: var(--label-color);
}

.x-icon {
  margin: {
    left: auto;
    right: auto;
  }

  margin-bottom: 16px;

  font-size: 8px;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-drop-area\x-file-drop-area.component.ts

import {
  nameof,
  parseXColor,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XFileDropAreaAction,
  XFileDropAreaCssVars,
  XFileDropAreaActionModel,
  XFileDropAreaContainerComponent,
} from './x-file-drop-area.typings';
import { Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-file-drop-area',
  templateUrl: './x-file-drop-area.component.html',
  styleUrls: ['./x-file-drop-area.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XFileDropAreaComponent extends XFileDropAreaContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Color Changed ...
    const isColorChanged = changeKeys.includes(
      nameof<XFileDropAreaComponent>('color')
    );
    if (isColorChanged) {
      await this.applyColor();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XFileDropAreaComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Provider ...
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XFileDropAreaActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe((model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XFileDropAreaAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async applyColor() {
    //
    const color = await this.getValueAsync(this.color);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFileDropAreaCssVars.LabelColor, `var(${fgColor})`);

    //
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-drop-area\x-file-drop-area.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XListModule } from '../x-list/x-list.module';
import { XFileDropAreaComponent } from './x-file-drop-area.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XFileDropAreaComponent],
  imports: [
    XListModule,
    XCardModule,
    XIconModule,
    CommonModule,
    XBaseComponentModule,
  ],
  exports: [
    XCardModule,
    XIconModule,
    XListModule,
    XBaseComponentModule,
    XFileDropAreaComponent,
  ],
})
export class XFileDropAreaModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-drop-area\x-file-drop-area.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import {
  XBaseComponentContainer,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../classes/i-x-base.container';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XIconIdentifier, XIconNames } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XFileDropAreaAction {
  Refresh = 'refresh',
}

export type XFileDropAreaActionIdentifier = XFileDropAreaAction | string;

export interface XFileDropAreaActionModel
  extends XBaseActionModel<XFileDropAreaActionIdentifier> {}

export enum XFileDropAreaCssVars {
  LabelColor = 'label-color',
}

//
//#region Container ...
export interface IXFileDropAreaContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XFileDropAreaActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  icon: XStandardType<XIconIdentifier>;
  message: XStandardType<XResourceIdentifier>;

  //
  disableFileCheck: XStandardType<boolean>;

  //
  supportsMultipleFiles: XStandardType<boolean>;
  allowedExtensions: XStandardType<XOneOrManyType<string>>;

  //
  dragDisabled: XStandardType<boolean>;
  contentTemplate: TemplateRef<any>;
  filesChangeEmiter: EventEmitter<XOneOrManyType<File>>;

  //
  successColor: XStandardType<XColorIdentifier>;
  errorColor: XStandardType<XColorIdentifier>;
  accentColor: XStandardType<XColorIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XFileDropAreaContainer
  extends XComponentCardContainer
  implements Readonly<IXFileDropAreaContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider = new Subject<XFileDropAreaActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  readonly icon: XStandardType<XIconIdentifier> = XIconNames.arrow_down;
  readonly message: XStandardType<XResourceIdentifier> =
    XResourceIDs.drag_drop_here;

  //
  readonly disableFileCheck: XStandardType<boolean> = false;

  //
  readonly allowedExtensions: XStandardType<XOneOrManyType<string>> = [];
  readonly supportsMultipleFiles: XStandardType<boolean> = true;

  //
  readonly dragDisabled: XStandardType<boolean> = false;
  readonly contentTemplate: TemplateRef<any>;
  readonly filesChangeEmiter = new EventEmitter<XOneOrManyType<File>>();

  //
  readonly successColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Success;
  readonly errorColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Danger;
  readonly accentColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Secondary;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XFileDropAreaContainerComponent
  extends XComponentCardContainerComponent
  implements IXFileDropAreaContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XFileDropAreaActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  icon: XStandardType<XIconIdentifier> = this.propertyProvider.icon;

  @Input()
  message: XStandardType<XResourceIdentifier> = this.propertyProvider.message;

  //
  @Input()
  disableFileCheck: XStandardType<boolean> =
    this.propertyProvider.disableFileCheck;

  //
  @Input()
  supportsMultipleFiles: XStandardType<boolean> =
    this.propertyProvider.supportsMultipleFiles;

  @Input()
  allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.allowedExtensions;

  //
  @Input()
  dragDisabled: XStandardType<boolean> = this.propertyProvider.dragDisabled;

  @Input()
  contentTemplate: TemplateRef<any> = this.propertyProvider.contentTemplate;

  @Output()
  filesChangeEmiter: EventEmitter<XOneOrManyType<File>> = new EventEmitter();

  //
  @Input()
  successColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.successColor;

  @Input()
  errorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.errorColor;

  @Input()
  accentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.accentColor;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFileDropAreaContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXFileDropAreaContainer
  ): Observable<IXFileDropAreaContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            icon: this.getValue(container ? container.icon : this.icon),
            message: this.getValue(
              container ? container.message : this.message
            ),
            disableFileCheck: this.getValue(
              container ? container.disableFileCheck : this.disableFileCheck
            ),
            supportsMultipleFiles: this.getValue(
              container
                ? container.supportsMultipleFiles
                : this.supportsMultipleFiles
            ),
            allowedExtensions: this.getValue(
              container ? container.allowedExtensions : this.allowedExtensions
            ),
            dragDisabled: this.getValue(
              container ? container.dragDisabled : this.dragDisabled
            ),
            successColor: this.getValue(
              container ? container.successColor : this.successColor
            ),
            errorColor: this.getValue(
              container ? container.errorColor : this.errorColor
            ),
            accentColor: this.getValue(
              container ? container.accentColor : this.accentColor
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            contentTemplate: container
              ? container.contentTemplate
              : this.contentTemplate,

            //
            filesChangeEmiter: this.filesChangeEmiter,
          };
        })
      );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentFileDropAreaContainer = {
  [TKey in keyof IXFileDropAreaContainer as `fileDropArea${Capitalize<
    string & TKey
  >}`]: IXFileDropAreaContainer[TKey];
};

export interface IXComponentFileDropAreaContainer
  extends TXComponentFileDropAreaContainer,
    IXBaseComponentContainer {
  showFileDropArea: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentFileDropAreaContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentFileDropAreaContainer>
{
  //
  //#region Props ...
  //
  readonly showFileDropArea: XStandardType<boolean> = true;

  //
  readonly fileDropAreaActionProvider: Subject<XFileDropAreaActionModel> =
    this.propertyProvider.actionProvider;

  readonly fileDropAreaCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly fileDropAreaColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;

  readonly fileDropAreaForegroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  readonly fileDropAreaIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.icon;

  readonly fileDropAreaMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.message;

  //
  readonly fileDropAreaDisableFileCheck: XStandardType<boolean> =
    this.propertyProvider.disableFileCheck;

  //
  readonly fileDropAreaSupportsMultipleFiles: XStandardType<boolean> =
    this.propertyProvider.supportsMultipleFiles;

  readonly fileDropAreaAllowedExtensions: XStandardType<
    XOneOrManyType<string>
  > = this.propertyProvider.allowedExtensions;

  //
  readonly fileDropAreaDragDisabled: XStandardType<boolean> =
    this.propertyProvider.dragDisabled;

  readonly fileDropAreaContentTemplate: TemplateRef<any> =
    this.propertyProvider.contentTemplate;

  readonly fileDropAreaFilesChangeEmiter: EventEmitter<XOneOrManyType<File>> =
    this.propertyProvider.filesChangeEmiter;

  //
  readonly fileDropAreaSuccessColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.successColor;

  readonly fileDropAreaErrorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.errorColor;

  readonly fileDropAreaAccentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.accentColor;

  //
  //#region ComponentsCard ...
  readonly fileDropAreaIsInPage: XStandardType<boolean> = false;
  readonly fileDropAreaWrapWithCard: XStandardType<boolean> = false;
  readonly fileDropAreaCardShowFooter: XStandardType<boolean> = false;
  readonly fileDropAreaCardShowActions: XStandardType<boolean> = false;
  readonly fileDropAreaCardColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly fileDropAreaCardForegroundColor: XStandardType<boolean> = false;
  readonly fileDropAreaCardTitle: XStandardType<XResourceIdentifier>;
  readonly fileDropAreaCardActionProvider: Subject<XCardActionModel> =
    new Subject<XCardActionModel>();
  readonly fileDropAreaCardSubTitle: XStandardType<XResourceIdentifier>;
  readonly fileDropAreaCardCssClass: XStandardType<XOneOrManyType<string>>;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XFileDropAreaContainer) {
    super();
  }
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
@Component({
  template: `
    <!-- 
        <ng-container *ngIf="extractProps() | async as props">
          <ng-container *ngIf="props.showFileDropArea">
            <x-file-drop-area
              [icon]="props.fileDropAreaIcon"
              [color]="props.fileDropAreaColor"
              [message]="props.fileDropAreaMessage"
              [cssClass]="props.fileDropAreaCssClass"
              [isInPage]="props.fileDropAreaIsInPage"
              [cardTitle]="props.fileDropAreaCardTitle"
              [cardColor]="props.fileDropAreaCardColor"
              [errorColor]="props.fileDropAreaErrorColor"
              [accentColor]="props.fileDropAreaAccentColor"
              [dragDisabled]="props.fileDropAreaDragDisabled"
              [successColor]="props.fileDropAreaSuccessColor"
              [cardCssClass]="props.fileDropAreaCardCssClass"
              [wrapWithCard]="props.fileDropAreaWrapWithCard"
              [cardSubTitle]="props.fileDropAreaCardSubTitle"
              [actionProvider]="props.fileDropAreaActionProvider"
              [cardShowFooter]="props.fileDropAreaCardShowFooter"
              [foregroundColor]="props.fileDropAreaForegroundColor"
              [contentTemplate]="props.fileDropAreaContentTemplate"
              [foregroundColor]="props.fileDropAreaForegroundColor"
              [disableFileCheck]="props.fileDropAreaDisableFileCheck"
              [allowedExtensions]="props.fileDropAreaAllowedExtensions"
              (filesChangeEmiter)="(props.fileDropAreaFilesChangeEmiter)"
              [cardActionProvider]="props.fileDropAreaCardActionProvider"
              [cardForegroundColor]="props.fileDropAreaCardForegroundColor"
              [supportsMultipleFiles]="props.fileDropAreaSupportsMultipleFiles"
            ></x-file-drop-area>
          </ng-container>
        </ng-container> 
    -->
  `,
})
export abstract class XComponentFileDropAreaContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentFileDropAreaContainer
{
  //
  //#region Props ...
  //
  @Input()
  fileDropAreaActionProvider: Subject<XFileDropAreaActionModel> =
    this.propertyProvider.fileDropAreaActionProvider;

  @Input()
  fileDropAreaCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaCssClass;

  //
  @Input()
  fileDropAreaColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaColor;

  @Input()
  fileDropAreaForegroundColor: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaForegroundColor;

  //
  @Input()
  fileDropAreaIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.fileDropAreaIcon;

  @Input()
  fileDropAreaMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaMessage;

  //
  @Input()
  fileDropAreaDisableFileCheck: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaDisableFileCheck;

  //
  @Input()
  fileDropAreaSupportsMultipleFiles: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaSupportsMultipleFiles;

  @Input()
  fileDropAreaAllowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaAllowedExtensions;

  //
  @Input()
  fileDropAreaDragDisabled: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaDragDisabled;

  @Input()
  fileDropAreaContentTemplate: TemplateRef<any> =
    this.propertyProvider.fileDropAreaContentTemplate;

  @Output()
  fileDropAreaFilesChangeEmiter: EventEmitter<XOneOrManyType<File>> =
    new EventEmitter();

  //
  @Input()
  fileDropAreaSuccessColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaSuccessColor;

  @Input()
  fileDropAreaErrorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaErrorColor;

  @Input()
  fileDropAreaAccentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaAccentColor;

  //
  @Input()
  showFileDropArea: XStandardType<boolean> =
    this.propertyProvider.showFileDropArea;

  //
  //#region ComponentsCard ...
  @Input()
  fileDropAreaIsInPage: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaIsInPage;

  @Input()
  fileDropAreaWrapWithCard: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaWrapWithCard;

  @Input()
  fileDropAreaCardShowFooter: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaCardShowFooter;

  @Input()
  fileDropAreaCardShowActions: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaCardShowActions;

  @Input()
  fileDropAreaCardColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaCardColor;

  @Input()
  fileDropAreaCardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaCardForegroundColor;

  @Input()
  fileDropAreaCardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaCardTitle;

  @Input()
  fileDropAreaCardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.fileDropAreaCardActionProvider;

  @Input()
  fileDropAreaCardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaCardSubTitle;

  @Input()
  fileDropAreaCardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaCardCssClass;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentFileDropAreaContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentFileDropAreaContainer
  ): Observable<IXComponentFileDropAreaContainer> {
    return forkJoin({
      //
      fileDropAreaCssClass: this.getValue(
        container ? container.fileDropAreaCssClass : this.fileDropAreaCssClass
      ),
      fileDropAreaColor: this.getValue(
        container ? container.fileDropAreaColor : this.fileDropAreaColor
      ),
      fileDropAreaForegroundColor: this.getValue(
        container
          ? container.fileDropAreaForegroundColor
          : this.fileDropAreaForegroundColor
      ),
      fileDropAreaIcon: this.getValue(
        container ? container.fileDropAreaIcon : this.fileDropAreaIcon
      ),
      fileDropAreaMessage: this.getValue(
        container ? container.fileDropAreaMessage : this.fileDropAreaMessage
      ),
      fileDropAreaDisableFileCheck: this.getValue(
        container
          ? container.fileDropAreaDisableFileCheck
          : this.fileDropAreaDisableFileCheck
      ),
      fileDropAreaSupportsMultipleFiles: this.getValue(
        container
          ? container.fileDropAreaSupportsMultipleFiles
          : this.fileDropAreaSupportsMultipleFiles
      ),
      fileDropAreaAllowedExtensions: this.getValue(
        container
          ? container.fileDropAreaAllowedExtensions
          : this.fileDropAreaAllowedExtensions
      ),
      fileDropAreaDragDisabled: this.getValue(
        container
          ? container.fileDropAreaDragDisabled
          : this.fileDropAreaDragDisabled
      ),
      fileDropAreaContentTemplate: this.getValue(
        container
          ? container.fileDropAreaContentTemplate
          : this.fileDropAreaContentTemplate
      ),
      fileDropAreaSuccessColor: this.getValue(
        container
          ? container.fileDropAreaSuccessColor
          : this.fileDropAreaSuccessColor
      ),
      fileDropAreaErrorColor: this.getValue(
        container
          ? container.fileDropAreaErrorColor
          : this.fileDropAreaErrorColor
      ),
      fileDropAreaAccentColor: this.getValue(
        container
          ? container.fileDropAreaAccentColor
          : this.fileDropAreaAccentColor
      ),
      showFileDropArea: this.getValue(
        container ? container.showFileDropArea : this.showFileDropArea
      ),
      fileDropAreaIsInPage: this.getValue(
        container ? container.fileDropAreaIsInPage : this.fileDropAreaIsInPage
      ),
      fileDropAreaWrapWithCard: this.getValue(
        container
          ? container.fileDropAreaWrapWithCard
          : this.fileDropAreaWrapWithCard
      ),
      fileDropAreaCardShowFooter: this.getValue(
        container
          ? container.fileDropAreaCardShowFooter
          : this.fileDropAreaCardShowFooter
      ),
      fileDropAreaCardShowActions: this.getValue(
        container
          ? container.fileDropAreaCardShowActions
          : this.fileDropAreaCardShowActions
      ),
      fileDropAreaCardColor: this.getValue(
        container ? container.fileDropAreaCardColor : this.fileDropAreaCardColor
      ),
      fileDropAreaCardForegroundColor: this.getValue(
        container
          ? container.fileDropAreaCardForegroundColor
          : this.fileDropAreaCardForegroundColor
      ),
      fileDropAreaCardTitle: this.getValue(
        container ? container.fileDropAreaCardTitle : this.fileDropAreaCardTitle
      ),
      fileDropAreaCardSubTitle: this.getValue(
        container
          ? container.fileDropAreaCardSubTitle
          : this.fileDropAreaCardSubTitle
      ),
      fileDropAreaCardCssClass: this.getValue(
        container
          ? container.fileDropAreaCardCssClass
          : this.fileDropAreaCardCssClass
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          fileDropAreaActionProvider: container
            ? container.fileDropAreaActionProvider
            : this.fileDropAreaActionProvider,

          //
          fileDropAreaCardActionProvider: container
            ? container.fileDropAreaCardActionProvider
            : this.fileDropAreaCardActionProvider,

          //
          fileDropAreaFilesChangeEmiter: this.fileDropAreaFilesChangeEmiter,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-upload\x-file-upload.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component Template -->
<ng-template #componentRef>
  <div
    class="x-file-upload x-wrapper"
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [isForeground]="getValue(foregroundColor) | async"
  >
    <!-- File Upload Input -->
    <input
      hidden
      #fileInput
      type="file"
      multiple="true"
      (change)="handleFilesSelected()"
    />

    <!-- File Drop -->
    <ng-container *ngIf="getValue(showFileDropArea) | async">
      <ng-container *ngTemplateOutlet="fileDragAreaRef"></ng-container>
    </ng-container>

    <!-- File List -->
    <ng-container *ngIf="getValue(showList) | async">
      <ng-container *ngTemplateOutlet="filesListRef"></ng-container>
    </ng-container>
  </div>
</ng-template>

<!-- File Drop Area Template -->
<ng-template #fileDragAreaRef>
  <div class="x-file-upload-file-drag-drop-wrapper">
    <x-file-drop-area
      [cardTitle]=""
      [cardSubTitle]=""
      [isInPage]="false"
      [loading]="loading"
      [wrapWithCard]="false"
      [cardShowFooter]="false"
      [uiDisabled]="uiDisabled"
      [cardForegroundColor]="false"
      [supportsMultipleFiles]="true"
      [icon]="getValue(fileDropAreaIcon) | async"
      [color]="getValue(fileDropAreaColor) | async"
      [actionProvider]="fileDropAreaActionProvider"
      (filesChangeEmiter)="handleFileDropped($event)"
      [cssClass]="getValue(fileDropAreaCssClass) | async"
      [cardColor]="getValue(cardColor) | async"
      [errorColor]="getValue(fileDropAreaErrorColor) | async"
      [accentColor]="getValue(fileDropAreaAccentColor) | async"
      [message]="getResourceValue(fileDropAreaMessage) | async"
      [successColor]="getValue(fileDropAreaSuccessColor) | async"
      [dragDisabled]="getValue(fileDropAreaDragDisabled) | async"
      [allowedExtensions]="getArrayValue(allowedExtensions) | async"
      [foregroundColor]="getValue(fileDropAreaForegroundColor) | async"
      [contentTemplate]="getValue(fileDropAreaContentTemplate) | async"
      [disableFileCheck]="getValue(fileDropAreaDisableFileCheck) | async"
    ></x-file-drop-area>
  </div>
</ng-template>

<!-- Files List Template -->
<ng-template #filesListRef>
  <x-list
    #listPresenter
    [loading]="loading"
    [selectableItems]="true"
    [foregroundColor]="false"
    [uiDisabled]="uiDisabled"
    [itemTemplate]="fileItemRef"
    [color]="getValue(color) | async"
    [actionProvider]="listActionProvider"
    [showEmpty]="getValue(showEmpty) | async"
    [filteringHandler]="listFilteringHandler"
    (orderChanged)="handleListOrderChanged()"
    (removeItems)="handleRemoveItems($event)"
    (emptyRefresh)="emptyRefresh.emit($event)"
    [emptyColor]="getValue(emptyColor) | async"
    [cssClass]="getValue(listCssClass) | async"
    [emptyForegroundMessageColor]="
      getValue(emptyForegroundMessageColor) | async
    "
    [showSearchBar]="
      (getValue(showSearchBar) | async) &&
      (countChildsValue(fileListItems) | async) > 1
    "
    [emptyMessage]="getValue(emptyMessage) | async"
    [showToolbar]="getValue(listShowToolbar) | async"
    [showDivider]="getValue(listShowDivider) | async"
    [showActionBar]="getValue(showActionBar) | async"
    [searchBarColor]="getValue(searchBarColor) | async"
    [actionBarColor]="getValue(actionBarColor) | async"
    [confirmRemove]="getValue(listConfirmRemove) | async"
    [items]="fileListItemsSubject.asObservable() | async"
    [emptyForegroundRefreshButtonColor]="
      getValue(emptyForegroundRefreshButtonColor) | async
    "
    [stickyToolbar]="getValue(listStickyToolbar) | async"
    [emptyHasRefresh]="getValue(emptyHasRefresh) | async"
    [actionBarActions]="getValue(actionBarActions) | async"
    [emptyRefreshIcon]="getValue(emptyRefreshIcon) | async"
    (selectionChanged)="handleFilesSelectionChanged($event)"
    [actionBarCssClass]="getValue(actionBarCssClass) | async"
    [searchBarCssClass]="getValue(searchBarCssClass) | async"
    [emptyMessageColor]="getValue(emptyMessageColor) | async"
    (actionBarActionFired)="actionBarActionFired.emit($event)"
    [enableReordering]="getValue(listEnableReordering) | async"
    [emptyForegroundColor]="getValue(emptyForegroundColor) | async"
    [actionBarHideActions]="getArrayValue(actionBarHideActions) | async"
    [emptyRefreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
    [searchBarSearchInputColor]="getValue(searchBarSearchInputColor) | async"
  >
  </x-list>
</ng-template>

<!-- File Item Template -->
<ng-template #fileItemRef let-item="item" let-index="index">
  <div fxLayout="row" class="x-file-item" [matTooltip]="item.data.name">
    <x-thumbnail
      fxFlex="nogrow"
      [loading]="loading"
      class="x-file-thumbnail"
      [uiDisabled]="uiDisabled"
      [type]="getThumbnailType(item.data) | async"
      [identifier]="getThumbnailIdentifier(item.data) | async"
    ></x-thumbnail>
    <div class="x-file-title text-truncate">
      {{ item.data.name }}
    </div>
  </div>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-upload\x-file-upload.component.scss

:host {
  --top-radius: 5px;
  --padding: 6px;
  --host-bg-color: var(--x-color-light);
}

:host ::ng-deep .x-card {
  box-shadow: unset !important;
}

:host ::ng-deep .x-thumbnail,
:host ::ng-deep .x-item {
  width: 32px !important;
  height: 32px !important;
  object-fit: contain !important;
  object-position: center !important;
}

:host ::ng-deep ion-searchbar {
  padding-left: unset;
  padding-right: unset;
  padding-inline-end: 0 !important;
  padding-inline-start: 0 !important;
}

.x-file-item {
  width: 100%;
  text-align: start;
}

.x-file-item,
.x-file-thumbnail {
  height: 32px;
}

.x-file-thumbnail {
  width: 32px;
}

.x-file-title,
.x-file-thumbnail {
  margin: {
    left: 4px;
    right: 4px;
  }
}

.x-file-title {
  text-align: start;

  margin: {
    top: auto;
    bottom: auto;
  }
}

.x-file-upload-file-drag-drop-wrapper {
  padding: var(--padding);
  background-color: var(--host-bg-color);
  border-top-left-radius: var(--top-radius);
  border-top-right-radius: var(--top-radius);
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-upload\x-file-upload.component.ts

import {
  ViewChild,
  Component,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XColor,
  nameof,
  isFile,
  isArray,
  toArray,
  isString,
  hasChild,
  XFileType,
  XFileBlob,
  getArrayOf,
  isXFileBlob,
  parseXColor,
  getFileInfo,
  isExtensionOk,
  XOneOrManyType,
  getErrorException,
  isNullOrUndefined,
  isNullOrEmptyString,
  XExceptionIdentifier,
  XBaseRangeRequestDto,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XFileStatus,
  XFileUploadAction,
  XFileUploadCssVars,
  XFileUploadActionModel,
  XFileUploadContainerComponent,
} from './x-file-upload.typings';
import { map } from 'rxjs/operators';
import {
  XActionBarItem,
  defaultActionBarAddItem,
  DefaultActionBarItemIds,
  defaultActionBarUploadItem,
  defaultActionBarRemoveItem,
  defaultActionBarSelectAllItem,
  defaultActionBarDeSelectAllItem,
  defaultActionBarInverseSelectionItem,
} from '../x-action-bar/x-action-bar.typings';
import { XIconNames } from '../x-icon/x-icon.typings';
import { BehaviorSubject, Subject, Subscription } from 'rxjs';
import { XListItem, XListAction } from '../x-list/x-list.typings';
import { XThumbnailType } from '../x-thumbnail/x-thumbnail.typings';
import { XFileDropAreaAction } from '../x-file-drop-area/x-file-drop-area.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-file-upload',
  templateUrl: './x-file-upload.component.html',
  styleUrls: ['./x-file-upload.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XFileUploadComponent extends XFileUploadContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  private addDisablerSubject = new BehaviorSubject<boolean>(false);
  private uploadDisablerSubject = new BehaviorSubject<boolean>(true);
  private removeDisablerSubject = new BehaviorSubject<boolean>(true);
  private selectAllDisablerSubject = new BehaviorSubject<boolean>(true);
  private deSelectAllDisablerSubject = new BehaviorSubject<boolean>(true);
  private inverseSelectionDisablerSubject = new BehaviorSubject<boolean>(true);

  //
  @ViewChild('fileInput', { static: false })
  private fileInputRef!: ElementRef;

  //
  private inputFiles: File[] = [];
  fileListItems: XListItem<File>[] = [];
  fileListItemsSubject: BehaviorSubject<XListItem<File>[]> =
    new BehaviorSubject([]);

  //
  get files() {
    return this.inputFiles;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.prepareActionBarActions();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const isColorChanged = changeKeys.includes(
      nameof<XFileUploadComponent>('color')
    );
    if (isColorChanged) {
      await this.applyColor();
    }

    //
    //#region ActionBar Actions ...
    const isActionBarActionsChanged = changeKeys.includes(
      nameof<XFileUploadComponent>('actionBarActions')
    );
    const isActionBarHideActionsChanged = changeKeys.includes(
      nameof<XFileUploadComponent>('actionBarHideActions')
    );
    if (isActionBarActionsChanged || isActionBarHideActionsChanged) {
      await this.prepareActionBarActions();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XFileUploadComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Ui Providers ...
  async canAddFile() {
    //
    const isMaxFilesOk = await this.isMaxAllowedFilesOk(
      this.inputFiles.length + 1
    );
    const isMaxSizeOk = await this.isMaxAllowedSizeOk();

    //
    const result = isMaxFilesOk && isMaxSizeOk;
    if (result) {
      this.addDisablerSubject.next(false);
    } else {
      this.addDisablerSubject.next(true);
    }

    //
    return result;
  }

  getThumbnailType(item: any) {
    return this.getValue(item).pipe(
      map((it) => {
        //
        let result = XThumbnailType.GENERAL;

        //
        let fileInfo;
        if (isString(it)) {
          fileInfo = getFileInfo(it);
        } else if (!isFile(it) && isXFileBlob(fileInfo)) {
          fileInfo = getFileInfo((it as XFileBlob).name);
        } else {
          fileInfo = getFileInfo((it as File).name);
        }

        //
        switch (fileInfo.type) {
          case XFileType.Image:
          case XFileType.CoverImage:
          case XFileType.ProfileImage:
            result = XThumbnailType.CUSTOM;
            break;
        }

        //
        return result;
      })
    );
  }

  getThumbnailIdentifier(item: any) {
    return this.getValue(item).pipe(
      map((it) => {
        //
        let result = '';
        if (!isFile(it)) {
          result = !isNullOrUndefined(it.name) ? it.name : it;
        } else {
          result = it;
        }

        //
        return result;
      })
    );
  }
  //#endregion

  //
  //#region Ui Handlers ...
  async handleFileDropped(files: File | File[]) {
    //
    if (!files) {
      return;
    }

    //
    this.loading = true;

    //
    if (!isArray(files) && files instanceof File) {
      await this.handleAddNewFile(files as File);
    } else {
      //
      const filesArr = files as File[];
      for (const file of filesArr) {
        await this.handleAddNewFile(file);
      }
    }

    //
    this.loading = false;
    this.fileChange.emit(this.inputFiles);
    this.handleChangeDetection();
  }

  async handleAddNewFile(file: File) {
    //
    if (this.isExists(file)) {
      return;
    }

    //
    const status = await this.getFileStatusForAdd(file);

    //
    await this.prepareActionBarActionsSubjects(this.getSelectedFiles());
    if (status !== XFileStatus.OK) {
      this.loading = false;
      await this.handleFileStatusForAdd(status);
      return;
    }

    //
    this.inputFiles.push(file);
    const fileListItem = await this.getListItem(file);
    this.fileListItems.push(fileListItem);
    this.fileListItemsSubject.next(this.fileListItems);
    this.handleChangeDetection();
  }

  async handleRemoveItems(request: XBaseRangeRequestDto<File>) {
    //
    if (!request || !hasChild(request.items)) {
      return;
    }

    //
    this.loading = true;

    //
    const filteredFiles = this.inputFiles.filter(
      (f) => !request.items.includes(f)
    );
    this.inputFiles = [];
    this.fileListItems = [];

    //
    if (!hasChild(filteredFiles)) {
      //
      this.inputFiles = [];
      this.fileListItems = [];
      this.fileListItemsSubject.next([]);
    } else {
      await this.handleFileDropped(filteredFiles);
    }

    //
    this.loading = false;
    this.fileChange.emit(this.inputFiles);
    this.handleChangeDetection();
  }

  async handleFilesSelectionChanged(items: XListItem<File>[]) {
    await this.prepareActionBarActionsSubjects(items);
  }

  async handleListOrderChanged() {
    //
    const listItems = await this.getArrayValueAsync(this.fileListItems);
    this.inputFiles = [...listItems.map((li) => li.data)];

    //
    this.loading = false;
    this.fileChange.emit(this.inputFiles);
    this.handleChangeDetection();
  }

  handleFilesSelected() {
    //
    if (!this.fileInputRef) {
      return;
    }

    //
    const fileInput = this.fileInputRef.nativeElement;
    if (!fileInput || !fileInput.files) {
      return;
    }

    //
    const files = getArrayOf<File>(fileInput.files);

    //
    if (!files || files.length === 0) {
      return;
    }

    //
    const file = files[0];
    if (!file) {
      return;
    }

    //
    fileInput.value = null;

    //
    this.handleFileDropped(files);
  }
  //#endregion

  //
  //#region Actions ...
  selectFiles() {
    //
    const isBussy = this.uiDisabled || this.loading;
    if (isBussy) {
      return;
    }

    //
    if (!this.fileInputRef) {
      return;
    }

    //
    const fileInput = this.fileInputRef.nativeElement;
    if (!fileInput) {
      return;
    }

    //
    fileInput.click();
  }

  isExists(file: File) {
    //
    const result =
      file && hasChild(this.inputFiles)
        ? this.inputFiles.findIndex(
            (f) =>
              f.name === file.name &&
              f.size === file.size &&
              f.type === file.type &&
              f.lastModified === file.lastModified
          ) > -1
        : false;

    //
    return result;
  }

  getSelectedFiles() {
    return this.fileListItems.filter((i) => i.isSelected === true);
  }

  /**
   * sync files with list ...
   * @param files
   */
  async handleSyncFiles(files: Array<File>) {
    //
    // first Remove List and Files ...
    await this.handleRemoveItems({
      items: this.files,
    });

    //
    // Drop New Files ...
    await this.handleFileDropped(files);
  }
  //#endregion

  //
  //#region Private ...
  /**
   * retrieve all exists files in list current size ...
   */
  private currentFilesSize() {
    //
    let result = 0;
    this.inputFiles.forEach((i) => (result += i.size));

    //
    return result;
  }

  /**
   * Check File Extension ...
   */
  private async isExtensionOk(file: File) {
    //
    if (!file) {
      return false;
    }

    //
    const allowedExtensionsType = await this.getValueAsync(
      this.allowedExtensions
    );
    if (!allowedExtensionsType) {
      return true;
    }

    //
    const allowedExtensions = toArray(allowedExtensionsType);
    if (!hasChild(allowedExtensions)) {
      return true;
    }

    //
    const result = isExtensionOk(file, allowedExtensions);
    return result;
  }

  /**
   * Check Number of Files is Ok after and before Upload ...
   */
  private async isMaxAllowedFilesOk(num?: number) {
    //
    if (!num && !hasChild(this.inputFiles)) {
      return true;
    }

    //
    if (!num) {
      num = this.inputFiles.length;
    }

    //
    const maxAllowedFiles = await this.getValueAsync(this.maxAllowedFiles);
    const result =
      maxAllowedFiles && maxAllowedFiles > 0 ? num <= maxAllowedFiles : true;

    //
    return result;
  }

  /**
   * Check all File Sizes after and before Upload ...
   */
  private async isMaxAllowedSizeOk(file?: File) {
    //
    let currentSize = this.currentFilesSize();

    //
    if (file) {
      currentSize += file.size;
    }

    //
    const maxAllowedSize = await this.getValueAsync(this.maxAllowedSize);

    //
    const result =
      maxAllowedSize && maxAllowedSize > 0
        ? currentSize <= maxAllowedSize
        : true;

    //
    return result;
  }

  /**
   * Check File Size satisfied Min Required Size ...
   */
  private async isMinAllowedFileSizeOk(file: File) {
    //
    const minAllowedFileSize = await this.getValueAsync(
      this.minAllowedFileSize
    );

    //
    const result =
      minAllowedFileSize && minAllowedFileSize > 0
        ? file.size >= minAllowedFileSize
        : true;

    //
    return result;
  }

  /**
   * Check File Size less than or Equals Max Required Size ...
   */
  private async isMaxAllowedFileSizeOk(file: File) {
    //
    const maxAllowedFileSize = await this.getValueAsync(
      this.maxAllowedFileSize
    );

    //
    const result =
      maxAllowedFileSize && maxAllowedFileSize > 0
        ? file.size <= maxAllowedFileSize
        : true;

    //
    return result;
  }

  /**
   * Check File Status for Add a File to List ...
   */
  private async getFileStatusForAdd(file: File) {
    //
    // Check Not Empty File ...
    if (!file || file.size === 0) {
      return XFileStatus.EMPTY;
    }

    //
    // Check File Extension Validation ...
    const isExtensionIsOk = await this.isExtensionOk(file);
    if (!isExtensionIsOk) {
      return XFileStatus.NOT_ALLOWED_TYPE;
    }

    //
    // Check File Min Size Ok ...
    const isMinAllowedFileSizeOk = await this.isMinAllowedFileSizeOk(file);
    if (!isMinAllowedFileSizeOk) {
      return XFileStatus.MIN_SIZE_NOT_REACHED;
    }

    //
    // Check File Max Size Ok ...
    const isMaxAllowedFileSizeOk = await this.isMaxAllowedFileSizeOk(file);
    if (!isMaxAllowedFileSizeOk) {
      return XFileStatus.SIZE_EXCEEDED;
    }

    //
    // Check Max Files Size Ok ...
    const isMaxAllowedSizeOk = await this.isMaxAllowedSizeOk(file);
    if (!isMaxAllowedSizeOk) {
      return XFileStatus.MAX_ALLOWED_SIZE_REACHED;
    }

    //
    // Check Max Files Ok ...
    const isMaxAllowedFilesOk = await this.isMaxAllowedFilesOk(
      this.inputFiles.length + 1
    );
    if (!isMaxAllowedFilesOk) {
      return XFileStatus.MAX_ALLOWED_FILES_REACHED;
    }

    //
    // Everythings Ok ...
    return XFileStatus.OK;
  }

  /**
   * recieve file status and act based of it (notifiy when exception) ...
   *
   * @param status File Status for Add ...
   */
  private async handleFileStatusForAdd(status: XFileStatus) {
    //
    let identifier: XExceptionIdentifier = null;
    switch (status) {
      //
      case XFileStatus.EMPTY:
        identifier = await this.getValueAsync(this.emptyFileException);
        break;

      //
      case XFileStatus.NOT_ALLOWED_TYPE:
        identifier = await this.getValueAsync(this.notAllowedFileException);
        break;

      //
      case XFileStatus.SIZE_EXCEEDED:
        identifier = await this.getValueAsync(this.maxAllowedFileSizeException);
        break;

      //
      case XFileStatus.MAX_ALLOWED_SIZE_REACHED:
        identifier = await this.getValueAsync(this.maxAllowedSizeException);
        break;

      //
      case XFileStatus.MAX_ALLOWED_FILES_REACHED:
        identifier = await this.getValueAsync(this.maxAllowedFilesException);
        break;

      //
      case XFileStatus.MIN_SIZE_NOT_REACHED:
        identifier = await this.getValueAsync(this.minAllowedFileSizeException);
        break;
    }

    //
    const exception = getErrorException(identifier);
    if (exception) {
      this.managerService.notificationService.presentErrorNotification({
        exception,
        dissmissable: true,
      });
    }
  }

  private handleChangeDetection() {
    //
    if (this.listActionProvider) {
      this.listActionProvider.next({
        action: XListAction.Refresh,
      });
    }

    //
    if (this.fileDropAreaActionProvider) {
      this.fileDropAreaActionProvider.next({
        action: XFileDropAreaAction.Refresh,
      });
    }

    //
    this.detectChanges();
  }

  //
  //#region ActionBar Preparations ...
  private async prepareActionBarActions() {
    //
    //#region Prepare Default Actions ...
    const hideActions = await this.getArrayValueAsync(
      this.actionBarHideActions
    );

    //
    const defaultActions: XOneOrManyType<XActionBarItem> = [];

    //
    // Add ...
    const addId = DefaultActionBarItemIds.Add.toString();
    if (!hideActions.includes(addId)) {
      defaultActions.push({
        //
        ...defaultActionBarAddItem,

        //
        index: 1,
        disabled: this.addDisablerSubject.asObservable(),

        //
        handler: () => {
          this.selectFiles();
        },
      });
    }

    //
    // Upload ...
    const uploadId = DefaultActionBarItemIds.Upload.toString();
    if (!hideActions.includes(uploadId)) {
      defaultActions.push({
        //
        ...defaultActionBarUploadItem,

        //
        index: 2,
        disabled: this.uploadDisablerSubject.asObservable(),
      });
    }

    //
    // Remove ...
    const removeId = DefaultActionBarItemIds.Remove.toString();
    if (!hideActions.includes(removeId)) {
      defaultActions.push({
        //
        ...defaultActionBarRemoveItem,

        //
        index: 3,
        disabled: this.removeDisablerSubject.asObservable(),
      });
    }

    //
    // Select All ...
    const selectAllId = DefaultActionBarItemIds.SelectAll.toString();
    if (!hideActions.includes(selectAllId)) {
      defaultActions.push({
        //
        ...defaultActionBarSelectAllItem,

        //
        index: 4,
        disabled: this.selectAllDisablerSubject.asObservable(),
      });
    }

    //
    // Inverse Selection ...
    const inverseSelectionId =
      DefaultActionBarItemIds.InverseSelection.toString();
    if (!hideActions.includes(inverseSelectionId)) {
      defaultActions.push({
        //
        ...defaultActionBarInverseSelectionItem,

        //
        index: 5,
        disabled: this.inverseSelectionDisablerSubject.asObservable(),
      });
    }

    //
    // De Select All ...
    const deselectAllId = DefaultActionBarItemIds.DeSelectAll.toString();
    if (!hideActions.includes(deselectAllId)) {
      defaultActions.push({
        //
        ...defaultActionBarDeSelectAllItem,

        //
        index: 6,
        disabled: this.deSelectAllDisablerSubject.asObservable(),
      });
    }
    //#endregion

    //
    if (!this.actionBarActions) {
      //
      this.actionBarActions = defaultActions;
      this.handleChangeDetection();
      return;
    }

    //
    const actions = await this.getArrayValueAsync(this.actionBarActions);
    if (!hasChild(actions)) {
      //
      this.actionBarActions = defaultActions;
      this.handleChangeDetection();
      return;
    }

    //
    this.actionBarActions = actions;
    this.handleChangeDetection();
  }

  private async prepareActionBarActionsSubjects(items: XListItem<File>[]) {
    //
    // Check Can Add ...
    const canAdd = await this.canAddFile();
    if (canAdd) {
      this.addDisablerSubject.next(false);
    } else {
      this.addDisablerSubject.next(true);
    }

    //
    // Check Select All ...
    if (!hasChild(this.fileListItems)) {
      //
      this.addDisablerSubject.next(false);
      this.uploadDisablerSubject.next(true);
      this.removeDisablerSubject.next(true);
      this.selectAllDisablerSubject.next(true);
      this.deSelectAllDisablerSubject.next(true);
      this.inverseSelectionDisablerSubject.next(true);
    } else {
      //
      this.uploadDisablerSubject.next(false);

      //
      // DeSelectAll ...
      if (!hasChild(items)) {
        //
        this.removeDisablerSubject.next(true);
        this.deSelectAllDisablerSubject.next(true);
        this.inverseSelectionDisablerSubject.next(true);
      } else {
        //
        this.removeDisablerSubject.next(false);
        this.deSelectAllDisablerSubject.next(false);
        this.inverseSelectionDisablerSubject.next(false);
      }

      //
      if (this.fileListItems.length === items.length) {
        //
        this.selectAllDisablerSubject.next(true);
        this.inverseSelectionDisablerSubject.next(true);
      } else {
        this.selectAllDisablerSubject.next(false);
      }
    }

    //
    this.handleChangeDetection();
  }
  //#endregion

  //
  //#region Handler Registerrers ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XFileUploadActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XFileUploadAction.SELECT_FILES:
            this.selectFiles();
            break;

          //
          case XFileUploadAction.CLEAR_LIST:
            await this.handleRemoveItems({
              items: this.files,
            });
            break;

          //
          case XFileUploadAction.SYNC_LIST:
            await this.handleSyncFiles(model.payload);
            break;

          //
          case XFileUploadAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region List Handlers ...
  private async getListItem(item: any) {
    //
    if (!item) {
      return null;
    }

    //
    const result: XListItem<any> = {
      data: item,
      disabled: this.uiDisabled,
      slideOptions: [
        {
          id: 'delete_file',
          icon: XIconNames.remove,
          title: this.ResourceIDs.delete,
          color: XColor.Danger,
          onlyIcon: true,
          disabled: this.uiDisabled,
          slot: 'end',
          handler: async () => {
            await this.confirmAndRemove(item);
          },
        },
      ],
    };

    //
    return result;
  }

  private isValidDataSource() {
    return hasChild(this.inputFiles);
  }

  private addToDataSource(item: XListItem<any>) {
    this.fileListItems.push(item);
  }

  private changeDataSourceItem(item: XListItem<any>) {
    //
    const index = this.fileListItems.findIndex((i) => i.data === item.data);
    if (index === -1) {
      return;
    }

    //
    this.fileListItems[index] = item;
  }

  private async clearListItems() {
    this.fileListItems = null;
    this.fileListItems = [];
  }

  private async refreshListItems(forceClean = false) {
    //
    if (forceClean) {
      await this.clearListItems();
    }

    //
    if (!this.isValidDataSource()) {
      return;
    }

    //
    for (const f of this.inputFiles) {
      //
      const fListItem = await this.getListItem(f);

      //
      if (forceClean) {
        this.addToDataSource(fListItem);
      } else {
        this.changeDataSourceItem(fListItem);
      }
    }
  }

  private async confirmAndRemove(items: XOneOrManyType<File>) {
    //
    if (!items) {
      return;
    }

    //
    const extractedItems: File[] = !items
      ? []
      : isArray(items)
      ? (items as File[])
      : [items as File];
    const removeRequest: XBaseRangeRequestDto<File> = {
      items: [...extractedItems],
    };

    //
    const confirmRemove = await this.getValueAsync(this.listConfirmRemove);
    if (confirmRemove) {
      await this.managerService.dialogService.presentAreYouSureYesNoDialog(
        () => {
          this.handleRemoveItems(removeRequest);
        }
      );
    } else {
      this.handleRemoveItems(removeRequest);
    }
  }
  //#endregion

  //
  //#region Color Appliers ...
  private async applyColor() {
    //
    const color = await this.getValueAsync(this.color);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFileUploadCssVars.HostBgColor, `var(${bgColor})`);

    //
    this.handleChangeDetection();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-upload\x-file-upload.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XListModule } from '../x-list/x-list.module';
import { XFileUploadComponent } from './x-file-upload.component';
import { XThumbnailModule } from '../x-thumbnail/x-thumbnail.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XFileDropAreaModule } from '../x-file-drop-area/x-file-drop-area.module';

@NgModule({
  declarations: [XFileUploadComponent],
  imports: [
    XCardModule,
    XListModule,
    CommonModule,
    XThumbnailModule,
    XFileDropAreaModule,
    XBaseComponentModule,
  ],
  exports: [
    XCardModule,
    XListModule,
    XThumbnailModule,
    XFileDropAreaModule,
    XBaseComponentModule,
    XFileUploadComponent,
  ],
})
export class XFileUploadModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-file-upload\x-file-upload.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  Injectable,
  ElementRef,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  hasChild,
  XFileBlob,
  XStandardType,
  XOneOrManyType,
  toNormalString,
  XColorIdentifier,
  fileToJsonString,
  XResourceIdentifier,
  XColorWithBrightness,
  XExceptionIdentifier,
} from 'x-framework-core';
import {
  XListItem,
  XListFilteringHandler,
  XComponentListContainer,
  IXComponentListContainer,
} from '../x-list/x-list.typings';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
} from '../x-card/x-card.typings';
import {
  XEmptyActionModel,
  XComponentEmptyContainer,
  IXComponentEmptyContainer,
} from '../x-empty/x-empty.typings';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import {
  XActionBarItem,
  XActionBarActionModel,
  XActionBarActionIdentifier,
  XActionBarActionFiredModel,
  XComponentActionBarContainer,
  IXComponentActionBarContainer,
} from '../x-action-bar/x-action-bar.typings';
import {
  XSearchBarActionModel,
  XSearchBarTypeIdentifier,
  XComponentSearchBarContainer,
  IXComponentSearchBarContainer,
} from '../x-search-bar/x-search-bar.typings';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import {
  XFileDropAreaActionModel,
  XComponentFileDropAreaContainer,
  IXComponentFileDropAreaContainer,
} from '../x-file-drop-area/x-file-drop-area.typings';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { IXFileContainer, XFileContainer } from '../classes/i-x-file.container';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XFileStatus {
  OK = 'ok',
  EMPTY = 'empty',
  SIZE_EXCEEDED = 'size_exceeded',
  NOT_ALLOWED_TYPE = 'not_allowed_type',
  MIN_SIZE_NOT_REACHED = 'min_size_not_reached',
  MAX_ALLOWED_FILES_REACHED = 'max_allowed_files_reached',
  MAX_ALLOWED_SIZE_REACHED = 'max_allowed_size_reached',
}

export type XFileStatusIdentifier = XFileStatus | string;

export enum XFileUploadAction {
  SELECT_FILES = 'select_files',
  CLEAR_LIST = 'clear_list',
  SYNC_LIST = 'sync_list',

  //
  Refresh = 'refresh',
}

export type XFileUploadActionIdentifier = XFileUploadAction | string;

export interface XFileUploadActionModel
  extends XBaseActionModel<XFileUploadActionIdentifier> {}

export const defaultFileUploadListFilteringHandler: XListFilteringHandler = (
  query: string,
  items: XListItem<File>[]
) => {
  //
  if (!query || !hasChild(items)) {
    return [];
  }

  //
  return items.filter((i) =>
    toNormalString(fileToJsonString(i.data)).includes(toNormalString(query))
  );
};

export enum XFileUploadCssVars {
  HostBgColor = 'host-bg-color',
}

//
//#region Container ...
//
type TXFileUploadContainer = IXComponentCardContainer &
  Partial<IXComponentFileDropAreaContainer> &
  Partial<IXComponentListContainer> &
  IXComponentActionBarContainer &
  IXComponentSearchBarContainer &
  IXComponentEmptyContainer &
  IXFileContainer;

export interface IXFileUploadContainer
  extends IXBaseContainer,
    TXFileUploadContainer {
  //
  actionProvider: Subject<XFileUploadActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  fileChange: EventEmitter<XOneOrManyType<File | XFileBlob | any>>;

  //
  actionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  >;
}

@Injectable({
  providedIn: 'root',
})
export class XFileUploadContainer
  extends XBaseContainer
  implements Readonly<IXFileUploadContainer>
{
  //
  //#region Props ...
  //
  //#region Self ...
  //
  readonly actionProvider: Subject<XFileUploadActionModel> =
    new Subject<XFileUploadActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly fileChange: EventEmitter<XOneOrManyType<File | XFileBlob | any>> = new EventEmitter<
    XOneOrManyType<File | XFileBlob>
  >();

  readonly actionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  >;
  //#endregion

  //
  //#region File Drop Props ...
  //
  readonly showFileDropArea: XStandardType<boolean> =
    this.fileDropAreaPropertyProvider.showFileDropArea;

  //
  readonly fileDropAreaActionProvider: Subject<XFileDropAreaActionModel> =
    this.fileDropAreaPropertyProvider.fileDropAreaActionProvider;
  readonly fileDropAreaCssClass: XStandardType<XOneOrManyType<string>> =
    this.fileDropAreaPropertyProvider.fileDropAreaCssClass;

  //
  readonly fileDropAreaColor: XStandardType<XColorIdentifier> =
    this.fileDropAreaPropertyProvider.fileDropAreaColor;
  readonly fileDropAreaForegroundColor: XStandardType<boolean> =
    this.fileDropAreaPropertyProvider.fileDropAreaForegroundColor;

  //
  readonly fileDropAreaIcon: XStandardType<XIconIdentifier> =
    this.fileDropAreaPropertyProvider.fileDropAreaIcon;
  readonly fileDropAreaMessage: XStandardType<XResourceIdentifier> =
    this.fileDropAreaPropertyProvider.fileDropAreaMessage;

  //
  readonly fileDropAreaDisableFileCheck: XStandardType<boolean> =
    this.fileDropAreaPropertyProvider.fileDropAreaDisableFileCheck;

  //
  readonly fileDropAreaSupportsMultipleFiles: XStandardType<boolean> =
    this.fileDropAreaPropertyProvider.fileDropAreaSupportsMultipleFiles;
  readonly fileDropAreaAllowedExtensions: XStandardType<
    XOneOrManyType<string>
  > = this.fileDropAreaPropertyProvider.fileDropAreaAllowedExtensions;

  //
  readonly fileDropAreaDragDisabled: XStandardType<boolean> =
    this.fileDropAreaPropertyProvider.fileDropAreaDragDisabled;
  readonly fileDropAreaContentTemplate: TemplateRef<any> =
    this.fileDropAreaPropertyProvider.fileDropAreaContentTemplate;
  readonly fileDropAreaFilesChangeEmiter: EventEmitter<XOneOrManyType<File>> =
    this.fileDropAreaPropertyProvider.fileDropAreaFilesChangeEmiter;

  //
  readonly fileDropAreaSuccessColor: XStandardType<XColorIdentifier> =
    this.fileDropAreaPropertyProvider.fileDropAreaSuccessColor;
  readonly fileDropAreaErrorColor: XStandardType<XColorIdentifier> =
    this.fileDropAreaPropertyProvider.fileDropAreaErrorColor;
  readonly fileDropAreaAccentColor: XStandardType<XColorIdentifier> =
    this.fileDropAreaPropertyProvider.fileDropAreaAccentColor;
  //#endregion

  //
  //#region Card Props ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  //#endregion

  //
  //#region List Props ...
  readonly showList: XStandardType<boolean> =
    this.listPropertyProvider.showList;
  readonly listConfirmRemove: XStandardType<boolean> =
    this.listPropertyProvider.listConfirmRemove;
  readonly listActionProvider = this.listPropertyProvider.listActionProvider;
  readonly listCssClass: XStandardType<XOneOrManyType<string>> =
    this.listPropertyProvider.listCssClass;
  readonly listShowToolbar: XStandardType<boolean> = true;
  readonly listStickyToolbar: XStandardType<boolean> =
    this.listPropertyProvider.listStickyToolbar;
  readonly listEnableReordering: XStandardType<boolean> =
    this.listPropertyProvider.listEnableReordering;
  readonly listShowDivider: XStandardType<boolean> =
    this.listPropertyProvider.listShowDivider;
  readonly listFilteringHandler: XListFilteringHandler =
    this.listPropertyProvider.listFilteringHandler;
  //#endregion

  //
  //#region ActionBar ...
  //
  readonly showActionBar: XStandardType<boolean> = true;

  //
  readonly actionBarActionProvider: Subject<XActionBarActionModel> =
    this.actionBarPropertyProvider.actionBarActionProvider;
  readonly actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.actionBarPropertyProvider.actionBarCssClass;

  //
  readonly actionBarColor: XStandardType<XColorIdentifier> =
    this.actionBarPropertyProvider.actionBarColor;
  readonly actionBarForegroundColor: XStandardType<boolean> =
    this.actionBarPropertyProvider.actionBarForegroundColor;

  //
  readonly actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.actionBarPropertyProvider.actionBarActions;

  //
  readonly actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.actionBarPropertyProvider.actionBarActionFired;
  //#endregion

  //
  //#region SearchBar ...
  //
  readonly showSearchBar: XStandardType<boolean> =
    this.searchBarPropertyProvider.showSearchBar;

  //
  readonly searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.searchBarPropertyProvider.searchBarActionProvider;
  readonly searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.searchBarPropertyProvider.searchBarCssClass;

  //
  readonly searchBarColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarColor;
  readonly searchBarForegroundColor: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarForegroundColor;

  //
  readonly searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchInputColor;
  readonly searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarForegroundSearchInputColor;

  //
  readonly searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.searchBarPropertyProvider.searchBarType;
  readonly searchBarTitle: XStandardType<XResourceIdentifier> =
    this.searchBarPropertyProvider.searchBarTitle;
  readonly searchBarValue: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarValue;
  readonly searchBarEnterKeyHint: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarEnterKeyHint;
  readonly searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.searchBarPropertyProvider.searchBarKeyboardType;
  readonly searchBarSpellcheck: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarSpellcheck;
  readonly searchBarDebounce: XStandardType<number> =
    this.searchBarPropertyProvider.searchBarDebounce;

  //
  readonly searchBarShowCancelButton: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarShowCancelButton;
  readonly searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonIcon;
  readonly searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonTitle;
  readonly searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonIconColor;

  //
  readonly searchBarShowClearButton: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarShowClearButton;
  readonly searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarClearButtonIcon;
  readonly searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarClearButtonIconColor;

  //
  readonly searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchButtonIcon;
  readonly searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchButtonIconColor;

  //
  readonly searchBarQueryChange: EventEmitter<string> =
    this.searchBarPropertyProvider.searchBarQueryChange;
  readonly searchBarOnEvent: EventEmitter<string> =
    this.searchBarPropertyProvider.searchBarOnEvent;
  //#endregion

  //
  //#region Empty Props ...
  readonly showEmpty: XStandardType<boolean> =
    this.emptyPropertyProvider.showEmpty;
  readonly emptyActionProvider: Subject<XEmptyActionModel> =
    this.emptyPropertyProvider.emptyActionProvider;
  readonly emptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.emptyPropertyProvider.emptyCssClass;
  readonly emptyColor: XStandardType<XColorIdentifier> =
    this.emptyPropertyProvider.emptyColor;
  readonly emptyForegroundColor: XStandardType<boolean> =
    this.emptyPropertyProvider.emptyForegroundColor;
  readonly emptyMessage: XStandardType<XResourceIdentifier> =
    this.emptyPropertyProvider.emptyMessage;
  readonly emptyMessageColor: XStandardType<XColorIdentifier> =
    this.emptyPropertyProvider.emptyMessageColor;
  readonly emptyForegroundMessageColor: XStandardType<boolean> =
    this.emptyPropertyProvider.emptyForegroundMessageColor;
  readonly emptyHasRefresh: XStandardType<boolean> =
    this.emptyPropertyProvider.emptyHasRefresh;
  readonly emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.emptyPropertyProvider.emptyRefreshButtonColor;
  readonly emptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.emptyPropertyProvider.emptyForegroundRefreshButtonColor;
  readonly emptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.emptyPropertyProvider.emptyRefreshIcon;
  readonly emptyRefresh: EventEmitter<void> =
    this.emptyPropertyProvider.emptyRefresh;
  //#endregion

  //
  //#region File Props ...
  readonly emptyFileException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.emptyFileException;
  readonly notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.notAllowedFileException;
  readonly allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.filePropertyProvider.allowedExtensions;
  readonly maxAllowedFiles: XStandardType<number> =
    this.filePropertyProvider.maxAllowedFiles;
  readonly maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedFilesException;
  readonly maxAllowedSize: XStandardType<number> =
    this.filePropertyProvider.maxAllowedSize;
  readonly maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedSizeException;
  readonly minAllowedFileSize: XStandardType<number> =
    this.filePropertyProvider.minAllowedFileSize;
  readonly minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.minAllowedFileSizeException;
  readonly maxAllowedFileSize: XStandardType<number> =
    this.filePropertyProvider.maxAllowedFileSize;
  readonly maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.filePropertyProvider.maxAllowedFileSizeException;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected filePropertyProvider: XFileContainer,
    protected cardPropertyProvider: XComponentCardContainer,
    protected listPropertyProvider: XComponentListContainer,
    protected emptyPropertyProvider: XComponentEmptyContainer,
    protected actionBarPropertyProvider: XComponentActionBarContainer,
    protected searchBarPropertyProvider: XComponentSearchBarContainer,
    protected fileDropAreaPropertyProvider: XComponentFileDropAreaContainer
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XFileUploadContainerComponent
  extends XBaseContainerComponent
  implements IXFileUploadContainer
{
  //
  //#region Props ...
  //
  //#region Self ...
  //
  @Input()
  actionProvider: Subject<XFileUploadActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Output()
  fileChange: EventEmitter<XOneOrManyType<File | XFileBlob | any>> = new EventEmitter();

  @Input()
  actionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  > = this.propertyProvider.actionBarHideActions;
  //#endregion

  //
  //#region File Drop Props ...
  //
  @Input()
  showFileDropArea: XStandardType<boolean> =
    this.propertyProvider.showFileDropArea;

  //
  @Input()
  fileDropAreaActionProvider: Subject<XFileDropAreaActionModel> =
    this.propertyProvider.fileDropAreaActionProvider;

  @Input()
  fileDropAreaCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaCssClass;

  //
  @Input()
  fileDropAreaColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaColor;

  @Input()
  fileDropAreaForegroundColor: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaForegroundColor;

  //
  @Input()
  fileDropAreaIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.fileDropAreaIcon;

  @Input()
  fileDropAreaMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.fileDropAreaMessage;

  //
  @Input()
  fileDropAreaDisableFileCheck: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaDisableFileCheck;

  //
  @Input()
  fileDropAreaSupportsMultipleFiles: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaSupportsMultipleFiles;

  @Input()
  fileDropAreaAllowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.fileDropAreaAllowedExtensions;

  //
  @Input()
  fileDropAreaDragDisabled: XStandardType<boolean> =
    this.propertyProvider.fileDropAreaDragDisabled;

  @Input()
  fileDropAreaContentTemplate: TemplateRef<any> =
    this.propertyProvider.fileDropAreaContentTemplate;

  @Output()
  fileDropAreaFilesChangeEmiter: EventEmitter<XOneOrManyType<File>> =
    new EventEmitter();

  //
  @Input()
  fileDropAreaSuccessColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaSuccessColor;

  @Input()
  fileDropAreaErrorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaErrorColor;

  @Input()
  fileDropAreaAccentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.fileDropAreaAccentColor;
  //#endregion

  //
  //#region Card Props ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;
  //#endregion

  //
  //#region List Props ...
  @Input()
  showList: XStandardType<boolean> = this.propertyProvider.showList;

  @Input()
  listConfirmRemove: XStandardType<boolean> =
    this.propertyProvider.listConfirmRemove;

  @Input()
  listActionProvider = this.propertyProvider.listActionProvider;

  @Input()
  listCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.listCssClass;

  @Input()
  listShowToolbar: XStandardType<boolean> =
    this.propertyProvider.listShowToolbar;

  @Input()
  listStickyToolbar: XStandardType<boolean> =
    this.propertyProvider.listStickyToolbar;

  @Input()
  listEnableReordering: XStandardType<boolean> =
    this.propertyProvider.listEnableReordering;

  @Input()
  listShowDivider: XStandardType<boolean> =
    this.propertyProvider.listShowDivider;

  @Input()
  listFilteringHandler: XListFilteringHandler =
    this.propertyProvider.listFilteringHandler;
  //#endregion

  //
  //#region ActionBar ...
  //
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  //
  @Input()
  actionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionBarActionProvider;

  @Input()
  actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  //
  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;

  //
  @Input()
  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actionBarActions;

  //
  @Output()
  actionBarActionFired: EventEmitter<XActionBarActionFiredModel> = new EventEmitter<XActionBarActionFiredModel>();
  //#endregion

  //
  //#region SearchBar ...
  //
  @Input()
  showSearchBar: XStandardType<boolean> = this.propertyProvider.showSearchBar;

  //
  @Input()
  searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.searchBarActionProvider;

  @Input()
  searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.searchBarCssClass;

  //
  @Input()
  searchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarColor;

  @Input()
  searchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundColor;

  //
  @Input()
  searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchInputColor;

  @Input()
  searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundSearchInputColor;

  //
  @Input()
  searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarType;

  @Input()
  searchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarTitle;

  @Input()
  searchBarValue: XStandardType<string> = this.propertyProvider.searchBarValue;

  @Input()
  searchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.searchBarEnterKeyHint;

  @Input()
  searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarKeyboardType;

  @Input()
  searchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.searchBarSpellcheck;

  @Input()
  searchBarDebounce: XStandardType<number> =
    this.propertyProvider.searchBarDebounce;

  //
  @Input()
  searchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.searchBarShowCancelButton;

  @Input()
  searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarCancelButtonIcon;

  @Input()
  searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarCancelButtonTitle;

  @Input()
  searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarCancelButtonIconColor;

  //
  @Input()
  searchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.searchBarShowClearButton;

  @Input()
  searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarClearButtonIcon;

  @Input()
  searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarClearButtonIconColor;

  //
  @Input()
  searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarSearchButtonIcon;

  @Input()
  searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchButtonIconColor;

  //
  @Output()
  searchBarQueryChange: EventEmitter<string> = new EventEmitter();

  @Output()
  searchBarOnEvent: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Empty Props ...
  @Input()
  showEmpty: XStandardType<boolean> = this.propertyProvider.showEmpty;

  @Input()
  emptyActionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.emptyActionProvider;

  @Input()
  emptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.emptyCssClass;

  @Input()
  emptyColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyColor;

  @Input()
  emptyForegroundColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundColor;

  @Input()
  emptyMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.emptyMessage;

  @Input()
  emptyMessageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyMessageColor;

  @Input()
  emptyForegroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundMessageColor;

  @Input()
  emptyHasRefresh: XStandardType<boolean> =
    this.propertyProvider.emptyHasRefresh;

  @Input()
  emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyRefreshButtonColor;

  @Input()
  emptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundRefreshButtonColor;

  @Input()
  emptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.emptyRefreshIcon;

  @Output()
  emptyRefresh: EventEmitter<void> = new EventEmitter();
  //#endregion

  //
  //#region File Props ...
  @Input()
  emptyFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.emptyFileException;

  @Input()
  notAllowedFileException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.notAllowedFileException;

  @Input()
  allowedExtensions: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.allowedExtensions;

  @Input()
  maxAllowedFiles: XStandardType<number> =
    this.propertyProvider.maxAllowedFiles;

  @Input()
  maxAllowedFilesException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFilesException;

  @Input()
  maxAllowedSize: XStandardType<number> = this.propertyProvider.maxAllowedSize;

  @Input()
  maxAllowedSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedSizeException;

  @Input()
  minAllowedFileSize: XStandardType<number> =
    this.propertyProvider.minAllowedFileSize;

  @Input()
  minAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.minAllowedFileSizeException;

  @Input()
  maxAllowedFileSize: XStandardType<number> =
    this.propertyProvider.maxAllowedFileSize;

  @Input()
  maxAllowedFileSizeException: XStandardType<XExceptionIdentifier> =
    this.propertyProvider.maxAllowedFileSizeException;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFileUploadContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXFileUploadContainer
  ): Observable<IXFileUploadContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),
      actionBarHideActions: this.getValue(
        container ? container.actionBarHideActions : this.actionBarHideActions
      ),
      showFileDropArea: this.getValue(
        container ? container.showFileDropArea : this.showFileDropArea
      ),
      fileDropAreaCssClass: this.getValue(
        container ? container.fileDropAreaCssClass : this.fileDropAreaCssClass
      ),
      fileDropAreaColor: this.getValue(
        container ? container.fileDropAreaColor : this.fileDropAreaColor
      ),
      fileDropAreaForegroundColor: this.getValue(
        container
          ? container.fileDropAreaForegroundColor
          : this.fileDropAreaForegroundColor
      ),
      fileDropAreaIcon: this.getValue(
        container ? container.fileDropAreaIcon : this.fileDropAreaIcon
      ),
      fileDropAreaMessage: this.getValue(
        container ? container.fileDropAreaMessage : this.fileDropAreaMessage
      ),
      fileDropAreaDisableFileCheck: this.getValue(
        container
          ? container.fileDropAreaDisableFileCheck
          : this.fileDropAreaDisableFileCheck
      ),
      fileDropAreaSupportsMultipleFiles: this.getValue(
        container
          ? container.fileDropAreaSupportsMultipleFiles
          : this.fileDropAreaSupportsMultipleFiles
      ),
      fileDropAreaAllowedExtensions: this.getValue(
        container
          ? container.fileDropAreaAllowedExtensions
          : this.fileDropAreaAllowedExtensions
      ),
      fileDropAreaDragDisabled: this.getValue(
        container
          ? container.fileDropAreaDragDisabled
          : this.fileDropAreaDragDisabled
      ),
      fileDropAreaContentTemplate: this.getValue(
        container
          ? container.fileDropAreaContentTemplate
          : this.fileDropAreaContentTemplate
      ),
      fileDropAreaSuccessColor: this.getValue(
        container
          ? container.fileDropAreaSuccessColor
          : this.fileDropAreaSuccessColor
      ),
      fileDropAreaErrorColor: this.getValue(
        container
          ? container.fileDropAreaErrorColor
          : this.fileDropAreaErrorColor
      ),
      fileDropAreaAccentColor: this.getValue(
        container
          ? container.fileDropAreaAccentColor
          : this.fileDropAreaAccentColor
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardCssClass: this.getValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      showList: this.getValue(container ? container.showList : this.showList),
      listConfirmRemove: this.getValue(
        container ? container.listConfirmRemove : this.listConfirmRemove
      ),
      listCssClass: this.getValue(
        container ? container.listCssClass : this.listCssClass
      ),
      listShowToolbar: this.getValue(
        container ? container.listShowToolbar : this.listShowToolbar
      ),
      listStickyToolbar: this.getValue(
        container ? container.listStickyToolbar : this.listStickyToolbar
      ),
      listEnableReordering: this.getValue(
        container ? container.listEnableReordering : this.listEnableReordering
      ),
      listShowDivider: this.getValue(
        container ? container.listShowDivider : this.listShowDivider
      ),
      listFilteringHandler: this.getValue(
        container ? container.listFilteringHandler : this.listFilteringHandler
      ),
      showActionBar: this.getValue(
        container ? container.showActionBar : this.showActionBar
      ),
      actionBarCssClass: this.getValue(
        container ? container.actionBarCssClass : this.actionBarCssClass
      ),
      actionBarColor: this.getValue(
        container ? container.actionBarColor : this.actionBarColor
      ),
      actionBarForegroundColor: this.getValue(
        container
          ? container.actionBarForegroundColor
          : this.actionBarForegroundColor
      ),
      actionBarActions: this.getValue(
        container ? container.actionBarActions : this.actionBarActions
      ),
      showSearchBar: this.getValue(
        container ? container.showSearchBar : this.showSearchBar
      ),
      searchBarCssClass: this.getValue(
        container ? container.searchBarCssClass : this.searchBarCssClass
      ),
      searchBarColor: this.getValue(
        container ? container.searchBarColor : this.searchBarColor
      ),
      searchBarForegroundColor: this.getValue(
        container
          ? container.searchBarForegroundColor
          : this.searchBarForegroundColor
      ),
      searchBarSearchInputColor: this.getValue(
        container
          ? container.searchBarSearchInputColor
          : this.searchBarSearchInputColor
      ),
      searchBarForegroundSearchInputColor: this.getValue(
        container
          ? container.searchBarForegroundSearchInputColor
          : this.searchBarForegroundSearchInputColor
      ),
      searchBarType: this.getValue(
        container ? container.searchBarType : this.searchBarType
      ),
      searchBarTitle: this.getValue(
        container ? container.searchBarTitle : this.searchBarTitle
      ),
      searchBarValue: this.getValue(
        container ? container.searchBarValue : this.searchBarValue
      ),
      searchBarEnterKeyHint: this.getValue(
        container ? container.searchBarEnterKeyHint : this.searchBarEnterKeyHint
      ),
      searchBarKeyboardType: this.getValue(
        container ? container.searchBarKeyboardType : this.searchBarKeyboardType
      ),
      searchBarSpellcheck: this.getValue(
        container ? container.searchBarSpellcheck : this.searchBarSpellcheck
      ),
      searchBarDebounce: this.getValue(
        container ? container.searchBarDebounce : this.searchBarDebounce
      ),
      searchBarShowCancelButton: this.getValue(
        container
          ? container.searchBarShowCancelButton
          : this.searchBarShowCancelButton
      ),
      searchBarCancelButtonIcon: this.getValue(
        container
          ? container.searchBarCancelButtonIcon
          : this.searchBarCancelButtonIcon
      ),
      searchBarCancelButtonTitle: this.getValue(
        container
          ? container.searchBarCancelButtonTitle
          : this.searchBarCancelButtonTitle
      ),
      searchBarCancelButtonIconColor: this.getValue(
        container
          ? container.searchBarCancelButtonIconColor
          : this.searchBarCancelButtonIconColor
      ),
      searchBarShowClearButton: this.getValue(
        container
          ? container.searchBarShowClearButton
          : this.searchBarShowClearButton
      ),
      searchBarClearButtonIcon: this.getValue(
        container
          ? container.searchBarClearButtonIcon
          : this.searchBarClearButtonIcon
      ),
      searchBarClearButtonIconColor: this.getValue(
        container
          ? container.searchBarClearButtonIconColor
          : this.searchBarClearButtonIconColor
      ),
      searchBarSearchButtonIcon: this.getValue(
        container
          ? container.searchBarSearchButtonIcon
          : this.searchBarSearchButtonIcon
      ),
      searchBarSearchButtonIconColor: this.getValue(
        container
          ? container.searchBarSearchButtonIconColor
          : this.searchBarSearchButtonIconColor
      ),
      showEmpty: this.getValue(
        container ? container.showEmpty : this.showEmpty
      ),
      emptyCssClass: this.getValue(
        container ? container.emptyCssClass : this.emptyCssClass
      ),
      emptyColor: this.getValue(
        container ? container.emptyColor : this.emptyColor
      ),
      emptyForegroundColor: this.getValue(
        container ? container.emptyForegroundColor : this.emptyForegroundColor
      ),
      emptyMessage: this.getValue(
        container ? container.emptyMessage : this.emptyMessage
      ),
      emptyMessageColor: this.getValue(
        container ? container.emptyMessageColor : this.emptyMessageColor
      ),
      emptyForegroundMessageColor: this.getValue(
        container
          ? container.emptyForegroundMessageColor
          : this.emptyForegroundMessageColor
      ),
      emptyHasRefresh: this.getValue(
        container ? container.emptyHasRefresh : this.emptyHasRefresh
      ),
      emptyRefreshButtonColor: this.getValue(
        container
          ? container.emptyRefreshButtonColor
          : this.emptyRefreshButtonColor
      ),
      emptyForegroundRefreshButtonColor: this.getValue(
        container
          ? container.emptyForegroundRefreshButtonColor
          : this.emptyForegroundRefreshButtonColor
      ),
      emptyRefreshIcon: this.getValue(
        container ? container.emptyRefreshIcon : this.emptyRefreshIcon
      ),
      emptyFileException: this.getValue(
        container ? container.emptyFileException : this.emptyFileException
      ),
      notAllowedFileException: this.getValue(
        container
          ? container.notAllowedFileException
          : this.notAllowedFileException
      ),
      allowedExtensions: this.getValue(
        container ? container.allowedExtensions : this.allowedExtensions
      ),
      maxAllowedFiles: this.getValue(
        container ? container.maxAllowedFiles : this.maxAllowedFiles
      ),
      maxAllowedFilesException: this.getValue(
        container
          ? container.maxAllowedFilesException
          : this.maxAllowedFilesException
      ),
      maxAllowedSize: this.getValue(
        container ? container.maxAllowedSize : this.maxAllowedSize
      ),
      maxAllowedSizeException: this.getValue(
        container
          ? container.maxAllowedSizeException
          : this.maxAllowedSizeException
      ),
      minAllowedFileSize: this.getValue(
        container ? container.minAllowedFileSize : this.minAllowedFileSize
      ),
      minAllowedFileSizeException: this.getValue(
        container
          ? container.minAllowedFileSizeException
          : this.minAllowedFileSizeException
      ),
      maxAllowedFileSize: this.getValue(
        container ? container.maxAllowedFileSize : this.maxAllowedFileSize
      ),
      maxAllowedFileSizeException: this.getValue(
        container
          ? container.maxAllowedFileSizeException
          : this.maxAllowedFileSizeException
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          fileChange: this.fileChange,

          //
          fileDropAreaActionProvider: container
            ? container.fileDropAreaActionProvider
            : this.fileDropAreaActionProvider,

          //
          fileDropAreaFilesChangeEmiter: this.fileDropAreaFilesChangeEmiter,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          listActionProvider: container
            ? container.listActionProvider
            : this.listActionProvider,

          //
          actionBarActionProvider: container
            ? container.actionBarActionProvider
            : this.actionBarActionProvider,

          //
          actionBarActionFired: this.actionBarActionFired,

          //
          searchBarActionProvider: container
            ? container.searchBarActionProvider
            : this.searchBarActionProvider,

          //
          searchBarQueryChange: this.searchBarQueryChange,
          searchBarOnEvent: this.searchBarOnEvent,

          //
          emptyActionProvider: container
            ? container.emptyActionProvider
            : this.emptyActionProvider,

          //
          emptyRefresh: this.emptyRefresh,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\x-form.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else formRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="formRef"></ng-container>

    <!-- Actions -->
    <div [ngClass]="getFormCardActionsCssClass() | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div
      footer
      *ngIf="getValue(cardShowFooter) | async"
      [ngClass]="getFormCardFooterCssClass() | async"
    >
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Form itSelf Template -->
<ng-template #formRef>
  <!-- Form Wrapper -->
  <div
    *ngIf="form"
    class="x-form x-wrapper"
    [class.mobile]="isMobileUi$ | async"
  >
    <!-- Form Element -->
    <form
      *ngIf="form"
      [formGroup]="form"
      [ngClass]="getFormCssClass() | async"
      style="width: 100%"
    >
      <ng-container *ngIf="hasChildValue(getFormControls()) | async">
        <!-- Presenting Props -->
        <ng-container
          *ngFor="
            let control of getFormControls() | async;
            index as controlIndex
          "
        >
          <!-- Check Control Exists -->
          <ng-container *ngIf="control">
            <ng-container
              *ngIf="getControlType(control) | async as controlType"
            >
              <!-- 1- Input Type (Text/Number/Password/Email/Url/Color/Tel) Type -->
              <ng-container
                *ngIf="
                  controlType === ControlTypes.Text ||
                  controlType === ControlTypes.Number ||
                  controlType === ControlTypes.Password ||
                  controlType === ControlTypes.Color ||
                  controlType === ControlTypes.Email ||
                  controlType === ControlTypes.Url ||
                  controlType === ControlTypes.Tel
                "
              >
                <!-- Auto Complete Wrapper -->
                <div
                  class="x-auto-complete-wrapper"
                  (xOnOutsideClicked)="
                    handleAutoCompleteOutsideClicked(control)
                  "
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- Form Control Wrapper -->
                  <div>
                    <!-- Not None Appearance -->
                    <ng-container
                      *ngIf="
                        (getControlAppearanceObject(control) | async) !==
                        FormControlAppearances.None
                      "
                    >
                      <!-- Mat Form Field -->
                      <mat-form-field
                        class="x-mat-form-field"
                        [class.apply-status]="
                          getValue(applyStatusOnControls) | async
                        "
                        [ngClass]="getFormFieldClasses(control) | async"
                        [appearance]="getControlAppearance(control) | async"
                      >
                        <!-- label -->
                        <mat-label
                          class="x-field-label"
                          *ngIf="
                            notEmptyValue(getControlLabel(control)) | async
                          "
                          >{{ getControlLabel(control) | async }}</mat-label
                        >

                        <!-- Has Formatter Input Control -->
                        <ng-container
                          *ngIf="controlHasFormatter(control) | async"
                        >
                          <input
                            matInput
                            autocomplete="off"
                            xFromInputFormatter
                            [inputFormatterType]="
                              getFormatterType(control) | async
                            "
                            [formControlName]="control.propName.toString()"
                            [enabledInputFormatter]="
                              controlHasFormatter(control) | async
                            "
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [stringInputFormatterType]="
                              getFormatterInputType(control) | async
                            "
                            [currencyLable]="
                              getFormatterCurrencyLabel(control) | async
                            "
                            (blur)="handleControlBlured(control.propName.toString())"
                            [locale]="getFormatterLocale(control) | async"
                            [stringFormatter]="
                              getFormatterStringFormatter(control) | async
                            "
                            (xOnEscape)="handleAutoCompleteEscaped(control)"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [ngClass]="getFormControlClasses(control) | async"
                            [readonly]="isInputControlReadOnly(control) | async"
                            [seperator]="getFormatterSeperator(control) | async"
                          />
                        </ng-container>

                        <!-- Has not Formatter Input Control -->
                        <ng-container
                          *ngIf="!(controlHasFormatter(control) | async)"
                        >
                          <input
                            matInput
                            autocomplete="off"
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            (blur)="handleControlBlured(control.propName.toString())"
                            (xOnEscape)="handleAutoCompleteEscaped(control)"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [readonly]="isInputControlReadOnly(control) | async"
                          />
                        </ng-container>

                        <!-- Progressbar for Loading -->
                        <ng-container
                          *ngTemplateOutlet="
                            loadingRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Prefix Icon -->
                        <span
                          matPrefix
                          *ngIf="controlHasPrefixIcon(control) | async"
                        >
                          <ng-container
                            *ngTemplateOutlet="
                              prefixIconRef;
                              context: { $implicit: control }
                            "
                          ></ng-container>
                        </span>

                        <!-- Suffix Icon -->
                        <span
                          matSuffix
                          *ngIf="controlHasSuffixIcon(control) | async"
                        >
                          <ng-container
                            *ngTemplateOutlet="
                              suffixIconRef;
                              context: { $implicit: control }
                            "
                          ></ng-container>
                        </span>
                      </mat-form-field>
                    </ng-container>

                    <!-- None Appearance -->
                    <ng-container
                      *ngIf="
                        (getControlAppearanceObject(control) | async) ===
                        FormControlAppearances.None
                      "
                    >
                      <!-- XForm Field -->
                      <div
                        xFocusHandler
                        [tabIndex]="controlIndex"
                        [enableHandler]="
                          !uiDisabled &&
                          !loading &&
                          !(getControlDisabled(control) | async)
                        "
                        [class.apply-status]="
                          getValue(applyStatusOnControls) | async
                        "
                        [ngClass]="getFormFieldClasses(control) | async"
                      >
                        <!-- Label -->
                        <ng-container
                          *ngTemplateOutlet="
                            byIconLabelRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Has Formatter Input Control -->
                        <ng-container
                          *ngIf="controlHasFormatter(control) | async"
                        >
                          <input
                            autocomplete="off"
                            xFromInputFormatter
                            [inputFormatterType]="
                              getFormatterType(control) | async
                            "
                            [formControlName]="control.propName.toString()"
                            [enabledInputFormatter]="
                              controlHasFormatter(control) | async
                            "
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            [stringInputFormatterType]="
                              getFormatterInputType(control) | async
                            "
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisabled(control) | async) ||
                              (isInputControlReadOnly(control) | async)
                            "
                            [currencyLable]="
                              getFormatterCurrencyLabel(control) | async
                            "
                            (blur)="handleControlBlured(control.propName.toString())"
                            [locale]="getFormatterLocale(control) | async"
                            [stringFormatter]="
                              getFormatterStringFormatter(control) | async
                            "
                            (focus)="handleControlFocused(control.propName.toString())"
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [seperator]="getFormatterSeperator(control) | async"
                          />
                        </ng-container>

                        <!-- Has not Formatter Input Control -->
                        <ng-container
                          *ngIf="!(controlHasFormatter(control) | async)"
                        >
                          <input
                            autocomplete="off"
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisabled(control) | async) ||
                              (isInputControlReadOnly(control) | async)
                            "
                            (blur)="handleControlBlured(control.propName.toString())"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                          />
                        </ng-container>

                        <!-- Progressbar for Loading -->
                        <ng-container
                          *ngTemplateOutlet="
                            loadingRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </div>
                    </ng-container>

                    <!-- Hint -->
                    <ng-container
                      *ngTemplateOutlet="
                        hintRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Errors -->
                    <ng-container
                      *ngTemplateOutlet="
                        errorsRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Auto Complete -->
                    <div
                      [id]="getAutoCompleteId(control)"
                      [ngClass]="getAutoCompleteCssClass(control) | async"
                      [class.open]="getAutoCompleteOpened(control) | async"
                      *ngIf="
                        (controlHasAutoComplete(control) | async) &&
                        (hasChildValue(getAutoCompleteItems(control)) | async)
                      "
                    >
                      <ng-container
                        *ngIf="
                          getControlAutoCompleteConfig(control)
                            | async as autoComplete
                        "
                      >
                        <ng-container
                          *ngIf="getAutoCompleteItems(control) as items"
                        >
                          <x-list
                            [items]="items"
                            [showEmpty]="false"
                            [showToolbar]="false"
                            [wrapWithCard]="false"
                            [clickableItems]="true"
                            [uiDisabled]="uiDisabled"
                            [color]="getAutoCompleteColor(control) | async"
                            (itemSelected)="
                              handleAutoCompleteResultSelected(
                                control,
                                autoComplete,
                                $event
                              )
                            "
                            [itemTemplate]="
                              getAutoCompleteItemTemplate(control) | async
                            "
                          ></x-list>
                        </ng-container>
                      </ng-container>
                    </div>
                  </div>
                </div>
              </ng-container>

              <!-- 2- Select -->
              <ng-container
                *ngIf="
                  (hasSelectConfig(control) | async) &&
                  controlType === ControlTypes.Select
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [matTooltip]="getControlTooltip(control) | async"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- Not None Appearance -->
                  <ng-container
                    *ngIf="
                      (getControlAppearanceObject(control) | async) !==
                      FormControlAppearances.None
                    "
                  >
                    <!-- Mat Form Field -->
                    <mat-form-field
                      class="x-mat-form-field"
                      [class.apply-status]="
                        getValue(applyStatusOnControls) | async
                      "
                      [ngClass]="getFormFieldClasses(control) | async"
                      [appearance]="getControlAppearance(control) | async"
                    >
                      <!-- label -->
                      <mat-label
                        class="x-field-label"
                        *ngIf="notEmptyValue(getControlLabel(control)) | async"
                        >{{ getControlLabel(control) | async }}</mat-label
                      >

                      <!-- Select -->
                      <mat-select
                        [formControlName]="control.propName.toString()"
                        [multiple]="getSelectMultiple(control) | async"
                        [ngClass]="getFormControlClasses(control) | async"
                        [xFocus]="getControlFocusHandler(control) | async"
                        [class.multiple]="getSelectMultiple(control) | async"
                        [panelClass]="getSelectPanelClasses(control) | async"
                        [placeholder]="getControlPlaceHolder(control) | async"
                      >
                        <mat-option
                          [value]="getValue(option.value) | async"
                          [ngClass]="getOptionClassName(option) | async"
                          *ngFor="
                            let option of getSelectOptions(control) | async
                          "
                        >
                          {{ getResourceValue(option.viewValue) | async }}
                        </mat-option>
                      </mat-select>

                      <!-- Progressbar for Loading -->
                      <ng-container
                        *ngTemplateOutlet="
                          loadingRef;
                          context: { $implicit: control }
                        "
                      ></ng-container>

                      <!-- Prefix Icon -->
                      <span
                        matPrefix
                        *ngIf="controlHasPrefixIcon(control) | async"
                      >
                        <ng-container
                          *ngTemplateOutlet="
                            prefixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </span>

                      <!-- Suffix Icon -->
                      <span
                        matSuffix
                        *ngIf="controlHasSuffixIcon(control) | async"
                      >
                        <ng-container
                          *ngTemplateOutlet="
                            suffixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </span>
                    </mat-form-field>
                  </ng-container>

                  <!-- None Appearance -->
                  <ng-container
                    *ngIf="
                      (getControlAppearanceObject(control) | async) ===
                      FormControlAppearances.None
                    "
                  >
                    <!-- XForm Field -->
                    <div
                      xFocusHandler
                      [tabIndex]="controlIndex"
                      [enableHandler]="
                        !uiDisabled &&
                        !loading &&
                        !(getControlDisabled(control) | async)
                      "
                      [class.apply-status]="
                        getValue(applyStatusOnControls) | async
                      "
                      [ngClass]="getFormFieldClasses(control) | async"
                    >
                      <!-- Label -->
                      <ng-container
                        *ngTemplateOutlet="
                          byIconLabelRef;
                          context: { $implicit: control }
                        "
                      ></ng-container>

                      <!-- Select -->
                      <mat-select
                        [formControlName]="control.propName.toString()"
                        [multiple]="getSelectMultiple(control) | async"
                        [ngClass]="getFormControlClasses(control) | async"
                        [xFocus]="getControlFocusHandler(control) | async"
                        [class.multiple]="getSelectMultiple(control) | async"
                        [panelClass]="getSelectPanelClasses(control) | async"
                        [placeholder]="getControlPlaceHolder(control) | async"
                      >
                        <mat-option
                          [value]="getValue(option.value) | async"
                          [ngClass]="getOptionClassName(option) | async"
                          *ngFor="
                            let option of getSelectOptions(control) | async
                          "
                        >
                          {{ getResourceValue(option.viewValue) | async }}
                        </mat-option>
                      </mat-select>

                      <!-- Progressbar for Loading -->
                      <ng-container
                        *ngTemplateOutlet="
                          loadingRef;
                          context: { $implicit: control }
                        "
                      ></ng-container>
                    </div>
                  </ng-container>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 3- Radio Type -->
              <ng-container
                *ngIf="
                  (hasRadioConfig(control) | async) &&
                  controlType === ControlTypes.Radio
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                    [matTooltip]="getControlTooltip(control) | async"
                  >
                    <!-- Label -->
                    <ng-container
                      *ngTemplateOutlet="
                        byIconLabelRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Content -->
                    <mat-radio-group
                      [formControlName]="control.propName.toString()"
                      *ngIf="notNullOrUndefinedValue(control) | async"
                      [xFocus]="getControlFocusHandler(control) | async"
                      [ngClass]="getFormControlClasses(control) | async"
                      [labelPosition]="getRadioLabelPosition(control) | async"
                    >
                      <mat-radio-button
                        #radioButton
                        class="x-radio-button"
                        [class.active]="radioButton.checked"
                        [value]="getValue(option.value) | async"
                        [ngClass]="getOptionClassName(option) | async"
                        *ngFor="let option of getRadioOptions(control) | async"
                      >
                        {{ getResourceValue(option.viewValue) | async }}
                      </mat-radio-button>
                    </mat-radio-group>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 4- Checkbox Type -->
              <ng-container *ngIf="controlType === ControlTypes.CheckBox">
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                    [matTooltip]="getControlTooltip(control) | async"
                  >
                    <!-- Content -->
                    <x-slotter
                      [layout]="SlotLayouts.HORIZONTAL"
                      [hasStartOrTopSlot]="
                        (controlHasIcons(control) | async) &&
                        (controlHasPrefixIcon(control) | async)
                      "
                      [hasEndOrBottomSlot]="
                        (controlHasIcons(control) | async) &&
                        (controlHasSuffixIcon(control) | async)
                      "
                      [startTopSlotCssClass]="'auto-size-flex'"
                      [endBottomSlotCssClass]="'auto-size-flex'"
                    >
                      <!-- Start -->
                      <x-slot [name]="SlotNames.START">
                        <!-- Prefix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            prefixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </x-slot>

                      <!-- Center -->
                      <x-slot [name]="SlotNames.CENTER">
                        <mat-checkbox
                          [formControlName]="control.propName.toString()"
                          [labelPosition]="
                            getCheckboxLabelPosition(control) | async
                          "
                          *ngIf="notNullOrUndefinedValue(control) | async"
                          [xFocus]="getControlFocusHandler(control) | async"
                          [ngClass]="getFormControlClasses(control) | async"
                          [class.has-icon]="controlHasIcons(control) | async"
                          (change)="
                            handleCheckboxCheckedChange(control, $event.checked)
                          "
                        >
                          <!-- label -->
                          <ng-container
                            *ngTemplateOutlet="
                              defaultFieldLabelRef;
                              context: { $implicit: control }
                            "
                          ></ng-container>
                        </mat-checkbox>
                      </x-slot>

                      <!-- End -->
                      <x-slot [name]="SlotNames.END">
                        <!-- Suffix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            suffixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </x-slot>
                    </x-slotter>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 5- Toggle Type -->
              <ng-container *ngIf="controlType === ControlTypes.Toggle">
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                    [matTooltip]="getControlTooltip(control) | async"
                  >
                    <!-- Content -->
                    <x-slotter
                      [layout]="SlotLayouts.HORIZONTAL"
                      [hasStartOrTopSlot]="
                        (controlHasIcons(control) | async) &&
                        (controlHasPrefixIcon(control) | async)
                      "
                      [hasEndOrBottomSlot]="
                        (controlHasIcons(control) | async) &&
                        (controlHasSuffixIcon(control) | async)
                      "
                      [startTopSlotCssClass]="'auto-size-flex'"
                      [endBottomSlotCssClass]="'auto-size-flex'"
                    >
                      <!-- Start -->
                      <x-slot [name]="SlotNames.START">
                        <!-- Prefix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            prefixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </x-slot>

                      <!-- Center -->
                      <x-slot [name]="SlotNames.CENTER">
                        <!-- Content -->
                        <mat-slide-toggle
                          [formControlName]="control.propName.toString()"
                          [labelPosition]="
                            getToggleLabelPosition(control) | async
                          "
                          *ngIf="notNullOrUndefinedValue(control) | async"
                          [xFocus]="getControlFocusHandler(control) | async"
                          [ngClass]="getFormControlClasses(control) | async"
                          [class.has-icon]="controlHasIcons(control) | async"
                        >
                          <!-- label -->
                          <mat-label
                            class="x-field-label"
                            *ngIf="
                              notEmptyValue(getControlLabel(control)) | async
                            "
                          >
                            {{ getControlLabel(control) | async }}
                          </mat-label>
                        </mat-slide-toggle>
                      </x-slot>

                      <!-- End -->
                      <x-slot [name]="SlotNames.END">
                        <!-- Suffix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            suffixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>
                      </x-slot>
                    </x-slotter>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 6- Slider Type -->
              <ng-container
                *ngIf="
                  (hasSliderConfig(control) | async) &&
                  controlType === ControlTypes.Slider
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                    [matTooltip]="getControlTooltip(control) | async"
                  >
                    <!-- Label -->
                    <ng-container
                      *ngTemplateOutlet="
                        byIconLabelRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Content -->
                    <!-- [disabled]="getControlDisabled(control) | async" -->
                    <mat-slider
                      [formControlName]="control.propName.toString()"
                      [min]="getSliderMin(control) | async"
                      [max]="getSliderMax(control) | async"
                      [step]="getSliderStep(control) | async"
                      [invert]="getSliderInvert(control) | async"
                      [vertical]="isSliderVertical(control) | async"
                      *ngIf="notNullOrUndefinedValue(control) | async"
                      [xFocus]="getControlFocusHandler(control) | async"
                      [ngClass]="getFormControlClasses(control) | async"
                      [displayWith]="getSliderFormatter(control) | async"
                      [thumbLabel]="showSliderThumbLabel(control) | async"
                      [class.vertical]="isSliderVertical(control) | async"
                    >
                    </mat-slider>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 7- Date Type -->
              <ng-container
                *ngIf="
                  (hasDateConfig(control) | async) &&
                  controlType === ControlTypes.Date
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- Not None Appearance -->
                  <ng-container
                    *ngIf="
                      (getControlAppearanceObject(control) | async) !==
                      FormControlAppearances.None
                    "
                  >
                    <!-- Form Field -->
                    <mat-form-field
                      class="x-mat-form-field"
                      [class.apply-status]="
                        getValue(applyStatusOnControls) | async
                      "
                      [ngClass]="getFormFieldClasses(control) | async"
                      [appearance]="getControlAppearance(control) | async"
                    >
                      <!-- label -->
                      <mat-label
                        class="x-field-label"
                        *ngIf="notEmptyValue(getControlLabel(control)) | async"
                        >{{ getControlLabel(control) | async }}</mat-label
                      >

                      <!-- Date Input -->
                      <input
                        matInput
                        autocomplete="off"
                        [matDatepicker]="dp"
                        [formControlName]="control.propName.toString()"
                        [readonly]="
                          (getControlDisabled(control) | async) ||
                          (isDatePickerInputDisabled(control) | async)
                        "
                        (blur)="handleControlBlured(control.propName.toString())"
                        (focus)="handleControlFocused(control.propName.toString())"
                        [matTooltip]="getControlTooltip(control) | async"
                        [xFocus]="getControlFocusHandler(control) | async"
                        [ngClass]="getFormControlClasses(control) | async"
                        [matDatepickerFilter]="getDateFilter(control) | async"
                        [placeholder]="getControlPlaceHolder(control) | async"
                        (dateInput)="
                          handleDatePickerDateInput(control.propName.toString(), $event)
                        "
                        (dateChange)="
                          handleDatePickerDateChange(control.propName.toString(), $event)
                        "
                      />

                      <!-- Date Toggler Icon -->
                      <!-- Prefix -->
                      <mat-datepicker-toggle
                        [for]="dp"
                        matPrefix
                        [disabled]="
                          (getControlDisabled(control) | async) ||
                          (isDatePickerToggleDisabled(control) | async)
                        "
                        *ngIf="isDatePickerHasPrefixIcon(control) | async"
                      >
                        <x-icon
                          [selectable]="true"
                          matDatepickerToggleIcon
                          class="x-date-picker-icon"
                          [name]="getDatePickerIcon(control) | async"
                          [color]="getDatePickerIconColor(control) | async"
                          [matTooltip]="
                            getDatePickerIconTooltip(control) | async
                          "
                          [class.x-status-handler]="
                            getDatePickerIconApplyStateColor(control) | async
                          "
                        >
                        </x-icon>
                      </mat-datepicker-toggle>

                      <!-- Suffix -->
                      <mat-datepicker-toggle
                        [for]="dp"
                        matSuffix
                        [disabled]="
                          (getControlDisabled(control) | async) ||
                          (isDatePickerToggleDisabled(control) | async)
                        "
                        *ngIf="isDatePickerHasSuffixIcon(control) | async"
                      >
                        <x-icon
                          [selectable]="true"
                          [color]="'cb-inherit'"
                          matDatepickerToggleIcon
                          class="x-date-picker-icon"
                          [name]="getDatePickerIcon(control) | async"
                          [color]="getDatePickerIconColor(control) | async"
                          [matTooltip]="
                            getDatePickerIconTooltip(control) | async
                          "
                          [class.x-status-handler]="
                            getDatePickerIconApplyStateColor(control) | async
                          "
                        >
                        </x-icon>
                      </mat-datepicker-toggle>

                      <!-- Date Picker Component -->
                      <mat-datepicker
                        #dp
                        [touchUi]="isMobileUi$ | async"
                        [startAt]="getDateStartAt(control) | async"
                        [disabled]="getControlDisabled(control) | async"
                        [ngClass]="getDatePickerClasses(control) | async"
                        [panelClass]="getDatePickerClasses(control) | async"
                      ></mat-datepicker>

                      <!-- Progressbar for Loading -->
                      <ng-container
                        *ngTemplateOutlet="
                          loadingRef;
                          context: { $implicit: control }
                        "
                      ></ng-container>
                    </mat-form-field>
                  </ng-container>

                  <!-- None Appearance -->
                  <ng-container
                    *ngIf="
                      (getControlAppearanceObject(control) | async) ===
                      FormControlAppearances.None
                    "
                  >
                    <!-- XForm Field -->
                    <div
                      xFocusHandler
                      [tabIndex]="controlIndex"
                      [enableHandler]="
                        !uiDisabled &&
                        !loading &&
                        !(getControlDisabled(control) | async)
                      "
                      [class.apply-status]="
                        getValue(applyStatusOnControls) | async
                      "
                      [ngClass]="getFormFieldClasses(control) | async"
                    >
                      <!-- Label -->
                      <ng-container
                        *ngTemplateOutlet="
                          byIconLabelRef;
                          context: { $implicit: control }
                        "
                      ></ng-container>

                      <!-- Content -->
                      <x-slotter
                        [layout]="SlotLayouts.HORIZONTAL"
                        [centerSlotCssClass]="'full-heigh'"
                        [startTopSlotCssClass]="'auto-size-flex'"
                        [endBottomSlotCssClass]="'auto-size-flex'"
                        [hasStartOrTopSlot]="
                          isDatePickerHasPrefixIcon(control) | async
                        "
                        [hasEndOrBottomSlot]="
                          isDatePickerHasSuffixIcon(control) | async
                        "
                      >
                        <!-- Start -->
                        <x-slot [name]="SlotNames.START">
                          <!-- Date Toggler Icon -->
                          <!-- Prefix -->
                          <mat-datepicker-toggle
                            [for]="dp"
                            matPrefix
                            [disabled]="
                              (getControlDisabled(control) | async) ||
                              (isDatePickerToggleDisabled(control) | async)
                            "
                            *ngIf="isDatePickerHasPrefixIcon(control) | async"
                          >
                            <x-icon
                              [selectable]="true"
                              matDatepickerToggleIcon
                              class="x-date-picker-icon"
                              [name]="getDatePickerIcon(control) | async"
                              [color]="getDatePickerIconColor(control) | async"
                              [matTooltip]="
                                getDatePickerIconTooltip(control) | async
                              "
                              [class.x-status-handler]="
                                getDatePickerIconApplyStateColor(control)
                                  | async
                              "
                            >
                            </x-icon>
                          </mat-datepicker-toggle>
                        </x-slot>

                        <!-- Center -->
                        <x-slot [name]="SlotNames.CENTER">
                          <!-- Date Input -->
                          <input
                            matInput
                            autocomplete="off"
                            [matDatepicker]="dp"
                            (dateInput)="
                              handleDatePickerDateInput(
                                control.propName.toString(),
                                $event
                              )
                            "
                            (dateChange)="
                              handleDatePickerDateChange(
                                control.propName.toString(),
                                $event
                              )
                            "
                            [matDatepickerFilter]="
                              getDateFilter(control) | async
                            "
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisabled(control) | async) ||
                              (isDatePickerInputDisabled(control) | async)
                            "
                            (blur)="handleControlBlured(control.propName.toString())"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                          />
                        </x-slot>

                        <!-- End -->
                        <x-slot [name]="SlotNames.END">
                          <!-- Date Toggler Icon -->
                          <!-- Suffix -->
                          <mat-datepicker-toggle
                            [for]="dp"
                            matSuffix
                            [disabled]="
                              (getControlDisabled(control) | async) ||
                              (isDatePickerToggleDisabled(control) | async)
                            "
                            *ngIf="isDatePickerHasSuffixIcon(control) | async"
                          >
                            <x-icon
                              [selectable]="true"
                              [color]="'cb-inherit'"
                              matDatepickerToggleIcon
                              class="x-date-picker-icon"
                              [name]="getDatePickerIcon(control) | async"
                              [color]="getDatePickerIconColor(control) | async"
                              [matTooltip]="
                                getDatePickerIconTooltip(control) | async
                              "
                              [class.x-status-handler]="
                                getDatePickerIconApplyStateColor(control)
                                  | async
                              "
                            >
                            </x-icon>
                          </mat-datepicker-toggle>
                        </x-slot>
                      </x-slotter>

                      <!-- Date Picker Component -->
                      <mat-datepicker
                        #dp
                        [touchUi]="isMobileUi$ | async"
                        [startAt]="getDateStartAt(control) | async"
                        [disabled]="getControlDisabled(control) | async"
                        [ngClass]="getDatePickerClasses(control) | async"
                        [panelClass]="getDatePickerClasses(control) | async"
                      ></mat-datepicker>

                      <!-- Progressbar for Loading -->
                      <ng-container
                        *ngTemplateOutlet="
                          loadingRef;
                          context: { $implicit: control }
                        "
                      ></ng-container>
                    </div>
                  </ng-container>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 8- Hidden Type -->
              <ng-container *ngIf="controlType === ControlTypes.Hidden">
                <input
                  type="hidden"
                  autocomplete="off"
                  [formControlName]="control.propName.toString()"
                />
              </ng-container>

              <!-- 9- Markdown Type -->
              <ng-container
                *ngIf="
                  (hasMarkdownConfig(control) | async) &&
                  controlType === ControlTypes.MarkDown
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <ng-container
                    *ngTemplateOutlet="
                      markdownRef;
                      context: {
                        $implicit: control,
                        controlIndex: controlIndex
                      }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 10- Avatar Upload -->
              <ng-container
                *ngIf="
                  (hasAvatarConfig(control) | async) &&
                  controlType === ControlTypes.AvatarUpload
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <ng-container
                    *ngTemplateOutlet="
                      avatarRef;
                      context: {
                        $implicit: control,
                        controlIndex: controlIndex
                      }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 11- Map Type -->
              <ng-container
                *ngIf="
                  (hasMapConfig(control) | async) &&
                  controlType === ControlTypes.Map
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                  >
                    <!-- Label -->
                    <ng-container
                      *ngTemplateOutlet="
                        defaultFieldLabelRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Control Input Element -->
                    <x-slotter
                      [hasStartOrTopSlot]="true"
                      [hasEndOrBottomSlot]="true"
                      [layout]="SlotLayouts.HORIZONTAL"
                      [startTopSlotCssClass]="'auto-size-flex'"
                      [endBottomSlotCssClass]="'auto-size-flex'"
                      *ngIf="notNullOrUndefinedValue(control) | async"
                    >
                      <!-- Start -->
                      <x-slot [name]="SlotNames.START">
                        <!-- Prefix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            prefixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Action Prefix Icons -->
                        <ng-container
                          *ngTemplateOutlet="
                            actionIconsRef;
                            context: {
                              $implicit: control,
                              position: ControlIconPositions.Prefix
                            }
                          "
                        ></ng-container>
                      </x-slot>

                      <!-- Center -->
                      <x-slot [name]="SlotNames.CENTER">
                        <!-- Has Formatter Input Control -->
                        <ng-container
                          *ngIf="controlHasFormatter(control) | async"
                        >
                          <input
                            class="x-input"
                            autocomplete="off"
                            xFromInputFormatter
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            (blur)="handleControlBlured(control.propName.toString())"
                            [locale]="getFormatterLocale(control) | async"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [seperator]="getFormatterSeperator(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisableInput(control) | async) ||
                              (getControlDisabled(control) | async)
                            "
                            [inputFormatterType]="
                              getFormatterType(control) | async
                            "
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                            [currencyLable]="
                              getFormatterCurrencyLabel(control) | async
                            "
                            [enabledInputFormatter]="
                              controlHasFormatter(control) | async
                            "
                            [stringFormatter]="
                              getFormatterStringFormatter(control) | async
                            "
                            [stringInputFormatterType]="
                              getFormatterInputType(control) | async
                            "
                          />
                        </ng-container>

                        <!-- Has not Formatter Input Control -->
                        <ng-container
                          *ngIf="!(controlHasFormatter(control) | async)"
                        >
                          <input
                            class="x-input"
                            autocomplete="off"
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            (blur)="handleControlBlured(control.propName.toString())"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisableInput(control) | async) ||
                              (getControlDisabled(control) | async)
                            "
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                          />
                        </ng-container>
                      </x-slot>

                      <!-- End -->
                      <x-slot [name]="SlotNames.END">
                        <!-- Suffix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            suffixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Action Suffix Icons -->
                        <ng-container
                          *ngTemplateOutlet="
                            actionIconsRef;
                            context: {
                              $implicit: control,
                              position: ControlIconPositions.Suffix
                            }
                          "
                        ></ng-container>
                      </x-slot>
                    </x-slotter>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Content -->
                    <ng-container
                      *ngIf="!(isMapPresentWithDialog(control) | async)"
                    >
                      <ng-container
                        *ngTemplateOutlet="
                          mapContentRef;
                          context: { $implicit: control }
                        "
                      >
                      </ng-container>
                    </ng-container>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 12- ColorPicker Type -->
              <ng-container
                *ngIf="
                  (hasColorPickerConfig(control) | async) &&
                  controlType === ControlTypes.ColorPicker
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                  >
                    <!-- Label -->
                    <ng-container
                      *ngTemplateOutlet="
                        defaultFieldLabelRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Control Input Element -->
                    <x-slotter
                      [hasStartOrTopSlot]="true"
                      [hasEndOrBottomSlot]="true"
                      [layout]="SlotLayouts.HORIZONTAL"
                      [startTopSlotCssClass]="'auto-size-flex'"
                      [endBottomSlotCssClass]="'auto-size-flex'"
                      *ngIf="notNullOrUndefinedValue(control) | async"
                    >
                      <!-- Start -->
                      <x-slot [name]="SlotNames.START">
                        <!-- Prefix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            prefixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Action Prefix Icons -->
                        <ng-container
                          *ngTemplateOutlet="
                            actionIconsRef;
                            context: {
                              $implicit: control,
                              position: ControlIconPositions.Prefix
                            }
                          "
                        ></ng-container>
                      </x-slot>

                      <!-- Center -->
                      <x-slot [name]="SlotNames.CENTER">
                        <div fxLayout="row" fxLayoutAlign="none">
                          <!-- Input Box -->
                          <div fxFlex>
                            <!-- Has Formatter Input Control -->
                            <ng-container
                              *ngIf="controlHasFormatter(control) | async"
                            >
                              <input
                                class="x-input"
                                autocomplete="off"
                                xFromInputFormatter
                                [formControlName]="control.propName.toString()"
                                [id]="getControlInputElementId(control)"
                                [type]="getControlType(control) | async"
                                (blur)="handleControlBlured(control.propName.toString())"
                                [locale]="getFormatterLocale(control) | async"
                                (focus)="handleControlFocused(control.propName.toString())"
                                [xFocus]="
                                  getControlFocusHandler(control) | async
                                "
                                [matTooltip]="
                                  getControlTooltip(control) | async
                                "
                                [seperator]="
                                  getFormatterSeperator(control) | async
                                "
                                [placeholder]="
                                  getControlPlaceHolder(control) | async
                                "
                                [readonly]="
                                  (getControlDisableInput(control) | async) ||
                                  (getControlDisabled(control) | async)
                                "
                                [inputFormatterType]="
                                  getFormatterType(control) | async
                                "
                                [ngClass]="
                                  getControlInputElementClasses(control) | async
                                "
                                [currencyLable]="
                                  getFormatterCurrencyLabel(control) | async
                                "
                                [enabledInputFormatter]="
                                  controlHasFormatter(control) | async
                                "
                                [stringFormatter]="
                                  getFormatterStringFormatter(control) | async
                                "
                                [readonly]="
                                  (getControlDisabled(control) | async) ||
                                  (getControlDisableInput(control) | async) ||
                                  (isInputControlReadOnly(control) | async)
                                "
                                [stringInputFormatterType]="
                                  getFormatterInputType(control) | async
                                "
                              />
                            </ng-container>

                            <!-- Has not Formatter Input Control -->
                            <ng-container
                              *ngIf="!(controlHasFormatter(control) | async)"
                            >
                              <input
                                class="x-input"
                                autocomplete="off"
                                [formControlName]="control.propName.toString()"
                                [id]="getControlInputElementId(control)"
                                [type]="getControlType(control) | async"
                                (blur)="handleControlBlured(control.propName.toString())"
                                (focus)="handleControlFocused(control.propName.toString())"
                                [xFocus]="
                                  getControlFocusHandler(control) | async
                                "
                                [matTooltip]="
                                  getControlTooltip(control) | async
                                "
                                [placeholder]="
                                  getControlPlaceHolder(control) | async
                                "
                                [readonly]="
                                  (getControlDisableInput(control) | async) ||
                                  (getControlDisabled(control) | async) ||
                                  (isInputControlReadOnly(control) | async)
                                "
                                [ngClass]="
                                  getControlInputElementClasses(control) | async
                                "
                              />
                            </ng-container>
                          </div>

                          <!-- Color Preview -->
                          <div
                            *ngIf="
                              isColorPickerPresentWithDialog(control) | async
                            "
                          >
                            <x-color
                              [size]="22"
                              [showLabel]="false"
                              [value]="{
                                key: 'color',
                                value: getControl(control.propName.toString()).value
                              }"
                              [borderWidth]="
                                getColorPickerColorViewBorderWidth(control)
                                  | async
                              "
                              [borderStyle]="
                                getColorPickerColorViewBorderStyle(control)
                                  | async
                              "
                              [borderColor]="
                                getColorPickerColorViewBorderColor(control)
                                  | async
                              "
                            ></x-color>
                          </div>
                        </div>
                      </x-slot>

                      <!-- End -->
                      <x-slot [name]="SlotNames.END">
                        <!-- Suffix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            suffixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Action Suffix Icons -->
                        <ng-container
                          *ngTemplateOutlet="
                            actionIconsRef;
                            context: {
                              $implicit: control,
                              position: ControlIconPositions.Suffix
                            }
                          "
                        ></ng-container>
                      </x-slot>
                    </x-slotter>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Content -->
                    <ng-container
                      *ngIf="!(isColorPickerPresentWithDialog(control) | async)"
                    >
                      <ng-container
                        *ngTemplateOutlet="
                          colorPickerContentRef;
                          context: { $implicit: control }
                        "
                      >
                      </ng-container>
                    </ng-container>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <!-- 13- Selector Type -->
              <ng-container
                *ngIf="
                  (hasSelectorConfig(control) | async) &&
                  controlType === ControlTypes.Selector
                "
              >
                <!-- Form Control Wrapper -->
                <div
                  [hidden]="getControlHidden(control) | async"
                  [class.show]="!(getControlHidden(control) | async)"
                  [ngClass]="getFormControlWrapperClasses(control) | async"
                  [class.readonly]="isInputControlReadOnly(control) | async"
                  [class.apply-status]="getValue(applyStatusOnControls) | async"
                >
                  <!-- XForm Field -->
                  <div
                    xFocusHandler
                    [tabIndex]="controlIndex"
                    [enableHandler]="
                      !uiDisabled &&
                      !loading &&
                      !(getControlDisabled(control) | async)
                    "
                    [class.apply-status]="
                      getValue(applyStatusOnControls) | async
                    "
                    [ngClass]="getFormFieldClasses(control) | async"
                  >
                    <!-- Label -->
                    <ng-container
                      *ngTemplateOutlet="
                        defaultFieldLabelRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>

                    <!-- Control Input Element -->
                    <x-slotter
                      [hasStartOrTopSlot]="true"
                      [hasEndOrBottomSlot]="true"
                      [layout]="SlotLayouts.HORIZONTAL"
                      [startTopSlotCssClass]="'auto-size-flex'"
                      [endBottomSlotCssClass]="'auto-size-flex'"
                      *ngIf="notNullOrUndefinedValue(control) | async"
                    >
                      <!-- Start -->
                      <x-slot [name]="SlotNames.START">
                        <!-- Prefix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            prefixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Action Prefix Icons -->
                        <ng-container
                          *ngTemplateOutlet="
                            actionIconsRef;
                            context: {
                              $implicit: control,
                              position: ControlIconPositions.Prefix
                            }
                          "
                        ></ng-container>
                      </x-slot>

                      <!-- Center -->
                      <x-slot [name]="SlotNames.CENTER">
                        <!-- Has Formatter Input Control -->
                        <ng-container
                          *ngIf="controlHasFormatter(control) | async"
                        >
                          <input
                            class="x-input"
                            autocomplete="off"
                            xFromInputFormatter
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            (blur)="handleControlBlured(control.propName.toString())"
                            [locale]="getFormatterLocale(control) | async"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [seperator]="getFormatterSeperator(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisabled(control) | async) ||
                              (getControlDisableInput(control) | async) ||
                              (isInputControlReadOnly(control) | async)
                            "
                            [inputFormatterType]="
                              getFormatterType(control) | async
                            "
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                            [currencyLable]="
                              getFormatterCurrencyLabel(control) | async
                            "
                            [enabledInputFormatter]="
                              controlHasFormatter(control) | async
                            "
                            [stringFormatter]="
                              getFormatterStringFormatter(control) | async
                            "
                            [stringInputFormatterType]="
                              getFormatterInputType(control) | async
                            "
                          />
                        </ng-container>

                        <!-- Has not Formatter Input Control -->
                        <ng-container
                          *ngIf="!(controlHasFormatter(control) | async)"
                        >
                          <input
                            class="x-input"
                            autocomplete="off"
                            [formControlName]="control.propName.toString()"
                            [id]="getControlInputElementId(control)"
                            [type]="getControlType(control) | async"
                            (blur)="handleControlBlured(control.propName.toString())"
                            (focus)="handleControlFocused(control.propName.toString())"
                            [matTooltip]="getControlTooltip(control) | async"
                            [xFocus]="getControlFocusHandler(control) | async"
                            [placeholder]="
                              getControlPlaceHolder(control) | async
                            "
                            [readonly]="
                              (getControlDisabled(control) | async) ||
                              (getControlDisableInput(control) | async) ||
                              (isInputControlReadOnly(control) | async)
                            "
                            [ngClass]="
                              getControlInputElementClasses(control) | async
                            "
                          />
                        </ng-container>
                      </x-slot>

                      <!-- End -->
                      <x-slot [name]="SlotNames.END">
                        <!-- Suffix Icon -->
                        <ng-container
                          *ngTemplateOutlet="
                            suffixIconRef;
                            context: { $implicit: control }
                          "
                        ></ng-container>

                        <!-- Action Suffix Icons -->
                        <ng-container
                          *ngTemplateOutlet="
                            actionIconsRef;
                            context: {
                              $implicit: control,
                              position: ControlIconPositions.Suffix
                            }
                          "
                        ></ng-container>
                      </x-slot>
                    </x-slotter>

                    <!-- Progressbar for Loading -->
                    <ng-container
                      *ngTemplateOutlet="
                        loadingRef;
                        context: { $implicit: control }
                      "
                    ></ng-container>
                  </div>

                  <!-- Hint -->
                  <ng-container
                    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
                  ></ng-container>

                  <!-- Errors -->
                  <ng-container
                    *ngTemplateOutlet="
                      errorsRef;
                      context: { $implicit: control }
                    "
                  ></ng-container>
                </div>
              </ng-container>
            </ng-container>
          </ng-container>
        </ng-container>
      </ng-container>
    </form>

    <!-- Custom Content -->
    <ng-content select="[contents]"></ng-content>

    <!-- Form Error Handlers -->
    <div
      [ngClass]="getFormErrorsClasses() | async"
      *ngIf="errors && (formHasErrorHandlers() | async)"
    >
      <!-- Errors -->
      <ng-container *ngIf="getFormErrorHandlers() | async as errorHandlers">
        <mat-error
          *ngFor="let handler of errorHandlers"
          [class.show]="formHasError(handler) | async"
          [ngClass]="getFormErrorClasses(handler) | async"
        >
          <span class="x-error-message">
            {{ getErrorHandlerErrorMessage(handler) | async }}
          </span>
        </mat-error>
      </ng-container>
    </div>
  </div>
</ng-template>

<!-- 9- MarkDown Template -->
<ng-template #markdownRef let-control let-controlIndex="controlIndex">
  <ng-container
    *ngTemplateOutlet="
      xFormFieldRef;
      context: {
        $implicit: control,
        controlIndex: controlIndex,
        fieldLabelRef: byIconLabelRef,
        fieldContentRef: markdownContentRef
      }
    "
  >
  </ng-container>
</ng-template>

<!-- 10 -Avatar Template -->
<ng-template #avatarRef let-control let-controlIndex="controlIndex">
  <ng-container
    *ngTemplateOutlet="
      xFormFieldRef;
      context: {
        $implicit: control,
        controlIndex: controlIndex,
        fieldLabelRef: defaultFieldLabelRef,
        fieldContentRef: avatarContentRef
      }
    "
  >
  </ng-container>
</ng-template>

<!-- //#region Templates -->
<!-- MarkDown Content Template -->
<ng-template #markdownContentRef let-control>
  <!-- Markdown Component -->
  <x-markdown
    [mode]="getMarkdownMode(control) | async"
    [isInPage]="isMarkdownInPage(control) | async"
    [content]="getMarkDownContent(control) | async"
    *ngIf="notNullOrUndefinedValue(control) | async"
    [cssClass]="getMarkdownCssClass(control) | async"
    [xFocus]="getControlFocusHandler(control) | async"
    [ngClass]="getFormControlClasses(control) | async"
    [cardTitle]="getMarkdownCardTitle(control) | async"
    [cardColor]="getMarkdownCardColor(control) | async"
    [toolbarForegroundColor]="
      getMarkDownToolbarForegroundColor(control) | async
    "
    [hasToolbar]="getMarkDownHasToolbar(control) | async"
    [wrapWithCard]="isMarkdownWrapWithCard(control) | async"
    [cardSubTitle]="getMarkdownCardSubTitle(control) | async"
    [toolbarColor]="getMarkDownToolbarColor(control) | async"
    [toolbarConfig]="getMarkdownToolbarConfig(control) | async"
    [actionProvider]="getMarkdownActionProvider(control) | async"
    [editorBoxColor]="getMarkdownEditorBoxColor(control) | async"
    [previewBoxColor]="getMarkdownPreviewBoxColor(control) | async"
    [toolbarCssClass]="getMarkdownToolbarCssClass(control) | async"
    [uiDisabled]="uiDisabled || (getControlDisabled(control) | async)"
    [cardForegroundColor]="getMarkdownCardForegroundColor(control) | async"
    (contentChange)="handleMarkDownContentChanged(control.propName.toString(), $event)"
  ></x-markdown>
</ng-template>

<!-- Avatar Content Template -->
<ng-template #avatarContentRef let-control>
  <!-- Avatar Upload -->
  <x-avatar-upload
    [color]="getAvatarColor(control) | async"
    [isInPage]="isAvatarInPage(control) | async"
    [actions]="getAvatarActions(control) | async"
    [cssClass]="getAvatarCssClass(control) | async"
    [cardColor]="getAvatarCardColor(control) | async"
    [cardTitle]="getAvatarCardTitle(control) | async"
    [ngClass]="getFormControlClasses(control) | async"
    [fileDropAreaForegroundColor]="
      isAvatarDropAreaForegroundColor(control) | async
    "
    [xFocus]="getControlFocusHandler(control) | async"
    [fileDropAreaActionProvider]="
      getAvatarDropAreaActionProvider(control) | async
    "
    [fileDropAreaDisableFileCheck]="
      isAvatarDropAreaDisableFileCheck(control) | async
    "
    [fileDropAreaContentTemplate]="
      getAvatarDropAreaContentTemplate(control) | async
    "
    [notAllowedFileException]="
      getAvatarNotAllowedFileException(control) | async
    "
    [fileDropAreaAllowedExtensions]="
      getAvatarDropAreaAllowedExtensions(control) | async
    "
    [wrapWithCard]="isAvatarWrapWithCard(control) | async"
    [fileDropAreaCardForegroundColor]="
      isAvatarDropAreaCardForegroundColor(control) | async
    "
    [cardSubTitle]="getAvatarCardSubTitle(control) | async"
    [minAllowedFileSizeException]="
      getAvatarMinAllowedFileSizeException(control) | async
    "
    [cardShowFooter]="avatarCardShowFooter(control) | async"
    [maxAllowedFileSizeException]="
      getAvatarMaxAllowedFileSizeException(control) | async
    "
    [fileDropAreaSupportsMultipleFiles]="
      isAvatarDropAreaSupportsMultipleFiles(control) | async
    "
    (avatarChange)="handleAvatarAvatarChange(control, $event)"
    [actionProvider]="getAvatarActionProvider(control) | async"
    [fileDropAreaIcon]="getAvatarDropAreaIcon(control) | async"
    [foregroundColor]="isAvatarForegroundColor(control) | async"
    (fileDropAreaFilesChangeEmiter)="
      handleAvatarFileDropAreaFilesChangeEmitter(control, $event)
    "
    [fileDropAreaColor]="getAvatarDropAreaColor(control) | async"
    [fileDropAreaIsInPage]="isAvatarDropAreaInPage(control) | async"
    [fileDropAreaMessage]="getAvatarDropAreaMessage(control) | async"
    [allowedExtensions]="getAvatarAllowedExtensions(control) | async"
    [placeHolderAvatar]="getAvatarPlaceHolderAvatar(control) | async"
    [uiDisabled]="uiDisabled || (getControlDisabled(control) | async)"
    [fileDropAreaCssClass]="getAvatarDropAreaCssClass(control) | async"
    [emptyFileException]="getAvatarEmptyFileException(control) | async"
    [minAllowedFileSize]="getAvatarMinAllowedFileSize(control) | async"
    [maxAllowedFileSize]="getAvatarMaxAllowedFileSize(control) | async"
    [cardForegroundColor]="isAvatarCardForegroundColor(control) | async"
    [fileDropAreaCardColor]="getAvatarDropAreaCardColor(control) | async"
    [fileDropAreaCardTitle]="getAvatarDropAreaCardTitle(control) | async"
    [fileDropAreaErrorColor]="getAvatarDropAreaErrorColor(control) | async"
    [fileDropAreaWrapWithCard]="isAvatarDropAreaWrapWithCard(control) | async"
    [fileDropAreaDragDisabled]="isAvatarDropAreaDragDisabled(control) | async"
    [fileDropAreaAccentColor]="getAvatarDropAreaAccesntColor(control) | async"
    [fileDropAreaSuccessColor]="getAvatarDropAreaSuccessColor(control) | async"
    [fileDropAreaCardSubTitle]="getAvatarDropAreaCardSubTitle(control) | async"
    [fileDropAreaCardShowFooter]="avatarDropAreaCardShowFooter(control) | async"
    [showFileDropArea]="
      !(isMobileUi$ | async) && (avatarCanDrop(control) | async)
    "
  >
  </x-avatar-upload>
</ng-template>

<!-- Map Content Template -->
<ng-template #mapContentRef let-control>
  <!-- Map Component -->
  <x-map
    [zoom]="getMapZoom(control) | async"
    [loading]="getValue(loading) | async"
    (isReady)="handleMapIsReady(control)"
    [isInPage]="isMapInPage(control) | async"
    [showZoom]="mapShowZoom(control) | async"
    [canSelect]="mapCanSelect(control) | async"
    [cssClass]="getMapCssClass(control) | async"
    [latitude]="getMapLatitude(control) | async"
    [showRotate]="mapShowRotate(control) | async"
    [showLocator]="mapShowLocator(control) | async"
    [cardTitle]="getMapCardTitle(control) | async"
    [longitude]="getMapLongitude(control) | async"
    [cardColor]="getMapCardColor(control) | async"
    [searchBarCancelButtonIcon]="
      getMapSearchBarCancelButtonIcon(control) | async
    "
    *ngIf="!(isMapPresentWithDialog(control) | async)"
    [ngClass]="getFormControlClasses(control) | async"
    [markerStyle]="getMapMarkerStyle(control) | async"
    [wrapWithCard]="isMapWrapWithCard(control) | async"
    [showSearchBar]="mapShowSearchBar(control) | async"
    [searchBarCancelButtonTitle]="
      getMapSearchBarCancelButtonTitle(control) | async
    "
    [cardSubTitle]="getMapCardSubTitle(control) | async"
    [cardShowFooter]="mapCardShowFooter(control) | async"
    [showZoomSlider]="mapShowZoomSlider(control) | async"
    [searchBarType]="getMapSearchBarType(control) | async"
    [showClearMarker]="mapShowClearMarker(control) | async"
    [showProgressBar]="mapShowProgressBar(control) | async"
    [actionProvider]="getMapActionProvider(control) | async"
    [searchBarColor]="getMapSearchBarColor(control) | async"
    [searchBarValue]="getMapSearchBarValue(control) | async"
    [searchBarTitle]="getMapSearchBarTitle(control) | async"
    [locatorPosition]="getMapLocatorPosition(control) | async"
    [showGoMarkedPlace]="mapShowGoMarkedPlace(control) | async"
    [progressBarColor]="getMapProgressBarColor(control) | async"
    [searchBarForegroundSearchInputColor]="
      getMapSearchBarForegroundSearchInputColor(control) | async
    "
    [searchBarDebounce]="getMapSearchBarDebounce(control) | async"
    [searchBarCssClass]="getMapSearchBarCssClass(control) | async"
    [searchBarPosition]="getMapSearchBarPosition(control) | async"
    (locationSelected)="handleMapLocationSelected(control, $event)"
    [cardForegroundColor]="isMapCardForegroundColor(control) | async"
    [showSelectedLocation]="mapShowSelectedLocation(control) | async"
    [searchPlaceProvider]="getMapSearchPlaceProvider(control) | async"
    [uiDisabled]="uiDisabled || (getControlDisabled(control) | async)"
    [clearMarkerPosition]="getMapClearMarkerPosition(control) | async"
    [searchPlaceProvider]="getMapSearchPlaceProvider(control) | async"
    [searchBarSpellcheck]="getMapSearchBarSpellcheck(control) | async"
    [searchBarSearchInputColor]="
      getMapSearchBarSearchInputColor(control) | async
    "
    [progressBarPosition]="getMapProgressBarPosition(control) | async"
    [searchResultListColor]="getMapSearchResultListColor(control) | async"
    (searchBarQueryChange)="handleMapSearchBarQueryChange(control, $event)"
    [searchBarActionProvider]="getMapSearchBarActionProvider(control) | async"
    [searchBarShowCancelButton]="mapShowSearchBarCancelButton(control) | async"
    [searchBarClearButtonIcon]="getMapSearchBarClearButtonIcon(control) | async"
    [searchBarForegroundColor]="getMapSearchBarForegroundColor(control) | async"
    [selectedLocationPosition]="getMapSelectedLocationPosition(control) | async"
    [searchBarClearButtonIconColor]="
      getMapSearchBarClearButtonIconColor(control) | async
    "
    [searchBarSearchButtonIconColor]="
      getMapSearchBarSearchButtonIconColor(control) | async
    "
  >
  </x-map>
  <!-- (zoomChanged)="handleMapZoomChanged(control, $event)" -->
</ng-template>

<!-- ColorPicker Content Template -->
<ng-template #colorPickerContentRef let-control>
  <!-- ColorPicker Component -->
  <x-color-picker
    [color]="getColorPickerColor(control) | async"
    [isInPage]="isColorPickerInPage(control) | async"
    [ngClass]="getFormControlClasses(control) | async"
    [cssClass]="getColorPickerCssClass(control) | async"
    [cardColor]="getColorPickerCardColor(control) | async"
    [showSelectedColorPreview]="
      colorPickerShowSelectedColorPreview(control) | async
    "
    [cardTitle]="getColorPickerCardTitle(control) | async"
    [representColorFormats]="
      getColorPickerRepresentColorFormats(control) | async
    "
    [presetColorsLableColor]="
      getColorPickerPresetColorsLableColor(control) | async
    "
    [copyToClipboardIconColor]="
      getColorPickerCopyToClipboardIconColor(control) | async
    "
    [copyToClipboardIconLabel]="
      getColorPickerCopyToClipboardIconLabel(control) | async
    "
    [cursorColor]="getColorPickerCursorColor(control) | async"
    *ngIf="!(isColorPickerPresentWithDialog(control) | async)"
    [wrapWithCard]="isColorPickerWrapWithCard(control) | async"
    [cardSubTitle]="getColorPickerCardSubTitle(control) | async"
    [presetColors]="getColorPickerPresetColors(control) | async"
    [outputFormat]="getColorPickerOutputFormat(control) | async"
    [cardShowFooter]="colorPickercardShowFooter(control) | async"
    [showHueChannel]="colorPickerShowHueChannel(control) | async"
    [showColorValue]="colorPickerShowColorValue(control) | async"
    [showColorInput]="colorPickerShowColorInput(control) | async"
    [selectedColor]="getColorPickerSelectedColor(control) | async"
    [actionProvider]="getColorPickerActionProvider(control) | async"
    [colorTextColor]="getColorPickerColorTextColor(control) | async"
    [foregroundColor]="isColorPickerForegroundColor(control) | async"
    [showPresetColors]="colorPickerShowPresetColors(control) | async"
    [showAlphaChannel]="colorPickerShowAlphaChannel(control) | async"
    (colorSelected)="handleColorPickerColorSelected(control, $event)"
    [uiDisabled]="uiDisabled || (getControlDisabled(control) | async)"
    [showPaletteChannel]="colorPickerShowPaletteChannel(control) | async"
    [presetColorsLable]="getColorPickerPresetColorsLable(control) | async"
    [showCopyToClipboard]="colorPickerShowCopyToClipboard(control) | async"
    [cardForegroundColor]="isColorPickerCardForegroundColor(control) | async"
    [clearColorIconColor]="getColorPickerClearColorIconColor(control) | async"
    [clearColorIconLabel]="getColorPickerClearColorIconLabel(control) | async"
    [colorInputIconColor]="getColorPickerColorInputIconColor(control) | async"
    [colorInputIconLabel]="getColorPickerColorInputIconLabel(control) | async"
  >
  </x-color-picker>
</ng-template>
<!-- //#endregion -->

<!-- By Icon Label Template -->
<ng-template #byIconLabelRef let-control>
  <!-- Label and Icons -->
  <x-slotter
    [layout]="SlotLayouts.HORIZONTAL"
    [hasStartOrTopSlot]="
      (controlHasIcons(control) | async) &&
      (controlHasPrefixIcon(control) | async)
    "
    [hasEndOrBottomSlot]="
      (controlHasIcons(control) | async) &&
      (controlHasSuffixIcon(control) | async)
    "
    [startTopSlotCssClass]="'auto-size-flex'"
    [endBottomSlotCssClass]="'auto-size-flex'"
  >
    <!-- Start -->
    <x-slot [name]="SlotNames.START">
      <!-- Prefix Icon -->
      <ng-container
        *ngTemplateOutlet="prefixIconRef; context: { $implicit: control }"
      ></ng-container>
    </x-slot>

    <!-- Center -->
    <x-slot [name]="SlotNames.CENTER">
      <!-- label -->
      <ng-container
        *ngTemplateOutlet="
          defaultFieldLabelRef;
          context: { $implicit: control }
        "
      ></ng-container>
    </x-slot>

    <!-- End -->
    <x-slot [name]="SlotNames.END">
      <!-- Suffix Icon -->
      <ng-container
        *ngTemplateOutlet="suffixIconRef; context: { $implicit: control }"
      ></ng-container>
    </x-slot>
  </x-slotter>
</ng-template>

<!-- By Icon Label Template -->
<ng-template #byIconContentRef let-control let-contentRef="contentRef">
  <!-- Label and Icons -->
  <x-slotter
    [layout]="SlotLayouts.HORIZONTAL"
    [hasStartOrTopSlot]="
      (controlHasIcons(control) | async) &&
      (controlHasPrefixIcon(control) | async)
    "
    [hasEndOrBottomSlot]="
      (controlHasIcons(control) | async) &&
      (controlHasSuffixIcon(control) | async)
    "
    [startTopSlotCssClass]="'auto-size-flex'"
    [endBottomSlotCssClass]="'auto-size-flex'"
  >
    <!-- Start -->
    <x-slot [name]="SlotNames.START">
      <!-- Prefix Icon -->
      <ng-container
        *ngTemplateOutlet="prefixIconRef; context: { $implicit: control }"
      ></ng-container>
    </x-slot>

    <!-- Center -->
    <x-slot [name]="SlotNames.CENTER">
      <ng-container *ngIf="contentRef">
        <ng-container
          *ngTemplateOutlet="contentRef; context: { $implicit: control }"
        ></ng-container>
      </ng-container>
    </x-slot>

    <!-- End -->
    <x-slot [name]="SlotNames.END">
      <!-- Suffix Icon -->
      <ng-container
        *ngTemplateOutlet="suffixIconRef; context: { $implicit: control }"
      ></ng-container>
    </x-slot>
  </x-slotter>
</ng-template>

<!-- Prefix Icon Template -->
<ng-template #prefixIconRef let-control>
  <!-- Prefix Icon -->
  <x-icon
    matPrefix
    [name]="getControlIconName(icon) | async"
    (clicked)="handleControlIconClicked(icon)"
    [color]="getControlIconColor(icon) | async"
    [matTooltip]="getControlIconTooltip(icon) | async"
    *ngIf="getControlPrefixIcon(control) | async as icon"
    [clickable]="notNullOrUndefinedValue(icon.handler) | async"
    [uiDisabled]="(getValue(uiDisabled) | async) && !icon.handler"
    [class.x-status-handler]="getControlIconApplyStateColor(icon) | async"
  >
  </x-icon>
</ng-template>

<!-- Suffix Icon Template -->
<ng-template #suffixIconRef let-control>
  <!-- Suffix Icon -->
  <x-icon
    matSuffix
    [name]="getControlIconName(icon) | async"
    (clicked)="handleControlIconClicked(icon)"
    [color]="getControlIconColor(icon) | async"
    [matTooltip]="getControlIconTooltip(icon) | async"
    *ngIf="getControlSuffixIcon(control) | async as icon"
    [clickable]="notNullOrUndefinedValue(icon.handler) | async"
    [uiDisabled]="(getValue(uiDisabled) | async) && !icon.handler"
    [class.x-status-handler]="getControlIconApplyStateColor(icon) | async"
  >
  </x-icon>
</ng-template>

<!-- Loading Ref Template -->
<ng-template #loadingRef let-control>
  <ion-progress-bar
    type="indeterminate"
    [color]="getProgressBarColor(control) | async"
    *ngIf="
      (controlHasProgressBar(control) | async) &&
      getControlStatus(control) === FormStatuses.Pending
    "
  ></ion-progress-bar>
</ng-template>

<!-- Hint Template -->
<ng-template #hintRef let-control>
  <mat-hint
    [ngClass]="getFormControlHintClasses(control) | async"
    *ngIf="notEmptyValue(getControlHint(control) | async) | async"
    >{{ getControlHint(control) | async }}</mat-hint
  >
</ng-template>

<!-- Errors Ref Template -->
<ng-template #errorsRef let-control>
  <div
    *ngIf="
      (controlHasErrors(control) | async) &&
      (controlHasErrorHandlers(control) | async)
    "
    [ngClass]="getControlErrorsClasses(control) | async"
  >
    <ng-container
      *ngIf="getControlErrorHandlers(control) | async as errorHandlers"
    >
      <mat-error
        *ngFor="let handler of errorHandlers"
        [class.show]="controlHasError(control, handler) | async"
        [ngClass]="getControlErrorClasses(control, handler) | async"
      >
        <span class="x-error-message">
          {{ getErrorHandlerErrorMessage(handler, control) | async }}
        </span>
      </mat-error>
    </ng-container>
  </div>
</ng-template>

<!-- XFormField Template -->
<ng-template
  #xFormFieldRef
  let-control
  let-controlIndex="controlIndex"
  let-fieldLabelRef="fieldLabelRef"
  let-fieldInputRef="fieldInputRef"
  let-fieldContentRef="fieldContentRef"
>
  <!-- XForm Field -->
  <div
    xFocusHandler
    [tabIndex]="controlIndex"
    [ngClass]="getFormFieldClasses(control) | async"
    [class.apply-status]="getValue(applyStatusOnControls) | async"
    [enableHandler]="
      !uiDisabled && !loading && !(getControlDisabled(control) | async)
    "
  >
    <!-- Label -->
    <ng-container *ngIf="fieldLabelRef">
      <div [ngClass]="getFormControlFieldLabelClasses(control) | async">
        <ng-container
          *ngTemplateOutlet="fieldLabelRef; context: { $implicit: control }"
        ></ng-container>
      </div>
    </ng-container>

    <!-- Input -->
    <ng-container *ngIf="fieldInputRef">
      <ng-container
        *ngTemplateOutlet="fieldInputRef; context: { $implicit: control }"
      ></ng-container>
    </ng-container>

    <!-- Show hint in below of input if exists -->
    <ng-container *ngIf="fieldInputRef">
      <!-- Progressbar for Loading -->
      <ng-container
        *ngTemplateOutlet="loadingRef; context: { $implicit: control }"
      ></ng-container>
    </ng-container>

    <!-- Content -->
    <ng-container
      *ngTemplateOutlet="fieldContentRef; context: { $implicit: control }"
    ></ng-container>

    <!-- Show hint in below of content when input not exists -->
    <ng-container *ngIf="!fieldInputRef">
      <!-- Progressbar for Loading -->
      <ng-container
        *ngTemplateOutlet="loadingRef; context: { $implicit: control }"
      ></ng-container>
    </ng-container>
  </div>

  <!-- Hint -->
  <ng-container
    *ngTemplateOutlet="hintRef; context: { $implicit: control }"
  ></ng-container>

  <!-- Errors -->
  <ng-container
    *ngTemplateOutlet="errorsRef; context: { $implicit: control }"
  ></ng-container>
</ng-template>

<!-- XField Label Template -->
<ng-template #defaultFieldLabelRef let-control>
  <!-- label -->
  <mat-label
    class="x-field-label"
    *ngIf="notEmptyValue(getControlLabel(control)) | async"
    >{{ getControlLabel(control) | async }}</mat-label
  >
</ng-template>

<!-- byIcon Input Element Template -->
<ng-template #byIconInputElemntRef let-control>
  <!-- Control Input Element -->
  <x-slotter
    [hasStartOrTopSlot]="true"
    [hasEndOrBottomSlot]="true"
    [layout]="SlotLayouts.HORIZONTAL"
    [startTopSlotCssClass]="'auto-size-flex'"
    [endBottomSlotCssClass]="'auto-size-flex'"
    *ngIf="notNullOrUndefinedValue(control) | async"
  >
    <!-- Start -->
    <x-slot [name]="SlotNames.START">
      <!-- Prefix Icon -->
      <ng-container
        *ngTemplateOutlet="prefixIconRef; context: { $implicit: control }"
      ></ng-container>

      <!-- Action Prefix Icons -->
      <ng-container
        *ngTemplateOutlet="
          actionIconsRef;
          context: {
            $implicit: control,
            position: ControlIconPositions.Prefix
          }
        "
      ></ng-container>
    </x-slot>

    <!-- Center -->
    <x-slot [name]="SlotNames.CENTER">
      <ng-container
        *ngTemplateOutlet="
          defaultXInputElement;
          context: { $implicit: control }
        "
      ></ng-container>
    </x-slot>

    <!-- End -->
    <x-slot [name]="SlotNames.END">
      <!-- Suffix Icon -->
      <ng-container
        *ngTemplateOutlet="suffixIconRef; context: { $implicit: control }"
      ></ng-container>

      <!-- Action Suffix Icons -->
      <ng-container
        *ngTemplateOutlet="
          actionIconsRef;
          context: {
            $implicit: control,
            position: ControlIconPositions.Suffix
          }
        "
      ></ng-container>
    </x-slot>
  </x-slotter>
</ng-template>

<!-- Default X Input Element Template -->
<ng-template #defaultXInputElement let-control>
  <!-- Has Formatter Input Control -->
  <ng-container *ngIf="controlHasFormatter(control) | async">
    <input
      class="x-input"
      autocomplete="off"
      xFromInputFormatter
      [formControlName]="control.propName.toString()"
      [id]="getControlInputElementId(control)"
      [type]="getControlType(control) | async"
      (blur)="handleControlBlured(control.propName.toString())"
      [locale]="getFormatterLocale(control) | async"
      (focus)="handleControlFocused(control.propName.toString())"
      [matTooltip]="getControlTooltip(control) | async"
      [xFocus]="getControlFocusHandler(control) | async"
      [seperator]="getFormatterSeperator(control) | async"
      [placeholder]="getControlPlaceHolder(control) | async"
      [inputFormatterType]="getFormatterType(control) | async"
      [ngClass]="getControlInputElementClasses(control) | async"
      [currencyLable]="getFormatterCurrencyLabel(control) | async"
      [enabledInputFormatter]="controlHasFormatter(control) | async"
      [stringFormatter]="getFormatterStringFormatter(control) | async"
      [stringInputFormatterType]="getFormatterInputType(control) | async"
    />
  </ng-container>

  <!-- Has not Formatter Input Control -->
  <ng-container *ngIf="!(controlHasFormatter(control) | async)">
    <input
      class="x-input"
      autocomplete="off"
      [formControlName]="control.propName.toString()"
      [id]="getControlInputElementId(control)"
      [type]="getControlType(control) | async"
      (blur)="handleControlBlured(control.propName.toString())"
      (focus)="handleControlFocused(control.propName.toString())"
      [matTooltip]="getControlTooltip(control) | async"
      [xFocus]="getControlFocusHandler(control) | async"
      [placeholder]="getControlPlaceHolder(control) | async"
      [ngClass]="getControlInputElementClasses(control) | async"
    />
  </ng-container>
</ng-template>

<!-- Action Icons Template -->
<ng-template #actionIconsRef let-control let-position="position">
  <!-- Clear -->
  <ng-container
    *ngIf="
      controlHasValue(control.propName.toString()) &&
      (hasControlClearIcon(control) | async) &&
      (notNullOrUndefinedValue(control) | async)
    "
  >
    <ng-container *ngIf="getControlClearIcon(control) | async as descriptor">
      <ng-container
        *ngIf="
          (getControlIconDescriptorPosition(descriptor) | async) === position
        "
      >
        <ng-container
          *ngTemplateOutlet="
            actionIconRef;
            context: {
              $implicit: descriptor,
              role: ControlIconRoles.Clear,
              control: control
            }
          "
        ></ng-container>
      </ng-container>
    </ng-container>
  </ng-container>

  <!-- Select -->
  <ng-container *ngIf="getControlType(control) | async as controlType">
    <!-- Recieve Control Type for Handling Without Dialog Actions -->
    <ng-container
      *ngIf="
        (controlType !== ControlTypes.Map &&
          controlType !== ControlTypes.ColorPicker) ||
        (controlType === ControlTypes.Map &&
          (isMapPresentWithDialog(control) | async)) ||
        (controlType === ControlTypes.ColorPicker &&
          (isColorPickerPresentWithDialog(control) | async))
      "
    >
      <!-- if Map/ColorPicker Control Presented Without Dialog, there is no need to has Select icon ... -->
      <ng-container *ngIf="getControlSelectIcon(control) | async as descriptor">
        <ng-container
          *ngIf="
            (getControlIconDescriptorPosition(descriptor) | async) === position
          "
        >
          <ng-container
            *ngTemplateOutlet="
              actionIconRef;
              context: {
                control: control,
                $implicit: descriptor,
                role: ControlIconRoles.Select
              }
            "
          ></ng-container>
        </ng-container>
      </ng-container>
    </ng-container>
  </ng-container>
</ng-template>

<!-- Action Icon Template -->
<ng-template
  #actionIconRef
  let-descriptor
  let-role="role"
  let-control="control"
>
  <x-icon
    [clickable]="true"
    class="x-action-icon"
    [uiDisabled]="getValue(uiDisabled) | async"
    [uiDisabled]="
      (getControlDisabled(control) | async) ||
      (getControlDisableIcon(control) | async)
    "
    [class.x-status-handler]="
      getControlIconDescriptorApplyStateColor(descriptor) | async
    "
    [name]="getControlIconDescriptorName(role, descriptor) | async"
    [color]="getControlIconDescriptorColor(role, descriptor) | async"
    (clicked)="handleControlActionIconClicked(role, descriptor, control)"
    [matTooltip]="getControlIconDescriptorTooltip(role, descriptor) | async"
  >
  </x-icon>
</ng-template>

<!-- Nothing Template -->
<ng-template #nothingRef></ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\x-form.component.scss

//
:host {
  //
  --regular-color: var(--x-color-dark);
  --valid-color: var(--x-color-success);
  --hint-color: var(--x-color-tertiary);
  --invalid-color: var(--x-color-danger);
  --active-color: var(--x-color-tertiary);
  --pending-color: var(--x-color-warning);
  --disabled-color: var(--x-color-medium);
  --accent-color: var(--x-color-secondary);
  --fill-color: var(--x-color-secondary-tint);
  --disabled-fill-color: var(--x-color-light-shade);

  //
  --hint-text-size: 7pt;
  --error-text-size: 7pt;

  //
  --form-fields-vertical-margin: 16px;
}

//
//#region Mixins ...
//
@mixin formFieldRegularProps {
  //
  x-icon {
    --size: 20px;
  }

  //
  .x-field-label {
  }

  //
  .auto-size-flex {
    flex: 0 0 auto !important;
  }

  //
  .full-heigh {
    * {
      height: 100%;
    }
  }
}

@mixin formInputElementProps {
  //
  .mat-input-element {
    @include colorHandler(var(--regular-color), false);
  }

  //
  .x-form-input-element {
    width: 100%;
    border: unset;
    outline: unset;
    border-color: transparent;

    //
    &:focus {
      // outline: var(--active-color) auto 1px;
    }

    //
    @include colorHandler(var(--regular-color), false);
    @include backgroundColorHandler(transparent, false);
  }

  //
  .x-form-date-input-element,
  .x-form-input-element:not(.mat-input-element) {
    height: 100% !important;
  }
}

@mixin formFieldHintProps(
  $color: var(--hint-color),
  $size: var(--hint-text-size)
) {
  .x-form-control-hint {
    //
    color: $color;
    font-size: $size;
  }
}

@mixin formFieldErrorProps(
  $color: var(--invalid-color),
  $size: var(--error-text-size)
) {
  //
  //
  $duration: 250ms;
  $transform-show: scaleY(1);
  $transform-hide: scaleY(0);
  .x-form-error,
  .x-form-field-error {
    //
    color: $color;
    font-size: $size;

    //
    height: 0;

    //
    transform: $transform-hide;
    -ms-transform: $transform-hide;
    -webkit-transform: $transform-hide;
    //
    transition: transform;
    -webkit-transition: transform;
    //
    transition-duration: $duration;
    -webkit-transition-duration: $duration;

    //
    &.show {
      //
      height: auto !important;
      //
      transform: $transform-show;
      -ms-transform: $transform-show;
      -webkit-transform: $transform-show;
      //
      transition-duration: $duration;
      -webkit-transition-duration: $duration;
    }
  }
}

@mixin formFieldProps {
  //
  .x-form-field {
    //
    @include dateProps;
    @include sliderProps;
    @include radioButtonProps;
    @include selectOptionProps;
    @include toogleColorHandler;
    @include checkBoxColorHandler;
    @include formFieldRegularProps;
    @include formInputElementProps;
    @include formFieldAppearanceHandler;
    @include colorHandler(var(--regular-color), false);
    @include backgroundColorHandler(transparent !important);

    //
    &.x-mat-form-field {
      //
      width: 100%;

      //
      &.mat-focused {
        @include formFieldFocusHandler;
      }
    }

    //
    &.x-form-radio-field {
      //
      outline: unset;

      //
      &.x-focused {
        //
        @include formFieldFocusHandler;
        @include iconStatusColorHandler(var(--active-color));
      }
    }

    //
    &:not(.x-mat-form-field):not(.x-form-radio-field) {
      //
      outline: unset;

      //
      &.x-focused {
        //
        @include formFieldFocusHandler;
        border-color: var(--active-color);
      }
    }
  }
}

@mixin autoCompleteProps {
  /* Auto Complete Styling */
  //
  $duration: 250ms;
  $transform-show: scaleY(1);
  $transform-hide: scaleY(0);

  .x-auto-complete-wrapper {
    position: unset;
    display: unset;
  }

  .x-auto-complete {
    //
    position: content;

    //
    &.x-auto-complete-margin {
      margin-top: 0;
    }

    //
    height: calc(50%);
    max-height: 300px;

    //
    left: 8px;
    right: 8px;
    overflow-y: scroll;

    //
    z-index: 113;

    //
    transform: $transform-hide;
    -ms-transform: $transform-hide;
    -webkit-transform: $transform-hide;

    //
    transition: transform;
    -webkit-transition: transform;

    //
    transition-duration: $duration;
    -webkit-transition-duration: $duration;

    //
    &.open {
      //
      transform: $transform-show;
      -ms-transform: $transform-show;
      -webkit-transform: $transform-show;

      //
      transition-duration: $duration;
      -webkit-transition-duration: $duration;
    }
  }
  /* END Auto Complete Styling */
}

@mixin selectOptionProps {
  //
  .x-select-option {
    width: 100%;
    margin-bottom: 4px;

    &:last-child {
      margin-bottom: 8px;
    }
  }
}

@mixin dateProps {
  //
  .mat-icon-button {
    display: table-cell;
    vertical-align: bottom;
  }
}

@mixin radioButtonProps($disabled: false) {
  //
  .x-radio-button {
    //
    @include radioColorHandler(var(--regular-color), true, false);

    //
    &.active {
      //
      @include radioColorHandler(var(--active-color) !important, true, true);
    }
  }

  &.x-form-radio-field {
    //
    outline: unset;

    //
    &.x-focused {
      //
      .x-field-label {
        color: var(--active-color) !important;
      }
      border-color: var(--active-color) !important;

      //
      @include iconStatusColorHandler(var(--active-color));
    }
  }
}

@mixin formFieldFocusHandler {
  //
  .mat-form-field-ripple {
    @include backgroundColorHandler(var(--active-color) !important, false);
  }

  //
  @include iconStatusColorHandler(var(--active-color));
  @include formFieldLabelColorHandler(var(--active-color));
  @include formFieldWrapperColorHandler(var(--active-color));
  @include matFormFieldOutlineColorHandler(var(--active-color) !important);
}

@mixin formFieldAppearanceHandler($disabled: false) {
  //
  // Label Colors ...
  @if $disabled {
    //
    @include formFieldLabelColorHandler(var(--disabled-color));

    //
    .x-form-field-appearance-fill:not(.x-mat-form-field) {
      border-color: var(--disabled-fill-color);
    }

    //
    .mat-select-value-text {
      color: var(--disabled-color) !important;
    }
  }

  //
  .mat-select-value-text {
    color: var(--regular-color);
  }

  //
  // None ...
  .x-form-field-appearance-none {
    //
    @if $disabled {
      @include formFieldLabelColorHandler(var(--disabled-color));
    } @else {
    }
  }

  //
  // Legacy ...
  // Standard ...
  .x-form-field-appearance-legacy,
  .x-form-field-appearance-standard {
    //
    @if $disabled {
      @include matFormFieldUnderlineColorHandler(
        var(--disabled-fill-color) !important,
        true
      );
    } @else {
      @include matFormFieldUnderlineColorHandler(var(--fill-color) !important);
    }

    //
    &:not(.x-mat-form-field) {
      @if $disabled {
      } @else {
        //
        padding: 8px;
        border-radius: 0;
        border-bottom-width: 1px;
        border-bottom-style: solid;
        border-color: var(--fill-color);
      }
    }
  }

  //
  // Outline ...
  .x-form-field-appearance-outline {
    //
    @if $disabled {
      @include matFormFieldOutlineColorHandler(var(--disabled-fill-color));
    } @else {
      @include formFieldLabelColorHandler(var(--regular-color));
      @include matFormFieldOutlineColorHandler(var(--fill-color));
    }

    //
    &:not(.x-mat-form-field) {
      @if $disabled {
        @include backgroundColorHandler(transaprent!important, false);
      } @else {
        //
        padding: 8px;
        border-radius: 3px;
        border-width: 1px;
        border-style: solid;
      }
    }
  }

  //
  // Fill ...
  .x-form-field-appearance-fill {
    //
    &.x-mat-form-field:not(.x-form-field-disabled) {
      //
      @if $disabled {
        //
        .mat-form-field-flex {
          @include colorHandler(var(--disabled-fill-color) !important, false);
          @include backgroundColorHandler(
            var(--disabled-fill-color) !important,
            false
          );
        }

        //
        @include matFormFieldUnderlineColorHandler(var(--disabled-color));
      } @else {
        //
        @include backgroundColorHandler(var(--fill-color) !important, false);

        //
        .mat-form-field-flex {
          @include colorHandler(var(--regular-color), false);
          @include backgroundColorHandler(var(--fill-color) !important, false);
        }

        //
        @include matFormFieldUnderlineColorHandler(var(--accent-color));
      }
    }

    //
    @if $disabled {
      //
      &.x-form-field-disabled {
        background-color: var(--disabled-fill-color) !important;
      }

      //
      .mat-form-field-flex {
        @include colorHandler(var(--disabled-color) !important, false);
        @include backgroundColorHandler(
          var(--disabled-fill-color) !important,
          false
        );
      }

      //
      .x-form-slider-control {
        //
        .mat-slider-thumb {
          background-color: var(--disabled-color) !important;
        }

        //
        .mat-slider-wrapper,
        .mat-slider-track-wrapper {
          background-color: var(--disabled-color) !important;
        }
      }

      //
      @include matFormFieldUnderlineColorHandler(var(--disabled-color));
    } @else {
      //
      .x-form-slider-control {
        .mat-slider-wrapper,
        .mat-slider-track-wrapper {
          background-color: var(--regular-color) !important;
        }
      }
    }

    //
    &:not(.x-mat-form-field) {
      @if $disabled {
        @include backgroundColorHandler(
          var(--disabled-fill-color) !important,
          false
        );
      } @else {
        //
        padding: 8px;
        border-radius: 3px;
        border-bottom-width: 1px;
        border-bottom-style: solid;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        border-color: var(--regular-color);

        //
        @include backgroundColorHandler(var(--fill-color) !important, false);
      }
    }
  }
}

@mixin formFieldStatusHandler {
  //
  // Global ...
  @include formFieldAppearanceHandler(false);

  //
  // Disabled ...
  &.x-form-field-wrapper-disabled {
    //
    * {
      cursor: default;
      pointer-events: none;
    }

    //
    x-icon.x-action-icon > * {
      color: var(--disabled-color) !important;
    }

    //
    .mat-form-field-disabled {
      color: var(--disabled-color) !important;
    }

    //
    .x-form-input-element {
      //
      opacity: 1 !important;
      border-color: var(--disabled-fill-color) !important;

      //
      @include colorHandler(var(--disabled-color) !important, false);
    }

    //
    .x-form-control {
      opacity: 1 !important;
      @include colorHandler(var(--disabled-color) !important, false);
    }

    //
    .mat-slide-toggle-bar {
      background-color: var(--disabled-fill-color) !important;
    }

    //
    @include sliderProps(true);
    @include formFieldAppearanceHandler(true);
    @include formFieldHintProps(var(--disabled-color));
    @include formFieldErrorProps(var(--disabled-color));
    @include checkBoxColorHandler(--disabled-color, true);
    @include iconStatusColorHandler(var(--disabled-color), true);
    @include formFieldWrapperColorHandler(var(--disabled-color) !important);
    @include radioColorHandler(var(--disabled-color) !important, true, true);
  }

  //
  // Valid ...
  &.x-form-field-wrapper-valid {
    //
    @include iconStatusColorHandler(var(--valid-color), true);

    //
    &.apply-status {
      @include formFieldWrapperColorHandler(var(--valid-color));
    }
  }

  //
  // Invalid ...
  &.x-form-field-wrapper-invalid {
    //
    @include iconStatusColorHandler(var(--invalid-color), true);

    //
    &.apply-status {
      @include formFieldWrapperColorHandler(var(--invalid-color));
    }
  }

  //
  // Pending ...
  &.x-form-field-wrapper-pending {
    //
    @include iconStatusColorHandler(var(--pending-color), true);

    //
    &.apply-status {
      @include formFieldWrapperColorHandler(var(--pending-color));
    }
  }
}

@mixin colorHandler($color: var(--regular-color), $applyToChilds: true) {
  color: $color;

  //
  @if $applyToChilds {
    * {
      :not(x-icon):not(x-icon > *) {
        color: $color;
      }
    }
  }

  //
  @include matFormFieldOutlineColorHandler($color);
}

@mixin backgroundColorHandler($color: transaprent, $applyToChilds: true) {
  //
  background-color: $color;

  //
  @if $applyToChilds {
    *:not(.x-field-label):not(.x-field-label
        *):not(.mat-form-field-ripple):not(.mat-slide-toggle-thumb):not(.mat-slide-toggle-bar):not(.mat-checkbox-frame):not(.mat-radio-inner-circle):not(.mat-form-field-underline):not(.mat-slide):not(.mat-slider
        *):not(.x-markdown *):not(.x-color *):not(.x-map
        *):not(.x-selected-color):not(.x-color-palette):not(.x-color-alpha) {
      background-color: $color;
    }
  }
}

@mixin formFieldWrapperColorHandler($color: var(--regular-color)) {
  //
  .x-form-input-element {
    // outline: $color auto 1px;
  }

  //
  .x-form-field:not(.x-mat-form-field) {
    border-color: $color;
  }

  //
  @include sliderProps;
  @include toogleColorHandler($color);
  @include checkBoxColorHandler($color);
  @include formFieldLabelColorHandler($color);
  @include matFormFieldOutlineColorHandler($color);
  @include iconStatusColorHandler($color !important);
  @include matFormFieldUnderlineColorHandler($color !important);
}

@mixin radioColorHandler(
  $color: var(--regular-color),
  $applyOnOuterCircle: false,
  $applyOnInnerCircle: false
) {
  //
  .mat-radio-label {
    //
    color: $color;

    //
    @if $applyOnOuterCircle {
      .mat-radio-outer-circle {
        border-color: $color;
      }
    }

    //
    @if $applyOnInnerCircle {
      .mat-radio-inner-circle {
        background-color: $color;
      }
    }
  }
}

@mixin checkBoxColorHandler($color: var(--regular-color), $disabled: false) {
  //
  .mat-checkbox-frame {
    @if $disabled {
      border-color: var(--disabled-color) !important;
    } @else {
      border-color: var(--regular-color);
    }
  }

  //
  .mat-checkbox-checked {
    .mat-checkbox-frame,
    .mat-checkbox-background {
      background-color: var(--active-color) !important;
    }
  }

  //
  .mat-checkbox-label {
    .x-field-label {
      width: 100%;
    }
  }

  //
  .x-form-control {
    &.has-icon {
      .mat-checkbox-layout {
        margin: {
          left: 4px;
          right: 4px;
        }
      }
    }
  }
}

@mixin toogleColorHandler($color: var(--regular-color)) {
  //
  :not(.x-form-field-disabled) {
    .mat-slide-toggle-bar {
      background-color: var(--active-color) !important;
    }
  }

  //
  .x-form-field-disabled {
    .mat-slide-toggle-bar {
      background-color: var(--disabled-color) !important;
    }
    .mat-slide-toggle-thumb {
      background-color: var(--disabled-fill-color) !important;
    }
  }

  //
  .mat-slide-toggle-thumb {
    background-color: $color !important;
  }

  //
  .x-form-control {
    &.has-icon {
      .mat-slide-toggle-label {
        margin: {
          left: 4px;
          right: 4px;
        }
      }
    }
  }
}

@mixin sliderProps($disabled: false) {
  //
  .x-form-slider-control {
    //
    @if $disabled {
      //
      cursor: default;
      pointer-events: none;
    }

    //
    &:not(.vertical) {
      width: 100%;
    }

    //
    &.vertical {
      display: block;
      margin: {
        left: auto;
        right: auto;
      }
    }

    //
    .mat-slider-wrapper,
    .mat-slider-track-wrapper {
      //
      @if $disabled {
        background-color: var(--disabled-fill-color) !important;
      } @else {
        background-color: var(--fill-color);
      }
    }

    //
    .mat-slider-thumb,
    .mat-slider-thumb-label,
    .mat-slider-thumb-label-text {
      //
      @if $disabled {
        color: var(--disabled-color) !important;
        background-color: var(--disabled-fill-color) !important;
      } @else {
        color: var(--accent-color);
        background-color: var(--active-color);
      }
    }

    //
    .mat-slider-track-fill {
      //
      @if $disabled {
        background-color: var(--disabled-color) !important;
      } @else {
        background-color: var(--active-color);
      }
    }

    //
    .mat-slider-vertical {
    }
  }
}

@mixin formFieldLabelColorHandler($color: var(--regular-color)) {
  //
  .mat-form-field-label {
    color: $color;
  }

  //
  .x-field-label {
    color: $color;
  }

  //
  .mat-select-placeholder {
    color: $color;
  }
}

@mixin iconStatusColorHandler($color: var(--regular-color), $important: false) {
  //
  x-icon.x-status-handler > * {
    //
    @if $important {
      color: $color !important;
    } @else {
      color: $color;
    }
  }
}

@mixin matFormFieldUnderlineColorHandler(
  $color: var(--fill-color),
  $clearBackground: false
) {
  //
  .mat-form-field-underline {
    //
    border-color: $color;
    background-color: $color;

    //
    &::before {
      background-color: $color;
    }

    //
    @if $clearBackground {
      background-image: none;
    }
  }

  //
  .mat-select-arrow {
    color: $color;
  }
}

@mixin matFormFieldOutlineColorHandler($color: var(--regular-color)) {
  //
  color: $color;

  //
  .mat-form-field-outline-start,
  .mat-form-field-outline-end,
  .mat-form-field-outline-gap {
    border-width: 1px !important;
  }

  //
  .mat-form-field-outline,
  .mat-form-field-outline-thick {
    color: $color;
    border-width: 1px !important;
  }
}
//#endregion

//
::ng-deep.cdk-overlay-pane {
  //
  transform: translateY(0px) !important;

  //
  .mat-datepicker-content-container {
    height: auto !important;
  }
}

//
::ng-deep.x-form-select-control {
  //
  min-width: 100% !important;

  //
  &:not(.x-form-select-control-panel) {
    max-width: 100% !important;
  }

  //
  &.x-form-select-control-panel {
  }

  // //
  // .mat-select-value-text {
  //   color: var(--regular-color) !important;
  // }

  //
  .x-select-option {
    //
    color: var(--regular-color) !important;
    background-color: var(--fill-color) !important;

    //
    &.mat-active {
      color: var(--active-color) !important;
    }

    //
    &.mat-selected {
      //
      color: var(--active-color) !important;

      //
      .mat-pseudo-checkbox-checked {
        background: var(--active-color) !important;
      }

      //
      .mat-pseudo-checkbox::after {
        color: var(--accent-color) !important;
      }
    }

    //
    &:hover {
      $level: 0.5;
      filter: brightness($level);
      -webkit-filter: brightness($level);
      color: var(--active-color) !important;
    }
  }
}

//
::ng-deep.x-form-control-date-picker {
  //
  &.mat-calendar {
    //
    color: var(--regular-color) !important;
    background-color: var(--fill-color) !important;

    //
    .mat-calendar-arrow {
      border-top-color: unset;
    }

    //
    .mat-calendar-body-today {
      color: var(--active-color);
      border-color: var(--acttive-color);
    }

    //
    .mat-calendar-table-header {
      color: var(--accent-color) !important;
    }

    //
    .mat-calendar-body-selected {
      //
      color: var(--accent-color);
      background-color: var(--active-color);
      border-color: var(--accent-color);
    }

    //
    .mat-calendar-next-button,
    .mat-calendar-previous-button,
    .mat-calendar-period-button {
      color: var(--active-color) !important;
    }

    //
    .mat-calendar-body-label {
      color: var(--active-color) !important;
    }

    //
    .mat-calendar-body-cell-content {
      color: var(--regular-color) !important;
    }

    //
    .mat-calendar-body-disabled {
      color: var(--disabled-color) !important;
      filter: contrast(0.5) !important;
    }
  }
}

//
:host ::ng-deep .x-form {
  //
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;

  //
  .x-form-field-wrapper {
    //
    width: 100%;
    margin-bottom: var(--form-fields-vertical-margin);

    //
    .mat-form-field-wrapper {
      padding-bottom: 0;
    }

    //
    .mat-form-field-underline {
      position: initial;
    }

    //
    .ng-invalid,
    .mat-form-field-invalid {
      //
      *:not(.x-field-label):not(ion-icon):not(.mat-form-field-outline):not(.mat-form-field-outline-thick):not(.mat-form-field-outline-end):not(.mat-form-field-outline-gap):not(.mat-form-field-outline-start) {
        color: var(--regular-color);
      }

      //
      &.apply-status {
        * {
          color: var(--invalid-color) !important;
        }
      }

      *:not(.x-field-label) {
        background-color: transparent;
      }
    }

    //
    &.readonly {
      //
      .x-field-label {
        color: var(--disabled-color) !important;
      }

      //
      .x-form-control {
        color: var(--disabled-color) !important;
      }
    }

    //
    @include formFieldProps;
    @include autoCompleteProps;
    @include formFieldStatusHandler;
    @include formFieldAppearanceHandler(false);
    @include formFieldHintProps(var(--hint-color), var(--hint-text-size));
    @include formFieldErrorProps(var(--invalid-color), var(--error-text-size));
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\x-form.component.ts

import {
  FormGroup,
  FormControl,
  AbstractControl,
  ValidationErrors,
} from '@angular/forms';
import {
  defaultFormControlClearIcon,
  defaultFormControlSelectIcon,
} from './x-form.tools';
import {
  keys,
  sort,
  nameof,
  XColor,
  isDate,
  assign,
  entries,
  XUiEvent,
  hasChild,
  isFunction,
  parseXColor,
  momentToDate,
  XResourceIDs,
  XBorderStyle,
  XExceptionIDs,
  XDialogResult,
  XOneOrManyType,
  isNullOrUndefined,
  isNullOrEmptyString,
  XColorWithBrightness,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XFormCssVar,
  XFormStatus,
  XFormUpdateOn,
  XFormBaseAction,
  XFormControlType,
  XFormActionModel,
  XControlIconRole,
  XFormErrorHandler,
  XFormControlConfig,
  XFormControlAction,
  XFormIconDescriptor,
  XControlIconPosition,
  XFormStatusIdentifier,
  XFormMapControlConfig,
  XControlIconDescriptor,
  XFormControlAppearance,
  XFormInputControlConfig,
  XFormRadioControlConfig,
  XFormContainerComponent,
  XFormSelectControlConfig,
  XFormSliderControlConfig,
  XFormSelectControlOption,
  XFormControlLabelPosition,
  XControlIconRoleIdentifier,
  XFormMarkdownControlConfig,
  XFormControlNameIdentifier,
  XFormMapControlPresentType,
  XFormCheckBoxControlConfig,
  XFormSelectorControlConfig,
  XBaseFormControlActionModel,
  XFormDatePickerControlConfig,
  XFormColorPickerControlConfig,
  XFormControlAutoCompleteConfig,
  XFormAvatarUploadControlConfig,
  XFormControlAppearanceDescriptor,
  XFormControlValueChangeEventModel,
  XFormControlStatusChangeEventModel,
  XFormColorPickerControlPresentType,
  XFormDatePickerControlPickerPosition,
} from './x-form.typings';
import {
  XMapAction,
  XMapLocation,
  XMapActionModel,
  XMapItemPosition,
  defaultMarkerStyle,
  defaultMapSearchPlaceProvider,
} from '../x-map/x-map.typings';
import { isMoment } from 'moment';
import {
  XMarkdownMode,
  XMarkdownActionModel,
} from '../x-markdown/x-markdown.typings';
import {
  XSearchBarType,
  XSearchBarActionModel,
  XSearchBarEnterKeyHint,
} from '../x-search-bar/x-search-bar.typings';
import {
  XColorPickerActionModel,
  XColorPickerOutputFormat,
} from '../x-color-picker/x-color-picker.typings';
import {
  XAvatarUploadActionModel,
  defaultAvatarUploadActionButtons,
} from '../x-avatar-upload/x-avatar-upload.typings';
import { map, concatMap, tap } from 'rxjs/operators';
import { XIconNames } from '../x-icon/x-icon.typings';
import { toLongLat, fromLongLat } from '../x-map/x-map.tools';
import { XStringInputFormatType } from 'x-framework-services';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { MatFormFieldAppearance } from '@angular/material/form-field';
import { of, Subject, Subscription, Observable, forkJoin } from 'rxjs';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
import { XSlotName, XSlotLayout } from '../x-slotter/x-slotter.typings';
import { XMapDialogConfig } from '../x-map/x-map-dialog/x-map-dialog.typings';
import { XFileDropAreaActionModel } from '../x-file-drop-area/x-file-drop-area.typings';
import { XColorPickerDialogConfig } from '../x-color-picker/x-color-picker-dialog/x-color-picker-dialog.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-form',
  templateUrl: './x-form.component.html',
  styleUrls: ['./x-form.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XFormComponent extends XFormContainerComponent {
  //
  //#region Props ...
  //
  private VALUE: any;
  private FORM: FormGroup;
  private FORM_CONFIG: any;
  private ERRORS: ValidationErrors;
  private actionProviders: any = {};
  private STATUS: XFormStatusIdentifier;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly SlotLayouts = Object.assign({}, XSlotLayout);
  readonly FormStatuses = Object.assign({}, XFormStatus);
  readonly ControlTypes = Object.assign({}, XFormControlType);
  readonly ControlIconRoles = Object.assign({}, XControlIconRole);
  readonly ControlIconPositions = Object.assign({}, XControlIconPosition);
  readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);

  //
  currentInputFocus: string;

  //
  get value() {
    return this.VALUE;
  }

  get status() {
    return this.STATUS;
  }

  get errors() {
    return this.ERRORS;
  }

  get isStatusValid() {
    return this.status === XFormStatus.Valid;
  }

  get form() {
    return this.FORM;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.applyColors();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    // Form Config ...
    const isFormConfigChanged = changeKeys.includes(
      nameof<XFormComponent>('formConfig')
    );
    if (isFormConfigChanged) {
      await this.prepareFormGroup();
    }

    //
    // Action Provider ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XFormComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      await this.registerActionProvider();
    }

    //
    //#region UI Disabled or Loading ...
    const isUiDisabledChanged = changeKeys.includes(
      nameof<XFormComponent>('uiDisabled')
    );
    if (isUiDisabledChanged) {
      await this.handleUiDisabledChanged();
    }
    //#endregion

    //
    //#region Color Change Detecotrs ...
    //
    const isRegularColorChanged = changeKeys.includes(
      nameof<XFormComponent>('regularColor')
    );
    if (isRegularColorChanged) {
      await this.applyRegularColor();
    }

    //
    const isAccentColorChanged = changeKeys.includes(
      nameof<XFormComponent>('accentColor')
    );
    if (isAccentColorChanged) {
      await this.applyAccentColor();
    }

    //
    const isHintColorChanged = changeKeys.includes(
      nameof<XFormComponent>('hintColor')
    );
    if (isHintColorChanged) {
      await this.applyHintColor();
    }

    //
    const isPendingColorChanged = changeKeys.includes(
      nameof<XFormComponent>('pendingColor')
    );
    if (isPendingColorChanged) {
      await this.applyPendingColor();
    }

    //
    const isActiveColorChanged = changeKeys.includes(
      nameof<XFormComponent>('activeColor')
    );
    if (isActiveColorChanged) {
      await this.applyActiveColor();
    }

    //
    const isValidColorChanged = changeKeys.includes(
      nameof<XFormComponent>('validColor')
    );
    if (isValidColorChanged) {
      await this.applyValidColor();
    }

    //
    const isInvalidColorChanged = changeKeys.includes(
      nameof<XFormComponent>('invalidColor')
    );
    if (isInvalidColorChanged) {
      await this.applyInvalidColor();
    }

    //
    const isFillColorChanged = changeKeys.includes(
      nameof<XFormComponent>('fillColor')
    );
    if (isFillColorChanged) {
      await this.applyFillColor();
    }

    //
    const isDisabledColorChanged = changeKeys.includes(
      nameof<XFormComponent>('disabledColor')
    );
    if (isDisabledColorChanged) {
      await this.applyDisabledColor();
    }

    //
    const isDisabledFillColorChanged = changeKeys.includes(
      nameof<XFormComponent>('disabledFillColor')
    );
    if (isDisabledFillColorChanged) {
      await this.applyDisabledFillColor();
    }
    //#endregion
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    this.managerService.dispatchResizeEvent();
    setTimeout(() => this.managerService.dispatchResizeEvent(), 500);
    this.detectChanges();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //
  //#region Form Providers ...
  //
  //#region Form Config ...
  /**
   * prepare form config object
   */
  getFormConfig() {
    return this.getValue(this.formConfig);
  }
  //#endregion

  //
  //#region Form Model ...
  /**
   * get form name ...
   */
  getFormName() {
    return this.getFormConfig().pipe(
      concatMap((formConfig) => this.getValue(formConfig.name))
    );
  }
  //#endregion

  //
  //#region Css Class Lists...
  /**
   * retrieve form cssClasses ...
   */
  getFormCssClass() {
    return this.getArrayValue(this.cssClass).pipe(
      concatMap((cssClasses) =>
        this.getFormName().pipe(
          map((formName) => {
            return {
              name: formName,
              cssClasses,
            };
          })
        )
      ),
      map((descriptor) => {
        return descriptor.cssClasses.concat(
          'x-form',
          `x-status-${this.STATUS}`,
          `x-form-${descriptor.name}`
        );
      })
    );
  }

  /**
   * retrieve form control wrapper classes list ...
   *
   * @param control form control config ...
   */
  getFormControlWrapperClasses(control: XFormControlConfig) {
    return this.getControlInfo(control).pipe(
      map((cInfo) => {
        //
        const fControl = this.getControl(control.propName);
        const isFocused =
          this.getControlInputElementId(control) === this.currentInputFocus;

        //
        return [
          'x-form-field-wrapper',
          `x-form-${cInfo.type}-field-wrapper`,
          `x-form-field-${control.index.toString()}-wrapper`,
          `x-form-field-${control.propName.toString()}-wrapper`,
          `x-form-field-appearance-${cInfo.appearance
            .toString()
            .toLowerCase()}-wrapper`,
          //
          // Focused ...
          ...(isFocused ? ['x-focused'] : []),
          //
          // Disabled ...
          ...(cInfo.disabled || fControl.disabled
            ? ['x-form-field-wrapper-disabled']
            : []),
          //
          // Valid ...
          ...(fControl.valid ? ['x-form-field-wrapper-valid'] : []),
          //
          // Invalid ...
          ...(fControl.invalid ? ['x-form-field-wrapper-invalid'] : []),
          //
          // Pending ...
          ...(fControl.pending ? ['x-form-field-wrapper-pending'] : []),
        ];
      })
    );
  }

  /**
   * retrieve form errors classes ...
   */
  getFormErrorsClasses() {
    return this.getFormName().pipe(
      map((formName) => {
        return ['x-form-errors', `x-form-${formName}-errors`];
      })
    );
  }

  /**
   * retrieve form specific error's class lists ...
   *
   * @param handler form error handler ...
   */
  getFormErrorClasses(handler: XFormErrorHandler) {
    return this.getFormName().pipe(
      concatMap((formName) =>
        this.getErrorHandlerErrorName(handler).pipe(
          map((errorName) => {
            return {
              formName,
              errorName,
            };
          })
        )
      ),
      map((descriptor) => {
        return [
          'x-form-error',
          `x-form-${descriptor.formName}-error`,
          `x-form-${descriptor.errorName}-error`,
          `x-form-${descriptor.formName}-${descriptor.errorName}-error`,
        ];
      })
    );
  }

  /**
   * retrieve form card footer css classes ...
   */
  getFormCardFooterCssClass() {
    return this.getFormName().pipe(
      map((formName) => {
        return ['x-form-footer', `x-form-${formName}-footer`];
      })
    );
  }

  /**
   * retrieve form card actions css classes ...
   */
  getFormCardActionsCssClass() {
    return this.getFormName().pipe(
      map((formName) => {
        return ['x-form-actions', `x-form-${formName}-actions`];
      })
    );
  }

  /**
   * retrieve form field classes list ...
   *
   * @param control form control config ...
   */
  getFormFieldClasses(control: XFormControlConfig) {
    return this.getControlInfo(control).pipe(
      map((cInfo) => {
        //
        const fControl = this.getControl(control.propName);
        const isFocused =
          this.getControlInputElementId(control) === this.currentInputFocus;

        //
        return [
          'x-form-field',
          `x-form-${cInfo.type}-field`,
          `x-form-field-${control.index.toString()}`,
          `x-form-field-${control.propName.toString()}`,
          `x-form-field-appearance-${cInfo.appearance
            .toString()
            .toLowerCase()}`,
          //
          // Focused ...
          ...(isFocused ? ['x-focused'] : []),
          //
          // Disabled ...
          ...(cInfo.disabled || fControl.disabled
            ? ['x-form-field-disabled']
            : []),
          //
          // Valid ...
          ...(fControl.valid ? ['x-form-field-valid'] : []),
          //
          // Invalid ...
          ...(fControl.invalid ? ['x-form-field-invalid'] : []),
          //
          // Pending ...
          ...(fControl.pending ? ['x-form-field-pending'] : []),
        ];
      })
    );
  }

  /**
   * retrieve form control classes list ...
   *
   * @param control form control config ...
   */
  getFormControlClasses(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      map((type) => {
        return [
          'x-form-control',
          `x-form-${type}-control`,
          `x-form-control-${control.index.toString()}`,
          `x-form-control-${control.propName.toString()}`,
        ];
      })
    );
  }

  /**
   * retrieve focusHandler of a form control ...
   *
   * @param control form control config ...
   */
  getControlFocusHandler(control: XFormControlConfig) {
    return this.getValue(control).pipe(map((config) => config.focusHandler));
  }

  /**
   * retriev a form control label classes ...
   */
  getFormControlFieldLabelClasses(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      map((type) => {
        return [
          'x-form-field-label',
          `x-form-${type}-field-label`,
          `x-form-field-label-${control.index.toString()}`,
          `x-form-field-label-${control.propName.toString()}`,
        ];
      })
    );
  }

  /**
   * retrieve form control hint class list
   */
  getFormControlHintClasses(control: XFormControlConfig) {
    return this.getControlInfo(control).pipe(
      map((cInfo) => {
        return [
          'x-form-control-hint',
          `x-form-${cInfo.type}-control-hint`,
          `x-form-control-${control.index.toString()}-hint`,
          `x-form-control-${control.propName.toString()}-hint`,
        ];
      })
    );
  }
  //#endregion

  //
  //#region Form Model ...
  /**
   * retrieve for model which provide's initial input for each field.
   * if the form model is <T> this type is Partial<T> ...
   */
  getFormModel() {
    return this.getFormConfig().pipe(
      concatMap((formConfig) => this.getOptionalValue(formConfig.model))
    );
  }

  /**
   * determines the form has intial value provider or not ...
   */
  formHasModel() {
    return this.getFormModel().pipe(
      map((formModel) => !isNullOrUndefined(formModel))
    );
  }
  //#endregion

  //
  //#region Form Updte On ...
  /**
   * retrieve form update strategy identifier ...
   */
  getFormUpdateOn() {
    return this.getFormConfig().pipe(
      concatMap((formConfig) =>
        this.getOptionalValue(formConfig.updateOn).pipe(
          map((updateOn) => (updateOn ? updateOn : XFormUpdateOn.CHANGE))
        )
      )
    );
  }
  //#endregion

  //
  //#region Form Validators ...
  /**
   * retrieve form Validators object ...
   */
  getFormValidators() {
    return this.getFormConfig().pipe(
      concatMap((formConfig) => this.getOptionalValue(formConfig.validators))
    );
  }

  /**
   * determines form has Validators or not ...
   */
  formHasValidators() {
    return this.getFormValidators().pipe(
      map((formValidators) => !isNullOrUndefined(formValidators))
    );
  }
  //#endregion

  //
  //#region Form Controls ...
  /**
   * retrieve form Controls Configs ...
   */
  getFormControls() {
    return this.getFormConfig().pipe(
      concatMap((formConfig) =>
        this.getArrayValue(formConfig.controls).pipe(
          map((arrVal) => sort(arrVal, (v1, v2) => v1.index - v2.index))
        )
      )
    );
  }
  //#endregion

  //
  //#region Form Error Handlers ...
  /**
   * retrieve form Error Handlers Descriptor object ...
   */
  getFormErrorHandlers() {
    return this.getFormConfig().pipe(
      concatMap((formConfig) => this.getArrayValue(formConfig.errorHandlers))
    );
  }

  /**
   * determines form has Error Handlers or not ...
   */
  formHasErrorHandlers() {
    return this.hasChildValue(this.getFormErrorHandlers());
  }

  /**
   * determines for has specific error or not ...
   *
   * @param handler form error handler ...
   */
  formHasError(handler: XFormErrorHandler) {
    return this.getValue(handler.errorName).pipe(
      map((errorName) => {
        return (
          !isNullOrUndefined(this.ERRORS) &&
          keys(this.ERRORS).includes(errorName) &&
          !isNullOrUndefined(this.ERRORS[errorName])
        );
      })
    );
  }

  /**
   * determine form has specific error by name ...
   *
   * @param name error name ...
   */
  formHasErrorByName(name: string) {
    return (
      !isNullOrUndefined(this.ERRORS) &&
      keys(this.ERRORS).includes(name) &&
      !isNullOrUndefined(this.ERRORS[name])
    );
  }

  /**
   * retrieve specific error from form ...
   *
   * @param handler form error handler ...
   */
  getFormError(handler: XFormErrorHandler) {
    return this.getErrorHandlerErrorName(handler).pipe(
      concatMap((errorName) =>
        this.formHasError(handler).pipe(
          map((hasError) => {
            return {
              errorName,
              hasError,
            };
          })
        )
      ),
      map((descriptor) =>
        descriptor.hasError ? this.ERRORS[descriptor.errorName] : null
      )
    );
  }

  /**
   * retrieve specific form error by name ...
   *
   * @param name error name ...
   */
  getFormErrorByName(name: string) {
    return this.formHasErrorByName(name) ? this.ERRORS[name] : null;
  }
  //#endregion
  //#endregion

  //
  //#region Form Control Providers ...
  /**
   * retrieve specific control config ...
   *
   * @param name control name
   */
  getFormControl(name: XFormControlNameIdentifier) {
    return this.getFormControls().pipe(
      map((controls) => controls.find((c) => c.propName === name))
    );
  }

  //
  getControlInputElementId(control: XFormControlConfig) {
    return `x-form-${control.propName.toString()}-input-element`;
  }

  //
  getControlInputElementIdByName(name: XFormControlNameIdentifier) {
    return `x-form-${name.toString()}-input-element`;
  }

  //
  getControlInputElementClasses(control: XFormControlConfig) {
    return this.getControlInputElementCssClasses(control).pipe(
      concatMap((inpCss) =>
        this.getControlType(control).pipe(
          map((type) => {
            //
            return [
              'x-form-input-element',
              `x-form-${type}-input-element`,
              `x-form-${control.index.toString()}-input-element`,
              `x-form-${control.propName.toString()}-input-element`,
              //
              ...inpCss.map((i) => i.toString()),
            ];
          })
        )
      )
    );
  }

  //
  getControlInputElementCssClasses(control: XFormControlConfig) {
    return this.getCustomControlConfig(control).pipe(
      concatMap((config) => {
        return this.getArrayValue(
          !isNullOrUndefined(config) && !isNullOrUndefined(config.inputCssClass)
            ? config.inputCssClass
            : []
        );
      })
    );
  }

  //
  getControlDisableInput(control: XFormControlConfig) {
    return this.getCustomControlConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(
          !isNullOrUndefined(config) && !isNullOrUndefined(config.disableInput)
            ? config.disableInput
            : false
        );
      })
    );
  }

  //
  isInputControlReadOnly(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      concatMap((type) => {
        //
        if (type === XFormControlType.Text) {
          return this.getControlTypeConfig(control).pipe(
            concatMap((config) =>
              config && (config as XFormInputControlConfig)
                ? this.getOptionalValue(
                    (config as XFormInputControlConfig).readonly
                  )
                : of(false)
            )
          );
        } else {
          return this.getValue(false);
        }
      })
    );
  }

  //
  getControlDisableIcon(control: XFormControlConfig) {
    return this.getCustomControlConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(
          !isNullOrUndefined(config) && !isNullOrUndefined(config.disableIcon)
            ? config.disableIcon
            : false
        );
      })
    );
  }

  //
  //#region Control Type Providers ...
  //
  //#region Type Descriptor ...
  /**
   * retrieve a control type descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlTypeDescriptor(control: XFormControlConfig) {
    return this.getValue(control.type);
  }
  //#endregion

  //
  //#region Control Type ...
  /**
   * retrieve a control type ...
   *
   * @param control form control config object ...
   */
  getControlType(control: XFormControlConfig) {
    return this.getControlTypeDescriptor(control).pipe(
      concatMap((typeDesc) =>
        this.getValue(
          typeDesc && typeDesc.type ? typeDesc.type : XFormControlType.Hidden
        )
      )
    );
  }
  //#endregion

  //
  //#region Control Type Config ...
  /**
   * retrieve a control type specific config object ...
   *
   * @param control form control config object ...
   */
  getControlTypeConfig(control: XFormControlConfig) {
    return this.getControlTypeDescriptor(control).pipe(
      concatMap((typeDesc) => this.getOptionalValue(typeDesc.config))
    );
  }
  //#endregion

  //
  //#region Control Action Icon Configs ...
  /**
   * determines a custom control has clear icon or not ...
   */
  hasControlClearIcon(control: XFormControlConfig): Observable<boolean> {
    return this.getCustomControlConfig(control).pipe(
      map((config) => {
        return (
          !isNullOrUndefined(config) && !isNullOrUndefined(config.clearIcon)
        );
      })
    );
  }

  /**
   * retrieve custom control clear icon
   *
   * @param control form control config object ...
   */
  getControlClearIcon(
    control: XFormControlConfig
  ): Observable<XControlIconDescriptor> {
    return this.getCustomControlConfig(control).pipe(
      concatMap((config) => {
        return !isNullOrUndefined(config)
          ? this.getValue(config.clearIcon)
          : of(null);
      }),
      map((iconSelector) => {
        return !isNullOrUndefined(iconSelector)
          ? iconSelector
          : defaultFormControlClearIcon;
      })
    );
  }

  /**
   * determines a custom control has select icon or not ...
   */
  hasControlSelectIcon(control: XFormControlConfig): Observable<boolean> {
    return this.getCustomControlConfig(control).pipe(
      map((config) => {
        return (
          !isNullOrUndefined(config) && !isNullOrUndefined(config.selectIcon)
        );
      })
    );
  }

  /**
   * retrieve custom control select icon
   *
   * @param control form control config object ...
   */
  getControlSelectIcon(
    control: XFormControlConfig
  ): Observable<XControlIconDescriptor> {
    return this.getCustomControlConfig(control).pipe(
      concatMap((config) => {
        return !isNullOrUndefined(config)
          ? this.getValue(config.selectIcon)
          : of(null);
      }),
      map((iconSelector) => {
        return !isNullOrUndefined(iconSelector)
          ? iconSelector
          : defaultFormControlSelectIcon;
      })
    );
  }

  //
  //#region XForm Custom Control IconDescriptor Providers ...
  /**
   * retrieve Custom Control Action Icon's Name ...
   *
   * @param role Icon Action Descriptor...
   * @param descriptor Selector icon descriptor ...
   */
  getControlIconDescriptorName(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor
  ) {
    return this.getValue(descriptor.name).pipe(
      map((name) => {
        return !isNullOrUndefined(name) && !isNullOrEmptyString(name.toString())
          ? name
          : role === this.ControlIconRoles.Select
          ? XIconNames.touch
          : XIconNames.clear;
      })
    );
  }

  /**
   * retrieve Custom Control Action Icon's color ...
   *
   * @param role Icon Action Descriptor...
   * @param descriptor Selector icon descriptor ...
   */
  getControlIconDescriptorColor(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor
  ) {
    return this.getValue(descriptor.color).pipe(
      map((color) => {
        return !isNullOrEmptyString(color)
          ? color
          : role === this.ControlIconRoles.Select
          ? XColorWithBrightness.Success
          : XColorWithBrightness.Danger;
      })
    );
  }

  /**
   * retrieve Custom Control Action Icon's applyStateColor ...
   *
   * @param descriptor Selector icon descriptor ...
   */
  getControlIconDescriptorApplyStateColor(descriptor: XControlIconDescriptor) {
    return this.getOptionalValue(descriptor.applyStateColor);
  }

  /**
   * retrieve Custom Control Action Icon's tooltip ...
   *
   * @param role Icon Action Descriptor...
   * @param descriptor Selector icon descriptor ...
   */
  getControlIconDescriptorTooltip(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor
  ) {
    return this.getResourceValue(descriptor.tooltip).pipe(
      map((tooltip) => {
        return !isNullOrEmptyString(tooltip)
          ? tooltip
          : role === this.ControlIconRoles.Select
          ? this.resourceProvider(this.ResourceIDs.select)
          : this.resourceProvider(this.ResourceIDs.clear);
      })
    );
  }

  /**
   * retrieve Custom Control Action Icon's position ...
   *
   * @param descriptor Selector icon descriptor ...
   */
  getControlIconDescriptorPosition(descriptor: XControlIconDescriptor) {
    return this.getValue(descriptor.position).pipe(
      map((position) => {
        return !isNullOrEmptyString(position)
          ? position
          : XControlIconPosition.Suffix;
      })
    );
  }
  //#endregion
  //#endregion

  //
  //#region Control Type Configs
  //
  //#region Markdown Config ...
  /**
   * retrieve Markdown Control Config ...
   *
   * @param control form control config object ...
   */
  getMarkdownConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormMarkdownControlConfig)
    );
  }

  /**
   * determines control has Markdown Config or not ...
   *
   * @param control form control config object ...
   */
  hasMarkdownConfig(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * determines markdown Card is In Page or not ...
   *
   * @param control form control config object ...
   */
  isMarkdownInPage(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.isInPage);
      })
    );
  }

  /**
   * retrieve markdown has toolbar ...
   *
   * @param control form control config object ...
   */
  getMarkDownHasToolbar(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.hasToolbar);
      })
    );
  }

  /**
   * retrieve markdown wrap with card ...
   *
   * @param control form control config object ...
   */
  isMarkdownWrapWithCard(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.wrapWithCard);
      })
    );
  }

  /**
   * retrieve markdown card color ...
   *
   * @param control form control config object ...
   */
  getMarkdownCardColor(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getValue(
          config && config.cardColor ? config.cardColor : XColor.Light
        );
      })
    );
  }

  /**
   * retrieve markdown editorBoxColor value ...
   *
   * @param control form control config object ...
   */
  getMarkdownEditorBoxColor(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getValue(
          config && config.editorBoxColor ? config.editorBoxColor : XColor.Light
        );
      })
    );
  }

  /**
   * retrieve markdown previewBoxColor value ...
   *
   * @param control form control config object ...
   */
  getMarkdownPreviewBoxColor(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getValue(
          config && config.previewBoxColor
            ? config.previewBoxColor
            : XColorWithBrightness.MediumTint
        );
      })
    );
  }

  /**
   * determines markdown toolbar color is foreground color or not ...
   *
   * @param control form control config object ...
   */
  getMarkdownCardForegroundColor(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.cardForegroundColor);
      })
    );
  }

  /**
   * retrieve markdown mode ...
   *
   * @param control form control config object ...
   */
  getMarkdownMode(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => this.getValue(config.mode || null)),
      map((mode) => (mode ? mode : XMarkdownMode.PREVIEW))
    );
  }

  /**
   * retrieve markdown Toolbar Color ...
   *
   * @param control form control config object ...
   */
  getMarkDownToolbarColor(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getValue(
          config && config.toolbarColor ? config.toolbarColor : XColor.Light
        );
      })
    );
  }

  /**
   * retrieve markdown Card Title ...
   *
   * @param control form control config object ...
   */
  getMarkdownCardTitle(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.cardTitle);
      })
    );
  }

  /**
   * retrieve markdown action provider ...
   *
   * @param control form control config object ...
   */
  getMarkdownActionProvider(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      map((config) => {
        //
        if (!config.actionProvider) {
          //
          if (!this.actionProviders[control.propName]) {
            this.actionProviders[control.propName] =
              new Subject<XMarkdownActionModel>();
          }

          //
          return this.actionProviders[
            control.propName
          ] as Subject<XMarkdownActionModel>;
        } else {
          return config.actionProvider;
        }
      })
    );
  }

  /**
   * determines markdown toolbar color is foreground color or not ...
   *
   * @param control form control config object ...
   */
  getMarkDownToolbarForegroundColor(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.toolbarForegroundColor);
      })
    );
  }

  /**
   * retrieve markdown cssClass ...
   *
   * @param control form control config object ...
   */
  getMarkdownCssClass(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getArrayValue(config.cssClass);
      })
    );
  }

  /**
   * retrieve markdown content ...
   *
   * @param control form control config object ...
   */
  getMarkDownContent(control: XFormControlConfig) {
    return this.getValue(control).pipe(
      map((ctrl) => {
        //
        if (!this.hasControl(ctrl.propName)) {
          return '';
        }

        //
        const xFormControl = this.getControl(ctrl.propName);
        if (!xFormControl) {
          return '';
        }

        //
        return (xFormControl.value || '') as string;
      })
    );
  }

  /**
   * retrieve markdown Card SubTitle ...
   *
   * @param control form control config object ...
   */
  getMarkdownCardSubTitle(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getResourceValue(config.cardSubTitle);
      })
    );
  }

  /**
   * retrieve markdown toolbar css class ...
   *
   * @param control form control config object ...
   */
  getMarkdownToolbarCssClass(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getArrayValue(config.toolbarCssClass);
      })
    );
  }

  /**
   * retrieve markdown toolbar config ...
   *
   * @param control form control config object ...
   */
  getMarkdownToolbarConfig(control: XFormControlConfig) {
    return this.getMarkdownConfig(control).pipe(
      concatMap((config) => {
        return this.getOptionalValue(config.toolbarConfig);
      })
    );
  }
  //#endregion

  //
  //#region Select/Radio Config ...
  /**
   * retrieve select control config ...
   *
   * @param control form control config object ...
   */
  getSelectConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormSelectControlConfig)
    );
  }

  /**
   * determines select control has config or not ...
   *
   * @param control form control config object ...
   */
  hasSelectConfig(control: XFormControlConfig) {
    return this.getSelectConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve select control options ...
   *
   * @param control form control config object ...
   */
  getSelectOptions(control: XFormControlConfig) {
    return this.getSelectConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.options))
    );
  }

  /**
   * determines select control can select multiple or not ...
   *
   * @param control form control config object ...
   */
  getSelectMultiple(control: XFormControlConfig) {
    return this.getSelectConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.multiple))
    );
  }

  /**
   * retrieve Select Control Panel Calsses ...
   */
  getSelectPanelClasses(control: XFormControlConfig) {
    return this.getFormControlClasses(control).pipe(
      concatMap((classes) =>
        this.getSelectMultiple(control).pipe(
          map((isMultiple) => ({ classes, isMultiple }))
        )
      ),
      map((selectInfo) => {
        return [
          ...selectInfo.classes,
          'x-form-select-control-panel',
          ...(selectInfo.isMultiple ? ['multiple'] : []),
        ];
      })
    );
  }

  /**
   * retrieve select control option css classes ...
   *
   * @param control form control config object ...
   */
  getOptionClassName(option: XFormSelectControlOption) {
    return this.getValue(option).pipe(
      map((opt) => ['x-select-option', `x-select-${opt.value}-option`])
    );
  }

  //
  // Radio ...
  /**
   * retrieve radio control config ...
   *
   * @param control form control config object ...
   */
  getRadioConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormRadioControlConfig)
    );
  }

  /**
   * determines radio control has config or not ...
   *
   * @param control form control config object ...
   */
  hasRadioConfig(control: XFormControlConfig) {
    return this.getRadioConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve radio control options ...
   *
   * @param control form control config object ...
   */
  getRadioOptions(control: XFormControlConfig) {
    return this.getRadioConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.options))
    );
  }

  /**
   * retrieve a radio control options label position ...
   *
   * @param control form control config object ...
   */
  getRadioLabelPosition(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.labelPosition
            : XFormControlLabelPosition.After
        ).pipe(map((result) => result as 'before' | 'after'))
      )
    );
  }
  //#endregion

  //
  //#region CheckBox Config ...
  /**
   * retrieve checkbox control config ...
   *
   * @param control form control config object ...
   */
  getCheckboxConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormCheckBoxControlConfig)
    );
  }

  /**
   * determines checkbox control has config or not ...
   *
   * @param control form control config object ...
   */
  hasCheckboxConfig(control: XFormControlConfig) {
    return this.getCheckboxConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve a checkbox control label position ...
   *
   * @param control form control config object ...
   */
  getCheckboxLabelPosition(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.labelPosition
            : XFormControlLabelPosition.After
        ).pipe(map((result) => result as 'before' | 'after'))
      )
    );
  }
  //#endregion

  //
  //#region Date Picker Config ...
  /**
   * retrieve date control config ...
   *
   * @param control form control config object ...
   */
  getDateConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormDatePickerControlConfig)
    );
  }

  /**
   * determines date control has config or not ...
   *
   * @param control form control config object ...
   */
  hasDateConfig(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve date control start at ...
   *
   * @param control form control config object ...
   */
  getDateStartAt(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getValue(config.startAt))
    );
  }

  /**
   * retrieve date control picker position ...
   *
   * @param control form control config object ...
   */
  getDatePickerPosition(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getValue(config.datePickerPosition))
    );
  }

  /**
   * retrieve date control filter ...
   *
   * @param control form control config object ...
   */
  getDateFilter(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getValue(config.dateFilter))
    );
  }

  /**
   * retrieve date control input handler ...
   *
   * @param control form control config object ...
   */
  getDateInputHandler(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getValue(config.dateInput))
    );
  }

  /**
   * retrieve date control change handler ...
   *
   * @param control form control config object ...
   */
  getDateChangeHandler(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getValue(config.dateChange))
    );
  }

  /**
   * determines date control input field disabled or not ...
   *
   * @param control form control config object ...
   */
  isDatePickerInputDisabled(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.disableInput))
    );
  }

  /**
   * determines date control input field disabled or not ...
   *
   * @param control form control config object ...
   */
  isDatePickerToggleDisabled(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.disablePicker))
    );
  }

  /**
   * retriece date control classes ...
   *
   * @param control form control config object ...
   */
  getDatePickerClasses(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      map((type) => {
        return [
          'x-date-picker',
          `x-form-control-${type}-picker`,
          `x-form-control-${control.propName.toString()}-date-picker`,
        ];
      })
    );
  }

  /**
   * retriece date control toggle icon name ...
   *
   * @param control form control config object ...
   */
  getDatePickerIcon(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.toggleIcon)),
      map((icon) => (icon ? icon : XIconNames.calendar))
    );
  }

  /**
   * retriece date control toggle icon color ...
   *
   * @param control form control config object ...
   */
  getDatePickerIconColor(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.iconColor)),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * retriece date control toggle icon tooltip ...
   *
   * @param control form control config object ...
   */
  getDatePickerIconTooltip(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.iconTooltip))
    );
  }

  /**
   * retriece date control toggle apply color state ...
   *
   * @param control form control config object ...
   */
  getDatePickerIconApplyStateColor(control: XFormControlConfig) {
    return this.getDateConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.applyStateColor))
    );
  }

  /**
   * determines date control icon position is suffix or not ...
   *
   * @param control form control config object ...
   */
  isDatePickerHasSuffixIcon(control: XFormControlConfig) {
    return this.getDatePickerPosition(control).pipe(
      map(
        (position) => position === XFormDatePickerControlPickerPosition.Suffix
      )
    );
  }

  /**
   * determines date control icon position is prefix or not ...
   *
   * @param control form control config object ...
   */
  isDatePickerHasPrefixIcon(control: XFormControlConfig) {
    return this.getDatePickerPosition(control).pipe(
      map(
        (position) => position === XFormDatePickerControlPickerPosition.Prefix
      )
    );
  }
  //#endregion

  //
  //#region Slider Config ...
  /**
   * retrieve slider control config ...
   *
   * @param control form control config object ...
   */
  getSliderConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormSliderControlConfig)
    );
  }

  /**
   * determines slider control has config or not ...
   *
   * @param control form control config object ...
   */
  hasSliderConfig(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve slider control min value ...
   *
   * @param control form control config object ...
   */
  getSliderMin(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.min)),
      map((min) => (min ? min : 0))
    );
  }

  /**
   * retrieve slider control max value ...
   *
   * @param control form control config object ...
   */
  getSliderMax(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.max)),
      map((max) => (max ? max : 99))
    );
  }

  /**
   * retrieve slider control step value ...
   *
   * @param control form control config object ...
   */
  getSliderStep(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.step)),
      map((step) => (step ? step : 1))
    );
  }

  /**
   * retrieve slider control invert value ...
   *
   * @param control form control config object ...
   */
  getSliderInvert(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.invert))
    );
  }

  /**
   * determines slider control is vertical or not ...
   *
   * @param control form control config object ...
   */
  isSliderVertical(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.vertical))
    );
  }

  /**
   * determines slider control show thumb label or not ...
   *
   * @param control form control config object ...
   */
  showSliderThumbLabel(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.thumbLabel))
    );
  }

  /**
   * retrieve slider formatter function ...
   */
  getSliderFormatter(control: XFormControlConfig) {
    return this.getSliderConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.formatter))
    );
  }
  //#endregion

  //
  //#region Toggle Config ...
  /**
   * retrieve a checkbox control label position ...
   *
   * @param control form control config object ...
   */
  getToggleLabelPosition(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.labelPosition
            : XFormControlLabelPosition.After
        ).pipe(map((result) => result as 'before' | 'after'))
      )
    );
  }
  //#endregion

  //
  //#region Map Config ...
  /**
   * retrieve map control config ...
   *
   * @param control form control config object ...
   */
  getMapConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormMapControlConfig)
    );
  }

  /**
   * determines map control has config or not ...
   *
   * @param control form control config object ...
   */
  hasMapConfig(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve map component's selected point value ...
   *
   * @param control form control config object ...
   */
  getMapSelectedPoint(control: XFormControlConfig): Observable<XMapLocation> {
    //
    const fControl = this.getControl(control.propName);
    if (!fControl || !fControl.value) {
      return of(null);
    }

    //
    const fValue = fControl.value as string;
    if (isNullOrEmptyString(fValue)) {
      return of(null);
    }

    //
    const result = fromLongLat(fValue);
    this.getMapActionProvider(control).pipe(
      tap((actionProvider) => {
        //
        if (actionProvider) {
          //
          actionProvider.next({
            action: XMapAction.SetCenter,
            payload: result,
          });
        }
      })
    );

    //
    return of(result);
  }

  /**
   * retrieve map control zoom value ...
   *
   * @param control form control config object ...
   */
  getMapLatitude(control: XFormControlConfig) {
    return this.getMapSelectedPoint(control).pipe(
      map((point) => (point && point.latitude ? +point.latitude : 0))
    );
  }

  /**
   * retrieve map control zoom value ...
   *
   * @param control form control config object ...
   */
  getMapLongitude(control: XFormControlConfig) {
    return this.getMapSelectedPoint(control).pipe(
      map((point) => (point && point.longitude ? +point.longitude : 0))
    );
  }

  /**
   * retrieve map control zoom value ...
   *
   * @param control form control config object ...
   */
  getMapZoom(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => (config.zoom ? this.getValue(config.zoom) : of(0)))
    );
  }

  /**
   * retrieve map control markerStyle value ...
   *
   * @param control form control config object ...
   */
  getMapMarkerStyle(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.markerStyle)),
      map((markerStyle) => (markerStyle ? markerStyle : defaultMarkerStyle))
    );
  }

  /**
   * retrieve map control cssClass value ...
   *
   * @param control form control config object ...
   */
  getMapCssClass(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.cssClass))
    );
  }

  /**
   * determines map control canSelect or not ...
   *
   * @param control form control config object ...
   */
  mapCanSelect(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.canSelect))
    );
  }

  /**
   * determines map control showProgressBar or not ...
   *
   * @param control form control config object ...
   */
  mapShowProgressBar(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showProgressBar)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  /**
   * retrieve map control progressBarColor value ...
   *
   * @param control form control config object ...
   */
  getMapProgressBarColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.progressBarColor)),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * retrieve map control progressBarPosition value ...
   *
   * @param control form control config object ...
   */
  getMapProgressBarPosition(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.progressBarPosition)),
      map((position) =>
        isNullOrEmptyString(position) ? XMapItemPosition.TOP : position
      )
    );
  }

  //
  //#region Map Search Bar ...
  /**
   * determines map control showSearchBar or not ...
   *
   * @param control form control config object ...
   */
  mapShowSearchBar(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showSearchBar)),
      map((result) => (isNullOrUndefined(result) ? true : result))
    );
  }

  /**
   * retrieve map control searchBarType value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarType(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarType)),
      map((result) =>
        isNullOrEmptyString(result) ? XSearchBarType.Search : result
      )
    );
  }

  /**
   * retrieve map control searchBarValue value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarValue(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getValue(config.searchBarValue))
    );
  }

  /**
   * retrieve map control searchBarDebounce value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarDebounce(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarDebounce)),
      map((result) =>
        isNullOrUndefined(result)
          ? this.config.defaultSearchDebounceTime
          : result
      )
    );
  }

  /**
   * retrieve map control searchBarSpellcheck value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarSpellcheck(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getValue(config.searchBarSpellcheck)),
      map((result) => (isNullOrUndefined(result) ? false : result))
    );
  }

  /**
   * retrieve map control searchPlaceProvider value ...
   *
   * @param control form control config object ...
   */
  getMapSearchPlaceProvider(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getValue(config.searchPlaceProvider)),
      map((result) =>
        isNullOrUndefined(result) ? defaultMapSearchPlaceProvider : result
      )
    );
  }

  /**
   * retrieve map control searchBarColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarColor)),
      map((result) =>
        isNullOrEmptyString(result) ? XColorWithBrightness.Primary : result
      )
    );
  }

  /**
   * retrieve map control searchBarForegroundColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarForegroundColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarForegroundColor)
      )
    );
  }

  /**
   * retrieve map control searchBarClearIcon value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarClearButtonIcon(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarClearButtonIcon)
      ),
      map((result) => (isNullOrUndefined(result) ? XIconNames.clear : result))
    );
  }

  /**
   * retrieve map control searchBarTitle value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarTitle(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getValue(config.searchBarTitle)),
      map((result) =>
        isNullOrUndefined(result) ? XResourceIDs.search : result
      )
    );
  }

  /**
   * retrieve map control searchBarSearchIcon value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarSearchButtonIcon(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarSearchButtonIcon)
      ),
      map((result) => (isNullOrUndefined(result) ? XIconNames.search : result))
    );
  }

  /**
   * retrieve map control searchBarInputColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarSearchInputColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarSearchInputColor)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XColorWithBrightness.Primary : result
      )
    );
  }

  /**
   * retrieve map control searchBarForegroundInputColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarForegroundSearchInputColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarForegroundSearchInputColor)
      )
    );
  }

  /**
   * retrieve map control searchResultListColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchResultListColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchResultListColor)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XColorWithBrightness.Light : result
      )
    );
  }

  /**
   * retrieve map control searchBarActionProvider value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarActionProvider(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      map((config) => {
        //
        const key = `${control.propName.toString()}_SearchBar`;
        if (!config.searchBarActionProvider) {
          //
          if (!this.actionProviders[key]) {
            this.actionProviders[key] = new Subject<XSearchBarActionModel>();
          }

          //
          return this.actionProviders[key] as Subject<XSearchBarActionModel>;
        } else {
          return config.searchBarActionProvider;
        }
      })
    );
  }

  /**
   * retrieve map control searchBarClearIconColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarClearButtonIconColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarClearButtonIconColor)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XColorWithBrightness.Light : result
      )
    );
  }

  /**
   * retrieve map control searchBarCssClass value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarCssClass(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.searchBarCssClass))
    );
  }

  /**
   * retrieve map control searchBarSearchIconColor value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarSearchButtonIconColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarSearchButtonIconColor)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XColorWithBrightness.Light : result
      )
    );
  }

  /**
   * retrieve map control searchBarCancelButtonIcon value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarCancelButtonIcon(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarCancelButtonIcon)
      ),
      map((result) => (isNullOrUndefined(result) ? XIconNames.cancel : result))
    );
  }

  /**
   * retrieve map control searchBarPosition value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarPosition(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarPosition)),
      map((result) =>
        isNullOrEmptyString(result) ? XMapItemPosition.TOP : result
      )
    );
  }

  /**
   * retrieve map control searchBarKeyboardType value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarKeyboardType(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarKeyboardType)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XSearchBarType.Search : result
      )
    );
  }

  /**
   * retrieve map control searchBarCancelButtonTitle value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarCancelButtonTitle(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getValue(config.searchBarCancelButtonTitle))
    );
  }

  /**
   * retrieve map control searchBarEnterKeyHint value ...
   *
   * @param control form control config object ...
   */
  getMapSearchBarEnterKeyHint(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarEnterKeyHint)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XSearchBarEnterKeyHint.Search : result
      )
    );
  }

  /**
   * determines map control searchBarShowCancelButton or not ...
   *
   * @param control form control config object ...
   */
  mapShowSearchBarCancelButton(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarShowCancelButton)
      )
    );
  }
  //#endregion

  /**
   * determines map control showSelectedLocation or not ...
   *
   * @param control form control config object ...
   */
  mapShowSelectedLocation(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showSelectedLocation)),
      map((result) => (isNullOrUndefined(result) ? false : result))
    );
  }

  /**
   * retrieve map control selectedLocationPosition value ...
   *
   * @param control form control config object ...
   */
  getMapSelectedLocationPosition(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.selectedLocationPosition)
      ),
      map((position) =>
        isNullOrEmptyString(position) ? XMapItemPosition.TOP : position
      )
    );
  }

  /**
   * determines map control showGoMarkedPlace or not ...
   *
   * @param control form control config object ...
   */
  mapShowGoMarkedPlace(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showGoMarkedPlace)),
      map((result) => (isNullOrUndefined(result) ? true : result))
    );
  }

  /**
   * retrieve map control goMarkedPlacePosition value ...
   *
   * @param control form control config object ...
   */
  getMapGoMarkedPlacePosition(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.goMarkedPlacePosition)
      ),
      map((position) =>
        isNullOrEmptyString(position) ? XMapItemPosition.TOP : position
      )
    );
  }

  /**
   * determines map control showClearMarker or not ...
   *
   * @param control form control config object ...
   */
  mapShowClearMarker(control: XFormControlConfig) {
    //
    // Clear Marker in Form Components only must shown when user decide to it and the present Type is with dialog
    // since the clear value handle clear marker actions in without dialog mode ...
    return this.getMapConfig(control).pipe(
      concatMap((config) =>
        forkJoin({
          showClearMarker: this.getOptionalValue(config.showClearMarker),
          withDialog: this.isMapPresentWithDialog(control),
        })
      ),
      map(
        (info) =>
          (isNullOrUndefined(info.showClearMarker)
            ? true
            : info.showClearMarker) && info.withDialog
      )
    );
  }

  /**
   * retrieve map control clearMarkerPosition value ...
   *
   * @param control form control config object ...
   */
  getMapClearMarkerPosition(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.clearMarkerPosition)),
      map((position) =>
        isNullOrEmptyString(position) ? XMapItemPosition.TOP : position
      )
    );
  }

  /**
   * determines map control showLocator or not ...
   *
   * @param control form control config object ...
   */
  mapShowLocator(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showLocator)),
      map((result) => (isNullOrUndefined(result) ? true : result))
    );
  }

  /**
   * retrieve map control locatorPosition value ...
   *
   * @param control form control config object ...
   */
  getMapLocatorPosition(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.locatorPosition)),
      map((position) =>
        isNullOrEmptyString(position) ? XMapItemPosition.TOP : position
      )
    );
  }

  /**
   * determines map control showZoom or not ...
   *
   * @param control form control config object ...
   */
  mapShowZoom(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showZoom)),
      map((result) => (isNullOrUndefined(result) ? true : result))
    );
  }

  /**
   * determines map control showRotate or not ...
   *
   * @param control form control config object ...
   */
  mapShowRotate(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showRotate)),
      map((result) => (isNullOrUndefined(result) ? true : result))
    );
  }

  /**
   * determines map control showZoomSlider or not ...
   *
   * @param control form control config object ...
   */
  mapShowZoomSlider(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showZoomSlider)),
      map((result) => (isNullOrUndefined(result) ? false : result))
    );
  }

  /**
   * determines map control isInPage or not ...
   *
   * @param control form control config object ...
   */
  isMapInPage(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.isInPage))
    );
  }

  /**
   * determines map control wrapWithCard or not ...
   *
   * @param control form control config object ...
   */
  isMapWrapWithCard(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.wrapWithCard))
    );
  }

  /**
   * retrieve map control cardColor value ...
   *
   * @param control form control config object ...
   */
  getMapCardColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardColor)),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * determines map control cardForegroundColor or not ...
   *
   * @param control form control config object ...
   */
  isMapCardForegroundColor(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardForegroundColor))
    );
  }

  /**
   * retrieve map control cardTitle value ...
   *
   * @param control form control config object ...
   */
  getMapCardTitle(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.cardTitle))
    );
  }

  /**
   * retrieve map control cardSubTitle value ...
   *
   * @param control form control config object ...
   */
  getMapCardSubTitle(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.cardSubTitle))
    );
  }

  /**
   * determines map control showCardFooter or not ...
   *
   * @param control form control config object ...
   */
  mapCardShowFooter(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardShowFooter))
    );
  }

  /**
   * retrieve map control actionProvider value ...
   *
   * @param control form control config object ...
   */
  getMapActionProvider(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      map((config) => {
        //
        if (!config.actionProvider) {
          //
          if (!this.actionProviders[control.propName]) {
            this.actionProviders[control.propName] =
              new Subject<XMapActionModel>();
          }

          //
          return this.actionProviders[
            control.propName
          ] as Subject<XMapActionModel>;
        } else {
          return config.actionProvider;
        }
      })
    );
  }

  //
  //#region Map Present Types ...
  /**
   * retrieve map control presentType value ...
   *
   * @param control form control config object ...
   */
  getMapPresentType(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.presentType)),
      map((presentType) =>
        !isNullOrEmptyString(presentType)
          ? presentType
          : XFormMapControlPresentType.WithoutDialog
      )
    );
  }

  /**
   * determines map control present WithoutDialog or not ...
   *
   * @param control form control config object ...
   */
  isMapPresentWithoutDialog(control: XFormControlConfig) {
    return this.getMapPresentType(control).pipe(
      map(
        (presentType) =>
          presentType === XFormMapControlPresentType.WithoutDialog
      )
    );
  }

  /**
   * determines map control present WithDialog or not ...
   *
   * @param control form control config object ...
   */
  isMapPresentWithDialog(control: XFormControlConfig) {
    return this.getMapPresentType(control).pipe(
      map(
        (presentType) => presentType === XFormMapControlPresentType.WithDialog
      )
    );
  }
  //#endregion
  //#endregion

  //
  //#region ColorPicker Config ...
  /**
   * retrieve color picker control config ...
   *
   * @param control form control config object ...
   */
  getColorPickerConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormColorPickerControlConfig)
    );
  }

  /**
   * determines color picker control has config or not ...
   *
   * @param control form control config object ...
   */
  hasColorPickerConfig(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * retrieve color picker control color value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.color)),
      map((color) => (isNullOrEmptyString(color) ? XColor.Light : color))
    );
  }

  /**
   * retrieve color picker control colorViewBorderColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColorViewBorderColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.colorViewBorderColor)),
      map((color) => (isNullOrEmptyString(color) ? XColor.Light : color))
    );
  }

  /**
   * retrieve color picker control colorViewBorderStyle value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColorViewBorderStyle(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.colorViewBorderStyle)),
      map((style) => (isNullOrEmptyString(style) ? XBorderStyle.Solid : style))
    );
  }

  /**
   * retrieve color picker control colorViewBorderWidth value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColorViewBorderWidth(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.colorViewBorderWidth))
    );
  }

  /**
   * retrieve color picker control cssClass value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCssClass(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.cssClass))
    );
  }

  /**
   * retrieve color picker control cardColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCardColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Light))
    );
  }

  /**
   * retrieve color picker control cardTitle value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCardTitle(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.cardTitle))
    );
  }

  /**
   * retrieve color picker control cursorColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCursorColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.cursorColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Dark))
    );
  }

  /**
   * retrieve color picker control presetColors value ...
   *
   * @param control form control config object ...
   */
  getColorPickerPresetColors(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.presetColors))
    );
  }

  /**
   * retrieve color picker control outputFormat value ...
   *
   * @param control form control config object ...
   */
  getColorPickerOutputFormat(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.outputFormat)),
      map((format) =>
        !isNullOrEmptyString(format) ? format : XColorPickerOutputFormat.XHEX
      )
    );
  }

  /**
   * retrieve color picker control cardSubTitle value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCardSubTitle(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.cardSubTitle))
    );
  }

  /**
   * retrieve color picker control colorTextColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColorTextColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.colorTextColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Dark))
    );
  }

  /**
   * determines color picker control showColorValue or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowColorValue(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showColorValue ? this.getValue(config.showColorValue) : of(true)
      )
    );
  }

  /**
   * determines color picker control showHueChannel or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowHueChannel(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showHueChannel ? this.getValue(config.showHueChannel) : of(true)
      )
    );
  }

  /**
   * determines color picker control foregroundColor or not ...
   *
   * @param control form control config object ...
   */
  isColorPickerForegroundColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.foregroundColor))
    );
  }

  /**
   * determines color picker control showPresetColors or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowPresetColors(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showPresetColors
          ? this.getValue(config.showPresetColors)
          : of(true)
      )
    );
  }

  /**
   * determines color picker control showAlphaChannel or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowAlphaChannel(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showAlphaChannel
          ? this.getValue(config.showAlphaChannel)
          : of(true)
      )
    );
  }

  /**
   * retrieve color picker control presetColorsLable value ...
   *
   * @param control form control config object ...
   */
  getColorPickerPresetColorsLable(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.presetColorsLable)),
      map((value) =>
        isNullOrEmptyString(value) ? XResourceIDs.preset_colors : value
      )
    );
  }

  /**
   * retrieve color picker control copyToClipboardIconLabel value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCopyToClipboardIconLabel(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        this.getResourceValue(config.copyToClipboardIconLabel)
      ),
      map((value) =>
        isNullOrEmptyString(value) ? XResourceIDs.copy_to_clipboard : value
      )
    );
  }

  /**
   * retrieve color picker control colorInputIconLabel value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColorInputIconLabel(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.colorInputIconLabel)),
      map((value) => (isNullOrEmptyString(value) ? XResourceIDs.input : value))
    );
  }

  /**
   * retrieve color picker control clearColorIconLabel value ...
   *
   * @param control form control config object ...
   */
  getColorPickerClearColorIconLabel(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.clearColorIconLabel)),
      map((value) => (isNullOrEmptyString(value) ? XResourceIDs.clear : value))
    );
  }

  /**
   * determines color picker control showPaletteChannel or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowPaletteChannel(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showPaletteChannel
          ? this.getValue(config.showPaletteChannel)
          : of(true)
      )
    );
  }

  /**
   * determines color picker control cardForegroundColor or not ...
   *
   * @param control form control config object ...
   */
  isColorPickerCardForegroundColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardForegroundColor))
    );
  }

  /**
   * determines color picker control showCopyToClipboard or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowCopyToClipboard(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showCopyToClipboard
          ? this.getValue(config.showCopyToClipboard)
          : of(false)
      )
    );
  }

  /**
   * determines color picker control showColorInput or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowColorInput(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showCopyToClipboard
          ? this.getValue(config.showColorInput)
          : of(true)
      )
    );
  }

  /**
   * retrieve color picker control representColorFormats value ...
   *
   * @param control form control config object ...
   */
  getColorPickerRepresentColorFormats(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.representColorFormats)),
      map((formats) =>
        hasChild(formats) ? formats : [XColorPickerOutputFormat.XHEX]
      )
    );
  }

  /**
   * retrieve color picker control presetColorsLableColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerPresetColorsLableColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.presetColorsLableColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Dark))
    );
  }

  /**
   * retrieve color picker control copyToClipboardIconColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerCopyToClipboardIconColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.copyToClipboardIconColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Tertiary))
    );
  }

  /**
   * retrieve color picker control copyToClipboardIconColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerColorInputIconColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.colorInputIconColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Secondary))
    );
  }

  /**
   * retrieve color picker control clearColorIconColor value ...
   *
   * @param control form control config object ...
   */
  getColorPickerClearColorIconColor(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getValue(config.clearColorIconColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Danger))
    );
  }

  /**
   * determines color picker control showSelectedColorPreview or not ...
   *
   * @param control form control config object ...
   */
  colorPickerShowSelectedColorPreview(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        config.showSelectedColorPreview
          ? this.getValue(config.showSelectedColorPreview)
          : of(true)
      )
    );
  }

  /**
   * determines color picker control isInPage or not ...
   *
   * @param control form control config object ...
   */
  isColorPickerInPage(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.isInPage))
    );
  }

  /**
   * determines color picker control wrapWithCard or not ...
   *
   * @param control form control config object ...
   */
  isColorPickerWrapWithCard(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.wrapWithCard))
    );
  }

  /**
   * determines color picker control showCardFooter or not ...
   *
   * @param control form control config object ...
   */
  colorPickercardShowFooter(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showCardFooter))
    );
  }

  /**
   * retrieve color picker component's selected value ...
   *
   * @param control form control config object ...
   */
  getColorPickerSelectedColor(control: XFormControlConfig) {
    //
    const fControl = this.getControl(control.propName);
    if (!fControl) {
      return of('');
    }

    //
    const fValue = fControl.value as string;
    if (isNullOrEmptyString(fValue)) {
      return of('');
    }

    //
    return of(fValue);
  }

  /**
   * retrieve color picker control actionProvider value ...
   *
   * @param control form control config object ...
   */
  getColorPickerActionProvider(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      map((config) => {
        //
        if (!config.actionProvider) {
          //
          if (!this.actionProviders[control.propName]) {
            this.actionProviders[control.propName] =
              new Subject<XMapActionModel>();
          }

          //
          return this.actionProviders[
            control.propName
          ] as Subject<XColorPickerActionModel>;
        } else {
          return config.actionProvider;
        }
      })
    );
  }

  //
  //#region ColorPicker Present Types ...
  /**
   * retrieve color picker control presentType value ...
   *
   * @param control form control config object ...
   */
  getColorPickerPresentType(control: XFormControlConfig) {
    return this.getColorPickerConfig(control).pipe(
      concatMap((config) =>
        !isNullOrUndefined(config) && !isNullOrUndefined(config.presentType)
          ? this.getOptionalValue(config.presentType)
          : of(null)
      ),
      map((presentType) =>
        !isNullOrEmptyString(presentType)
          ? presentType
          : XFormColorPickerControlPresentType.WithoutDialog
      )
    );
  }

  /**
   * determines color picker control present WithoutDialog or not ...
   *
   * @param control form control config object ...
   */
  isColorPickerPresentWithoutDialog(control: XFormControlConfig) {
    return this.getColorPickerPresentType(control).pipe(
      map(
        (presentType) =>
          presentType === XFormColorPickerControlPresentType.WithoutDialog
      )
    );
  }

  /**
   * determines color picker control present WithDialog or not ...
   *
   * @param control form control config object ...
   */
  isColorPickerPresentWithDialog(control: XFormControlConfig) {
    return this.getColorPickerPresentType(control).pipe(
      map(
        (presentType) =>
          presentType === XFormColorPickerControlPresentType.WithDialog
      )
    );
  }
  //#endregion
  //#endregion

  //
  //#region Avatar Config ...
  /**
   * retrieve avatar upload control config ...
   *
   * @param control form control config object ...
   */
  getAvatarConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormAvatarUploadControlConfig)
    );
  }

  /**
   * determines avatar upload control has config or not ...
   *
   * @param control form control config object ...
   */
  hasAvatarConfig(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }

  /**
   * determines avatar upload control canDrop or not ...
   *
   * @param control form control config object ...
   */
  avatarCanDrop(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.canDrop))
    );
  }

  /**
   * determines avatar upload control isInPage or not ...
   *
   * @param control form control config object ...
   */
  isAvatarInPage(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.isInPage))
    );
  }

  /**
   * determines avatar upload control wrapWithCard or not ...
   *
   * @param control form control config object ...
   */
  isAvatarWrapWithCard(control: XFormControlConfig) {
    return this.getMapConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.wrapWithCard))
    );
  }

  /**
   * retrieve avatar upload control color value ...
   *
   * @param control form control config object ...
   */
  getAvatarColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.color)),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * determines map control showCardFooter or not ...
   *
   * @param control form control config object ...
   */
  avatarCardShowFooter(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.showCardFooter))
    );
  }

  /**
   * determines avatar upload control foregroundColor or not ...
   *
   * @param control form control config object ...
   */
  isAvatarForegroundColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.foregroundColor))
    );
  }

  /**
   * retrieve avatar upload control placeHolderAvatar value ...
   *
   * @param control form control config object ...
   */
  getAvatarPlaceHolderAvatar(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getValue(config.placeHolderAvatar))
    );
  }

  /**
   * retrieve avatar upload control minAllowedFileSize value ...
   *
   * @param control form control config object ...
   */
  getAvatarMinAllowedFileSize(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.minAllowedFileSize)),
      map((minAllowedFileSize) =>
        minAllowedFileSize ? minAllowedFileSize : this.config.minAllowedFileSize
      )
    );
  }

  /**
   * retrieve avatar upload control maxAllowedFileSize value ...
   *
   * @param control form control config object ...
   */
  getAvatarMaxAllowedFileSize(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.maxAllowedFileSize)),
      map((maxAllowedFileSize) =>
        maxAllowedFileSize
          ? maxAllowedFileSize
          : this.config.maxProfileImageSize
      )
    );
  }

  /**
   * retrieve avatar upload control cardColor value ...
   *
   * @param control form control config object ...
   */
  getAvatarCardColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardColor)),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * determines avatar upload control cardForegroundColor or not ...
   *
   * @param control form control config object ...
   */
  isAvatarCardForegroundColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.cardForegroundColor))
    );
  }

  /**
   * determines avatar upload control fileDropAreaIsInPage or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaInPage(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.fileDropAreaIsInPage))
    );
  }

  /**
   * retrieve avatar upload control cardTitle value ...
   *
   * @param control form control config object ...
   */
  getAvatarCardTitle(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.cardTitle))
    );
  }

  /**
   * retrieve avatar upload control cssClass value ...
   *
   * @param control form control config object ...
   */
  getAvatarCssClass(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.cssClass))
    );
  }

  /**
   * determines avatar upload control fileDropAreaWrapWithCard or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaWrapWithCard(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaWrapWithCard)
      )
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaIcon value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaIcon(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getValue(config.fileDropAreaIcon)),
      map((icon) => (icon ? icon : XIconNames.arrow_down))
    );
  }

  /**
   * determines avatar upload control fileDropAreaDragDisabled or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaDragDisabled(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaDragDisabled)
      )
    );
  }

  /**
   * retrieve avatar upload control cardSubTitle value ...
   *
   * @param control form control config object ...
   */
  getAvatarCardSubTitle(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.cardSubTitle))
    );
  }

  /**
   * retrieve avatar control actionProvider value ...
   *
   * @param control form control config object ...
   */
  getAvatarActionProvider(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      map((config) => {
        //
        if (!config.actionProvider) {
          //
          if (!this.actionProviders[control.propName]) {
            this.actionProviders[control.propName] =
              new Subject<XAvatarUploadActionModel>();
          }

          //
          return this.actionProviders[
            control.propName
          ] as Subject<XAvatarUploadActionModel>;
        } else {
          return config.actionProvider;
        }
      })
    );
  }

  /**
   * determines map control fileDropAreaShowCardFooter or not ...
   *
   * @param control form control config object ...
   */
  avatarDropAreaCardShowFooter(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaShowCardFooter)
      )
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaColor value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.fileDropAreaColor)),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * determines avatar upload control fileDropAreaForegroundColor or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaForegroundColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaForegroundColor)
      )
    );
  }

  /**
   * determines avatar upload control fileDropAreaDisableFileCheck or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaDisableFileCheck(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaDisableFileCheck)
      )
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaCardColor value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaCardColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaCardColor)
      ),
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * determines avatar upload control fileDropAreaCardForegroundColor or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaCardForegroundColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaCardForegroundColor)
      )
    );
  }

  /**
   * retrieve avatar upload control emptyFileException value ...
   *
   * @param control form control config object ...
   */
  getAvatarEmptyFileException(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.emptyFileException)),
      map((exception) => (exception ? exception : XExceptionIDs.EmptyFile))
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaErrorColor value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaErrorColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaErrorColor)
      ),
      map((color) => (color ? color : XColor.Danger))
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaMessage value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaMessage(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getOptionalValue(config.fileDropAreaMessage)),
      map((message) => (message ? message : XResourceIDs.drag_drop_here)),
      concatMap((resource) => this.getResourceValue(resource))
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaAccentColor value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaAccesntColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaAccentColor)
      ),
      map((color) => (color ? color : XColor.Secondary))
    );
  }

  /**
   * retrieve avatar upload control allowedExtensions value ...
   *
   * @param control form control config object ...
   */
  getAvatarAllowedExtensions(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.allowedExtensions)),
      map((allowedExtensions) =>
        hasChild(allowedExtensions)
          ? allowedExtensions
          : this.config.allowedImageExtensions
      )
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaCardTitle value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaCardTitle(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getResourceValue(config.fileDropAreaCardTitle))
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaSuccessColor value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaSuccessColor(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaSuccessColor)
      ),
      map((color) => (color ? color : XColor.Success))
    );
  }

  /**
   * determines avatar upload control fileDropAreaSupportsMultipleFiles or not ...
   *
   * @param control form control config object ...
   */
  isAvatarDropAreaSupportsMultipleFiles(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaSupportsMultipleFiles)
      )
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaCssClass value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaCssClass(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.fileDropAreaCssClass))
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaCardSubTitle value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaCardSubTitle(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getResourceValue(config.fileDropAreaCardSubTitle)
      )
    );
  }

  /**
   * retrieve avatar upload control emptyFileException value ...
   *
   * @param control form control config object ...
   */
  getAvatarNotAllowedFileException(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.notAllowedFileException)
      ),
      map((exception) => (exception ? exception : XExceptionIDs.InavlidFile))
    );
  }

  /**
   * retrieve avatar upload control fileDropAreaContentTemplate value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaContentTemplate(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.fileDropAreaContentTemplate)
      )
    );
  }

  /**
   * retrieve avatar upload control minAllowedFileSizeException value ...
   *
   * @param control form control config object ...
   */
  getAvatarMinAllowedFileSizeException(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.minAllowedFileSizeException)
      ),
      map((exception) =>
        exception ? exception : XExceptionIDs.MinFilesSizeExceeded
      )
    );
  }

  /**
   * retrieve avatar upload control maxAllowedFileSizeException value ...
   *
   * @param control form control config object ...
   */
  getAvatarMaxAllowedFileSizeException(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getOptionalValue(config.maxAllowedFileSizeException)
      ),
      map((exception) =>
        exception ? exception : XExceptionIDs.MaxFilesSizeExceeded
      )
    );
  }

  /**
   * retrieve avatar control actions value ...
   *
   * @param control form control config object ...
   */
  getAvatarActions(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) => this.getArrayValue(config.actions)),
      concatMap((actions) =>
        hasChild(actions)
          ? of(actions)
          : this.getArrayValue(defaultAvatarUploadActionButtons)
      )
    );
  }

  /**
   * retrieve avatar control fileDropAreaActionProvider value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaActionProvider(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      map((config) => {
        //
        const key = `${control.propName.toString()}_drop_area`;

        //
        if (!config.fileDropAreaActionProvider) {
          //
          if (!this.actionProviders[key]) {
            this.actionProviders[key] = new Subject<XFileDropAreaActionModel>();
          }

          //
          return this.actionProviders[key] as Subject<XFileDropAreaActionModel>;
        } else {
          return config.fileDropAreaActionProvider;
        }
      })
    );
  }

  /**
   * retrieve avatar upload control allowedExtensions value ...
   *
   * @param control form control config object ...
   */
  getAvatarDropAreaAllowedExtensions(control: XFormControlConfig) {
    return this.getAvatarConfig(control).pipe(
      concatMap((config) =>
        this.getArrayValue(config.fileDropAreaAllowedExtensions)
      ),
      map((allowedExtensions) =>
        hasChild(allowedExtensions)
          ? allowedExtensions
          : this.config.allowedImageExtensions
      )
    );
  }
  //#endregion

  //
  //#region Selector Config ...
  /**
   * retrieve selector control config ...
   *
   * @param control form control config object ...
   */
  getSelectorConfig(control: XFormControlConfig) {
    return this.getControlTypeConfig(control).pipe(
      map((config) => config as XFormSelectorControlConfig)
    );
  }

  /**
   * determines selector control has config or not ...
   *
   * @param control form control config object ...
   */
  hasSelectorConfig(control: XFormControlConfig) {
    return this.getSelectorConfig(control).pipe(
      map((config) => !isNullOrUndefined(config))
    );
  }
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Control Update On ...
  /**
   * retrieve a control UpdateOn strategy ...
   *
   * @param control form control config object ...
   */
  getControlUpdateOn(control: XFormControlConfig) {
    return this.getOptionalValue(control.updateOn).pipe(
      map((updateOn) => (updateOn ? updateOn : XFormUpdateOn.CHANGE))
    );
  }
  //#endregion

  //
  //#region Control Validators ...
  /**
   * retrieve a control validators descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlValidators(control: XFormControlConfig) {
    return this.getOptionalValue(control.validators);
  }

  /**
   * determines a control has Validators or not ...
   *
   * @param control form control config object ...
   */
  controlHasValidators(control: XFormControlConfig) {
    return this.getControlValidators(control).pipe(
      map((controlValidators) => !isNullOrUndefined(controlValidators))
    );
  }
  //#endregion

  //
  //#region Control Event Handlers ...
  /**
   * retrieve a control event handlers descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlEventHandlers(control: XFormControlConfig) {
    return this.getOptionalValue(control.eventHandlers);
  }

  /**
   * determines a control has validators or not ...
   *
   * @param control form control config object ...
   */
  controlHasEventHandlers(control: XFormControlConfig) {
    return this.getControlEventHandlers(control).pipe(
      map((eventHandlers) => !isNullOrUndefined(eventHandlers))
    );
  }
  //#endregion

  //
  //#region Control Appearance Descriptor ...
  /**
   * retrieve a control appearance descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlAppearanceDesc(
    control: XFormControlConfig
  ): Observable<XFormControlAppearanceDescriptor> {
    return !isNullOrUndefined(control)
      ? this.getValue(control.appearance)
      : of(null);
  }

  //
  //#region Appearance Parts ...
  //
  //#region Control Hidden ...
  /**
   * determines a control hidden or not ...
   *
   * @param control form control config object ...
   */
  getControlHidden(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig) ? appearanceConfig.hidden : true
        )
      )
    );
  }
  //#endregion

  //
  //#region Control Disabled ...
  /**
   * determines a control is disabled or not ...
   *
   * @param control form control config object ...
   */
  getControlDisabled(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.disabled
            : true
        )
      )
    );
  }
  //#endregion

  //
  //#region Control Hint ...
  /**
   * retrieve a control hint ...
   *
   * @param control form control config object ...
   */
  getControlHint(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getResourceValue(
          !isNullOrUndefined(appearanceConfig) ? appearanceConfig.hint : ''
        )
      )
    );
  }
  //#endregion

  //
  //#region Control Label ...
  /**
   * retrieve a control label ...
   *
   * @param control form control config object ...
   */
  getControlLabel(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getResourceValue(
          !isNullOrUndefined(appearanceConfig) ? appearanceConfig.label : ''
        )
      )
    );
  }
  //#endregion

  //
  //#region Control Tooltip ...
  /**
   * retrieve a control tooltip ...
   *
   * @param control form control config object ...
   */
  getControlTooltip(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getResourceValue(
          !isNullOrUndefined(appearanceConfig) ? appearanceConfig.tooltip : ''
        )
      )
    );
  }
  //#endregion

  //
  //#region Control PlaceHolder ...
  /**
   * retrieve a control placeholder ...
   *
   * @param control form control config object ...
   */
  getControlPlaceHolder(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getResourceValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.placeholder
            : ''
        )
      )
    );
  }
  //#endregion

  //
  //#region Control CssClass ...
  /**
   * retrieve a control cssClaases ...
   *
   * @param control form control config object ...
   */
  getControlCssClass(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getArrayValue(
          !isNullOrUndefined(appearanceConfig) ? appearanceConfig.cssClass : []
        )
      ),
      concatMap((cssClasses) =>
        this.getControlType(control).pipe(
          map((type) => {
            return {
              type,
              cssClasses,
            };
          })
        )
      ),
      map((descriptor) => {
        return descriptor.cssClasses.concat(
          'x-form-control',
          `x-form-control-${control.index}`,
          `x-form-control-${descriptor.type}`
        );
      })
    );
  }
  //#endregion

  //
  //#region Control Icons Descriptor ...
  /**
   * retrieve a control icons descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlIconDesc(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig) ? appearanceConfig.icons : null
        )
      )
    );
  }

  /**
   * determines a control has icons descriptor object or not ...
   *
   * @param control form control config object ...
   */
  controlHasIcons(control: XFormControlConfig) {
    return this.getControlIconDesc(control).pipe(
      map((iconDesc) => !isNullOrUndefined(iconDesc))
    );
  }

  /**
   * determines a control has suffix icon or not ...
   *
   * @param control form control config object ...
   */
  controlHasSuffixIcon(control: XFormControlConfig) {
    return this.getControlIconDesc(control).pipe(
      concatMap((iconDesc) =>
        this.getOptionalValue(iconDesc ? iconDesc.suffix : null).pipe(
          map((iconSuffix) => !isNullOrUndefined(iconSuffix))
        )
      )
    );
  }

  /**
   * retrieve a control suffix icon object ...
   *
   * @param control form control config object ...
   */
  getControlSuffixIcon(control: XFormControlConfig) {
    return this.getControlIconDesc(control).pipe(
      concatMap((iconDesc) =>
        this.getOptionalValue(iconDesc ? iconDesc.suffix : null)
      )
    );
  }

  /**
   * determines a control has prefix icon or not ...
   *
   * @param control form control config object ...
   */
  controlHasPrefixIcon(control: XFormControlConfig) {
    return this.getControlIconDesc(control).pipe(
      concatMap((iconDesc) =>
        this.getOptionalValue(iconDesc ? iconDesc.prefix : null).pipe(
          map((iconPrefix) => !isNullOrUndefined(iconPrefix))
        )
      )
    );
  }

  /**
   * retrieve a control prefix icon ...
   *
   * @param control form control config object ...
   */
  getControlPrefixIcon(control: XFormControlConfig) {
    return this.getControlIconDesc(control).pipe(
      concatMap((iconDesc) =>
        this.getOptionalValue(iconDesc ? iconDesc.prefix : null)
      )
    );
  }

  /**
   * retrieve an icon descriptor's icon name ...
   *
   * @param icon form control icon descriptor ...
   */
  getControlIconName(icon: XFormIconDescriptor) {
    return this.getValue(icon.name);
  }

  /**
   * retrieve an icon descriptor's icon color ...
   *
   * @param icon form control icon descriptor ...
   */
  getControlIconColor(icon: XFormIconDescriptor) {
    return this.getOptionalValue(icon.color).pipe(
      map((color) => (color ? color : XColor.Light))
    );
  }

  /**
   * retrieve an icon descriptor's icon tooltip ...
   *
   * @param icon form control icon descriptor ...
   */
  getControlIconTooltip(icon: XFormIconDescriptor) {
    return this.getResourceValue(icon.tooltip);
  }

  /**
   * retrieve an icon descriptor's apply state color ...
   *
   * @param icon form control icon descriptor ...
   */
  getControlIconApplyStateColor(icon: XFormIconDescriptor) {
    return this.getOptionalValue(icon.applyStateColor);
  }
  //#endregion

  //
  //#region Control Formatter Config ...
  /**
   * retrieve a control formatter config descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlFormatterConfig(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.formatter
            : null
        )
      )
    );
  }

  /**
   * determines a control has formatter config descriptor or not ...
   *
   * @param control form control config object ...
   */
  controlHasFormatter(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      map((formatterConfig) => !isNullOrUndefined(formatterConfig))
    );
  }

  /**
   * retrieve a control formatter locale ...
   *
   * @param control form control config object ...
   */
  getFormatterLocale(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      concatMap((formatterConfig) => this.getValue(formatterConfig.locale))
    );
  }

  /**
   * retrieve a control formatter seperator ...
   *
   * @param control form control config object ...
   */
  getFormatterSeperator(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      concatMap((formatterConfig) =>
        this.getOptionalValue(formatterConfig.seperator)
      )
    );
  }

  /**
   * retrieve a control formatter type ...
   *
   * @param control form control config object ...
   */
  getFormatterType(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      concatMap((formatterConfig) => this.getValue(formatterConfig.type))
    );
  }

  /**
   *  retrieve a control formatter currency label ...
   *
   * @param control form control config object ...
   */
  getFormatterCurrencyLabel(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      concatMap((formatterConfig) =>
        isNullOrUndefined(formatterConfig.currencyLable)
          ? of('')
          : this.getResourceValue(formatterConfig.currencyLable)
      )
    );
  }

  /**
   * retrieve a control formatter string formatter ...
   *
   * @param control form control config object ...
   */
  getFormatterStringFormatter(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      concatMap((formatterConfig) =>
        this.getArrayValue(formatterConfig.stringFormatter)
      )
    );
  }

  /**
   * retrieve a control formatter string formatter ...
   *
   * @param control form control config object ...
   */
  getFormatterInputType(control: XFormControlConfig) {
    return this.getControlFormatterConfig(control).pipe(
      concatMap((formatterConfig) =>
        isNullOrUndefined(formatterConfig.inputType)
          ? of(XStringInputFormatType.None)
          : this.getValue(formatterConfig.inputType)
      )
    );
  }
  //#endregion

  //
  //#region Control Progressbar ...
  /**
   * retrieve a control progress bar config descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlProgressBarConfig(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.progressBar
            : null
        )
      )
    );
  }

  /**
   * retrieve a control has progress bar object descriptor or not ...
   *
   * @param control form control config object ...
   */
  controlHasProgressBar(control: XFormControlConfig) {
    return this.getControlProgressBarConfig(control).pipe(
      map((progressBarConfig) => !isNullOrUndefined(progressBarConfig))
    );
  }

  /**
   * retrieve a control progressbar color ...
   *
   * @param control form control config object ...
   */
  getProgressBarColor(control: XFormControlConfig) {
    return this.getControlProgressBarConfig(control).pipe(
      concatMap((progressBarConfig) => this.getValue(progressBarConfig.color))
    );
  }
  //#endregion

  //
  //#region Control AutoComplete ...
  /**
   * retrieve an id number for form control auto complete view ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteId(control: XFormControlConfig) {
    return `x-${control.propName.toString()}-auto-complete`;
  }

  /**
   * retrieve a control autocomplete config object ...
   *
   * @param control form control config object ...
   */
  getControlAutoCompleteConfig(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.autoComplete
            : null
        )
      )
    );
  }

  /**
   * retrieve a control has autocomplete or not ...
   *
   * @param control form control config object ...
   */
  controlHasAutoComplete(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      map((autoConpleteConfig) => !isNullOrUndefined(autoConpleteConfig))
    );
  }

  /**
   * retrieve a control autocomplete opened ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteOpened(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      concatMap((autoCompleteConfig) =>
        this.getOptionalValue(autoCompleteConfig.opened)
      )
    );
  }

  /**
   * retrieve a control autocomplete item template ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteItemTemplate(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      concatMap((autoCompleteConfig) =>
        this.getValue(autoCompleteConfig.itemTemplate)
      )
    );
  }

  /**
   * retrieve a control autocomplete color ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteColor(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      concatMap((autoCompleteConfig) =>
        this.getOptionalValue(autoCompleteConfig.color).pipe(
          map((color) => (color ? color : XColor.Light))
        )
      )
    );
  }

  /**
   * retrieve a control autocomplete onSelect ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteOnSelect(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      concatMap((autoCompleteConfig) =>
        this.getValue(autoCompleteConfig.onSelect)
      )
    );
  }

  /**
   * retrieve a control autocomplete Items ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteItems(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      concatMap((autoCompleteConfig) => {
        return !isNullOrUndefined(autoCompleteConfig.items) &&
          isFunction(autoCompleteConfig.items)
          ? this.getArrayValue(autoCompleteConfig.items())
          : this.getValue([]);
      })
    );
  }

  /**
   * retrieve autocomplete CssClass ...
   *
   * @param control form control config object ...
   */
  getAutoCompleteCssClass(control: XFormControlConfig) {
    return this.getControlAutoCompleteConfig(control).pipe(
      concatMap((autoCompleteConfig) =>
        this.getArrayValue(autoCompleteConfig.cssClass).pipe(
          map((cssClasses) =>
            cssClasses.concat(
              'x-auto-complete',
              `x-${control.propName.toString()}-auto-complete`
            )
          )
        )
      )
    );
  }

  /**
   * retrieve control errors classes ...
   *
   * @param control form control config ...
   */
  getControlErrorsClasses(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      map((type) => {
        return [
          'x-form-control-errors',
          `x-form-${type}-control-errors`,
          `x-form-control-${control.index.toString()}-errors`,
          `x-form-control-${control.propName.toString()}-errors`,
        ];
      })
    );
  }

  /**
   * retrieve special control's special error's class list ...
   *
   * @param control form control config ...
   * @param handler form error handler instance ...
   */
  getControlErrorClasses(
    control: XFormControlConfig,
    handler: XFormErrorHandler
  ) {
    return this.getControlType(control).pipe(
      concatMap((type) =>
        this.getErrorHandlerErrorName(handler).pipe(
          map((errorName) => {
            return {
              type,
              errorName,
            };
          })
        )
      ),
      map((descriptor) => {
        return [
          'x-form-field-error',
          `x-form-field-${descriptor.type}-error`,
          `x-form-field-${descriptor.errorName}-error`,
          `x-form-field-${control.propName.toString()}-error`,
          `x-form-field-${descriptor.type}-${descriptor.errorName}-error`,
        ];
      })
    );
  }
  //#endregion

  //
  //#region Control Appearance ...
  /**
   * retrieve a control appearance ...
   *
   * @param control form control config object ...
   */
  getControlAppearance(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig) &&
            !isNullOrUndefined(appearanceConfig.appearance)
            ? appearanceConfig.appearance
            : XFormControlAppearance.Fill
        ).pipe(map((result) => result as MatFormFieldAppearance))
      )
    );
  }
  getControlAppearanceObject(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig) &&
            !isNullOrUndefined(appearanceConfig.appearance)
            ? appearanceConfig.appearance
            : XFormControlAppearance.Fill
        )
      )
    );
  }
  //#endregion

  //
  //#region Control Label Position ...
  /**
   * retrieve a control label position ...
   *
   * @param control form control config object ...
   */
  getControlLabelPosition(control: XFormControlConfig) {
    return this.getControlAppearanceDesc(control).pipe(
      concatMap((appearanceConfig) =>
        this.getOptionalValue(
          !isNullOrUndefined(appearanceConfig)
            ? appearanceConfig.labelPosition
            : XFormControlLabelPosition.Before
        )
      )
    );
  }
  //#endregion
  //#endregion
  //#endregion

  //
  //#region Control Error Handlers ...
  /**
   * retrieve a control error handlers descriptor object ...
   *
   * @param control form control config object ...
   */
  getControlErrorHandlers(control: XFormControlConfig) {
    return this.getArrayValue(control.errorHandlers);
  }

  /**
   * determines a control has error handlers descriptors object or not ...
   *
   * @param control form control config object ...
   */
  controlHasErrorHandlers(control: XFormControlConfig) {
    return this.hasChildValue(this.getControlErrorHandlers(control));
  }

  controlHasErrors(control: XFormControlConfig) {
    return this.getValue(control).pipe(
      concatMap((ctrl) =>
        this.getValue(this.getControl(ctrl.propName)).pipe(
          map((fControl) => ({ ctrl, fControl }))
        )
      ),
      map((ctrlInfo) => {
        return isNullOrUndefined(ctrlInfo.ctrl) ||
          isNullOrUndefined(ctrlInfo.fControl)
          ? false
          : !isNullOrUndefined(ctrlInfo.fControl.errors);
      })
    );
  }

  /**
   * determines a control has specific error or not ...
   *
   * @param controlName control name ...
   * @param errorName error name ...
   */
  controlHasErrorByName(
    controlName: XFormControlNameIdentifier,
    errorName: string
  ) {
    //
    const control = this.getControl(controlName);
    if (!control) {
      return false;
    }

    //
    const result =
      !isNullOrUndefined(control.errors) &&
      keys(control.errors).includes(errorName) &&
      !isNullOrUndefined(control.errors[errorName]);

    //
    return result;
  }

  /**
   * determine a control has specific error or not ...
   *
   * @param control form control config ...
   * @param handler form error handler instance ...
   */
  controlHasError(control: XFormControlConfig, handler: XFormErrorHandler) {
    return this.getErrorHandlerErrorName(handler).pipe(
      map((errorName) => {
        return this.controlHasErrorByName(control.propName, errorName);
      })
    );
  }

  controlGetError(control: XFormControlConfig, handler: XFormErrorHandler) {
    return this.getErrorHandlerErrorName(handler).pipe(
      map((errorName) => {
        return this.controlHasErrorByName(control.propName, errorName);
      })
    );
  }

  /**
   * retrieve specific error of a control ...
   *
   * @param controlName control name ...
   * @param errorName error name ...
   */
  getControlErrorByName(
    controlName: XFormControlNameIdentifier,
    errorName: string
  ) {
    //
    const control = this.getControl(controlName);
    if (!this.controlHasErrorByName(controlName, errorName)) {
      return false;
    }

    //
    return control.errors[errorName] || null;
  }

  /**
   * retrieve specific error of a control ...
   *
   * @param control form control config ...
   * @param handler form error handler instance ...
   */
  getControlError(control: XFormControlConfig, handler: XFormErrorHandler) {
    return this.getErrorHandlerErrorName(handler).pipe(
      map((errorName) => {
        if (!isNullOrUndefined(control)) {
          return this.getControlErrorByName(control.propName, errorName);
        } else {
          return this.getFormErrorByName(errorName);
        }
      })
    );
  }
  //#endregion
  //#endregion

  //
  //#region Error Handler Providers ...
  /**
   * retrieve an error handler error name ...
   *
   * @param errorHandler an error handler descriptor object ...
   */
  getErrorHandlerErrorName(errorHandler: XFormErrorHandler) {
    return this.getValue(errorHandler.errorName);
  }

  /**
   * retrieve an error handler error message ...
   *
   * @param errorHandler an error handler descriptor object ...
   */
  getErrorHandlerErrorMessage(
    errorHandler: XFormErrorHandler,
    control?: XFormControlConfig
  ) {
    //
    if (
      errorHandler.errorDataProvider &&
      isFunction(errorHandler.errorDataProvider)
    ) {
      //
      return this.getControlError(control, errorHandler).pipe(
        concatMap((controlError) =>
          this.getResourceValue(errorHandler.errorDataProvider(controlError))
        )
      );
    } else {
      return this.getResourceValue(errorHandler.errorMessage);
    }
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  //
  //#region Common Controls UI Handlers ...
  async handleControlBlured(name: XFormControlNameIdentifier) {
    //
    if (!this.FORM_CONFIG || !this.hasControl(name)) {
      return;
    }

    //
    const control = await this.getValueAsync(this.getFormControl(name));
    if (!control) {
      return;
    }

    //
    const eventModel: XBaseFormControlActionModel = {
      name: control.propName,
    };

    //
    this.controlBlured.emit(eventModel);

    //
    const eventHandlers = await this.getValueAsync(
      this.getControlEventHandlers(control)
    );
    if (!eventHandlers || !eventHandlers.onBlured) {
      return;
    }

    //
    await this.getValueAsync(eventHandlers.onBlured(eventModel));
  }

  async handleControlFocused(name: XFormControlNameIdentifier) {
    //
    if (!this.FORM_CONFIG || !this.hasControl(name)) {
      return;
    }

    //
    const control = await this.getValueAsync(this.getFormControl(name));
    if (!control) {
      return;
    }

    //
    const inputId = await this.getValueAsync(
      this.getControlInputElementId(control)
    );
    this.currentInputFocus = inputId;

    //
    const eventModel: XBaseFormControlActionModel = {
      name: control.propName,
    };

    //
    this.controlFocused.emit(eventModel);

    //
    const eventHandlers = await this.getValueAsync(
      this.getControlEventHandlers(control)
    );
    if (!eventHandlers || !eventHandlers.onFocused) {
      return;
    }

    //
    await this.getValueAsync(eventHandlers.onFocused(eventModel));
  }

  async handleControlValueChanged(event: XFormControlValueChangeEventModel) {
    //
    if (
      !this.FORM ||
      this.uiDisabled ||
      !this.FORM_CONFIG ||
      !this.hasControl(event.name)
    ) {
      return;
    }

    //
    const control = await this.getValueAsync(this.getFormControl(event.name));
    if (control) {
      //
      const isDisabled = await this.getValueAsync(
        this.getControlDisabled(control)
      );
      if (isDisabled) {
        return;
      }
    }

    //
    this.controlValueChanged.emit(event);
  }

  async handleControlStatusChanged(event: XFormControlStatusChangeEventModel) {
    //
    if (
      !this.FORM ||
      this.uiDisabled ||
      !this.FORM_CONFIG ||
      !this.hasControl(event.name)
    ) {
      return;
    }

    //
    const control = await this.getValueAsync(this.getFormControl(event.name));
    if (control) {
      //
      const isDisabled = await this.getValueAsync(
        this.getControlDisabled(control)
      );
      if (isDisabled) {
        return;
      }
    }

    //
    this.controlStatusChanged.emit(event);
  }

  async handleControlIconClicked(icon: XFormIconDescriptor) {
    //
    if (!this.FORM || !this.FORM_CONFIG || !icon.handler) {
      return;
    }

    //
    await this.getValueAsync(icon.handler());
  }

  async handleControlActionIconClicked(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor,
    control: XFormControlConfig
  ) {
    //
    const type = await this.getValueAsync(this.getControlType(control));

    //
    switch (type) {
      //
      case XFormControlType.Map:
        //
        await this.handleMapIconClicked(role, descriptor, control);
        break;

      //
      case XFormControlType.Selector:
        await this.handleItemSelectorIconClicked(role, descriptor, control);
        break;

      //
      case XFormControlType.ColorPicker:
        //
        await this.handleColorPickerIconClicked(role, descriptor, control);
        break;

      //
      default:
        break;
    }
  }
  //#endregion

  //
  //#region DatePicker UI Handlers ...
  async handleDatePickerDateInput(
    name: XFormControlNameIdentifier,
    event: MatDatepickerInputEvent<any>
  ) {
    //
    let result: Date = null;
    const isDateInput = isDate(event.value);
    const isMomentInput = isMoment(event.value);
    if (isMomentInput) {
      result = momentToDate(event.value);
    } else if (isDateInput) {
      result = event.value as Date;
    }

    //
    await this.setValue(name, result);

    //
    if (!this.FORM_CONFIG || !this.hasControl(name)) {
      return;
    }

    //
    const control = await this.getValueAsync(this.getFormControl(name));
    if (!control) {
      return;
    }

    //
    const handler = await this.getValueAsync(this.getDateInputHandler(control));
    if (!handler) {
      return;
    }

    //
    // Fire Hanlder ...
    await this.getValueAsync(handler(result));
  }

  async handleDatePickerDateChange(
    name: XFormControlNameIdentifier,
    event: MatDatepickerInputEvent<any>
  ) {
    //
    let result: Date = null;
    const isDateInput = isDate(event.value);
    const isMomentInput = isMoment(event.value);
    if (isMomentInput) {
      result = momentToDate(event.value);
    } else if (isDateInput) {
      result = event.value as Date;
    }

    //
    await this.setValue(name, result);

    //
    if (!this.FORM_CONFIG || !this.hasControl(name)) {
      return;
    }

    //
    const control = await this.getValueAsync(this.getFormControl(name));
    if (!control) {
      return;
    }

    //
    const handler = await this.getValueAsync(
      this.getDateChangeHandler(control)
    );
    if (!handler) {
      return;
    }

    //
    // Fire Hanlder ...
    await this.getValueAsync(handler(result));
  }
  //#endregion

  //
  //#region Auto Complete Controls UI Handlers ...
  async handleAutoCompleteResultSelected(
    control: XFormControlConfig,
    autoComplete: XFormControlAutoCompleteConfig,
    event: any
  ) {
    //
    if (!this.FORM || !this.FORM_CONFIG || !this.hasControl(control.propName)) {
      return;
    }

    //
    const formCtrl = this.getControl(control.propName);
    if (!formCtrl) {
      return;
    }

    //
    formCtrl.setValue(event.data);
    const formInputControl = this.getElementById(
      this.getControlInputElementId(control)
    );
    if (formInputControl) {
      (formInputControl as HTMLInputElement).select();
    }

    //
    if (autoComplete.onSelect && isFunction(autoComplete.onSelect)) {
      await this.getValueAsync(autoComplete.onSelect(event));
    }

    //
    this.focusOnControl(control.propName);

    //
    this.detectChanges();
  }

  async handleAutoCompleteEscaped(control: XFormControlConfig) {
    //
    if (!this.FORM || !this.FORM_CONFIG || !this.hasControl(control.propName)) {
      return;
    }

    //
    const autoComplete = await this.getValueAsync(
      this.getControlAutoCompleteConfig(control)
    );
    if (
      autoComplete &&
      autoComplete.onEscape &&
      isFunction(autoComplete.onEscape)
    ) {
      await this.getValueAsync(autoComplete.onEscape());
    }

    //
    this.detectChanges();
  }

  async handleAutoCompleteOutsideClicked(control: XFormControlConfig) {
    //
    if (!this.FORM || !this.FORM_CONFIG || !this.hasControl(control.propName)) {
      return;
    }

    //
    const autoComplete = await this.getValueAsync(
      this.getControlAutoCompleteConfig(control)
    );
    if (
      autoComplete &&
      autoComplete.onOutsideClicked &&
      isFunction(autoComplete.onOutsideClicked)
    ) {
      await this.getValueAsync(autoComplete.onOutsideClicked());
    }

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Markdown Control UI Handlers ...
  async handleMarkDownContentChanged(
    name: XFormControlNameIdentifier,
    event: string
  ) {
    //
    if (!this.hasControl(name)) {
      return;
    }

    //
    await this.setValue(name, event);

    //
    const controlConfig = await this.getValueAsync(this.getFormControl(name));
    if (!controlConfig) {
      return;
    }

    //
    const markDownConfig = await this.getValueAsync(
      this.getMarkdownConfig(controlConfig)
    );
    if (!markDownConfig || !markDownConfig.contentChanged) {
      return;
    }

    //
    await this.getValueAsync(markDownConfig.contentChanged(event));
  }
  //#endregion

  //
  //#region Checkbox Control UI Handlers ...
  async handleCheckboxCheckedChange(
    control: XFormControlConfig,
    checked: boolean
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getCheckboxConfig(control));
    if (!config || !config.checkedChanged) {
      return;
    }

    //
    await this.getValueAsync(config.checkedChanged(checked));
  }
  //#endregion

  //
  //#region Map UI Handlers ...
  async handleMapIsReady(control: XFormControlConfig) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getMapConfig(control));
    if (!config || !config.isReady) {
      return;
    }

    //
    config.isReady.emit();
  }

  async handleMapZoomChanged(control: XFormControlConfig, zoom: number) {
  }

  async handleMapLocationSelected(
    control: XFormControlConfig,
    event: XMapLocation
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const longLat = event ? toLongLat(event) : null;
    await this.setValue(control.propName, longLat);

    //
    const config = await this.getValueAsync(this.getMapConfig(control));
    if (!config || !config.locationSelected) {
      return;
    }

    //
    config.locationSelected.emit(event);
  }

  async handleMapIconClicked(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor,
    control: XFormControlConfig
  ) {
    //
    const presentType = await this.getValueAsync(
      this.getMapPresentType(control)
    );

    //
    switch (role) {
      //
      case this.ControlIconRoles.Clear:
        await this.managerService.dialogService.presentAreYouSureYesNoDialog(
          async () => {
            //
            await this.setValue(control.propName, undefined);

            //
            const mapControlActionPtovider = await this.getValueAsync(
              this.getMapActionProvider(control)
            );
            if (
              mapControlActionPtovider &&
              presentType === XFormMapControlPresentType.WithoutDialog
            ) {
              //
              // Clear Marker ...
              mapControlActionPtovider.next({
                action: XMapAction.ClearMarker,
              });
            }

            //
            // Notify Handler ...
            if (
              !isNullOrUndefined(descriptor) &&
              !isNullOrUndefined(descriptor.handler) &&
              isFunction(descriptor.handler)
            ) {
              await this.getValueAsync(descriptor.handler());
            }
          }
        );
        break;

      //
      case this.ControlIconRoles.Select:
        //
        // Notify Handler ...
        if (
          !isNullOrUndefined(descriptor) &&
          !isNullOrUndefined(descriptor.handler) &&
          isFunction(descriptor.handler)
        ) {
          await this.getValueAsync(descriptor.handler());
        }

        //
        let center: XMapLocation = null;
        const longLatControl = this.getControl(control.propName);
        if (longLatControl) {
          //
          const longLat = longLatControl.value as string;
          if (!isNullOrEmptyString(longLat)) {
            center = fromLongLat(longLat);
          }
        }

        //
        // Prevent go forward if map control present type does't support map dialog ...
        if (presentType === XFormMapControlPresentType.WithoutDialog) {
          return;
        }

        //
        // Prepare Selector Dialog Config ...
        const controlConfig = await this.getValueAsync(
          this.getMapConfig(control)
        );
        const dialogConfig: XMapDialogConfig = {
          ...controlConfig,
          //
          latitude: center && center.latitude ? +center.latitude : 0,
          longitude: center && center.longitude ? +center.longitude : 0,

          //
          showSelectedLocation: true,
        };

        //
        // Open Dialog ...
        await this.mapDialogService.openDialog(dialogConfig);

        //
        // Wait for Handle Dialog Result ...
        const s = this.mapDialogService.onResult
          .asObservable()
          .subscribe(async (result) => {
            //
            let value: any;

            //
            if (
              !isNullOrUndefined(result) &&
              !isNullOrUndefined(result.payload) &&
              result.result === XDialogResult.Ok
            ) {
              value = toLongLat(result.payload);
            }

            //
            if (!isNullOrUndefined(value)) {
              await this.setValue(control.propName, value);
            }

            //
            s.unsubscribe();
          });
        break;
    }
  }

  async handleMapControlInputValueChanged(
    control: XFormControlConfig,
    event: string
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getMapConfig(control));
    if (
      !config ||
      !config.inputValueChanged ||
      !isFunction(config.inputValueChanged)
    ) {
      return;
    }

    //
    await this.getValueAsync(config.inputValueChanged(event));
  }

  async handleMapSearchBarQueryChange(
    control: XFormControlConfig,
    event: string
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getMapConfig(control));
    if (!config || !config.searchBarQueryChange) {
      return;
    }

    //
    config.searchBarQueryChange.emit(event);
  }
  //#endregion

  //
  //#region ColorPicker UI Handlers ...
  async handleColorPickerIconClicked(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor,
    control: XFormControlConfig
  ) {
    //
    switch (role) {
      //
      case this.ControlIconRoles.Clear:
        await this.managerService.dialogService.presentAreYouSureYesNoDialog(
          async () => {
            //
            await this.setValue(control.propName, undefined);

            //
            // Notify Handler ...
            if (
              !isNullOrUndefined(descriptor) &&
              !isNullOrUndefined(descriptor.handler) &&
              isFunction(descriptor.handler)
            ) {
              await this.getValueAsync(descriptor.handler());
            }
          }
        );
        break;

      //
      case this.ControlIconRoles.Select:
        //
        // Notify Handler ...
        if (
          !isNullOrUndefined(descriptor) &&
          !isNullOrUndefined(descriptor.handler) &&
          isFunction(descriptor.handler)
        ) {
          await this.getValueAsync(descriptor.handler());
        }

        //
        const presentType = await this.getValueAsync(
          this.getColorPickerPresentType(control)
        );

        //
        // Prevent go forward if color picker control present type does't support map dialog ...
        if (presentType === XFormColorPickerControlPresentType.WithoutDialog) {
          return;
        }

        //
        let selectedColor: string = null;
        const colorPickerControl = this.getControl(control.propName);
        if (colorPickerControl) {
          //
          const colorVal = colorPickerControl.value as string;
          if (!isNullOrEmptyString(colorVal)) {
            selectedColor = colorVal;
          }
        }

        //
        // Prepare Selector Dialog Config ...
        const controlConfig = await this.getValueAsync(
          this.getColorPickerConfig(control)
        );
        const dialogConfig: XColorPickerDialogConfig = {
          ...controlConfig,

          //
          selectedColor,
        };

        //
        // Open Dialog ...
        await this.colorPickerDialogService.openDialog(dialogConfig);

        //
        // Wait for Handle Dialog Result ...
        const s = this.colorPickerDialogService.onResult
          .asObservable()
          .subscribe(async (result) => {
            //
            let value: any;

            //
            if (
              !isNullOrUndefined(result) &&
              !isNullOrUndefined(result.payload) &&
              result.result === XDialogResult.Ok
            ) {
              value = result.payload;
            }

            //
            if (!isNullOrUndefined(value)) {
              await this.setValue(control.propName, value);
            }

            //
            s.unsubscribe();
          });
        break;
    }
  }

  async handleColorPickerControlInputValueChanged(
    control: XFormControlConfig,
    event: string
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getColorPickerConfig(control));
    if (
      !config ||
      !config.inputValueChanged ||
      !isFunction(config.inputValueChanged)
    ) {
      return;
    }

    //
    await this.getValueAsync(config.inputValueChanged(event));
  }

  async handleColorPickerColorSelected(
    control: XFormControlConfig,
    event: string
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    await this.setValue(control.propName, event);

    //
    const config = await this.getValueAsync(this.getColorPickerConfig(control));
    if (!config || !config.colorSelected) {
      return;
    }

    //
    config.colorSelected.emit(event);
  }
  //#endregion

  //
  //#region Avatar UI Handlers ...
  async handleAvatarFileDropAreaFilesChangeEmitter(
    control: XFormControlConfig,
    event?: XOneOrManyType<File>
  ) {
    //
    if (!event || !this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getAvatarConfig(control));
    if (
      !config ||
      !config.fileDropAreaFilesChangeEmiter ||
      !isFunction(config.fileDropAreaFilesChangeEmiter)
    ) {
      return;
    }

    //
    await this.getValueAsync(config.fileDropAreaFilesChangeEmiter(event));
  }

  async handleAvatarAvatarChange(control: XFormControlConfig, event?: File) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    await this.setValue(control.propName, event);

    //
    const config = await this.getValueAsync(this.getAvatarConfig(control));
    if (!config || !config.avatarChange || !isFunction(config.avatarChange)) {
      return;
    }

    //
    await this.getValueAsync(config.avatarChange(event));
  }
  //#endregion

  //
  //#region Selector UI Handlers ...
  async handleItemSelectorIconClicked(
    role: XControlIconRoleIdentifier,
    descriptor: XControlIconDescriptor,
    control: XFormControlConfig
  ) {
    //
    switch (role) {
      //
      case this.ControlIconRoles.Clear:
        await this.managerService.dialogService.presentAreYouSureYesNoDialog(
          async () => {
            //
            await this.setValue(control.propName, undefined);

            //
            // Notify Handler ...
            if (
              !isNullOrUndefined(descriptor) &&
              !isNullOrUndefined(descriptor.handler) &&
              isFunction(descriptor.handler)
            ) {
              await this.getValueAsync(descriptor.handler());
            }
          }
        );
        break;

      //
      case this.ControlIconRoles.Select:
        //
        // Notify Handler ...
        if (
          !isNullOrUndefined(descriptor) &&
          !isNullOrUndefined(descriptor.handler) &&
          isFunction(descriptor.handler)
        ) {
          await this.getValueAsync(descriptor.handler());
        }

        //
        // Prepare Selector Dialog Config ...
        const controlConfig = await this.getValueAsync(
          this.getSelectorConfig(control)
        );
        const dialogConfig: XFormSelectorControlConfig = {
          ...controlConfig,
        };

        //
        // Open Dialog ...
        await this.selectorDialogService.openDialog(dialogConfig);

        //
        // Wait for Handle Dialog Result ...
        const s = this.selectorDialogService.onResult
          .asObservable()
          .subscribe(async (result) => {
            //
            let content: any;

            //
            if (
              !isNullOrUndefined(result) &&
              !isNullOrUndefined(result.payload) &&
              result.result === XDialogResult.Ok
            ) {
              content = result.payload;
            }

            //
            if (!isNullOrUndefined(content)) {
              //
              const value =
                !controlConfig ||
                !controlConfig.formatSelectedItem ||
                !isFunction(controlConfig.formatSelectedItem)
                  ? content
                  : await this.getValueAsync(
                      controlConfig.formatSelectedItem(content)
                    );

              //
              await this.setValue(control.propName, value);
            }

            //
            s.unsubscribe();
          });
        break;
    }
  }

  async handleSelectorControlInputValueChanged(
    control: XFormControlConfig,
    event: string
  ) {
    //
    if (!this.hasControl(control.propName)) {
      return;
    }

    //
    const config = await this.getValueAsync(this.getSelectorConfig(control));
    if (
      !config ||
      !config.inputValueChanged ||
      !isFunction(config.inputValueChanged)
    ) {
      return;
    }

    //
    await this.getValueAsync(config.inputValueChanged(event));
  }
  //#endregion
  //#endregion

  //
  //#region Actions ...
  /**
   * determine a control exists or not ...
   *
   * @param name a control name ...
   */
  hasControl(name: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return false;
    }

    //
    const controlName = this.getControlName(name);
    if (!controlName) {
      return false;
    }

    //
    return (
      this.FORM.contains(controlName) ||
      !isNullOrUndefined(this.FORM.controls[controlName])
    );
  }

  /**
   * retrieve a control based on it's name ...
   *
   * @param name a control name ...
   */
  getControl(name: XFormControlNameIdentifier) {
    //
    if (!this.hasControl(name)) {
      return null;
    }

    //
    const controlName = this.getControlName(name);
    if (!controlName) {
      return null;
    }

    //
    return this.FORM.get(controlName) || null;
  }

  /**
   * retrieve a form control status if exists in form ...
   *
   * @param control a form control config ...
   */
  getControlStatus(control: XFormControlConfig) {
    //
    const mControl = this.getControl(control.propName);
    if (!mControl) {
      return XFormStatus.UnKnown;
    }

    //
    return mControl.status as XFormStatus;
  }

  /**
   * focus on a control ...
   *
   * @param name a control name ...
   */
  focusOnControl(name: XFormControlNameIdentifier) {
    //
    if (!this.hasControl(name)) {
      return;
    }

    //
    const inputElement = this.getElementById(
      this.getControlInputElementIdByName(name)
    );
    if (!inputElement) {
      return;
    }

    //
    setTimeout(() => inputElement.focus(), 100);
  }

  /**
   * blur from a control ...
   *
   * @param name a control name ...
   */
  blurOfControl(name: XFormControlNameIdentifier) {
    //
    if (!this.hasControl(name)) {
      return;
    }

    //
    const inputElement = this.getElementById(
      this.getControlInputElementIdByName(name)
    );
    if (!inputElement) {
      return;
    }

    //
    setTimeout(() => inputElement.blur(), 100);
  }

  /**
   * determines specific control has value or not ...
   *
   * @param name a control name ...
   */
  controlHasValue(name: XFormControlNameIdentifier) {
    return !this.hasControl(name)
      ? false
      : this.getControl(name).value &&
          !isNullOrEmptyString(this.getControl(name).value.toString());
  }

  /**
   * retrieve an specific control value ...
   *
   * @param name a control name ...
   */
  getControlValue(name: XFormControlNameIdentifier) {
    return !this.hasControl(name) ? undefined : this.getControl(name).value;
  }

  /**
   * determines a control has error by it's name ...
   *
   * @param name a control name ...
   */
  controlHasErrorByControlName(name: XFormControlNameIdentifier) {
    return !this.hasControl(name)
      ? false
      : !isNullOrUndefined(this.getControl(name).errors);
  }

  /**
   * set a value to an specific control or form ...
   *
   * @param name a control name ...
   * @param value a value of any type ...
   */
  async setValue(name?: XFormControlNameIdentifier, value?: any) {
    //
    if (!name) {
      return;
    }

    //
    if (name) {
      //
      const control = this.getControl(name);
      if (!control) {
        return;
      }

      //
      control.setValue(value);
    } else {
      this.FORM.setValue(value, { emitEvent: true });
    }

    //
    this.detectChanges();
  }

  /**
   * trigger markAsDirty on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async markAsDirty(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG || this.uiDisabled) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.markAsDirty();
      this.detectChanges();
      return;
    }

    //
    control.markAsDirty();
    this.detectChanges();
  }

  /**
   * trigger markAsTouched on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async markAsTouched(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.markAsTouched();
      this.detectChanges();
      return;
    }

    //
    control.markAsTouched();
    this.detectChanges();
  }

  /**
   * trigger markAsPending on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async markAsPending(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.markAsPending();
      this.detectChanges();
      return;
    }

    //
    control.markAsPending();
    this.detectChanges();
  }

  /**
   * trigger markAsPristine on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async markAsPristine(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.markAsPristine();
      this.detectChanges();
      return;
    }

    //
    control.markAsPristine();
    this.detectChanges();
  }

  /**
   * trigger markAsUntouched on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async markAsUnTouched(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.markAsUntouched();
      this.detectChanges();
      return;
    }

    //
    control.markAsUntouched();
    this.detectChanges();
  }

  /**
   * trigger updateValueAndValidity on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async updateValueAndValidity(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.updateValueAndValidity({ emitEvent: true });
      this.detectChanges();
      return;
    }

    //
    control.updateValueAndValidity({ emitEvent: true });
    this.detectChanges();
  }

  /**
   * trigger setError on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param errors: an instance of ValidationErrors object ...
   * @param name a control name ...
   */
  async setError(errors: ValidationErrors, name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.setErrors(errors, { emitEvent: true });
      this.detectChanges();
      return;
    }

    //
    control.setErrors(errors, { emitEvent: true });
    this.detectChanges();
  }

  /**
   * clearErrors on form or specific control ...
   * if provide a control name and it's exists ...
   *
   * @param name a control name ...
   */
  async clearError(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.setErrors(null, { emitEvent: true });
      this.detectChanges();
      return;
    }

    //
    control.setErrors(null, { emitEvent: true });
    this.detectChanges();
  }

  /**
   * enable form or form control ...
   *
   * @param name a control name ...
   */
  async enable(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.enable({ emitEvent: true });
      this.detectChanges();
      return;
    }

    //
    control.enable({ emitEvent: true });
    this.detectChanges();
  }

  /**
   * disable form or form control ...
   *
   * @param name a control name ...
   */
  async disable(name?: XFormControlNameIdentifier) {
    //
    if (!this.FORM || !this.FORM_CONFIG) {
      return;
    }

    //
    const control = this.getControl(name);
    if (!control) {
      //
      this.FORM.disable({ emitEvent: true });
      this.detectChanges();
      return;
    }

    //
    control.disable({ emitEvent: true });
    this.detectChanges();
  }

  /**
   * remove all data and form elements ...
   */
  async clearData() {
    //
    // this.formConfig = null;
    this.FORM_CONFIG = null;
    this.FORM = null;

    //
    this.detectChanges();

    //
    await this.prepareFormGroup();
  }

  /**
   * refresh component ...
   */
  async refresh() {
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Prepare Form Group ...
  private async prepareFormGroup() {
    //
    //#region Form Config ...
    //
    this.currentInputFocus = undefined;
    const modelFormConfig = await this.getValueAsync(this.formConfig);
    if (!modelFormConfig) {
      return;
    }

    //
    const formConfig = {
      name: await this.getValueAsync(this.getFormName()),
      model: (await this.getValueAsync(this.formHasModel()))
        ? await this.getValueAsync(this.getFormModel())
        : undefined,
      updateOn: await this.getValueAsync(this.getFormUpdateOn()),
      controls: await Promise.all(
        (
          await this.getValueAsync(this.getFormControls())
        ).map(async (c) => {
          //
          //#region Action Provider ...
          let actionProvider = c.actionProvider ? c.actionProvider : undefined;

          //
          if (!actionProvider) {
            //
            if (!this.actionProviders[c.propName]) {
              this.actionProviders[c.propName] = new Subject<any>();
            }

            //
            actionProvider = this.actionProviders[c.propName];
          }
          //#endregion

          //
          //#region Control ...
          return {
            index: c.index,
            propName: c.propName,
            type: {
              type: await this.getValueAsync(this.getControlType(c)),
              config: await this.getValueAsync(this.getControlTypeConfig(c)),
            },
            updateOn: await this.getValueAsync(this.getControlUpdateOn(c)),
            validators: (await this.getValueAsync(this.controlHasValidators(c)))
              ? await this.getValueAsync(this.getControlValidators(c))
              : undefined,
            actionProvider,
            eventHandlers: (await this.getValueAsync(
              this.controlHasEventHandlers(c)
            ))
              ? await this.getValueAsync(this.getControlEventHandlers(c))
              : undefined,
            appearance: {
              hidden: await this.getValueAsync(this.getControlHidden(c)),
              disabled: await this.getValueAsync(this.getControlDisabled(c)),
              hint: await this.getValueAsync(this.getControlHint(c)),
              label: await this.getValueAsync(this.getControlLabel(c)),
              tooltip: await this.getValueAsync(this.getControlTooltip(c)),
              placeholder: await this.getValueAsync(
                this.getControlPlaceHolder(c)
              ),
              cssClass: await this.getValueAsync(this.getControlCssClass(c)),
              icons: (await this.getValueAsync(this.controlHasIcons(c)))
                ? {
                    suffix: (await this.getValueAsync(
                      this.controlHasSuffixIcon(c)
                    ))
                      ? {
                          name: await this.getValueAsync(
                            this.getControlIconName(
                              await this.getValueAsync(
                                this.getControlSuffixIcon(c)
                              )
                            )
                          ),
                          color: await this.getValueAsync(
                            this.getControlIconColor(
                              await this.getValueAsync(
                                this.getControlSuffixIcon(c)
                              )
                            )
                          ),
                          applyStateColor: await this.getValueAsync(
                            this.getControlIconApplyStateColor(
                              await this.getValueAsync(
                                this.getControlSuffixIcon(c)
                              )
                            )
                          ),
                          handler: (
                            await this.getValueAsync(
                              this.getControlSuffixIcon(c)
                            )
                          ).handler,
                        }
                      : undefined,
                    prefix: (await this.getValueAsync(
                      this.controlHasPrefixIcon(c)
                    ))
                      ? {
                          name: await this.getValueAsync(
                            this.getControlIconName(
                              await this.getValueAsync(
                                this.getControlPrefixIcon(c)
                              )
                            )
                          ),
                          color: await this.getValueAsync(
                            this.getControlIconColor(
                              await this.getValueAsync(
                                this.getControlPrefixIcon(c)
                              )
                            )
                          ),
                          applyStateColor: await this.getValueAsync(
                            this.getControlIconApplyStateColor(
                              await this.getValueAsync(
                                this.getControlPrefixIcon(c)
                              )
                            )
                          ),
                          handler: (
                            await this.getValueAsync(
                              this.getControlPrefixIcon(c)
                            )
                          ).handler,
                        }
                      : undefined,
                  }
                : undefined,
              formatter: (await this.getValueAsync(this.controlHasFormatter(c)))
                ? {
                    locale: await this.getValueAsync(
                      this.getFormatterLocale(c)
                    ),
                    seperator: await this.getValueAsync(
                      this.getFormatterSeperator(c)
                    ),
                    type: await this.getValueAsync(this.getFormatterType(c)),
                    currencyLable: await this.getValueAsync(
                      this.getFormatterCurrencyLabel(c)
                    ),
                    inputType: await this.getValueAsync(
                      this.getFormatterInputType(c)
                    ),
                    stringFormatter: await this.getValueAsync(
                      this.getFormatterStringFormatter(c)
                    ),
                  }
                : undefined,
              progressBar: (await this.getValueAsync(
                this.controlHasProgressBar(c)
              ))
                ? {
                    color: await this.getValueAsync(
                      this.getProgressBarColor(c)
                    ),
                  }
                : undefined,
              autoComplete: (await this.getValueAsync(
                this.controlHasAutoComplete(c)
              ))
                ? {
                    itemTemplate: await this.getValueAsync(
                      this.getAutoCompleteItemTemplate(c)
                    ),
                    opened: await this.getValueAsync(
                      this.getAutoCompleteOpened(c)
                    ),
                    color: await this.getValueAsync(
                      this.getAutoCompleteColor(c)
                    ),
                    onSelect: await this.getValueAsync(
                      this.getAutoCompleteOnSelect(c)
                    ),
                    items: await this.getValueAsync(
                      this.getAutoCompleteItems(c)
                    ),
                    cssClass: await this.getValueAsync(
                      this.getAutoCompleteCssClass(c)
                    ),
                  }
                : undefined,
              appearance: await this.getValueAsync(
                this.getControlAppearance(c)
              ),
              labelPosition: await this.getValueAsync(
                this.getControlLabelPosition(c)
              ),
            },
            errorHandlers: (await this.getValueAsync(
              this.controlHasErrorHandlers(c)
            ))
              ? await Promise.all(
                  (
                    await this.getValueAsync(this.getControlErrorHandlers(c))
                  ).map(async (e) => {
                    return {
                      errorName: await this.getValueAsync(e.errorName),
                      errorMessage: await this.getResourceValueAsync(
                        e.errorMessage
                      ),
                    };
                  })
                )
              : undefined,
          };
          //#endregion
        })
      ),
      validators: (await this.getValueAsync(this.formHasValidators()))
        ? await this.getValueAsync(this.getFormValidators())
        : undefined,
      errorHandlers: (await this.getValueAsync(this.formHasErrorHandlers()))
        ? await Promise.all(
            (
              await this.getValueAsync(this.getFormErrorHandlers())
            ).map(async (e) => {
              return {
                errorName: await this.getValueAsync(e.errorName),
                errorMessage: await this.getResourceValueAsync(e.errorMessage),
              };
            })
          )
        : undefined,
    };

    //
    if (!this.FORM_CONFIG) {
      this.FORM_CONFIG = formConfig;
    }
    //#endregion

    //
    //#region Preparing Form Controls ...
    const controls = assign(
      {},
      ...formConfig.controls
        .map((c) => {
          //
          const initialValue = {
            value:
              !isNullOrUndefined(formConfig.model) &&
              !isNullOrUndefined(formConfig.model[c.propName.toString()])
                ? formConfig.model[c.propName.toString()]
                : null,
            disabled:
              this.uiDisabled ||
              (c.appearance && c.appearance.disabled
                ? c.appearance.disabled
                : false),
          };

          //
          const formControl = new FormControl(initialValue, {
            updateOn: c.updateOn.toString() as 'change' | 'blur' | 'submit',
            validators: c.validators ? c.validators.validators : null,
            asyncValidators: c.validators ? c.validators.asyncValidators : null,
          });

          //
          //#region Apply Event Handlers ...
          //
          //#region Value Change ...
          formControl.valueChanges.subscribe(async (res) => {
            //
            const eventModel: XFormControlValueChangeEventModel = {
              name: c.propName,
              value: res,
            };

            //
            if (
              c.eventHandlers &&
              c.eventHandlers.valueChanged &&
              isFunction(c.eventHandlers.valueChanged)
            ) {
              c.eventHandlers.valueChanged(eventModel);
            }

            //
            await this.handleControlValueChanged(eventModel);

            //
            //#region Handle Map Control Input Value Change Action ...
            const controlConfig = await this.getValueAsync(
              this.getFormControl(c.propName)
            );
            if (controlConfig) {
              //
              const type = await this.getValueAsync(
                this.getControlType(controlConfig)
              );

              //
              if (type === XFormControlType.Map) {
                await this.handleMapControlInputValueChanged(
                  controlConfig,
                  res
                );
              } else if (type === XFormControlType.ColorPicker) {
                await this.handleColorPickerControlInputValueChanged(
                  controlConfig,
                  res
                );
              } else if (type === XFormControlType.Selector) {
                await this.handleSelectorControlInputValueChanged(
                  controlConfig,
                  res
                );
              }
            }
            //#endregion

            //
            this.detectChanges();
          });

          //
          // Notify Initial Value ...
          if (
            !isNullOrUndefined(initialValue) &&
            !isNullOrUndefined(initialValue.value)
          ) {
            //
            const eventModel: XFormControlValueChangeEventModel = {
              name: c.propName,
              value: initialValue.value,
            };

            //
            if (
              c.eventHandlers &&
              c.eventHandlers.valueChanged &&
              isFunction(c.eventHandlers.valueChanged)
            ) {
              c.eventHandlers.valueChanged(eventModel);
            }

            //
            this.controlValueChanged.emit(eventModel);
          }
          //#endregion

          //
          //#region Status Change ...
          formControl.statusChanges.subscribe(async (res) => {
            //
            const eventModel: XFormControlStatusChangeEventModel = {
              name: c.propName,
              status: res,
            };

            //
            if (
              c.eventHandlers &&
              c.eventHandlers.statusChanged &&
              isFunction(c.eventHandlers.statusChanged)
            ) {
              c.eventHandlers.statusChanged(eventModel);
            }

            //
            await this.handleControlStatusChanged(eventModel);

            //
            this.detectChanges();
          });
          //#endregion
          //#endregion

          //
          //#region Apply Action Provider ...
          if (c.actionProvider) {
            //
            c.actionProvider.asObservable().subscribe(async (model) => {
              //
              if (
                !model ||
                !model.name ||
                isNullOrEmptyString(model.name.toString()) ||
                !this.hasControl(model.name)
              ) {
                return;
              }

              //
              switch (model.action) {
                //
                case XFormBaseAction.Enable:
                  await this.enable(model.name);
                  break;

                //
                case XFormBaseAction.Disable:
                  await this.disable(model.name);
                  break;

                //
                case XFormBaseAction.SetError:
                  //
                  const errors = model.payload as ValidationErrors;
                  if (model.payload && errors) {
                    await this.setError(errors, model.name);
                  }
                  break;

                //
                case XFormBaseAction.ClearError:
                  await this.clearError(model.name);
                  break;

                //
                case XFormBaseAction.MarkAsDirty:
                  await this.markAsDirty(model.name);
                  break;

                //
                case XFormBaseAction.MarkAsTouched:
                  await this.markAsTouched(model.name);
                  break;

                //
                case XFormBaseAction.MarkAsPending:
                  await this.markAsPending(model.name);
                  break;

                //
                case XFormBaseAction.MarkAsPristine:
                  await this.markAsPending(model.name);
                  break;

                //
                case XFormBaseAction.MarkAsUnTouched:
                  await this.markAsTouched(model.name);
                  break;

                //
                case XFormBaseAction.UpdateValueAndValidity:
                  await this.updateValueAndValidity(model.name);
                  break;
              }
            });
          }
          //#endregion

          //
          return { name: c.propName, control: formControl };
        })
        .map((fc) => {
          return { key: fc.name.toString(), value: fc.control };
        })
    ) as { [key: string]: AbstractControl };
    //#endregion

    //
    //#region Prepare Form Group ...
    if (!this.FORM) {
      //
      this.FORM = new FormGroup(
        controls,
        formConfig.validators
          ? {
              updateOn: formConfig.updateOn.toString() as
                | 'change'
                | 'blur'
                | 'submit',
              validators: formConfig.validators.validators || null,
              asyncValidators: formConfig.validators.asyncValidators || null,
            }
          : null
      );

      //
      // Form Value Changed ...
      this.FORM.valueChanges.subscribe((res) => {
        //
        if (this.uiDisabled) {
          return;
        }

        //
        this.valueChanged.emit({
          name: formConfig.name,
          value: res,
        });

        //
        this.VALUE = res;

        //
        this.detectChanges();
      });

      //
      // Form Status Changed ...
      this.FORM.statusChanges.subscribe((res) => {
        //
        if (this.uiDisabled) {
          return;
        }

        //
        this.STATUS = res;

        //
        if (this.FORM.errors) {
          this.ERRORS = this.FORM.errors;
        } else {
          this.ERRORS = null;
        }

        //
        this.statusChanged.emit({
          name: formConfig.name,
          status: res,
        });

        //
        this.detectChanges();
      });
    } else {
      //
      entries(controls).forEach((ctrlEtry) => {
        //
        const name = ctrlEtry[0];
        const control = ctrlEtry[1] as AbstractControl;

        //
        if (this.FORM.contains(name)) {
          this.FORM.setControl(name, control);
        }
      });
    }
    //#endregion

    //
    this.FORM.markAllAsTouched();
    this.FORM.updateValueAndValidity();

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Control Config Object ...
  //
  private getControlName(name?: XFormControlNameIdentifier) {
    return name ? name.toString() : null;
  }

  //
  private getCustomControlConfig(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      concatMap((type) => {
        switch (type) {
          //
          case XFormControlType.Map:
            return this.getMapConfig(control);

          //
          case XFormControlType.Selector:
            return this.getSelectorConfig(control);

          //
          case XFormControlType.ColorPicker:
            return this.getColorPickerConfig(control);

          //
          default:
            return of(null);
        }
      })
    );
  }

  //
  private getControlInfo(control: XFormControlConfig) {
    return this.getControlType(control).pipe(
      concatMap((type) =>
        this.getControlDisabled(control).pipe(
          map((disabled) => ({ type, disabled }))
        )
      ),
      concatMap((typeInfo) =>
        this.getControlAppearance(control).pipe(
          map((appearance) => ({ ...typeInfo, appearance }))
        )
      )
    );
  }
  //#endregion

  //
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XFormActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XFormBaseAction.Enable:
            await this.enable(model.name);
            break;

          //
          case XFormBaseAction.Disable:
            await this.disable(model.name);
            break;

          //
          case XFormBaseAction.Refresh:
            await this.refresh();
            break;

          //
          case XFormBaseAction.SetError:
            //
            if (model.payload) {
              await this.setError(model.payload, model.name);
            }
            break;

          //
          case XFormBaseAction.ClearError:
            await this.clearError(model.name);
            break;

          //
          case XFormBaseAction.MarkAsDirty:
            await this.markAsDirty(model.name);
            break;

          //
          case XFormBaseAction.MarkAsTouched:
            await this.markAsTouched(model.name);
            break;

          //
          case XFormBaseAction.MarkAsPending:
            await this.markAsPending(model.name);
            break;

          //
          case XFormBaseAction.MarkAsPristine:
            await this.markAsPending(model.name);
            break;

          //
          case XFormBaseAction.MarkAsUnTouched:
            await this.markAsUnTouched(model.name);
            break;

          //
          case XFormBaseAction.UpdateValueAndValidity:
            await this.updateValueAndValidity(model.name);
            break;

          //
          case XFormBaseAction.SetValue:
            await this.setValue(model.name, model.payload);
            break;

          //
          case XFormBaseAction.ClearData:
            await this.clearData();
            break;

          //
          case XFormControlAction.Focuse:
            if (model.name) {
              this.focusOnControl(model.name);
            }
            break;

          //
          case XFormControlAction.Blur:
            if (model.name) {
              this.blurOfControl(model.name);
            }
            break;
        }
      });
  }

  //
  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }

  //
  private async handleUiDisabledChanged() {
    //
    if (this.uiDisabled) {
      await this.disable();
    } else {
      await this.enable();
    }
  }

  //
  //#region Color Appliers ...
  private async applyColors() {
    //
    await this.applyRegularColor();
    await this.applyAccentColor();
    await this.applyHintColor();
    await this.applyPendingColor();
    await this.applyActiveColor();
    await this.applyValidColor();
    await this.applyInvalidColor();
    await this.applyFillColor();
    await this.applyDisabledColor();
    await this.applyDisabledFillColor();
  }

  private async applyRegularColor() {
    //
    const color = await this.getValueAsync(this.regularColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.RegularColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.RegularColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyAccentColor() {
    //
    let color = await this.getValueAsync(this.accentColor);
    if (!color) {
      color = XColor.Secondary;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.AccentColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.AccentColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyHintColor() {
    //
    let color = await this.getValueAsync(this.hintColor);
    if (!color) {
      color = XColor.Tertiary;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.HintColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.HintColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyPendingColor() {
    //
    let color = await this.getValueAsync(this.pendingColor);
    if (!color) {
      color = XColor.Warning;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.PendingColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.PendingColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyActiveColor() {
    //
    let color = await this.getValueAsync(this.activeColor);
    if (!color) {
      color = XColor.Tertiary;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.ActiveColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.ActiveColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyValidColor() {
    //
    let color = await this.getValueAsync(this.validColor);
    if (!color) {
      color = XColor.Success;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.ValidColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.ValidColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyInvalidColor() {
    //
    let color = await this.getValueAsync(this.invalidColor);
    if (!color) {
      color = XColor.Danger;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.InvalidColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.InvalidColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyFillColor() {
    //
    let color = await this.getValueAsync(this.fillColor);
    if (!color) {
      color = XColorWithBrightness.SecondaryTint;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.FillColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.FillColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyDisabledColor() {
    //
    let color = await this.getValueAsync(this.disabledColor);
    if (!color) {
      color = XColorWithBrightness.LightTint;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.DisableColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.DisableColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }

  private async applyDisabledFillColor() {
    //
    let color = await this.getValueAsync(this.disabledFillColor);
    if (!color) {
      color = XColorWithBrightness.LightShade;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XFormCssVar.DisabledFillColor, `var(${bgColor})`);
    this.setCssVar(
      XFormCssVar.DisabledFillColor,
      `var(${bgColor})`,
      document.documentElement
    );

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\x-form.module.ts

import {
  LocaleFactory,
  XPersianDateAdapter,
  MatDateFormatFactory,
} from 'x-framework-core';
import {
  DateAdapter,
  MAT_DATE_LOCALE,
  MAT_DATE_FORMATS,
} from '@angular/material/core';
import { CommonModule } from '@angular/common';
import { XMapModule } from '../x-map/x-map.module';
import { LOCALE_ID, NgModule } from '@angular/core';
import { XFormComponent } from './x-form.component';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XListModule } from '../x-list/x-list.module';
import { XColorModule } from '../x-color/x-color.module';
import { XDialogsModule } from '../dialogs/x-dialog.module';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { XMarkdownModule } from '../x-markdown/x-markdown.module';
import { XDataPagerModule } from '../x-data-pager/x-data-pager.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XFileUploadModule } from '../x-file-upload/x-file-upload.module';
import { XColorPickerModule } from '../x-color-picker/x-color-picker.module';
import { XAvatarUploadModule } from '../x-avatar-upload/x-avatar-upload.module';
import { MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';

@NgModule({
  entryComponents: [],
  declarations: [XFormComponent],
  imports: [
    XMapModule,
    XCardModule,
    XListModule,
    XIconModule,
    FormsModule,
    CommonModule,
    XColorModule,
    XDialogsModule,
    XSlotterModule,
    XMarkdownModule,
    XDataPagerModule,
    XFileUploadModule,
    XColorPickerModule,
    ReactiveFormsModule,
    XAvatarUploadModule,
    XBaseComponentModule,
  ],
  exports: [
    XMapModule,
    XCardModule,
    XListModule,
    XIconModule,
    FormsModule,
    CommonModule,
    XColorModule,
    XDialogsModule,
    XFormComponent,
    XSlotterModule,
    XMarkdownModule,
    XDataPagerModule,
    XFileUploadModule,
    XColorPickerModule,
    ReactiveFormsModule,
    XAvatarUploadModule,
    XBaseComponentModule,
  ],
  providers: [
    {
      provide: LOCALE_ID,
      useFactory: LocaleFactory,
    },
    {
      provide: MAT_DATE_LOCALE,
      useFactory: LocaleFactory,
    },
    {
      deps: [MAT_DATE_LOCALE],
      provide: MAT_DATE_FORMATS,
      useFactory: MatDateFormatFactory,
    },
    {
      provide: DateAdapter,
      useClass: XPersianDateAdapter,
      deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS],
    },
  ],
})
export class XFormModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\x-form.tools.ts

import { XIconNames } from '../x-icon/x-icon.typings';
import { XColorWithBrightness } from 'x-framework-core';
import { XControlIconPosition, XControlIconDescriptor } from './x-form.typings';

export const defaultFormControlClearIcon: XControlIconDescriptor = {
  name: XIconNames.clear,
  applyStateColor: false,
  color: XColorWithBrightness.Danger,
  position: XControlIconPosition.Suffix,
}

export const defaultFormControlSelectIcon: XControlIconDescriptor = {
  name: XIconNames.touch,
  applyStateColor: false,
  color: XColorWithBrightness.Success,
  position: XControlIconPosition.Suffix,
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\x-form.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Renderer2,
  Component,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XLocale,
  XBaseDto,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XExceptionIdentifier,
  XColorWithBrightness,
  XBorderStyleIdentifier,
} from 'x-framework-core';
import {
  XManagerService,
  XFormInputFormatterIdentifier,
  XStringInputFormatTypeIdentifier,
} from 'x-framework-services';
import {
  XMarkdownActionModel,
  XMarkdownModeIdentifier,
  XMarkdownToolbarConfigProps,
} from '../x-markdown/x-markdown.typings';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import {
  XAvatarUploadActionModel,
  XAvatarUploadActionButton,
} from '../x-avatar-upload/x-avatar-upload.typings';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { AbstractControlOptions } from '@angular/forms';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { IXMapContainer, XMapActionModel } from '../x-map/x-map.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XMapDialogService } from '../x-map/x-map-dialog/x-map-dialog.service';
import { XColorPickerActionModel } from '../x-color-picker/x-color-picker.typings';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';
import { XFileDropAreaActionModel } from '../x-file-drop-area/x-file-drop-area.typings';
import { XSelectorDialogConfig } from '../dialogs/x-selector-dialog/x-selector-dialog.typings';
import { XSelectorDialogService } from '../dialogs/x-selector-dialog/x-selector-dialog.service';
import { XColorPickerDialogConfig } from '../x-color-picker/x-color-picker-dialog/x-color-picker-dialog.typings';
import { XColorPickerDialogService } from '../x-color-picker/x-color-picker-dialog/x-color-picker-dialog.service';

//
//#region Css Vars ...
export enum XFormCssVar {
  FillColor = 'fill-color',
  HintColor = 'hint-color',
  ValidColor = 'valid-color',
  ActiveColor = 'active-color',
  AccentColor = 'accent-color',
  RegularColor = 'regular-color',
  PendingColor = 'pending-color',
  InvalidColor = 'invalid-color',
  DisableColor = 'disabled-color',
  DisabledFillColor = 'disabled-fill-color',
}
//#endregion

//
//#region Base Form Control Action Model ...
export type XFormControlNameIdentifier = string | number | symbol;

export interface XBaseFormControlActionModel {
  name?: XFormControlNameIdentifier;
}
//#endregion

//
//#region Control Auto Complete Config ...
export interface XFormControlAutoCompleteConfig<T extends XBaseDto = any> {
  opened?: XStandardType<boolean>;
  color?: XStandardType<XColorIdentifier>;
  onSelect?: (item: T) => XStandardType<void>;
  items: () => XStandardType<XOneOrManyType<T>>;
  itemTemplate: XStandardType<TemplateRef<any>>;
  cssClass?: XStandardType<XOneOrManyType<string>>;
  onEscape?: () => XStandardType<void>;
  onOutsideClicked?: () => XStandardType<void>;
}
//#endregion

//
//#region XForm Validator Model ...
export type XFormValidatorDescriptor = Partial<
  Pick<AbstractControlOptions, 'validators' | 'asyncValidators'>
>;
//#endregion

//
//#region Error Handler Configs ...
export interface XFormErrorHandler {
  errorName: XStandardType<string>;
  errorMessage: XStandardType<XResourceIdentifier>;
  errorDataProvider?: (errorObject: any) => XStandardType<XResourceIdentifier>;
}
//#endregion

//
//#region Formatter Config ...
export interface XFormControlFormatterConfig {
  locale?: XStandardType<XLocale>;
  seperator?: XStandardType<string>;
  type: XStandardType<XFormInputFormatterIdentifier>;
  inputType?: XStandardType<XStringInputFormatTypeIdentifier>;
  currencyLable?: XStandardType<XResourceIdentifier>;
  stringFormatter?: XStandardType<XOneOrManyType<number>>;
}
//#endregion

//
//#region Control Progressbar Config ...
export interface XFormControlProgressbarConfig {
  color: XStandardType<XColorIdentifier>;
}
//#endregion

//
//#region XForm Control Appearance ...
export enum XFormControlAppearance {
  None = 'none',
  Legacy = 'legacy',
  Standard = 'standard',
  Fill = 'fill',
  Outline = 'outline',
}

export type XFormControlAppearanceIdentifier = XFormControlAppearance | string;

export interface XFormControlAppearanceDescriptor {
  hidden?: XStandardType<boolean>;
  disabled?: XStandardType<boolean>;
  hint?: XStandardType<XResourceIdentifier>;
  label: XStandardType<XResourceIdentifier>;
  tooltip?: XStandardType<XResourceIdentifier>;
  placeholder?: XStandardType<XResourceIdentifier>;
  cssClass?: XStandardType<XOneOrManyType<string>>;
  icons?: XStandardType<XFormControlIconDescriptor>;
  formatter?: XStandardType<XFormControlFormatterConfig>;
  progressBar?: XStandardType<XFormControlProgressbarConfig>;
  autoComplete?: XStandardType<XFormControlAutoCompleteConfig>;
  appearance?: XStandardType<XFormControlAppearanceIdentifier>;
  labelPosition?: XStandardType<XFormControlLabelPositionIdentifier>;
}
//#endregion

//
//#region Icon Configs ...
export enum XControlIconPosition {
  Prefix = 'prefix',
  Suffix = 'suffix',
}

export type XControlIconPositionIdentifier = XControlIconPosition | string;

export enum XControlIconRole {
  Clear = 'clear',
  Select = 'select',
}

export type XControlIconRoleIdentifier = XControlIconRole | string;

export interface XControlIconDescriptor {
  handler?: () => XStandardType<void>;
  name?: XStandardType<XIconIdentifier>;
  color?: XStandardType<XColorIdentifier>;
  applyStateColor?: XStandardType<boolean>;
  tooltip?: XStandardType<XResourceIdentifier>;
  position?: XStandardType<XControlIconPositionIdentifier>;
}

export interface XFormIconDescriptor
  extends Pick<
    Partial<XControlIconDescriptor>,
    'handler' | 'name' | 'color' | 'applyStateColor' | 'tooltip'
  > {}

export interface XFormControlIconDescriptor {
  suffix?: XStandardType<XFormIconDescriptor>;
  prefix?: XStandardType<XFormIconDescriptor>;
}
//#endregion

//
//#region Form Control Status and Values Changed ...
export interface XFormControlValueChangeEventModel
  extends XBaseFormControlActionModel {
  value: any;
}

export interface XFormControlStatusChangeEventModel
  extends XBaseFormControlActionModel {
  status: XFormStatusIdentifier;
}

// tslint:disable-next-line:no-empty-interface
export interface XFormValueChangeEventModel
  extends XFormControlValueChangeEventModel {}

// tslint:disable-next-line:no-empty-interface
export interface XFormStatusChangeEventModel
  extends XFormControlStatusChangeEventModel {}

//
// Form and Form Control Status ...
export enum XFormStatus {
  UnKnown = 'UNKNOWN',
  Valid = 'VALID',
  Invalid = 'INVALID',
  Pending = 'PENDING',
  Disabled = 'DISABLED',
}

export type XFormStatusIdentifier = XFormStatus | string;

export interface XFormControlEventsDescriptor {
  statusChanged?: (
    model: XFormControlStatusChangeEventModel
  ) => XStandardType<void>;
  valueChanged?: (
    model: XFormControlValueChangeEventModel
  ) => XStandardType<void>;
  onBlured?: (model: XBaseFormControlActionModel) => XStandardType<void>;
  onFocused?: (model: XBaseFormControlActionModel) => XStandardType<void>;
}
//#endregion

//
//#region Form Control Label Position ...
export enum XFormControlLabelPosition {
  After = 'after',
  Before = 'before',
}

export type XFormControlLabelPositionIdentifier =
  | XFormControlLabelPosition
  | string;
//#endregion

//
//#region Form and Form Control Action Provider ...
export enum XFormBaseAction {
  //
  MarkAsDirty = 'mark_as_dirty',
  MarkAsTouched = 'mark_as_touched',
  MarkAsPending = 'mark_as_pending',
  MarkAsPristine = 'mark_as_pristine',
  MarkAsUnTouched = 'mark_as_untouched',
  UpdateValueAndValidity = 'update_value_and_validity',
  //
  SetError = 'set_error',
  ClearError = 'clear_error',
  //
  SetValue = 'set_value',
  //
  Enable = 'enable',
  Disable = 'disable',
  //
  Refresh = 'refresh',
  //
  ClearData = 'clear_data',
}

export enum XFormControlAction {
  Focuse = 'focuse',
  Blur = 'blur',
}

export type XFormControlActionIdentifier =
  | XFormBaseAction
  | XFormControlAction
  | string;

export interface XFormControlActionProviderModel
  extends XBaseActionModel<XFormControlActionIdentifier>,
    XBaseFormControlActionModel {}

export interface XFormActionModel extends XFormControlActionProviderModel {}
//#endregion

//
//#region XForm and XForm Control Update On ...
export enum XFormUpdateOn {
  BLUR = 'blur',
  CHANGE = 'change',
  SUBMIT = 'submit',
}

export type XFormUpdateOnIdentifier = XFormUpdateOn | string;
//#endregion

//
//#region Form Control Types ...
export enum XFormControlType {
  Text = 'text',
  Number = 'number',
  Password = 'password',
  Color = 'color',
  Email = 'email',
  Tel = 'tel',
  Url = 'url',
  Select = 'select',
  Radio = 'radio',
  CheckBox = 'checkbox',
  Hidden = 'hidden',
  Date = 'date',
  Slider = 'slider',
  Toggle = 'toggle',
  MarkDown = 'markdown',
  Map = 'map',
  ColorPicker = 'color_picker',
  AvatarUpload = 'avatar_upload',
  Selector = 'selector',
}

export type XFormControlTypeIdentifier = XFormControlType | string;

export type XFormControlTypeConfig =
  | XFormMapControlConfig
  | XFormRadioControlConfig
  | XFormInputControlConfig
  | XFormSelectControlConfig
  | XFormSliderControlConfig
  | XFormSelectorControlConfig
  | XFormMarkdownControlConfig
  | XFormCheckBoxControlConfig
  | XFormDatePickerControlConfig
  | XFormColorPickerControlConfig
  | XFormAvatarUploadControlConfig
  | undefined;
export interface XFormControlTypeDescriptor {
  type: XStandardType<XFormControlTypeIdentifier>;
  config?: XStandardType<XFormControlTypeConfig>;
}

//
//#region Type Configs ...
//
//#region Input Type ...
export interface XFormInputControlConfig {
  readonly?: XStandardType<boolean>;
}
//#endregion

//
//#region Select Type ...
export interface XFormSelectControlOption<T extends XBaseDto = any> {
  value: XStandardType<T>;
  viewValue: XStandardType<string>;
}

export interface XFormSelectControlConfig<T extends XBaseDto = any> {
  options?: XStandardType<XOneOrManyType<XFormSelectControlOption<T>>>;
  multiple?: XStandardType<boolean>;
}
//#endregion

//
//#region Radio Type ...
export interface XFormRadioControlOption<T extends XBaseDto = any> {
  value: XStandardType<T>;
  viewValue: XStandardType<string>;
}

export interface XFormRadioControlConfig<T extends XBaseDto = any> {
  options?: XStandardType<XOneOrManyType<XFormRadioControlOption<T>>>;
}
//#endregion

//
//#region Date Control Config ...
export enum XFormDatePickerControlPickerPosition {
  Suffix = 'suffix',
  Prefix = 'prefix',
}

export type XFormDatePickerControlPickerPositionIdentifier =
  | XFormDatePickerControlPickerPosition
  | string;

export interface XFormDatePickerControlConfig {
  startAt?: XStandardType<Date>;
  dateFilter?: (d: any) => boolean;
  applyStateColor?: XStandardType<boolean>;
  toggleIcon?: XStandardType<XIconIdentifier>;
  iconColor?: XStandardType<XColorIdentifier>;
  datePickerPosition: XStandardType<XFormDatePickerControlPickerPositionIdentifier>;
  disableInput?: XStandardType<boolean>;
  disablePicker?: XStandardType<boolean>;
  iconTooltip?: XStandardType<XResourceIdentifier>;
  dateInput?: (event: Date) => XStandardType<void>;
  dateChange?: (event: Date) => XStandardType<void>;
}
//#endregion

//
//#region Slider Control ...
export interface XFormSliderControlConfig {
  min?: XStandardType<number>;
  max?: XStandardType<number>;
  step?: XStandardType<number>;
  invert?: XStandardType<boolean>;
  vertical?: XStandardType<boolean>;
  thumbLabel?: XStandardType<boolean>;
  formatter?: (value: number) => string;
}
//#endregion

//
//#region Markdown Control Config ...
export interface XFormMarkdownControlConfig {
  //
  isInPage?: XStandardType<boolean>;
  hasToolbar?: XStandardType<boolean>;
  wrapWithCard?: XStandardType<boolean>;
  cardColor?: XStandardType<XColorIdentifier>;
  cardForegroundColor?: XStandardType<boolean>;
  mode?: XStandardType<XMarkdownModeIdentifier>;
  toolbarColor?: XStandardType<XColorIdentifier>;
  cardTitle?: XStandardType<XResourceIdentifier>;
  actionProvider?: Subject<XMarkdownActionModel>;
  toolbarForegroundColor?: XStandardType<boolean>;
  cssClass?: XStandardType<XOneOrManyType<string>>;
  editorBoxColor?: XStandardType<XColorIdentifier>;
  previewBoxColor?: XStandardType<XColorIdentifier>;
  cardSubTitle?: XStandardType<XResourceIdentifier>;
  toolbarCssClass?: XStandardType<XOneOrManyType<string>>;
  contentChanged?: (content: string) => XStandardType<void>;
  toolbarConfig?: XStandardType<XMarkdownToolbarConfigProps>;
}
//#endregion

//
//#region  Check Box Config ...
export interface XFormCheckBoxControlConfig {
  checkedChanged?: (checked: boolean) => XStandardType<any>;
}
//#endregion

//
//#region Map Config ...
export enum XFormMapControlPresentType {
  WithDialog = 'width_dialog',
  WithoutDialog = 'without_dialog',
}

export type XFormMapControlPresentTypeIdentifier =
  | XFormMapControlPresentType
  | string;

//
export interface XFormMapControlConfig
  extends Pick<
    Partial<IXMapContainer>,
    | 'zoom'
    | 'markerStyle'
    | 'cssClass'
    | 'canSelect'
    //
    | 'showProgressBar'
    | 'progressBarColor'
    | 'progressBarPosition'
    //
    | 'showSearchBar'
    | 'searchBarType'
    | 'searchBarValue'
    | 'searchBarDebounce'
    | 'searchBarQueryChange'
    | 'searchBarSpellcheck'
    | 'searchPlaceProvider'
    | 'searchBarColor'
    | 'searchBarForegroundColor'
    | 'searchBarClearButtonIcon'
    | 'searchBarTitle'
    | 'searchBarSearchButtonIcon'
    | 'searchBarSearchInputColor'
    | 'searchBarForegroundSearchInputColor'
    | 'searchResultListColor'
    | 'searchBarActionProvider'
    | 'searchBarClearButtonIconColor'
    | 'searchBarCssClass'
    | 'searchBarSearchButtonIconColor'
    | 'searchBarCancelButtonIcon'
    | 'searchBarPosition'
    | 'searchBarKeyboardType'
    | 'searchBarCancelButtonTitle'
    | 'searchBarEnterKeyHint'
    | 'searchBarShowCancelButton'
    //
    | 'showSelectedLocation'
    | 'selectedLocationPosition'
    //
    | 'showGoMarkedPlace'
    | 'goMarkedPlacePosition'
    //
    | 'showClearMarker'
    | 'clearMarkerPosition'
    //
    | 'showLocator'
    | 'locatorPosition'
    //
    | 'showZoom'
    | 'showRotate'
    | 'showZoomSlider'
    //
    | 'isReady'
    | 'locationSelected'
    //
    | 'isInPage'
    | 'wrapWithCard'
    | 'cardShowFooter'
    | 'cardColor'
    | 'cardForegroundColor'
    | 'cardTitle'
    | 'cardSubTitle'
  > {
  //
  disableIcon?: XStandardType<boolean>;
  disableInput?: XStandardType<boolean>;
  actionProvider?: Subject<XMapActionModel>;
  clearIcon?: XStandardType<XControlIconDescriptor>;
  selectIcon?: XStandardType<XControlIconDescriptor>;
  inputCssClass?: XStandardType<XOneOrManyType<string>>;
  inputValueChanged?: (value?: string) => XStandardType<string>;
  presentType?: XStandardType<XFormMapControlPresentTypeIdentifier>;
}
//#endregion

//
//#region ColorPicker Config ...
export enum XFormColorPickerControlPresentType {
  WithDialog = 'width_dialog',
  WithoutDialog = 'without_dialog',
  // Both = 'both',
}

export type XFormColorPickerControlPresentTypeIdentifier =
  | XFormColorPickerControlPresentType
  | string;

//
export interface XFormColorPickerControlConfig
  extends Pick<
    Partial<XColorPickerDialogConfig>,
    | 'color'
    | 'cssClass'
    | 'cardColor'
    | 'cardTitle'
    | 'cursorColor'
    | 'presetColors'
    | 'outputFormat'
    | 'cardSubTitle'
    | 'colorSelected'
    | 'colorTextColor'
    | 'showColorValue'
    | 'showHueChannel'
    | 'showColorInput'
    | 'foregroundColor'
    | 'showPresetColors'
    | 'showAlphaChannel'
    | 'presetColorsLable'
    | 'showPaletteChannel'
    | 'cardForegroundColor'
    | 'showCopyToClipboard'
    | 'colorInputIconLabel'
    | 'colorInputIconColor'
    | 'clearColorIconColor'
    | 'clearColorIconLabel'
    | 'representColorFormats'
    | 'presetColorsLableColor'
    | 'copyToClipboardIconLabel'
    | 'copyToClipboardIconColor'
    | 'showSelectedColorPreview'
  > {
  //
  isInPage?: XStandardType<boolean>;
  disableIcon?: XStandardType<boolean>;
  disableInput?: XStandardType<boolean>;
  wrapWithCard?: XStandardType<boolean>;
  showCardFooter?: XStandardType<boolean>;
  colorViewBorderWidth?: XStandardType<number>;
  actionProvider?: Subject<XColorPickerActionModel>;
  clearIcon?: XStandardType<XControlIconDescriptor>;
  selectIcon?: XStandardType<XControlIconDescriptor>;
  inputCssClass?: XStandardType<XOneOrManyType<string>>;
  colorViewBorderColor?: XStandardType<XColorIdentifier>;
  colorViewBorderStyle?: XStandardType<XBorderStyleIdentifier>;
  inputValueChanged?: (value?: string) => XStandardType<string>;
  presentType?: XStandardType<XFormColorPickerControlPresentTypeIdentifier>;
}
//#endregion

//
//#region Avatar Upload Config ...
export interface XFormAvatarUploadControlConfig {
  fileDropAreaFilesChangeEmiter?: (
    files?: XOneOrManyType<File>
  ) => XStandardType<void>;
  canDrop?: XStandardType<boolean>;
  isInPage?: XStandardType<boolean>;
  wrapWithCard?: XStandardType<boolean>;
  color?: XStandardType<XColorIdentifier>;
  showCardFooter?: XStandardType<boolean>;
  foregroundColor?: XStandardType<boolean>;
  placeHolderAvatar?: XStandardType<string>;
  minAllowedFileSize?: XStandardType<number>;
  maxAllowedFileSize?: XStandardType<number>;
  cardColor?: XStandardType<XColorIdentifier>;
  cardForegroundColor?: XStandardType<boolean>;
  fileDropAreaIsInPage?: XStandardType<boolean>;
  cardTitle?: XStandardType<XResourceIdentifier>;
  cssClass?: XStandardType<XOneOrManyType<string>>;
  fileDropAreaWrapWithCard?: XStandardType<boolean>;
  fileDropAreaIcon?: XStandardType<XIconIdentifier>;
  fileDropAreaDragDisabled?: XStandardType<boolean>;
  cardSubTitle?: XStandardType<XResourceIdentifier>;
  actionProvider?: Subject<XAvatarUploadActionModel>;
  fileDropAreaShowCardFooter?: XStandardType<boolean>;
  fileDropAreaColor?: XStandardType<XColorIdentifier>;
  fileDropAreaForegroundColor?: XStandardType<boolean>;
  avatarChange?: (avatar?: File) => XStandardType<void>;
  fileDropAreaDisableFileCheck?: XStandardType<boolean>;
  fileDropAreaCardColor?: XStandardType<XColorIdentifier>;
  fileDropAreaCardForegroundColor?: XStandardType<boolean>;
  emptyFileException?: XStandardType<XExceptionIdentifier>;
  fileDropAreaErrorColor?: XStandardType<XColorIdentifier>;
  fileDropAreaMessage?: XStandardType<XResourceIdentifier>;
  fileDropAreaAccentColor?: XStandardType<XColorIdentifier>;
  allowedExtensions?: XStandardType<XOneOrManyType<string>>;
  fileDropAreaCardTitle?: XStandardType<XResourceIdentifier>;
  fileDropAreaSuccessColor?: XStandardType<XColorIdentifier>;
  fileDropAreaSupportsMultipleFiles?: XStandardType<boolean>;
  fileDropAreaCssClass?: XStandardType<XOneOrManyType<string>>;
  fileDropAreaCardSubTitle?: XStandardType<XResourceIdentifier>;
  notAllowedFileException?: XStandardType<XExceptionIdentifier>;
  fileDropAreaContentTemplate?: XStandardType<TemplateRef<any>>;
  fileDropAreaActionProvider?: Subject<XFileDropAreaActionModel>;
  minAllowedFileSizeException?: XStandardType<XExceptionIdentifier>;
  maxAllowedFileSizeException?: XStandardType<XExceptionIdentifier>;
  actions?: XStandardType<XOneOrManyType<XAvatarUploadActionButton>>;
  fileDropAreaAllowedExtensions?: XStandardType<XOneOrManyType<string>>;
}
//#endregion

//
//#region Selector Config ...
export interface XFormSelectorControlConfig
  extends Partial<XSelectorDialogConfig> {
  //
  clearIcon?: XStandardType<XControlIconDescriptor>;
  selectIcon?: XStandardType<XControlIconDescriptor>;
  inputCssClass?: XStandardType<XOneOrManyType<string>>;
  inputValueChanged?: (value?: string) => XStandardType<string>;
  formatSelectedItem?: (item: XOneOrManyType<any>) => XStandardType<string>;
}
//#endregion
//#endregion
//#endregion

//
//#region Form Control Config ...
export interface XFormControlConfig<T extends XBaseDto = any> {
  index: number;
  propName: keyof Partial<T>;
  focusHandler?: Subject<any>;
  type: XStandardType<XFormControlTypeDescriptor>;
  updateOn?: XStandardType<XFormUpdateOnIdentifier>;
  validators?: XStandardType<XFormValidatorDescriptor>;
  actionProvider?: Subject<XFormControlActionProviderModel>;
  eventHandlers?: XStandardType<XFormControlEventsDescriptor>;
  appearance: XStandardType<XFormControlAppearanceDescriptor>;
  errorHandlers?: XStandardType<XOneOrManyType<XFormErrorHandler>>;
}
//#endregion

//
// Form Config ...
export interface XFormConfig<T extends XBaseDto = any> {
  name: XStandardType<string>;
  model?: XStandardType<Partial<T>>;
  updateOn?: XStandardType<XFormUpdateOnIdentifier>;
  validators?: XStandardType<XFormValidatorDescriptor>;
  controls: XStandardType<XOneOrManyType<XFormControlConfig<T>>>;
  errorHandlers?: XStandardType<XOneOrManyType<XFormErrorHandler>>;
}

//
//#region Inside Card Container ...
export interface IXFormContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XFormActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;
  formConfig: XStandardType<XFormConfig>;
  applyStatusOnControls: XStandardType<boolean>;

  //
  regularColor: XStandardType<XColorIdentifier>;
  accentColor: XStandardType<XColorIdentifier>;
  hintColor: XStandardType<XColorIdentifier>;
  activeColor: XStandardType<XColorIdentifier>;
  validColor: XStandardType<XColorIdentifier>;
  invalidColor: XStandardType<XColorIdentifier>;
  pendingColor: XStandardType<XColorIdentifier>;
  fillColor: XStandardType<XColorIdentifier>;
  disabledColor: XStandardType<XColorIdentifier>;
  disabledFillColor: XStandardType<XColorIdentifier>;

  //
  valueChanged: EventEmitter<XFormValueChangeEventModel>;
  statusChanged: EventEmitter<XFormStatusChangeEventModel>;
  controlFocused: EventEmitter<XBaseFormControlActionModel>;
  controlBlured: EventEmitter<XBaseFormControlActionModel>;
  controlValueChanged: EventEmitter<XFormControlValueChangeEventModel>;
  controlStatusChanged: EventEmitter<XFormControlStatusChangeEventModel>;
}

@Injectable({
  providedIn: 'root',
})
export class XFormContainer
  extends XComponentCardContainer
  implements Readonly<IXFormContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XFormActionModel> =
    new Subject<XFormActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;
  readonly formConfig: XStandardType<XFormConfig> = null;
  readonly applyStatusOnControls: XStandardType<boolean> = false;

  //
  readonly regularColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly accentColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Secondary;
  readonly hintColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Tertiary;
  readonly activeColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Tertiary;
  readonly validColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Success;
  readonly invalidColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Danger;
  readonly pendingColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;
  readonly fillColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.SecondaryTint;
  readonly disabledColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightTint;
  readonly disabledFillColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightShade;

  //
  readonly valueChanged: EventEmitter<XFormValueChangeEventModel> =
    new EventEmitter<XFormValueChangeEventModel>();
  readonly statusChanged: EventEmitter<XFormStatusChangeEventModel> =
    new EventEmitter<XFormStatusChangeEventModel>();
  readonly controlFocused: EventEmitter<XBaseFormControlActionModel> =
    new EventEmitter<XBaseFormControlActionModel>();
  readonly controlBlured: EventEmitter<XBaseFormControlActionModel> =
    new EventEmitter<XBaseFormControlActionModel>();
  readonly controlValueChanged: EventEmitter<XFormControlValueChangeEventModel> =
    new EventEmitter<XFormControlValueChangeEventModel>();
  readonly controlStatusChanged: EventEmitter<XFormControlStatusChangeEventModel> =
    new EventEmitter<XFormControlStatusChangeEventModel>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XFormContainerComponent
  extends XComponentCardContainerComponent
  implements IXFormContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XFormActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  formConfig: XStandardType<XFormConfig> = this.propertyProvider.formConfig;

  @Input()
  applyStatusOnControls: XStandardType<boolean> =
    this.propertyProvider.applyStatusOnControls;

  //
  @Input()
  regularColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.regularColor;

  @Input()
  accentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.accentColor;

  @Input()
  hintColor: XStandardType<XColorIdentifier> = this.propertyProvider.hintColor;

  @Input()
  activeColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.activeColor;

  @Input()
  validColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.validColor;

  @Input()
  invalidColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.invalidColor;

  @Input()
  pendingColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.pendingColor;

  @Input()
  fillColor: XStandardType<XColorIdentifier> = this.propertyProvider.fillColor;

  @Input()
  disabledColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.disabledColor;

  @Input()
  disabledFillColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.disabledFillColor;

  //
  @Output()
  valueChanged: EventEmitter<XFormValueChangeEventModel> = new EventEmitter();

  @Output()
  statusChanged: EventEmitter<XFormStatusChangeEventModel> = new EventEmitter();

  @Output()
  controlFocused: EventEmitter<XBaseFormControlActionModel> = new EventEmitter();

  @Output()
  controlBlured: EventEmitter<XBaseFormControlActionModel> = new EventEmitter();

  @Output()
  controlValueChanged: EventEmitter<XFormControlValueChangeEventModel> = new EventEmitter();

  @Output()
  controlStatusChanged: EventEmitter<XFormControlStatusChangeEventModel> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFormContainer,
    protected mapDialogService: XMapDialogService,
    protected selectorDialogService: XSelectorDialogService,
    protected colorPickerDialogService: XColorPickerDialogService
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXFormContainer
  ): Observable<IXFormContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            formConfig: this.getValue(
              container ? container.formConfig : this.formConfig
            ),
            applyStatusOnControls: this.getValue(
              container
                ? container.applyStatusOnControls
                : this.applyStatusOnControls
            ),
            regularColor: this.getValue(
              container ? container.regularColor : this.regularColor
            ),
            accentColor: this.getValue(
              container ? container.accentColor : this.accentColor
            ),
            hintColor: this.getValue(
              container ? container.hintColor : this.hintColor
            ),
            activeColor: this.getValue(
              container ? container.activeColor : this.activeColor
            ),
            validColor: this.getValue(
              container ? container.validColor : this.validColor
            ),
            invalidColor: this.getValue(
              container ? container.invalidColor : this.invalidColor
            ),
            pendingColor: this.getValue(
              container ? container.pendingColor : this.pendingColor
            ),
            fillColor: this.getValue(
              container ? container.fillColor : this.fillColor
            ),
            disabledColor: this.getValue(
              container ? container.disabledColor : this.disabledColor
            ),
            disabledFillColor: this.getValue(
              container ? container.disabledFillColor : this.disabledFillColor
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            valueChanged: this.valueChanged,
            statusChanged: this.statusChanged,
            controlFocused: this.controlFocused,
            controlBlured: this.controlBlured,
            controlValueChanged: this.controlValueChanged,
            controlStatusChanged: this.controlStatusChanged,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-form\validators\x-form-validators.ts

import {
  isValidURL,
  XCountryCode,
  isNullOrUndefined,
  isValidPhoneNumber,
  isNullOrEmptyString,
  toNumber,
  toXString,
} from 'x-framework-core';
import { ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';

export class XFormValidators {
  /**
   * check given date in between start and end date ...
   *
   * @param startDate start date for validating
   * @param endDate end date for validating
   */
  static validateDate(
    startDate: Date | string,
    endDate: Date | string
  ): ValidatorFn {
    //
    const errorObject = {
      startDate,
      endDate,
      currentDate: new Date(),
      xValidateDate: true,
    };

    //
    const validatorFn = (control: AbstractControl): ValidationErrors | null => {
      //
      if (
        isNullOrEmptyString(control.value) ||
        isNullOrUndefined(startDate) ||
        isNullOrUndefined(endDate)
      ) {
        return null;
      }

      //
      const mStartDate = new Date(startDate);
      const mEndDate = new Date(endDate);
      const mDate = new Date(control.value);

      //
      const isValid = mDate >= mStartDate && mDate <= mEndDate;
      if (!isValid) {
        //
        errorObject.currentDate = mDate;
        return errorObject;
      }

      //
      return null;
    };

    //
    return validatorFn;
  }

  /**
   *  check given value be a valid phone number ...
   *
   * @param defaultCountryCode an optional value which used for country code if not exists in value
   */
  static validatePhoneNumber(defaultCountry?: XCountryCode): ValidatorFn {
    //
    const errorObject = {
      xPhoneNumberValidator: true,
    };

    //
    const validatorFn = (control: AbstractControl): ValidationErrors | null => {
      //
      if (isNullOrEmptyString(control.value)) {
        return null;
      }

      //
      const value = control.value as string;
      const isValid = isValidPhoneNumber(value, defaultCountry);

      //
      if (!isValid) {
        return errorObject;
      }

      //
      return null;
    };

    //
    return validatorFn;
  }

  /**
   * check given value is Valid URL or not ...
   */
  static validateUrl(): ValidatorFn {
    //
    const errorObject = {
      xUrlValidator: true,
    };

    //
    const validatorFn = (control: AbstractControl): ValidationErrors | null => {
      //
      if (isNullOrEmptyString(control.value)) {
        return null;
      }

      //
      const value = control.value as string;
      const isValid = isValidURL(value);

      //
      if (!isValid) {
        return errorObject;
      }

      //
      return null;
    };

    //
    return validatorFn;
  }

  /**
   * check give value notEquals to Zero ...
   * usually works for id's in Selector Component ...
   */
  static validateNotZero(): ValidatorFn {
    //
    const errorObject = {
      xNotZeroValidator: true,
    };

    //
    const validatorFn = (control: AbstractControl): ValidationErrors | null => {
      //
      if (isNullOrEmptyString(control.value)) {
        return null;
      }

      //
      const value = control.value as string;
      const isValid =
        !isNullOrEmptyString(value) &&
        (toXString(value) !== '0' || toNumber(value) !== 0);

      //
      if (!isValid) {
        return errorObject;
      }

      //
      return null;
    };

    //
    return validatorFn;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-frame\x-frame.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="v-card v-no-margin-card"
    [color]="getValue(cardColor) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [ngClass]="getValue(cardCssClass) | async"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [subTitle]="getResourceValue(cardSubTitle) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <div [ngClass]="getCssClasses('container') | async">
    <!-- iFrame By ContentURL -->
    <ng-container *ngIf="notEmptyValue(contentUrl) | async">
      <iframe
        [id]="id"
        #frameView
        scrolling="no"
        [src]="contentUrl"
        allowtransparency="true"
        (load)="onContentLoaded.emit($event)"
        *ngIf="notEmptyValue(contentUrl) | async"
        [ngClass]="getCssClasses('frame') | async"
      ></iframe>
    </ng-container>

    <!-- iFrame By Content -->
    <ng-container *ngIf="notEmptyValue(content) | async">
      <iframe
        [id]="id"
        #frameView
        scrolling="no"
        [srcdoc]="contentHTML"
        allowtransparency="true"
        *ngIf="notEmptyValue(content) | async"
        (load)="onContentLoaded.emit($event)"
        [ngClass]="getCssClasses('frame') | async"
      ></iframe>
    </ng-container>
  </div>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-frame\x-frame.component.scss

:host {
    --x-frame-height: auto;
  }
  
  .v-frame-frame,
  .v-frame-container {
    width: 100%;
    height: var(--x-frame-height);
  }
  
  .v-frame-frame {
    border: unset;
  }
  

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-frame\x-frame.component.ts

import {
  Component,
  ViewChild,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  generateUuid,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XFrameAction,
  XFrameActionModel,
  XFrameContainerComponent,
} from './x-frame.typings';
import { Subject, Subscription } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { SafeHtml, SafeResourceUrl } from '@angular/platform-browser';

@Component({
  selector: 'x-frame',
  templateUrl: './x-frame.component.html',
  styleUrls: ['./x-frame.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XFrameComponent extends XFrameContainerComponent {
  //
  //#region Props ...
  //
  readonly id = generateUuid('VFrame');

  //
  private ORIGIN: string = '';
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private FRAME_VIEW: ElementRef<HTMLIFrameElement> = null;

  //
  @ViewChild('frameView', { static: false })
  set frameView(v: ElementRef<HTMLIFrameElement>) {
    //
    if (isNullOrUndefined(v)) {
      return;
    }

    //
    this.FRAME_VIEW = v;
    this.applyFrameProps();
  }

  //
  //#region Getters ...
  get iFrame() {
    return this.FRAME_VIEW!.nativeElement;
  }

  get contentWindow() {
    return this.iFrame!.contentWindow;
  }

  get contentDocuent() {
    return this.iFrame!.contentDocument;
  }

  get innerHTML() {
    return this.iFrame!.innerHTML;
  }

  get innerText() {
    return this.iFrame!.innerText;
  }

  get frameView() {
    return this.FRAME_VIEW;
  }

  get origin() {
    return this.ORIGIN;
  }
  //#endregion

  //
  contentHTML: SafeHtml;
  contentUrl: SafeResourceUrl;
  //#endregion

  //
  //#region Constructor ...
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Action Providers ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XFrameComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Url Changed ...
    const isUrlChanged = changeKeys.includes(nameof<XFrameComponent>('url'));
    if (isUrlChanged) {
      this.prepareContentUrl();
    }
    //#endregion

    //
    //#region Content ...
    const isContentChanged = changeKeys.includes(
      nameof<XFrameComponent>('content')
    );
    if (isContentChanged) {
      this.prepareContent();
    }
    //#endregion

    //
    //#region VFrame Props ...
    //
    //#region Allow ...
    const isAllowChanged = changeKeys.includes(
      nameof<XFrameComponent>('allow')
    );
    if (isAllowChanged) {
      this.handleAllowChanged();
    }
    //#endregion

    //
    //#region FrameLoading ...
    const isFrameLoadingChanged = changeKeys.includes(
      nameof<XFrameComponent>('frameLoading')
    );
    if (isFrameLoadingChanged) {
      this.handleFrameLoadingChanged();
    }
    //#endregion

    //
    //#region FrameReferrerPolicy ...
    const isReferrerPolicyChanged = changeKeys.includes(
      nameof<XFrameComponent>('referrerPolicy')
    );
    if (isReferrerPolicyChanged) {
      this.handleReferrerPolicyChanged();
    }
    //#endregion

    //
    //#region FrameSandbox ...
    const isSandboxChanged = changeKeys.includes(
      nameof<XFrameComponent>('sandbox')
    );
    if (isSandboxChanged) {
      this.handleSandboxChanged();
    }
    //#endregion

    //
    //#region AllowFullScreen ...
    const isAllowFullScreenChanged = changeKeys.includes(
      nameof<XFrameComponent>('allowFullscreen')
    );
    if (isAllowFullScreenChanged) {
      this.handleAllowFullScreenChanged();
    }
    //#endregion

    //
    //#region AllowPaymentRequest ...
    const isAllowPaymentRequestChanged = changeKeys.includes(
      nameof<XFrameComponent>('allowPaymentRequest')
    );
    if (isAllowPaymentRequestChanged) {
      this.handleAllowPaymentRequestChanged();
    }
    //#endregion

    //
    //#region AddAllowTransparency ...
    if (
      isAllowChanged ||
      isSandboxChanged ||
      isFrameLoadingChanged ||
      isReferrerPolicyChanged ||
      isAllowFullScreenChanged ||
      isAllowPaymentRequestChanged
    ) {
      this.addTransparency();
    }
    //#endregion
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  registerInstanceHandlers() {
    super.registerInstanceHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Task Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  getCssClasses(infix: string) {
    return this.getValue(false).pipe(
      concatMap(() => this.getArrayValue(this.cssClass)),
      map((classes) => {
        //
        const result: string[] = [];

        //
        result.push(`v-frame-${infix}`);
        result.push(`v-frame-${this.id}-${infix}`);

        //
        if (this.hasChild(classes)) {
          result.push(...classes);
        }

        //
        return result;
      })
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  //#endregion

  //
  //#region Actions ...
  appendElement(element: HTMLElement) {
    //
    if (
      isNullOrUndefined(element) ||
      isNullOrUndefined(this.frameView) ||
      isNullOrUndefined(this.frameView.nativeElement)
    ) {
      return;
    }

    //
    this.appendChild(this.frameView.nativeElement, element);
  }

  loadHtml(html: string) {
    //
    if (
      isNullOrEmptyString(html) ||
      isNullOrUndefined(this.frameView) ||
      isNullOrUndefined(this.frameView.nativeElement)
    ) {
      return;
    }

    //
    this.frameView.nativeElement.contentWindow.frames.document.body.insertAdjacentHTML(
      'beforebegin',
      html
    );
  }

  loadText(text: string) {
    //
    if (
      isNullOrEmptyString(text) ||
      isNullOrUndefined(this.frameView) ||
      isNullOrUndefined(this.frameView.nativeElement)
    ) {
      return;
    }

    //
    this.frameView.nativeElement.contentWindow.frames.document.body.insertAdjacentText(
      'beforebegin',
      text
    );
  }

  loadElement(element: HTMLElement) {
    //
    if (
      isNullOrUndefined(element) ||
      isNullOrUndefined(this.frameView) ||
      isNullOrUndefined(this.frameView.nativeElement)
    ) {
      return;
    }

    //
    this.frameView.nativeElement.contentWindow.frames.document.body.insertAdjacentElement(
      'beforebegin',
      element
    );
  }

  applyUrl(url: string) {
    //
    this.contentUrl = url;
    this.detectChanges();
  }

  applyContent(content: string) {
    //
    this.content = content;
    this.detectChanges();
  }

  fullScreen(options?: FullscreenOptions) {
    this.frameView.nativeElement.requestFullscreen(options);
  }
  //#endregion

  //
  //#region Abstracts ...
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XFrameActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      // tslint:disable-next-line: deprecation
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XFrameAction.Refresh:
            this.detectChanges();
            break;

          //
          case XFrameAction.AppendElement:
            this.appendElement(model.payload);
            break;

          //
          case XFrameAction.ApplyContent:
            this.applyContent(model.payload);
            break;

          //
          case XFrameAction.ApplyUrl:
            this.applyUrl(model.payload);
            break;

          //
          case XFrameAction.LoadElement:
            this.loadElement(model.payload);
            break;

          //
          case XFrameAction.LoadText:
            this.loadText(model.payload);
            break;

          //
          case XFrameAction.LoadHtml:
            this.loadHtml(model.payload);
            break;

          //
          case XFrameAction.FullScreen:
            this.fullScreen(model.payload);
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region Preparation ...
  private prepareContent(content?: string) {
    this.getValue(this.content).subscribe((tContent) => {
      //
      this.ORIGIN = '';
      this.contentUrl = '';
      this.contentHTML = '';

      //
      content = !isNullOrEmptyString(content) ? content : tContent;
      if (isNullOrEmptyString(content)) {
        this.detectChanges();
        return;
      }

      //
      this.contentHTML =
        this.managerService.securityService.toSafeHTML(content);
    });
  }

  private prepareContentUrl(url?: string) {
    this.getValue(this.url).subscribe((tUrl) => {
      //
      this.ORIGIN = '';
      this.contentUrl = '';
      this.contentHTML = '';

      //
      url = !isNullOrEmptyString(url) ? url : tUrl;
      if (isNullOrEmptyString(tUrl)) {
        this.detectChanges();
        return;
      }

      //
      // Retrieve Origin ...
      const mUrl = new URL(url);
      this.ORIGIN = mUrl.origin;

      //
      this.contentUrl =
        this.managerService.securityService.toSafeResourceURL(url);
    });
  }

  private applyFrameAttribute(
    attr: string,
    value: string,
    element: ElementRef<HTMLIFrameElement>
  ) {
    //
    if (isNullOrUndefined(element) || isNullOrEmptyString(attr)) {
      return;
    }

    //
    if (!isNullOrEmptyString(value)) {
      //
      // Set Attribute ...
      this.setAttribute(attr, value, element);
    } else {
      //
      // Remove Attribute ...
      this.removeAttribute(attr, element);
    }
  }

  //
  //#region Frame Props ...
  private applyFrameProps() {
    this.addTransparency();
    this.handleAllowChanged();
    this.handleSandboxChanged();
    this.handleFrameLoadingChanged();
    this.handleReferrerPolicyChanged();
    this.handleAllowFullScreenChanged();
    this.handleAllowPaymentRequestChanged();
  }

  private addTransparency() {
    //
    const value = true.toString();
    if (
      isNullOrEmptyString(value) ||
      isNullOrUndefined(this.frameView) ||
      isNullOrUndefined(this.frameView.nativeElement)
    ) {
      return;
    }

    //
    this.applyFrameAttribute('allowtransparency', value, this.frameView);
    this.detectChanges();
  }

  private handleAllowChanged() {
    this.getArrayValue(this.allow).subscribe((allows) => {
      //
      if (
        isNullOrUndefined(this.frameView) ||
        isNullOrUndefined(this.frameView.nativeElement)
      ) {
        return;
      }

      //
      const value = this.hasChild(allows) ? allows.join('; ') : null;

      //
      this.applyFrameAttribute('allow', value, this.frameView);
      this.detectChanges();
    });
  }

  private handleSandboxChanged() {
    this.getValue(this.sandbox).subscribe((value) => {
      //
      if (
        isNullOrEmptyString(value) ||
        isNullOrUndefined(this.frameView) ||
        isNullOrUndefined(this.frameView.nativeElement)
      ) {
        return;
      }

      //
      this.applyFrameAttribute('sandbox', value, this.frameView);
      this.detectChanges();
    });
  }

  private handleFrameLoadingChanged() {
    this.getValue(this.frameLoading).subscribe((value) => {
      //
      if (
        isNullOrEmptyString(value) ||
        isNullOrUndefined(this.frameView) ||
        isNullOrUndefined(this.frameView.nativeElement)
      ) {
        return;
      }

      //
      this.applyFrameAttribute('loading', value, this.frameView);
      this.detectChanges();
    });
  }

  private handleReferrerPolicyChanged() {
    this.getArrayValue(this.referrerPolicy).subscribe((referrerPolicies) => {
      //
      if (
        isNullOrUndefined(this.frameView) ||
        isNullOrUndefined(this.frameView.nativeElement)
      ) {
        return;
      }

      //
      const value = this.hasChild(referrerPolicies)
        ? referrerPolicies.join('|')
        : null;

      //
      this.applyFrameAttribute('referrerpolicy', value, this.frameView);
      this.detectChanges();
    });
  }

  private handleAllowFullScreenChanged() {
    this.getValue(this.allowFullscreen).subscribe((allowFullscreen) => {
      //
      const value = allowFullscreen.toString();
      if (
        isNullOrEmptyString(value) ||
        isNullOrUndefined(this.frameView) ||
        isNullOrUndefined(this.frameView.nativeElement)
      ) {
        return;
      }

      //
      this.applyFrameAttribute('allowfullscreen', value, this.frameView);
      this.detectChanges();
    });
  }

  private handleAllowPaymentRequestChanged() {
    this.getValue(this.allowPaymentRequest).subscribe((allowPaymentRequest) => {
      //
      const value = allowPaymentRequest.toString();
      if (
        isNullOrEmptyString(value) ||
        isNullOrUndefined(this.frameView) ||
        isNullOrUndefined(this.frameView.nativeElement)
      ) {
        return;
      }

      //
      this.applyFrameAttribute('allowpaymentrequest', value, this.frameView);
      this.detectChanges();
    });
  }
  //#endregion
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-frame\x-frame.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XFrameComponent } from './x-frame.component';
import { XCardModule } from '../x-card/x-card.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XFrameComponent],
  imports: [CommonModule, XBaseComponentModule, XCardModule],
  exports: [CommonModule, XBaseComponentModule, XCardModule, XFrameComponent],
})
export class XFrameModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-frame\x-frame.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { forkJoin, Observable, Subject } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XOneOrManyType, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XFrameAction {
  Refresh = 'refresh',
  AppendElement = 'appendElement',
  LoadHtml = 'loadHtml',
  LoadText = 'loadText',
  LoadElement = 'loadElement',
  ApplyUrl = 'applyUrl',
  ApplyContent = 'applyContent',
  FullScreen = 'fullScreen',
}

export type XFrameActionIdentifier = XFrameAction | string;

export interface XFrameActionModel
  extends XBaseActionModel<XFrameActionIdentifier> {}

//
//#region XFrame Loading ...
export enum XFrameLoading {
  Eager = 'eager',
  Lazy = 'lazy',
}

export type XFrameLoadingIdentifier = XFrameLoading | string;
//#endregion

//
//#region XFrame ReferrerPolicy ...
export enum XFrameReferrerPolicy {
  NoReferrer = 'no-referrer',
  NoReferrerWhenDowngrade = 'no-referrer-when-downgrade',
  Origin = 'origin',
  OriginWhenCrossOrigin = 'origin-when-cross-origin',
  SameOrigin = 'same-origin',
  StrictOriginWhenCrossOrigin = 'strict-origin-when-cross-origin',
  UnsafeUrl = 'unsafe-url',
}

export type XFrameReferrerPolicyIdentifier = XFrameReferrerPolicy | string;
//#endregion

//
//#region Sandbox ...
export enum XFrameSandbox {
  AllowForms = 'allow-forms',
  AllowModals = 'allow-modals',
  AllowOrientationLock = 'allow-orientation-lock',
  AllowPointerLock = 'allow-pointer-lock',
  AllowPopups = 'allow-popups',
  AllowPopupsToEscapeSandbox = 'allow-popups-to-escape-sandbox',
  AllowPresentation = 'allow-presentation',
  AllowSameOrigin = 'allow-same-origin',
  AllowScripts = 'allow-scripts',
  AllowTopNavigation = 'allow-top-navigation',
  AllowTopNavigationByUserActivation = 'allow-top-navigation-by-user-activation',
}

export type XFrameSandboxIdentifier = XFrameSandbox | string;
//#endregion

//
//#region Containers ...
export interface IXFrameContainer extends IXComponentCardContainer {
  url: XStandardType<string>;
  content: XStandardType<string>;
  onContentLoaded: EventEmitter<any>;
  allowFullscreen: XStandardType<boolean>;
  actionProvider: Subject<XFrameActionModel>;
  allowPaymentRequest: XStandardType<boolean>;
  allow: XStandardType<XOneOrManyType<string>>;
  cssClass: XStandardType<XOneOrManyType<string>>;
  sandbox: XStandardType<XFrameSandboxIdentifier>;
  frameLoading: XStandardType<XFrameLoadingIdentifier>;
  referrerPolicy: XStandardType<XOneOrManyType<XFrameReferrerPolicyIdentifier>>;
}

@Injectable({
  providedIn: 'root',
})
export class XFrameContainer
  extends XComponentCardContainer
  implements Readonly<IXFrameContainer>
{
  //
  //#region Props ...
  readonly url: XStandardType<string>;
  readonly content: XStandardType<string>;
  readonly referrerPolicy: XStandardType<
    XOneOrManyType<XFrameReferrerPolicyIdentifier>
  > = null;
  readonly actionProvider: Subject<XFrameActionModel> =
    new Subject<XFrameActionModel>();
  readonly allow: XStandardType<XOneOrManyType<string>>;
  readonly allowFullscreen: XStandardType<boolean> = false;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;
  readonly allowPaymentRequest: XStandardType<boolean> = false;
  readonly sandbox: XStandardType<XFrameSandboxIdentifier> = null;
  readonly frameLoading: XStandardType<XFrameLoadingIdentifier> = null;
  readonly onContentLoaded: EventEmitter<any> = new EventEmitter<any>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XFrameContainerComponent
  extends XComponentCardContainerComponent
  implements IXFrameContainer
{
  //
  //#region Props ...
  @Input()
  allowFullscreen: XStandardType<boolean> =
    this.propertyProvider.allowFullscreen;

  @Input()
  actionProvider: Subject<XFrameActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  allowPaymentRequest: XStandardType<boolean> =
    this.propertyProvider.allowPaymentRequest;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  sandbox: XStandardType<XFrameSandboxIdentifier> =
    this.propertyProvider.sandbox;

  @Input()
  frameLoading: XStandardType<XFrameLoadingIdentifier> =
    this.propertyProvider.frameLoading;

  @Input()
  url: XStandardType<string> = this.propertyProvider.url;

  @Input()
  content: XStandardType<string> = this.propertyProvider.content;

  @Input()
  referrerPolicy: XStandardType<
    XOneOrManyType<XFrameReferrerPolicyIdentifier>
  > = this.propertyProvider.referrerPolicy;

  @Output()
  onContentLoaded: EventEmitter<any> = this.propertyProvider.onContentLoaded;

  @Input()
  allow: XStandardType<XOneOrManyType<string>> = this.propertyProvider.allow;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFrameContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXFrameContainer
  ): Observable<IXFrameContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            url: this.getValue(container ? container.url : this.url),
            content: this.getValue(
              container ? container.content : this.content
            ),
            allowFullscreen: this.getValue(
              container ? container.allowFullscreen : this.allowFullscreen
            ),
            allowPaymentRequest: this.getValue(
              container
                ? container.allowPaymentRequest
                : this.allowPaymentRequest
            ),
            allow: this.getArrayValue(container ? container.allow : this.allow),
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),
            sandbox: this.getValue(
              container ? container.sandbox : this.sandbox
            ),
            frameLoading: this.getValue(
              container ? container.frameLoading : this.frameLoading
            ),
            referrerPolicy: this.getValue(
              container ? container.referrerPolicy : this.referrerPolicy
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            onContentLoaded: container
              ? container.onContentLoaded
              : this.onContentLoaded,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-grid\x-grid.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else holderRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="holderRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Grid Template -->
<ng-template #holderRef>
  <div class="x-grid x-wrapper" [class.mobile]="isMobileUi$ | async">
    <!-- Handle Childs -->
    <ng-container *ngIf="items && items.length > 0">
      <ng-container *ngIf="preparedType">
        <!-- Item Holder -->
        <ng-container
          *ngTemplateOutlet="
            isTypeLinear()
              ? linearRef
              : isTypeGrid()
              ? gridRef
              : isTypeFlex()
              ? flexRef
              : isTypeStaggedGrid()
              ? staggedGrid
              : nothingRef
          "
        ></ng-container>
      </ng-container>
    </ng-container>

    <!-- Empty Handler -->
    <ng-container
      *ngIf="
        (getValue(showEmpty) | async) &&
        (!items || (items && items.length === 0))
      "
    >
      <x-empty
        (refresh)="emptyRefresh.emit($event)"
        [color]="getValue(emptyColor) | async"
        [message]="getResourceValue(emptyMessage) | async"
        [refreshIcon]="getValue(emptyRefreshIcon) | async"
        [messageColor]="getValue(emptyMessageColor) | async"
        [hasRefresh]="getValue(emptyHasRefresh) | async"
        [foregroundRefreshButtonColor]="
          getValue(emptyForegroundRefreshButtonColor) | async
        "
        [foregroundColor]="getValue(emptyForegroundColor) | async"
        [refreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
        [foregroundMessageColor]="getValue(emptyForegroundMessageColor) | async"
      ></x-empty>
    </ng-container>
  </div>
</ng-template>

<!-- Linear Ref -->
<ng-template #linearRef>
  <!--  -->
  <ion-list [ngClass]="getValue(cssClass) | async">
    <ng-container *ngFor="let item of items; index as i">
      <ion-item lines="none" class="transparent">
        <ng-container
          *ngTemplateOutlet="itemHolderRef; context: { $implicit: item }"
        >
        </ng-container>
      </ion-item>
    </ng-container>
  </ion-list>
</ng-template>

<!-- Grid Ref -->
<ng-template #gridRef>
  <!--  -->
  <ion-grid [ngClass]="getValue(cssClass) | async">
    <ng-container *ngFor="let item of items; index as i">
      <ng-container *ngIf="isNewRow(i, preparedCols)">
        <ng-container
          *ngTemplateOutlet="rowRef; context: { $implicit: i }"
        ></ng-container>
      </ng-container>
    </ng-container>
  </ion-grid>
</ng-template>

<!-- Flex Ref -->
<ng-template #flexRef>
  <!--  -->
  <div
    class="grid-box"
    [fxLayoutGap]="gap"
    [fxLayout]="layout"
    [fxLayoutAlign]="align"
    *ngIf="items && items.length > 0"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <ng-container *ngFor="let item of items; index as i">
      <div [fxFlexAlign]="'stretch'">
        <ng-container
          *ngTemplateOutlet="itemHolderRef; context: { $implicit: item }"
        >
        </ng-container>
      </div>
    </ng-container>
  </div>
</ng-template>

<!-- StaggedGrid Ref -->
<ng-template #staggedGrid>
  <gridster
    #gridster
    *ngIf="options"
    [options]="options"
    [ngClass]="getValue(cssClass) | async"
    [class.inCard]="getValue(wrapWithCard) | async"
  >
    <gridster-item
      [item]="toStaggedGrid(idx)"
      *ngFor="let item of items; index as idx"
    >
      <ng-container
        *ngTemplateOutlet="itemHolderRef; context: { $implicit: item }"
      >
      </ng-container>
    </gridster-item>
  </gridster>
</ng-template>

<!-- Nothing Ref -->
<ng-template #nothingRef></ng-template>

<!-- Grid Row Template -->
<ng-template #rowRef let-index>
  <ion-row>
    <ion-col *ngFor="let item of colItems; index as i">
      <ng-container
        *ngTemplateOutlet="
          itemHolderRef;
          context: { $implicit: items[i + index] }
        "
      >
      </ng-container>
    </ion-col>
  </ion-row>
</ng-template>

<!-- Item Holder Template -->
<ng-template #itemHolderRef let-item>
  <button
    matRipple
    *ngIf="item"
    class="transparent"
    class="x-grid-item-wrapper"
    (click)="handleItemSelected(item)"
    [hidden]="getItemHide(item) | async"
    [disabled]="getItemDisabled(item) | async"
    [matRippleDisabled]="
      (getItemDisabled(item) | async) || !(getValue(selectableItems) | async)
    "
  >
    <ng-container
      *ngTemplateOutlet="
        itemTemplate ? itemTemplate : defaultItemRef;
        context: { $implicit: item }
      "
    ></ng-container>
  </button>
</ng-template>

<!-- Default Item Ref -->
<ng-template #defaultItemRef let-item>
  <h5>{{ item | json }}</h5>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-grid\x-grid.component.scss

ion-item-option > ion-item {
  --background: transparent !important;
}

ion-button.transparent-button {
  --box-shadow: transparent !important;
  --background: transparent !important;
  --background-hover: transparent !important;
  --background-focused: transparent !important;
  --background-activated: transparent !important;
}

ion-list,
ion-item.item,
ion-item-divider {
  --background: transparent !important;
}

ion-item-divider {
  --color: var(--ion-color-dark-shade);
}

ion-item.item.mobile {
  --ion-safe-area-end: 0px;

  --padding-start: 0px;
  --inner-padding-start: 0px;

  --padding-end: 0px;
  --inner-padding-end: 0px;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
}

.x-menu {
  z-index: 10;
}

.x-check {
  z-index: 1;
}

.grid-box {
  margin-top: 8px;
}

.grid-box.mobile {
  margin-top: 0px;
}

gridster {
  width: 100%;
  display: flex;
  direction: ltr;
  position: inherit;
  flex-direction: column;
  background-color: transparent;
  height: calc(var(--grid-height, inherit));

  &.inCard {
    position: relative;
  }
}

gridster-item {
  padding: 0;
  background-color: transparent;
}

.x-grid-item-wrapper {
  position: relative;
  background: inherit;
}

a[href=''],
[disabled],
.disable {
  color: inherit;
  cursor: default;
  pointer-events: none;
  text-decoration: none;
}

.transparent {
  --background: transparent !important;
  background-color: transparent !important;
}

x-card {
  width: inherit;
  height: inherit;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-grid\x-grid.component.ts

import {
  XGridItem,
  XGridAction,
  XGridActionModel,
  XGridPresentType,
  defaultGridOptions,
  XStaggedGridOptions,
  XGridContainerComponent,
} from './x-grid.typings';
import { Subject, Subscription } from 'rxjs';
import { nameof, cloneJson, toObservable } from 'x-framework-core';
import { GridsterItem, GridsterComponent } from 'angular-gridster2';
import { Component, ViewChild, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-grid',
  templateUrl: './x-grid.component.html',
  styleUrls: ['./x-grid.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XGridComponent extends XGridContainerComponent {
  //
  //#region Props ...
  //
  private COLS: number;
  private TYPE: XGridPresentType;
  private GRIDSTER: GridsterComponent;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  colItems: number[] = [];

  get preparedCols() {
    return this.COLS;
  }

  get preparedType() {
    return this.TYPE;
  }

  //
  @ViewChild('gridster', { static: false })
  set gridster(v: GridsterComponent) {
    //
    if (!v) {
      return;
    }

    //
    this.GRIDSTER = v;
    this.prepareData();
  }

  get gridster() {
    return this.GRIDSTER;
  }
  //#endregion

  //
  //#region LifeCycles ...
  afterViewInit() {
    super.afterContentInit();

    //
    this.prepareData();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XGridComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    const isColsChanged = changeKeys.includes(nameof<XGridComponent>('cols'));
    const isTypeChanged = changeKeys.includes(nameof<XGridComponent>('type'));
    const isOptionsChanged = changeKeys.includes(
      nameof<XGridComponent>('options')
    );

    //
    if (isColsChanged) {
      this.COLS = await this.getValueAsync(this.cols);
      this.detectChanges();
    }

    //
    if (isTypeChanged) {
      this.TYPE = await this.getValueAsync(this.type);
      this.detectChanges();
    }

    //
    if (isColsChanged || isTypeChanged || isOptionsChanged) {
      this.prepareData();
    }
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Ui Provider Actions ...
  //
  //#region Type Detectors ...
  isTypeLinear() {
    return this.preparedType === XGridPresentType.AsLinear;
  }

  isTypeGrid() {
    return this.preparedType === XGridPresentType.AsGrid;
  }

  isTypeFlex() {
    return this.preparedType === XGridPresentType.AsFlex;
  }

  isTypeStaggedGrid() {
    return this.preparedType === XGridPresentType.AsStaggedGrid;
  }
  //#endregion

  //
  isNewRow(i: number, cols: number) {
    return (i + 1) % cols === 1;
  }

  //
  toStaggedGrid(index: number) {
    return {
      id: index,
      data: this.items[index],
      cols: 1,
      rows: 1,
      x: 0,
      y: 0,
    } as GridsterItem;
  }

  //
  getItemHref(item?: XGridItem<any>) {
    //
    if (!item || !item.href) {
      return toObservable('');
    }

    //
    return this.getValue(item.href);
  }

  //
  getItemHide(item?: XGridItem<any>) {
    //
    if (!item || !item.hide) {
      return toObservable(false);
    }

    //
    return this.getValue(item.hide);
  }

  //
  getItemDisabled(item?: XGridItem<any>) {
    //
    if (!item || !item.disabled) {
      return toObservable(false);
    }

    //
    return this.getValue(item.disabled);
  }
  //#endregion

  //
  //#region Type Detectors ...
  //#endregion

  //
  //#region Actions ...
  async handleItemSelected(item: XGridItem<any>) {
    //
    if (!this.selectableItems) {
      return;
    }

    //
    const hided = await this.toPromise(this.getItemHide(item));
    const disabled = await this.toPromise(this.getItemDisabled(item));

    //
    if (!hided && !disabled) {
      this.itemSelected.emit(item);
    }
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XGridActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.actionProvider.asObservable()
    ).subscribe(async (model) => {
      //
      if (!model) {
        return;
      }

      //
      switch (model.action) {
        //
        case XGridAction.Refresh:
          this.detectChanges();
          break;
      }
    });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  private prepareData() {
    //
    if (!this.preparedCols) {
      return;
    }

    //
    const mCols = this.preparedCols < 0 ? 0 : this.preparedCols;
    this.colItems = Array.from(Array(mCols).keys());

    //
    setTimeout(() => {
      //
      this.options = {
        ...cloneJson(defaultGridOptions),
        minCols: mCols,
        maxCols: mCols,
      } as XStaggedGridOptions;

      //
      this.detectChanges();
    }, 300);

    //
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-grid\x-grid.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GridsterModule } from 'angular-gridster2';
import { XGridComponent } from './x-grid.component';
import { XCardModule } from '../x-card/x-card.module';
import { XEmptyModule } from '../x-empty/x-empty.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XGridComponent],
  imports: [
    CommonModule,
    XCardModule,
    XEmptyModule,
    GridsterModule,
    XBaseComponentModule,
  ],
  exports: [XCardModule, XGridComponent, XEmptyModule, XBaseComponentModule],
})
export class XGridModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-grid\x-grid.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  IXCardEmptyContainer,
  XCardEmptyContainer,
  XCardEmptyContainerComponent,
} from '../classes/i-x-card-empty.container';
import { concatMap, map } from 'rxjs/operators';
import { GridsterConfig } from 'angular-gridster2';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XStandardType, XOneOrManyType } from 'x-framework-core';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export interface XGridItem<T> {
  data: T;
  hide?: XStandardType<boolean>;
  disabled?: XStandardType<boolean>;
  href?: XStandardType<XOneOrManyType<string>>;
}

export interface XStaggedGridOptions extends Partial<GridsterConfig> {}
export const defaultGridOptions: XStaggedGridOptions = {
  gridType: 'scrollVertical',
  compactType: 'compactLeft&Up',
  disableWarnings: true,
  draggable: {
    enabled: true,
  },
  pushItems: true,
  resizable: {
    enabled: true,
  },
  minCols: 2,
  maxCols: 2,
  minRows: 1,
};

export enum XGridPresentType {
  AsLinear,
  AsGrid,
  AsFlex,
  AsStaggedGrid,
}

export enum XGridAction {
  Refresh = 'refresh',
}

export type XGridActionIdentifier = XGridAction | string;

export interface XGridActionModel
  extends XBaseActionModel<XGridActionIdentifier> {}

//
//#region Inside Card/Empty Container ...
export interface IXGridContainer extends IXCardEmptyContainer {
  //
  actionProvider: Subject<XGridActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  options: XStandardType<XStaggedGridOptions>;
  cols: XStandardType<number>;
  type: XStandardType<XGridPresentType>;
  gap: string;
  layout: string;
  align: string;
  selectableItems: XStandardType<boolean>;
  items: XGridItem<any>[];
  itemTemplate: TemplateRef<any>;

  //
  itemSelected: EventEmitter<XGridItem<any>>;
}

@Injectable({
  providedIn: 'root',
})
export class XGridContainer
  extends XCardEmptyContainer
  implements Readonly<IXGridContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XGridActionModel> =
    new Subject<XGridActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly options: XStandardType<XStaggedGridOptions>;
  readonly cols: XStandardType<number>;
  readonly type: XStandardType<XGridPresentType>;
  readonly gap: string = '1px';
  readonly layout: string = 'row wrap';
  readonly align: string = 'center start';
  readonly selectableItems: XStandardType<boolean> = false;
  readonly items: XGridItem<any>[];
  readonly itemTemplate: TemplateRef<any>;

  //
  readonly itemSelected: EventEmitter<XGridItem<any>>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XGridContainerComponent
  extends XCardEmptyContainerComponent
  implements IXGridContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XGridActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  options: XStandardType<XStaggedGridOptions> = this.propertyProvider.options;

  @Input()
  cols: XStandardType<number> = this.propertyProvider.cols;

  @Input()
  type: XStandardType<XGridPresentType> = this.propertyProvider.type;

  @Input()
  gap: string = this.propertyProvider.gap;

  @Input()
  layout: string = this.propertyProvider.layout;

  @Input()
  align: string = this.propertyProvider.align;

  @Input()
  selectableItems: XStandardType<boolean> =
    this.propertyProvider.selectableItems;

  @Input()
  items: XGridItem<any>[] = this.propertyProvider.items;

  @Input()
  itemTemplate: TemplateRef<any> = this.propertyProvider.itemTemplate;

  //
  @Output()
  itemSelected: EventEmitter<XGridItem<any>> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XGridContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXGridContainer
  ): Observable<IXGridContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            options: this.getValue(
              container ? container.options : this.options
            ),
            cols: this.getValue(container ? container.cols : this.cols),
            type: this.getValue(container ? container.type : this.type),
            gap: this.getValue(container ? container.gap : this.gap),
            layout: this.getValue(container ? container.layout : this.layout),
            align: this.getValue(container ? container.align : this.align),
            selectableItems: this.getValue(
              container ? container.selectableItems : this.selectableItems
            ),
            items: this.getValue(container ? container.items : this.items),
            itemTemplate: this.getValue(
              container ? container.itemTemplate : this.itemTemplate
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            itemSelected: this.itemSelected,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-icon\x-icon.component.html

<!-- Wrapper -->
<div
  *ngIf="getValue(name) | async"
  [slot]="getValue(slot) | async"
  (click)="handleClicked($event)"
  [xColor]="getValue(color) | async"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
  [isForeground]="getValue(foregroundColor) | async"
  class="x-icon x-wrapper ion-no-margin ion-no-padding"
  [class.ion-activatable]="
    (getValue(selectable) | async) || (getValue(clickable) | async)
  "
  [class.rtl]="(getValue(applyDirection) | async) && managerService.isRtl"
>
  <!-- Ripple Effect -->
  <ion-ripple-effect
    *ngIf="
      !(getValue(uiDisabled) | async) &&
      ((getValue(selectable) | async) || (getValue(clickable) | async))
    "
  ></ion-ripple-effect>

  <!-- Regular Icons -->
  <ion-icon
    class="ion-no-margin ion-no-padding"
    [name]="!(isCustomIcon() | async) ? (icon | async) : ''"
    [src]="(isCustomIcon() | async) ? (customIconUrl() | async) : ''"
  >
  </ion-icon>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-icon\x-icon.component.scss

:host {
  //
  --size: 24px;

  //
  display: inline-block;
  position: relative;

  //
  font-size: var(--size);
  line-height: var(--size);
  width: var(--size);
  height: var(--size);

  //
  min-width: var(--size);
  min-height: var(--size);

  //
  margin: 0 !important;
  padding: 0 !important;
}

//
$delay: 0;
$duration: 1;
$transform: scaleX(-1);

.x-icon {
  //
  display: inline-block;
  position: relative;

  //
  margin: 0 !important;
  padding: 0 !important;

  //
  box-sizing: border-box !important;

  //
  min-width: var(--size);
  min-height: var(--size);

  &.rtl {
    //
    transform: $transform;
    -ms-transform: $transform;
    -webkit-transform: $transform;

    //
    transition: transform;
    -webkit-transition: transform;

    //
    transition-delay: $delay;
    -webkit-transition-delay: $delay;

    //
    transition-duration: $duration;
    -webkit-transition-duration: $duration;
  }
}

ion-icon,
ion-ripple-effect {
  font-size: var(--size);

  min-width: var(--size);
  min-height: var(--size);
}

.ion-activatable {
  cursor: pointer;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-icon\x-icon.component.ts

import {
  XIconAction,
  XCustomIcons,
  XProvidedIcons,
  XIconActionModel,
  XIconContainerComponent,
} from './x-icon.typings';
import { map } from 'rxjs/operators';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-icon',
  templateUrl: './x-icon.component.html',
  styleUrls: ['./x-icon.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XIconComponent extends XIconContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XIconComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Icon Provider Properties ...
  get providedIcons() {
    return XProvidedIcons;
  }

  get icon() {
    return this.getValue(this.name).pipe(
      map((name) => {
        return (this.providedIcons as any)[name.toString()] as string;
      })
    );
  }
  //#endregion

  //
  //#region UI Providers ...
  isCustomIcon() {
    return this.getValue(this.name).pipe(
      map((name) => {
        return name && XCustomIcons.includes(name.toString());
      })
    );
  }

  customIconUrl() {
    return this.getValue(this.name).pipe(
      map((name) => {
        //
        const svgName = (this.providedIcons as any)[name.toString()]
        return this.name
          ? `./assets/icon/svg/${svgName}.svg`
          : null;
      })
    );
  }
  //#endregion

  //
  //#region Action Handlers ...
  async handleClicked(event?: Event) {
    //
    const clickable = await this.toPromise(this.getValue(this.clickable));
    const disabled = this.uiDisabled;
    if (!clickable || disabled) {
      return;
    }

    //
    if (event && event.stopPropagation) {
      event.stopPropagation();
    }

    //
    this.clicked.emit(event);
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XIconActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XIconAction.Click:
            this.clicked.emit();
            break;

          //
          case XIconAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-icon\x-icon.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconComponent } from './x-icon.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XIconComponent],
  imports: [CommonModule, XBaseComponentModule],
  exports: [XBaseComponentModule, XIconComponent],
})
export class XIconModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-icon\x-icon.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XIconAction {
  Click = 'click',
  Refresh = 'refresh',
}

export type XIconActionIdentifier = XIconAction | string;

export interface XIconActionModel
  extends XBaseActionModel<XIconActionIdentifier> {}

export enum XIconNames {
  tv = 'tv',
  ol = 'ol',
  ul = 'ul',
  job = 'job',
  add = 'add',
  sad = 'sad',
  web = 'web',
  map = 'map',
  dns = 'dns',
  dvr = 'dvr',
  fax = 'fax',
  mic = 'mic',
  mop = 'mop',
  info = 'info',
  lock = 'lock',
  mail = 'mail',
  shop = 'shop',
  edit = 'edit',
  done = 'done',
  list = 'list',
  user = 'user',
  star = 'star',
  sync = 'sync',
  undo = 'undo',
  redo = 'redo',
  home = 'home',
  flag = 'flag',
  save = 'save',
  last = 'last',
  next = 'next',
  copy = 'copy',
  link = 'link',
  bold = 'bold',
  code = 'code',
  crop = 'crop',
  call = 'call',
  tabs = 'tabs',
  cast = 'cast',
  wifi = 'wifi',
  stop = 'stop',
  play = 'play',
  phone = 'phone',
  reply = 'reply',
  label = 'label',
  liked = 'liked',
  terms = 'terms',
  event = 'event',
  clear = 'clear',
  input = 'input',
  close = 'close',
  users = 'users',
  files = 'files',
  login = 'login',
  posts = 'posts',
  enter = 'enter',
  power = 'power',
  cloud = 'cloud',
  first = 'first',
  touch = 'touch',
  error = 'error',
  print = 'print',
  share = 'share',
  image = 'image',
  audio = 'audio',
  video = 'video',
  guide = 'guide',
  quote = 'quote',
  toast = 'toast',
  cards = 'cards',
  mouse = 'mouse',
  album = 'album',
  badge = 'badge',
  radio = 'radio',
  watch = 'watch',
  movie = 'movie',
  pause = 'pause',
  unlock = 'unlock',
  cancel = 'cancel',
  expand = 'expand',
  mobile = 'mobile',
  search = 'search',
  survey = 'survey',
  remove = 'remove',
  invite = 'invite',
  logout = 'logout',
  school = 'school',
  server = 'server',
  upload = 'upload',
  locate = 'locate',
  italic = 'italic',
  repeat = 'repeat',
  report = 'report',
  policy = 'policy',
  router = 'router',
  shield = 'shield',
  stylus = 'stylus',
  tv_off = 'tv_off',
  swords = 'swords',
  nearby = 'nearby',
  flight = 'flight',
  memory = 'memory',
  replay = 'replay',
  visible = 'visible',
  checked = 'checked',
  unliked = 'unliked',
  profile = 'profile',
  account = 'account',
  avatars = 'avatars',
  archive = 'archive',
  refresh = 'refresh',
  unknown = 'unknown',
  preview = 'preview',
  heading = 'heading',
  toolbar = 'toolbar',
  dialogs = 'dialogs',
  dialpad = 'dialpad',
  vpn_key = 'vpn_key',
  widgets = 'widgets',
  passkey = 'passkey',
  mic_off = 'mic_off',
  monitor = 'monitor',
  gpp_bad = 'gpp_bad',
  charger = 'charger',
  devices = 'devices',
  capture = 'capture',
  live_tv = 'live_tv',
  sd_card = 'sd_card',
  verified = 'verified',
  collapse = 'collapse',
  arrow_up = 'arrow_up',
  contacts = 'contacts',
  contrast = 'contrast',
  calendar = 'calendar',
  register = 'register',
  explorer = 'explorer',
  settings = 'settings',
  business = 'business',
  bookmark = 'bookmark',
  chat_box = 'chat_box',
  location = 'location',
  previous = 'previous',
  download = 'download',
  language = 'language',
  document = 'document',
  logo_npm = 'logo_npm',
  logo_pwa = 'logo_pwa',
  logo_rss = 'logo_rss',
  logo_usd = 'logo_usd',
  logo_vue = 'logo_vue',
  dropdown = 'dropdown',
  add_call = 'add_call',
  sim_card = 'sim_card',
  security = 'security',
  password = 'password',
  podcasts = 'podcasts',
  wifi_off = 'wifi_off',
  no_sound = 'no_sound',
  keyboard = 'keyboard',
  exercise = 'exercise',
  computer = 'computer',
  autoplay = 'autoplay',
  autostop = 'autostop',
  videocam = 'videocam',
  label_off = 'label_off',
  swap_vert = 'swap_vert',
  dashboard = 'dashboard',
  checkmark = 'checkmark',
  star_half = 'star_half',
  thumbs_up = 'thumbs_up',
  bookmarks = 'bookmarks',
  bluetooth = 'bluetooth',
  analytics = 'analytics',
  clipboard = 'clipboard',
  logo_css3 = 'logo_css3',
  logo_edge = 'logo_edge',
  logo_sass = 'logo_sass',
  touch_app = 'touch_app',
  voicemail = 'voicemail',
  call_made = 'call_made',
  subtitles = 'subtitles',
  music_off = 'music_off',
  encrypted = 'encrypted',
  apartment = 'apartment',
  dark_mode = 'dark_mode',
  deskphone = 'deskphone',
  autopause = 'autopause',
  video_cam = 'video_cam',
  swap_horiz = 'swap_horiz',
  first_name = 'first_name',
  un_checked = 'un_checked',
  arrow_down = 'arrow_down',
  arrow_back = 'arrow_back',
  select_all = 'select_all',
  management = 'management',
  logo_apple = 'logo_apple',
  logo_ionic = 'logo_ionic',
  logo_html5 = 'logo_html5',
  blockchain = 'blockchain',
  logo_react = 'logo_react',
  logo_skype = 'logo_skype',
  logo_slack = 'logo_slack',
  logo_yahoo = 'logo_yahoo',
  data_table = 'data_table',
  device_hub = 'device_hub',
  call_split = 'call_split',
  router_off = 'router_off',
  light_mode = 'light_mode',
  mouse_lock = 'mouse_lock',
  nightlight = 'nightlight',
  volume_off = 'volume_off',
  volume_low = 'volume_low',
  play_arrow = 'play_arrow',
  video_call = 'video_call',
  video_chat = 'video_chat',
  video_file = 'video_file',
  voice_chat = 'voice_chat',
  fit_screen = 'fit_screen',
  fullscreen = 'fullscreen',
  rotate_left = 'rotate_left',
  visible_off = 'visible_off',
  friendships = 'friendships',
  thumbs_down = 'thumbs_down',
  chat_bubble = 'chat_bubble',
  stats_chart = 'stats_chart',
  logo_amazon = 'logo_amazon',
  logo_chrome = 'logo_chrome',
  logo_docker = 'logo_docker',
  logo_github = 'logo_github',
  logo_gitlab = 'logo_gitlab',
  logo_google = 'logo_google',
  blockchain1 = 'blockchain1',
  logo_nodejs = 'logo_nodejs',
  logo_paypal = 'logo_paypal',
  voicemail_2 = 'voicemail_2',
  devices_off = 'devices_off',
  exit_to_app = 'exit_to_app',
  ring_volume = 'ring_volume',
  call_missed = 'call_missed',
  privacy_tip = 'privacy_tip',
  dual_screen = 'dual_screen',
  engineering = 'engineering',
  volume_high = 'volume_high',
  volume_mute = 'volume_mute',
  play_circle = 'play_circle',
  fast_rewind = 'fast_rewind',
  rotate_right = 'rotate_right',
  chat_bubbles = 'chat_bubbles',
  deselect_all = 'deselect_all',
  arrow_dropup = 'arrow_dropup',
  logo_android = 'logo_android',
  logo_angular = 'logo_angular',
  logo_bitcoin = 'logo_bitcoin',
  logo_dropbox = 'logo_dropbox',
  logo_firefox = 'logo_firefox',
  logo_windows = 'logo_windows',
  logo_twitter = 'logo_twitter',
  logo_youtube = 'logo_youtube',
  wifi_calling = 'wifi_calling',
  phone_locked = 'phone_locked',
  phone_missed = 'phone_missed',
  call_quality = 'call_quality',
  screen_share = 'screen_share',
  room_service = 'room_service',
  other_houses = 'other_houses',
  dock_to_left = 'dock_to_left',
  keyboard_off = 'keyboard_off',
  cameraswitch = 'cameraswitch',
  architecture = 'architecture',
  fast_forward = 'fast_forward',
  arrow_forward = 'arrow_forward',
  accessibility = 'accessibility',
  cloud_offline = 'cloud_offline',
  menu_vertical = 'menu_vertical',
  logo_electron = 'logo_electron',
  logo_facebook = 'logo_facebook',
  logo_firebase = 'logo_firebase',
  logo_linkedin = 'logo_linkedin',
  logo_markdown = 'logo_markdown',
  logo_whatsapp = 'logo_whatsapp',
  color_palette = 'color_palette',
  phone_enabled = 'phone_enabled',
  phone_in_talk = 'phone_in_talk',
  call_received = 'call_received',
  print_connect = 'print_connect',
  screen_record = 'screen_record',
  shield_person = 'shield_person',
  verified_user = 'verified_user',
  network_check = 'network_check',
  encrypted_off = 'encrypted_off',
  dock_to_right = 'dock_to_right',
  electric_bolt = 'electric_bolt',
  reset_password = 'reset_password',
  customers_club = 'customers_club',
  checkmark_done = 'checkmark_done',
  arrow_dropdown = 'arrow_dropdown',
  logo_instagram = 'logo_instagram',
  logo_wordpress = 'logo_wordpress',
  devices_fold_2 = 'devices_fold_2',
  perm_phone_msg = 'perm_phone_msg',
  phone_callback = 'phone_callback',
  phone_disabled = 'phone_disabled',
  settings_phone = 'settings_phone',
  print_disabled = 'print_disabled',
  wifi_tethering = 'wifi_tethering',
  speech_to_text = 'speech_to_text',
  mouse_lock_off = 'mouse_lock_off',
  directions_car = 'directions_car',
  local_shipping = 'local_shipping',
  cast_connected = 'cast_connected',
  voice_chat_off = 'voice_chat_off',
  password_change = 'password_change',
  menu_horizontal = 'menu_horizontal',
  logo_javascript = 'logo_javascript',
  phone_forwarded = 'phone_forwarded',
  string_resources = 'string_resources',
  chatbox_ellipses = 'chatbox_ellipses',
  checkmark_circle = 'checkmark_circle',
  select_all_group = 'select_all_group',
  blockchain_block = 'blockchain_block',
  friendship_block = 'friendship_block',
  blockchain_token = 'blockchain_token',
  document_scanner = 'document_scanner',
  splitscreen_left = 'splitscreen_left',
  close_fullscreen = 'close_fullscreen',
  inverse_selection = 'inverse_selection',
  friendship_reuest = 'friendship_reuest',
  friendship_friend = 'friendship_friend',
  friendship_reject = 'friendship_reject',
  friendship_cancel = 'friendship_cancel',
  friendship_accept = 'friendship_accept',
  stop_screen_share = 'stop_screen_share',
  missed_video_call = 'missed_video_call',
  keyboard_lock_off = 'keyboard_lock_off',
  video_camera_back = 'video_camera_back',
  deselect_all_group = 'deselect_all_group',
  logo_stackoverflow = 'logo_stackoverflow',
  logo_web_component = 'logo_web_component',
  blockchain_address = 'blockchain_address',
  friendship_unblock = 'friendship_unblock',
  friendship_unknown = 'friendship_unknown',
  wifi_calling_bar_1 = 'wifi_calling_bar_1',
  wifi_calling_bar_2 = 'wifi_calling_bar_2',
  video_camera_front = 'video_camera_front',
  picture_in_picture = 'picture_in_picture',
  logo_apple_appstore = 'logo_apple_appstore',
  friendship_follower = 'friendship_follower',
  friendship_unfollow = 'friendship_unfollow',
  signal_cellular_alt = 'signal_cellular_alt',
  signal_disconnected = 'signal_disconnected',
  chat_bubble_ellipses = 'chat_bubble_ellipses',
  friendship_following = 'friendship_following',
  call_missed_outgoing = 'call_missed_outgoing',
  audio_video_receiver = 'audio_video_receiver',
  admin_panel_settings = 'admin_panel_settings',
  checkmark_done_circle = 'checkmark_done_circle',
  logo_google_playstore = 'logo_google_playstore',
  video_camera_back_add = 'video_camera_back_add',
  blockchain_transaction = 'blockchain_transaction',
  display_external_input = 'display_external_input',
  picture_in_picture_off = 'picture_in_picture_off',
  video_camera_front_off = 'video_camera_front_off',
  phone_bluetooth_speaker = 'phone_bluetooth_speaker',
  desktop_access_disabled = 'desktop_access_disabled',
  blockchain_smart_contract = 'blockchain_smart_contract',
  blockchain_contract_address = 'blockchain_contract_address',
}

export const defaultIcon = '';

export type XIconName = [keyof typeof XIconNames];

export type XIconIdentifier = XIconName | XIconNames | string;

export type XIconsIndex = {
  [key in keyof typeof XIconNames]?: string;
};

//
// Contains Path of Custom icons File Names ...
export const XProvidedIcons = {
  tv: 'tv',
  ol: 'ol',
  ul: 'ul',
  add: 'add',
  dns: 'dns',
  dvr: 'dvr',
  fax: 'fax',
  mic: 'mic',
  mop: 'mop',
  sad: 'sad',
  map: 'map',
  mail: 'mail',
  undo: 'undo',
  redo: 'redo',
  home: 'home',
  flag: 'flag',
  save: 'save',
  sync: 'sync',
  play: 'play',
  star: 'star',
  cast: 'cast',
  wifi: 'wifi',
  call: 'call',
  tabs: 'tabs',
  stop: 'stop',
  code: 'code',
  bold: 'bold',
  crop: 'crop',
  web: 'globe',
  link: 'link',
  last: 'last',
  next: 'next',
  copy: 'copy',
  phone: 'call',
  guide: 'help',
  reply: 'reply',
  label: 'label',
  visible: 'eye',
  liked: 'heart',
  mobile: 'call',
  shop: 'basket',
  event: 'pulse',
  clear: 'close',
  edit: 'create',
  close: 'close',
  enter: 'enter',
  power: 'power',
  pause: 'pause',
  cloud: 'cloud',
  toast: 'toast',
  cards: 'cards',
  mouse: 'mouse',
  album: 'album',
  badge: 'badge',
  radio: 'radio',
  watch: 'watch',
  movie: 'movie',
  image: 'image',
  quote: 'quote',
  touch: 'touch',
  error: 'error',
  first: 'first',
  print: 'print',
  cancel: 'close',
  remove: 'trash',
  terms: 'hammer',
  input: 'pencil',
  login: 'log-in',
  posts: 'albums',
  files: 'folder',
  italic: 'italic',
  locate: 'locate',
  search: 'search',
  job: 'megaphone',
  list: 'list-box',
  rewind: 'rewind',
  school: 'school',
  server: 'server',
  repeat: 'repeat',
  report: 'report',
  policy: 'policy',
  router: 'router',
  shield: 'shield',
  stylus: 'stylus',
  tv_off: 'tv_off',
  swords: 'swords',
  nearby: 'nearby',
  flight: 'flight',
  memory: 'memory',
  replay: 'replay',
  unknown: 'attach',
  video: 'videocam',
  done: 'checkmark',
  logout: 'log-out',
  avatars: 'images',
  account: 'person',
  invite: 'contract',
  archive: 'archive',
  toolbar: 'toolbar',
  dialogs: 'dialogs',
  dialpad: 'dialpad',
  vpn_key: 'vpn_key',
  widgets: 'widgets',
  passkey: 'passkey',
  mic_off: 'mic_off',
  monitor: 'monitor',
  gpp_bad: 'gpp_bad',
  charger: 'charger',
  devices: 'devices',
  sd_card: 'sd_card',
  capture: 'capture',
  live_tv: 'live_tv',
  preview: 'preview',
  heading: 'heading',
  refresh: 'refresh',
  lock: 'lock-closed',
  unlock: 'lock-open',
  un_checked: 'close',
  survey: 'newspaper',
  chat_box: 'chatbox',
  verified: 'verified',
  arrow_up: 'arrow-up',
  business: 'business',
  contrast: 'contrast',
  first_name: 'person',
  calendar: 'calendar',
  checked: 'checkmark',
  dashboard: 'journal',
  explorer: 'browsers',
  settings: 'settings',
  bookmark: 'bookmark',
  dropdown: 'dropdown',
  add_call: 'add_call',
  sim_card: 'sim_card',
  security: 'security',
  password: 'password',
  podcasts: 'podcasts',
  wifi_off: 'wifi_off',
  no_sound: 'no_sound',
  keyboard: 'keyboard',
  exercise: 'exercise',
  computer: 'computer',
  videocam: 'videocam',
  autoplay: 'autoplay',
  autostop: 'autostop',
  logo_npm: 'logo-npm',
  logo_pwa: 'logo-pwa',
  logo_rss: 'logo-rss',
  logo_usd: 'logo-usd',
  logo_vue: 'logo-vue',
  previous: 'previous',
  location: 'location',
  language: 'language',
  document: 'document',
  expand: 'expand_more',
  friendships: 'people',
  user: 'person-circle',
  video_cam: 'videocam',
  share: 'share-social',
  deselect_all: 'close',
  label_off: 'label_off',
  visible_off: 'eye-off',
  users: 'people-circle',
  checkmark: 'checkmark',
  star_half: 'star-half',
  bookmarks: 'bookmarks',
  thumbs_up: 'thumbs-up',
  bluetooth: 'bluetooth',
  logo_css3: 'logo-css3',
  logo_edge: 'logo-edge',
  logo_sass: 'logo-sass',
  voicemail: 'voicemail',
  call_made: 'call_made',
  touch_app: 'touch_app',
  subtitles: 'subtitles',
  music_off: 'music_off',
  encrypted: 'encrypted',
  apartment: 'apartment',
  dark_mode: 'dark_mode',
  deskphone: 'deskphone',
  autopause: 'autopause',
  upload: 'cloud-upload',
  audio: 'musical-notes',
  clipboard: 'clipboard',
  analytics: 'analytics',
  select_all: 'checkbox',
  management: 'construct',
  unliked: 'heart-outline',
  arrow_down: 'arrow-down',
  arrow_back: 'arrow-back',
  profile: 'person-circle',
  register: 'finger-print',
  customers_club: 'trophy',
  volume_off: 'volume-off',
  volume_low: 'volume-low',
  logo_html5: 'logo-html5',
  logo_ionic: 'logo-ionic',
  logo_apple: 'logo-apple',
  logo_react: 'logo-react',
  logo_skype: 'logo-skype',
  logo_slack: 'logo-slack',
  logo_yahoo: 'logo-yahoo',
  blockchain: 'blockchain',
  data_table: 'data_table',
  device_hub: 'device_hub',
  call_split: 'call_split',
  router_off: 'router_off',
  light_mode: 'light_mode',
  mouse_lock: 'mouse_lock',
  nightlight: 'nightlight',
  play_arrow: 'play_arrow',
  video_call: 'video_call',
  video_chat: 'video_chat',
  video_file: 'video_file',
  voice_chat: 'voice_chat',
  fit_screen: 'fit_screen',
  fullscreen: 'fullscreen',
  friendship_reuest: 'add',
  arrow_dropup: 'caret-up',
  collapse: 'chevron_right',
  contacts: 'people-circle',
  friendship_block: 'block',
  chat_bubble: 'chatbubble',
  info: 'information-circle',
  rotate_left: 'rotate_left',
  swap_vert: 'swap-vertical',
  friendship_friend: 'forum',
  download: 'cloud-download',
  thumbs_down: 'thumbs-down',
  stats_chart: 'stats-chart',
  volume_high: 'volume-high',
  volume_mute: 'volume-mute',
  play_circle: 'play-circle',
  logo_amazon: 'logo-amazon',
  logo_chrome: 'logo-chrome',
  logo_docker: 'logo-docker',
  logo_github: 'logo-github',
  logo_gitlab: 'logo-gitlab',
  logo_google: 'logo-google',
  logo_paypal: 'logo-paypal',
  blockchain1: 'blockchain1',
  logo_nodejs: 'logo-nodejs',
  voicemail_2: 'voicemail_2',
  devices_off: 'devices_off',
  exit_to_app: 'exit_to_app',
  ring_volume: 'ring_volume',
  call_missed: 'call_missed',
  privacy_tip: 'privacy_tip',
  dual_screen: 'dual_screen',
  engineering: 'engineering',
  fast_rewind: 'fast_rewind',
  reset_password: 'construct',
  chat_bubbles: 'chatbubbles',
  friendship_reject: 'remove',
  friendship_follower: 'chat',
  rotate_right: 'rotate_right',
  logo_android: 'logo-android',
  logo_angular: 'logo-angular',
  logo_bitcoin: 'logo-bitcoin',
  logo_dropbox: 'logo-dropbox',
  logo_firefox: 'logo-firefox',
  logo_twitter: 'logo-twitter',
  logo_youtube: 'logo-youtube',
  logo_windows: 'logo-windows',
  wifi_calling: 'wifi_calling',
  phone_locked: 'phone_locked',
  phone_missed: 'phone_missed',
  call_quality: 'call_quality',
  screen_share: 'screen_share',
  room_service: 'room_service',
  other_houses: 'other_houses',
  dock_to_left: 'dock_to_left',
  keyboard_off: 'keyboard_off',
  cameraswitch: 'cameraswitch',
  architecture: 'architecture',
  fast_forward: 'fast_forward',
  arrow_dropdown: 'caret-down',
  string_resources: 'pricetags',
  swap_horiz: 'swap-horizontal',
  arrow_forward: 'arrow-forward',
  password_change: 'lock-closed',
  cloud_offline: 'cloud-offline',
  accessibility: 'accessibility',
  logo_electron: 'logo-electron',
  logo_facebook: 'logo-facebook',
  logo_firebase: 'logo-firebase',
  logo_linkedin: 'logo-linkedin',
  logo_markdown: 'logo-markdown',
  logo_whatsapp: 'logo-whatsapp',
  phone_enabled: 'phone_enabled',
  phone_in_talk: 'phone_in_talk',
  call_received: 'call_received',
  print_connect: 'print_connect',
  screen_record: 'screen_record',
  shield_person: 'shield_person',
  verified_user: 'verified_user',
  network_check: 'network_check',
  encrypted_off: 'encrypted_off',
  dock_to_right: 'dock_to_right',
  electric_bolt: 'electric_bolt',
  color_palette: 'color-palette',
  friendship_accept: 'checkmark',
  friendship_following: 'comment',
  checkmark_done: 'checkmark-done',
  logo_instagram: 'logo-instagram',
  logo_wordpress: 'logo-wordpress',
  devices_fold_2: 'devices_fold_2',
  perm_phone_msg: 'perm_phone_msg',
  phone_callback: 'phone_callback',
  phone_disabled: 'phone_disabled',
  settings_phone: 'settings_phone',
  print_disabled: 'print_disabled',
  wifi_tethering: 'wifi_tethering',
  speech_to_text: 'speech_to_text',
  mouse_lock_off: 'mouse_lock_off',
  directions_car: 'directions_car',
  local_shipping: 'local_shipping',
  cast_connected: 'cast_connected',
  voice_chat_off: 'voice_chat_off',
  logo_javascript: 'logo-javascript',
  phone_forwarded: 'phone_forwarded',
  menu_vertical: 'ellipsis-vertical',
  chatbox_ellipses: 'chatbox-ellipses',
  checkmark_circle: 'checkmark-circle',
  blockchain_block: 'blockchain-block',
  blockchain_token: 'blockchain-token',
  document_scanner: 'document_scanner',
  splitscreen_left: 'splitscreen_left',
  close_fullscreen: 'close_fullscreen',
  friendship_unblock: 'thumbs_up_down',
  friendship_unfollow: 'highlight_off',
  select_all_group: 'select_all_group',
  stop_screen_share: 'stop_screen_share',
  missed_video_call: 'missed_video_call',
  keyboard_lock_off: 'keyboard_lock_off',
  video_camera_back: 'video_camera_back',
  menu_horizontal: 'ellipsis-horizontal',
  logo_stackoverflow: 'logo-stackoverflow',
  blockchain_address: 'blockchain-address',
  logo_web_component: 'logo-web-component',
  wifi_calling_bar_1: 'wifi_calling_bar_1',
  wifi_calling_bar_2: 'wifi_calling_bar_2',
  video_camera_front: 'video_camera_front',
  picture_in_picture: 'picture_in_picture',
  deselect_all_group: 'deselect_all_group',
  logo_apple_appstore: 'logo-apple-appstore',
  signal_cellular_alt: 'signal_cellular_alt',
  signal_disconnected: 'signal_disconnected',
  friendship_cancel: 'remove_circle_outline',
  inverse_selection: 'remove-circle-outline',
  chat_bubble_ellipses: 'chatbubble-ellipses',
  call_missed_outgoing: 'call_missed_outgoing',
  audio_video_receiver: 'audio_video_receiver',
  admin_panel_settings: 'admin_panel_settings',
  friendship_unknown: 'radio_button_unchecked',
  checkmark_done_circle: 'checkmark-done-circle',
  video_camera_back_add: 'video_camera_back_add',
  logo_google_playstore: 'logo-google-playstore',
  blockchain_transaction: 'blockchain-transaction',
  video_camera_front_off: 'video_camera_front_off',
  display_external_input: 'display_external_input',
  picture_in_picture_off: 'picture_in_picture_off',
  phone_bluetooth_speaker: 'phone_bluetooth_speaker',
  desktop_access_disabled: 'desktop_access_disabled',
  blockchain_smart_contract: 'blockchain-smart-contract',
  blockchain_contract_address: 'blockchain-contract-address',
};

export const XCustomIcons: string[] = [
  XIconNames.ol,
  XIconNames.ul,
  XIconNames.tv,
  XIconNames.dns,
  XIconNames.dvr,
  XIconNames.fax,
  XIconNames.mic,
  XIconNames.mop,
  XIconNames.call,
  XIconNames.tabs,
  XIconNames.cast,
  XIconNames.wifi,
  XIconNames.stop,
  XIconNames.cast,
  XIconNames.link,
  XIconNames.bold,
  XIconNames.code,
  XIconNames.undo,
  XIconNames.redo,
  XIconNames.next,
  XIconNames.last,
  XIconNames.list,
  XIconNames.toast,
  XIconNames.cards,
  XIconNames.mouse,
  XIconNames.album,
  XIconNames.badge,
  XIconNames.radio,
  XIconNames.watch,
  XIconNames.guide,
  XIconNames.image,
  XIconNames.quote,
  XIconNames.touch,
  XIconNames.error,
  XIconNames.first,
  XIconNames.reply,
  XIconNames.label,
  XIconNames.movie,
  XIconNames.repeat,
  XIconNames.report,
  XIconNames.policy,
  XIconNames.router,
  XIconNames.shield,
  XIconNames.stylus,
  XIconNames.tv_off,
  XIconNames.swords,
  XIconNames.nearby,
  XIconNames.flight,
  XIconNames.memory,
  XIconNames.replay,
  XIconNames.expand,
  XIconNames.italic,
  XIconNames.toolbar,
  XIconNames.devices,
  XIconNames.dialogs,
  XIconNames.dialpad,
  XIconNames.vpn_key,
  XIconNames.widgets,
  XIconNames.passkey,
  XIconNames.mic_off,
  XIconNames.monitor,
  XIconNames.gpp_bad,
  XIconNames.charger,
  XIconNames.devices,
  XIconNames.sd_card,
  XIconNames.capture,
  XIconNames.live_tv,
  XIconNames.monitor,
  XIconNames.preview,
  XIconNames.heading,
  XIconNames.dropdown,
  XIconNames.add_call,
  XIconNames.sim_card,
  XIconNames.security,
  XIconNames.password,
  XIconNames.podcasts,
  XIconNames.wifi_off,
  XIconNames.no_sound,
  XIconNames.keyboard,
  XIconNames.exercise,
  XIconNames.computer,
  XIconNames.videocam,
  XIconNames.autoplay,
  XIconNames.autostop,
  XIconNames.previous,
  XIconNames.collapse,
  XIconNames.verified,
  XIconNames.touch_app,
  XIconNames.voicemail,
  XIconNames.call_made,
  XIconNames.subtitles,
  XIconNames.music_off,
  XIconNames.encrypted,
  XIconNames.apartment,
  XIconNames.dark_mode,
  XIconNames.deskphone,
  XIconNames.autopause,
  XIconNames.label_off,
  XIconNames.data_table,
  XIconNames.device_hub,
  XIconNames.call_split,
  XIconNames.router_off,
  XIconNames.light_mode,
  XIconNames.mouse_lock,
  XIconNames.nightlight,
  XIconNames.play_arrow,
  XIconNames.video_call,
  XIconNames.video_chat,
  XIconNames.video_file,
  XIconNames.voice_chat,
  XIconNames.fit_screen,
  XIconNames.fullscreen,
  XIconNames.blockchain,
  XIconNames.voicemail_2,
  XIconNames.devices_off,
  XIconNames.exit_to_app,
  XIconNames.ring_volume,
  XIconNames.call_missed,
  XIconNames.privacy_tip,
  XIconNames.dual_screen,
  XIconNames.engineering,
  XIconNames.fast_rewind,
  XIconNames.blockchain1,
  XIconNames.logo_docker,
  XIconNames.logo_gitlab,
  XIconNames.logo_paypal,
  XIconNames.rotate_left,
  XIconNames.wifi_calling,
  XIconNames.phone_locked,
  XIconNames.phone_missed,
  XIconNames.call_quality,
  XIconNames.screen_share,
  XIconNames.room_service,
  XIconNames.other_houses,
  XIconNames.dock_to_left,
  XIconNames.keyboard_off,
  XIconNames.cameraswitch,
  XIconNames.architecture,
  XIconNames.rotate_right,
  XIconNames.fast_forward,
  XIconNames.phone_enabled,
  XIconNames.phone_in_talk,
  XIconNames.call_received,
  XIconNames.print_connect,
  XIconNames.screen_record,
  XIconNames.shield_person,
  XIconNames.verified_user,
  XIconNames.network_check,
  XIconNames.encrypted_off,
  XIconNames.dock_to_right,
  XIconNames.electric_bolt,
  XIconNames.devices_fold_2,
  XIconNames.perm_phone_msg,
  XIconNames.phone_callback,
  XIconNames.phone_disabled,
  XIconNames.settings_phone,
  XIconNames.print_disabled,
  XIconNames.wifi_tethering,
  XIconNames.speech_to_text,
  XIconNames.mouse_lock_off,
  XIconNames.directions_car,
  XIconNames.local_shipping,
  XIconNames.cast_connected,
  XIconNames.voice_chat_off,
  XIconNames.phone_forwarded,
  XIconNames.document_scanner,
  XIconNames.splitscreen_left,
  XIconNames.close_fullscreen,
  XIconNames.blockchain_block,
  XIconNames.blockchain_token,
  XIconNames.select_all_group,
  XIconNames.friendship_block,
  XIconNames.stop_screen_share,
  XIconNames.missed_video_call,
  XIconNames.keyboard_lock_off,
  XIconNames.video_camera_back,
  XIconNames.friendship_friend,
  XIconNames.friendship_reject,
  XIconNames.friendship_cancel,
  XIconNames.wifi_calling_bar_1,
  XIconNames.wifi_calling_bar_2,
  XIconNames.video_camera_front,
  XIconNames.picture_in_picture,
  XIconNames.blockchain_address,
  XIconNames.deselect_all_group,
  XIconNames.friendship_unblock,
  XIconNames.friendship_unknown,
  XIconNames.signal_cellular_alt,
  XIconNames.signal_disconnected,
  XIconNames.friendship_follower,
  XIconNames.friendship_unfollow,
  XIconNames.call_missed_outgoing,
  XIconNames.audio_video_receiver,
  XIconNames.admin_panel_settings,
  XIconNames.friendship_following,
  XIconNames.video_camera_back_add,
  XIconNames.video_camera_front_off,
  XIconNames.display_external_input,
  XIconNames.picture_in_picture_off,
  XIconNames.blockchain_transaction,
  XIconNames.phone_bluetooth_speaker,
  XIconNames.desktop_access_disabled,
  XIconNames.blockchain_smart_contract,
  XIconNames.blockchain_contract_address,
];

export type XIconSlot = 'start' | 'end';

//
//#region Container ...
export interface IXIconContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XIconActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  slot: XStandardType<XIconSlot>;
  name: XStandardType<XIconIdentifier>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  applyDirection: XStandardType<boolean>;

  //
  selectable: XStandardType<boolean>;
  clickable: XStandardType<boolean>;
  clicked: EventEmitter<Event>;
}

@Injectable({
  providedIn: 'root',
})
export class XIconContainer
  extends XBaseContainer
  implements Readonly<IXIconContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XIconActionModel> =
    new Subject<XIconActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly slot: XStandardType<XIconSlot> = 'start';
  readonly name: XStandardType<XIconIdentifier>;

  //
  readonly color: XStandardType<XColorIdentifier>;
  readonly foregroundColor: XStandardType<boolean> = true;

  readonly selectable: XStandardType<boolean> = false;
  readonly clickable: XStandardType<boolean> = false;
  readonly applyDirection: XStandardType<boolean> = false;

  //
  readonly clicked = new EventEmitter<Event>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XIconContainerComponent
  extends XBaseContainerComponent
  implements IXIconContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XIconActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  slot: XStandardType<XIconSlot> = this.propertyProvider.slot;

  @Input()
  name: XStandardType<XIconIdentifier> = this.propertyProvider.name;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  @Input()
  selectable: XStandardType<boolean> = this.propertyProvider.selectable;

  @Input()
  clickable: XStandardType<boolean> = this.propertyProvider.clickable;

  @Input()
  applyDirection: XStandardType<boolean> = this.propertyProvider.applyDirection;

  @Output()
  clicked: EventEmitter<Event> = new EventEmitter<Event>();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XIconContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXIconContainer
  ): Observable<IXIconContainer> {
    return forkJoin({
      //
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),

      //
      slot: this.getValue(container ? container.slot : this.slot),
      name: this.getValue(container ? container.name : this.name),
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),
      selectable: this.getValue(
        container ? container.selectable : this.selectable
      ),
      clickable: this.getValue(
        container ? container.clickable : this.clickable
      ),
      applyDirection: this.getValue(
        container ? container.applyDirection : this.applyDirection
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          clicked: this.clicked,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-ide\x-ide.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [subTitle]="getResourceValue(cardSubTitle) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <div
    class="x-component-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <!-- Toolbar -->
    <x-action-bar
      [color]="actionBarColor"
      [cssClass]="actionBarCssClass"
      [actionProvider]="actionBarActionProvider"
      [foregroundColor]="actionBarForegroundColor"
      (actionFired)="handleActionBarActionFired($event)"
      *ngIf="
        (getValue(showActionBar) | async) &&
        (hasChildValue(actionBarActions) | async) &&
        (hasChildValue(availableActionBarItems) | async)
      "
      [actions]="getArrayValue(actionBarActions) | async"
    ></x-action-bar>

    <!-- Tabs  -->
    <ng-container *ngIf="(countChildsValue(Sessions) | async) > 1">
      <x-tabs
        [loopTabs]="true"
        [swipable]="false"
        [actionProvider]="tabsActionProvider"
        [alignTabs]="getValue(alignTabs) | async"
        [headerPosition]="TabHeaderPositions.ABOVE"
        [tabIndicatorColor]="ColorNames.Transparent"
        [headerColor]="getValue(tabHeaderColor) | async"
        [activeTabColor]="getValue(activeTabColor) | async"
        (selectedTabChange)="handleTabsSelectedTabChanged($event)"
      >
        <x-tab *ngFor="let tab of Sessions">
          <div x-tab-label>
            {{ tab.name }}
          </div>
        </x-tab>
      </x-tabs>
    </ng-container>

    <!-- Editor -->
    <div class="code-editor" #codeEditor></div>
  </div>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-ide\x-ide.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-ide\x-ide.component.ts

import {
  Component,
  ViewChild,
  ElementRef,
  HostListener,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XIDEMode,
  XIDETheme,
  XIDEAction,
  XIDEActionModel,
  XIDEMinFontSize,
  XIDEMaxFontSize,
  XIDEActionBarItem,
  XIDETabDescriptor,
  XIDEModeIdentifier,
  XIDEDefaultTabName,
  XIDEThemeIdentifier,
  XIDEContainerComponent,
} from './x-ide.typings';
import {
  nameof,
  toArray,
  isNumber,
  toBoolean,
  isBoolean,
  countChild,
  XStandardType,
  toNormalString,
  XOneOrManyType,
  isNullOrUndefined,
  isNullOrEmptyString,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XTabAlign,
  XTabsAction,
  XTabChangeEvent,
  XTabsActionModel,
  XTabsHeaderPosition,
} from '../x-tabs/x-tabs.typings';
import * as ace from 'ace-builds';
import {
  XActionBarItem,
  XActionBarItemSlot,
  XActionBarActionFiredModel,
} from '../x-action-bar/x-action-bar.typings';
import { concatMap, map } from 'rxjs/operators';
import 'ace-builds/src-noconflict/ext-beautify';
import { XIconNames } from '../x-icon/x-icon.typings';
import 'ace-builds/src-noconflict/ext-language_tools';
import { forkJoin, Subject, Subscription } from 'rxjs';
import { XPopoverOption } from '../dialogs/x-popover/x-popover.typings';

//
//#region Themes ...
import 'ace-builds/src-noconflict/theme-ambiance';
import 'ace-builds/src-noconflict/theme-chaos';
import 'ace-builds/src-noconflict/theme-chrome';
import 'ace-builds/src-noconflict/theme-cloud9_day';
import 'ace-builds/src-noconflict/theme-cloud9_night';
import 'ace-builds/src-noconflict/theme-cloud9_night_low_color';
import 'ace-builds/src-noconflict/theme-clouds';
import 'ace-builds/src-noconflict/theme-clouds_midnight';
import 'ace-builds/src-noconflict/theme-cobalt';
import 'ace-builds/src-noconflict/theme-crimson_editor';
import 'ace-builds/src-noconflict/theme-dawn';
import 'ace-builds/src-noconflict/theme-dracula';
import 'ace-builds/src-noconflict/theme-dreamweaver';
import 'ace-builds/src-noconflict/theme-eclipse';
import 'ace-builds/src-noconflict/theme-github';
import 'ace-builds/src-noconflict/theme-gob';
import 'ace-builds/src-noconflict/theme-gruvbox';
import 'ace-builds/src-noconflict/theme-gruvbox_dark_hard';
import 'ace-builds/src-noconflict/theme-gruvbox_light_hard';
import 'ace-builds/src-noconflict/theme-idle_fingers';
import 'ace-builds/src-noconflict/theme-iplastic';
import 'ace-builds/src-noconflict/theme-katzenmilch';
import 'ace-builds/src-noconflict/theme-kr_theme';
import 'ace-builds/src-noconflict/theme-kuroir';
import 'ace-builds/src-noconflict/theme-merbivore';
import 'ace-builds/src-noconflict/theme-merbivore_soft';
import 'ace-builds/src-noconflict/theme-mono_industrial';
import 'ace-builds/src-noconflict/theme-monokai';
import 'ace-builds/src-noconflict/theme-nord_dark';
import 'ace-builds/src-noconflict/theme-one_dark';
import 'ace-builds/src-noconflict/theme-pastel_on_dark';
import 'ace-builds/src-noconflict/theme-solarized_dark';
import 'ace-builds/src-noconflict/theme-solarized_light';
import 'ace-builds/src-noconflict/theme-sqlserver';
import 'ace-builds/src-noconflict/theme-terminal';
import 'ace-builds/src-noconflict/theme-textmate';
import 'ace-builds/src-noconflict/theme-tomorrow';
import 'ace-builds/src-noconflict/theme-tomorrow_night';
import 'ace-builds/src-noconflict/theme-tomorrow_night_blue';
import 'ace-builds/src-noconflict/theme-tomorrow_night_bright';
import 'ace-builds/src-noconflict/theme-tomorrow_night_eighties';
import 'ace-builds/src-noconflict/theme-twilight';
import 'ace-builds/src-noconflict/theme-vibrant_ink';
import 'ace-builds/src-noconflict/theme-xcode';
//#endregion

//
//#region Modes ...
import 'ace-builds/src-noconflict/mode-abap';
import 'ace-builds/src-noconflict/mode-abc';
import 'ace-builds/src-noconflict/mode-actionscript';
import 'ace-builds/src-noconflict/mode-ada';
import 'ace-builds/src-noconflict/mode-alda';
import 'ace-builds/src-noconflict/mode-apache_conf';
import 'ace-builds/src-noconflict/mode-apex';
import 'ace-builds/src-noconflict/mode-applescript';
import 'ace-builds/src-noconflict/mode-aql';
import 'ace-builds/src-noconflict/mode-asciidoc';
import 'ace-builds/src-noconflict/mode-asl';
import 'ace-builds/src-noconflict/mode-assembly_x86';
import 'ace-builds/src-noconflict/mode-autohotkey';
import 'ace-builds/src-noconflict/mode-batchfile';
import 'ace-builds/src-noconflict/mode-c9search';
import 'ace-builds/src-noconflict/mode-c_cpp';
import 'ace-builds/src-noconflict/mode-cirru';
import 'ace-builds/src-noconflict/mode-clojure';
import 'ace-builds/src-noconflict/mode-cobol';
import 'ace-builds/src-noconflict/mode-coffee';
import 'ace-builds/src-noconflict/mode-coldfusion';
import 'ace-builds/src-noconflict/mode-crystal';
import 'ace-builds/src-noconflict/mode-csharp';
import 'ace-builds/src-noconflict/mode-csound_document';
import 'ace-builds/src-noconflict/mode-csound_orchestra';
import 'ace-builds/src-noconflict/mode-csound_score';
import 'ace-builds/src-noconflict/mode-csp';
import 'ace-builds/src-noconflict/mode-css';
import 'ace-builds/src-noconflict/mode-curly';
import 'ace-builds/src-noconflict/mode-d';
import 'ace-builds/src-noconflict/mode-dart';
import 'ace-builds/src-noconflict/mode-diff';
import 'ace-builds/src-noconflict/mode-django';
import 'ace-builds/src-noconflict/mode-dockerfile';
import 'ace-builds/src-noconflict/mode-dot';
import 'ace-builds/src-noconflict/mode-drools';
import 'ace-builds/src-noconflict/mode-edifact';
import 'ace-builds/src-noconflict/mode-eiffel';
import 'ace-builds/src-noconflict/mode-ejs';
import 'ace-builds/src-noconflict/mode-elixir';
import 'ace-builds/src-noconflict/mode-elm';
import 'ace-builds/src-noconflict/mode-erlang';
import 'ace-builds/src-noconflict/mode-forth';
import 'ace-builds/src-noconflict/mode-fortran';
import 'ace-builds/src-noconflict/mode-fsharp';
import 'ace-builds/src-noconflict/mode-fsl';
import 'ace-builds/src-noconflict/mode-ftl';
import 'ace-builds/src-noconflict/mode-gcode';
import 'ace-builds/src-noconflict/mode-gherkin';
import 'ace-builds/src-noconflict/mode-gitignore';
import 'ace-builds/src-noconflict/mode-glsl';
import 'ace-builds/src-noconflict/mode-gobstones';
import 'ace-builds/src-noconflict/mode-golang';
import 'ace-builds/src-noconflict/mode-graphqlschema';
import 'ace-builds/src-noconflict/mode-groovy';
import 'ace-builds/src-noconflict/mode-haml';
import 'ace-builds/src-noconflict/mode-handlebars';
import 'ace-builds/src-noconflict/mode-haskell';
import 'ace-builds/src-noconflict/mode-haskell_cabal';
import 'ace-builds/src-noconflict/mode-haxe';
import 'ace-builds/src-noconflict/mode-hjson';
import 'ace-builds/src-noconflict/mode-html';
import 'ace-builds/src-noconflict/mode-html_elixir';
import 'ace-builds/src-noconflict/mode-html_ruby';
import 'ace-builds/src-noconflict/mode-ini';
import 'ace-builds/src-noconflict/mode-io';
import 'ace-builds/src-noconflict/mode-ion';
import 'ace-builds/src-noconflict/mode-jack';
import 'ace-builds/src-noconflict/mode-jade';
import 'ace-builds/src-noconflict/mode-java';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/mode-json';
import 'ace-builds/src-noconflict/mode-json5';
import 'ace-builds/src-noconflict/mode-jsoniq';
import 'ace-builds/src-noconflict/mode-jsp';
import 'ace-builds/src-noconflict/mode-jssm';
import 'ace-builds/src-noconflict/mode-jsx';
import 'ace-builds/src-noconflict/mode-julia';
import 'ace-builds/src-noconflict/mode-kotlin';
import 'ace-builds/src-noconflict/mode-latex';
import 'ace-builds/src-noconflict/mode-latte';
import 'ace-builds/src-noconflict/mode-less';
import 'ace-builds/src-noconflict/mode-liquid';
import 'ace-builds/src-noconflict/mode-lisp';
import 'ace-builds/src-noconflict/mode-livescript';
import 'ace-builds/src-noconflict/mode-logiql';
import 'ace-builds/src-noconflict/mode-logtalk';
import 'ace-builds/src-noconflict/mode-lsl';
import 'ace-builds/src-noconflict/mode-lua';
import 'ace-builds/src-noconflict/mode-luapage';
import 'ace-builds/src-noconflict/mode-lucene';
import 'ace-builds/src-noconflict/mode-makefile';
import 'ace-builds/src-noconflict/mode-markdown';
import 'ace-builds/src-noconflict/mode-mask';
import 'ace-builds/src-noconflict/mode-matlab';
import 'ace-builds/src-noconflict/mode-maze';
import 'ace-builds/src-noconflict/mode-mediawiki';
import 'ace-builds/src-noconflict/mode-mel';
import 'ace-builds/src-noconflict/mode-mips';
import 'ace-builds/src-noconflict/mode-mixal';
import 'ace-builds/src-noconflict/mode-mushcode';
import 'ace-builds/src-noconflict/mode-mysql';
import 'ace-builds/src-noconflict/mode-nginx';
import 'ace-builds/src-noconflict/mode-nim';
import 'ace-builds/src-noconflict/mode-nix';
import 'ace-builds/src-noconflict/mode-nsis';
import 'ace-builds/src-noconflict/mode-nunjucks';
import 'ace-builds/src-noconflict/mode-objectivec';
import 'ace-builds/src-noconflict/mode-ocaml';
import 'ace-builds/src-noconflict/mode-partiql';
import 'ace-builds/src-noconflict/mode-pascal';
import 'ace-builds/src-noconflict/mode-perl';
import 'ace-builds/src-noconflict/mode-pgsql';
import 'ace-builds/src-noconflict/mode-php';
import 'ace-builds/src-noconflict/mode-php_laravel_blade';
import 'ace-builds/src-noconflict/mode-pig';
import 'ace-builds/src-noconflict/mode-plain_text';
import 'ace-builds/src-noconflict/mode-powershell';
import 'ace-builds/src-noconflict/mode-praat';
import 'ace-builds/src-noconflict/mode-prisma';
import 'ace-builds/src-noconflict/mode-prolog';
import 'ace-builds/src-noconflict/mode-properties';
import 'ace-builds/src-noconflict/mode-protobuf';
import 'ace-builds/src-noconflict/mode-puppet';
import 'ace-builds/src-noconflict/mode-python';
import 'ace-builds/src-noconflict/mode-qml';
import 'ace-builds/src-noconflict/mode-r';
import 'ace-builds/src-noconflict/mode-raku';
import 'ace-builds/src-noconflict/mode-razor';
import 'ace-builds/src-noconflict/mode-rdoc';
import 'ace-builds/src-noconflict/mode-red';
import 'ace-builds/src-noconflict/mode-redshift';
import 'ace-builds/src-noconflict/mode-rhtml';
import 'ace-builds/src-noconflict/mode-robot';
import 'ace-builds/src-noconflict/mode-rst';
import 'ace-builds/src-noconflict/mode-ruby';
import 'ace-builds/src-noconflict/mode-rust';
import 'ace-builds/src-noconflict/mode-sac';
import 'ace-builds/src-noconflict/mode-sass';
import 'ace-builds/src-noconflict/mode-scad';
import 'ace-builds/src-noconflict/mode-scala';
import 'ace-builds/src-noconflict/mode-scheme';
import 'ace-builds/src-noconflict/mode-scrypt';
import 'ace-builds/src-noconflict/mode-scss';
import 'ace-builds/src-noconflict/mode-sh';
import 'ace-builds/src-noconflict/mode-sjs';
import 'ace-builds/src-noconflict/mode-slim';
import 'ace-builds/src-noconflict/mode-smarty';
import 'ace-builds/src-noconflict/mode-smithy';
import 'ace-builds/src-noconflict/mode-snippets';
import 'ace-builds/src-noconflict/mode-soy_template';
import 'ace-builds/src-noconflict/mode-space';
import 'ace-builds/src-noconflict/mode-sparql';
import 'ace-builds/src-noconflict/mode-sql';
import 'ace-builds/src-noconflict/mode-sqlserver';
import 'ace-builds/src-noconflict/mode-stylus';
import 'ace-builds/src-noconflict/mode-svg';
import 'ace-builds/src-noconflict/mode-swift';
import 'ace-builds/src-noconflict/mode-tcl';
import 'ace-builds/src-noconflict/mode-terraform';
import 'ace-builds/src-noconflict/mode-tex';
import 'ace-builds/src-noconflict/mode-text';
import 'ace-builds/src-noconflict/mode-textile';
import 'ace-builds/src-noconflict/mode-toml';
import 'ace-builds/src-noconflict/mode-tsx';
import 'ace-builds/src-noconflict/mode-turtle';
import 'ace-builds/src-noconflict/mode-twig';
import 'ace-builds/src-noconflict/mode-typescript';
import 'ace-builds/src-noconflict/mode-vala';
import 'ace-builds/src-noconflict/mode-vbscript';
import 'ace-builds/src-noconflict/mode-velocity';
import 'ace-builds/src-noconflict/mode-verilog';
import 'ace-builds/src-noconflict/mode-vhdl';
import 'ace-builds/src-noconflict/mode-visualforce';
import 'ace-builds/src-noconflict/mode-wollok';
import 'ace-builds/src-noconflict/mode-xml';
import 'ace-builds/src-noconflict/mode-xquery';
import 'ace-builds/src-noconflict/mode-yaml';
import 'ace-builds/src-noconflict/mode-zeek';
//#endregion

@Component({
  selector: 'x-ide',
  templateUrl: './x-ide.component.html',
  styleUrls: ['./x-ide.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XIDEComponent extends XIDEContainerComponent {
  //
  //#region Props ...
  //
  //#region Private Props ...
  private CODE_EDITOR_BEAUTIFY: any = null;
  private CODE_EDITOR: ace.Ace.Editor = null;
  private CODE_EDITOR_UNDO_MANAGER: any;

  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private CONTENT_CHANGE_SUBJECT = new Subject<string>();

  //
  readonly Modes = Object.assign({}, XIDEMode);
  readonly Themes = Object.assign({}, XIDETheme);
  readonly TabAligns = Object.assign({}, XTabAlign);
  readonly IconNames = Object.assign({}, XIconNames);
  readonly ColorNames = Object.assign({}, XColorWithBrightness);
  readonly ActionBarItems = Object.assign({}, XIDEActionBarItem);
  readonly ActionBarItemSlots = Object.assign({}, XActionBarItemSlot);
  readonly TabHeaderPositions = Object.assign({}, XTabsHeaderPosition);

  //
  readonly tabsActionProvider = new Subject<XTabsActionModel>();
  //#endregion

  //
  @ViewChild('codeEditor')
  codeEditorElmRef: ElementRef<HTMLDivElement>;

  @HostListener('resize', ['$event'])
  onResize (event: any) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.CODE_EDITOR.resize();
  }

  //
  lastContent: string;
  currentTab = XIDEDefaultTabName;

  //
  readonly Sessions: Pick<
    XIDETabDescriptor,
    'name' | 'session' | 'canSave' | 'canBeautify'
  >[] = [];
  //#endregion

  //
  //#region Constructor ...
  //#endregion

  //
  //#region LifeCycles ...
  afterViewInit () {
    super.afterViewInit();

    //
    this.prepareCodeEditor();
  }

  async onChange (changeKeys: string[]): Promise<void> {
    //
    //#region Action Provider Changed ...
    //
    const isActionProviderChaged = changeKeys.includes(
      nameof<XIDEComponent>('actionProvider')
    );
    if (isActionProviderChaged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Theme Changed ...
    const isThemeChanged = changeKeys.includes(nameof<XIDEComponent>('theme'));
    if (isThemeChanged) {
      this.setTheme(this.theme);
    }
    //#endregion

    //
    //#region Mode Changed ...
    const isModeChanged = changeKeys.includes(nameof<XIDEComponent>('mode'));
    if (isModeChanged) {
      this.setMode(this.mode);
    }
    //#endregion

    //
    //#region MinLines Changed ...
    const isMinLinesChanged = changeKeys.includes(
      nameof<XIDEComponent>('minLines')
    );
    if (isMinLinesChanged) {
      this.setMinLines(this.minLines);
    }
    //#endregion

    //
    //#region MaxLines Changed ...
    const isMaxLinesChanged = changeKeys.includes(
      nameof<XIDEComponent>('maxLines')
    );
    if (isMaxLinesChanged) {
      this.setMaxLines(this.maxLines);
    }
    //#endregion

    //
    //#region FontSize Changed ...
    const isFontSizeChanged = changeKeys.includes(
      nameof<XIDEComponent>('fontSize')
    );
    if (isFontSizeChanged) {
      this.setFontSize(this.fontSize);
    }
    //#endregion

    //
    //#region Readonly Changed ...
    const isReadonlyChanged = changeKeys.includes(
      nameof<XIDEComponent>('readonly')
    );
    if (isReadonlyChanged) {
      this.setReadOnly(this.readonly);
    }
    //#endregion

    //
    //#region Content Changed ...
    const isContentChanged = changeKeys.includes(
      nameof<XIDEComponent>('content')
    );
    if (isContentChanged) {
      this.setContent(this.content);
    }
    //#endregion

    //
    //#region WordWrapping Changed ...
    const isWordWrappingChanged = changeKeys.includes(
      nameof<XIDEComponent>('wordWrapping')
    );
    if (isWordWrappingChanged) {
      this.setWordWrapping(this.wordWrapping);
    }
    //#endregion

    //
    //#region ShowPrintMargin Changed ...
    const isShowPrintMarginChanged = changeKeys.includes(
      nameof<XIDEComponent>('showPrintMargin')
    );
    if (isShowPrintMarginChanged) {
      this.setShowPrintMargin(this.showPrintMargin);
    }
    //#endregion

    //
    //#region HighlightActiveLine Changed ...
    const isHighlightActiveLineChanged = changeKeys.includes(
      nameof<XIDEComponent>('highlightActiveLine')
    );
    if (isHighlightActiveLineChanged) {
      this.setHighlightActiveLine(this.highlightActiveLine);
    }
    //#endregion

    //
    //#region BasicAutocompletion Changed ...
    const isBasicAutocompletionChanged = changeKeys.includes(
      nameof<XIDEComponent>('basicAutocompletion')
    );
    if (isBasicAutocompletionChanged) {
      this.setBasicAutocompletion(this.basicAutocompletion);
    }
    //#endregion

    //
    //#region DefaultTabName Changed ...
    const isDefaultTabNameChanged = changeKeys.includes(
      nameof<XIDEComponent>('defaultTabName')
    );
    if (isDefaultTabNameChanged) {
      await this.handleDefaultTabNameChanged(this.defaultTabName);
    }
    //#endregion

    //
    //#region ActionBar ...
    const isDefaultActionBarItemColorChanged = changeKeys.includes(
      nameof<XIDEComponent>('defaultActionBarItemColor')
    );
    const isAvailableActionBarItemsChanged = changeKeys.includes(
      nameof<XIDEComponent>('availableActionBarItems')
    );
    if (
      isAvailableActionBarItemsChanged ||
      isDefaultActionBarItemColorChanged
    ) {
      //
      if (isNullOrUndefined(this.availableActionBarItems)) {
        this.availableActionBarItems =
          this.propertyProvider.availableActionBarItems;
      }
      await this.prepareActionBar();
    }
    //#endregion
  }

  onDestroy () {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  registerViewHandlers (): void {
    super.registerViewHandlers();

    //
    this.registerActionProvider();

    //
    this.applyTakeUntilWrapper(
      this.CONTENT_CHANGE_SUBJECT.asObservable()
    ).subscribe(async (content) => {
      //
      if (content === this.lastContent) {
        return;
      }

      //
      this.lastContent = content;
      this.updateCurrentTabDescriptor({
        canSave: true,
        canBeautify: true,
      });

      //
      this.contentChange.emit(content);

      //
      await this.prepareActionBar();
    });
  }
  //#endregion

  //
  //#region Task Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  //#endregion

  //
  //#region UI Handlers ...
  async handleActionBarActionFired (model: XActionBarActionFiredModel) {
    //
    if (isNullOrUndefined(model) || isNullOrEmptyString(model.action)) {
      return;
    }

    //
    switch (model.action) {
      //
      case this.ActionBarItems.ChangeMode:
        this.handleActionModeFired(model.event);
        break;

      //
      case this.ActionBarItems.ChangeTheme:
        this.handleActionThemFired(model.event);
        break;

      //
      case this.ActionBarItems.Close:
        this.closeTab(this.currentTab);
        break;

      //
      case this.ActionBarItems.Undo:
        this.undo();
        break;

      //
      case this.ActionBarItems.Redo:
        this.redo();
        break;

      //
      case this.ActionBarItems.Beautify:
        await this.beautifyCode();
        break;

      //
      case this.ActionBarItems.Save:
        //
        this.updateCurrentTabDescriptor({
          canSave: false,
        });
        this.prepareActionBar();
        break;
    }

    //
    this.actionBarActionFired.emit(model);
  }

  async handleTabsSelectedTabChanged (event: XTabChangeEvent) {
    //
    if (
      event.index < 0 ||
      isNullOrUndefined(event) ||
      event.index > this.Sessions.length ||
      isNullOrUndefined(this.CODE_EDITOR)
    ) {
      return;
    }

    //
    const descriptor = this.Sessions[event.index];
    this.showTab(descriptor.name);
  }
  //#endregion

  //
  //#region Actions ...
  setTheme (theme: XStandardType<XIDEThemeIdentifier>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(theme).subscribe((value) => {
      //
      if (isNullOrEmptyString(value)) {
        return;
      }

      //
      this.CODE_EDITOR.setTheme(value);
    });
  }

  setMode (mode: XStandardType<XIDEModeIdentifier>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(mode).subscribe((value) => {
      //
      if (isNullOrEmptyString(value)) {
        return;
      }

      //
      this.CODE_EDITOR.getSession().setMode(value);
      this.detectChanges();
    });
  }

  setMinLines (minLines: XStandardType<number>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(minLines).subscribe((value) => {
      //
      if (value <= 0) {
        return;
      }

      //
      this.CODE_EDITOR.setOption('minLines', value);
      this.detectChanges();
    });
  }

  setMaxLines (maxLines: XStandardType<number>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(maxLines).subscribe((value) => {
      //
      if (value <= 0) {
        return;
      }

      //
      this.CODE_EDITOR.setOption('maxLines', value);
      this.detectChanges();
    });
  }

  setFontSize (fontSize: XStandardType<number>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(fontSize).subscribe((value) => {
      //
      if (value <= XIDEMinFontSize || value >= XIDEMaxFontSize) {
        return;
      }

      //
      this.CODE_EDITOR.setFontSize(value);
      this.detectChanges();
    });
  }

  setReadOnly (readonly: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(readonly).subscribe((value) => {
      //
      this.CODE_EDITOR.setReadOnly(value);
      this.detectChanges();
    });
  }

  getFontSize () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return '';
    }

    //
    return this.CODE_EDITOR.getFontSize();
  }

  setHighlightActiveLine (highlightActiveLine: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(highlightActiveLine).subscribe((value) => {
      //
      this.CODE_EDITOR.setOption('highlightActiveLine', value);
      this.detectChanges();
    });
  }

  setBasicAutocompletion (basicAutocompletion: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(basicAutocompletion).subscribe((value) => {
      //
      this.CODE_EDITOR.setOption('enableBasicAutocompletion' as any, value);
      this.detectChanges();
    });
  }

  setWordWrapping (wordWrapping: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(wordWrapping).subscribe((value) => {
      //
      this.CODE_EDITOR.getSession().setUseWrapMode(value);
      this.detectChanges();
    });
  }

  setShowLineNumbers (showLineNumbers: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(showLineNumbers).subscribe((value) => {
      //
      this.CODE_EDITOR.setOption('showLineNumbers', value);
      this.detectChanges();
    });
  }

  setShowPrintMargin (showPrintMargin: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(showPrintMargin).subscribe((value) => {
      //
      this.CODE_EDITOR.setShowPrintMargin(value);
      this.detectChanges();
    });
  }

  setShowFoldWidgets (showFoldWidgets: XStandardType<boolean>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(showFoldWidgets).subscribe((value) => {
      //
      this.CODE_EDITOR.setShowFoldWidgets(value);
      this.detectChanges();
    });
  }

  //
  //#region IDEEdit ...
  async beautifyCode () {
    //
    if (
      isNullOrUndefined(this.CODE_EDITOR) ||
      isNullOrUndefined(this.CODE_EDITOR_BEAUTIFY)
    ) {
      return;
    }

    //
    const session = this.CODE_EDITOR.getSession();
    this.CODE_EDITOR_BEAUTIFY.beautify(session);

    //
    this.updateCurrentTabDescriptor({
      canBeautify: false,
    });
    await this.prepareActionBar();
  }

  getContent () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return '';
    }

    //
    const result = this.CODE_EDITOR.getValue();
    return result;
  }

  setContent (value: XStandardType<string>) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    this.getValue(value).subscribe((content) => {
      //
      if (isNullOrEmptyString(content)) {
        return;
      }

      //
      if (value === this.getContent()) {
        return;
      }

      //
      this.CODE_EDITOR.setValue(content);
    });
  }

  clearcontent (force = false) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    if (!!force) {
      //
      this.CODE_EDITOR.setValue('');
      this.detectChanges();
    } else {
      //
      this.managerService.dialogService.presentAreYouSureYesNoDialog(() => {
        this.CODE_EDITOR.setValue('');
        this.detectChanges();
      });
    }
  }

  goToLine (lineNumber: number) {
    //
    const totalLines = this.getTotalLines();
    if (
      lineNumber <= 0 ||
      totalLines <= 0 ||
      lineNumber > totalLines ||
      isNullOrUndefined(this.CODE_EDITOR)
    ) {
      return;
    }

    //
    this.CODE_EDITOR.gotoLine(lineNumber, 0, true);
  }

  getTotalLines () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return -1;
    }

    //
    return this.CODE_EDITOR.getSession().getLength();
  }

  canUndo () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return false;
    }

    //
    const session = this.CODE_EDITOR.getSession();
    if (isNullOrUndefined(session)) {
      return false;
    }

    //
    const undoManager = session.getUndoManager();
    if (isNullOrUndefined(undoManager)) {
      return false;
    }

    //
    return undoManager.canUndo();
  }

  undo (dontSelect = true) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    const session = this.CODE_EDITOR.getSession();
    if (isNullOrUndefined(session)) {
      return;
    }

    //
    const undoManager = session.getUndoManager();
    if (isNullOrUndefined(undoManager) || !this.canUndo()) {
      return;
    }

    //
    undoManager.undo(session, dontSelect);
    this.prepareActionBar();
  }

  canRedo () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return false;
    }

    //
    const session = this.CODE_EDITOR.getSession();
    if (isNullOrUndefined(session)) {
      return false;
    }

    //
    const undoManager = session.getUndoManager();
    if (isNullOrUndefined(undoManager)) {
      return false;
    }

    //
    return undoManager.canRedo();
  }

  redo (dontSelect = true) {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    const session = this.CODE_EDITOR.getSession();
    if (isNullOrUndefined(session)) {
      return;
    }

    //
    const undoManager = session.getUndoManager();
    if (isNullOrUndefined(undoManager) || !this.canRedo()) {
      return;
    }

    //
    undoManager.redo(session, dontSelect);
    this.prepareActionBar();
  }

  cleaEditorHistory () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    const session = this.CODE_EDITOR.getSession();
    if (isNullOrUndefined(session)) {
      return;
    }

    //
    const undoManager = session.getUndoManager();
    if (isNullOrUndefined(undoManager)) {
      return;
    }

    //
    undoManager.reset();
    this.prepareActionBar();
  }
  //#endregion

  //
  //#region IDETabs ...
  getTabDescritor (name: string) {
    //
    if (
      isNullOrEmptyString(name) ||
      !this.hasChild(this.Sessions) ||
      isNullOrUndefined(this.CODE_EDITOR)
    ) {
      return null;
    }

    //
    return this.Sessions.find(
      (sd) => toNormalString(sd.name) === toNormalString(name)
    );
  }

  getTabDescritorIndex (name: string) {
    //
    if (
      isNullOrEmptyString(name) ||
      !this.hasChild(this.Sessions) ||
      isNullOrUndefined(this.CODE_EDITOR)
    ) {
      return null;
    }

    //
    return this.Sessions.findIndex(
      (sd) => toNormalString(sd.name) === toNormalString(name)
    );
  }

  getCurrentTabDescritor () {
    return this.getTabDescritor(this.currentTab);
  }

  updateCurrentTabDescriptor (
    options: Pick<XIDETabDescriptor, 'canSave' | 'canBeautify'>
  ) {
    //
    const currentTabDescriptor = this.getCurrentTabDescritor();
    const currentTabdescriptorIndex = this.Sessions.findIndex(
      (sd) => toNormalString(sd.name) === toNormalString(this.currentTab)
    );
    if (
      currentTabdescriptorIndex < 0 ||
      isNullOrUndefined(currentTabDescriptor)
    ) {
      return;
    }

    //
    const optionsKeys = Object.keys(options);
    const hasCanSave = optionsKeys.includes(
      nameof<XIDETabDescriptor>('canSave')
    );
    const hasCanBeautify = optionsKeys.includes(
      nameof<XIDETabDescriptor>('canBeautify')
    );

    //
    const canSave = hasCanSave ? options.canSave : currentTabDescriptor.canSave;
    const canBeautify = hasCanBeautify
      ? options.canBeautify
      : currentTabDescriptor.canBeautify;

    //
    this.Sessions[currentTabdescriptorIndex] = {
      ...currentTabDescriptor,
      canSave,
      canBeautify,
    };

    //
    this.detectChanges();
  }

  addTab (
    tab: Pick<XIDETabDescriptor, 'name' | 'mode' | 'content' | 'forceShow'>
  ) {
    //
    if (isNullOrUndefined(tab) || isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    // Set Default Content if not Provided ...
    if (isNullOrEmptyString(tab.content)) {
      tab.content = '';
    }

    //
    // Set Default Mode if Not Provided ...
    if (
      isNullOrEmptyString(tab.mode) ||
      isNullOrUndefined(tab.mode as XIDEMode) ||
      !isNullOrUndefined(this.getTabDescritor(tab.name))
    ) {
      tab.mode = XIDEMode.Text;
    }

    //
    // Create Editor Session ...
    const session = ace.createEditSession(tab.content, tab.mode as any);
    const undoManager = new this.CODE_EDITOR_UNDO_MANAGER();

    //
    session.setUndoManager(undoManager);
    undoManager.reset();

    //
    this.Sessions.push({
      session,
      name: tab.name,
    });

    //
    if (!!tab.forceShow) {
      //
      this.runTimedOut(() => {
        //
        this.tabsActionProvider.next({
          action: XTabsAction.TO,
          payload: this.Sessions.length - 1,
        });
      }, 500);
    }

    //
    this.detectChanges();
  }

  addTabs (
    tabs: XOneOrManyType<Pick<XIDETabDescriptor, 'name' | 'mode' | 'content'>>
  ) {
    //
    if (!this.hasChild(tabs) || isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    toArray(tabs).forEach((tab) =>
      this.addTab({
        ...tab,
        forceShow: false,
      })
    );
  }

  showTab (name: string) {
    //
    if (
      isNullOrEmptyString(name) ||
      !this.hasChild(this.Sessions) ||
      isNullOrUndefined(this.CODE_EDITOR)
    ) {
      return;
    }

    //
    const tabDescriptor = this.getTabDescritor(name);
    if (isNullOrUndefined(tabDescriptor)) {
      return;
    }

    //
    this.currentTab = tabDescriptor.name;
    this.lastContent = tabDescriptor.session.getValue();
    this.CODE_EDITOR.setSession(tabDescriptor.session);

    //
    this.prepareActionBar();
  }

  closeTab (name: string, force = false) {
    //
    this.getValue(this.defaultTabName).subscribe((defaultTabName) => {
      //
      if (
        isNullOrEmptyString(name) ||
        !this.hasChild(this.Sessions) ||
        isNullOrUndefined(this.CODE_EDITOR) ||
        toNormalString(name) === toNormalString(defaultTabName)
      ) {
        return;
      }

      //
      const destinationTab = this.getTabDescritor(name);
      const destinationTabIndex = this.getTabDescritorIndex(name);
      if (isNullOrUndefined(destinationTab) || destinationTabIndex < 0) {
        return;
      }

      //
      if (!!force || !destinationTab.canSave) {
        //
        this.handleRemoveTab(destinationTab.name, destinationTabIndex);
        this.detectChanges();
      } else {
        //
        this.managerService.dialogService.presentAreYouSureYesNoDialog(() => {
          //
          this.handleRemoveTab(destinationTab.name, destinationTabIndex);
          this.detectChanges();
        });
      }
    });
  }
  //#endregion
  //#endregion

  //
  //#region Abstracts ...
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider () {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XIDEActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe((action) => {
        //
        if (isNullOrUndefined(action) || isNullOrEmptyString(action.action)) {
          return;
        }

        //
        switch (action.action) {
          //
          // Undo ...
          case XIDEAction.Undo:
            //
            const undoDontSelect = toBoolean(action.payload) || false;
            this.undo(undoDontSelect);
            break;

          //
          // Redo ...
          case XIDEAction.Redo:
            //
            const redoDontSelect = toBoolean(action.payload) || false;
            this.redo(redoDontSelect);
            break;

          //
          // CleaEditorHistory ...
          case XIDEAction.CleaEditorHistory:
            this.cleaEditorHistory();
            break;

          //
          // AddTab ...
          case XIDEAction.AddTab:
            //
            const tabDescriptor = action.payload as XIDETabDescriptor;
            if (
              !isNullOrUndefined(tabDescriptor) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.addTab({
                ...tabDescriptor,
              });
            }
            break;

          //
          // AddTabs ...
          case XIDEAction.AddTabs:
            //
            const tabDescriptors = action.payload as XIDETabDescriptor[];
            if (
              this.hasChild(tabDescriptors) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.addTabs([...tabDescriptors]);
            }
            break;

          //
          // ShowTab ...
          case XIDEAction.ShowTab:
            //
            const showTabDescriptor = action.payload as string;
            if (
              !isNullOrEmptyString(showTabDescriptor) &&
              !isNullOrEmptyString(action.payload)
            ) {
              //
              const index = this.getTabDescritorIndex(showTabDescriptor);

              //
              this.runTimedOut(() => {
                //
                this.tabsActionProvider.next({
                  action: XTabsAction.TO,
                  payload: index,
                });
              }, 500).then(() => {
                this.showTab(showTabDescriptor);
              });
            }
            break;

          //
          // CloseTab ...
          case XIDEAction.CloseTab:
            //
            const forceCloseTab = action.payload.force || false;
            const closeTabDescriptor = action.payload.name as string;
            if (
              !isNullOrEmptyString(closeTabDescriptor) &&
              !isNullOrEmptyString(action.payload)
            ) {
              this.closeTab(closeTabDescriptor, forceCloseTab);
            }
            break;

          //
          // Refresh ...
          case XIDEAction.Refresh:
            this.detectChanges();
            break;

          //
          // SetMode ...
          case XIDEAction.SetMode:
            //
            const mode = action.payload as XIDEMode;
            if (
              !isNullOrUndefined(mode) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setMode(mode);
            }
            break;

          //
          // Beautify ...
          case XIDEAction.Beautify:
            this.beautifyCode();
            break;

          //
          // SetTheme ...
          case XIDEAction.SetTheme:
            //
            const theme = action.payload as XIDETheme;
            if (
              !isNullOrUndefined(theme) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setTheme(theme);
            }
            break;

          //
          // GoToLine ...
          case XIDEAction.GoToLine:
            //
            const lineNumber = action.payload as number;
            if (
              isNumber(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.goToLine(lineNumber);
            }
            break;

          //
          // SetContent ...
          case XIDEAction.SetContent:
            //
            const content = action.payload as string;
            if (
              !isNullOrEmptyString(content) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setContent(content);
            }
            break;

          //
          // SetMinLines ...
          case XIDEAction.SetMinLines:
            //
            const minLines = action.payload as number;
            if (
              isNumber(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setMinLines(minLines);
            }
            break;

          //
          // SetMaxLines ...
          case XIDEAction.SetMaxLines:
            //
            const maxLines = action.payload as number;
            if (
              isNumber(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setMaxLines(maxLines);
            }
            break;

          //
          // SetReadOnly ...
          case XIDEAction.SetReadOnly:
            //
            const isReadonly = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setReadOnly(isReadonly);
            }
            break;

          //
          // SetFontSize ...
          case XIDEAction.SetFontSize:
            //
            const fontSize = action.payload as number;
            if (
              isNumber(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setFontSize(fontSize);
            }
            break;

          //
          // ClearContent ...
          case XIDEAction.ClearContent:
            //
            const isForceClearContent = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.clearcontent(isForceClearContent);
            } else {
              this.clearcontent();
            }
            break;

          //
          // SetWordWrapping ...
          case XIDEAction.SetWordWrapping:
            //
            const isWrapping = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setWordWrapping(isWrapping);
            }
            break;

          //
          // SetShowFoldWidgets ...
          case XIDEAction.SetShowFoldWidgets:
            //
            const showFoldingWidgets = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setReadOnly(showFoldingWidgets);
            }
            break;

          //
          // SetShowLineNumbers ...
          case XIDEAction.SetShowLineNumbers:
            //
            const showLineNumbers = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setShowLineNumbers(showLineNumbers);
            }
            break;

          //
          // SetShowPrintMargin ...
          case XIDEAction.SetShowPrintMargin:
            //
            const showPrintMargin = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setShowPrintMargin(showPrintMargin);
            }
            break;

          //
          // SetHighlightActiveLine ...
          case XIDEAction.SetHighlightActiveLine:
            //
            const highlightActiveLine = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setHighlightActiveLine(highlightActiveLine);
            }
            break;

          //
          // SetBasicAutocompletion ...
          case XIDEAction.SetBasicAutocompletion:
            //
            const basicAutoCompletion = toBoolean(action.payload);
            if (
              isBoolean(action.payload) &&
              !isNullOrUndefined(action.payload)
            ) {
              this.setBasicAutocompletion(basicAutoCompletion);
            }
            break;
        }
      });
  }

  private unregisterActionProvider () {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private prepareCodeEditor () {
    //
    const element = this.codeEditorElmRef.nativeElement;
    if (isNullOrUndefined(element)) {
      return;
    }

    //
    this.CODE_EDITOR_BEAUTIFY = ace.require('ace/ext/beautify');
    this.CODE_EDITOR_UNDO_MANAGER = ace.require('ace/undomanager').UndoManager;

    //
    this.CODE_EDITOR = ace.edit(element, {
      minLines: 1,
      maxLines: 1,
    });

    //
    //#region Attach Undo Manager ...
    this.CODE_EDITOR.getSession().setUndoManager(
      new this.CODE_EDITOR_UNDO_MANAGER()
    );
    //#endregion

    //
    //#region Apply Code Editor Options ...
    this.setMode(this.mode);
    this.setTheme(this.theme);
    this.setMinLines(this.minLines);
    this.setMaxLines(this.maxLines);
    this.setFontSize(this.fontSize);
    this.setReadOnly(this.readonly);
    this.setWordWrapping(this.wordWrapping);
    this.setShowLineNumbers(this.showLineNumbers);
    this.setShowPrintMargin(this.showPrintMargin);
    this.setShowFoldWidgets(this.showFoldWidgets);
    this.setHighlightActiveLine(this.highlightActiveLine);
    this.setBasicAutocompletion(this.basicAutocompletion);
    //#endregion

    //
    //#region Setting Default Content ...
    this.setContent(this.content);

    //
    // Reset Undo Manager after first code attached ...
    this.CODE_EDITOR.getSession().getUndoManager().reset();
    //#endregion

    //
    //#region Default Session Handler ...
    forkJoin({
      mode: this.getValue(this.mode),
      content: this.getValue(this.content),
      name: this.getValue(this.defaultTabName).pipe(
        map((defTabName) => defTabName || XIDEDefaultTabName)
      ),
    }).subscribe((info) => {
      //
      const name = info.name;
      const session = this.CODE_EDITOR.getSession();
      this.Sessions.push({
        session,
        name,
      });
    });
    //#endregion

    //
    //#region Code Editor Event Handlers ...
    //
    // Change Event handler ...
    this.CODE_EDITOR.on('change', (e) => {
      //
      const currentContent = this.getContent();
      this.CONTENT_CHANGE_SUBJECT.next(currentContent);
    });
    //#endregion

    //
    this.prepareActionBar();

    //
    // Detectin Changes ...
    this.detectChanges();
  }

  private async prepareActionBar () {
    //
    if (isNullOrUndefined(this.CODE_EDITOR)) {
      return;
    }

    //
    const showActionBar = await this.getValueAsync(this.showActionBar);
    if (!showActionBar) {
      return;
    }

    //
    //#region DefaultActionBarActions ...
    //
    const defaultThemeItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      title: this.ResourceIDs.theme,
      icon: this.IconNames.color_palette,
      slot: this.ActionBarItemSlots.START,
      id: this.ActionBarItems.ChangeTheme,
      iconColor: this.defaultActionBarItemColor,
    };

    //
    const defaultModeItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      icon: this.IconNames.list,
      title: this.ResourceIDs.ide_mode,
      id: this.ActionBarItems.ChangeMode,
      slot: this.ActionBarItemSlots.START,
      iconColor: this.defaultActionBarItemColor,
    };

    //
    const defaultSaveItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      icon: this.IconNames.save,
      title: this.ResourceIDs.save,
      id: this.ActionBarItems.Save,
      slot: this.ActionBarItemSlots.END,
      iconColor: this.defaultActionBarItemColor,
    };

    //
    const defaultCloseItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      icon: this.IconNames.close,
      title: this.ResourceIDs.close,
      id: this.ActionBarItems.Close,
      slot: this.ActionBarItemSlots.END,
      iconColor: this.ColorNames.Danger,
    };

    //
    const defaultUndoItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      icon: this.IconNames.undo,
      title: this.ResourceIDs.undo,
      id: this.ActionBarItems.Undo,
      slot: this.ActionBarItemSlots.END,
      iconColor: this.defaultActionBarItemColor,
    };

    //
    const defaultRedoItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      icon: this.IconNames.redo,
      title: this.ResourceIDs.redo,
      id: this.ActionBarItems.Redo,
      slot: this.ActionBarItemSlots.END,
      iconColor: this.defaultActionBarItemColor,
    };

    //
    const defaultBeautifyItem: XActionBarItem = {
      onlyIcon: true,
      disabled: this.uiDisabled,
      icon: this.IconNames.ul,
      id: this.ActionBarItems.Beautify,
      slot: this.ActionBarItemSlots.END,
      title: this.ResourceIDs.ide_format,
      iconColor: this.defaultActionBarItemColor,
    };
    //#endregion

    //
    this.actionBarActions = [];
    this.detectChanges();

    //
    const availableItems = await this.getArrayValueAsync(
      this.availableActionBarItems
    );
    if (!this.hasChild(availableItems)) {
      return;
    }

    //
    const actionBarItems: XActionBarItem[] = [];
    const currentTabDescriptor = this.getCurrentTabDescritor();

    //
    //#region Save ...
    const hasSaveItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.Save);

    //
    if (hasSaveItem) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.Save
      );
      const providedSaveItem = selectedItem.descriptor;
      let saveItem = {
        ...defaultSaveItem,
      };
      if (!isNullOrUndefined(providedSaveItem)) {
        saveItem = {
          ...saveItem,
          ...providedSaveItem,
        };
      }

      //
      saveItem = {
        ...saveItem,
        disabled:
          this.uiDisabled || !isNullOrUndefined(currentTabDescriptor)
            ? !currentTabDescriptor.canSave
            : true,
      };

      //
      actionBarItems.push(saveItem);
    }
    //#endregion

    //
    //#region Close ...
    const hasCloseItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.Close);
    const hasCloseOnDefaultTab = await this.getValueAsync(
      this.hasCloseOnDefaultTab
    );

    //
    const currentTabIndex = this.getTabDescritorIndex(this.currentTab);
    if (
      hasCloseItem
      && (!!hasCloseOnDefaultTab
        ? true
        : currentTabIndex !== 0
      )
      && currentTabIndex > -1) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.Close
      );
      const providedCloseItem = selectedItem.descriptor;
      let closeItem = {
        ...defaultCloseItem,
      };
      if (!isNullOrUndefined(providedCloseItem)) {
        closeItem = {
          ...closeItem,
          ...providedCloseItem,
        };
      }

      //
      closeItem = {
        ...closeItem,
        disabled: this.uiDisabled,
      };

      //
      actionBarItems.push(closeItem);
    }
    //#endregion

    //
    //#region Unde ...
    const hasUndoItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.Undo);

    //
    if (hasUndoItem) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.Undo
      );
      const providedUndoItem = selectedItem.descriptor;
      let undoItem = {
        ...defaultUndoItem,
      };
      if (!isNullOrUndefined(providedUndoItem)) {
        undoItem = {
          ...undoItem,
          ...providedUndoItem,
        };
      }

      //
      // Prepare Disable/Enable Toggling of Item ...
      undoItem = {
        ...undoItem,
        disabled:
          this.uiDisabled ||
          !this.CODE_EDITOR.getSession().getUndoManager().canUndo() ||
          isNullOrUndefined(this.CODE_EDITOR.getSession().getUndoManager()),
      };

      //
      actionBarItems.push(undoItem);
    }
    //#endregion

    //
    //#region Redo ...
    const hasRedoItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.Redo);

    //
    if (hasRedoItem) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.Redo
      );
      const providedRedoItem = selectedItem.descriptor;
      let redoItem = {
        ...defaultRedoItem,
      };
      if (!isNullOrUndefined(providedRedoItem)) {
        redoItem = {
          ...redoItem,
          ...providedRedoItem,
        };
      }

      //
      // Prepare Disable/Enable Toggling of Item ...
      redoItem = {
        ...redoItem,
        disabled:
          this.uiDisabled ||
          !this.CODE_EDITOR.getSession().getUndoManager().canRedo() ||
          isNullOrUndefined(this.CODE_EDITOR.getSession().getUndoManager()),
      };

      //
      actionBarItems.push(redoItem);
    }
    //#endregion

    //
    //#region Beautify ...
    const hasBeautifyItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.Beautify);

    //
    if (hasBeautifyItem) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.Beautify
      );
      const providedBeautifyItem = selectedItem.descriptor;
      let beautifyItem = {
        ...defaultBeautifyItem,
      };
      if (!isNullOrUndefined(providedBeautifyItem)) {
        beautifyItem = {
          ...beautifyItem,
          ...providedBeautifyItem,
        };
      }

      //
      // Prepare Disable/Enable Toggling of Item ...
      beautifyItem = {
        ...beautifyItem,
        disabled:
          this.uiDisabled ||
            isNullOrUndefined(this.CODE_EDITOR) ||
            !isNullOrUndefined(currentTabDescriptor)
            ? !currentTabDescriptor.canBeautify
            : true || isNullOrUndefined(this.CODE_EDITOR_BEAUTIFY),
      };

      //
      actionBarItems.push(beautifyItem);
    }
    //#endregion

    //
    //#region Mode ...
    const hasModeChangeItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.ChangeMode);

    //
    if (hasModeChangeItem) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.ChangeMode
      );
      const providedModeItem = selectedItem.descriptor;
      let modeItem = {
        ...defaultModeItem,
      };
      if (!isNullOrUndefined(providedModeItem)) {
        modeItem = {
          ...modeItem,
          ...providedModeItem,
        };
      }

      //
      modeItem = {
        ...modeItem,
        disabled: this.uiDisabled,
      };

      //
      actionBarItems.push(modeItem);
    }
    //#endregion

    //
    //#region Theme ...
    const hasThemeChangeItem = availableItems
      .map((ai) => ai.actionId)
      .includes(this.ActionBarItems.ChangeTheme);

    //
    if (hasThemeChangeItem) {
      //
      const selectedItem = availableItems.find(
        (ai) => ai.actionId === this.ActionBarItems.ChangeTheme
      );
      const providedThemeItem = selectedItem.descriptor;
      let themeItem = {
        ...defaultThemeItem,
      };
      if (!isNullOrUndefined(providedThemeItem)) {
        themeItem = {
          ...themeItem,
          ...providedThemeItem,
        };
      }

      //
      themeItem = {
        ...themeItem,
        disabled: this.uiDisabled,
      };

      //
      actionBarItems.push(themeItem);
    }
    //#endregion

    //
    this.actionBarActions = [...actionBarItems];
    this.detectChanges();
  }

  //
  //#region Tabs Handlers ...
  private async handleDefaultTabNameChanged (
    defaultTabName: XStandardType<string>
  ) {
    //
    let value = await this.getValueAsync(defaultTabName);
    if (isNullOrEmptyString(value)) {
      value = XIDEDefaultTabName;
    }

    //
    if (!this.hasChild(this.Sessions)) {
      return;
    }

    //
    if (
      this.currentTab === XIDEDefaultTabName ||
      this.currentTab === this.Sessions[0].name
    ) {
      this.currentTab = value;
    }

    //
    this.Sessions[0].name = value;

    //
    this.prepareActionBar();
    this.detectChanges();
  }

  private handleRemoveTab (name: string, destinationTabIndex: number) {
    //
    if (
      destinationTabIndex <= 0 ||
      isNullOrEmptyString(name) ||
      destinationTabIndex > this.Sessions.length - 1
    ) {
      return;
    }

    //
    this.Sessions.splice(destinationTabIndex, 1);
    this.detectChanges();
    this.tabsActionProvider.next({
      action: XTabsAction.Refresh,
    });

    //
    const canShowNewTab = name === this.currentTab;
    if (canShowNewTab) {
      //
      const newTabIndex = this.Sessions.length - 1;

      //
      if (countChild(this.Sessions) > 1) {
        //
        this.tabsActionProvider.next({
          action: XTabsAction.TO,
          payload: newTabIndex,
        });

        //
        this.tabsActionProvider.next({
          action: XTabsAction.Refresh,
        });
      }

      //
      const newTab = this.Sessions[newTabIndex];
      this.showTab(newTab.name);
    }

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region ActionBar Handlers ...
  private handleActionModeFired (event: Event) {
    this.getValue(this.mode)
      .pipe(
        concatMap((mode) =>
          this.getArrayValue(this.availableActionBarItems).pipe(
            map((availableItems) => {
              return {
                mode,
                availableItems,
              };
            })
          )
        )
      )
      .subscribe((info) => {
        //
        // Selected Item Model ...
        const selectedItemModel = info.availableItems.find(
          (ai) => ai.actionId === this.ActionBarItems.ChangeMode
        );

        //
        // get Item Colors ...
        const itemsColor = selectedItemModel.itemsColor;

        //
        // Retrieve Popover Present Options ...
        const popoverPresentOptions = selectedItemModel.popoverPresentOptions;

        //
        // Generate Available Modes ...
        const availableModes = Object.keys(this.Modes)
          //
          // By Filtering Curent Mode ...
          .filter((key) => this.Modes[key] !== info.mode);

        //
        // Convert Available Modes to XPopoverOptions ...
        const options = availableModes.map((key) => {
          return {
            id: key,
            color: !isNullOrEmptyString(itemsColor)
              ? itemsColor
              : this.ColorNames.Dark,
            title: key,
            handler: () => {
              //
              this.mode = this.Modes[key];
              this.setMode(this.Modes[key]);
            },
          } as XPopoverOption;
        });

        //
        // Present Popover Options ...
        this.popoverService.presentContextMenu(
          options,
          event,
          popoverPresentOptions
        );
      });
  }

  private handleActionThemFired (event: Event) {
    this.getValue(this.theme)
      .pipe(
        concatMap((theme) =>
          this.getArrayValue(this.availableActionBarItems).pipe(
            map((availableItems) => {
              return {
                theme,
                availableItems,
              };
            })
          )
        )
      )
      .subscribe((info) => {
        //
        // Selected Item Model ...
        const selectedItemModel = info.availableItems.find(
          (ai) => ai.actionId === this.ActionBarItems.ChangeTheme
        );

        //
        // get Item Colors ...
        const itemsColor = selectedItemModel.itemsColor;

        //
        // Retrieve Popover Present Options ...
        const popoverPresentOptions = selectedItemModel.popoverPresentOptions;

        //
        // Generate Available Themes ...
        const availableThemes = Object.keys(this.Themes)
          //
          // By Filtering Curent Theme ...
          .filter((key) => this.Themes[key] !== info.theme);

        //
        // Convert Available Themes to XPopoverOptions ...
        const options = availableThemes.map((key) => {
          return {
            id: key,
            color: !isNullOrEmptyString(itemsColor)
              ? itemsColor
              : this.ColorNames.Dark,
            title: key,
            handler: () => {
              //
              this.theme = this.Themes[key];
              this.setTheme(this.Themes[key]);
            },
          } as XPopoverOption;
        });

        //
        // Present Popover Options ...
        this.popoverService.presentContextMenu(
          options,
          event,
          popoverPresentOptions
        );
      });
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-ide\x-ide.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIDEComponent } from './x-ide.component';
import { XTabsModule } from '../x-tabs/x-tabs.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XActionBarModule } from '../x-action-bar/x-action-bar.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XIDEComponent],
  imports: [
    XTabsModule,
    XIconModule,
    XCardModule,
    CommonModule,
    XActionBarModule,
    XBaseComponentModule,
  ],
  exports: [
    XTabsModule,
    XIconModule,
    XCardModule,
    CommonModule,
    XIDEComponent,
    XActionBarModule,
    XBaseComponentModule,
  ],
})
export class XIDEModule { }

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-ide\x-ide.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XCardActionModel,
  XComponentCardContainer,
  IXComponentCardContainer,
} from '../x-card/x-card.typings';
import * as ace from 'ace-builds';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import {
  XActionBarItem,
  XActionBarActionModel,
  XActionBarActionFiredModel,
  XComponentActionBarContainer,
  IXComponentActionBarContainer,
} from '../x-action-bar/x-action-bar.typings';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XPopoverService } from '../dialogs/x-popover/x-popover.service';
import { XTabAlign, XTabAlignIdentifier } from '../x-tabs/x-tabs.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XPopoverPresentOptions } from '../dialogs/x-popover/x-popover.typings';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Action Provider ...
export enum XIDEAction {
  //
  Refresh = 'refresh',
  SetReadOnly = 'set_read_only',

  //
  AddTab = 'add_tab',
  ShowTab = 'show_tab',
  AddTabs = 'add_tabs',
  CloseTab = 'close_tab',

  //
  Undo = 'undo',
  Redo = 'redo',
  CleaEditorHistory = 'clear_editor_history',
  Beautify = 'beautify',
  GoToLine = 'go_to_line',
  SetContent = 'set_content',
  ClearContent = 'clear_content',

  //
  SetMode = 'set_mode',
  SetTheme = 'set_theme',
  SetMinLines = 'set_min_lines',
  SetMaxLines = 'set_max_lines',
  SetFontSize = 'set_font_size',
  SetWordWrapping = 'set_word_wrapping',
  SetShowFoldWidgets = 'set_show_fold_widgets',
  SetShowLineNumbers = 'set_show_line_numbers',
  SetShowPrintMargin = 'set_show_print_margin',
  SetHighlightActiveLine = 'set_highlight_active_line',
  SetBasicAutocompletion = 'set_basic_autoc_ompletion',
}

export type XIDEActionIdentifier = XIDEAction | string;

export interface XIDEActionModel
  extends XBaseActionModel<XIDEActionIdentifier> {}
//#endregion

//
// Font Size Constants ...
export const XIDEMinFontSize = 12;
export const XIDEMaxFontSize = 25;

export const XIDEDefaultTabName = 'DEFAULT';

//
type AnyTypeIndex = {
  [key: string]: any;
};

//
export type XIDEOptionsType = Partial<ace.Ace.EditorOptions> & AnyTypeIndex;

//
//#region Theme ...
export enum XIDETheme {
  Ambiance = 'ace/theme/ambiance',
  Chaos = 'ace/theme/chaos',
  Chrome = 'ace/theme/chrome',
  Cloud9Day = 'ace/theme/cloud9_day',
  Cloud9Night = 'ace/theme/cloud9_night',
  Cloud9NightLowColor = 'ace/theme/cloud9_night_low_color',
  Clouds = 'ace/theme/clouds',
  CloudsMidnight = 'ace/theme/clouds_midnight',
  Cobalt = 'ace/theme/cobalt',
  CrimsonEditor = 'ace/theme/crimson_editor',
  Dawn = 'ace/theme/dawn',
  Dracula = 'ace/theme/dracula',
  Dreamweaver = 'ace/theme/dreamweaver',
  Eclipse = 'ace/theme/eclipse',
  Github = 'ace/theme/github',
  Gob = 'ace/theme/gob',
  Gruvbox = 'ace/theme/gruvbox',
  GruvboxDarkHard = 'ace/theme/gruvbox_dark_hard',
  GruvboxLightHard = 'ace/theme/gruvbox_light_hard',
  IdleFingers = 'ace/theme/idle_fingers',
  Iplastic = 'ace/theme/iplastic',
  Katzenmilch = 'ace/theme/katzenmilch',
  KrTheme = 'ace/theme/kr_theme',
  Kuroir = 'ace/theme/kuroir',
  Merbivore = 'ace/theme/merbivore',
  MerbivoreSoft = 'ace/theme/merbivore_soft',
  MonoIndustrial = 'ace/theme/mono_industrial',
  Monokai = 'ace/theme/monokai',
  NordDark = 'ace/theme/nord_dark',
  OneDark = 'ace/theme/one_dark',
  PastelOnDark = 'ace/theme/pastel_on_dark',
  SolarizedDark = 'ace/theme/solarized_dark',
  SolarizedLight = 'ace/theme/solarized_light',
  Sqlserver = 'ace/theme/sqlserver',
  Terminal = 'ace/theme/terminal',
  Textmate = 'ace/theme/textmate',
  Tomorrow = 'ace/theme/tomorrow',
  TomorrowNight = 'ace/theme/tomorrow_night',
  TomorrowNightBlue = 'ace/theme/tomorrow_night_blue',
  TomorrowNightBright = 'ace/theme/tomorrow_night_bright',
  TomorrowNightEighties = 'ace/theme/tomorrow_night_eighties',
  Twilight = 'ace/theme/twilight',
  VibrantInk = 'ace/theme/vibrant_ink',
  Xcode = 'ace/theme/xcode',
}

export type XIDEThemeIdentifier = XIDETheme | string;
//#endregion

//
//#region Mode ...
export enum XIDEMode {
  Abap = 'ace/mode/abap',
  Abc = 'ace/mode/abc',
  Actionscript = 'ace/mode/actionscript',
  Ada = 'ace/mode/ada',
  Alda = 'ace/mode/alda',
  ApacheConf = 'ace/mode/apache_conf',
  Apex = 'ace/mode/apex',
  Applescript = 'ace/mode/applescript',
  Aql = 'ace/mode/aql',
  Asciidoc = 'ace/mode/asciidoc',
  Asl = 'ace/mode/asl',
  AssemblyX86 = 'ace/mode/assembly_x86',
  Autohotkey = 'ace/mode/autohotkey',
  Batchfile = 'ace/mode/batchfile',
  C9search = 'ace/mode/c9search',
  CCpp = 'ace/mode/c_cpp',
  Cirru = 'ace/mode/cirru',
  Clojure = 'ace/mode/clojure',
  Cobol = 'ace/mode/cobol',
  Coffee = 'ace/mode/coffee',
  Coldfusion = 'ace/mode/coldfusion',
  Crystal = 'ace/mode/crystal',
  Csharp = 'ace/mode/csharp',
  CsoundDocument = 'ace/mode/csound_document',
  CsoundOrchestra = 'ace/mode/csound_orchestra',
  CsoundScore = 'ace/mode/csound_score',
  Csp = 'ace/mode/csp',
  Css = 'ace/mode/css',
  Curly = 'ace/mode/curly',
  D = 'ace/mode/d',
  Dart = 'ace/mode/dart',
  Diff = 'ace/mode/diff',
  Django = 'ace/mode/django',
  Dockerfile = 'ace/mode/dockerfile',
  Dot = 'ace/mode/dot',
  Drools = 'ace/mode/drools',
  Edifact = 'ace/mode/edifact',
  Eiffel = 'ace/mode/eiffel',
  Ejs = 'ace/mode/ejs',
  Elixir = 'ace/mode/elixir',
  Elm = 'ace/mode/elm',
  Erlang = 'ace/mode/erlang',
  Forth = 'ace/mode/forth',
  Fortran = 'ace/mode/fortran',
  Fsharp = 'ace/mode/fsharp',
  Fsl = 'ace/mode/fsl',
  Ftl = 'ace/mode/ftl',
  Gcode = 'ace/mode/gcode',
  Gherkin = 'ace/mode/gherkin',
  Gitignore = 'ace/mode/gitignore',
  Glsl = 'ace/mode/glsl',
  Gobstones = 'ace/mode/gobstones',
  Golang = 'ace/mode/golang',
  Graphqlschema = 'ace/mode/graphqlschema',
  Groovy = 'ace/mode/groovy',
  Haml = 'ace/mode/haml',
  Handlebars = 'ace/mode/handlebars',
  Haskell = 'ace/mode/haskell',
  HaskellCabal = 'ace/mode/haskell_cabal',
  Haxe = 'ace/mode/haxe',
  Hjson = 'ace/mode/hjson',
  Html = 'ace/mode/html',
  HtmlElixir = 'ace/mode/html_elixir',
  HtmlRuby = 'ace/mode/html_ruby',
  Ini = 'ace/mode/ini',
  Io = 'ace/mode/io',
  Ion = 'ace/mode/ion',
  Jack = 'ace/mode/jack',
  Jade = 'ace/mode/jade',
  Java = 'ace/mode/java',
  Javascript = 'ace/mode/javascript',
  Json = 'ace/mode/json',
  Json5 = 'ace/mode/json5',
  Jsoniq = 'ace/mode/jsoniq',
  Jsp = 'ace/mode/jsp',
  Jssm = 'ace/mode/jssm',
  Jsx = 'ace/mode/jsx',
  Julia = 'ace/mode/julia',
  Kotlin = 'ace/mode/kotlin',
  Latex = 'ace/mode/latex',
  Latte = 'ace/mode/latte',
  Less = 'ace/mode/less',
  Liquid = 'ace/mode/liquid',
  Lisp = 'ace/mode/lisp',
  Livescript = 'ace/mode/livescript',
  Logiql = 'ace/mode/logiql',
  Logtalk = 'ace/mode/logtalk',
  Lsl = 'ace/mode/lsl',
  Lua = 'ace/mode/lua',
  Luapage = 'ace/mode/luapage',
  Lucene = 'ace/mode/lucene',
  Makefile = 'ace/mode/makefile',
  Markdown = 'ace/mode/markdown',
  Mask = 'ace/mode/mask',
  Matlab = 'ace/mode/matlab',
  Maze = 'ace/mode/maze',
  Mediawiki = 'ace/mode/mediawiki',
  Mel = 'ace/mode/mel',
  Mips = 'ace/mode/mips',
  Mixal = 'ace/mode/mixal',
  Mushcode = 'ace/mode/mushcode',
  Mysql = 'ace/mode/mysql',
  Nginx = 'ace/mode/nginx',
  Nim = 'ace/mode/nim',
  Nix = 'ace/mode/nix',
  Nsis = 'ace/mode/nsis',
  Nunjucks = 'ace/mode/nunjucks',
  ObjectiveC = 'ace/mode/objectivec',
  Ocaml = 'ace/mode/ocaml',
  Partiql = 'ace/mode/partiql',
  Pascal = 'ace/mode/pascal',
  Perl = 'ace/mode/perl',
  Pgsql = 'ace/mode/pgsql',
  Php = 'ace/mode/php',
  PhpLaravelBlade = 'ace/mode/php_laravel_blade',
  Pig = 'ace/mode/pig',
  PlainText = 'ace/mode/plain_text',
  Powershell = 'ace/mode/powershell',
  Praat = 'ace/mode/praat',
  Prisma = 'ace/mode/prisma',
  Prolog = 'ace/mode/prolog',
  Properties = 'ace/mode/properties',
  Protobuf = 'ace/mode/protobuf',
  Puppet = 'ace/mode/puppet',
  Python = 'ace/mode/python',
  Qml = 'ace/mode/qml',
  R = 'ace/mode/r',
  Raku = 'ace/mode/raku',
  Razor = 'ace/mode/razor',
  Rdoc = 'ace/mode/rdoc',
  Red = 'ace/mode/red',
  Redshift = 'ace/mode/redshift',
  Rhtml = 'ace/mode/rhtml',
  Robot = 'ace/mode/robot',
  Rst = 'ace/mode/rst',
  Ruby = 'ace/mode/ruby',
  Rust = 'ace/mode/rust',
  Sac = 'ace/mode/sac',
  Sass = 'ace/mode/sass',
  Scad = 'ace/mode/scad',
  Scala = 'ace/mode/scala',
  Scheme = 'ace/mode/scheme',
  Scrypt = 'ace/mode/scrypt',
  Scss = 'ace/mode/scss',
  Sh = 'ace/mode/sh',
  Sjs = 'ace/mode/sjs',
  Slim = 'ace/mode/slim',
  Smarty = 'ace/mode/smarty',
  Smithy = 'ace/mode/smithy',
  Snippets = 'ace/mode/snippets',
  SoyTemplate = 'ace/mode/soy_template',
  Space = 'ace/mode/space',
  Sparql = 'ace/mode/sparql',
  Sql = 'ace/mode/sql',
  Sqlserver = 'ace/mode/sqlserver',
  Stylus = 'ace/mode/stylus',
  Svg = 'ace/mode/svg',
  Swift = 'ace/mode/swift',
  Tcl = 'ace/mode/tcl',
  Terraform = 'ace/mode/terraform',
  Tex = 'ace/mode/tex',
  Text = 'ace/mode/text',
  Textile = 'ace/mode/textile',
  Toml = 'ace/mode/toml',
  Tsx = 'ace/mode/tsx',
  Turtle = 'ace/mode/turtle',
  Twig = 'ace/mode/twig',
  Typescript = 'ace/mode/typescript',
  Vala = 'ace/mode/vala',
  Vbscript = 'ace/mode/vbscript',
  Velocity = 'ace/mode/velocity',
  Verilog = 'ace/mode/verilog',
  Vhdl = 'ace/mode/vhdl',
  Visualforce = 'ace/mode/visualforce',
  Wollok = 'ace/mode/wollok',
  Xml = 'ace/mode/xml',
  Xquery = 'ace/mode/xquery',
  Yaml = 'ace/mode/yaml',
  Zeek = 'ace/mode/zeek',
}

export type XIDEModeIdentifier = XIDEMode | string;
//#endregion

//
//#region ActionBar ...
export enum XIDEActionBarItem {
  ChangeMode = 'change_mode',
  ChangeTheme = 'change_theme',
  Save = 'save',
  Close = 'close',
  Undo = 'undo',
  Redo = 'redo',
  Beautify = 'beautify',
}

export type XIDEActionBarItemIdentifier = XIDEActionBarItem | string;

export type XIDEActionBarItemDescriptor = Partial<
  Pick<
    XActionBarItem,
    | 'slot'
    | 'icon'
    | 'title'
    | 'color'
    | 'onlyIcon'
    | 'iconColor'
    | 'titleColor'
  >
>;

export interface XIDEAvailableActionBarItem {
  itemsColor?: XColorIdentifier;
  actionId: XIDEActionBarItemIdentifier;
  descriptor?: XIDEActionBarItemDescriptor;
  popoverPresentOptions?: XPopoverPresentOptions;
}
//#endregion

//
//#region IDETabs ...
export interface XIDETabDescriptor {
  name: string;
  content?: string;
  canSave?: boolean;
  forceShow?: boolean;
  canBeautify?: boolean;
  mode?: XIDEModeIdentifier;
  session?: ace.Ace.EditSession;
}
//#endregion

//
//#region Container ...
type TXIDEContainer = IXComponentCardContainer & IXComponentActionBarContainer;
export interface IXIDEContainer extends TXIDEContainer {
  //
  //#region Props ...
  content: XStandardType<string>;
  minLines: XStandardType<number>;
  maxLines: XStandardType<number>;
  fontSize: XStandardType<number>;
  readonly: XStandardType<boolean>;
  contentChange: EventEmitter<string>;
  wordWrapping: XStandardType<boolean>;
  defaultTabName: XStandardType<string>;
  showLineNumbers: XStandardType<boolean>;
  showPrintMargin: XStandardType<boolean>;
  showFoldWidgets: XStandardType<boolean>;
  mode: XStandardType<XIDEModeIdentifier>;
  actionProvider: Subject<XIDEActionModel>;
  theme: XStandardType<XIDEThemeIdentifier>;
  highlightActiveLine: XStandardType<boolean>;
  basicAutocompletion: XStandardType<boolean>;
  hasCloseOnDefaultTab: XStandardType<boolean>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  //#region Tabs ...
  alignTabs: XStandardType<XTabAlignIdentifier>;
  tabHeaderColor: XStandardType<XColorIdentifier>;
  activeTabColor: XStandardType<XColorIdentifier>;
  //#endregion

  //
  //#region ActionBar ...
  defaultActionBarItemColor: XStandardType<XColorIdentifier>;
  availableActionBarItems: XStandardType<
    XOneOrManyType<XIDEAvailableActionBarItem>
  >;
  //#endregion
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
export class XIDEContainer extends XBaseContainer implements IXIDEContainer {
  //
  //#region Props ...
  //
  //#region Self Props ...
  readonly content: XStandardType<string> = '';
  readonly minLines: XStandardType<number> = 10;
  readonly fontSize: XStandardType<number> = XIDEMinFontSize;
  readonly readonly: XStandardType<boolean> = false;
  readonly actionProvider: Subject<XIDEActionModel> =
    new Subject<XIDEActionModel>();
  readonly maxLines: XStandardType<number> = Infinity;
  readonly wordWrapping: XStandardType<boolean> = true;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;
  readonly showLineNumbers: XStandardType<boolean> = true;
  readonly showFoldWidgets: XStandardType<boolean> = true;
  readonly showPrintMargin: XStandardType<boolean> = false;
  readonly highlightActiveLine: XStandardType<boolean> = true;
  readonly basicAutocompletion: XStandardType<boolean> = true;
  readonly hasCloseOnDefaultTab: XStandardType<boolean> = false;
  readonly defaultTabName: XStandardType<string> = XIDEDefaultTabName;
  readonly mode: XStandardType<XIDEModeIdentifier> = XIDEMode.Typescript;
  readonly theme: XStandardType<XIDEThemeIdentifier> = XIDETheme.Dracula;
  readonly contentChange: EventEmitter<string> = new EventEmitter<string>();

  //
  //#region Tabs ...
  readonly alignTabs: XStandardType<XTabAlignIdentifier> = XTabAlign.START;
  readonly tabHeaderColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly activeTabColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;
  //#endregion

  //
  readonly availableActionBarItems: XStandardType<
    XOneOrManyType<XIDEAvailableActionBarItem>
  > = [
    {
      actionId: XIDEActionBarItem.Save,
    },
    {
      actionId: XIDEActionBarItem.Close,
    },
    {
      actionId: XIDEActionBarItem.Undo,
    },
    {
      actionId: XIDEActionBarItem.Redo,
    },
    {
      actionId: XIDEActionBarItem.Beautify,
    },
    {
      actionId: XIDEActionBarItem.ChangeTheme,
    },
    {
      actionId: XIDEActionBarItem.ChangeMode,
    },
  ];
  readonly defaultActionBarItemColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  //#endregion

  //
  //#region ComponentCard Props ...
  readonly isInPage: XStandardType<boolean> =
    this.cardPropertyProvider.isInPage;
  readonly wrapWithCard: XStandardType<boolean> =
    this.cardPropertyProvider.wrapWithCard;
  readonly cardActionProvider: Subject<XCardActionModel> =
    this.cardPropertyProvider.cardActionProvider;
  readonly cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.cardPropertyProvider.cardCssClass;
  readonly cardColor: XStandardType<XColorIdentifier> =
    this.cardPropertyProvider.cardColor;
  readonly cardForegroundColor: XStandardType<boolean> =
    this.cardPropertyProvider.cardForegroundColor;
  readonly cardTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardTitle;
  readonly cardSubTitle: XStandardType<XResourceIdentifier> =
    this.cardPropertyProvider.cardSubTitle;
  readonly cardShowFooter: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowFooter;
  readonly cardShowActions: XStandardType<boolean> =
    this.cardPropertyProvider.cardShowActions;
  //#endregion

  //
  //#region ComponentActionBar Props ...
  readonly showActionBar: XStandardType<boolean> =
    this.actionBarPropertyProvider.showActionBar;
  readonly actionBarActionProvider: Subject<XActionBarActionModel> =
    this.actionBarPropertyProvider.actionBarActionProvider;
  readonly actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.actionBarPropertyProvider.actionBarCssClass;
  readonly actionBarColor: XStandardType<XColorIdentifier> =
    this.actionBarPropertyProvider.actionBarColor;
  readonly actionBarForegroundColor: XStandardType<boolean> =
    this.actionBarPropertyProvider.actionBarForegroundColor;
  readonly actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.actionBarPropertyProvider.actionBarActions;
  readonly actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.actionBarPropertyProvider.actionBarActionFired;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected cardPropertyProvider: XComponentCardContainer,
    protected actionBarPropertyProvider: XComponentActionBarContainer
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export class XIDEContainerComponent
  extends XBaseContainerComponent
  implements IXIDEContainer
{
  //
  //#region Props ...
  //
  //#region Self Props ...
  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  defaultTabName: XStandardType<string> = this.propertyProvider.defaultTabName;

  @Input()
  hasCloseOnDefaultTab: XStandardType<boolean> = this.propertyProvider.hasCloseOnDefaultTab;

  @Input()
  theme: XStandardType<XIDEThemeIdentifier> = this.propertyProvider.theme;

  @Input()
  mode: XStandardType<XIDEModeIdentifier> = this.propertyProvider.mode;

  @Input()
  minLines: XStandardType<number> = this.propertyProvider.minLines;

  @Input()
  maxLines: XStandardType<number> = this.propertyProvider.maxLines;

  @Input()
  fontSize: XStandardType<number> = this.propertyProvider.fontSize;

  @Input()
  readonly: XStandardType<boolean> = this.propertyProvider.readonly;

  @Input()
  highlightActiveLine: XStandardType<boolean> =
    this.propertyProvider.highlightActiveLine;

  @Input()
  wordWrapping: XStandardType<boolean> = this.propertyProvider.wordWrapping;

  @Input()
  showLineNumbers: XStandardType<boolean> =
    this.propertyProvider.showLineNumbers;

  @Input()
  showPrintMargin: XStandardType<boolean> =
    this.propertyProvider.showPrintMargin;

  @Input()
  showFoldWidgets: XStandardType<boolean> =
    this.propertyProvider.showFoldWidgets;

  @Input()
  basicAutocompletion: XStandardType<boolean> =
    this.propertyProvider.basicAutocompletion;

  @Input()
  content: XStandardType<string> = this.propertyProvider.content;

  //
  @Input()
  actionProvider: Subject<XIDEActionModel> =
    this.propertyProvider.actionProvider;

  @Output()
  contentChange: EventEmitter<string> = this.propertyProvider.contentChange;

  //
  //#region Tabs ...
  @Input()
  alignTabs: XStandardType<XTabAlignIdentifier> =
    this.propertyProvider.alignTabs;

  @Input()
  tabHeaderColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.tabHeaderColor;

  @Input()
  activeTabColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.activeTabColor;
  //#endregion

  //
  //#region ActionBar ...
  @Input()
  defaultActionBarItemColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.defaultActionBarItemColor;

  @Input()
  availableActionBarItems: XStandardType<
    XOneOrManyType<XIDEAvailableActionBarItem>
  > = this.propertyProvider.availableActionBarItems;
  //#endregion
  //#endregion

  //
  //#region ComponentCard Props ...
  @Input()
  isInPage: XStandardType<boolean> = this.propertyProvider.isInPage;

  @Input()
  wrapWithCard: XStandardType<boolean> = this.propertyProvider.wrapWithCard;

  @Input()
  cardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;

  @Input()
  cardColor: XStandardType<XColorIdentifier> = this.propertyProvider.cardColor;

  @Input()
  cardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;

  @Input()
  cardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;

  @Input()
  cardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;

  @Input()
  cardShowFooter: XStandardType<boolean> = this.propertyProvider.cardShowFooter;

  @Input()
  cardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;

  @Input()
  cardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;
  //#endregion

  //
  //#region ComponentActionBar Props ...
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  @Input()
  actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;

  @Input()
  actionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionBarActionProvider;

  @Output()
  actionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.propertyProvider.actionBarActionFired;

  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actionBarActions;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XIDEContainer,
    protected popoverService: XPopoverService
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstracts ...
  public extractProps(container?: IXIDEContainer): Observable<IXIDEContainer> {
    return forkJoin({
      //
      //#region Self Props ...
      minLines: this.getValue(container ? container.minLines : this.minLines),
      maxLines: this.getValue(container ? container.maxLines : this.maxLines),
      fontSize: this.getValue(container ? container.fontSize : this.fontSize),
      readonly: this.getValue(container ? container.readonly : this.readonly),
      content: this.getValue(container ? container.content : this.content),
      mode: this.getValue(container ? container.mode : this.mode),
      theme: this.getValue(container ? container.theme : this.theme),
      highlightActiveLine: this.getValue(
        container ? container.highlightActiveLine : this.highlightActiveLine
      ),
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),
      basicAutocompletion: this.getValue(
        container ? container.basicAutocompletion : this.basicAutocompletion
      ),
      wordWrapping: this.getValue(
        container ? container.wordWrapping : this.wordWrapping
      ),
      showPrintMargin: this.getValue(
        container ? container.showPrintMargin : this.showPrintMargin
      ),
      showLineNumbers: this.getValue(
        container ? container.showLineNumbers : this.showLineNumbers
      ),
      showFoldWidgets: this.getValue(
        container ? container.showFoldWidgets : this.showFoldWidgets
      ),
      defaultTabName: this.getValue(
        container ? container.defaultTabName : this.defaultTabName
      ),
      hasCloseOnDefaultTab: this.getValue(
        container ? container.hasCloseOnDefaultTab : this.hasCloseOnDefaultTab
      ),

      //
      //#region Tabs ...
      alignTabs: this.getValue(
        container ? container.alignTabs : this.alignTabs
      ),
      tabHeaderColor: this.getValue(
        container ? container.tabHeaderColor : this.tabHeaderColor
      ),
      activeTabColor: this.getValue(
        container ? container.activeTabColor : this.activeTabColor
      ),
      //#endregion

      //
      // ActionBar ...
      defaultActionBarItemColor: this.getValue(
        container
          ? container.defaultActionBarItemColor
          : this.defaultActionBarItemColor
      ),
      availableActionBarItems: this.getArrayValue(
        container
          ? container.availableActionBarItems
          : this.availableActionBarItems
      ),
      //#endregion

      //
      //#region XComponentCard ...
      cardCssClass: this.getArrayValue(
        container ? container.cardCssClass : this.cardCssClass
      ),
      isInPage: this.getValue(container ? container.isInPage : this.isInPage),
      wrapWithCard: this.getValue(
        container ? container.wrapWithCard : this.wrapWithCard
      ),
      cardShowFooter: this.getValue(
        container ? container.cardShowFooter : this.cardShowFooter
      ),
      cardShowActions: this.getValue(
        container ? container.cardShowActions : this.cardShowActions
      ),
      cardColor: this.getValue(
        container ? container.cardColor : this.cardColor
      ),
      cardForegroundColor: this.getValue(
        container ? container.cardForegroundColor : this.cardForegroundColor
      ),
      cardTitle: this.getValue(
        container ? container.cardTitle : this.cardTitle
      ),
      cardSubTitle: this.getValue(
        container ? container.cardSubTitle : this.cardSubTitle
      ),
      //#endregion

      //
      //#region XComponentActionBar ...
      showActionBar: this.getValue(
        container ? container.showActionBar : this.showActionBar
      ),
      actionBarCssClass: this.getValue(
        container ? container.actionBarCssClass : this.actionBarCssClass
      ),
      actionBarColor: this.getValue(
        container ? container.actionBarColor : this.actionBarColor
      ),
      actionBarForegroundColor: this.getValue(
        container
          ? container.actionBarForegroundColor
          : this.actionBarForegroundColor
      ),
      actionBarActions: this.getValue(
        container ? container.actionBarActions : this.actionBarActions
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          cardActionProvider: container
            ? container.cardActionProvider
            : this.cardActionProvider,

          //
          actionBarActionProvider: container
            ? container.actionBarActionProvider
            : this.actionBarActionProvider,

          //
          contentChange: this.contentChange,
          actionBarActionFired: this.actionBarActionFired,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-image-cropper\x-image-cropper.component.html

<div
  xOnEscPress="onCancel()"
  [style.text-align]="alignImage"
  class="x-image-croppert x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
>
  <img
    #sourceImage
    class="source-image"
    [src]="safeImgDataUrl"
    (load)="handleImageLoadedInView()"
    [style.visibility]="imageVisible ? 'visible' : 'hidden'"
  />
  <div
    class="cropper"
    *ngIf="imageVisible"
    [class.rounded]="roundCropper"
    [style.top.px]="cropper.y1"
    [style.left.px]="cropper.x1"
    [style.width.px]="cropper.x2 - cropper.x1"
    [style.height.px]="cropper.y2 - cropper.y1"
    [style.margin-left]="alignImage === 'center' ? marginLeft : null"
    [style.visibility]="imageVisible ? 'visible' : 'hidden'"
  >
    <div
      (mousedown)="handleStartMove($event, 'move')"
      (touchstart)="handleStartMove($event, 'move')"
      class="move"
    >
      &nbsp;
    </div>
    <span
      class="resize topleft"
      (mousedown)="handleStartMove($event, 'resize', 'topleft')"
      (touchstart)="handleStartMove($event, 'resize', 'topleft')"
      ><span class="square"></span
    ></span>
    <span class="resize top"><span class="square"></span></span>
    <span
      class="resize topright"
      (mousedown)="handleStartMove($event, 'resize', 'topright')"
      (touchstart)="handleStartMove($event, 'resize', 'topright')"
      ><span class="square"></span
    ></span>
    <span class="resize right"><span class="square"></span></span>
    <span
      class="resize bottomright"
      (mousedown)="handleStartMove($event, 'resize', 'bottomright')"
      (touchstart)="handleStartMove($event, 'resize', 'bottomright')"
      ><span class="square"></span
    ></span>
    <span class="resize bottom"><span class="square"></span></span>
    <span
      class="resize bottomleft"
      (mousedown)="handleStartMove($event, 'resize', 'bottomleft')"
      (touchstart)="handleStartMove($event, 'resize', 'bottomleft')"
      ><span class="square"></span
    ></span>
    <span class="resize left"><span class="square"></span></span>
    <span
      class="resize-bar top"
      (mousedown)="handleStartMove($event, 'resize', 'top')"
      (touchstart)="handleStartMove($event, 'resize', 'top')"
    ></span>
    <span
      class="resize-bar right"
      (mousedown)="handleStartMove($event, 'resize', 'right')"
      (touchstart)="handleStartMove($event, 'resize', 'right')"
    ></span>
    <span
      class="resize-bar bottom"
      (mousedown)="handleStartMove($event, 'resize', 'bottom')"
      (touchstart)="handleStartMove($event, 'resize', 'bottom')"
    ></span>
    <span
      class="resize-bar left"
      (mousedown)="handleStartMove($event, 'resize', 'left')"
      (touchstart)="handleStartMove($event, 'resize', 'left')"
    ></span>
  </div>

  <div class="actions" fxLayout="col" fxLayoutAlign="space-between center">
    <button color="primary" mat-raised-button (click)="handleCancel()">
      <x-icon [name]="'cancel'"></x-icon>
    </button>

    <button color="primary" mat-raised-button (click)="handleRotateLeft()">
      <x-icon [name]="'rotate_left'"></x-icon>
    </button>

    <button color="primary" mat-raised-button (click)="handleFlipHorizontal()">
      <x-icon [name]="'swap_horiz'"></x-icon>
    </button>

    <button color="primary" mat-raised-button (click)="handleFlipVertical()">
      <x-icon [name]="'swap_vert'"></x-icon>
    </button>

    <button color="primary" mat-raised-button (click)="handleRotateRight()">
      <x-icon [name]="'rotate_right'"></x-icon>
    </button>

    <button color="primary" mat-raised-button (click)="handleOk()">
      <x-icon [name]="'done'"></x-icon>
    </button>
  </div>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-image-cropper\x-image-cropper.component.scss

:host::ng-deep .mat-dialog-container {
  padding: 0 !important;
  margin: 0 !important;
}

x-icon,
mat-icon {
  color: white;
}


:host {
  display: flex;
  position: relative;

  width: 100%;
  max-width: 100%;
  max-height: 100%;

  padding-top: 6px;

  overflow: hidden;

  text-align: center;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

:host > div {
  position: relative;
  width: 100%;
}

:host div > img.source-image {
  max-width: 98%;
  max-height: 98%;
}

.actions {
  margin: 2px;
  margin-top: 6px;
}

.actions > button {
  width: 36px;
  min-width: 36px;

  height: 36px36px;
  min-height: 36px;

  padding: 0;
}

.cropper {
  position: absolute;
  display: flex;
  color: #53535c;
  background: transparent;
  outline-color: rgba(255, 255, 255, 0.3);
  outline-width: 100vw;
  outline-style: solid;
  touch-action: none;
}
@media (orientation: portrait) {
  .cropper {
    outline-width: 100vh;
  }
}
.cropper:after {
  position: absolute;
  content: '';
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  pointer-events: none;
  border: dashed 1px;
  opacity: 0.75;
  color: inherit;
  z-index: 1;
}
.cropper .move {
  width: 100%;
  cursor: move;
  border: 1px solid rgba(255, 255, 255, 0.5);
}
.cropper .resize {
  position: absolute;
  display: inline-block;
  line-height: 6px;
  padding: 8px;
  opacity: 0.85;
  z-index: 1;
}
.cropper .resize .square {
  display: inline-block;
  background: #53535c;
  width: 6px;
  height: 6px;
  border: 1px solid rgba(255, 255, 255, 0.5);
  box-sizing: content-box;
}
.cropper .resize.topleft {
  top: -12px;
  left: -12px;
  cursor: nwse-resize;
}
.cropper .resize.top {
  top: -12px;
  left: calc(50% - 12px);
  cursor: ns-resize;
}
.cropper .resize.topright {
  top: -12px;
  right: -12px;
  cursor: nesw-resize;
}
.cropper .resize.right {
  top: calc(50% - 12px);
  right: -12px;
  cursor: ew-resize;
}
.cropper .resize.bottomright {
  bottom: -12px;
  right: -12px;
  cursor: nwse-resize;
}
.cropper .resize.bottom {
  bottom: -12px;
  left: calc(50% - 12px);
  cursor: ns-resize;
}
.cropper .resize.bottomleft {
  bottom: -12px;
  left: -12px;
  cursor: nesw-resize;
}
.cropper .resize.left {
  top: calc(50% - 12px);
  left: -12px;
  cursor: ew-resize;
}
.cropper .resize-bar {
  position: absolute;
  z-index: 1;
}
.cropper .resize-bar.top {
  top: -11px;
  left: 11px;
  width: calc(100% - 22px);
  height: 22px;
  cursor: ns-resize;
}
.cropper .resize-bar.right {
  top: 11px;
  right: -11px;
  height: calc(100% - 22px);
  width: 22px;
  cursor: ew-resize;
}
.cropper .resize-bar.bottom {
  bottom: -11px;
  left: 11px;
  width: calc(100% - 22px);
  height: 22px;
  cursor: ns-resize;
}
.cropper .resize-bar.left {
  top: 11px;
  left: -11px;
  height: calc(100% - 22px);
  width: 22px;
  cursor: ew-resize;
}
.cropper.rounded {
  outline-color: transparent;
}
.cropper.rounded:after {
  border-radius: 100%;
  box-shadow: 0 0 0 100vw rgba(255, 255, 255, 0.3);
}
@media (orientation: portrait) {
  .cropper.rounded:after {
    box-shadow: 0 0 0 100vh rgba(255, 255, 255, 0.3);
  }
}
.cropper.rounded .move {
  border-radius: 100%;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-image-cropper\x-image-cropper.component.ts

import {
  NgZone,
  Inject,
  Component,
  ViewChild,
  Renderer2,
  ElementRef,
  HostListener,
  ChangeDetectorRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  isPromise,
  resizeCanvas,
  XDialogResult,
  isNullOrUndefined,
  isNullOrEmptyString,
  resetExifOrientation,
  transformBase64BasedOnExifRotation,
} from 'x-framework-core';
import {
  XMoveStart,
  XDimensions,
  XOutputType,
  XCropperPosition,
  XImageCroppedEvent,
  XImageCropperAction,
  XImageCropperContainer,
  XImageCropperProperties,
  XImageCropperActionModel,
  XImageCropperContainerComponent,
} from './x-image-cropper.typings';
import { Subject, Subscription } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { SafeUrl, SafeStyle, DomSanitizer } from '@angular/platform-browser';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from './../config/x-framework-components.config';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-image-cropper',
  templateUrl: './x-image-cropper.component.html',
  styleUrls: ['./x-image-cropper.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XImageCropperComponent extends XImageCropperContainerComponent {
  //
  //#region Props ...
  private originalImage: any;
  private originalBase64: string;
  private moveStart: XMoveStart;
  private maxSize: XDimensions;
  private originalSize: XDimensions;
  private setImageMaxSizeRetries = 0;
  private cropperScaledMinWidth = 20;
  private cropperScaledMinHeight = 20;

  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  safeImgDataUrl: SafeUrl | string;
  marginLeft: SafeStyle | string = '0px';
  marginRight: SafeStyle | string = '0px';
  imageVisible = false;

  //
  @ViewChild('sourceImage', { static: false })
  sourceImage: ElementRef;

  //
  //#region Host Listeners ...
  @HostListener('window:resize')
  onResize(): void {
    this.resizeCropperPosition();
    this.setMaxSize();
    this.setCropperScaledMinSize();
  }

  @HostListener('document:mousemove', ['$event'])
  @HostListener('document:touchmove', ['$event'])
  moveImg(event: any): void {
    if (this.moveStart.active) {
      event.stopPropagation();
      event.preventDefault();
      if (this.moveStart.type === 'move') {
        this.move(event);
        this.checkCropperPosition(true);
      } else if (this.moveStart.type === 'resize') {
        this.resize(event);
        this.checkCropperPosition(false);
      }
      this.changeDetector.detectChanges();
    }
  }

  @HostListener('document:mouseup')
  @HostListener('document:touchend')
  moveStop(): void {
    if (this.moveStart.active) {
      this.moveStart.active = false;
      this.doAutoCrop();
    }
  }
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XImageCropperContainer,
    private sanitizer: DomSanitizer,
    @Inject(MAT_DIALOG_DATA) private data: any,
    private dialogRef: MatDialogRef<XImageCropperComponent>
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region LifeCycles ...
  onInit() {
    this.startUp();
  }

  onChanges(changeKeys: string[]): void {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XImageCropperComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region ImageFileChanged ...
    const isImageFileChangedChanged = changeKeys.includes(
      nameof<XImageCropperComponent>('imageFileChanged')
    );
    if (isImageFileChangedChanged) {
      //
      this.initCropper();
      if (this.imageFileChanged) {
        this.loadImageFile(this.imageFileChanged);
      }
    }
    //#endregion

    //
    //#region Image Changed Event Changed ...
    const isImageChangedEventChanged = changeKeys.includes(
      nameof<XImageCropperComponent>('imageChangedEvent')
    );
    if (isImageChangedEventChanged) {
      //
      this.initCropper();
      if (
        this.imageChangedEvent &&
        this.imageChangedEvent.target &&
        this.imageChangedEvent.target.files &&
        this.imageChangedEvent.target.files.length > 0
      ) {
        this.loadImageFile(this.imageChangedEvent.target.files[0]);
      }
    }
    //#endregion

    //
    //#region CropperChanged ...
    const isCropperChanged = changeKeys.includes(
      nameof<XImageCropperComponent>('cropper')
    );
    if (isCropperChanged) {
      //
      this.setMaxSize();
      this.setCropperScaledMinSize();
      this.checkCropperPosition(false);
      this.doAutoCrop();
      this.changeDetector.markForCheck();
    }
    //#endregion

    //
    //#region Aspect Ratio Changed ...
    const isAspectRatioChanged = changeKeys.includes(
      nameof<XImageCropperComponent>('aspectRatio')
    );
    if (isAspectRatioChanged && this.imageVisible) {
      this.resetCropperPosition();
    }
    //#endregion

    //
    //#region ImageBase64 Changed ...
    const isImageBase64Changed = changeKeys.includes(
      nameof<XImageCropperComponent>('imageBase64')
    );
    if (isImageBase64Changed) {
      //
      this.initCropper();
      if (!isNullOrEmptyString(this.imageBase64)) {
        this.loadBase64Image(this.imageBase64);
      }
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Handlers ...
  handleImageLoadedInView(): void {
    if (this.originalImage != null) {
      this.imageLoaded.emit();
      this.setImageMaxSizeRetries = 0;
      setTimeout(() => this.checkImageMaxSizeRecursively());
    }
  }

  handleCancel(): void {
    this.dialogRef.close({
      result: XDialogResult.Cancel,
    });
  }

  handleOk(): void {
    //
    // Get Crop Result and make Dialog close
    // Strategy based on it ...
    const res = this.crop();

    //
    // if result is null ...
    if (isNullOrUndefined(res)) {
      //
      this.dialogRef.close({
        event: null,
        result: XDialogResult.Ok,
      });
      //
      // if result is Promise Object ...
    } else if (isPromise(res)) {
      //
      (res as Promise<XImageCroppedEvent>).then((event) => {
        this.dialogRef.close({
          event,
          result: XDialogResult.Ok,
        });
      });
      //
      // if result ir ImagedCroppedEvent instance ...
    } else {
      this.dialogRef.close({
        event: res,
        result: XDialogResult.Ok,
      });
    }
  }

  handleRotateLeft() {
    this.rotateLeft();
  }

  handleFlipHorizontal() {
    this.flipHorizontal();
  }

  handleFlipVertical() {
    this.flipVertical();
  }

  handleRotateRight() {
    this.rotateRight();
  }

  handleStartMove(
    event: any,
    moveType: string,
    position: string | null = null
  ): void {
    event.preventDefault();
    this.moveStart = {
      active: true,
      type: moveType,
      position,
      clientX: this.getClientX(event),
      clientY: this.getClientY(event),
      ...this.cropper,
    };
  }
  //#endregion

  //
  //#region Actions ...
  rotateLeft() {
    this.transformBase64(8);
  }

  rotateRight() {
    this.transformBase64(6);
  }

  flipHorizontal() {
    this.transformBase64(2);
  }

  flipVertical() {
    this.transformBase64(4);
  }

  crop(
    outputType: XOutputType = this.outputType
  ): XImageCroppedEvent | Promise<XImageCroppedEvent> | null {
    if (this.sourceImage.nativeElement && this.originalImage != null) {
      this.startCropImage.emit();
      const imagePosition = this.getImagePosition();
      const width = imagePosition.x2 - imagePosition.x1;
      const height = imagePosition.y2 - imagePosition.y1;

      const cropCanvas = document.createElement('canvas') as HTMLCanvasElement;
      cropCanvas.width = width;
      cropCanvas.height = height;

      const ctx = cropCanvas.getContext('2d');
      if (ctx) {
        ctx.drawImage(
          this.originalImage,
          imagePosition.x1,
          imagePosition.y1,
          width,
          height,
          0,
          0,
          width,
          height
        );
        const output = {
          width,
          height,
          imagePosition,
          cropperPosition: { ...this.cropper },
        };
        const resizeRatio = this.getResizeRatio(width);
        if (resizeRatio !== 1) {
          output.width = Math.floor(width * resizeRatio);
          output.height = Math.floor(height * resizeRatio);
          resizeCanvas(cropCanvas, output.width, output.height);
        }

        //
        const result = this.cropToOutputType(outputType, cropCanvas, output);
        return result;
      }
    }
    return null;
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XImageCropperActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XImageCropperAction.Refresh:
            this.detectChanges();
            break;

          //
          case XImageCropperAction.RotateLeft:
            this.rotateLeft();
            break;

          //
          case XImageCropperAction.RotateRight:
            this.rotateRight();
            break;

          //
          case XImageCropperAction.FlipVertical:
            this.flipVertical();
            break;

          //
          case XImageCropperAction.FlipHorizontal:
            this.flipHorizontal();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private startUp() {
    //
    // Handle Dialog Data ...
    if (this.data) {
      //
      //#region Handle Properties ...
      if (this.data.props) {
        //
        const props = this.data.props as XImageCropperProperties;

        //
        if (props.imagefile) {
          this.imageFileChanged = props.imagefile;
        }

        //
        if (props.imageChangedEvent) {
          this.imageChangedEvent = props.imageChangedEvent;
        }

        //
        if (props.imageBase64) {
          this.imageBase64 = props.imageBase64;
        }

        //
        if (props.format) {
          this.format = props.format;
        }

        //
        if (props.outputType) {
          this.outputType = props.outputType;
        }

        //
        if (props.alignImage) {
          this.alignImage = props.alignImage;
        }

        //
        if (props.cropper) {
          this.cropper = props.cropper;
        }

        //
        if (props.autoCrop) {
          this.autoCrop = props.autoCrop;
        }

        //
        if (props.aspectRatio) {
          this.aspectRatio = props.aspectRatio;
        }

        //
        if (props.imageQuality) {
          this.imageQuality = props.imageQuality;
        }

        //
        if (props.resizeToWidth) {
          this.resizeToWidth = props.resizeToWidth;
        }

        //
        if (props.roundCropper) {
          this.roundCropper = props.roundCropper;
        }

        //
        if (props.onlyScaleDown) {
          this.onlyScaleDown = props.onlyScaleDown;
        }

        //
        if (props.cropperMinWidth) {
          this.cropperMinWidth = props.cropperMinWidth;
        }

        //
        if (props.cropperMinHeight) {
          this.cropperMinHeight = props.cropperMinHeight;
        }

        //
        if (props.maintainAspectRatio) {
          this.maintainAspectRatio = props.maintainAspectRatio;
        }
      }
      //#endregion
    }

    //
    this.initCropper();
    if (this.imageFileChanged) {
      this.loadImageFile(this.imageFileChanged);
    }
  }

  private initCropper(): void {
    this.imageVisible = false;
    this.originalImage = null;
    this.safeImgDataUrl =
      'data:image/png;base64,iVBORw0KGg' +
      'oAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAU' +
      'AAarVyFEAAAAASUVORK5CYII=';
    this.moveStart = {
      active: false,
      type: null,
      position: null,
      x1: 0,
      y1: 0,
      x2: 0,
      y2: 0,
      clientX: 0,
      clientY: 0,
    };
    this.maxSize = {
      width: 0,
      height: 0,
    };
    this.originalSize = {
      width: 0,
      height: 0,
    };
    this.cropper.x1 = -100;
    this.cropper.y1 = -100;
    this.cropper.x2 = 10000;
    this.cropper.y2 = 10000;
  }

  private loadImageFile(file: File): void {
    const fileReader = new FileReader();
    fileReader.onload = (event: any) => {
      const imageType = file.type;
      if (this.isValidImageType(imageType)) {
        resetExifOrientation(event.target.result)
          .then((resultBase64: string) => this.loadBase64Image(resultBase64))
          .catch(() => this.loadImageFailed.emit());
      } else {
        this.loadImageFailed.emit();
      }
    };
    fileReader.readAsDataURL(file);
  }

  private isValidImageType(type: string): boolean {
    return /image\/(png|jpg|jpeg|bmp|gif|tiff)/.test(type);
  }

  private loadBase64Image(imageBase64: string): void {
    this.originalBase64 = imageBase64;
    this.safeImgDataUrl =
      this.sanitizer.bypassSecurityTrustResourceUrl(imageBase64);
    this.originalImage = new Image();
    this.originalImage.onload = () => {
      this.originalSize.width = this.originalImage.width;
      this.originalSize.height = this.originalImage.height;
      this.changeDetector.markForCheck();
    };
    this.originalImage.src = imageBase64;
  }

  private checkImageMaxSizeRecursively(): void {
    if (this.setImageMaxSizeRetries > 40) {
      this.loadImageFailed.emit();
    } else if (
      this.sourceImage &&
      this.sourceImage.nativeElement &&
      this.sourceImage.nativeElement.offsetWidth > 0
    ) {
      this.setMaxSize();
      this.setCropperScaledMinSize();
      this.resetCropperPosition();
      this.cropperReady.emit();
      this.changeDetector.markForCheck();
    } else {
      this.setImageMaxSizeRetries++;
      setTimeout(() => {
        this.checkImageMaxSizeRecursively();
      }, 50);
    }
  }

  private transformBase64(exifOrientation: number): void {
    if (this.originalBase64) {
      transformBase64BasedOnExifRotation(
        this.originalBase64,
        exifOrientation
      ).then((rotatedBase64: string) => this.loadBase64Image(rotatedBase64));
    }
  }

  private resizeCropperPosition(): void {
    const sourceImageElement = this.sourceImage.nativeElement;
    if (
      this.maxSize.width !== sourceImageElement.offsetWidth ||
      this.maxSize.height !== sourceImageElement.offsetHeight
    ) {
      this.cropper.x1 =
        (this.cropper.x1 * sourceImageElement.offsetWidth) / this.maxSize.width;
      this.cropper.x2 =
        (this.cropper.x2 * sourceImageElement.offsetWidth) / this.maxSize.width;
      this.cropper.y1 =
        (this.cropper.y1 * sourceImageElement.offsetHeight) /
        this.maxSize.height;
      this.cropper.y2 =
        (this.cropper.y2 * sourceImageElement.offsetHeight) /
        this.maxSize.height;
    }
  }

  private resetCropperPosition(): void {
    const sourceImageElement = this.sourceImage.nativeElement;
    if (!this.maintainAspectRatio) {
      this.cropper.x1 = 0;
      this.cropper.x2 = sourceImageElement.offsetWidth;
      this.cropper.y1 = 0;
      this.cropper.y2 = sourceImageElement.offsetHeight;
    } else if (
      sourceImageElement.offsetWidth / this.aspectRatio <
      sourceImageElement.offsetHeight
    ) {
      this.cropper.x1 = 0;
      this.cropper.x2 = sourceImageElement.offsetWidth;
      const cropperHeight = sourceImageElement.offsetWidth / this.aspectRatio;
      this.cropper.y1 = (sourceImageElement.offsetHeight - cropperHeight) / 2;
      this.cropper.y2 = this.cropper.y1 + cropperHeight;
    } else {
      this.cropper.y1 = 0;
      this.cropper.y2 = sourceImageElement.offsetHeight;
      const cropperWidth = sourceImageElement.offsetHeight * this.aspectRatio;
      this.cropper.x1 = (sourceImageElement.offsetWidth - cropperWidth) / 2;
      this.cropper.x2 = this.cropper.x1 + cropperWidth;
    }
    this.doAutoCrop();
    this.imageVisible = true;
  }

  private setMaxSize(): void {
    const sourceImageElement = this.sourceImage.nativeElement;
    this.maxSize.width = sourceImageElement.offsetWidth;
    this.maxSize.height = sourceImageElement.offsetHeight;
    this.marginLeft = this.sanitizer.bypassSecurityTrustStyle(
      'calc(50% - ' + this.maxSize.width / 2 + 'px)'
    );
    this.marginRight = this.sanitizer.bypassSecurityTrustStyle(
      'calc(50% - ' + this.maxSize.width / 2 + 'px)'
    );
  }

  private setCropperScaledMinSize(): void {
    if (this.originalImage) {
      this.setCropperScaledMinWidth();
      this.setCropperScaledMinHeight();
    } else {
      this.cropperScaledMinWidth = 20;
      this.cropperScaledMinHeight = 20;
    }
  }

  private setCropperScaledMinWidth(): void {
    this.cropperScaledMinWidth =
      this.cropperMinWidth > 0
        ? Math.max(
            20,
            (this.cropperMinWidth / this.originalImage.width) *
              this.maxSize.width
          )
        : 20;
  }

  private setCropperScaledMinHeight(): void {
    if (this.maintainAspectRatio) {
      this.cropperScaledMinHeight = Math.max(
        20,
        this.cropperScaledMinWidth / this.aspectRatio
      );
    } else if (this.cropperMinHeight > 0) {
      this.cropperScaledMinHeight = Math.max(
        20,
        (this.cropperMinHeight / this.originalImage.height) *
          this.maxSize.height
      );
    } else {
      this.cropperScaledMinHeight = 20;
    }
  }

  private checkCropperPosition(maintainSize = false): void {
    if (this.cropper.x1 < 0) {
      this.cropper.x2 -= maintainSize ? this.cropper.x1 : 0;
      this.cropper.x1 = 0;
    }
    if (this.cropper.y1 < 0) {
      this.cropper.y2 -= maintainSize ? this.cropper.y1 : 0;
      this.cropper.y1 = 0;
    }
    if (this.cropper.x2 > this.maxSize.width) {
      this.cropper.x1 -= maintainSize
        ? this.cropper.x2 - this.maxSize.width
        : 0;
      this.cropper.x2 = this.maxSize.width;
    }
    if (this.cropper.y2 > this.maxSize.height) {
      this.cropper.y1 -= maintainSize
        ? this.cropper.y2 - this.maxSize.height
        : 0;
      this.cropper.y2 = this.maxSize.height;
    }
  }

  private move(event: any) {
    const diffX = this.getClientX(event) - this.moveStart.clientX;
    const diffY = this.getClientY(event) - this.moveStart.clientY;

    this.cropper.x1 = this.moveStart.x1 + diffX;
    this.cropper.y1 = this.moveStart.y1 + diffY;
    this.cropper.x2 = this.moveStart.x2 + diffX;
    this.cropper.y2 = this.moveStart.y2 + diffY;
  }

  private resize(event: any): void {
    const diffX = this.getClientX(event) - this.moveStart.clientX;
    const diffY = this.getClientY(event) - this.moveStart.clientY;
    switch (this.moveStart.position) {
      case 'left':
        this.cropper.x1 = Math.min(
          this.moveStart.x1 + diffX,
          this.cropper.x2 - this.cropperScaledMinWidth
        );
        break;
      case 'topleft':
        this.cropper.x1 = Math.min(
          this.moveStart.x1 + diffX,
          this.cropper.x2 - this.cropperScaledMinWidth
        );
        this.cropper.y1 = Math.min(
          this.moveStart.y1 + diffY,
          this.cropper.y2 - this.cropperScaledMinHeight
        );
        break;
      case 'top':
        this.cropper.y1 = Math.min(
          this.moveStart.y1 + diffY,
          this.cropper.y2 - this.cropperScaledMinHeight
        );
        break;
      case 'topright':
        this.cropper.x2 = Math.max(
          this.moveStart.x2 + diffX,
          this.cropper.x1 + this.cropperScaledMinWidth
        );
        this.cropper.y1 = Math.min(
          this.moveStart.y1 + diffY,
          this.cropper.y2 - this.cropperScaledMinHeight
        );
        break;
      case 'right':
        this.cropper.x2 = Math.max(
          this.moveStart.x2 + diffX,
          this.cropper.x1 + this.cropperScaledMinWidth
        );
        break;
      case 'bottomright':
        this.cropper.x2 = Math.max(
          this.moveStart.x2 + diffX,
          this.cropper.x1 + this.cropperScaledMinWidth
        );
        this.cropper.y2 = Math.max(
          this.moveStart.y2 + diffY,
          this.cropper.y1 + this.cropperScaledMinHeight
        );
        break;
      case 'bottom':
        this.cropper.y2 = Math.max(
          this.moveStart.y2 + diffY,
          this.cropper.y1 + this.cropperScaledMinHeight
        );
        break;
      case 'bottomleft':
        this.cropper.x1 = Math.min(
          this.moveStart.x1 + diffX,
          this.cropper.x2 - this.cropperScaledMinWidth
        );
        this.cropper.y2 = Math.max(
          this.moveStart.y2 + diffY,
          this.cropper.y1 + this.cropperScaledMinHeight
        );
        break;
    }

    if (this.maintainAspectRatio) {
      this.checkAspectRatio();
    }
  }

  private checkAspectRatio(): void {
    let overflowX = 0;
    let overflowY = 0;

    switch (this.moveStart.position) {
      case 'top':
        this.cropper.x2 =
          this.cropper.x1 +
          (this.cropper.y2 - this.cropper.y1) * this.aspectRatio;
        overflowX = Math.max(this.cropper.x2 - this.maxSize.width, 0);
        overflowY = Math.max(0 - this.cropper.y1, 0);
        if (overflowX > 0 || overflowY > 0) {
          this.cropper.x2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY * this.aspectRatio
              : overflowX;
          this.cropper.y1 +=
            overflowY * this.aspectRatio > overflowX
              ? overflowY
              : overflowX / this.aspectRatio;
        }
        break;
      case 'bottom':
        this.cropper.x2 =
          this.cropper.x1 +
          (this.cropper.y2 - this.cropper.y1) * this.aspectRatio;
        overflowX = Math.max(this.cropper.x2 - this.maxSize.width, 0);
        overflowY = Math.max(this.cropper.y2 - this.maxSize.height, 0);
        if (overflowX > 0 || overflowY > 0) {
          this.cropper.x2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY * this.aspectRatio
              : overflowX;
          this.cropper.y2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY
              : overflowX / this.aspectRatio;
        }
        break;
      case 'topleft':
        this.cropper.y1 =
          this.cropper.y2 -
          (this.cropper.x2 - this.cropper.x1) / this.aspectRatio;
        overflowX = Math.max(0 - this.cropper.x1, 0);
        overflowY = Math.max(0 - this.cropper.y1, 0);
        if (overflowX > 0 || overflowY > 0) {
          this.cropper.x1 +=
            overflowY * this.aspectRatio > overflowX
              ? overflowY * this.aspectRatio
              : overflowX;
          this.cropper.y1 +=
            overflowY * this.aspectRatio > overflowX
              ? overflowY
              : overflowX / this.aspectRatio;
        }
        break;
      case 'topright':
        this.cropper.y1 =
          this.cropper.y2 -
          (this.cropper.x2 - this.cropper.x1) / this.aspectRatio;
        overflowX = Math.max(this.cropper.x2 - this.maxSize.width, 0);
        overflowY = Math.max(0 - this.cropper.y1, 0);
        if (overflowX > 0 || overflowY > 0) {
          this.cropper.x2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY * this.aspectRatio
              : overflowX;
          this.cropper.y1 +=
            overflowY * this.aspectRatio > overflowX
              ? overflowY
              : overflowX / this.aspectRatio;
        }
        break;
      case 'right':
      case 'bottomright':
        this.cropper.y2 =
          this.cropper.y1 +
          (this.cropper.x2 - this.cropper.x1) / this.aspectRatio;
        overflowX = Math.max(this.cropper.x2 - this.maxSize.width, 0);
        overflowY = Math.max(this.cropper.y2 - this.maxSize.height, 0);
        if (overflowX > 0 || overflowY > 0) {
          this.cropper.x2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY * this.aspectRatio
              : overflowX;
          this.cropper.y2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY
              : overflowX / this.aspectRatio;
        }
        break;
      case 'left':
      case 'bottomleft':
        this.cropper.y2 =
          this.cropper.y1 +
          (this.cropper.x2 - this.cropper.x1) / this.aspectRatio;
        overflowX = Math.max(0 - this.cropper.x1, 0);
        overflowY = Math.max(this.cropper.y2 - this.maxSize.height, 0);
        if (overflowX > 0 || overflowY > 0) {
          this.cropper.x1 +=
            overflowY * this.aspectRatio > overflowX
              ? overflowY * this.aspectRatio
              : overflowX;
          this.cropper.y2 -=
            overflowY * this.aspectRatio > overflowX
              ? overflowY
              : overflowX / this.aspectRatio;
        }
        break;
    }
  }

  private doAutoCrop(): void {
    if (this.autoCrop) {
      this.crop();
    }
  }

  private getImagePosition(): XCropperPosition {
    const sourceImageElement = this.sourceImage.nativeElement;
    const ratio = this.originalSize.width / sourceImageElement.offsetWidth;
    return {
      x1: Math.round(this.cropper.x1 * ratio),
      y1: Math.round(this.cropper.y1 * ratio),
      x2: Math.min(
        Math.round(this.cropper.x2 * ratio),
        this.originalSize.width
      ),
      y2: Math.min(
        Math.round(this.cropper.y2 * ratio),
        this.originalSize.height
      ),
    };
  }

  private cropToOutputType(
    outputType: XOutputType,
    cropCanvas: HTMLCanvasElement,
    output: XImageCroppedEvent
  ): XImageCroppedEvent | Promise<XImageCroppedEvent> {
    switch (outputType) {
      case 'file':
        return this.cropToFile(cropCanvas).then((result: Blob | null) => {
          output.file = result;
          this.imageCropped.emit(output);
          return output;
        });
      case 'both':
        return this.cropToFile(cropCanvas).then((result: Blob | null) => {
          output.base64 = this.cropToBase64(cropCanvas);
          output.file = result;
          this.imageCropped.emit(output);
          return output;
        });
      default:
        output.base64 = this.cropToBase64(cropCanvas);
        this.imageCropped.emit(output);
        return output;
    }
  }

  private cropToBase64(cropCanvas: HTMLCanvasElement): string {
    const imageBase64 = cropCanvas.toDataURL(
      'image/' + this.format,
      this.getQuality()
    );
    this.imageCroppedBase64.emit(imageBase64);
    return imageBase64;
  }

  private cropToFile(cropCanvas: HTMLCanvasElement): Promise<Blob | null> {
    return this.getCanvasBlob(cropCanvas).then((result: Blob | null) => {
      if (result) {
        this.imageCroppedFile.emit(result);
      }
      return result;
    });
  }

  private getCanvasBlob(cropCanvas: HTMLCanvasElement): Promise<Blob | null> {
    return new Promise((resolve) => {
      cropCanvas.toBlob(
        (result: Blob | null) => this.zone.run(() => resolve(result)),
        'image/' + this.format,
        this.getQuality()
      );
    });
  }

  private getQuality(): number {
    return Math.min(1, Math.max(0, this.imageQuality / 100));
  }

  private getResizeRatio(width: number): number {
    return this.resizeToWidth > 0 &&
      (!this.onlyScaleDown || width > this.resizeToWidth)
      ? this.resizeToWidth / width
      : 1;
  }

  private getClientX(event: any): number {
    return (
      event.clientX ||
      (event.touches && event.touches[0] && event.touches[0].clientX)
    );
  }

  private getClientY(event: any): number {
    return (
      event.clientY ||
      (event.touches && event.touches[0] && event.touches[0].clientY)
    );
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-image-cropper\x-image-cropper.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XImageCropperService } from './x-image-cropper.service';
import { XImageCropperComponent } from './x-image-cropper.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  providers: [XImageCropperService],
  declarations: [XImageCropperComponent],
  entryComponents: [XImageCropperComponent],
  imports: [CommonModule, XBaseComponentModule, XIconModule],
  exports: [XBaseComponentModule, XIconModule, XImageCropperComponent]
})
export class XImageCropperModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-image-cropper\x-image-cropper.service.ts

import {
  toFileBlob,
  blobToFile,
  XDialogResult,
  isNullOrUndefined,
} from 'x-framework-core';
import {
  MatDialog,
  MatDialogRef,
  MatDialogConfig,
} from '@angular/material/dialog';
import {
  XImageCroppedEvent,
  XImageCropperResult,
  XImageCropperProperties,
} from './x-image-cropper.typings';
import { XImageCropperComponent } from './x-image-cropper.component';
import { Injectable, Output, EventEmitter, Inject } from '@angular/core';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

@Injectable({
  providedIn: 'root',
})
export class XImageCropperService {
  //
  //#region Props ...
  //
  private dialogMaxWidth: number;
  private dialogMinWidth: number;

  //
  private sourceFile: File;
  private DIALOG_REF: MatDialogRef<XImageCropperComponent>;

  //
  // tslint:disable-next-line:no-output-on-prefix
  @Output()
  onResult = new EventEmitter<XImageCropperResult>();

  //
  minAllowedImageSize: number;
  maxAllowedImageSize: number;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    private dialog: MatDialog,
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig
  ) {
    //
    this.minAllowedImageSize = config.minAllowedImageSize;
    this.maxAllowedImageSize = config.maxAllowedImageSize;

    //
    this.dialogMinWidth = Math.min(window.innerWidth * 0.8, 300);
    this.dialogMaxWidth = Math.min(window.innerWidth * 0.8, 400);
  }
  //#endregion

  //
  //#region Public Actions ...
  /**
   * show Image Cropper ...
   * @param file the source image file ...
   * @param props Image Cropper Properties ...
   */
  showCropper(file: File, props?: XImageCropperProperties) {
    //
    this.sourceFile = file;

    //
    // Prepare Cropper Props ...
    let cropperProps: XImageCropperProperties;
    if (props) {
      cropperProps = props;
      if (!props.imagefile) {
        cropperProps.imagefile = file;
      }
      cropperProps.maxSingleFileThumbSize = this.maxAllowedImageSize;
    } else {
      cropperProps = this.getDefaultCropperProperties(file);
    }

    //
    const config: MatDialogConfig = {
      width: this.dialogMinWidth + 'px',
      minWidth: this.dialogMinWidth + 'px',
      maxWidth: this.dialogMaxWidth + 'px',
      data: {
        props: cropperProps,
      },
    };

    //
    // Show Cropper inside a Dialog ...
    this.DIALOG_REF = this.dialog.open(XImageCropperComponent, config);
    this.DIALOG_REF.disableClose = true;

    //
    //#region Register Event Handlers ...
    //
    // Subscribe to Event Listener for catching Cropper Change ...
    const cropperComponent = this.DIALOG_REF
      ? this.DIALOG_REF.componentInstance
      : null;
    if (cropperComponent) {
      //
      if (cropperProps.startCropImage) {
        cropperComponent.startCropImage.asObservable().subscribe(() => {
          cropperProps.startCropImage();
        });
      }

      //
      if (cropperProps.imageCropped) {
        cropperComponent.imageCropped
          .asObservable()
          .subscribe((event: XImageCroppedEvent) => {
            cropperProps.imageCropped(event);
          });
      }

      //
      if (cropperProps.imageCroppedBase64) {
        cropperComponent.imageCroppedBase64
          .asObservable()
          .subscribe((data: string) => {
            cropperProps.imageCroppedBase64(data);
          });
      }

      //
      if (cropperProps.imageCroppedFile) {
        cropperComponent.imageCroppedFile
          .asObservable()
          .subscribe((mFile: Blob) => {
            cropperProps.imageCroppedFile(mFile);
          });
      }

      //
      if (cropperProps.imageLoaded) {
        cropperComponent.imageLoaded.asObservable().subscribe(() => {
          cropperProps.imageLoaded();
        });
      }

      //
      if (cropperProps.cropperReady) {
        cropperComponent.cropperReady.asObservable().subscribe(() => {
          cropperProps.cropperReady();
        });
      }

      //
      if (cropperProps.loadImageFailed) {
        cropperComponent.loadImageFailed.asObservable().subscribe(() => {
          cropperProps.loadImageFailed();
        });
      }
    }
    //#endregion

    //
    // Handle Dialog Result ...
    this.DIALOG_REF.afterClosed().subscribe((result) => {
      //
      if (isNullOrUndefined(result) || result.result !== XDialogResult.Ok) {
        //
        // Cancel Cropper ...
        this.onResult.emit({ result: XDialogResult.Cancel });
      } else {
        //
        // Use Cropper Result ...
        const res: XImageCroppedEvent = result.event as XImageCroppedEvent;

        //
        let mFile: File;
        if (res.file) {
          //
          mFile = this.toFile(res.file);
          this.sourceFile = mFile;
        }

        //
        this.onResult.emit({
          result: result.result,
          data: res.base64,
          file: mFile,
        });
      }
    });
  }

  /**
   * close opened Dialog manually ...
   */
  closeCropper() {
    //
    if (this.DIALOG_REF != null) {
      this.DIALOG_REF.close();
    }

    //
    this.DIALOG_REF = null;
  }
  //#endregion

  //
  //#region Private Actions ...
  /**
   * Prepare Default Properties for Image Cropper ...
   */
  private getDefaultCropperProperties(file: File) {
    return {
      //
      imagefile: file,

      //
      aspectRatio: 1,
      maintainAspectRatio: true,

      //
      resizeToWidth: this.dialogMinWidth,
      cropperMinWidth: this.minAllowedImageSize,
      cropperMinHeight: this.minAllowedImageSize,

      //
      onlyScaleDown: true,
      roundCropper: true,

      //
      format: 'png',
      outputType: 'both',

      //
      maxSingleFileThumbSize: this.maxAllowedImageSize,
    } as XImageCropperProperties;
  }

  /**
   * Generate a File Blob Object based on Source Image and Cropped file ...
   */
  private toFile(file: Blob): File {
    //
    if (!this.sourceFile) {
      return null;
    }

    //
    const fileBlob = toFileBlob(this.sourceFile);

    //
    return blobToFile(file, fileBlob.name);
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-image-cropper\x-image-cropper.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XDialogResult, XOneOrManyType, XStandardType } from 'x-framework-core';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Definitions ...
export interface XCropperPosition {
  x1: number;
  y1: number;
  x2: number;
  y2: number;
}

export interface XDimensions {
  width: number;
  height: number;
}

export interface XImageCroppedEvent {
  base64?: string | null;
  file?: Blob | null;
  width: number;
  height: number;
  cropperPosition: XCropperPosition;
  imagePosition: XCropperPosition;
}

export interface XMoveStart {
  active: boolean;
  type: string | null;
  position: string | null;
  x1: number;
  y1: number;
  x2: number;
  y2: number;
  clientX: number;
  clientY: number;
}

export interface XImageCropperProperties {
  //
  imageChangedEvent?: (event: any) => void;

  //
  imagefile?: File;
  imageBase64?: string;

  //
  format?: XImageFormat;
  outputType?: XOutputType;
  alignImage?: XImageAlign;
  cropper?: XCropperPosition;

  //
  autoCrop?: boolean;
  aspectRatio?: number;
  imageQuality?: number;
  resizeToWidth?: number;
  roundCropper?: boolean;
  onlyScaleDown?: boolean;
  cropperMinWidth?: number;
  cropperMinHeight?: number;
  maintainAspectRatio?: boolean;

  //
  maxSingleFileThumbSize: number;

  //
  startCropImage?: () => void;
  imageCropped?: (event: XImageCroppedEvent) => void;
  imageCroppedBase64?: (data: string) => void;
  imageCroppedFile?: (file: Blob) => void;
  imageLoaded?: () => void;
  cropperReady?: () => void;
  loadImageFailed?: () => void;
}

export interface XImageCropperResult {
  result: XDialogResult;
  data?: string;
  file?: File;
}

export type XOutputType = 'base64' | 'file' | 'both';

export type XImageFormat = 'png' | 'jpeg' | 'bmp' | 'webp' | 'ico';

export type XImageAlign = 'left' | 'center';
//#endregion

export enum XImageCropperAction {
  //
  RotateLeft = 'rotate-left',
  RotateRight = 'rotate-right',
  FlipHorizontal = 'flip-horizontal',
  FlipVertical = 'flip-vertical',

  //
  Refresh = 'refresh',
}

export type XImageCropperActionIdentifier = XImageCropperAction | string;

export interface XImageCropperActionModel
  extends XBaseActionModel<XImageCropperActionIdentifier> {}

//
//#region Container ...
export interface IXImageCropperContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XImageCropperActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  imageFileChanged: File;
  imageChangedEvent: any;
  imageBase64: string;
  format: XImageFormat;
  outputType: XOutputType;
  maintainAspectRatio: boolean;
  aspectRatio: number;
  resizeToWidth: number;
  cropperMinWidth: number;
  cropperMinHeight: number;
  roundCropper: boolean;
  onlyScaleDown: boolean;
  imageQuality: number;
  autoCrop: boolean;
  alignImage: XImageAlign;
  cropper: XCropperPosition;

  //
  startCropImage: EventEmitter<void>;
  imageCropped: EventEmitter<XImageCroppedEvent>;
  imageCroppedBase64: EventEmitter<string>;
  imageCroppedFile: EventEmitter<Blob>;
  imageLoaded: EventEmitter<void>;
  cropperReady: EventEmitter<void>;
  loadImageFailed: EventEmitter<void>;
}

@Injectable({
  providedIn: 'root',
})
export class XImageCropperContainer
  extends XBaseContainer
  implements Readonly<IXImageCropperContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XImageCropperActionModel> =
    new Subject<XImageCropperActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly imageFileChanged: File;
  readonly imageChangedEvent: any;
  readonly imageBase64: string;
  readonly format: XImageFormat = 'png';
  readonly outputType: XOutputType = 'both';
  readonly maintainAspectRatio: boolean = true;
  readonly aspectRatio: number = 1;
  readonly resizeToWidth: number = 0;
  readonly cropperMinWidth: number = 0;
  readonly cropperMinHeight: number = 0;
  readonly roundCropper: boolean = false;
  readonly onlyScaleDown: boolean = false;
  readonly imageQuality: number = 92;
  readonly autoCrop: boolean = true;
  readonly alignImage: XImageAlign = 'center';
  readonly cropper: XCropperPosition = {
    x1: -100,
    y1: -100,
    x2: 10000,
    y2: 10000,
  };

  //
  readonly startCropImage: EventEmitter<void> = new EventEmitter<void>();
  readonly imageCropped: EventEmitter<XImageCroppedEvent> =
    new EventEmitter<XImageCroppedEvent>();
  readonly imageCroppedBase64: EventEmitter<string> =
    new EventEmitter<string>();
  readonly imageCroppedFile: EventEmitter<Blob> = new EventEmitter<Blob>();
  readonly imageLoaded: EventEmitter<void> = new EventEmitter<void>();
  readonly cropperReady: EventEmitter<void> = new EventEmitter<void>();
  readonly loadImageFailed: EventEmitter<void> = new EventEmitter<void>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XImageCropperContainerComponent
  extends XBaseContainerComponent
  implements IXImageCropperContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XImageCropperActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  imageFileChanged: File = this.propertyProvider.imageFileChanged;

  @Input()
  imageChangedEvent: any = this.propertyProvider.imageChangedEvent;

  @Input()
  imageBase64: string = this.propertyProvider.imageBase64;

  @Input()
  format: XImageFormat = this.propertyProvider.format;

  @Input()
  outputType: XOutputType = this.propertyProvider.outputType;

  @Input()
  maintainAspectRatio: boolean = this.propertyProvider.maintainAspectRatio;

  @Input()
  aspectRatio: number = this.propertyProvider.aspectRatio;

  @Input()
  resizeToWidth: number = this.propertyProvider.resizeToWidth;

  @Input()
  cropperMinWidth: number = this.propertyProvider.cropperMinWidth;

  @Input()
  cropperMinHeight: number = this.propertyProvider.cropperMinHeight;

  @Input()
  roundCropper: boolean = this.propertyProvider.roundCropper;

  @Input()
  onlyScaleDown: boolean = this.propertyProvider.onlyScaleDown;

  @Input()
  imageQuality: number = this.propertyProvider.imageQuality;

  @Input()
  autoCrop: boolean = this.propertyProvider.autoCrop;

  @Input()
  alignImage: XImageAlign = this.propertyProvider.alignImage;

  @Input()
  cropper: XCropperPosition = this.propertyProvider.cropper;

  //
  @Output()
  startCropImage: EventEmitter<void> = new EventEmitter();

  @Output()
  imageCropped: EventEmitter<XImageCroppedEvent> = new EventEmitter();

  @Output()
  imageCroppedBase64: EventEmitter<string> = new EventEmitter();

  @Output()
  imageCroppedFile: EventEmitter<Blob> = new EventEmitter();

  @Output()
  imageLoaded: EventEmitter<void> = new EventEmitter();

  @Output()
  cropperReady: EventEmitter<void> = new EventEmitter();

  @Output()
  loadImageFailed: EventEmitter<void> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XImageCropperContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXImageCropperContainer
  ): Observable<IXImageCropperContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      imageFileChanged: this.getValue(
        container ? container.imageFileChanged : this.imageFileChanged
      ),
      imageChangedEvent: this.getValue(
        container ? container.imageChangedEvent : this.imageChangedEvent
      ),
      imageBase64: this.getValue(
        container ? container.imageBase64 : this.imageBase64
      ),
      format: this.getValue(container ? container.format : this.format),
      outputType: this.getValue(
        container ? container.outputType : this.outputType
      ),
      maintainAspectRatio: this.getValue(
        container ? container.maintainAspectRatio : this.maintainAspectRatio
      ),
      aspectRatio: this.getValue(
        container ? container.aspectRatio : this.aspectRatio
      ),
      resizeToWidth: this.getValue(
        container ? container.resizeToWidth : this.resizeToWidth
      ),
      cropperMinWidth: this.getValue(
        container ? container.cropperMinWidth : this.cropperMinWidth
      ),
      cropperMinHeight: this.getValue(
        container ? container.cropperMinHeight : this.cropperMinHeight
      ),
      roundCropper: this.getValue(
        container ? container.roundCropper : this.roundCropper
      ),
      onlyScaleDown: this.getValue(
        container ? container.onlyScaleDown : this.onlyScaleDown
      ),
      imageQuality: this.getValue(
        container ? container.imageQuality : this.imageQuality
      ),
      autoCrop: this.getValue(container ? container.autoCrop : this.autoCrop),
      alignImage: this.getValue(
        container ? container.alignImage : this.alignImage
      ),
      cropper: this.getValue(container ? container.cropper : this.cropper),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          startCropImage: this.startCropImage,
          imageCropped: this.imageCropped,
          imageCroppedBase64: this.imageCroppedBase64,
          imageCroppedFile: this.imageCroppedFile,
          imageLoaded: this.imageLoaded,
          cropperReady: this.cropperReady,
          loadImageFailed: this.loadImageFailed,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-list\x-list.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else listRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="listRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- List ItSelf Template -->
<ng-template #listRef>
  <div class="x-list x-wrapper" [class.mobile]="isMobileUi$ | async">
    <!-- Action and Search Area Container  -->
    <div
      class="x-list-toolbar"
      *ngIf="getValue(showToolbar) | async"
      [class.sticky]="getValue(stickyToolbar) | async"
    >
      <!-- ActionBar -->
      <x-action-bar
        *ngIf="getValue(showActionBar) | async"
        [color]="getValue(actionBarColor) | async"
        [actionProvider]="actionBarActionProvider"
        [cssClass]="getValue(actionBarCssClass) | async"
        (actionFired)="handleActionBarActionFired($event)"
        [actions]="getArrayValue(actionBarActions) | async"
        [foregroundColor]="getValue(actionBarForegroundColor) | async"
      ></x-action-bar>

      <!-- SearchBar -->
      <x-search-bar
        (queryChange)="filterItems($event)"
        *ngIf="getValue(showSearchBar) | async"
        (onEvent)="searchBarOnEvent.emit($event)"
        [type]="getValue(searchBarType) | async"
        [actionProvider]="searchBarActionProvider"
        [color]="getValue(searchBarColor) | async"
        [value]="getValue(searchBarValue) | async"
        [debounce]="getValue(searchBarDebounce) | async"
        [cssClass]="getValue(searchBarCssClass) | async"
        [title]="getResourceValue(searchBarTitle) | async"
        [spellcheck]="getValue(searchBarSpellcheck) | async"
        [foregroundSearchInputColor]="
          getValue(searchBarForegroundSearchInputColor) | async
        "
        [enterKeyHint]="getValue(searchBarEnterKeyHint) | async"
        [keyboardType]="getValue(searchBarKeyboardType) | async"
        [clearButtonIcon]="getValue(searchBarClearButtonIcon) | async"
        [foregroundColor]="getValue(searchBarForegroundColor) | async"
        [searchButtonIcon]="getValue(searchBarSearchButtonIcon) | async"
        [searchInputColor]="getValue(searchBarSearchInputColor) | async"
        [cancelButtonIcon]="getValue(searchBarCancelButtonIcon) | async"
        [showCancelButton]="getValue(searchBarShowCancelButton) | async"
        [cancelButtonTitle]="getValue(searchBarCancelButtonTitle) | async"
        [clearButtonIconColor]="getValue(searchBarClearButtonIconColor) | async"
        [searchButtonIconColor]="
          getValue(searchBarSearchButtonIconColor) | async
        "
      ></x-search-bar>
    </div>

    <!-- List -->
    <ion-list
      #list
      class="ion-no-padding ion-no-margin"
      [ngClass]="getValue(cssClass) | async"
      *ngIf="hasChildValue(filteredItems) | async"
    >
      <ion-reorder-group
        id="reorder"
        disabled="false"
        (ionItemReorder)="handleDoReorderItems($event)"
      >
        <ng-container *ngFor="let item of filteredItems; let idx = index">
          <ng-container
            *ngTemplateOutlet="
              (getValue(showOptionsAsSlide) | async) &&
              (hasChildValue(item.slideOptions) | async)
                ? slidingItemRef
                : nonSlidingItemRef;
              context: { item: item, index: idx }
            "
          ></ng-container>
        </ng-container>
      </ion-reorder-group>
    </ion-list>

    <!-- Empty List Handler -->
    <ng-container
      *ngIf="
        !filteredItems ||
        (filteredItems &&
          filteredItems.length === 0 &&
          (getValue(showEmpty) | async))
      "
    >
      <ng-container *ngTemplateOutlet="defaultEmptyListRef"></ng-container>
    </ng-container>
  </div>
</ng-template>

<!-- Sliding Item Ref -->
<ng-template #slidingItemRef let-item="item" let-index="index">
  <ion-item-sliding
    #slide
    [xColor]="getValue(color) | async"
    [isForeground]="getValue(foregroundColor) | async"
  >
    <!-- Item It Self Clickable -->
    <ion-item
      class="item slidable"
      xStopClickPropagation
      [button]="clickableItems"
      [xColor]="getValue(color) | async"
      [lines]="getIonItemLine() | async"
      [class.mobile]="isMobileUi$ | async"
      (click)="handleActionItemSelected(item)"
      [disabled]="getValue(item.disabled) | async"
      [class.ion-hide]="getValue(item.hide) | async"
      [class.in-card]="getValue(wrapWithCard) | async"
      [detail]="getValue(item.showDetailedIcon) | async"
      [isForeground]="getValue(foregroundColor) | async"
    >
      <ng-container
        *ngTemplateOutlet="itemHolderRef; context: { item: item, index: index }"
      >
      </ng-container>

      <!-- Itemp ReOrdering -->
      <ion-reorder slot="end" *ngIf="enableReordering"></ion-reorder>

      <!-- Menu Button -->
      <ion-button
        slot="end"
        class="x-menu"
        [xColor]="'inherit'"
        xStopClickPropagation
        [class.button-solid]="false"
        [disabled]="getValue(item.disabled) | async"
        *ngIf="
          (getValue(showItemOptions) | async) &&
          (getValue(showOptionsAsMenu) | async) &&
          (hasChildValue(item.slideOptions) | async)
        "
        (click)="handleMoreButtonClicked($event, slide, item)"
      >
        <x-icon [name]="'menu_vertical'"></x-icon>
      </ion-button>
    </ion-item>

    <!-- Item End Menus -->
    <ion-item-options
      side="end"
      *ngIf="
        (getValue(showItemOptions) | async) &&
        (hasChildValue(item.slideOptions) | async)
      "
    >
      <ion-item-option
        xStopClickPropagation
        [slot]="getOptionSlot(option) | async"
        [color]="getValue(option.color) | async"
        [matTooltip]="getResourceValue(option.title) | async"
        *ngFor="let option of getEndSlides(item)"
        [disabled]="
          (getValue(item.disabled) | async) ||
          (getValue(option.disabled) | async)
        "
        (click)="handleActionOptionSelected(slide, option.id, item)"
      >
        <ng-container
          *ngTemplateOutlet="
            slidingOptionTemplate
              ? slidingOptionTemplate
              : defaultSlidingOptionRef;
            context: { $implicit: option }
          "
        ></ng-container>
      </ion-item-option>
    </ion-item-options>

    <!-- Item Start Menus -->
    <ion-item-options
      side="start"
      *ngIf="
        (getValue(showItemOptions) | async) &&
        (hasChildValue(item.slideOptions) | async)
      "
    >
      <ion-item-option
        xStopClickPropagation
        [slot]="getOptionSlot(option) | async"
        [color]="getValue(option.color) | async"
        [matTooltip]="getResourceValue(option.title) | async"
        *ngFor="let option of getStartSlides(item)"
        [disabled]="
          (getValue(item.disabled) | async) ||
          (getValue(option.disabled) | async)
        "
        (click)="handleActionOptionSelected(slide, option.id, item)"
      >
        <ng-container
          *ngTemplateOutlet="
            slidingOptionTemplate
              ? slidingOptionTemplate
              : defaultSlidingOptionRef;
            context: { $implicit: option }
          "
        ></ng-container>
      </ion-item-option>
    </ion-item-options>
  </ion-item-sliding>
</ng-template>

<!-- Non Sliding Item Ref -->
<ng-template #nonSlidingItemRef let-item="item" let-index="index">
  <!-- Item It Self -->
  <ion-item
    xStopClickPropagation
    class="item not-slideable"
    [button]="clickableItems"
    [xColor]="getValue(color) | async"
    [lines]="getIonItemLine() | async"
    [class.mobile]="isMobileUi$ | async"
    (click)="handleActionItemSelected(item)"
    [disabled]="getValue(item.disabled) | async"
    [class.ion-hide]="getValue(item.hide) | async"
    [class.in-card]="getValue(wrapWithCard) | async"
    [isForeground]="getValue(foregroundColor) | async"
    [detail]="getValue(item.showDetailedIcon) | async"
  >
    <ng-container
      *ngTemplateOutlet="itemHolderRef; context: { item: item, index: index }"
    >
    </ng-container>

    <!-- Menu Button -->
    <ion-button
      slot="end"
      class="x-menu"
      [xColor]="'inherit'"
      xStopClickPropagation
      [class.button-solid]="false"
      [disabled]="getValue(item.disabled) | async"
      *ngIf="
        (getValue(showItemOptions) | async) &&
        (getValue(showOptionsAsMenu) | async) &&
        (hasChildValue(item.slideOptions) | async)
      "
      (click)="handleMoreButtonClicked($event, null, item)"
    >
      <x-icon [name]="'menu_vertical'"></x-icon>
    </ion-button>

    <!-- Item Menus -->
    <ion-buttons
      slot="end"
      *ngIf="
        (getValue(showItemOptions) | async) &&
        !(getValue(showOptionsAsMenu) | async) &&
        (hasChildValue(item.slideOptions) | async)
      "
    >
      <!-- Start Options -->
      <ion-button
        xStopClickPropagation
        [slot]="getOptionSlot(option) | async"
        [color]="getValue(option.color) | async"
        [disabled]="
          (getValue(item.disabled) | async) ||
          (getValue(option.disabled) | async)
        "
        *ngFor="let option of getStartSlides(item)"
        [matTooltip]="getResourceValue(option.title) | async"
        (click)="handleActionOptionSelected(null, option.id, item)"
      >
        <ng-container
          *ngTemplateOutlet="
            nonSlidingOptionTemplate
              ? nonSlidingOptionTemplate
              : defaultNonSlidingOptionRef;
            context: { $implicit: option }
          "
        ></ng-container>
      </ion-button>

      <!-- End Options -->
      <ion-button
        xStopClickPropagation
        [slot]="getOptionSlot(option) | async"
        [color]="getValue(option.color) | async"
        [disabled]="
          (getValue(item.disabled) | async) ||
          (getValue(option.disabled) | async)
        "
        *ngFor="let option of getEndSlides(item)"
        [matTooltip]="getResourceValue(option.title) | async"
        (click)="handleActionOptionSelected(null, option.id, item)"
      >
        <ng-container
          *ngTemplateOutlet="
            nonSlidingOptionTemplate
              ? nonSlidingOptionTemplate
              : defaultNonSlidingOptionRef;
            context: { $implicit: option }
          "
        ></ng-container>
      </ion-button>
    </ion-buttons>

    <!-- Itemp ReOrdering -->
    <ion-reorder
      slot="end"
      *ngIf="
        (getValue(enableReordering) | async) &&
        (countChildsValue(items) | async) ===
          (countChildsValue(filteredItems) | async)
      "
    ></ion-reorder>
  </ion-item>
</ng-template>

<!-- Item Holder Template -->
<ng-template #itemHolderRef let-item="item" let-index="index">
  <!-- Itemp Template -->
  <ng-container
    *ngTemplateOutlet="
      itemTemplate ? itemTemplate : defaultItemRef;
      context: { item: item, index: index }
    "
  >
  </ng-container>

  <!-- Itemp Checkbox -->
  <ion-checkbox
    slot="start"
    class="x-check"
    [checked]="item.isSelected"
    *ngIf="getValue(selectableItems) | async"
    [disabled]="getValue(item.disabled) | async"
  ></ion-checkbox>
</ng-template>

<!-- Default Sliding Option Ref -->
<ng-template #defaultSlidingOptionRef let-option>
  <ion-item lines="none">
    <ion-label
      *ngIf="
        (notEmptyValue(option.title) | async) &&
        (getOptionOnlyIcon(option) | async) === false
      "
      >{{ getResourceValue(option.title) | async }}</ion-label
    >
    <x-icon
      *ngIf="option.icon"
      [foregroundColor]="false"
      [name]="getValue(option.icon) | async"
      [color]="getValue(option.color) | async"
    ></x-icon>
  </ion-item>
</ng-template>

<!-- Default Non Sliding Option Ref -->
<ng-template #defaultNonSlidingOptionRef let-option>
  <ng-container *ngIf="!(getOptionOnlyIcon(option) | async)">
    {{ getResourceValue(option.title) | async }}
  </ng-container>
  <x-icon
    *ngIf="option.icon"
    [foregroundColor]="true"
    [name]="getValue(option.icon) | async"
    [color]="getValue(option.color) | async"
  ></x-icon>
</ng-template>

<!-- Default Item Ref -->
<ng-template #defaultItemRef let-item="item" let-index="index">
  <h5 style="width: 100%">{{ item | json }}</h5>
</ng-template>

<!-- Default Empty Content -->
<ng-template #defaultEmptyListRef>
  <x-empty
    (refresh)="emptyRefresh.emit($event)"
    [color]="getValue(emptyColor) | async"
    [message]="getResourceValue(emptyMessage) | async"
    [refreshIcon]="getValue(emptyRefreshIcon) | async"
    [messageColor]="getValue(emptyMessageColor) | async"
    [hasRefresh]="getValue(emptyHasRefresh) | async"
    [foregroundRefreshButtonColor]="
      getValue(emptyForegroundRefreshButtonColor) | async
    "
    [foregroundColor]="getValue(emptyForegroundColor) | async"
    [refreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
    [foregroundMessageColor]="getValue(emptyForegroundMessageColor) | async"
  ></x-empty>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-list\x-list.component.scss

ion-item-option > ion-item {
  --background: transparent !important;
}

ion-list,
ion-item.item,
ion-item-divider {
  --background: transparent !important;
}

ion-item-divider {
  --color: var(--ion-color-dark-shade);
}

ion-item.item.mobile {
  --ion-safe-area-end: 0px;

  --padding-start: 8px;
  --inner-padding-start: 0px;

  --padding-end: 8px;
  --inner-padding-end: 0px;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
}

.x-menu {
  z-index: 10;
}

.x-check {
  z-index: 1;
}

x-card {
  width: inherit;
  height: inherit;
}

:host ::ng-deep .button-solid {
  --color: unset !important;
  --background: unset !important;
  --box-shadow: unset !important;
}

.x-list-toolbar {
  &.sticky {
    top: 0;
    position: sticky;
    position: -webkit-sticky;
    z-index: 200;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-list\x-list.component.ts

import {
  nameof,
  toArray,
  hasChild,
  isFunction,
  removeItem,
  XOneOrManyType,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XListItem,
  XListAction,
  XListActionModel,
  XListItemSlideOption,
  XListContainerComponent,
} from './x-list.typings';
import {
  XActionBarItem,
  XActionBarAction,
  XActionBarActionModel,
  DefaultActionBarItemIds,
  defaultActionBarAddItem,
  defaultActionBarRemoveItem,
  XActionBarActionFiredModel,
  defaultActionBarSelectAllItem,
  defaultActionBarDeSelectAllItem,
  defaultActionBarInverseSelectionItem,
} from '../x-action-bar/x-action-bar.typings';
import { map, concatMap } from 'rxjs/operators';
import { IonItemSliding } from '@ionic/angular';
import {
  XPopoverResult,
  XPopoverOption,
  XPopoverComponentProps,
} from '../dialogs/x-popover/x-popover.typings';
import { moveItemInArray } from '@angular/cdk/drag-drop';
import { XButtonType } from '../x-button/x-button.typings';
import { Subject, Subscription, BehaviorSubject, of } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { toPopoverOptions } from '../dialogs/x-popover/x-popover.tools';
import { XSearchBarAction } from '../x-search-bar/x-search-bar.typings';
import { XPopoverComponent } from '../dialogs/x-popover/x-popover.component';

@Component({
  // tslint:disable-next-line: component-selector
  selector: 'x-list',
  templateUrl: './x-list.component.html',
  styleUrls: ['./x-list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XListComponent extends XListContainerComponent {
  //
  //#region Props ...
  //
  private SELECTED_ITEMS: XListItem<any>[] = [];
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  private ACTION_BAR_ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  private HIDE_ADD = false;
  private DISABLE_ADD = false;
  private addDisablerSubject = new BehaviorSubject<boolean>(false);

  //
  private HIDE_REMOVE = false;
  private DISABLE_REMOVE = false;
  private removeDisablerSubject = new BehaviorSubject<boolean>(true);

  //
  private HIDE_SELECT_ALL = false;
  private DISABLE_SELECT_ALL = false;
  private selectAllDisablerSubject = new BehaviorSubject<boolean>(true);

  //
  private HIDE_DESELECT_ALL = false;
  private DISABLE_DESELECT_ALL = false;
  private deSelectAllDisablerSubject = new BehaviorSubject<boolean>(true);

  //
  private HIDE_INVERSE_SELECTION = false;
  private DISABLE_INVERSE_SELECTION = false;
  private inverseSelectionDisablerSubject = new BehaviorSubject<boolean>(true);

  //
  readonly ButtonTypes = Object.assign({}, XButtonType);

  //
  filteredItems: XListItem<any>[] = [];

  //
  get count() {
    return this.items ? this.getValue(this.items) : of(0);
  }

  get filteredItemsCount() {
    return this.filteredItems ? this.filteredItems.length : 0;
  }

  get selectedItems() {
    return this.SELECTED_ITEMS;
  }
  //#endregion

  //
  //#region LifeCycle ...
  async onInit() {
    super.onInit();

    //
    // await this.handleStartup();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XListComponent>('actionProvider')
    );
    const isActionBarActionProviderChanged = changeKeys.includes(
      nameof<XListComponent>('actionBarActionProvider')
    );
    if (isActionProviderChanged || isActionBarActionProviderChanged) {
      this.registerActionProvider();
    }

    //
    const isItemsChanged = changeKeys.includes(nameof<XListComponent>('items'));
    if (isItemsChanged) {
      //
      await this.handleStartup();
      this.itemsChanged.emit();
    }

    //
    const isActionBarActionsChanged = changeKeys.includes(
      nameof<XListComponent>('actionBarActions')
    );
    const isActionBarHideActionsChanged = changeKeys.includes(
      nameof<XListComponent>('actionBarHideActions')
    );
    if (isActionBarActionsChanged || isActionBarHideActionsChanged) {
      await this.prepareActionBarActions();
    }
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  getStartSlides(item: XListItem<any>) {
    return item && item.slideOptions
      ? item.slideOptions.filter((opt) => opt.slot === 'start')
      : null;
  }

  getEndSlides(item: XListItem<any>) {
    return item && item.slideOptions
      ? item.slideOptions.filter((opt) => opt.slot === 'end')
      : null;
  }

  getOptionSlot(option: XListItemSlideOption) {
    return this.getOptionOnlyIcon(option).pipe(
      map((onlyIcon) => {
        return onlyIcon ? 'icon-only' : '';
      })
    );
  }

  getOptionIconSlot(option: XListItemSlideOption) {
    return this.getOptionOnlyIcon(option).pipe(
      concatMap((onlyIcon) => {
        return this.getOptionSlot(option).pipe(
          map((slot) => {
            return onlyIcon ? '' : slot;
          })
        );
      })
    );
  }

  getNonSlidingOptionIconSlot(option: XListItemSlideOption) {
    return this.getOptionOnlyIcon(option).pipe(
      map((onlyIcon) => {
        return onlyIcon ? 'icon-only' : 'end';
      })
    );
  }

  getOptionOnlyIcon(option: XListItemSlideOption) {
    return this.getValue(!isNullOrUndefined(option) ? option.onlyIcon : false);
  }

  getIonItemLine() {
    return this.getValue(this.showDivider).pipe(
      map((showDivider) => (showDivider ? 'full' : 'none'))
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  handleActionItemSelected(item: XListItem<any>) {
    //
    if (this.selectableItems) {
      item.isSelected = !item.isSelected;
      this.issueSelectionChanged();
    }

    //
    this.itemSelected.emit(item);
  }

  handleDoReorderItems(ev: CustomEvent | any) {
    //
    const to = ev.detail.to;
    // tslint:disable-next-line:no-shadowed-variable
    const from = ev.detail.from;
    moveItemInArray(this.filteredItems, from, to);

    //
    this.orderChanged.emit();
    ev.detail.complete();
  }

  async handleMoreButtonClicked(
    event: Event,
    slide: IonItemSliding,
    item: XListItem<any>
  ) {
    //
    if (!isNullOrUndefined(slide)) {
      await slide.closeOpened();
    }

    //
    // Disable Only Icons for Menu Options ...
    const options: XPopoverOption[] = toArray(toPopoverOptions(item));
    options.forEach((o) => {
      //
      o.onlyIcon = false;

      //
      o.slot = 'end';

      //
      o.handler = undefined;
    });

    //
    // Prepare PopOver Component Props ...
    const componentProps: XPopoverComponentProps = {
      options,
    };

    //
    // Present Context Menu Options ...
    const popOver = await this.managerService.dialogService.presentPopover({
      event,
      componentProps,
      translucent: true,
      showBackdrop: true,
      component: XPopoverComponent,
    });

    //
    // Extract Data from PopOver ...
    const { data } = await popOver.onWillDismiss();
    if (!data) {
      return;
    }

    //
    const result = data as XPopoverResult;
    if (
      isNullOrUndefined(result.option) ||
      isNullOrEmptyString(result.option.id)
    ) {
      return;
    }

    //
    this.handleActionOptionSelected(slide, result.option.id, item);
  }

  async handleActionOptionSelected(
    slide: IonItemSliding,
    id: string,
    item: XListItem<any>
  ) {
    //
    if (isNullOrEmptyString(id) || isNullOrUndefined(item)) {
      return;
    }

    //
    // if (!isNullOrUndefined)
    const action = item.slideOptions.find((i) => i.id === id);
    if (
      !isNullOrUndefined(action) &&
      !isNullOrUndefined(action.handler) &&
      isFunction(action.handler)
    ) {
      await this.getValueAsync(action.handler());
    }

    //
    if (this.closeOptionsAfterSelect && !isNullOrUndefined(slide)) {
      //
      slide.closeOpened().then(() => {
        //
        this.optionSelected.emit({
          id,
          data: item,
        });
      });
    } else {
      //
      this.optionSelected.emit({
        id,
        data: item,
      });
    }
  }

  async handleActionRemoveItems() {
    //
    if (!this.hasChild(this.SELECTED_ITEMS)) {
      return;
    }

    //
    const items = this.SELECTED_ITEMS.map((i) => i.data);

    //
    const confirmRemove = await this.getValueAsync(this.confirmRemove);
    if (confirmRemove) {
      await this.managerService.dialogService.presentAreYouSureYesNoDialog(
        () => {
          this.removeItems.emit({
            items,
          });
        }
      );
    } else {
      this.removeItems.emit({
        items,
      });
    }
  }

  async handleActionBarActionFired(model: XActionBarActionFiredModel) {
    //
    if (isNullOrUndefined(model) || isNullOrEmptyString(model.action)) {
      return;
    }

    //
    switch (model.action) {
      //
      case DefaultActionBarItemIds.SelectAll:
        await this.selectAll();
        break;

      //
      case DefaultActionBarItemIds.DeSelectAll:
        await this.deselectAll();
        break;

      //
      case DefaultActionBarItemIds.InverseSelection:
        await this.inverseSelection();
        break;

      //
      case DefaultActionBarItemIds.Remove:
        //
        await this.handleActionRemoveItems();
        break;
    }

    //
    this.actionBarActionFired.emit(model);
  }
  //#endregion

  //
  //#region Actions ...
  async selectAll() {
    //
    this.filteredItems.forEach((item) => {
      if (!item.disabled) {
        //
        item.isSelected = true;
        this.changeDetector.markForCheck();
      }
    });

    //
    this.issueSelectionChanged();
    this.handleChangeDetection();
  }

  async deselectAll() {
    //
    this.filteredItems.forEach((item) => {
      if (!item.disabled) {
        //
        item.isSelected = false;
        this.changeDetector.markForCheck();
      }
    });

    //
    this.issueSelectionChanged();
    this.handleChangeDetection();
  }

  async inverseSelection() {
    //
    this.filteredItems.forEach((item) => {
      if (!item.disabled) {
        //
        item.isSelected = !item.isSelected;
        this.changeDetector.markForCheck();
      }
    });

    //
    this.issueSelectionChanged();
    this.handleChangeDetection();
  }

  async clearFilter(fill = false) {
    this.filteredItems = [];

    //
    if (fill) {
      //
      const items = await this.getValueAsync(this.items);
      this.filteredItems = items;
    }

    //
    this.handleChangeDetection();
  }

  async filterItems(query: string) {
    //
    this.searchBarQueryChange.emit(query);

    //
    await this.clearFilter();
    const items = await this.getValueAsync(this.items);

    //
    if (isNullOrEmptyString(query)) {
      //
      this.filteredItems = items;
      this.handleChangeDetection();
      return;
    }

    //
    const result = await this.getValueAsync(
      this.filteringHandler(query, items)
    );
    this.filteredItems = result;

    //
    this.handleChangeDetection();
  }
  //#endregion

  //
  //#region Private ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XListActionModel>();
    }

    //
    if (!this.actionBarActionProvider) {
      this.actionBarActionProvider = new Subject<XActionBarActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XListAction.Add:
            //
            const newItem = model.payload as XListItem<any>;
            if (newItem) {
              //
              const items = await this.getArrayValueAsync(this.items);
              this.items = [...items, newItem];
              this.itemsChanged.emit();
              this.handleChangeDetection();
            }
            break;

          //
          case XListAction.Set:
            //
            const newItems = model.payload as XListItem<any>[];
            if (newItems) {
              //
              this.items = [...newItems];
              this.itemsChanged.emit();
              this.handleChangeDetection();
            }
            break;

          //
          case XListAction.Remove:
            //
            const mustRemoveItem = model.payload as XListItem<any>;
            if (mustRemoveItem) {
              //
              const items = await this.getArrayValueAsync(this.items);
              this.items = [...removeItem(items, mustRemoveItem)];
              this.itemsChanged.emit();
              this.handleChangeDetection();
            }
            break;

          //
          case XListAction.SelectAll:
            await this.selectAll();
            break;

          //
          case XListAction.DeselectAll:
            await this.deselectAll();
            break;

          //
          case XListAction.InverseSelection:
            await this.inverseSelection();
            break;

          //
          case XListAction.Refresh:
            this.handleChangeDetection();
            break;
        }
      });

    //
    // Handle ActionBar Provider ...
    this.ACTION_BAR_ACTION_PROVIDER_SUBSCRIPTION = this.actionBarActionProvider
      .asObservable()
      .subscribe((model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XActionBarAction.EnableAction:
            //
            switch (model.id) {
              //
              case DefaultActionBarItemIds.Add:
                //
                this.DISABLE_ADD = false;
                this.addDisablerSubject.next(false);
                break;

              //
              case DefaultActionBarItemIds.Remove:
                //
                this.DISABLE_REMOVE = false;
                this.removeDisablerSubject.next(false);
                break;

              //
              case DefaultActionBarItemIds.SelectAll:
                //
                this.DISABLE_SELECT_ALL = false;
                this.selectAllDisablerSubject.next(false);
                break;

              //
              case DefaultActionBarItemIds.DeSelectAll:
                //
                this.DISABLE_DESELECT_ALL = false;
                this.deSelectAllDisablerSubject.next(false);
                break;

              //
              case DefaultActionBarItemIds.InverseSelection:
                //
                this.DISABLE_INVERSE_SELECTION = false;
                this.inverseSelectionDisablerSubject.next(false);
                break;
            }
            break;

          //
          case XActionBarAction.DisbaleAction:
            //
            switch (model.id) {
              //
              case DefaultActionBarItemIds.Add:
                //
                this.DISABLE_ADD = true;
                this.addDisablerSubject.next(true);
                break;

              //
              case DefaultActionBarItemIds.Remove:
                //
                this.DISABLE_REMOVE = true;
                this.removeDisablerSubject.next(true);
                break;

              //
              case DefaultActionBarItemIds.SelectAll:
                //
                this.DISABLE_SELECT_ALL = true;
                this.selectAllDisablerSubject.next(true);
                break;

              //
              case DefaultActionBarItemIds.DeSelectAll:
                //
                this.DISABLE_DESELECT_ALL = true;
                this.deSelectAllDisablerSubject.next(true);
                break;

              //
              case DefaultActionBarItemIds.InverseSelection:
                //
                this.DISABLE_INVERSE_SELECTION = true;
                this.inverseSelectionDisablerSubject.next(true);
                break;
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (this.ACTION_PROVIDER_SUBSCRIPTION) {
      //
      this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
      this.ACTION_PROVIDER_SUBSCRIPTION = null;
    }

    //
    if (this.ACTION_BAR_ACTION_PROVIDER_SUBSCRIPTION) {
      //
      this.ACTION_BAR_ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
      this.ACTION_BAR_ACTION_PROVIDER_SUBSCRIPTION = null;
    }
  }

  private async handleStartup() {
    //
    await this.prepareActionBarActions();

    //
    this.SELECTED_ITEMS = [];

    //
    const items = await this.getValueAsync(
      isNullOrUndefined(this.items) ? [] : this.items
    );
    if (!hasChild(items)) {
      this.filteredItems = [];
    } else {
      this.filteredItems = items;
    }

    //
    this.issueSelectionChanged();
    // this.handleChangeDetection();
  }

  private handleChangeDetection() {
    //
    this.actionBarActionProvider.next({
      action: XActionBarAction.Refresh,
    });

    //
    this.searchBarActionProvider.next({
      action: XSearchBarAction.Refresh,
    });

    //
    this.detectChanges();
  }

  private issueSelectionChanged() {
    //
    this.SELECTED_ITEMS = [];
    if (!this.selectableItems) {
      return;
    }

    //
    const selectedItems = this.filteredItems.filter((item) => item.isSelected);
    this.SELECTED_ITEMS = [...selectedItems];

    //
    this.selectionChanged.emit(selectedItems);
    this.prepareActionBarActionsSubjects(selectedItems);
  }

  //
  //#region ActionBar Preparations ...
  private async prepareActionBarActions() {
    //
    //#region Prepare Default Actions ...
    const hideActions = await this.getArrayValueAsync(
      this.actionBarHideActions
    );

    //
    let idx = 0;
    const defaultActions: XOneOrManyType<XActionBarItem> = [];

    //
    // Remove ...
    const removeId = DefaultActionBarItemIds.Remove.toString();
    if (!hideActions.includes(removeId)) {
      //
      defaultActions.push({
        //
        ...defaultActionBarRemoveItem,

        //
        index: idx,
        disabled: this.removeDisablerSubject.asObservable(),
      });

      //
      idx++;
    }

    //
    // Add ...
    const addId = DefaultActionBarItemIds.Add.toString();
    if (!hideActions.includes(addId)) {
      //
      defaultActions.push({
        //
        ...defaultActionBarAddItem,

        //
        index: idx,
        disabled: this.addDisablerSubject.asObservable(),
      });

      //
      idx++;
    }

    //
    // Select All ...
    const selectAllId = DefaultActionBarItemIds.SelectAll.toString();
    if (!hideActions.includes(selectAllId)) {
      //
      defaultActions.push({
        //
        ...defaultActionBarSelectAllItem,

        //
        index: idx,
        disabled: this.selectAllDisablerSubject.asObservable(),
      });

      //
      idx++;
    }

    //
    // Inverse Selection ...
    const inverseSelectionId =
      DefaultActionBarItemIds.InverseSelection.toString();
    if (!hideActions.includes(inverseSelectionId)) {
      //
      defaultActions.push({
        //
        ...defaultActionBarInverseSelectionItem,

        //
        index: idx,
        disabled: this.inverseSelectionDisablerSubject.asObservable(),
      });

      //
      idx++;
    }

    //
    // De Select All ...
    const deselectAllId = DefaultActionBarItemIds.DeSelectAll.toString();
    if (!hideActions.includes(deselectAllId)) {
      //
      defaultActions.push({
        //
        ...defaultActionBarDeSelectAllItem,

        //
        index: idx,
        disabled: this.deSelectAllDisablerSubject.asObservable(),
      });

      //
      idx++;
    }
    //#endregion

    //
    const useDefaultActions = await this.getValueAsync(this.useDefaultActions);
    if (!this.actionBarActions && !!useDefaultActions) {
      //
      this.actionBarActions = defaultActions;
      return;
    }

    //
    const actions = await this.getArrayValueAsync(this.actionBarActions);
    if (!hasChild(actions) && !!useDefaultActions) {
      //
      this.actionBarActions = defaultActions;
      return;
    }

    //
    this.actionBarActions = actions;
  }

  private async prepareActionBarActionsSubjects(items: XListItem<File>[]) {
    //
    const listItems = await this.getValueAsync(this.items);

    //
    // Check Select All ...
    if (!hasChild(listItems)) {
      //
      // Remove ...
      if (!this.DISABLE_REMOVE) {
        this.removeDisablerSubject.next(true);
      }

      //
      // Select All ...
      if (!this.DISABLE_SELECT_ALL) {
        this.selectAllDisablerSubject.next(true);
      }

      //
      // Deselect All ...
      if (!this.DISABLE_DESELECT_ALL) {
        this.deSelectAllDisablerSubject.next(true);
      }

      //
      // Inverse Selection ...
      if (!this.DISABLE_INVERSE_SELECTION) {
        this.inverseSelectionDisablerSubject.next(true);
      }
    } else {
      //
      // DeSelectAll ...
      if (!hasChild(items)) {
        //
        // Remove ...
        if (!this.DISABLE_REMOVE) {
          this.removeDisablerSubject.next(true);
        }

        //
        // Deselect All ...
        if (!this.DISABLE_DESELECT_ALL) {
          this.deSelectAllDisablerSubject.next(true);
        }

        //
        // Inverse Selection ...
        if (!this.DISABLE_INVERSE_SELECTION) {
          this.inverseSelectionDisablerSubject.next(true);
        }
      } else {
        //
        // Remove ...
        if (!this.DISABLE_REMOVE) {
          this.removeDisablerSubject.next(false);
        }

        //
        // Deselect All ...
        if (!this.DISABLE_DESELECT_ALL) {
          this.deSelectAllDisablerSubject.next(false);
        }

        //
        // Inverse Selection ...
        if (!this.DISABLE_INVERSE_SELECTION) {
          this.inverseSelectionDisablerSubject.next(false);
        }
      }

      //
      if (listItems.length === items.length) {
        //
        // Select All ...
        if (!this.DISABLE_SELECT_ALL) {
          this.selectAllDisablerSubject.next(true);
        }

        //
        // Inverse Selection ...
        if (!this.DISABLE_INVERSE_SELECTION) {
          this.inverseSelectionDisablerSubject.next(true);
        }
      } else {
        //
        // Select All ...
        if (!this.DISABLE_SELECT_ALL) {
          this.selectAllDisablerSubject.next(false);
        }
      }
    }

    //
    this.handleChangeDetection();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-list\x-list.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XListComponent } from './x-list.component';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XEmptyModule } from '../x-empty/x-empty.module';
import { XActionBarModule } from '../x-action-bar/x-action-bar.module';
import { XSearchBarModule } from '../x-search-bar/x-search-bar.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XListComponent],
  imports: [
    XIconModule,
    XCardModule,
    XEmptyModule,
    CommonModule,
    XActionBarModule,
    XSearchBarModule,
    XBaseComponentModule,
  ],
  exports: [
    XIconModule,
    XCardModule,
    XEmptyModule,
    CommonModule,
    XListComponent,
    XActionBarModule,
    XSearchBarModule,
    XBaseComponentModule,
  ],
})
export class XListModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-list\x-list.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  Injectable,
  ElementRef,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  isFile,
  hasChild,
  toJsonString,
  XStandardType,
  XOneOrManyType,
  toNormalString,
  XColorIdentifier,
  fileToJsonString,
  XResourceIdentifier,
  XBaseRangeRequestDto,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XBaseComponentContainer,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../classes/i-x-base.container';
import {
  XCardEmptyActionSearchBarContainer,
  IXCardEmptyActionSearchBarContainer,
  XCardEmptyActionSearchBarContainerComponent,
} from '../classes/i-x-card-empty.container';
import {
  XActionBarItem,
  XActionBarActionModel,
  XActionBarActionIdentifier,
  XActionBarActionFiredModel,
} from '../x-action-bar/x-action-bar.typings';
import {
  XSearchBarActionModel,
  XSearchBarTypeIdentifier,
  XSearchBarEventIdentifier,
} from '../x-search-bar/x-search-bar.typings';
import { concatMap, map } from 'rxjs/operators';
import { ViewportRuler } from '@angular/cdk/overlay';
import { Subject, forkJoin, Observable } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XCardActionModel } from '../x-card/x-card.typings';
import { XEmptyActionModel } from '../x-empty/x-empty.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Definitions ...
export type XListItemOptionSlot = 'end' | 'start';

export interface XListItemSlideOption {
  id: string;
  icon?: XStandardType<XIconIdentifier>;
  title?: XStandardType<XResourceIdentifier>;
  color?: XStandardType<XColorIdentifier>;
  onlyIcon?: XStandardType<boolean>;
  slot: XStandardType<XListItemOptionSlot>;
  handler?: (info?: any) => XStandardType<void>;
  disabled?: XStandardType<boolean>;
}

export interface XListItem<T> {
  data: T;
  hide?: XStandardType<boolean>;
  disabled?: XStandardType<boolean>;
  isSelected?: boolean;
  href?: XOneOrManyType<string>;
  showDetailedIcon?: XStandardType<boolean>;
  slideOptions?: XListItemSlideOption[];
}

export interface XListItemOptionSelectedEvent {
  id: string;
  data: any;
}

export enum XListAction {
  //
  Add = 'add',
  Set = 'set',
  Remove = 'remove',

  //
  SelectAll = 'select_all',
  DeselectAll = 'deselect_all',
  InverseSelection = 'inverse_selection',

  //
  Refresh = 'refresh',
}

export type XListActionIdentifier = XListAction | string;

export interface XListActionModel
  extends XBaseActionModel<XListActionIdentifier> {}

export type XListFilteringHandler = (
  query: string,
  source: XListItem<any>[]
) => XStandardType<XListItem<any>[]>;

export function itemToJson(item: any) {
  return isFile(item) ? fileToJsonString(item) : toJsonString(item);
}

export const defaultListFilteringHandler: XListFilteringHandler = (
  query: string,
  items: XListItem<any>[]
) => {
  //
  if (!query || !hasChild(items)) {
    return [];
  }

  //
  const result = items.filter((i) =>
    toNormalString(itemToJson(i.data)).includes(toNormalString(query))
  );

  //
  return result;
};
//#endregion

//
//#region Container ...
export interface IXListContainer extends IXCardEmptyActionSearchBarContainer {
  //
  actionProvider: Subject<XListActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  actionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  >;

  //
  useDefaultActions: XStandardType<boolean>;

  //
  showToolbar: XStandardType<boolean>;
  stickyToolbar: XStandardType<boolean>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  enableReordering: XStandardType<boolean>;
  showDivider: XStandardType<boolean>;
  showOptionsAsSlide: XStandardType<boolean>;
  closeOptionsAfterSelect: XStandardType<boolean>;
  showOptionsAsMenu: XStandardType<boolean>;

  //
  selectableItems: XStandardType<boolean>;
  clickableItems: XStandardType<boolean>;
  itemTemplate: TemplateRef<any>;
  slidingOptionTemplate: TemplateRef<any>;
  nonSlidingOptionTemplate: TemplateRef<any>;

  //
  itemSelected: EventEmitter<XListItem<any>>;
  optionSelected: EventEmitter<XListItemOptionSelectedEvent>;
  confirmRemove: XStandardType<boolean>;
  removeItems: EventEmitter<XBaseRangeRequestDto<any>>;

  //
  selectionChanged: EventEmitter<XListItem<any>[]>;
  orderChanged: EventEmitter<void>;
  items: XStandardType<XListItem<any>[]>;
  showItemOptions: XStandardType<boolean>;
  itemsChanged: EventEmitter<void>;
  filteringHandler: XListFilteringHandler;
}

@Injectable({
  providedIn: 'root',
})
export class XListContainer
  extends XCardEmptyActionSearchBarContainer
  implements Readonly<IXListContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XListActionModel> =
    new Subject<XListActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly actionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  >;

  readonly useDefaultActions: XStandardType<boolean> = true;

  readonly showToolbar: XStandardType<boolean> = false;
  readonly stickyToolbar: XStandardType<boolean> = true;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly enableReordering: XStandardType<boolean> = false;
  readonly showDivider: XStandardType<boolean> = true;
  readonly showOptionsAsSlide: XStandardType<boolean> = false;
  readonly closeOptionsAfterSelect: XStandardType<boolean> = true;
  readonly showOptionsAsMenu: XStandardType<boolean> = false;

  //
  readonly selectableItems: XStandardType<boolean> = false;
  readonly clickableItems: XStandardType<boolean> = false;
  readonly itemTemplate: TemplateRef<any>;
  readonly slidingOptionTemplate: TemplateRef<any>;
  readonly nonSlidingOptionTemplate: TemplateRef<any>;

  //
  readonly itemSelected: EventEmitter<XListItem<any>> = new EventEmitter<
    XListItem<any>
  >();
  readonly optionSelected: EventEmitter<XListItemOptionSelectedEvent> =
    new EventEmitter<XListItemOptionSelectedEvent>();
  readonly confirmRemove: XStandardType<boolean> = true;
  readonly removeItems: EventEmitter<XBaseRangeRequestDto<any>> =
    new EventEmitter<XBaseRangeRequestDto<any>>();

  //
  readonly selectionChanged: EventEmitter<XListItem<any>[]> = new EventEmitter<
    XListItem<any>[]
  >();
  readonly orderChanged: EventEmitter<void> = new EventEmitter<void>();
  readonly items: XStandardType<XListItem<any>[]> = [];
  readonly showItemOptions: XStandardType<boolean> = true;
  readonly itemsChanged: EventEmitter<void> = new EventEmitter<void>();
  readonly filteringHandler: XListFilteringHandler =
    defaultListFilteringHandler;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XListContainerComponent
  extends XCardEmptyActionSearchBarContainerComponent
  implements IXListContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XListActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  actionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  > = this.propertyProvider.actionBarHideActions;

  @Input()
  useDefaultActions: XStandardType<boolean> =
    this.propertyProvider.useDefaultActions;

  @Input()
  showToolbar: XStandardType<boolean> = this.propertyProvider.showToolbar;

  @Input()
  stickyToolbar: XStandardType<boolean> = this.propertyProvider.stickyToolbar;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  enableReordering: XStandardType<boolean> =
    this.propertyProvider.enableReordering;

  @Input()
  showDivider: XStandardType<boolean> = this.propertyProvider.showDivider;

  @Input()
  showOptionsAsSlide: XStandardType<boolean> =
    this.propertyProvider.showOptionsAsSlide;

  @Input()
  closeOptionsAfterSelect: XStandardType<boolean> =
    this.propertyProvider.closeOptionsAfterSelect;

  @Input()
  showOptionsAsMenu: XStandardType<boolean> =
    this.propertyProvider.showOptionsAsMenu;

  @Input()
  selectableItems: XStandardType<boolean> =
    this.propertyProvider.selectableItems;

  @Input()
  clickableItems: XStandardType<boolean> = this.propertyProvider.clickableItems;

  @Input()
  itemTemplate: TemplateRef<any> = this.propertyProvider.itemTemplate;

  @Input()
  slidingOptionTemplate: TemplateRef<any> =
    this.propertyProvider.slidingOptionTemplate;

  @Input()
  nonSlidingOptionTemplate: TemplateRef<any> =
    this.propertyProvider.nonSlidingOptionTemplate;

  //
  @Output()
  itemSelected: EventEmitter<XListItem<any>> = new EventEmitter();

  @Output()
  optionSelected: EventEmitter<XListItemOptionSelectedEvent> = new EventEmitter();

  @Input()
  confirmRemove: XStandardType<boolean> = this.propertyProvider.confirmRemove;

  @Output()
  removeItems: EventEmitter<XBaseRangeRequestDto<any>> = new EventEmitter();

  //
  @Output()
  selectionChanged: EventEmitter<XListItem<any>[]> = new EventEmitter();

  @Output()
  orderChanged: EventEmitter<void> = new EventEmitter();

  //
  @Input()
  items: XStandardType<XListItem<any>[]> = this.propertyProvider.items;

  @Input()
  showItemOptions: XStandardType<boolean> =
    this.propertyProvider.showItemOptions;

  @Output()
  itemsChanged: EventEmitter<void> = new EventEmitter();

  //
  @Input()
  filteringHandler: XListFilteringHandler =
    this.propertyProvider.filteringHandler;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XListContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXListContainer
  ): Observable<IXListContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            actionBarHideActions: this.getValue(
              container
                ? container.actionBarHideActions
                : this.actionBarHideActions
            ),
            useDefaultActions: this.getValue(
              container ? container.useDefaultActions : this.useDefaultActions
            ),
            showToolbar: this.getValue(
              container ? container.showToolbar : this.showToolbar
            ),
            stickyToolbar: this.getValue(
              container ? container.stickyToolbar : this.stickyToolbar
            ),
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            enableReordering: this.getValue(
              container ? container.enableReordering : this.enableReordering
            ),
            showDivider: this.getValue(
              container ? container.showDivider : this.showDivider
            ),
            showOptionsAsSlide: this.getValue(
              container ? container.showOptionsAsSlide : this.showOptionsAsSlide
            ),
            closeOptionsAfterSelect: this.getValue(
              container
                ? container.closeOptionsAfterSelect
                : this.closeOptionsAfterSelect
            ),
            showOptionsAsMenu: this.getValue(
              container ? container.showOptionsAsMenu : this.showOptionsAsMenu
            ),
            selectableItems: this.getValue(
              container ? container.selectableItems : this.selectableItems
            ),
            clickableItems: this.getValue(
              container ? container.clickableItems : this.clickableItems
            ),
            itemTemplate: this.getValue(
              container ? container.itemTemplate : this.itemTemplate
            ),
            slidingOptionTemplate: this.getValue(
              container
                ? container.slidingOptionTemplate
                : this.slidingOptionTemplate
            ),
            nonSlidingOptionTemplate: this.getValue(
              container
                ? container.nonSlidingOptionTemplate
                : this.nonSlidingOptionTemplate
            ),
            confirmRemove: this.getValue(
              container ? container.confirmRemove : this.confirmRemove
            ),
            items: this.getValue(container ? container.items : this.items),
            showItemOptions: this.getValue(
              container ? container.showItemOptions : this.showItemOptions
            ),
            filteringHandler: this.getValue(
              container ? container.filteringHandler : this.filteringHandler
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            itemsChanged: this.itemsChanged,
            itemSelected: this.itemSelected,
            optionSelected: this.optionSelected,
            removeItems: this.removeItems,
            selectionChanged: this.selectionChanged,
            orderChanged: this.orderChanged,
          };
        })
      );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentListContainer = {
  [TKey in keyof IXListContainer as `list${Capitalize<
    string & TKey
  >}`]: IXListContainer[TKey];
};

export interface IXComponentListContainer
  extends TXComponentListContainer,
    IXBaseComponentContainer {
  showList: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentListContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentListContainer>
{
  //
  //#region Props ...
  //
  //#region Self ...
  //
  readonly showList: XStandardType<boolean> = true;

  //
  readonly listActionProvider: Subject<XListActionModel> =
    this.propertyProvider.actionProvider;
  readonly listCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly listActionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  > = this.propertyProvider.actionBarHideActions;
  readonly listUseDefaultActions: XStandardType<boolean> =
    this.propertyProvider.useDefaultActions;
  readonly listShowToolbar: XStandardType<boolean> =
    this.propertyProvider.showToolbar;
  readonly listStickyToolbar: XStandardType<boolean> =
    this.propertyProvider.stickyToolbar;

  //
  readonly listColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;
  readonly listForegroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  readonly listEnableReordering: XStandardType<boolean> =
    this.propertyProvider.enableReordering;
  readonly listShowDivider: XStandardType<boolean> =
    this.propertyProvider.showDivider;
  readonly listShowOptionsAsSlide: XStandardType<boolean> =
    this.propertyProvider.showOptionsAsSlide;
  readonly listCloseOptionsAfterSelect: XStandardType<boolean> =
    this.propertyProvider.closeOptionsAfterSelect;
  readonly listShowOptionsAsMenu: XStandardType<boolean> =
    this.propertyProvider.showOptionsAsMenu;

  //
  readonly listSelectableItems: XStandardType<boolean> =
    this.propertyProvider.selectableItems;
  readonly listClickableItems: XStandardType<boolean> =
    this.propertyProvider.clickableItems;
  readonly listItemTemplate: TemplateRef<any> =
    this.propertyProvider.itemTemplate;
  readonly listSlidingOptionTemplate: TemplateRef<any> =
    this.propertyProvider.slidingOptionTemplate;
  readonly listNonSlidingOptionTemplate: TemplateRef<any> =
    this.propertyProvider.nonSlidingOptionTemplate;

  //
  readonly listItemSelected: EventEmitter<XListItem<any>> =
    this.propertyProvider.itemSelected;
  readonly listOptionSelected: EventEmitter<XListItemOptionSelectedEvent> =
    this.propertyProvider.optionSelected;
  readonly listConfirmRemove: XStandardType<boolean> =
    this.propertyProvider.confirmRemove;
  readonly listRemoveItems: EventEmitter<XBaseRangeRequestDto<any>> =
    this.propertyProvider.removeItems;

  //
  readonly listSelectionChanged: EventEmitter<XListItem<any>[]> =
    this.propertyProvider.selectionChanged;
  readonly listOrderChanged: EventEmitter<void> =
    this.propertyProvider.orderChanged;
  readonly listItems: XStandardType<XListItem<any>[]> =
    this.propertyProvider.items;
  readonly listShowItemOptions: XStandardType<boolean> =
    this.propertyProvider.showItemOptions;
  readonly listItemsChanged: EventEmitter<void> =
    this.propertyProvider.itemsChanged;
  readonly listFilteringHandler: XListFilteringHandler =
    this.propertyProvider.filteringHandler;
  //#endregion

  //
  //#region List Card ...
  readonly listIsInPage: XStandardType<boolean> =
    this.propertyProvider.isInPage;
  readonly listWrapWithCard: XStandardType<boolean> =
    this.propertyProvider.wrapWithCard;
  readonly listCardShowFooter: XStandardType<boolean> =
    this.propertyProvider.cardShowFooter;
  readonly listCardShowActions: XStandardType<boolean> =
    this.propertyProvider.cardShowActions;
  readonly listCardColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.cardColor;
  readonly listCardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.cardForegroundColor;
  readonly listCardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardTitle;
  readonly listCardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.cardActionProvider;
  readonly listCardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cardSubTitle;
  readonly listCardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cardCssClass;
  //#endregion

  //
  //#region List Empty ...
  //
  readonly listShowEmpty: XStandardType<boolean> =
    this.propertyProvider.showEmpty;
  readonly listEmptyHasRefresh: XStandardType<boolean> =
    this.propertyProvider.emptyHasRefresh;
  readonly listEmptyActionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.emptyActionProvider;
  readonly listEmptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.emptyCssClass;
  readonly listEmptyColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyColor;
  readonly listEmptyForegroundColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundColor;
  readonly listEmptyMessageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyMessageColor;
  readonly listEmptyForegroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundMessageColor;
  readonly listEmptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyRefreshButtonColor;
  readonly listEmptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.emptyForegroundRefreshButtonColor;
  readonly listEmptyMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.emptyMessage;
  readonly listEmptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.emptyRefreshIcon;
  readonly listEmptyRefresh: EventEmitter<void> =
    this.propertyProvider.emptyRefresh;
  //#endregion

  //
  //#region ActionBar ...
  //
  readonly listShowActionBar: XStandardType<boolean> =
    this.propertyProvider.showActionBar;

  //
  readonly listActionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.actionBarActionProvider;
  readonly listActionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  //
  readonly listActionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;
  readonly listActionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;

  //
  readonly listActionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.actionBarActions;

  //
  readonly listActionBarActionFired: EventEmitter<XActionBarActionFiredModel> =
    this.propertyProvider.actionBarActionFired;
  //#endregion

  //
  //#region SearchBar ...
  //
  readonly listShowSearchBar: XStandardType<boolean> =
    this.propertyProvider.showSearchBar;

  //
  readonly listSearchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.searchBarActionProvider;
  readonly listSearchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.searchBarCssClass;

  //
  readonly listSearchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarColor;
  readonly listSearchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundColor;

  //
  readonly listSearchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchInputColor;
  readonly listSearchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundSearchInputColor;

  //
  readonly listSearchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarType;
  readonly listSearchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarTitle;
  readonly listSearchBarValue: XStandardType<string> =
    this.propertyProvider.searchBarValue;
  readonly listSearchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.searchBarEnterKeyHint;
  readonly listSearchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarKeyboardType;
  readonly listSearchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.searchBarSpellcheck;
  readonly listSearchBarDebounce: XStandardType<number> =
    this.propertyProvider.searchBarDebounce;

  //
  readonly listSearchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.searchBarShowCancelButton;
  readonly listSearchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarCancelButtonIcon;
  readonly listSearchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarCancelButtonTitle;
  readonly listSearchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarCancelButtonIconColor;

  //
  readonly listSearchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.searchBarShowClearButton;
  readonly listSearchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarClearButtonIcon;
  readonly listSearchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarClearButtonIconColor;

  //
  readonly listSearchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarSearchButtonIcon;
  readonly listSearchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchButtonIconColor;

  //
  readonly listSearchBarQueryChange: EventEmitter<string> =
    this.propertyProvider.searchBarQueryChange;
  readonly listSearchBarOnEvent: EventEmitter<XSearchBarEventIdentifier> =
    this.propertyProvider.searchBarOnEvent;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XListContainer) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XComponentListContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentListContainer
{
  //
  //#region Props ...
  //
  //#region Self ...
  //
  @Input()
  showList: XStandardType<boolean> = this.propertyProvider.showList;

  //
  @Input()
  listActionProvider: Subject<XListActionModel> =
    this.propertyProvider.listActionProvider;

  @Input()
  listCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.listCssClass;

  //
  @Input()
  listActionBarHideActions: XStandardType<
    XOneOrManyType<XActionBarActionIdentifier>
  > = this.propertyProvider.listActionBarHideActions;

  @Input()
  listUseDefaultActions: XStandardType<boolean> =
    this.propertyProvider.listUseDefaultActions;

  @Input()
  listShowToolbar: XStandardType<boolean> =
    this.propertyProvider.listShowToolbar;

  @Input()
  listStickyToolbar: XStandardType<boolean> =
    this.propertyProvider.listStickyToolbar;

  //
  @Input()
  listColor: XStandardType<XColorIdentifier> = this.propertyProvider.listColor;

  @Input()
  listForegroundColor: XStandardType<boolean> =
    this.propertyProvider.listForegroundColor;

  //
  @Input()
  listEnableReordering: XStandardType<boolean> =
    this.propertyProvider.listEnableReordering;

  @Input()
  listShowDivider: XStandardType<boolean> =
    this.propertyProvider.listShowDivider;

  @Input()
  listShowOptionsAsSlide: XStandardType<boolean> =
    this.propertyProvider.listShowOptionsAsSlide;

  @Input()
  listCloseOptionsAfterSelect: XStandardType<boolean> =
    this.propertyProvider.listCloseOptionsAfterSelect;

  @Input()
  listShowOptionsAsMenu: XStandardType<boolean> =
    this.propertyProvider.listShowOptionsAsMenu;

  //
  @Input()
  listSelectableItems: XStandardType<boolean> =
    this.propertyProvider.listSelectableItems;

  @Input()
  listClickableItems: XStandardType<boolean> =
    this.propertyProvider.listClickableItems;

  @Input()
  listItemTemplate: TemplateRef<any> = this.propertyProvider.listItemTemplate;

  @Input()
  listSlidingOptionTemplate: TemplateRef<any> =
    this.propertyProvider.listSlidingOptionTemplate;

  @Input()
  listNonSlidingOptionTemplate: TemplateRef<any> =
    this.propertyProvider.listNonSlidingOptionTemplate;

  //
  @Output()
  listItemSelected: EventEmitter<XListItem<any>> = new EventEmitter();

  @Output()
  listOptionSelected: EventEmitter<XListItemOptionSelectedEvent> = new EventEmitter();

  @Input()
  listConfirmRemove: XStandardType<boolean> =
    this.propertyProvider.listConfirmRemove;

  @Output()
  listRemoveItems: EventEmitter<XBaseRangeRequestDto<any>> = new EventEmitter();

  //
  @Output()
  listSelectionChanged: EventEmitter<XListItem<any>[]> = new EventEmitter();

  @Output()
  listOrderChanged: EventEmitter<void> = new EventEmitter();

  @Input()
  listItems: XStandardType<XListItem<any>[]> = this.propertyProvider.listItems;

  @Input()
  listShowItemOptions: XStandardType<boolean> =
    this.propertyProvider.listShowItemOptions;

  @Output()
  listItemsChanged: EventEmitter<void> = new EventEmitter();

  @Input()
  listFilteringHandler: XListFilteringHandler =
    this.propertyProvider.listFilteringHandler;
  //#endregion

  //
  //#region List Card ...
  @Input()
  listIsInPage: XStandardType<boolean> = this.propertyProvider.listIsInPage;

  @Input()
  listWrapWithCard: XStandardType<boolean> =
    this.propertyProvider.listWrapWithCard;

  @Input()
  listCardShowFooter: XStandardType<boolean> =
    this.propertyProvider.listCardShowFooter;

  @Input()
  listCardShowActions: XStandardType<boolean> =
    this.propertyProvider.listCardShowActions;

  @Input()
  listCardColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listCardColor;

  @Input()
  listCardForegroundColor: XStandardType<boolean> =
    this.propertyProvider.listCardForegroundColor;

  @Input()
  listCardTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.listCardTitle;

  @Input()
  listCardActionProvider: Subject<XCardActionModel> =
    this.propertyProvider.listCardActionProvider;

  @Input()
  listCardSubTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.listCardSubTitle;

  @Input()
  listCardCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.listCardCssClass;
  //#endregion

  //
  //#region List Empty ...
  //
  @Input()
  listShowEmpty: XStandardType<boolean> = this.propertyProvider.listShowEmpty;

  @Input()
  listEmptyHasRefresh: XStandardType<boolean> =
    this.propertyProvider.listEmptyHasRefresh;

  @Input()
  listEmptyActionProvider: Subject<XEmptyActionModel> =
    this.propertyProvider.listEmptyActionProvider;

  @Input()
  listEmptyCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.listEmptyCssClass;

  @Input()
  listEmptyColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listEmptyColor;

  @Input()
  listEmptyForegroundColor: XStandardType<boolean> =
    this.propertyProvider.listEmptyForegroundColor;

  @Input()
  listEmptyMessageColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listEmptyMessageColor;

  @Input()
  listEmptyForegroundMessageColor: XStandardType<boolean> =
    this.propertyProvider.listEmptyForegroundMessageColor;

  @Input()
  listEmptyRefreshButtonColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listEmptyRefreshButtonColor;

  @Input()
  listEmptyForegroundRefreshButtonColor: XStandardType<boolean> =
    this.propertyProvider.listEmptyForegroundRefreshButtonColor;

  @Input()
  listEmptyMessage: XStandardType<XResourceIdentifier> =
    this.propertyProvider.listEmptyMessage;

  @Input()
  listEmptyRefreshIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.listEmptyRefreshIcon;

  @Output()
  listEmptyRefresh: EventEmitter<void> = new EventEmitter();
  //#endregion

  //
  //#region ActionBar ...
  //
  @Input()
  listShowActionBar: XStandardType<boolean> =
    this.propertyProvider.listShowActionBar;

  //
  @Input()
  listActionBarActionProvider: Subject<XActionBarActionModel> =
    this.propertyProvider.listActionBarActionProvider;

  @Input()
  listActionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.listActionBarCssClass;

  //
  @Input()
  listActionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listActionBarColor;

  @Input()
  listActionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.listActionBarForegroundColor;

  //
  @Input()
  listActionBarActions: XStandardType<XOneOrManyType<XActionBarItem>> =
    this.propertyProvider.listActionBarActions;

  //
  @Output()
  listActionBarActionFired: EventEmitter<XActionBarActionFiredModel> = new EventEmitter<XActionBarActionFiredModel>();
  //#endregion

  //
  //#region SearchBar ...
  //
  @Input()
  listShowSearchBar: XStandardType<boolean> =
    this.propertyProvider.listShowSearchBar;

  //
  @Input()
  listSearchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.listSearchBarActionProvider;

  @Input()
  listSearchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.listSearchBarCssClass;

  //
  @Input()
  listSearchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listSearchBarColor;

  @Input()
  listSearchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.listSearchBarForegroundColor;

  //
  @Input()
  listSearchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listSearchBarSearchInputColor;

  @Input()
  listSearchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.listSearchBarForegroundSearchInputColor;

  //
  @Input()
  listSearchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.listSearchBarType;

  @Input()
  listSearchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.listSearchBarTitle;

  @Input()
  listSearchBarValue: XStandardType<string> =
    this.propertyProvider.listSearchBarValue;

  @Input()
  listSearchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.listSearchBarEnterKeyHint;

  @Input()
  listSearchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.listSearchBarKeyboardType;

  @Input()
  listSearchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.listSearchBarSpellcheck;

  @Input()
  listSearchBarDebounce: XStandardType<number> =
    this.propertyProvider.listSearchBarDebounce;

  //
  @Input()
  listSearchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.listSearchBarShowCancelButton;

  @Input()
  listSearchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.listSearchBarCancelButtonIcon;

  @Input()
  listSearchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.listSearchBarCancelButtonTitle;

  @Input()
  listSearchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listSearchBarCancelButtonIconColor;

  //
  @Input()
  listSearchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.listSearchBarShowClearButton;

  @Input()
  listSearchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.listSearchBarClearButtonIcon;

  @Input()
  listSearchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listSearchBarClearButtonIconColor;

  //
  @Input()
  listSearchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.listSearchBarSearchButtonIcon;

  @Input()
  listSearchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.listSearchBarSearchButtonIconColor;

  //
  @Output()
  listSearchBarQueryChange: EventEmitter<string> = new EventEmitter();

  @Output()
  listSearchBarOnEvent: EventEmitter<string> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentListContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentListContainer
  ): Observable<IXComponentListContainer> {
    return forkJoin({
      //
      showList: this.getValue(container ? container.showList : this.showList),
      listCssClass: this.getValue(
        container ? container.listCssClass : this.listCssClass
      ),
      listActionBarHideActions: this.getValue(
        container
          ? container.listActionBarHideActions
          : this.listActionBarHideActions
      ),
      listUseDefaultActions: this.getValue(
        container ? container.listUseDefaultActions : this.listUseDefaultActions
      ),
      listShowToolbar: this.getValue(
        container ? container.listShowToolbar : this.listShowToolbar
      ),
      listStickyToolbar: this.getValue(
        container ? container.listStickyToolbar : this.listStickyToolbar
      ),
      listColor: this.getValue(
        container ? container.listColor : this.listColor
      ),
      listForegroundColor: this.getValue(
        container ? container.listForegroundColor : this.listForegroundColor
      ),
      listEnableReordering: this.getValue(
        container ? container.listEnableReordering : this.listEnableReordering
      ),
      listShowDivider: this.getValue(
        container ? container.listShowDivider : this.listShowDivider
      ),
      listShowOptionsAsSlide: this.getValue(
        container
          ? container.listShowOptionsAsSlide
          : this.listShowOptionsAsSlide
      ),
      listCloseOptionsAfterSelect: this.getValue(
        container
          ? container.listCloseOptionsAfterSelect
          : this.listCloseOptionsAfterSelect
      ),
      listShowOptionsAsMenu: this.getValue(
        container ? container.listShowOptionsAsMenu : this.listShowOptionsAsMenu
      ),
      listSelectableItems: this.getValue(
        container ? container.listSelectableItems : this.listSelectableItems
      ),
      listClickableItems: this.getValue(
        container ? container.listClickableItems : this.listClickableItems
      ),
      listItemTemplate: this.getValue(
        container ? container.listItemTemplate : this.listItemTemplate
      ),
      listSlidingOptionTemplate: this.getValue(
        container
          ? container.listSlidingOptionTemplate
          : this.listSlidingOptionTemplate
      ),
      listNonSlidingOptionTemplate: this.getValue(
        container
          ? container.listNonSlidingOptionTemplate
          : this.listNonSlidingOptionTemplate
      ),
      listConfirmRemove: this.getValue(
        container ? container.listConfirmRemove : this.listConfirmRemove
      ),
      listItems: this.getValue(
        container ? container.listItems : this.listItems
      ),
      listShowItemOptions: this.getValue(
        container ? container.listShowItemOptions : this.listShowItemOptions
      ),
      listFilteringHandler: this.getValue(
        container ? container.listFilteringHandler : this.listFilteringHandler
      ),
      listIsInPage: this.getValue(
        container ? container.listIsInPage : this.listIsInPage
      ),
      listWrapWithCard: this.getValue(
        container ? container.listWrapWithCard : this.listWrapWithCard
      ),
      listCardShowFooter: this.getValue(
        container ? container.listCardShowFooter : this.listCardShowFooter
      ),
      listCardShowActions: this.getValue(
        container ? container.listCardShowActions : this.listCardShowActions
      ),
      listCardColor: this.getValue(
        container ? container.listCardColor : this.listCardColor
      ),
      listCardForegroundColor: this.getValue(
        container
          ? container.listCardForegroundColor
          : this.listCardForegroundColor
      ),
      listCardTitle: this.getValue(
        container ? container.listCardTitle : this.listCardTitle
      ),
      listCardSubTitle: this.getValue(
        container ? container.listCardSubTitle : this.listCardSubTitle
      ),
      listCardCssClass: this.getValue(
        container ? container.listCardCssClass : this.listCardCssClass
      ),
      listShowEmpty: this.getValue(
        container ? container.listShowEmpty : this.listShowEmpty
      ),
      listEmptyHasRefresh: this.getValue(
        container ? container.listEmptyHasRefresh : this.listEmptyHasRefresh
      ),
      listEmptyCssClass: this.getValue(
        container ? container.listEmptyCssClass : this.listEmptyCssClass
      ),
      listEmptyColor: this.getValue(
        container ? container.listEmptyColor : this.listEmptyColor
      ),
      listEmptyForegroundColor: this.getValue(
        container
          ? container.listEmptyForegroundColor
          : this.listEmptyForegroundColor
      ),
      listEmptyMessageColor: this.getValue(
        container ? container.listEmptyMessageColor : this.listEmptyMessageColor
      ),
      listEmptyForegroundMessageColor: this.getValue(
        container
          ? container.listEmptyForegroundMessageColor
          : this.listEmptyForegroundMessageColor
      ),
      listEmptyRefreshButtonColor: this.getValue(
        container
          ? container.listEmptyRefreshButtonColor
          : this.listEmptyRefreshButtonColor
      ),
      listEmptyForegroundRefreshButtonColor: this.getValue(
        container
          ? container.listEmptyForegroundRefreshButtonColor
          : this.listEmptyForegroundRefreshButtonColor
      ),
      listEmptyMessage: this.getValue(
        container ? container.listEmptyMessage : this.listEmptyMessage
      ),
      listEmptyRefreshIcon: this.getValue(
        container ? container.listEmptyRefreshIcon : this.listEmptyRefreshIcon
      ),
      listShowActionBar: this.getValue(
        container ? container.listShowActionBar : this.listShowActionBar
      ),
      listActionBarCssClass: this.getValue(
        container ? container.listActionBarCssClass : this.listActionBarCssClass
      ),
      listActionBarColor: this.getValue(
        container ? container.listActionBarColor : this.listActionBarColor
      ),
      listActionBarForegroundColor: this.getValue(
        container
          ? container.listActionBarForegroundColor
          : this.listActionBarForegroundColor
      ),
      listActionBarActions: this.getValue(
        container ? container.listActionBarActions : this.listActionBarActions
      ),
      listShowSearchBar: this.getValue(
        container ? container.listShowSearchBar : this.listShowSearchBar
      ),
      listSearchBarCssClass: this.getValue(
        container ? container.listSearchBarCssClass : this.listSearchBarCssClass
      ),
      listSearchBarColor: this.getValue(
        container ? container.listSearchBarColor : this.listSearchBarColor
      ),
      listSearchBarForegroundColor: this.getValue(
        container
          ? container.listSearchBarForegroundColor
          : this.listSearchBarForegroundColor
      ),
      listSearchBarSearchInputColor: this.getValue(
        container
          ? container.listSearchBarSearchInputColor
          : this.listSearchBarSearchInputColor
      ),
      listSearchBarForegroundSearchInputColor: this.getValue(
        container
          ? container.listSearchBarForegroundSearchInputColor
          : this.listSearchBarForegroundSearchInputColor
      ),
      listSearchBarType: this.getValue(
        container ? container.listSearchBarType : this.listSearchBarType
      ),
      listSearchBarTitle: this.getValue(
        container ? container.listSearchBarTitle : this.listSearchBarTitle
      ),
      listSearchBarValue: this.getValue(
        container ? container.listSearchBarValue : this.listSearchBarValue
      ),
      listSearchBarEnterKeyHint: this.getValue(
        container
          ? container.listSearchBarEnterKeyHint
          : this.listSearchBarEnterKeyHint
      ),
      listSearchBarKeyboardType: this.getValue(
        container
          ? container.listSearchBarKeyboardType
          : this.listSearchBarKeyboardType
      ),
      listSearchBarSpellcheck: this.getValue(
        container
          ? container.listSearchBarSpellcheck
          : this.listSearchBarSpellcheck
      ),
      listSearchBarDebounce: this.getValue(
        container ? container.listSearchBarDebounce : this.listSearchBarDebounce
      ),
      listSearchBarShowCancelButton: this.getValue(
        container
          ? container.listSearchBarShowCancelButton
          : this.listSearchBarShowCancelButton
      ),
      listSearchBarCancelButtonIcon: this.getValue(
        container
          ? container.listSearchBarCancelButtonIcon
          : this.listSearchBarCancelButtonIcon
      ),
      listSearchBarCancelButtonTitle: this.getValue(
        container
          ? container.listSearchBarCancelButtonTitle
          : this.listSearchBarCancelButtonTitle
      ),
      listSearchBarCancelButtonIconColor: this.getValue(
        container
          ? container.listSearchBarCancelButtonIconColor
          : this.listSearchBarCancelButtonIconColor
      ),
      listSearchBarShowClearButton: this.getValue(
        container
          ? container.listSearchBarShowClearButton
          : this.listSearchBarShowClearButton
      ),
      listSearchBarClearButtonIcon: this.getValue(
        container
          ? container.listSearchBarClearButtonIcon
          : this.listSearchBarClearButtonIcon
      ),
      listSearchBarClearButtonIconColor: this.getValue(
        container
          ? container.listSearchBarClearButtonIconColor
          : this.listSearchBarClearButtonIconColor
      ),
      listSearchBarSearchButtonIcon: this.getValue(
        container
          ? container.listSearchBarSearchButtonIcon
          : this.listSearchBarSearchButtonIcon
      ),
      listSearchBarSearchButtonIconColor: this.getValue(
        container
          ? container.listSearchBarSearchButtonIconColor
          : this.listSearchBarSearchButtonIconColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          listActionProvider: container
            ? container.listActionProvider
            : this.listActionProvider,

          //
          listItemSelected: this.listItemSelected,
          listOptionSelected: this.listOptionSelected,
          listRemoveItems: this.listRemoveItems,
          listSelectionChanged: this.listSelectionChanged,
          listOrderChanged: this.listOrderChanged,
          listItemsChanged: this.listItemsChanged,

          //
          listCardActionProvider: container
            ? container.listCardActionProvider
            : this.listCardActionProvider,
          listEmptyActionProvider: container
            ? container.listEmptyActionProvider
            : this.listEmptyActionProvider,

          //
          listEmptyRefresh: this.listEmptyRefresh,

          //
          listActionBarActionProvider: container
            ? container.listActionBarActionProvider
            : this.listActionBarActionProvider,

          //
          listActionBarActionFired: this.listActionBarActionFired,

          //
          listSearchBarActionProvider: container
            ? container.listSearchBarActionProvider
            : this.listSearchBarActionProvider,

          //
          listSearchBarQueryChange: this.listSearchBarQueryChange,
          listSearchBarOnEvent: this.listSearchBarOnEvent,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else mapRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="mapRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!--  Map Ref -->
<ng-template #mapRef>
  <div class="x-map x-wrapper" [hidden]="!(getValue(showMapView) | async)">
    <!-- Map View -->
    <div #mapView [id]="componentId" class="x-map-inner"></div>

    <!-- Top -->
    <div class="x-map-top">
      <!-- Search -->
      <ng-container
        *ngTemplateOutlet="
          (getValue(showSearchBar) | async) &&
          (getValue(searchBarPosition) | async) === ItemPositions.TOP
            ? searchRef
            : nothingRef
        "
      >
      </ng-container>

      <!-- ProgressBar -->
      <ng-container
        *ngTemplateOutlet="
          (getValue(showProgressBar) | async) &&
          (getValue(progressBarPosition) | async) === ItemPositions.TOP
            ? progressBarRef
            : nothingRef
        "
      >
      </ng-container>

      <!-- Plugins -->
      <div #pluginsBar>
        <ng-container
          *ngTemplateOutlet="
            (canShowPlugins(ItemPositions.TOP) | async)
              ? pluginsRef
              : nothingRef;
            context: { position: ItemPositions.TOP }
          "
        >
        </ng-container>
      </div>
    </div>

    <!-- Bottom -->
    <div class="x-map-bottom">
      <!-- Plugins -->
      <ng-container
        *ngTemplateOutlet="
          (canShowPlugins(ItemPositions.BOTTOM) | async)
            ? pluginsRef
            : nothingRef;
          context: { position: ItemPositions.BOTTOM }
        "
      >
      </ng-container>

      <!-- Search -->
      <ng-container
        *ngTemplateOutlet="
          (getValue(showSearchBar) | async) &&
          (getValue(searchBarPosition) | async) === ItemPositions.BOTTOM
            ? searchRef
            : nothingRef
        "
      >
      </ng-container>

      <!-- ProgressBar -->
      <ng-container
        *ngTemplateOutlet="
          (getValue(showProgressBar) | async) &&
          (getValue(progressBarPosition) | async) === ItemPositions.BOTTOM
            ? progressBarRef
            : nothingRef
        "
      >
      </ng-container>
    </div>
  </div>
</ng-template>

<!-- Search Template -->
<ng-template #searchRef>
  <!-- SearchBar -->
  <x-search-bar
    #searchBar
    (queryChange)="handleSeach($event)"
    *ngIf="getValue(showSearchBar) | async"
    [type]="getValue(searchBarType) | async"
    (onEvent)="handleSearchBarEvents($event)"
    [loading]="getValue(isSearching) | async"
    [actionProvider]="searchBarActionProvider"
    [color]="getValue(searchBarColor) | async"
    [value]="getValue(searchBarValue) | async"
    [uiDisabled]="getValue(isSearching) | async"
    [debounce]="getValue(searchBarDebounce) | async"
    [cssClass]="getValue(searchBarCssClass) | async"
    [title]="getResourceValue(searchBarTitle) | async"
    [spellcheck]="getValue(searchBarSpellcheck) | async"
    [foregroundSearchInputColor]="
      getValue(searchBarForegroundSearchInputColor) | async
    "
    [enterKeyHint]="getValue(searchBarEnterKeyHint) | async"
    [keyboardType]="getValue(searchBarKeyboardType) | async"
    [foregroundColor]="getValue(searchBarForegroundColor) | async"
    [clearButtonIcon]="getValue(searchBarClearButtonIcon) | async"
    [searchButtonIcon]="getValue(searchBarSearchButtonIcon) | async"
    [searchInputColor]="getValue(searchBarSearchInputColor) | async"
    [cancelButtonIcon]="getValue(searchBarCancelButtonIcon) | async"
    [showCancelButton]="getValue(searchBarShowCancelButton) | async"
    [cancelButtonTitle]="getValue(searchBarCancelButtonTitle) | async"
    [clearButtonIconColor]="getValue(searchBarClearButtonIconColor) | async"
    [searchButtonIconColor]="getValue(searchBarSearchButtonIconColor) | async"
  ></x-search-bar>

  <!-- Search Result -->
  <x-list
    xMouseHandler
    [showEmpty]="false"
    [wrapWithCard]="false"
    [clickableItems]="true"
    class="x-map-search-list"
    [uiDisabled]="uiDisabled"
    [itemTemplate]="resultRef"
    [items]="searchResultListItems"
    (itemSelected)="handleChangeLocation($event)"
    [class.open]="getValue(searchPaneOpen) | async"
    [color]="getValue(searchResultListColor) | async"
    [enableHandler]="getValue(searchPaneOpen) | async"
    [class.has-search]="getValue(showSearchBar) | async"
    (onWindowFinished)="handleLocationResultClickOutside($event)"
  ></x-list>
</ng-template>

<!-- ProgressBar Template -->
<ng-template #progressBarRef>
  <!-- Progressbar for Loading -->
  <ion-progress-bar
    type="indeterminate"
    [color]="getValue(progressBarColor) | async"
    *ngIf="
      (getValue(showProgressBar) | async) &&
      (getValue(loading) | async)
    "
  ></ion-progress-bar>
</ng-template>

<!-- Plugins Template -->
<ng-template #pluginsRef let-position="position">
  <div class="x-map-plugins-wrapper">
    <x-slotter
      [hasCenterSlot]="false"
      [layout]="SlotLayouts.HORIZONTAL"
      [endBottomSlotCssClass]="'x-auto-size-flex'"
    >
      <!-- Start -->
      <x-slot [name]="SlotNames.START" [cssClass]="'ion-text-start'">
        <!-- Locator -->
        <ng-container
          *ngTemplateOutlet="
            (getValue(showLocator) | async) &&
            (getValue(locatorPosition) | async) === position
              ? locatorRef
              : nothingRef
          "
        ></ng-container>

        <!-- Go Marked Place -->
        <ng-container
          *ngTemplateOutlet="
            (getValue(showGoMarkedPlace) | async) &&
            (getValue(goMarkedPlacePosition) | async) === position
              ? goMarkedPlaceRef
              : nothingRef
          "
        ></ng-container>

        <!-- Clear Marker -->
        <ng-container
          *ngTemplateOutlet="
            (getValue(showClearMarker) | async) &&
            (getValue(clearMarkerPosition) | async) === position
              ? clearMarkerRef
              : nothingRef
          "
        ></ng-container>
      </x-slot>

      <!-- End -->
      <x-slot
        [name]="SlotNames.END"
        class="x-auto-size-flex"
        [cssClass]="'ion-text-end'"
      >
        <!-- Selected Location -->
        <ng-container
          *ngTemplateOutlet="
            (getValue(showSelectedLocation) | async) &&
            (getValue(selectedLocationPosition) | async) === position
              ? selectedLocationRef
              : nothingRef
          "
        ></ng-container>
      </x-slot>
    </x-slotter>
  </div>
</ng-template>

<!-- SelectedLocation Template -->
<ng-template #selectedLocationRef>
  <span
    *ngIf="notEmptyValue(currentPosition) | async"
    class="x-map-plugin x-selected-location ol-control"
  >
    <button (click)="handleSlectedLocationClicked(currentPosition)">
      {{ getValue(currentPosition) | async }}
    </button>
  </span>
</ng-template>

<!-- Locator Template -->
<ng-template #locatorRef>
  <span class="x-map-plugin x-locator ol-control">
    <button
      (click)="handleLocate()"
      [disabled]="
        (getValue(uiDisabled) | async) || (getValue(loading) | async)
      "
    >
      <ion-icon name="locate-outline"></ion-icon>
    </button>
  </span>
</ng-template>

<!-- GoMarkedLocation Template -->
<ng-template #goMarkedPlaceRef>
  <span
    *ngIf="getValue(hasMarker) | async"
    class="x-map-plugin x-go-marked-place ol-control"
  >
    <button
      (click)="handleGoMarkedPlace()"
      [disabled]="
        (getValue(uiDisabled) | async) || (getValue(loading) | async)
      "
    >
      <ion-icon name="navigate"></ion-icon>
    </button>
  </span>
</ng-template>

<!-- ClearMarker Template -->
<ng-template #clearMarkerRef>
  <span
    *ngIf="getValue(hasMarker) | async"
    class="x-map-plugin x-clear-marker ol-control"
  >
    <button
      (click)="handleClearMarker()"
      [disabled]="
        (getValue(uiDisabled) | async) || (getValue(loading) | async)
      "
    >
      <ion-icon name="close-outline"></ion-icon>
    </button>
  </span>
</ng-template>

<!-- Place Result Template -->
<ng-template #resultRef let-item>
  {{ item.data.display_name }}
</ng-template>

<!-- Nothing Template -->
<ng-template #nothingRef></ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map.component.scss

:host {
  --map-height: 200px;
  --map-controls-offest: 5px;
}

:host ::ng-deep .x-auto-size-flex {
  flex-grow: 0 !important;
  flex-shrink: 1 !important;
  flex-basis: auto !important;
}

.x-map {
  position: relative;
}

.x-map-inner {
  height: var(--map-height);
  width: 100%;
}

.x-map-top,
.x-map-bottom {
  width: 100%;
  position: absolute;
  background-color: transparent;
}

.x-map-top {
  top: 0;
}

.x-map-bottom {
  bottom: 0;
}

.x-map-plugins-wrapper {
  margin: .5em;
}

.x-map-plugin {
  &.ol-control {
    padding: 0;
    position: relative;
    margin-top: 0 !important;
  }

  button {
    padding: 3px;
    width: fit-content;
    display: inline-block;
  }
}

//
//#region Search Result ...
//
$duration: 250ms;
$transform-show: scaleY(1);
$transform-hide: scaleY(0);

//
.x-map-search-list {
  //
  position: absolute;

  //
  z-index: 110;

  //
  top: 0;
  left: 0;
  right: 0;
  height: 0;

  //
  transform: $transform-hide;
  -ms-transform: $transform-hide;
  -webkit-transform: $transform-hide;

  //
  transition: transform;
  -webkit-transition: transform;

  //
  transition-duration: $duration;
  -webkit-transition-duration: $duration;

  //
  &.open {
    //
    height: auto;

    //
    transform: $transform-show;
    -ms-transform: $transform-show;
    -webkit-transform: $transform-show;

    //
    transition-duration: $duration;
    -webkit-transition-duration: $duration;
  }

  //
  &.has-search {
    top: 56px;
  }
}
//#endregion

//
//#region Ol Theme ...

:host ::ng-deep {
  .x-content.mat-card-content {
    padding: 0 !important;
  }

  .ol-control {
    margin-top: var(--map-controls-offest);
  }

  .ol-box {
    box-sizing: border-box;
    border-radius: 2px;
    border: 2px solid blue;
  }

  .ol-mouse-position {
    top: 8px;
    right: 8px;
    position: absolute;
  }

  .ol-scale-line {
    background: rgba(0, 60, 136, 0.3);
    border-radius: 4px;
    bottom: 8px;
    left: 8px;
    padding: 2px;
    position: absolute;
  }

  .ol-scale-line-inner {
    border: 1px solid #eee;
    border-top: none;
    color: #eee;
    font-size: 10px;
    text-align: center;
    margin: 1px;
    will-change: contents, width;
    transition: all 0.25s;
  }

  .ol-scale-bar {
    position: absolute;
    bottom: 8px;
    left: 8px;
  }

  .ol-scale-step-marker {
    width: 1px;
    height: 15px;
    background-color: #000000;
    float: right;
    z-index: 10;
  }

  .ol-scale-step-text {
    position: absolute;
    bottom: -5px;
    font-size: 12px;
    z-index: 11;
    color: #000000;
    text-shadow: -2px 0 #ffffff, 0 2px #ffffff, 2px 0 #ffffff, 0 -2px #ffffff;
  }

  .ol-scale-text {
    position: absolute;
    font-size: 14px;
    text-align: center;
    bottom: 25px;
    color: #000000;
    text-shadow: -2px 0 #ffffff, 0 2px #ffffff, 2px 0 #ffffff, 0 -2px #ffffff;
  }

  .ol-scale-singlebar {
    position: relative;
    height: 10px;
    z-index: 9;
    border: 1px solid black;
  }

  .ol-unsupported {
    display: none;
  }

  .ol-viewport,
  .ol-unselectable {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  }

  .ol-selectable {
    -webkit-touch-callout: default;
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    user-select: text;
  }

  .ol-grabbing {
    cursor: -webkit-grabbing;
    cursor: -moz-grabbing;
    cursor: grabbing;
  }

  .ol-grab {
    cursor: move;
    cursor: -webkit-grab;
    cursor: -moz-grab;
    cursor: grab;
  }

  .ol-control {
    position: absolute;
    background-color: rgba(255, 255, 255, 0.4);
    border-radius: 4px;
    padding: 2px;
  }

  .ol-control:hover {
    background-color: rgba(255, 255, 255, 0.6);
  }

  .ol-zoom {
    top: 0.5em;
    left: 0.5em;
  }

  .ol-rotate {
    top: 0.5em;
    right: 0.5em;
    transition: opacity 0.25s linear, visibility 0s linear;
  }

  .ol-rotate.ol-hidden {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.25s linear, visibility 0s linear 0.25s;
  }

  .ol-zoom-extent {
    top: 4.643em;
    left: 0.5em;
  }

  .ol-full-screen {
    right: 0.5em;
    top: 0.5em;
  }

  .ol-control button {
    display: block;
    margin: 1px;
    padding: 0;
    color: white;
    font-size: 1.14em;
    font-weight: bold;
    text-decoration: none;
    text-align: center;
    height: 1.375em;
    width: 1.375em;
    line-height: 0.4em;
    background-color: rgba(0, 60, 136, 0.5);
    border: none;
    border-radius: 2px;
  }

  .ol-control button::-moz-focus-inner {
    border: none;
    padding: 0;
  }

  .ol-zoom-extent button {
    line-height: 1.4em;
  }

  .ol-compass {
    display: block;
    font-weight: normal;
    font-size: 1.2em;
    will-change: transform;
  }

  .ol-touch .ol-control button {
    font-size: 1.5em;
  }

  .ol-touch .ol-zoom-extent {
    top: 5.5em;
  }

  .ol-control button:hover,
  .ol-control button:focus {
    text-decoration: none;
    background-color: rgba(0, 60, 136, 0.7);
  }

  .ol-zoom .ol-zoom-in {
    border-radius: 2px 2px 0 0;
  }

  .ol-zoom .ol-zoom-out {
    border-radius: 0 0 2px 2px;
  }

  .ol-attribution {
    text-align: right;
    bottom: 0.5em;
    right: 0.5em;
    max-width: calc(100% - 1.3em);
  }

  .ol-attribution ul {
    margin: 0;
    padding: 0 0.5em;
    color: #000;
    text-shadow: 0 0 2px #fff;
  }

  .ol-attribution li {
    display: inline;
    list-style: none;
  }

  .ol-attribution li:not(:last-child):after {
    content: ' ';
  }

  .ol-attribution img {
    max-height: 2em;
    max-width: inherit;
    vertical-align: middle;
  }

  .ol-attribution ul,
  .ol-attribution button {
    display: inline-block;
  }

  .ol-attribution.ol-collapsed ul {
    display: none;
  }

  .ol-attribution:not(.ol-collapsed) {
    background: rgba(255, 255, 255, 0.8);
  }

  .ol-attribution.ol-uncollapsible {
    bottom: 0;
    right: 0;
    border-radius: 4px 0 0;
  }

  .ol-attribution.ol-uncollapsible img {
    margin-top: -0.2em;
    max-height: 1.6em;
  }

  .ol-attribution.ol-uncollapsible button {
    display: none;
  }

  .ol-zoomslider {
    top: 4.5em;
    left: 0.5em;
    height: 200px;
  }

  .ol-zoomslider button {
    position: relative;
    height: 10px;
  }

  .ol-touch .ol-zoomslider {
    top: 5.5em;
  }

  .ol-overviewmap {
    left: 0.5em;
    bottom: 0.5em;
  }

  .ol-overviewmap.ol-uncollapsible {
    bottom: 0;
    left: 0;
    border-radius: 0 4px 0 0;
  }

  .ol-overviewmap .ol-overviewmap-map,
  .ol-overviewmap button {
    display: inline-block;
  }

  .ol-overviewmap .ol-overviewmap-map {
    border: 1px solid #7b98bc;
    height: 150px;
    margin: 2px;
    width: 150px;
  }

  .ol-overviewmap:not(.ol-collapsed) button {
    bottom: 1px;
    left: 2px;
    position: absolute;
  }

  .ol-overviewmap.ol-collapsed .ol-overviewmap-map,
  .ol-overviewmap.ol-uncollapsible button {
    display: none;
  }

  .ol-overviewmap:not(.ol-collapsed) {
    background: rgba(255, 255, 255, 0.8);
  }

  .ol-overviewmap-box {
    border: 2px dotted rgba(0, 60, 136, 0.7);
  }

  .ol-overviewmap .ol-overviewmap-box:hover {
    cursor: move;
  }

  //
  // Disable Attribution ...
  .ol-attribution {
    display: none !important;
  }
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map.component.ts

import {
  of,
  Subject,
  forkJoin,
  Observable,
  throwError,
  Subscription,
} from 'rxjs';
import {
  Component,
  ViewChild,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XMapAction,
  XMapCSSVar,
  XPlaceResult,
  XMapLocation,
  XMapActionModel,
  XMapItemPosition,
  XMapContainerComponent,
  XMapItemPositionIdentifier,
} from './x-map.typings';
import {
  DragPan,
  DragZoom,
  PinchZoom,
  DragRotate,
  Interaction,
  PinchRotate,
  KeyboardPan,
  KeyboardZoom,
  MouseWheelZoom,
  DoubleClickZoom,
} from 'ol/interaction';
import {
  tap,
  map,
  concatMap,
  catchError,
  debounceTime,
  distinctUntilChanged,
} from 'rxjs/operators';
import {
  nameof,
  XHeader,
  applyLocale,
  applyFinally,
  XContentType,
  generateUuid,
  XExceptionIDs,
  XStandardType,
  toNormalString,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import { Point } from 'ol/geom';
import BaseLayer from 'ol/layer/Base';
import { MapOptions } from 'ol/PluggableMap';
import {
  XSearchBarEvent,
  XSearchBarEventIdentifier,
} from '../x-search-bar/x-search-bar.typings';
import { toMapLocation } from './x-map.tools';
import { fromLonLat, transform } from 'ol/proj';
import { HttpHeaders } from '@angular/common/http';
import { XListItem } from '../x-list/x-list.typings';
import { OSM, Vector as VectorSource } from 'ol/source';
import { Map, View, Feature, Overlay, Kinetic } from 'ol';
import { Zoom, Rotate, ZoomSlider, Control } from 'ol/control';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
import { XSlotLayout, XSlotName } from '../x-slotter/x-slotter.typings';
import { XSearchBarComponent } from '../x-search-bar/x-search-bar.component';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-map',
  templateUrl: './x-map.component.html',
  styleUrls: ['./x-map.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XMapComponent extends XMapContainerComponent {
  //
  //#region Props ...
  //
  private ZOOM_DELTA = 2;
  private IMPORTANT_ZOOM = 18;
  private MAP: Map = undefined;
  private ANIMATION_DURATION = 500;
  private MARKER: VectorLayer<any>;
  private ZOOM_CONTROL = new Zoom();
  private IS_MAP_INITIALIZED = false;
  private MARKER_POSITION: XMapLocation;
  private ROTATE_CONTROL = new Rotate();
  private ACTION_WAITIONG_DURATION = 500;
  private MAP_OPTIONS: MapOptions = undefined;
  private ZOOM_SLIDER_CONTROL = new ZoomSlider();
  private positionProvider = new Subject<string>();
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private searchFilterSubject$ = new Subject<string>();

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly componentId = generateUuid('XMapComponent');
  readonly SlotLayouts = Object.assign({}, XSlotLayout);
  readonly ItemPositions = Object.assign({}, XMapItemPosition);

  //
  isSearching = false;
  searchPaneOpen = false;
  currentPosition: string = '';
  searchResult: XPlaceResult[] = [];
  showMapView: XStandardType<boolean> = false;
  searchResultListItems: XListItem<XPlaceResult>[] = [];

  //
  get isInitialized() {
    return this.IS_MAP_INITIALIZED;
  }

  get map() {
    return this.MAP;
  }

  get options() {
    return this.MAP_OPTIONS as MapOptions;
  }

  get hasMarker() {
    return !isNullOrUndefined(this.MARKER);
  }

  get markerPosition() {
    return this.MARKER_POSITION;
  }

  get currentZoom() {
    return !this.isInitialized ? -1 : this.MAP.getView().getZoom();
  }

  get currentLatitude() {
    //
    if (!this.isInitialized) {
      return -1;
    }

    //
    const lonLat = this.map.getView().getCenter();
    const location = toMapLocation(lonLat);

    //
    return +location.latitude;
  }

  get currentLongitude() {
    //
    if (!this.isInitialized) {
      return -1;
    }

    //
    const lonLat = this.map.getView().getCenter();
    const location = toMapLocation(lonLat);

    //
    return +location.longitude;
  }

  @ViewChild('mapView')
  mapView: ElementRef;

  @ViewChild('pluginsBar')
  pluginsBar: ElementRef<HTMLDivElement>;

  @ViewChild('searchBar')
  searchBar: XSearchBarComponent;
  //#endregion

  //
  //#region LifeCycles ...
  async afterViewInit() {
    super.afterViewInit();

    //
    await this.initializeMap();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region CanSelect Change ...
    const isCanSelectChanged = changeKeys.includes(
      nameof<XMapComponent>('canSelect')
    );
    if (isCanSelectChanged) {
      await this.applyCanSelect();
    }
    //#endregion

    //
    //#region ActionProvider changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XMapComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Location Info changed ...
    // const isZoomChanged = changeKeys.includes(nameof<XMapComponent>('zoom'));
    // const isLatitudeChanged = changeKeys.includes(
    //   nameof<XMapComponent>('latitude')
    // );
    // const isLongitudeChanged = changeKeys.includes(
    //   nameof<XMapComponent>('longitude')
    // );
    // const isAddMarkerOnSelectedLocationChanged = changeKeys.includes(
    //   nameof<XMapComponent>('addMarkerOnSelectedLocation')
    // );
    // if (
    //   isZoomChanged ||
    //   isLatitudeChanged ||
    //   isLongitudeChanged ||
    //   isAddMarkerOnSelectedLocationChanged
    // ) {
    //   await this.applyLocationInfo();
    // }
    //#endregion

    //
    //#region Show Zoom changed ...
    const isShowZoomChanged = changeKeys.includes(
      nameof<XMapComponent>('showZoom')
    );
    if (isShowZoomChanged) {
      await this.applyZoom();
    }
    //#endregion

    //
    //#region Show Rotate changed ...
    const isShowRotateChanged = changeKeys.includes(
      nameof<XMapComponent>('showRotate')
    );
    if (isShowRotateChanged) {
      await this.applyRotate();
    }
    //#endregion

    //
    //#region Show Zoom Slider changed ...
    const isShowZoomSliderChanged = changeKeys.includes(
      nameof<XMapComponent>('showZoomSlider')
    );
    if (isShowZoomSliderChanged) {
      await this.applyZoomSlider();
    }
    //#endregion

    //
    //#region Offset Height Handling ...
    const isShowSearchBarChanged = changeKeys.includes(
      nameof<XMapComponent>('showSearchBar')
    );
    const isSearchBarPositionChanged = changeKeys.includes(
      nameof<XMapComponent>('searchBarPosition')
    );
    const isShowSelectedLoactionChanged = changeKeys.includes(
      nameof<XMapComponent>('showSelectedLocation')
    );
    const isSelectedLocationPositionChanged = changeKeys.includes(
      nameof<XMapComponent>('selectedLocationPosition')
    );
    if (
      isShowRotateChanged ||
      isShowZoomChanged ||
      isShowZoomSliderChanged ||
      isShowSearchBarChanged ||
      isSearchBarPositionChanged ||
      isShowSelectedLoactionChanged ||
      isSelectedLocationPositionChanged
    ) {
      await this.applyContentOffset();
    }
    //#endregion
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    // Register Action Provider ...
    this.registerActionProvider();

    //
    // Register Search ...
    this.registerPluginHandler();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Providers ...
  async getCenter(
    latitude?: number | XStandardType<number>,
    longitude?: number | XStandardType<number>
  ) {
    //
    const desiredLatitude = await this.getNormallizedLatitued(latitude);
    const desiredLongitude = await this.getNormallizedLongitude(longitude);
    const result = fromLonLat([desiredLongitude, desiredLatitude]);

    //
    return result;
  }
  //#endregion

  //
  //#region UI Providers ...
  canShowPlugins(position: XMapItemPositionIdentifier) {
    return this.getValue(position).pipe(
      concatMap((pos) =>
        isNullOrEmptyString(pos)
          ? of(false)
          : forkJoin({
              //
              // progressBar: forkJoin({
              //   canShow: this.getValue(this.showProgressBar),
              //   position: this.getValue(this.progressBarPosition),
              // }),

              //
              locator: forkJoin({
                canShow: this.getValue(this.showLocator),
                position: this.getValue(this.locatorPosition),
              }),

              //
              clearMarker: forkJoin({
                canShow: this.getValue(this.showClearMarker),
                position: this.getValue(this.clearMarkerPosition),
              }),

              //
              goMarkedPlace: forkJoin({
                canShow: this.getValue(this.showGoMarkedPlace),
                position: this.getValue(this.goMarkedPlacePosition),
              }),

              //
              selectedLocation: forkJoin({
                canShow: this.getValue(this.showSelectedLocation),
                position: this.getValue(this.selectedLocationPosition),
              }),
            }).pipe(
              map((info) => {
                return {
                  pos,
                  info,
                };
              }),
              map((data) => {
                return (
                  //
                  // ProgressBar ...
                  // (data.info.progressBar.canShow &&
                  //   data.info.progressBar.position === data.pos) ||
                  //
                  // Locator ...
                  (data.info.locator.canShow &&
                    data.info.locator.position === data.pos) ||
                  //
                  // ClearMarker ...
                  (data.info.clearMarker.canShow &&
                    data.info.clearMarker.position === data.pos) ||
                  //
                  // Go Marked Place ...
                  (data.info.goMarkedPlace.canShow &&
                    data.info.goMarkedPlace.position === data.pos) ||
                  //
                  // Selected Location ...
                  (data.info.selectedLocation.canShow &&
                    data.info.selectedLocation.position === data.pos)
                );
              })
            )
      )
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleSeach(query: string) {
    //
    this.searchBarQueryChange.emit(query);

    //
    if (this.isSearching) {
      //
      setTimeout(
        async () => await this.handleSeach(query),
        this.ACTION_WAITIONG_DURATION
      );

      //
      return;
    }

    //
    if (isNullOrEmptyString(query)) {
      //
      this.searchResult = [];
      this.isSearching = false;
      this.searchPaneOpen = false;
      this.searchResultListItems = [];
      await this.setLoadingState(false);
      return;
    }

    //
    query = toNormalString(query);
    this.searchFilterSubject$.next(query);
  }

  async handleSearchBarEvents(event: XSearchBarEventIdentifier) {
    //
    switch (event) {
      //
      case XSearchBarEvent.FOCUS:
        //
        setTimeout(() => {
          //
          this.searchPaneOpen =
            this.hasChild(this.searchResult) &&
            this.hasChild(this.searchResultListItems);

          //
          this.detectChanges();
        }, this.ACTION_WAITIONG_DURATION);
        break;

      //
      case XSearchBarEvent.BLUR:
        //
        setTimeout(() => {
          //
          this.searchPaneOpen = false;
          this.detectChanges();
        }, this.ACTION_WAITIONG_DURATION);
        break;
    }
  }

  async handleChangeLocation(item: XListItem<XPlaceResult>) {
    //
    const zoom = this.IMPORTANT_ZOOM - 4;
    const addMarker = await this.getValueAsync(
      this.addMarkerOnSelectedLocation
    );
    if (addMarker) {
      await this.addMarker(item.data.lat, item.data.lon, zoom);
    } else {
      await this.setCenter(item.data.lat, item.data.lon, zoom);
    }
  }

  async handleLocationResultClickOutside(event: any) {
    //
    this.searchPaneOpen = false;
    this.detectChanges();
  }

  async handleGoMarkedPlace() {
    await this.goMarkedPlace();
  }

  async handleClearMarker() {
    await this.clearMarker();
  }

  async handleLocate() {
    await this.locate();
  }

  async handleSlectedLocationClicked(message: string) {
    //
    message = applyLocale(message, 'en-US');
    this.managerService.saveToClipboard(message);
  }
  //#endregion

  //
  //#region Actions ...
  async updateMap() {
    //
    // Recalling ...
    if (!this.isInitialized) {
      setTimeout(async () => {
        await this.updateMap();
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    //#region Update ...
    this.MAP.render();
    this.detectChanges();
    this.MAP.updateSize();
    this.MAP.renderSync();
    this.managerService.dispatchResizeEvent();
    if (this.mapView && this.mapView.nativeElement) {
      //
      const mapView = this.mapView.nativeElement as HTMLElement;
      mapView.blur();
      mapView.focus();
    }
    //#endregion

    //
    setTimeout(async () => {
      await this.updateMap();
    }, this.ACTION_WAITIONG_DURATION);
  }

  async setZoom(zoom?: number | XStandardType<number>) {
    //
    const desiredZoom = await this.getNormallizedZoom(zoom);

    //
    if (!this.isInitialized) {
      //
      setTimeout(async () => {
        await this.setZoom(zoom);
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    this.map.getView().animate({
      zoom: desiredZoom,
      duration: this.ANIMATION_DURATION,
    });
  }

  async setCenter(
    latitude?: number | XStandardType<number>,
    longitude?: number | XStandardType<number>,
    zoom?: number | XStandardType<number>
  ) {
    //
    const desiredZoom = await this.getNormallizedZoom(zoom);
    const center = await this.getCenter(latitude, longitude);

    //
    if (!this.isInitialized) {
      //
      setTimeout(async () => {
        await this.setCenter(latitude, longitude, zoom);
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    this.map.getView().animate({
      center,
      zoom: desiredZoom,
      duration: this.ANIMATION_DURATION,
    });
  }

  async addMarker(
    latitude?: number | XStandardType<number>,
    longitude?: number | XStandardType<number>,
    zoom?: number | XStandardType<number>
  ) {
    //
    if (!this.isInitialized) {
      //
      setTimeout(async () => {
        await this.addMarker(latitude, longitude, zoom);
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    const desiredZoom = await this.getNormallizedZoom(zoom);
    const desiredLat = await this.getNormallizedLatitued(latitude);
    const desiredLong = await this.getNormallizedLongitude(longitude);

    //
    if (this.MARKER) {
      //
      this.MAP.removeLayer(this.MARKER);
      this.MARKER = null;
      this.actionProvider.next({ action: XMapAction.HasNotMarker });
    }

    //
    this.MARKER_POSITION = {
      zoom: desiredZoom.toString(),
      latitude: desiredLat.toString(),
      longitude: desiredLong.toString(),
    };

    //
    const markerStyle = await this.getValueAsync(this.markerStyle);

    //
    this.MARKER = new VectorLayer({
      source: new VectorSource({
        features: [
          new Feature({
            geometry: new Point(
              transform([desiredLong, desiredLat], 'EPSG:4326', 'EPSG:3857')
            ),
          }),
        ],
      }),
      style: markerStyle,
    });

    //
    // Netify Selected Location ...
    await this.updateSelectedLocation(latitude, longitude);
    this.actionProvider.next({ action: XMapAction.HasMarker });

    //
    // Emit Location Selected Event ...
    this.locationSelected.emit(this.MARKER_POSITION);

    //
    this.MAP.addLayer(this.MARKER);

    //
    setTimeout(async () => {
      await this.setCenter(desiredLat, desiredLong, desiredZoom);
    }, this.ACTION_WAITIONG_DURATION);
  }

  async clearMarker() {
    //
    if (!this.MARKER) {
      return;
    }

    //
    if (!this.isInitialized) {
      //
      setTimeout(async () => {
        await this.clearMarker();
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    this.MAP.removeLayer(this.MARKER);
    this.MARKER = null;
    this.MARKER_POSITION = null;

    //
    this.positionProvider.next('');
    this.actionProvider.next({
      action: XMapAction.HasNotMarker,
    });
  }

  async goMarkedPlace() {
    //
    if (!this.hasMarker) {
      return;
    }

    //
    if (!this.isInitialized) {
      //
      setTimeout(async () => {
        await this.goMarkedPlace();
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    await this.setCenter(
      +this.markerPosition.latitude,
      +this.markerPosition.longitude
    );
  }

  async locate() {
    //
    await this.setLoadingState(true);
    const location = await this.getValueAsync(
      applyFinally(
        this.managerService.geoLocationService.getCurrentLocation().pipe(
          catchError((err) => {
            //
            this.showError(err);

            //
            return throwError(err);
          })
        ),
        async () => await this.setLoadingState(false)
      )
    );

    //
    if (!isNullOrUndefined(location)) {
      //
      this.zoom = this.IMPORTANT_ZOOM;
      this.latitude = location.coords.latitude;
      this.longitude = location.coords.longitude;

      //
      await this.addMarker(
        location.coords.latitude,
        location.coords.longitude,
        this.zoom
      );
    }
  }
  //#endregion

  //
  //#region Private ...
  private async prepareMapOptions() {
    //
    this.MAP = null;
    this.MAP_OPTIONS = null;
    this.showMapView = false;

    //
    const zoom = 0; // await this.getNormallizedZoom();

    //
    const desiredLatitude = 0; // await this.getNormallizedLatitued();
    const desiredLongitude = 0; // await this.getNormallizedLongitude();
    const center = fromLonLat([desiredLatitude, desiredLongitude]);

    //
    const view = new View({
      zoom,
      center,
    });

    //
    const layers: BaseLayer[] = [];
    layers.push(
      new TileLayer({
        source: new OSM(),
      })
    );

    //
    const controls: Control[] = await this.getControls();

    //
    const interactions: Interaction[] = await this.getInteractions();

    //
    const overlays: Overlay[] = [];

    //
    this.MAP_OPTIONS = {
      view,
      layers,
      controls,
      overlays,
      target: this.componentId,
      interactions,
      keyboardEventTarget: this.componentId,
    };
  }

  private async initializeMap() {
    //
    if (!isNullOrUndefined(this.MAP)) {
      this.MAP = null;
    }

    //
    await this.prepareMapOptions();

    //
    this.MAP = new Map(this.options);

    //
    //#region Attached Events ...
    //
    // Map Event Handler ...
    this.MAP.on('rendercomplete', async (e: any) => {
      //
      this.isReady.emit();

      //
      this.IS_MAP_INITIALIZED = true;

      //
      this.managerService.dispatchResizeEvent();
      Promise.resolve().then(() => this.managerService.dispatchResizeEvent());
      setTimeout(
        () => this.managerService.dispatchResizeEvent(),
        this.ACTION_WAITIONG_DURATION
      );
    });

    //
    // Zoom End ...
    this.MAP.on('moveend', async (e: any) => {
      //
      const zoom = this.map.getView().getZoom();
      const lonLat = this.map.getView().getCenter();
      const location = toMapLocation(lonLat);
      location.zoom = zoom.toString();

      //
      this.zoom = zoom;

      //
      this.viewChanged.emit(location);
    });
    //#endregion

    //
    this.showMapView = true;
    await this.applyContentOffset();
    await this.updateMap();
    await this.applyCanSelect();

    //
    await this.applyLocationInfo();
  }

  private async getInteractions() {
    //
    const result: Interaction[] = [];

    //
    const delta = this.ZOOM_DELTA;
    const duration = this.ANIMATION_DURATION;

    //
    const dragPan = true;
    const pinchZoom = true;
    const onFocusOnly = true;
    const pinchRotate = true;
    const keyboardPan = true;
    const keyboardZoom = true;
    const shiftDragZoom = true;
    const mouseWheelZoom = true;
    const doubleClickZoom = true;
    const altShiftDragRotate = true;

    //
    //#region Preperation ...
    //
    // DragRotate ...
    const kinetic = new Kinetic(-0.005, 0.05, 100);
    if (altShiftDragRotate) {
      result.push(new DragRotate());
    }

    //
    // DoubleClickZoom ...
    if (doubleClickZoom) {
      result.push(
        new DoubleClickZoom({
          delta,
          duration,
        })
      );
    }

    //
    // DragPan ...
    if (dragPan) {
      result.push(
        new DragPan({
          onFocusOnly,
          kinetic,
        })
      );
    }

    //
    // PinchRotate ...
    if (pinchRotate) {
      result.push(new PinchRotate());
    }

    //
    // PinchZoom ...
    if (pinchZoom) {
      result.push(
        new PinchZoom({
          duration,
        })
      );
    }

    //
    // KeyboardPan ...
    if (keyboardPan) {
      result.push(new KeyboardPan());
    }

    //
    // KeyboardZoom ...
    if (keyboardZoom) {
      result.push(
        new KeyboardZoom({
          delta,
          duration,
        })
      );
    }

    //
    // MouseWheelZoom ...
    if (mouseWheelZoom) {
      result.push(
        new MouseWheelZoom({
          duration,
          onFocusOnly,
        })
      );
    }

    //
    // DragZoom ...
    if (shiftDragZoom) {
      result.push(
        new DragZoom({
          duration,
        })
      );
    }
    //#endregion

    //
    return result;
  }

  private async getControls() {
    //
    const result: Control[] = [];

    //
    //#region Adding Controls ...
    //
    // Zoom ...
    const showZoom = await this.getValueAsync(this.showZoom);
    if (showZoom) {
      result.push(this.ZOOM_CONTROL);
    }

    //
    // Rotate ...
    const showRotate = await this.getValueAsync(this.showRotate);
    if (showRotate) {
      result.push(this.ROTATE_CONTROL);
    }

    //
    // ZoomSlider ...
    const showZoomSlider = await this.getValueAsync(this.showZoomSlider);
    if (showZoomSlider) {
      result.push(this.ZOOM_SLIDER_CONTROL);
    }
    //#endregion

    //
    return result;
  }

  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XMapActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XMapAction.AddMarker:
            //
            if (model.payload) {
              //
              const result: XMapLocation = model.payload;
              if (result) {
                //
                const lat = +result.latitude;
                const long = +result.longitude;
                const zoom = +result.zoom;

                //
                await this.addMarker(lat, long, zoom);
              }
            }
            break;

          //
          case XMapAction.ClearMarker:
            await this.clearMarker();
            break;

          //
          case XMapAction.GoMarkedPlace:
            await this.goMarkedPlace();
            break;

          //
          case XMapAction.SetCenter:
            //
            if (model.payload) {
              //
              const result: XMapLocation = model.payload;
              if (result) {
                //
                const zoom = +result.zoom;
                const lat = +result.latitude;
                const long = +result.longitude;

                //
                await this.setCenter(lat, long, zoom);
              }
            }
            break;

          //
          case XMapAction.SetZoom:
            //
            if (model.payload) {
              //
              const zoom = +model.payload;
              await this.setZoom(zoom);
            }
            break;

          //
          case XMapAction.Locate:
            await this.locate();
            break;

          //
          case XMapAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }

  private registerPluginHandler() {
    //
    // Register Query Change ...
    this.applyTakeUntilWrapper(
      this.searchFilterSubject$
        .asObservable()
        .pipe(debounceTime(800), distinctUntilChanged())
    ).subscribe(async (query) => {
      await this.handleSearchAction(query);
    });

    //
    // Register LocationProvider ...
    this.applyTakeUntilWrapper(this.positionProvider.asObservable()).subscribe(
      (position) => {
        //
        this.currentPosition = position;
        this.detectChanges();
      }
    );
  }

  private async getNormallizedLatitued(
    latitude?: number | XStandardType<number>
  ) {
    const result = await this.getValueAsync(latitude || this.latitude || 0);
    return result || 0;
  }

  private async getNormallizedLongitude(
    longitude?: number | XStandardType<number>
  ) {
    const result = await this.getValueAsync(longitude || this.longitude || 0);
    return result || 0;
  }

  private async getNormallizedZoom(zoom?: number | XStandardType<number>) {
    //
    let result = 0;
    if (!zoom) {
      result = await this.getValueAsync(this.zoom);
    } else {
      result = await this.getValueAsync(zoom);
    }

    //
    result =
      result > this.IMPORTANT_ZOOM
        ? this.IMPORTANT_ZOOM
        : result < 0
        ? 0
        : result;

    //
    return result || 0;
  }

  private async getSummarySelectedLocationMessage(
    latitude?: number | XStandardType<number>,
    longitude?: number | XStandardType<number>
  ) {
    //
    const desiredLat = await this.getNormallizedLatitued(latitude);
    const desiredLong = await this.getNormallizedLongitude(longitude);

    //
    const mLat = this.applyLocale(desiredLat.toString());
    const mLong = this.applyLocale(desiredLong.toString());

    //
    const result = `${mLat}, ${mLong}`;
    return result;
  }

  //
  private async applyLocationInfo() {
    //
    if (!this.isInitialized) {
      //
      setTimeout(
        async () => await this.applyLocationInfo(),
        this.ACTION_WAITIONG_DURATION
      );
      //
      return;
    }

    //
    const normallizedZoom = await this.getNormallizedZoom();
    const latitude = await this.getNormallizedLatitued();
    const longitude = await this.getNormallizedLongitude();

    //
    const addMarker = await this.getValueAsync(
      this.addMarkerOnSelectedLocation
    );
    const zoom = addMarker ? this.IMPORTANT_ZOOM : normallizedZoom;

    //
    if (this.longitude && this.latitude) {
      //
      setTimeout(async () => {
        //
        if (addMarker) {
          await this.addMarker(latitude, longitude, zoom);
        } else {
          await this.setCenter(latitude, longitude, zoom);
        }
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    if (this.zoom) {
      //
      setTimeout(async () => {
        await this.setZoom(normallizedZoom);
      }, 1000);

      //
      return;
    }
  }

  private async applyContentOffset() {
    //
    let result = 5;

    //
    const showSerach = await this.getValueAsync(this.showSearchBar);
    if (showSerach) {
      //
      const searchBarPosition = await this.getValueAsync(
        this.searchBarPosition
      );

      //
      if (searchBarPosition === XMapItemPosition.TOP && this.searchBar) {
        //
        const searchBarHeight = this.searchBar.getElementBoundaryHeight();
        if (searchBarHeight === 0) {
          //
          setTimeout(
            async () => await this.applyContentOffset(),
            this.ACTION_WAITIONG_DURATION
          );

          //
          return;
        }

        //
        result += searchBarHeight;
      }
    }

    //
    const canShowPluginBarAtTop = await this.getValueAsync(
      this.canShowPlugins(this.ItemPositions.TOP)
    );
    if (canShowPluginBarAtTop && this.pluginsBar) {
      //
      const pluginsBarHeight =
        this.pluginsBar.nativeElement.getBoundingClientRect().height;
      if (pluginsBarHeight === 0) {
        //
        setTimeout(
          async () => await this.applyContentOffset(),
          this.ACTION_WAITIONG_DURATION
        );

        //
        return;
      }

      //
      result += pluginsBarHeight;
    }

    //
    this.setCssVar(XMapCSSVar.MapControlOffset, `${result}px`);
  }

  private async applyCanSelect() {
    //
    if (!this.isInitialized) {
      //
      setTimeout(
        async () => await this.applyCanSelect(),
        this.ACTION_WAITIONG_DURATION
      );

      //
      return;
    }

    //
    // Add Marker on Map Clicked ...
    const canSelect = await this.getValueAsync(this.canSelect);
    if (canSelect) {
      this.MAP.on('click', async (args: any) => {
        //
        this.searchPaneOpen = false;

        //
        const lonlat = transform(args.coordinate, 'EPSG:3857', 'EPSG:4326');

        //
        // tslint:disable-next-line:no-shadowed-variable
        const lat = lonlat[1] as number;

        //
        // tslint:disable-next-line:no-shadowed-variable
        const long = lonlat[0] as number;

        //
        // , this.IMPORTANT_ZOOM - 6
        await this.addMarker(lat, long);
      });
    }
  }

  //
  //#region Plugin Handlers ...
  private async applyZoom(forceUpdate = true) {
    //
    if (!this.isInitialized) {
      //
      setTimeout(
        async () => await this.applyZoom(),
        this.ACTION_WAITIONG_DURATION
      );

      //
      return;
    }

    //
    const showZoom = await this.getValueAsync(this.showZoom);
    if (showZoom) {
      this.map.addControl(this.ZOOM_CONTROL);
    } else {
      this.map.removeControl(this.ZOOM_CONTROL);
    }

    //
    if (forceUpdate) {
      await this.updateMap();
    }
  }

  private async applyRotate(forceUpdate = true) {
    //
    if (!this.isInitialized) {
      //
      setTimeout(
        async () => await this.applyRotate(),
        this.ACTION_WAITIONG_DURATION
      );

      //
      return;
    }

    //
    const showRotate = await this.getValueAsync(this.showRotate);
    if (showRotate) {
      this.map.addControl(this.ROTATE_CONTROL);
    } else {
      this.map.removeControl(this.ROTATE_CONTROL);
    }

    //
    if (forceUpdate) {
      await this.updateMap();
    }
  }

  private async applyZoomSlider(forceUpdate = true) {
    //
    if (!this.isInitialized) {
      //
      setTimeout(
        async () => await this.applyZoomSlider(),
        this.ACTION_WAITIONG_DURATION
      );

      //
      return;
    }

    //
    const showZoomSlider = await this.getValueAsync(this.showZoomSlider);
    if (showZoomSlider) {
      this.map.addControl(this.ZOOM_SLIDER_CONTROL);
    } else {
      this.map.removeControl(this.ZOOM_SLIDER_CONTROL);
    }

    //
    if (forceUpdate) {
      await this.updateMap();
    }
  }

  //
  //#region SelectedLocation Plugin ...
  private async updateSelectedLocation(
    latitude?: number | XStandardType<number>,
    longitude?: number | XStandardType<number>
  ) {
    //
    if (!this.isInitialized) {
      //
      setTimeout(async () => {
        await this.updateSelectedLocation(latitude, longitude);
      }, this.ACTION_WAITIONG_DURATION);

      //
      return;
    }

    //
    const message = await this.getSummarySelectedLocationMessage(
      latitude,
      longitude
    );
    this.positionProvider.next(message);
  }
  //#endregion

  //
  //#region SearchBar Plugin ...
  private async handleSearchAction(query: string) {
    //
    const url = this.searchPlaceProvider(query);

    //
    this.searchResult = [];
    this.searchPaneOpen = false;
    this.searchResultListItems = [];

    //
    await this.setLoadingState(true);
    applyFinally(this.getPlaceResults(url), async () => {
      this.isSearching = false;
      await this.setLoadingState(false);
    }).subscribe(
      async (res) => {
        //
        this.searchResult = res;
        await this.prepareSearchListItems();
      },
      () => {
        this.showError(XExceptionIDs.ActionFailed);
      }
    );
  }

  private getPlaceResults(url: string): Observable<XPlaceResult[]> {
    //
    const headers: HttpHeaders = new HttpHeaders();
    headers.set(XHeader.Accept, XContentType.AppJson);

    //
    return applyFinally(
      this.applyLoadingWrapper(
        this.managerService.httpClient
          .get<XPlaceResult[]>(url, { headers })
          .pipe(
            map((res) => {
              return res.map((r) => {
                //
                const mPlace: XPlaceResult = {
                  display_name: r.display_name,
                  lat: +r.lat,
                  lon: +r.lon,
                };

                //
                return mPlace;
              });
            }),
            tap((res) => {
              if (!this.hasChild(res)) {
                this.searchPaneOpen = false;
              }
            })
          )
      ),
      () => this.setLoadingState(false)
    );
  }

  private async prepareSearchListItems() {
    //
    if (!this.hasChild(this.searchResult)) {
      //
      this.searchPaneOpen = false;
      await this.setLoadingState(false);
      return;
    }

    //
    this.searchResultListItems = this.searchResult.map((r) => {
      //
      const li: XListItem<XPlaceResult> = {
        data: r,
      };

      //
      return li;
    });

    //
    this.searchPaneOpen = true;
    await this.setLoadingState(false);

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XMapComponent } from './x-map.component';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XListModule } from '../x-list/x-list.module';
import { XMapContainerComponent } from './x-map.typings';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XMapDialogService } from './x-map-dialog/x-map-dialog.service';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XMapDialogComponent } from './x-map-dialog/x-map-dialog.component';

@NgModule({
  imports: [
    XListModule,
    XCardModule,
    XIconModule,
    CommonModule,
    XSlotterModule,
    XBaseComponentModule,
  ],
  exports: [
    XListModule,
    XCardModule,
    XIconModule,
    CommonModule,
    XMapComponent,
    XSlotterModule,
    XMapDialogComponent,
    XBaseComponentModule,
  ],
  providers: [XMapDialogService],
  entryComponents: [XMapDialogComponent],
  declarations: [XMapComponent, XMapDialogComponent, XMapContainerComponent],
})
export class XMapModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map.tools.ts

import { XMapLocation } from './x-map.typings';
import { isNullOrEmptyString, hasChild, isString, isArray, countChild } from 'x-framework-core';

/**
 * get given position link for open on Google Maps ...
 *
 * @param model map result model ...
 */
export function getGoogleMapLink(model: XMapLocation) {
  return `https://maps.google.com/?q=${model.latitude},${model.longitude}`;
}

/**
 * convert a point string to Map Result ...
 *
 * @param longLat the string which represent point ...
 */
export function fromLongLat(longLat: string) {
  //
  if (isNullOrEmptyString(longLat)) {
    return null;
  }

  //
  const longLatArr = longLat.toString().split(',');
  if (!hasChild(longLatArr) || longLatArr.length !== 2) {
    return null;
  }

  //
  const result: XMapLocation = {
    latitude: longLatArr[1],
    longitude: longLatArr[0],
  };

  //
  return result;
}

export function toMapLocation(source: string | Array<number>) {
  //
  if (isString(source)) {
    return fromLongLat(source as string);
  }

  //
  if (!isArray(source) || countChild(source as Array<number>) < 2) {
    return null;
  }

  //
  const longLatArr = source as Array<number>;

  //
  const result: XMapLocation = {
    latitude: longLatArr[1].toString(),
    longitude: longLatArr[0].toString(),
  };

  //
  return result;
}

/**
 * convert a point object to point string ...
 *
 * @param point the map selected object to convert it ...
 */
export function toLongLat(point: XMapLocation) {
  return `${point.longitude},${point.latitude}`;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XCardContainer,
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import {
  XSearchBarActionModel,
  XSearchBarTypeIdentifier,
  XComponentSearchBarContainer,
  IXComponentSearchBarContainer,
} from '../x-search-bar/x-search-bar.typings';
import { concatMap, map } from 'rxjs/operators';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { Style, Circle, Stroke, Fill } from 'ol/style';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { IXBaseContainer } from '../classes/i-x-base.container';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export interface XPlaceResult {
  display_name: string;
  lat: number;
  lon: number;
}

export enum XMapItemPosition {
  TOP = 'top',
  BOTTOM = 'bottom',
}

export type XMapItemPositionIdentifier = XMapItemPosition | string;

export enum XMapAction {
  //
  Locate = 'locate',

  //
  AddMarker = 'add_marker',
  HasMarker = 'has_marker',
  ClearMarker = 'clear-marker',
  HasNotMarker = 'has_not_marker',
  GoMarkedPlace = 'go-marked-place',

  //
  SetCenter = 'set_center',
  SetZoom = 'set_zoom',

  //
  Refresh = 'refresh',
}

export type XMapActionIdentifier = XMapAction | string;

export interface XMapActionModel
  extends XBaseActionModel<XMapActionIdentifier> {}

export enum XMapCSSVar {
  MapControlOffset = 'map-controls-offest',
}

export type XMapCSSVarIdentification = XMapCSSVar | string;

export interface XMapLocation {
  zoom?: string;
  latitude?: string;
  longitude?: string;
}

export type XMapStyle = Style;

export const defaultMarkerStyle: XMapStyle = new Style({
  image: new Circle({
    radius: 10,
    stroke: new Stroke({
      color: 'rgb(255,0,0)',
      width: 5,
    }),
    fill: new Fill({
      color: 'rgba(255,0,0,.3)',
    }),
  }),
  stroke: new Stroke({
    color: 'rgb(255,0,0)',
    width: 5,
  }),
  fill: new Fill({
    color: 'rgba(255,165,0,.3)',
  }),
});

export const defaultMapSearchPlaceProvider = (query: string) => {
  return (
    'https://nominatim.openstreetmap.org/search?q=' +
    query +
    '&format=json&limit=5'
  );
};

//
//#region Inside Card Container ...
type TXMapContainer = IXComponentCardContainer & IXComponentSearchBarContainer;
export interface IXMapContainer extends IXBaseContainer, TXMapContainer {
  //
  actionProvider: Subject<XMapActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  zoom: XStandardType<number>;
  latitude: XStandardType<number>;
  longitude: XStandardType<number>;

  canSelect: XStandardType<boolean>;
  addMarkerOnSelectedLocation: XStandardType<boolean>;

  //
  isReady: EventEmitter<void>;
  viewChanged: EventEmitter<XMapLocation>;
  locationSelected: EventEmitter<XMapLocation>;
  // zoomChanged: EventEmitter<number>;

  markerStyle: XStandardType<XMapStyle>;

  showProgressBar: XStandardType<boolean>;
  progressBarColor: XStandardType<XColorIdentifier>;
  progressBarPosition: XStandardType<XMapItemPositionIdentifier>;

  showSelectedLocation: XStandardType<boolean>;
  selectedLocationPosition: XStandardType<XMapItemPositionIdentifier>;

  showGoMarkedPlace: XStandardType<boolean>;
  goMarkedPlacePosition: XStandardType<XMapItemPositionIdentifier>;

  showClearMarker: XStandardType<boolean>;
  clearMarkerPosition: XStandardType<XMapItemPositionIdentifier>;

  showLocator: XStandardType<boolean>;
  locatorPosition: XStandardType<XMapItemPositionIdentifier>;

  showZoom: XStandardType<boolean>;
  showRotate: XStandardType<boolean>;
  showZoomSlider: XStandardType<boolean>;

  //
  searchBarPosition: XStandardType<XMapItemPositionIdentifier>;
  searchPlaceProvider: (query: string) => string;
  searchResultListColor: XStandardType<XColorIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XMapContainer
  extends XComponentCardContainer
  implements Readonly<IXMapContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XMapActionModel> =
    new Subject<XMapActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly zoom: XStandardType<number> = 0;
  readonly latitude: XStandardType<number> = 0;
  readonly longitude: XStandardType<number> = 0;

  readonly canSelect: XStandardType<boolean> = true;
  readonly addMarkerOnSelectedLocation: XStandardType<boolean> = true;

  //
  readonly isReady: EventEmitter<void> = new EventEmitter();
  readonly viewChanged: EventEmitter<XMapLocation> =
    new EventEmitter<XMapLocation>();
  readonly locationSelected: EventEmitter<XMapLocation> =
    new EventEmitter<XMapLocation>();
  // zoomChanged: EventEmitter<number>;

  readonly markerStyle: XStandardType<XMapStyle> = defaultMarkerStyle;

  readonly showProgressBar: XStandardType<boolean> = true;
  readonly progressBarColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;
  readonly progressBarPosition: XStandardType<XMapItemPositionIdentifier> =
    XMapItemPosition.TOP;

  readonly showSelectedLocation: XStandardType<boolean> = true;
  readonly selectedLocationPosition: XStandardType<XMapItemPositionIdentifier> =
    XMapItemPosition.TOP;

  readonly showGoMarkedPlace: XStandardType<boolean> = true;
  readonly goMarkedPlacePosition: XStandardType<XMapItemPositionIdentifier> =
    XMapItemPosition.TOP;

  readonly showClearMarker: XStandardType<boolean> = true;
  readonly clearMarkerPosition: XStandardType<XMapItemPositionIdentifier> =
    XMapItemPosition.TOP;

  readonly showLocator: XStandardType<boolean> = true;
  readonly locatorPosition: XStandardType<XMapItemPositionIdentifier> =
    XMapItemPosition.TOP;

  readonly showZoom: XStandardType<boolean> = true;
  readonly showRotate: XStandardType<boolean> = true;
  readonly showZoomSlider: XStandardType<boolean> = false;

  //
  readonly searchBarPosition: XStandardType<XMapItemPositionIdentifier> =
    XMapItemPosition.TOP;
  readonly searchPlaceProvider: (query: string) => string =
    defaultMapSearchPlaceProvider;
  readonly searchResultListColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;

  //
  //#region ComponentSearchBar ...
  //
  readonly showSearchBar: XStandardType<boolean> =
    this.searchBarPropertyProvider.showSearchBar;

  //
  readonly searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.searchBarPropertyProvider.searchBarActionProvider;
  readonly searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.searchBarPropertyProvider.searchBarCssClass;

  //
  readonly searchBarColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarColor;
  readonly searchBarForegroundColor: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarForegroundColor;

  //
  readonly searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchInputColor;
  readonly searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarForegroundSearchInputColor;

  //
  readonly searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.searchBarPropertyProvider.searchBarType;
  readonly searchBarTitle: XStandardType<XResourceIdentifier> =
    this.searchBarPropertyProvider.searchBarTitle;
  readonly searchBarValue: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarValue;
  readonly searchBarEnterKeyHint: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarEnterKeyHint;
  readonly searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.searchBarPropertyProvider.searchBarKeyboardType;
  readonly searchBarSpellcheck: XStandardType<boolean> =
    this.searchBarPropertyProvider.searchBarSpellcheck;
  readonly searchBarDebounce: XStandardType<number> =
    this.searchBarPropertyProvider.searchBarDebounce;

  //
  readonly searchBarShowCancelButton: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarShowCancelButton;
  readonly searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonIcon;
  readonly searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonTitle;
  readonly searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarCancelButtonIconColor;

  //
  readonly searchBarShowClearButton: XStandardType<string> =
    this.searchBarPropertyProvider.searchBarShowClearButton;
  readonly searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarClearButtonIcon;
  readonly searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarClearButtonIconColor;

  //
  readonly searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchButtonIcon;
  readonly searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.searchBarPropertyProvider.searchBarSearchButtonIconColor;

  //
  readonly searchBarQueryChange: EventEmitter<string> =
    this.searchBarPropertyProvider.searchBarQueryChange;
  readonly searchBarOnEvent: EventEmitter<string> =
    this.searchBarPropertyProvider.searchBarOnEvent;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected propertyProvider: XCardContainer,
    protected searchBarPropertyProvider: XComponentSearchBarContainer
  ) {
    super(propertyProvider);
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export class XMapContainerComponent
  extends XComponentCardContainerComponent
  implements IXMapContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XMapActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  zoom: XStandardType<number> = this.propertyProvider.zoom;

  @Input()
  latitude: XStandardType<number> = this.propertyProvider.latitude;

  @Input()
  longitude: XStandardType<number> = this.propertyProvider.longitude;

  @Input()
  canSelect: XStandardType<boolean> = this.propertyProvider.canSelect;

  @Input()
  addMarkerOnSelectedLocation: XStandardType<boolean> =
    this.propertyProvider.addMarkerOnSelectedLocation;

  //
  @Output()
  isReady: EventEmitter<void> = new EventEmitter();

  @Output()
  viewChanged: EventEmitter<XMapLocation> = new EventEmitter();

  @Output()
  locationSelected: EventEmitter<XMapLocation> = new EventEmitter();
  // zoomChanged: EventEmitter<number>;

  @Input()
  markerStyle: XStandardType<XMapStyle> = this.propertyProvider.markerStyle;

  @Input()
  showProgressBar: XStandardType<boolean> =
    this.propertyProvider.showProgressBar;

  @Input()
  progressBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.progressBarColor;

  @Input()
  progressBarPosition: XStandardType<XMapItemPositionIdentifier> =
    this.propertyProvider.progressBarPosition;

  @Input()
  showSelectedLocation: XStandardType<boolean> =
    this.propertyProvider.showSelectedLocation;

  @Input()
  selectedLocationPosition: XStandardType<XMapItemPositionIdentifier> =
    this.propertyProvider.selectedLocationPosition;

  @Input()
  showGoMarkedPlace: XStandardType<boolean> =
    this.propertyProvider.showGoMarkedPlace;

  @Input()
  goMarkedPlacePosition: XStandardType<XMapItemPositionIdentifier> =
    this.propertyProvider.goMarkedPlacePosition;

  @Input()
  showClearMarker: XStandardType<boolean> =
    this.propertyProvider.showClearMarker;

  @Input()
  clearMarkerPosition: XStandardType<XMapItemPositionIdentifier> =
    this.propertyProvider.clearMarkerPosition;

  @Input()
  showLocator: XStandardType<boolean> = this.propertyProvider.showLocator;

  @Input()
  locatorPosition: XStandardType<XMapItemPositionIdentifier> =
    this.propertyProvider.locatorPosition;

  @Input()
  showZoom: XStandardType<boolean> = this.propertyProvider.showZoom;

  @Input()
  showRotate: XStandardType<boolean> = this.propertyProvider.showRotate;

  @Input()
  showZoomSlider: XStandardType<boolean> = this.propertyProvider.showZoomSlider;

  //
  @Input()
  searchBarPosition: XStandardType<XMapItemPositionIdentifier> =
    this.propertyProvider.searchBarPosition;

  @Input()
  searchPlaceProvider: (query: string) => string =
    this.propertyProvider.searchPlaceProvider;

  @Input()
  searchResultListColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchResultListColor;

  //
  //#region ComponentSearchBar ...
  //
  @Input()
  showSearchBar: XStandardType<boolean> = this.propertyProvider.showSearchBar;

  //
  @Input()
  searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.searchBarActionProvider;

  @Input()
  searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.searchBarCssClass;

  //
  @Input()
  searchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarColor;

  @Input()
  searchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundColor;

  //
  @Input()
  searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchInputColor;

  @Input()
  searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundSearchInputColor;

  //
  @Input()
  searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarType;

  @Input()
  searchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarTitle;

  @Input()
  searchBarValue: XStandardType<string> = this.propertyProvider.searchBarValue;

  @Input()
  searchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.searchBarEnterKeyHint;

  @Input()
  searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarKeyboardType;

  @Input()
  searchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.searchBarSpellcheck;

  @Input()
  searchBarDebounce: XStandardType<number> =
    this.propertyProvider.searchBarDebounce;

  //
  @Input()
  searchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.searchBarShowCancelButton;

  @Input()
  searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarCancelButtonIcon;

  @Input()
  searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarCancelButtonTitle;

  @Input()
  searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarCancelButtonIconColor;

  //
  @Input()
  searchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.searchBarShowClearButton;

  @Input()
  searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarClearButtonIcon;

  @Input()
  searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarClearButtonIconColor;

  //
  @Input()
  searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarSearchButtonIcon;

  @Input()
  searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchButtonIconColor;

  //
  @Output()
  searchBarQueryChange: EventEmitter<string> = new EventEmitter();

  @Output()
  searchBarOnEvent: EventEmitter<string> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XMapContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(container?: IXMapContainer): Observable<IXMapContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            zoom: this.getValue(container ? container.zoom : this.zoom),
            latitude: this.getValue(
              container ? container.latitude : this.latitude
            ),
            longitude: this.getValue(
              container ? container.longitude : this.longitude
            ),
            canSelect: this.getValue(
              container ? container.canSelect : this.canSelect
            ),
            addMarkerOnSelectedLocation: this.getValue(
              container
                ? container.addMarkerOnSelectedLocation
                : this.addMarkerOnSelectedLocation
            ),
            markerStyle: this.getValue(
              container ? container.markerStyle : this.markerStyle
            ),
            showProgressBar: this.getValue(
              container ? container.showProgressBar : this.showProgressBar
            ),
            progressBarColor: this.getValue(
              container ? container.progressBarColor : this.progressBarColor
            ),
            progressBarPosition: this.getValue(
              container
                ? container.progressBarPosition
                : this.progressBarPosition
            ),
            showSelectedLocation: this.getValue(
              container
                ? container.showSelectedLocation
                : this.showSelectedLocation
            ),
            selectedLocationPosition: this.getValue(
              container
                ? container.selectedLocationPosition
                : this.selectedLocationPosition
            ),
            showGoMarkedPlace: this.getValue(
              container ? container.showGoMarkedPlace : this.showGoMarkedPlace
            ),
            goMarkedPlacePosition: this.getValue(
              container
                ? container.goMarkedPlacePosition
                : this.goMarkedPlacePosition
            ),
            showClearMarker: this.getValue(
              container ? container.showClearMarker : this.showClearMarker
            ),
            clearMarkerPosition: this.getValue(
              container
                ? container.clearMarkerPosition
                : this.clearMarkerPosition
            ),
            showLocator: this.getValue(
              container ? container.showLocator : this.showLocator
            ),
            locatorPosition: this.getValue(
              container ? container.locatorPosition : this.locatorPosition
            ),
            showZoom: this.getValue(
              container ? container.showZoom : this.showZoom
            ),
            showRotate: this.getValue(
              container ? container.showRotate : this.showRotate
            ),
            showZoomSlider: this.getValue(
              container ? container.showZoomSlider : this.showZoomSlider
            ),
            searchBarPosition: this.getValue(
              container ? container.searchBarPosition : this.searchBarPosition
            ),
            searchPlaceProvider: this.getValue(
              container
                ? container.searchPlaceProvider
                : this.searchPlaceProvider
            ),
            searchResultListColor: this.getValue(
              container
                ? container.searchResultListColor
                : this.searchResultListColor
            ),
            showSearchBar: this.getValue(
              container ? container.showSearchBar : this.showSearchBar
            ),
            searchBarCssClass: this.getValue(
              container ? container.searchBarCssClass : this.searchBarCssClass
            ),
            searchBarColor: this.getValue(
              container ? container.searchBarColor : this.searchBarColor
            ),
            searchBarForegroundColor: this.getValue(
              container
                ? container.searchBarForegroundColor
                : this.searchBarForegroundColor
            ),
            searchBarSearchInputColor: this.getValue(
              container
                ? container.searchBarSearchInputColor
                : this.searchBarSearchInputColor
            ),
            searchBarForegroundSearchInputColor: this.getValue(
              container
                ? container.searchBarForegroundSearchInputColor
                : this.searchBarForegroundSearchInputColor
            ),
            searchBarType: this.getValue(
              container ? container.searchBarType : this.searchBarType
            ),
            searchBarTitle: this.getValue(
              container ? container.searchBarTitle : this.searchBarTitle
            ),
            searchBarValue: this.getValue(
              container ? container.searchBarValue : this.searchBarValue
            ),
            searchBarEnterKeyHint: this.getValue(
              container
                ? container.searchBarEnterKeyHint
                : this.searchBarEnterKeyHint
            ),
            searchBarKeyboardType: this.getValue(
              container
                ? container.searchBarKeyboardType
                : this.searchBarKeyboardType
            ),
            searchBarSpellcheck: this.getValue(
              container
                ? container.searchBarSpellcheck
                : this.searchBarSpellcheck
            ),
            searchBarDebounce: this.getValue(
              container ? container.searchBarDebounce : this.searchBarDebounce
            ),
            searchBarShowCancelButton: this.getValue(
              container
                ? container.searchBarShowCancelButton
                : this.searchBarShowCancelButton
            ),
            searchBarCancelButtonIcon: this.getValue(
              container
                ? container.searchBarCancelButtonIcon
                : this.searchBarCancelButtonIcon
            ),
            searchBarCancelButtonTitle: this.getValue(
              container
                ? container.searchBarCancelButtonTitle
                : this.searchBarCancelButtonTitle
            ),
            searchBarCancelButtonIconColor: this.getValue(
              container
                ? container.searchBarCancelButtonIconColor
                : this.searchBarCancelButtonIconColor
            ),
            searchBarShowClearButton: this.getValue(
              container
                ? container.searchBarShowClearButton
                : this.searchBarShowClearButton
            ),
            searchBarClearButtonIcon: this.getValue(
              container
                ? container.searchBarClearButtonIcon
                : this.searchBarClearButtonIcon
            ),
            searchBarClearButtonIconColor: this.getValue(
              container
                ? container.searchBarClearButtonIconColor
                : this.searchBarClearButtonIconColor
            ),
            searchBarSearchButtonIcon: this.getValue(
              container
                ? container.searchBarSearchButtonIcon
                : this.searchBarSearchButtonIcon
            ),
            searchBarSearchButtonIconColor: this.getValue(
              container
                ? container.searchBarSearchButtonIconColor
                : this.searchBarSearchButtonIconColor
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            isReady: this.isReady,
            viewChanged: this.viewChanged,
            locationSelected: this.locationSelected,

            //
            searchBarActionProvider: container
              ? container.searchBarActionProvider
              : this.searchBarActionProvider,

            //
            searchBarQueryChange: this.searchBarQueryChange,
            searchBarOnEvent: this.searchBarOnEvent,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map-dialog\x-map-dialog.component.html

<section #mapDialogContainer class="x-map-dialog-container">
  <!-- Title Toolbar -->
  <section #mapDialogToolbar>
    <ion-toolbar class="v-toolbar" [xColor]="'primary'">
      <!-- Slots -->
      <div class="toolbar-area">
        <x-slotter>
          <!-- Start -->
          <x-slot
            [name]="SlotNames.START"
            [cssClass]="['ion-text-start', 'ion-float-start']"
          >
            <div
              matRipple
              (click)="handleBack()"
              class="toolbar-item toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="backIcon"
                [loading]="loading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>

          <!-- Center -->
          <x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
            <ion-title class="toolbar-item center-vertical-flex">
              <h3>
                {{ getTitle() | async }}
              </h3>
            </ion-title>
          </x-slot>

          <!-- End -->
          <x-slot
            [name]="SlotNames.END"
            [cssClass]="['ion-text-end', 'ion-float-end']"
          >
            <div
              matRipple
              (click)="handleSelect()"
              *ngIf="dialogCanSelect() && (canSelect | async)"
              class="toolbar-item toolbar-button center-vertical-flex"
            >
              <x-icon
                [name]="selectIcon"
                [loading]="loading"
                [color]="'cb-inherit'"
                [uiDisabled]="uiDisabled"
              ></x-icon>
            </div>
          </x-slot>
        </x-slotter>
      </div>
    </ion-toolbar>
  </section>

  <!-- Map Component -->
  <x-map
    [isInPage]="false"
    [zoom]="zoom | async"
    [loading]="loading"
    [wrapWithCard]="false"
    [showSearchBar]="true"
    [uiDisabled]="uiDisabled"
    [latitude]="latitude | async"
    [cssClass]="cssClass | async"
    [showZoom]="showZoom | async"
    (isReady)="handleMapIsReady()"
    [longitude]="longitude | async"
    [canSelect]="canSelect | async"
    [showRotate]="showRotate | async"
    [actionProvider]="actionProvider"
    [showLocator]="showLocator | async"
    [markerStyle]="markerStyle | async"
    [searchBarType]="searchBarType | async"
    [searchBarValue]="searchBarValue | async"
    [searchBarColor]="searchBarColor | async"
    [searchBarTitle]="searchBarTitle | async"
    [searchBarColor]="searchBarColor | async"
    [showZoomSlider]="showZoomSlider | async"
    [searchBarPosition]="MapItemPositions.TOP"
    [showProgressBar]="showProgressBar | async"
    [showClearMarker]="showClearMarker | async"
    [locatorPosition]="locatorPosition | async"
    [progressBarPosition]="MapItemPositions.TOP"
    [progressBarColor]="progressBarColor | async"
    [searchBarForegroundSearchInputColor]="
      searchBarForegroundSearchInputColor | async
    "
    [searchBarForegroundSearchInputColor]="
      searchBarForegroundSearchInputColor | async
    "
    [showGoMarkedPlace]="showGoMarkedPlace | async"
    [searchBarDebounce]="searchBarDebounce | async"
    [searchBarCssClass]="searchBarCssClass | async"
    [searchBarSpellcheck]="searchBarSpellcheck | async"
    [searchPlaceProvider]="searchPlaceProvider | async"
    [clearMarkerPosition]="clearMarkerPosition | async"
    [showSelectedLocation]="showSelectedLocation | async"
    (locationSelected)="handleMapLocationSelected($event)"
    [goMarkedPlacePosition]="goMarkedPlacePosition | async"
    [searchBarKeyboardType]="searchBarKeyboardType | async"
    [searchBarEnterKeyHint]="searchBarEnterKeyHint | async"
    [searchResultListColor]="searchResultListColor | async"
    (searchBarQueryChange)="handleSearchBarQueryChange($event)"
    [searchBarActionProvider]="searchBarActionProvider | async"
    [selectedLocationPosition]="selectedLocationPosition | async"
    [searchBarClearButtonIcon]="searchBarClearButtonIcon | async"
    [searchBarForegroundColor]="searchBarForegroundColor | async"
    [searchBarSearchButtonIcon]="searchBarSearchButtonIcon | async"
    [searchBarSearchInputColor]="searchBarSearchInputColor | async"
    [searchBarShowCancelButton]="searchBarShowCancelButton | async"
    [searchBarCancelButtonIcon]="searchBarCancelButtonIcon | async"
    [searchBarClearButtonIconColor]="searchBarClearIconColor | async"
    [searchBarCancelButtonTitle]="searchBarCancelButtonTitle | async"
    [searchBarSearchButtonIconColor]="searchBarSearchButtonIconColor | async"
  >
  </x-map>
</section>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map-dialog\x-map-dialog.component.scss

:host {
  --x-map-height: 200px;
}

::ng-deep.fullscreen-dialog {
  //
  width: 80vw !important;
  max-width: 80vw !important;

  //
  height: 80vh !important;
  max-height: 80vh !important;

  //
  &.mobile {
    //
    width: 100vw !important;
    max-width: 100vw !important;

    //
    height: 100vh !important;
    max-height: 100vh !important;
  }

  //
  .mat-dialog-container {
    padding: 0 !important;
  }
}

:host ::ng-deep x-map {
  //
  --map-height: var(--x-map-height);
}

.x-map-dialog-container {
  height: 100%;
}

x-icon {
  --size: 24px;
}

.toolbar-area {
  margin: 8px;
}

.toolbar-item {
  width: auto;
  height: 48px;
}

.toolbar-button {
  width: 32px;
  margin: {
    left: 4px;
    right: 4px;
  }
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map-dialog\x-map-dialog.component.ts

import {
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  ChangeDetectorRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XColor,
  isFunction,
  XResourceIDs,
  XDialogResult,
  isNullOrUndefined,
  isNullOrEmptyString,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XMapAction,
  XMapLocation,
  XMapActionModel,
  XMapItemPosition,
  defaultMarkerStyle,
  defaultMapSearchPlaceProvider,
} from '../x-map.typings';
import { ViewChild } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import {
  XSearchBarType,
  XSearchBarShowButton,
  XSearchBarActionModel,
  XSearchBarEnterKeyHint,
} from '../../x-search-bar/x-search-bar.typings';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XBasePage } from '../../classes/x-base-page';
import { XManagerService } from 'x-framework-services';
import { XIconNames } from '../../x-icon/x-icon.typings';
import { XSlotName } from '../../x-slotter/x-slotter.typings';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { XMapDialogConfig, XMapdialogCssVar } from './x-map-dialog.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-map-dialog',
  templateUrl: './x-map-dialog.component.html',
  styleUrls: ['./x-map-dialog.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XMapDialogComponent extends XBasePage {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly selectIcon = XIconNames.checked;
  readonly backIcon = XIconNames.arrow_back;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly MapItemPositions = Object.assign({}, XMapItemPosition);

  //
  hasMarker: boolean;
  selectedLocation: XMapLocation;
  actionProvider = new Subject<XMapActionModel>();

  //
  @ViewChild('mapDialogContainer')
  mapDialogContainer: ElementRef<HTMLElement>;

  @ViewChild('mapDialogToolbar')
  mapDialogToolbar: ElementRef<HTMLElement>;
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    @Inject(MAT_DIALOG_DATA) public data: any,
    public dialogRef: MatDialogRef<XMapDialogComponent>
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    this.preventBack = true;
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    await this.applyMapSize();
  }

  public registerViewHandlers(): void {
    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider.subscribe(
      (action) => {
        switch (action.action) {
          //
          case XMapAction.HasMarker:
            //
            this.hasMarker = true;
            this.detectChanges();
            break;

          //
          case XMapAction.HasNotMarker:
            this.hasMarker = false;
            this.detectChanges();
            break;
        }
      }
    );
  }

  public onDestroy(): void {
    //
    if (!isNullOrUndefined(this.ACTION_PROVIDER_SUBSCRIPTION)) {
      //
      this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
      this.ACTION_PROVIDER_SUBSCRIPTION = null;
    }
  }
  //#endregion

  //
  //#region UI Providers ...
  /**
   * retrieve map dialog config ...
   */
  getData() {
    return this.getValue(this.data as XMapDialogConfig);
  }

  /**
   * determines map dialog config exists or not ...
   */
  hasData() {
    return !isNullOrUndefined(this.getData());
  }

  /**
   * determines dialog can select result or not ...
   */
  dialogCanSelect() {
    return (
      this.hasMarker &&
      !isNullOrUndefined(this.selectedLocation) &&
      !isNullOrEmptyString(this.selectedLocation.latitude) &&
      !isNullOrEmptyString(this.selectedLocation.longitude)
    );
  }

  /**
   * retrieve dialog title ...
   */
  getTitle() {
    return this.getData().pipe(
      concatMap((config) => this.getResourceValue(config.title))
    );
  }

  //
  get markerStyle() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.markerStyle)),
      map((style) => (style ? style : defaultMarkerStyle))
    );
  }

  get cssClass() {
    return this.getData().pipe(
      concatMap((config) => this.getArrayValue(config.cssClass))
    );
  }

  get progressBarColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.progressBarColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Warning))
    );
  }

  get canSelect() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getValue(config.canSelect ? config.canSelect : true)
      )
    );
  }

  //
  get latitude() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.latitude))
    );
  }

  get longitude() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.longitude))
    );
  }

  //
  get zoom() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.zoom))
    );
  }

  //
  get showRotate() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showRotate)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get showZoom() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showZoom)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get showZoomSlider() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showZoomSlider)),
      map((canShow) => (isNullOrUndefined(canShow) ? false : canShow))
    );
  }

  get showLocator() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showLocator)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get showClearMarker() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showClearMarker)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get showGoMarkedPlace() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showGoMarkedPlace)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get showSelectedLocation() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showSelectedLocation)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get showSearchBar() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showSearchBar)),
      map((canShow) => (isNullOrUndefined(canShow) ? true : canShow))
    );
  }

  get searchResultListColor() {
    return this.getData().pipe(
      concatMap((config) => this.getValue(config.searchResultListColor)),
      map((color) => (!isNullOrEmptyString(color) ? color : XColor.Light))
    );
  }

  // //
  // get isInPage() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getOptionalValue(config.isInPage))
  //   );
  // }

  // get wrapWithCard() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getOptionalValue(config.wrapWithCard))
  //   );
  // }

  // get cardColor() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getValue(config.progressBarColor)),
  //     map((color) => (!isNullOrEmptyString(color) ? color : XColor.Light))
  //   );
  // }

  // get cardForegroundColor() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getOptionalValue(config.cardForegroundColor))
  //   );
  // }

  // get cardTitle() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getResourceValue(config.cardTitle))
  //   );
  // }

  // get cardSubTitle() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getResourceValue(config.cardSubTitle))
  //   );
  // }

  // get cardShowFooter() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getOptionalValue(config.cardShowFooter))
  //   );
  // }

  //
  get showProgressBar() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.showProgressBar)),
      map((result) => (isNullOrUndefined(result) ? true : result))
    );
  }

  // get progressBarPosition() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getOptionalValue(config.progressBarPosition)),
  //     map((result) =>
  //       isNullOrEmptyString(result) ? XMapItemPosition.TOP : result
  //     )
  //   );
  // }

  get locatorPosition() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.locatorPosition)),
      map((result) =>
        isNullOrEmptyString(result) ? XMapItemPosition.TOP : result
      )
    );
  }

  get clearMarkerPosition() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.clearMarkerPosition)),
      map((result) =>
        isNullOrEmptyString(result) ? XMapItemPosition.TOP : result
      )
    );
  }

  get goMarkedPlacePosition() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.goMarkedPlacePosition)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XMapItemPosition.TOP : result
      )
    );
  }

  get selectedLocationPosition() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.selectedLocationPosition)
      ),
      map((result) =>
        isNullOrEmptyString(result) ? XMapItemPosition.TOP : result
      )
    );
  }

  //
  //#region Search ...
  get searchPlaceProvider() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchPlaceProvider)),
      map((provider) =>
        !provider || !isFunction(provider)
          ? defaultMapSearchPlaceProvider
          : provider
      )
    );
  }

  get searchBarType() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarType)),
      map((data) => (isNullOrEmptyString(data) ? XSearchBarType.Search : data))
    );
  }

  get searchBarValue() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarValue))
    );
  }

  get searchBarColor() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarColor)),
      map((data) =>
        isNullOrEmptyString(data) ? XColorWithBrightness.Primary : data
      )
    );
  }

  get searchBarTitle() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarTitle)),
      map((data) =>
        !data || isNullOrEmptyString(data.toString())
          ? XResourceIDs.search
          : data
      )
    );
  }

  // get searchBarPosition() {
  //   return this.getData().pipe(
  //     concatMap((config) => this.getOptionalValue(config.searchBarPosition)),
  //     map((data) => (isNullOrEmptyString(data) ? XMapItemPosition.TOP : data))
  //   );
  // }

  get searchBarCssClass() {
    return this.getData().pipe(
      concatMap((config) => this.getArrayValue(config.searchBarCssClass))
    );
  }

  get searchBarDebounce() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarDebounce)),
      map((data) =>
        isNullOrUndefined(data) ? this.config.defaultSearchDebounceTime : data
      )
    );
  }

  get searchBarClearButtonIcon() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarClearButtonIcon)
      ),
      map((data) =>
        !data || isNullOrEmptyString(data.toString()) ? XIconNames.clear : data
      )
    );
  }

  get searchBarSearchInputColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarSearchInputColor)
      ),
      map((data) =>
        isNullOrEmptyString(data) ? XColorWithBrightness.Primary : data
      )
    );
  }

  get searchBarSpellcheck() {
    return this.getData().pipe(
      concatMap((config) => this.getOptionalValue(config.searchBarSpellcheck))
    );
  }

  get searchBarSearchButtonIcon() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarSearchButtonIcon)
      ),
      map((data) =>
        !data || isNullOrEmptyString(data.toString()) ? XIconNames.search : data
      )
    );
  }

  get searchBarKeyboardType() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarKeyboardType)
      ),
      map((data) => (isNullOrEmptyString(data) ? XSearchBarType.Search : data))
    );
  }

  get searchBarEnterKeyHint() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarEnterKeyHint)
      ),
      map((data) =>
        isNullOrEmptyString(data) ? XSearchBarEnterKeyHint.Search : data
      )
    );
  }

  get searchBarClearIconColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarClearButtonIconColor)
      ),
      map((data) =>
        isNullOrEmptyString(data) ? XColorWithBrightness.Light : data
      )
    );
  }

  get searchBarActionProvider() {
    return this.getData().pipe(
      map((data) =>
        !isNullOrUndefined(data.searchBarActionProvider)
          ? data.searchBarActionProvider
          : new Subject<XSearchBarActionModel>()
      )
    );
  }

  get searchBarSearchButtonIconColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarSearchButtonIconColor)
      ),
      map((data) =>
        isNullOrEmptyString(data) ? XColorWithBrightness.Light : data
      )
    );
  }

  get searchBarForegroundColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarForegroundColor)
      )
    );
  }

  get searchBarShowCancelButton() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarShowCancelButton)
      ),
      map((data) =>
        isNullOrEmptyString(data) ? XSearchBarShowButton.Never : data
      )
    );
  }

  get searchBarCancelButtonIcon() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarCancelButtonIcon)
      ),
      map((data) =>
        !data || isNullOrEmptyString(data.toString()) ? XIconNames.cancel : data
      )
    );
  }

  get searchBarCancelButtonTitle() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getResourceValue(config.searchBarCancelButtonTitle)
      )
    );
  }

  get searchBarForegroundSearchInputColor() {
    return this.getData().pipe(
      concatMap((config) =>
        this.getOptionalValue(config.searchBarForegroundSearchInputColor)
      )
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleBack() {
    this.dialogRef.close();
  }

  async handleSelect() {
    //
    if (!this.dialogCanSelect()) {
      return;
    }

    //
    this.dialogRef.close({
      result: XDialogResult.Ok,
      payload: this.selectedLocation,
    });
  }

  async handleMapIsReady() {
    //
    const config = await this.getValueAsync(this.getData());
    if (!config || !config.isReady) {
      return;
    }

    //
    config.isReady.emit();
  }

  async handleSearchBarQueryChange(event: string) {
    //
    const config = await this.getValueAsync(this.getData());
    if (!config || !config.searchBarQueryChange) {
      return;
    }

    //
    config.searchBarQueryChange.emit(event);
  }

  async handleMapLocationSelected(event: XMapLocation) {
    //
    this.selectedLocation = event;

    //
    const config = await this.getValueAsync(this.getData());
    if (!config || !config.locationSelected) {
      return;
    }

    //
    config.locationSelected.emit(event);

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  async applyMapSize() {
    //
    let result = 0;

    //
    //#region DialogHeight ...
    //
    if (!this.mapDialogContainer) {
      //
      setTimeout(async () => await this.applyMapSize(), 500);
      return;
    }
    const height =
      this.mapDialogContainer.nativeElement.getBoundingClientRect().height;
    if (height === 0) {
      //
      setTimeout(async () => await this.applyMapSize(), 500);
      return;
    }

    //
    result += height;
    //#endregion

    //
    //#region ToolbarHeight ...
    if (!this.mapDialogToolbar) {
      //
      setTimeout(async () => await this.applyMapSize(), 500);
      return;
    }
    const toolbarHeight =
      this.mapDialogToolbar.nativeElement.getBoundingClientRect().height;
    if (toolbarHeight === 0) {
      //
      setTimeout(async () => await this.applyMapSize(), 500);
      return;
    }

    //
    result -= toolbarHeight;
    //#endregion

    //
    this.setCssVar(XMapdialogCssVar.XMapHeight, `${result}px`);
    this.detectChanges();

    //
    this.runTimedOut(() => {
      this.managerService.dispatchResizeEvent();
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map-dialog\x-map-dialog.service.ts

import {
  XColor,
  toPromise,
  XBaseService,
  XResourceIDs,
  XDialogResult,
  isNullOrUndefined,
} from 'x-framework-core';
import {
  MatDialog,
  MatDialogRef,
  MatDialogConfig,
} from '@angular/material/dialog';
import { XManagerService } from 'x-framework-services';
import { XMapLocation } from '../x-map.typings';
import { XMapDialogComponent } from './x-map-dialog.component';
import { Injectable, Output, EventEmitter, Inject } from '@angular/core';
import { XMapDialogResult, XMapDialogConfig } from './x-map-dialog.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

@Injectable({
  providedIn: 'root',
})
export class XMapDialogService extends XBaseService {
  //
  //#region Props ...
  private MOBILE_CLASS_NAME = 'mobile';
  private DIALOG_REF: MatDialogRef<XMapDialogComponent>;

  //
  isMobile$ = this.managerService.isMobileUi$;

  // tslint:disable-next-line:no-output-on-prefix
  @Output()
  onResult = new EventEmitter<XMapDialogResult>();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    private dialog: MatDialog,
    private managerService: XManagerService
  ) {
    super(config);
  }
  //#endregion

  //
  //#region Actions ...
  /**
   * Open Select Map Dialog ...
   */
  async openDialog(config?: XMapDialogConfig) {
    //
    // Prevent Double Bubble ...
    if (this.DIALOG_REF) {
      return;
    }

    //
    // Prepare Config ...
    if (!config) {
      config = {};
    }

    //
    //#region Default Config for Map Component ...
    //
    // Zoom ...
    if (config && !config.zoom) {
      config.zoom = 6;
    }

    //
    // canSelect ...
    let selectable = false;
    if (config && !isNullOrUndefined(config.canSelect)) {
      //
      selectable = await toPromise(config.canSelect);
      config.canSelect = selectable;
    }

    //
    // Set Default Title ...
    if (config && !config.title) {
      //
      if (selectable) {
        config.title = XResourceIDs.map_dialog_select_title;
      } else {
        config.title = XResourceIDs.map_dialog_title;
      }
    }

    //
    // Search Bar Color ...
    if (config && !config.searchBarColor) {
      config.searchBarColor = XColor.Primary;
    }

    //
    // Progress Bar Color ...
    if (config && !config.progressBarColor) {
      config.progressBarColor = XColor.Warning;
    }

    //
    // Search Result List Color ...
    if (config && !config.searchResultListColor) {
      config.searchResultListColor = XColor.Medium;
    }
    //#endregion

    //
    // Prepare Dialog Config ...
    const data: MatDialogConfig = {
      data: config,
    };

    //
    data.panelClass = 'fullscreen-dialog';

    //
    // Show Cropper inside a Dialog ...
    this.DIALOG_REF = this.dialog.open(XMapDialogComponent, data);
    this.DIALOG_REF.disableClose = true;

    //
    // Attach Mobile Class ...
    this.isMobile$.subscribe((isMobile) => {
      //
      if (isMobile) {
        if (this.DIALOG_REF) {
          this.DIALOG_REF.addPanelClass(this.MOBILE_CLASS_NAME);
        }
      } else {
        if (this.DIALOG_REF) {
          this.DIALOG_REF.removePanelClass(this.MOBILE_CLASS_NAME);
        }
      }
    });

    //
    // Handle Backdrop ...
    this.DIALOG_REF.backdropClick().subscribe(() => {
      //
      // Cancel Dialog ...
      this.onResult.emit({ result: XDialogResult.Cancel });

      //
      this.closeDialog();
    });

    //
    // Handle Dialog Result ...
    this.DIALOG_REF.afterClosed().subscribe((model: XMapDialogResult) => {
      //
      if (isNullOrUndefined(model) || model.result !== XDialogResult.Ok) {
        //
        // Cancel Dialog ...
        this.onResult.emit({ result: XDialogResult.Cancel });
      } else {
        //
        // Handle Results ...
        this.onResult.emit(model);
      }

      //
      this.closeDialog();
    });
  }

  /**
   * Close Select Map Dialog ...
   */
  closeDialog() {
    //
    // Prevent Go Forward ...
    if (!this.DIALOG_REF) {
      return;
    }

    //
    // Close Dialog ...
    this.DIALOG_REF.close();
    this.DIALOG_REF = null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-map\x-map-dialog\x-map-dialog.typings.ts

import { XDialogResult } from 'x-framework-core';
import { XMapLocation, IXMapContainer } from '../x-map.typings';
import { XBaseDialogConfig } from '../../classes/i-x-base-dialog.container';

export interface XMapDialogResult {
  payload?: XMapLocation;
  result: XDialogResult;
}

export interface XMapDialogConfig
  extends XBaseDialogConfig,
    Pick<
      Partial<IXMapContainer>,
      | 'zoom'
      | 'isReady'
      | 'latitude'
      | 'cssClass'
      | 'showZoom'
      | 'longitude'
      | 'canSelect'
      | 'showRotate'
      | 'showLocator'
      | 'markerStyle'
      | 'showSearchBar'
      | 'searchBarType'
      | 'actionProvider'
      | 'searchBarValue'
      | 'searchBarColor'
      | 'searchBarTitle'
      | 'searchBarColor'
      | 'showZoomSlider'
      | 'showProgressBar'
      | 'showClearMarker'
      | 'locatorPosition'
      | 'progressBarColor'
      | 'locationSelected'
      | 'searchBarCssClass'
      | 'showGoMarkedPlace'
      | 'searchBarDebounce'
      | 'searchPlaceProvider'
      | 'clearMarkerPosition'
      | 'searchBarSpellcheck'
      | 'searchBarQueryChange'
      | 'showSelectedLocation'
      | 'goMarkedPlacePosition'
      | 'searchBarKeyboardType'
      | 'searchBarEnterKeyHint'
      | 'searchResultListColor'
      | 'searchBarActionProvider'
      | 'searchBarClearButtonIcon'
      | 'searchBarForegroundColor'
      | 'selectedLocationPosition'
      | 'searchBarForegroundColor'
      | 'searchBarSearchButtonIcon'
      | 'searchBarSearchInputColor'
      | 'searchBarSearchInputColor'
      | 'searchBarShowCancelButton'
      | 'searchBarCancelButtonIcon'
      | 'searchBarCancelButtonTitle'
      | 'searchBarClearButtonIconColor'
      | 'searchBarSearchButtonIconColor'
      | 'searchBarForegroundSearchInputColor'
    > {}

export enum XMapdialogCssVar {
  XMapHeight = 'x-map-height',
}

export type XMapdialogCssVarIdentifier = XMapdialogCssVar | string;

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-markdown\x-markdown.component.html

<!-- WrapWithCard Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentRef">
  <x-card
    class="x-card"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [showActions]="getValue(cardShowActions) | async"
    [subTitle]="getResourceValue(cardSubTitle) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
    [showHeader]="
      (notEmptyValue(getResourceValue(cardTitle) | async) | async) ||
      (notEmptyValue(getResourceValue(cardSubTitle) | async) | async)
    "
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component Template -->
<ng-template #componentRef>
  <div
    *ngIf="preparedMode"
    class="x-markdown x-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <!-- Toolbar -->
    <div
      class="x-toolbar-wrapper"
      *ngIf="
        (getValueStream(hasToolbar) | async) &&
        ((isEditMode() | async) || (isBothMode() | async))
      "
    >
      <ion-toolbar
        class="x-editor-toolbar"
        [xColor]="getValue(toolbarColor) | async"
        [ngClass]="getValue(toolbarCssClass) | async"
        [isForeground]="getValue(toolbarForegroundColor) | async"
      >
        <!-- Start Slot -->
        <ion-buttons slot="start" *ngIf="preparedToolbarConfig">
          <ng-container
            *ngTemplateOutlet="toolbarRef; context: { $implicit: 'start' }"
          ></ng-container>
        </ion-buttons>

        <!-- End Slot -->
        <ion-buttons slot="end" *ngIf="preparedToolbarConfig">
          <ng-container
            *ngTemplateOutlet="toolbarRef; context: { $implicit: 'end' }"
          ></ng-container>
        </ion-buttons>
      </ion-toolbar>
    </div>

    <!-- Content -->
    <x-slotter
      [hasCenterSlot]="false"
      [layout]="getSlotterLayout() | async"
      [hasStartOrTopSlot]="(isEditMode() | async) || (isBothMode() | async)"
      [hasEndOrBottomSlot]="(isPreviewMode() | async) || (isBothMode() | async)"
    >
      <!-- Edit Area -->
      <x-slot [name]="'start'">
        <div class="x-editor-wrapper">
          <!-- Editor -->
          <textarea
            #markdownEditor
            class="x-editor"
            [(ngModel)]="innerContent"
            (keyup)="handleContentChanged(innerContent)"
            (change)="handleContentChanged(innerContent)"
            (ngModelChange)="handleContentChanged(innerContent)"
            [xColor]="(getValue(editorBoxColor) | async) || 'cb-inherit'"
          ></textarea>
        </div>
      </x-slot>

      <!-- Preview Area -->
      <x-slot [name]="'end'">
        <div
          class="x-preview-wrapper"
          [class.no-border]="isPreviewMode() | async"
          [xColor]="(getValue(previewBoxColor) | async) || 'cb-inherit'"
        >
          <markdown class="x-preview" [data]="innerContent || ' '"> </markdown>
        </div>
      </x-slot>
    </x-slotter>
  </div>
</ng-template>

<!-- Toolbar Template Ref -->
<ng-template #toolbarRef let-slot>
  <ng-container *ngIf="preparedToolbarConfig">
    <ng-container *ngFor="let name of toolbarItems">
      <ng-container
        *ngIf="hasToolbarItem(name) && getToolbarItem(name).slot === slot"
      >
        <ng-container
          *ngTemplateOutlet="
            name === 'guide' ? guideToolbarItemRef : toolbarItemRef;
            context: { $implicit: name }
          "
        ></ng-container>
      </ng-container>
    </ng-container>
  </ng-container>
</ng-template>

<!-- Toolbar Guide Item Template Ref -->
<ng-template #guideToolbarItemRef let-name>
  <a
    target="_blank"
    style="text-decoration: none; color: unset"
    href="https://guides.github.com/features/mastering-markdown/"
  >
    <x-icon
      [clickable]="false"
      [selectable]="true"
      [loading]="loading"
      class="x-toolbar-icon"
      [color]="'transparent'"
      [uiDisabled]="uiDisabled"
      [foregroundColor]="false"
      [name]="getValue(getToolbarItem(name).icon)"
      [matTooltip]="getResourceValue(getToolbarItem(name).title) | async"
    ></x-icon>
  </a>
</ng-template>

<!-- Toolbar Item Template Ref -->
<ng-template #toolbarItemRef let-name>
  <x-icon
    [clickable]="true"
    [selectable]="true"
    [loading]="loading"
    class="x-toolbar-icon"
    [color]="'transparent'"
    [uiDisabled]="uiDisabled"
    [foregroundColor]="false"
    (clicked)="handleToolbarItemClicked(name)"
    [name]="getValue(getToolbarItem(name).icon)"
    [matTooltip]="getResourceValue(getToolbarItem(name).title) | async"
  ></x-icon>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-markdown\x-markdown.component.scss

:host ::ng-deep a {
    pointer-events: none;
}

:host {
  width: inherit;
  height: inherit;
}

textarea {
  resize: none;
}

.x-editor {
  width: 100%;
  min-height: 300px;
  height: fit-content;
}

.x-toolbar-wrapper {
  margin-bottom: 0px;
}

.x-preview {
  text-align: justify;
}

.x-preview-wrapper {
  height: fit-content;
  min-height: 300px;
  border: 1px solid gray;
  padding: {
    left: 4px;
    right: 4px;
  }
}

.x-toolbar-icon {
  margin: {
    left: 4px;
    right: 4px;
  }
}

.no-border {
  border: 0;
  min-height: unset;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-markdown\x-markdown.component.ts

import {
  ViewChild,
  Component,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  keys,
  nameof,
  insertText,
  XStandardType,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XMarkdownMode,
  XMarkdownAction,
  XMarkdownActionModel,
  XMarkdownToolbarItem,
  XMarkdownModeIdentifier,
  XMarkdownToolbarItemName,
  XMarkdownToolbarConfigProps,
  XMarkdownContainerComponent,
  defaultXMarkdownToolbarConfig,
} from './x-markdown.typings';
import { map } from 'rxjs/operators';
import 'node_modules/prismjs/prism.js';
import 'node_modules/marked/marked.min.js';
import { Subject, Subscription } from 'rxjs';
import { XSlotLayout } from '../x-slotter/x-slotter.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-markdown',
  templateUrl: './x-markdown.component.html',
  styleUrls: ['./x-markdown.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XMarkdownComponent extends XMarkdownContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private TOOLBAR_CONFIG: XMarkdownToolbarConfigProps;

  //
  readonly SlotterLayouts = Object.assign({}, XSlotLayout);

  //
  innerContent: string = this.content;
  preparedMode: XMarkdownModeIdentifier;
  toolbarItems = keys(defaultXMarkdownToolbarConfig);

  //
  get preparedToolbarConfig() {
    return this.TOOLBAR_CONFIG;
  }

  //
  @ViewChild('markdownEditor', { static: false })
  editor: ElementRef;
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.prepareMode();
    await this.prepareToolbarConfig(null);
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XMarkdownComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Content Chaned ...
    const isContentChanged = changeKeys.includes(
      nameof<XMarkdownComponent>('content')
    );
    if (isContentChanged && this.content !== this.innerContent) {
      this.innerContent = this.content;
      this.detectChanges();
    }
    //#endregion

    //
    //#region Model Changed ...
    const isModeChanged = changeKeys.includes(
      nameof<XMarkdownComponent>('mode')
    );
    if (isModeChanged) {
      await this.prepareMode();
      await this.prepareToolbarConfig(null);
    }
    //#endregion
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    // Register Action Provider ...
    this.registerActionProvider();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //
  getSlotterLayout() {
    return this.isMobileUi$.pipe(
      map((isMobileUi) =>
        isMobileUi ? XSlotLayout.VERTICAL : XSlotLayout.HORIZONTAL
      )
    );
  }

  //
  //#region Mode Detectors ...
  //
  isEditMode() {
    return this.getValueStream(this.mode).pipe(
      map((preparedMode) => preparedMode === XMarkdownMode.EDIT)
    );
  }

  //
  isPreviewMode() {
    return this.getValueStream(this.mode).pipe(
      map((preparedMode) => preparedMode === XMarkdownMode.PREVIEW)
    );
  }

  //
  isBothMode() {
    return this.getValueStream(this.mode).pipe(
      map((preparedMode) => preparedMode === XMarkdownMode.BOTH)
    );
  }
  //#endregion

  //
  //#region Toolbar Providers ...
  hasToolbarItem(name: XMarkdownToolbarItemName | any) {
    //
    if (!this.TOOLBAR_CONFIG) {
      return false;
    }

    //
    const itesNamess = keys(this.TOOLBAR_CONFIG);
    const result = itesNamess.includes(name);

    //
    return result;
  }

  getToolbarItem(name: XMarkdownToolbarItemName | any) {
    //
    if (!this.hasToolbarItem(name)) {
      return null;
    }

    //
    const item = this.TOOLBAR_CONFIG[name];
    const defaultItem = this.getDefaultItemConfig(name);

    //
    const result: XMarkdownToolbarItem = {
      ...defaultItem,
      ...item,
    };

    //
    return result;
  }
  //#endregion
  //#endregion

  //
  //#region Ui Handlers ...
  handleContentChanged(content: string) {
    //
    if (!content) {
      content = '';
    }

    //
    this.innerContent = content;
    this.contentChange.emit(content);
  }

  async handleToolbarItemClicked(name: XMarkdownToolbarItemName) {
    //
    switch (name) {
      //
      case 'preview':
        await this.handleToggleMode();
        break;

      //
      case 'bold':
        await this.handleAddContent('**', true);
        break;

      //
      case 'code':
        await this.handleAddContent('`', true);
        break;

      //
      case 'heading':
        await this.handleAddContent('# ', false);
        break;

      //
      case 'image':
        await this.handleAddContent('![alt text](image url)', false);
        break;

      //
      case 'italic':
        await this.handleAddContent('*', true);
        break;

      //
      case 'link':
        await this.handleAddContent('[title](url)', false);
        break;

      //
      case 'ol':
        await this.handleAddContent('1. ', false);
        break;

      //
      case 'ul':
        await this.handleAddContent('- ', false);
        break;

      //
      case 'quote':
        await this.handleAddContent('> ', false);
        break;
    }

    //
    if (this.editor) {
      this.editor.nativeElement.focus();
    }
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XMarkdownActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XMarkdownAction.Refresh:
            this.detectChanges();
            break;

          //
          case XMarkdownAction.FireToolbarButtons:
            //
            if (!isNullOrEmptyString(model.payload)) {
              await this.handleToolbarItemClicked(model.payload);
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async prepareMode() {
    this.preparedMode = await this.getValueAsync(this.mode);
    await this.changeMode(this.preparedMode);
  }

  private async prepareToolbarConfig(
    config: XStandardType<XMarkdownToolbarConfigProps>
  ) {
    //
    if (!config) {
      this.TOOLBAR_CONFIG = defaultXMarkdownToolbarConfig;
      return;
    }

    //
    const cfg = await this.getValueAsync(config);

    //
    this.TOOLBAR_CONFIG = {
      ...cfg,
    };

    //
    this.detectChanges();
  }

  private getDefaultItemConfig(name: XMarkdownToolbarItemName) {
    return defaultXMarkdownToolbarConfig[name];
  }

  private async changeMode(mode: XMarkdownModeIdentifier) {
    //
    this.mode = mode;
    this.preparedMode = mode;

    //
    this.detectChanges();
  }

  private async handleToggleMode() {
    //
    const mode = await this.getValueAsync(this.mode);

    //
    switch (mode) {
      case XMarkdownMode.BOTH:
        await this.changeMode(XMarkdownMode.EDIT);
        break;

      case XMarkdownMode.EDIT:
        await this.changeMode(XMarkdownMode.BOTH);
        break;
    }
  }

  private getCurrentPosition() {
    //
    if (!this.editor) {
      return {
        start: -1,
        end: -1,
      };
    }

    //
    const start = this.editor.nativeElement.selectionStart;
    const end = this.editor.nativeElement.selectionEnd;

    //
    const result = {
      start: start ? +start : -1,
      end: end ? +end : -1,
    };

    //
    return result;
  }

  private async handleAddContent(content: string, double: boolean = false) {
    //
    if (!content) {
      return;
    }

    //
    const pos = this.getCurrentPosition();

    //
    if (!pos) {
      return;
    }

    //
    let selectedContent: string;
    if (pos.start === pos.end) {
      selectedContent = '';
    } else {
      //
      selectedContent = this.innerContent.substring(pos.start, pos.end);
      this.innerContent = this.innerContent.replace(selectedContent, '');
    }

    //
    let insertedContent: string;
    if (isNullOrEmptyString(selectedContent)) {
      insertedContent = double ? `${content}${content}` : `${content}`;
    } else {
      insertedContent = double
        ? `${content}${selectedContent}${content}`
        : `${content}${selectedContent}`;
    }

    //
    const startPos = pos.start > -1 ? pos.start : this.innerContent.length;
    this.innerContent = insertText(
      this.innerContent || '',
      insertedContent,
      startPos
    );
    this.contentChange.emit(this.innerContent);
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-markdown\x-markdown.module.ts

import { NgModule } from '@angular/core';
import { MarkdownModule } from 'ngx-markdown';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from './../x-icon/x-icon.module';
import { XMarkdownComponent } from './x-markdown.component';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XMarkdownComponent],
  imports: [
    CommonModule,
    XIconModule,
    XCardModule,
    XSlotterModule,
    XBaseComponentModule,
    MarkdownModule.forRoot(),
  ],
  exports: [
    XIconModule,
    XCardModule,
    MarkdownModule,
    XSlotterModule,
    XMarkdownComponent,
    XBaseComponentModule,
  ],
})
export class XMarkdownModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-markdown\x-markdown.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XIconIdentifier, XIconNames } from '../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XMarkdownMode {
  EDIT = 'edit',
  PREVIEW = 'preview',
  BOTH = 'both',
}

export type XMarkdownModeIdentifier = XMarkdownMode | string;

export enum XMarkdownToolbarItemSlot {
  START = 'start',
  END = 'end',
}

export type XMarkdownToolbarItemSlotIdentifier =
  | XMarkdownToolbarItemSlot
  | string;

export type XMarkdownToolbarItemName =
  | 'image'
  | 'link'
  | 'ul'
  | 'ol'
  | 'quote'
  | 'code'
  | 'bold'
  | 'italic'
  | 'heading'
  | 'preview'
  | 'guide';

export interface XMarkdownToolbarItem {
  icon?: XIconIdentifier;
  title?: XResourceIdentifier;
  slot?: XMarkdownToolbarItemSlotIdentifier;
}

export type XMarkdownToolbarConfigProps = {
  [key in XMarkdownToolbarItemName]?: XMarkdownToolbarItem;
};

export const defaultXMarkdownToolbarConfig: XMarkdownToolbarConfigProps = {
  heading: {
    icon: XIconNames.heading,
    title: XResourceIDs.x_markdown_heading,
    slot: XMarkdownToolbarItemSlot.START,
  },
  bold: {
    icon: XIconNames.bold,
    title: XResourceIDs.x_markdown_bold,
    slot: XMarkdownToolbarItemSlot.START,
  },
  italic: {
    icon: XIconNames.italic,
    title: XResourceIDs.x_markdown_italic,
    slot: XMarkdownToolbarItemSlot.START,
  },
  quote: {
    icon: XIconNames.quote,
    title: XResourceIDs.x_markdown_quote,
    slot: XMarkdownToolbarItemSlot.START,
  },
  code: {
    icon: XIconNames.code,
    title: XResourceIDs.x_markdown_code,
    slot: XMarkdownToolbarItemSlot.START,
  },
  ol: {
    icon: XIconNames.ol,
    title: XResourceIDs.x_markdown_ol,
    slot: XMarkdownToolbarItemSlot.START,
  },
  ul: {
    icon: XIconNames.ul,
    title: XResourceIDs.x_markdown_ul,
    slot: XMarkdownToolbarItemSlot.START,
  },
  link: {
    icon: XIconNames.link,
    title: XResourceIDs.x_markdown_link,
    slot: XMarkdownToolbarItemSlot.START,
  },
  image: {
    icon: XIconNames.image,
    title: XResourceIDs.x_markdown_image,
    slot: XMarkdownToolbarItemSlot.START,
  },
  preview: {
    icon: XIconNames.preview,
    title: XResourceIDs.x_markdown_preview,
    slot: XMarkdownToolbarItemSlot.END,
  },
  guide: {
    icon: XIconNames.guide,
    title: XResourceIDs.x_markdown_guide,
    slot: XMarkdownToolbarItemSlot.END,
  },
};

export enum XMarkdownAction {
  //
  FireToolbarButtons = 'fire_toolbar_buttons',

  //
  Refresh = 'refresh',
}

export type XMarkdownActionIdentifier = XMarkdownAction | string;

export interface XMarkdownActionModel
  extends XBaseActionModel<XMarkdownActionIdentifier> {}

//
//#region Inside Card Container ...
export interface IXMarkdownContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XMarkdownActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  content: string;
  contentChange: EventEmitter<string>;

  //
  mode: XStandardType<XMarkdownModeIdentifier>;

  //
  editorBoxColor: XStandardType<XColorIdentifier>;
  previewBoxColor: XStandardType<XColorIdentifier>;

  //
  //#region Toolbar ...
  hasToolbar: XStandardType<boolean>;
  toolbarCssClass: XStandardType<XOneOrManyType<string>>;
  toolbarColor: XStandardType<XColorIdentifier>;
  toolbarForegroundColor: XStandardType<boolean>;
  toolbarConfig: XStandardType<XMarkdownToolbarConfigProps>;
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
export class XMarkdownContainer
  extends XComponentCardContainer
  implements Readonly<IXMarkdownContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XMarkdownActionModel> =
    new Subject<XMarkdownActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly content: string = '';
  readonly contentChange = new EventEmitter<string>();

  //
  readonly mode: XStandardType<XMarkdownModeIdentifier> = XMarkdownMode.BOTH;

  //
  readonly editorBoxColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Transparent;
  readonly previewBoxColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Transparent;

  //
  //#region Toolbar ...
  readonly hasToolbar: XStandardType<boolean> = true;
  readonly toolbarCssClass: XStandardType<XOneOrManyType<string>>;
  readonly toolbarColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly toolbarForegroundColor: XStandardType<boolean> = false;
  readonly toolbarConfig: XStandardType<XMarkdownToolbarConfigProps>;
  //#endregion
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XMarkdownContainerComponent
  extends XComponentCardContainerComponent
  implements IXMarkdownContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XMarkdownActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  content: string = this.propertyProvider.content;

  @Output()
  contentChange: EventEmitter<string> = new EventEmitter();

  //
  @Input()
  mode: XStandardType<XMarkdownModeIdentifier> = this.propertyProvider.mode;

  //
  @Input()
  editorBoxColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.editorBoxColor;

  @Input()
  previewBoxColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.previewBoxColor;

  //
  //#region Toolbar ...
  @Input()
  hasToolbar: XStandardType<boolean> = this.propertyProvider.hasToolbar;

  @Input()
  toolbarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.toolbarCssClass;

  @Input()
  toolbarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarColor;

  @Input()
  toolbarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.toolbarForegroundColor;

  @Input()
  toolbarConfig: XStandardType<XMarkdownToolbarConfigProps> =
    this.propertyProvider.toolbarConfig;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XMarkdownContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXMarkdownContainer
  ): Observable<IXMarkdownContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            content: this.getValue(
              container ? container.content : this.content
            ),

            //
            mode: this.getValue(container ? container.mode : this.mode),

            //
            editorBoxColor: this.getValue(
              container ? container.editorBoxColor : this.editorBoxColor
            ),
            previewBoxColor: this.getValue(
              container ? container.previewBoxColor : this.previewBoxColor
            ),

            //
            hasToolbar: this.getValue(
              container ? container.hasToolbar : this.hasToolbar
            ),
            toolbarCssClass: this.getArrayValue(
              container ? container.toolbarCssClass : this.toolbarCssClass
            ),
            toolbarColor: this.getValue(
              container ? container.toolbarColor : this.toolbarColor
            ),
            toolbarForegroundColor: this.getValue(
              container
                ? container.toolbarForegroundColor
                : this.toolbarForegroundColor
            ),
            toolbarConfig: this.getValue(
              container ? container.toolbarConfig : this.toolbarConfig
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            contentChange: this.contentChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentsRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" [ngClass]="getValue(cssClass) | async" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component's itSelf Template -->
<ng-template #componentsRef>
  <!-- Check Prepared Model Exists -->
  <div
    class="x-model-presenter x-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    *ngIf="notNullOrUndefinedValue(preparedModel) | async"
  >
    <x-expandable-panel
      [isAccordion]="false"
      [applyColors]="false"
      [multiExpanded]="true"
      class="x-model-presenter-panel"
      [actionProvider]="panelActionProvider"
      [hideDescriptionOnSmallScreens]="false"
      [cardColor]="getValue(cardColor) | async"
      [wrapWithCard]="getValue(wrapWithCard) | async"
      [cssClass]="getArrayValue(panelCssClass) | async"
      [cardForegroundColor]="getValue(cardForegroundColor) | async"
    >
      <ng-container *ngFor="let key of keys(preparedModel)">
        <ng-container *ngIf="canShowKey(key) | async as canShowTheKey">
          <x-panel
            [id]="key"
            [uiDisabled]="
              !(hasContentItem(key) | async) &&
              (canDescribe(key, preparedModel[key]) | async)
            "
            [hideToggle]="
              !(hasContentItem(key) | async) &&
              (canDescribe(key, preparedModel[key]) | async)
            "
          >
            <!-- Title (Key) -->
            <div
              x-panel-title
              [isForeground]="true"
              class="x-model-presenter-title"
              *ngIf="
                canShowTheKey &&
                (getValue(showLabels) | async) &&
                (canShowLabelKey(key) | async)
              "
              [class.hasLabel]="
                canShowTheKey &&
                (getValue(showLabels) | async) &&
                (canShowLabelKey(key) | async)
              "
              [xColor]="getValue(labelColor) | async"
              [ngClass]="getModelPresenterTitleClasses(key)"
            >
              <span class="x-title-span text-truncate">
                {{ getTitle(key) | async }}
              </span>
            </div>

            <!-- Content (Value) -->
            <!-- if Has Not ContentItem and Descibeable -->
            <div
              x-panel-description
              [isForeground]="true"
              class="x-model-presenter-content"
              [xColor]="getValue(valueColor) | async"
              *ngIf="
                !(hasContentItem(key) | async) &&
                (canDescribe(key, preparedModel[key]) | async)
              "
              [ngClass]="getModelPresenterContentClasses(key)"
            >
              <x-slotter
                [hasCenterSlot]="false"
                [cssClass]="'full-with'"
                [layout]="SlotLayouts.HORIZONTAL"
                [hasEndOrBottomSlot]="
                  notNullOrUndefinedValue(propertyActionTemplate) | async
                "
              >
                <!-- Start -->
                <x-slot
                  [name]="SlotNames.START"
                  [cssClass]="['ion-text-start', 'text-truncate']"
                >
                  {{ getContent(key, preparedModel[key]) | async }}
                </x-slot>

                <!-- End -->
                <x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
                  <!-- propertyActionTemplate -->
                  <ng-container
                    *ngIf="
                      notNullOrUndefinedValue(propertyActionTemplate) | async
                    "
                  >
                    <ng-container
                      *ngTemplateOutlet="
                        propertyActionTemplate;
                        context: {
                          key: key,
                          prefix: prefix,
                          value: preparedModel[key]
                        }
                      "
                    >
                    </ng-container>
                  </ng-container>
                </x-slot>
              </x-slotter>
            </div>

            <!-- if Has not ContentItem && not Describeable -->
            <div
              [isForeground]="true"
              class="x-model-presenter-content"
              [xColor]="getValue(valueColor) | async"
              [ngClass]="getModelPresenterContentClasses(key)"
              *ngIf="
                !(hasContentItem(key) | async) &&
                !(canDescribe(key, preparedModel[key]) | async)
              "
            >
              <ng-container
                *ngTemplateOutlet="
                  nestedRef;
                  context: {
                    key: key,
                    value: preparedModel[key],
                    prefix: getKeyPrefix(key),
                    hiddenModelKeys: (getHiddenModelKeys(key) | async),
                    availableModelKeys: (getAvailableModelKeys(key) | async),
                    sortOrderModelKeys: (getSortOrderModelKeys(key) | async),
                    hiddenLabelModelKeys: (getHiddenLabelModelKeys(key) | async)
                  }
                "
              >
              </ng-container>
            </div>

            <!-- if Has ContentItem -->
            <div
              [isForeground]="true"
              class="x-model-presenter-content"
              *ngIf="hasContentItem(key) | async"
              [xColor]="getValue(valueColor) | async"
              [ngClass]="getModelPresenterContentClasses(key)"
            >
              <ng-container
                *ngTemplateOutlet="
                  (getContentItem(key, preparedModel[key]) | async).content
                "
              ></ng-container>
            </div>
          </x-panel>
        </ng-container>
      </ng-container>
    </x-expandable-panel>
  </div>
</ng-template>

<!-- NestedRef -->
<ng-template
  #nestedRef
  let-key="key"
  let-value="value"
  let-prefix="prefix"
  let-hiddenModelKeys="hiddenModelKeys"
  let-availableModelKeys="availableModelKeys"
  let-sortOrderModelKeys="sortOrderModelKeys"
  let-hiddenLabelModelKeys="hiddenLabelModelKeys"
>
  <x-model-presenter
    [model]="value"
    [attr.keyPrefix]="prefix"
    [panelCssClass]="panelCssClass"
    [actionProvider]="actionProvider"
    [color]="getValue(color) | async"
    [hiddenModelKeys]="hiddenModelKeys"
    [modelKeyProvider]="modelKeyProvider"
    [availableModelKeys]="availableModelKeys"
    [sortOrderModelKeys]="sortOrderModelKeys"
    [modelValueProvider]="modelValueProvider"
    [panelActionProvider]="panelActionProvider"
    [showLabels]="getValue(showLabels) | async"
    [labelColor]="getValue(labelColor) | async"
    [valueColor]="getValue(valueColor) | async"
    [cssClass]="getArrayValue(cssClass) | async"
    [hiddenLabelModelKeys]="hiddenLabelModelKeys"
    [headerColor]="getValue(headerColor) | async"
    (onModelChanged)="onModelChanged.emit($event)"
    [contentColor]="getValue(contentColor) | async"
    [startupState]="getValue(startupState) | async"
    [propertyActionTemplate]="propertyActionTemplate"
    [foregroundColor]="getValue(foregroundColor) | async"
    [hideOptionalValues]="getValue(hideOptionalValues) | async"
    [headerHighlightColor]="getValue(headerHighlightColor) | async"
    *ngIf="
      (notEmptyValue(key) | async) && (notNullOrUndefinedValue(value) | async)
    "
  >
  </x-model-presenter>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter.component.scss

:host {
  //
  --header-highlight-color: var(--x-color-light-shade);

  //
  --header-background-color: var(--x-color-light);
  --header-foreground-color: var(--x-color-light-contrast);

  //
  --content-background-color: var(--x-color-light);
  --content-foreground-color: var(--x-color-light-contrast);

  //
  --panel-background-color: var(--x-color-light);
  --panel-foreground-color: var(--x-color-light-contrast);
}

:host ::ng-deep .x-card {
  .x-actions {
    display: none;
  }

  .x-content {
    padding: 0px !important;
    margin: 0px;
  }
}

:host ::ng-deep .x-expandable-panel {
  //
  &.mobile {
    .mat-expansion-panel-header {
      margin: {
        top: 2px;
        bottom: 6px;
      }
    }
  }

  .mat-expansion-panel {
    //
    box-shadow: unset;
    color: var(--panel-foreground-color);
    background-color: var(--panel-background-color);

    //
    .mat-expansion-panel-body {
      color: var(--panel-foreground-color);
      background-color: var(--panel-background-color);
    }

    //
    .mat-expansion-panel-header {
      //
      color: var(--header-foreground-color);
      background-color: var(--header-background-color);

      //
      // Header HighLight ...
      &.cdk-keyboard-focused,
      &.cdk-program-focused,
      &:not(.mat-expanded):hover {
        background-color: var(--header-highlight-color);
      }

      &.mobile {
        .mat-content {
          flex-direction: column;
        }

        .x-model-presenter-title {
          .x-title-span {
            max-width: calc(80%);
          }
        }
      }

      .x-expandable-panel-header-title {
        //
        flex-grow: 0;
        margin-left: 4px !important;

        //
        .x-model-presenter-title {
          width: 100%;
          word-wrap: unset;
          white-space: pre;
        }

        //
        .x-model-presenter-title.hasLabel {
          .x-title-span::after {
            content: " : ";
          }
        }
      }

      .x-expandable-panel-header-description {
        //
        overflow: hidden;
        margin-left: 4px !important;

        //
        .x-model-presenter-content {
          width: 100%;
        }

        .x-end-bottom-slot {
          flex: 0 0 auto !important;
        }

        .x-start-top-slot {
          overflow: hidden;
        }

        .x-start-top-slot-inner-container {
          max-width: calc(90%);
        }
      }
    }

    //
    .mat-expansion-panel-content {
      color: var(--content-foreground-color);
      background-color: var(--content-background-color);
    }
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter.component.ts

import {
  Component,
  QueryList,
  ContentChildren,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  tap,
  map,
  filter,
  mergeMap,
  switchMap,
  concatMap,
} from 'rxjs/operators';
import {
  keys,
  nameof,
  isDate,
  isArray,
  toArray,
  isNumber,
  isString,
  isObject,
  toXString,
  isFunction,
  parseXColor,
  isSameObject,
  XOneOrManyType,
  XModelProperty,
  toNormalString,
  getPropertyValue,
  setPropertyValue,
  isNullOrUndefined,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XModelPresenterAction,
  XModelPresenterCssVars,
  XModelPresenterActionModel,
  XModelPresenterStartupState,
  XModelPresenterContainerComponent,
} from './x-model-presenter.typings';
import {
  XExpandablePanelAction,
  XExpandablePanelActionModel,
} from '../x-expandable-panel/x-expandable-panel.typings';
import { XSlotLayout, XSlotName } from '../x-slotter/x-slotter.typings';
import { of, from, Subject, forkJoin, Observable, Subscription } from 'rxjs';
import { XModelPresenterItemComponent } from './x-model-presenter-item/x-model-presenter-item.component';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-model-presenter',
  templateUrl: './x-model-presenter.component.html',
  styleUrls: ['./x-model-presenter.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XModelPresenterComponent<
  T = any
> extends XModelPresenterContainerComponent<T> {
  //
  //#region Props ...
  private MODEL: T;
  private keyPrefix = '';
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly SlotLayouts = Object.assign({}, XSlotLayout);

  //
  get prefix() {
    return this.keyPrefix;
  }

  get preparedModel() {
    return this.MODEL;
  }

  //
  @ContentChildren(XModelPresenterItemComponent)
  items: QueryList<XModelPresenterItemComponent>;
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.prepareModel();
    await this.handleStartupState();
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    // prepare KeyPrefix ...
    const keyPrefix = this.element.nativeElement
      ? (this.element.nativeElement as HTMLElement).getAttribute('keyPrefix')
      : '';
    if (!isNullOrEmptyString(keyPrefix)) {
      this.keyPrefix += `${keyPrefix}`;
    }
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);
    //
    //#region Model Changed ...
    const isModelChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('model')
    );

    //
    if (isModelChanged) {
      await this.prepareModel();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('actionProvider')
    );
    const isPanelActionProviderChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('panelActionProvider')
    );
    if (isActionProviderChanged || isPanelActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Color Changes ...
    //
    //#region Color Changed ...
    const isColorChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('color')
    );
    if (isColorChanged) {
      await this.applyPanelColor();
    }
    //#endregion

    //
    //#region Header Highlight Color Changed ...
    const isHeaderHighlightColorChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('headerHighlightColor')
    );
    if (isHeaderHighlightColorChanged) {
      await this.applyHeaderHighlightColor();
    }
    //#endregion

    //
    //#region Header Color Changed ...
    const isHeaderColorChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('headerColor')
    );
    if (isHeaderColorChanged) {
      await this.applyHeaderColor();
    }
    //#endregion

    //
    //#region Content Color Changed ...
    const isContentColorChanged = changeKeys.includes(
      nameof<XModelPresenterComponent>('contentColor')
    );
    if (isContentColorChanged) {
      await this.applyContentColor();
    }
    //#endregion
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  keys(model: T) {
    //
    let modelKeys = keys(model);

    //
    if (!this.hasChild(this.sortOrderModelKeys)) {
      return modelKeys;
    }

    //
    const sortOrderArray = toArray(this.sortOrderModelKeys) as string[];
    const result: string[] = [
      ...sortOrderArray.filter((k) => modelKeys.includes(k)),
      ...modelKeys.filter((k) => !sortOrderArray.includes(k)),
    ];

    //
    return result;
  }

  canShowKey(key: string) {
    //
    return forkJoin({
      key: this.getValue(key),
      model: this.getValue(this.model),
      prefix: this.getValue(this.keyPrefix),
      hiddenModelKeys: this.getArrayValue(this.hiddenModelKeys),
      hideOptionalValues: this.getValue(this.hideOptionalValues),
      availableModelKeys: this.getArrayValue(this.availableModelKeys),
    }).pipe(
      map((provider) => {
        //
        const value = provider.model[provider.key];

        //
        const modelHasValue =
          !isNullOrUndefined(provider.model) &&
          !isNullOrUndefined(value) &&
          !isNullOrEmptyString(toXString(provider.model[provider.key]));
        const hasChildHiddens = this.hasChild(provider.hiddenModelKeys);
        const hasChildAvailables = this.hasChild(provider.availableModelKeys);

        //
        if (!hasChildAvailables && !hasChildHiddens) {
          //
          if (!modelHasValue && provider.hideOptionalValues) {
            return false;
          }

          //
          return true;
        }

        //
        const isInHiddens = provider.hiddenModelKeys.includes(
          provider.key as any
        );
        const isInAvailables = provider.availableModelKeys.includes(
          provider.key as any
        );

        //
        if (
          (!hasChildAvailables && !isInHiddens) ||
          (isInAvailables && !hasChildHiddens) ||
          (isInAvailables && !isInHiddens) ||
          (!isInAvailables &&
            !isInHiddens &&
            isNumber(provider.key) &&
            !isNullOrEmptyString(provider.prefix))
        ) {
          //
          if (!modelHasValue && provider.hideOptionalValues) {
            return false;
          }

          //
          return true;
        }

        //
        return false;
      })
    );
  }

  canShowLabelKey(key: string) {
    return this.getArrayValue(this.hiddenLabelModelKeys).pipe(
      map((hiddenLabelsModelKeys) => {
        //
        const hasChildHiddenLabels = this.hasChild(hiddenLabelsModelKeys);

        //
        if (!hasChildHiddenLabels) {
          return true;
        }

        //
        const isInHiddens = hiddenLabelsModelKeys.includes(key as any);
        return hasChildHiddenLabels && !isInHiddens;
      })
    );
  }

  getTitle(key: string): Observable<string> {
    return this.getValue(this.modelKeyProvider).pipe(
      concatMap((provider) => {
        return isNullOrUndefined(provider) || !isFunction(provider)
          ? this.getValue(key)
          : this.getValue(provider(key as any));
      })
    );
  }

  hasContentItem(key: string) {
    return of(this.items).pipe(
      concatMap((items) => {
        return isNullOrUndefined(items)
          ? of(false)
          : this.getArrayValue(items.toArray()).pipe(
              switchMap((items) =>
                from(items).pipe(
                  mergeMap((item) =>
                    this.getValue(item.name).pipe(
                      map((name) => ({ name, item }))
                    )
                  ),
                  filter(
                    (itemData) =>
                      toNormalString(itemData.name) === toNormalString(key)
                  ),
                  map((dataObject) => !isNullOrUndefined(dataObject))
                )
              )
            );
      })
    );
  }

  getContentItem(
    key: string,
    value: any
  ): Observable<XModelPresenterItemComponent> {
    return this.hasContentItem(key).pipe(
      concatMap((isExists) => {
        return !isExists
          ? of(null)
          : of(this.items).pipe(
              concatMap((items) => {
                return isNullOrUndefined(items)
                  ? of(null)
                  : this.getArrayValue(items.toArray()).pipe(
                      switchMap((items) =>
                        from(items).pipe(
                          mergeMap((item) =>
                            this.getValue(item.name).pipe(
                              map((name) => ({ name, item }))
                            )
                          ),
                          filter(
                            (itemData) =>
                              toNormalString(itemData.name) ===
                              toNormalString(key)
                          ),
                          tap((dataObject) => (dataObject.item.value = value)),
                          map((dataObject) => dataObject.item)
                        )
                      )
                    );
              })
            );
      })
    );
  }

  getContent(key: string, value: any): Observable<string> {
    return this.getValue(this.modelValueProvider).pipe(
      concatMap((provider) => {
        return isNullOrUndefined(provider) || !isFunction(provider)
          ? this.getValue(value)
          : this.getValue(provider(key as any, value));
      })
    );
  }

  canDescribe(key: string, value: any) {
    return of(value).pipe(
      map(
        (eVal) =>
          isDate(eVal) ||
          isFunction(eVal) ||
          (!isArray(eVal) && !isObject(eVal))
      )
    );
  }

  getModelPresenterTitleClasses(key: string) {
    return [`x-model-presenter-${key}-title`];
  }

  getModelPresenterContentClasses(key: string) {
    return [`x-model-presenter-${key}-content`];
  }

  //
  //#region Neste ...
  getKeyPrefix(key: string) {
    //
    const result = isNullOrEmptyString(this.keyPrefix)
      ? `${key}`
      : `${this.keyPrefix}.${key}`;

    //
    return result;
  }

  getHiddenModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.hiddenModelKeys);
  }

  getSortOrderModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.sortOrderModelKeys);
  }

  getAvailableModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.availableModelKeys);
  }

  getHiddenLabelModelKeys(key: string) {
    return this.prepareNestedCollection(key, this.hiddenLabelModelKeys);
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  //#endregion

  //
  //#region Actions ...
  async expandAll() {
    this.panelActionProvider.next({
      action: XExpandablePanelAction.ExpandAll,
    });
  }

  async collapseAll() {
    this.panelActionProvider.next({
      action: XExpandablePanelAction.CollapseAll,
    });
  }

  async setModelProperty(property: XModelProperty) {
    //
    if (!property || isNullOrEmptyString(property.key)) {
      return;
    }

    //
    const model = this.MODEL;
    const nodeValue = getPropertyValue(model, property.prefix);
    const modelKeys = isNullOrUndefined(nodeValue) ? [] : keys(nodeValue);
    if (!this.hasChild(modelKeys)) {
      return;
    }

    //
    const isExists = modelKeys.includes(property.key as string);
    if (!isExists) {
      return;
    }

    //
    // normalize value ...
    const normalValue = isString(property.value)
      ? !isNullOrEmptyString(property.value) &&
        !isNullOrUndefined(property.value)
        ? property.value.toString().trim()
        : undefined
      : isNullOrUndefined(property.value)
      ? undefined
      : property.value;

    //
    if (nodeValue[property.key] === normalValue) {
      return;
    }

    //
    const setterPropertyPath = isNullOrEmptyString(property.prefix)
      ? `${property.key as string}`
      : `${property.prefix}.${property.key as string}`;

    //
    setPropertyValue(model, setterPropertyPath, normalValue);

    //
    this.MODEL = {
      ...model,
    };
    this.detectChanges();
    this.onModelChanged.emit(model);
  }

  async getModelProperty(key: keyof T | string) {
    //
    if (isNullOrEmptyString(key)) {
      return null;
    }

    //
    const model = await this.getValueAsync(this.model);
    const modelKeys = await this.getValueAsync(this.keys(model));
    if (!this.hasChild(modelKeys)) {
      return null;
    }

    //
    const isExists = modelKeys.includes(key);
    if (!isExists) {
      return null;
    }

    //
    const result = model[key as any];
    return result;
  }

  async getModelViewProperty(key: keyof T | string) {
    //
    if (isNullOrEmptyString(key)) {
      return null;
    }

    //
    const model = await this.getValueAsync(this.model);
    const modelKeys = await this.getValueAsync(this.keys(model));
    if (!this.hasChild(modelKeys)) {
      return null;
    }

    //
    const isExists = modelKeys.includes(key);
    if (!isExists) {
      return null;
    }

    //
    const result = await this.getValueAsync(
      this.getContent(key, model[key as any])
    );
    return result;
  }

  async setModel(model: T) {
    //
    if (!model || isSameObject(model as any, this.MODEL as any)) {
      return;
    }

    //
    this.model = {
      ...model,
    };
    this.MODEL = {
      ...model,
    };

    //
    await this.prepareModel();
    this.onModelChanged.emit(model);
  }

  async getModel() {
    //
    const result = await this.getValueAsync(this.model);
    return result;
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XModelPresenterActionModel>();
    }

    //
    if (!this.panelActionProvider) {
      this.panelActionProvider = new Subject<XExpandablePanelActionModel>();
    }

    //
    let modelProperty: XModelProperty = null;

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XModelPresenterAction.Refresh:
            this.detectChanges();
            break;

          //
          case XModelPresenterAction.ExpandAll:
            await this.expandAll();
            break;

          //
          case XModelPresenterAction.CollapseAll:
            await this.collapseAll();
            break;

          //
          case XModelPresenterAction.ClearProperty:
            //
            modelProperty = model.payload as XModelProperty;
            if (
              !model.payload ||
              isNullOrUndefined(model) ||
              isNullOrUndefined(model.payload) ||
              isNullOrEmptyString(model.action)
            ) {
              return;
            }

            //
            modelProperty.value = null;

            //
            await this.setModelProperty(modelProperty);
            break;

          //
          case XModelPresenterAction.SetProperty:
            //
            modelProperty = model.payload as XModelProperty;
            if (
              !model.payload ||
              isNullOrUndefined(model) ||
              isNullOrUndefined(model.payload) ||
              isNullOrEmptyString(model.action)
            ) {
              return;
            }

            //
            await this.setModelProperty(modelProperty);
            break;

          //
          case XModelPresenterAction.SetModel:
            //
            const resetModel = model.payload as T;
            if (!model || !model.payload || !resetModel) {
              return;
            }

            //
            await this.setModel(resetModel);
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async prepareModel() {
    //
    this.MODEL = null;
    this.detectChanges();

    //
    if (!this.model) {
      return;
    }

    //
    this.MODEL = await this.getValueAsync(this.model);
    this.detectChanges();
  }

  private prepareNestedCollection(
    key: string,
    collection: XOneOrManyType<string>
  ) {
    //
    return forkJoin({
      key: this.getValue(key),
      prefix: this.getValue(this.getKeyPrefix(key)),
      model: this.getValue(this.preparedModel[key]),
      collection: this.getArrayValue(collection),
    }).pipe(
      map((provider) => {
        //
        const allAvailableCollectionItems = provider.collection;

        //
        const keySelector = isNullOrUndefined(provider.prefix)
          ? `${provider.key}.`
          : `${provider.prefix}.`;

        //
        const canFilter =
          !isNumber(provider.key) &&
          !provider.prefix.includes(`${provider.key}.`);
        const result = canFilter
          ? allAvailableCollectionItems
              .filter((k) => k.includes(`${provider.key}.`))
              .map((k) => k.replace(`${provider.key}.`, ''))
          : allAvailableCollectionItems;

        //
        return result;
      })
    );
  }

  private async handleStartupState() {
    //
    const state = await this.getValueAsync(this.startupState);
    if (isNullOrEmptyString(state)) {
      return;
    }

    //
    switch (state) {
      //
      case XModelPresenterStartupState.CollapseAll:
        await this.collapseAll();
        break;

      //
      case XModelPresenterStartupState.ExpandAll:
        await this.expandAll();
        break;
    }
  }

  //
  //#region Color Appliers ...
  private async applyPanelColor() {
    //
    const color = await this.getValueAsync(this.color);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XModelPresenterCssVars.PanelForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XModelPresenterCssVars.PanelBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyHeaderColor() {
    //
    const color = await this.getValueAsync(this.headerColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XModelPresenterCssVars.HeaderForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XModelPresenterCssVars.HeaderBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyHeaderHighlightColor() {
    //
    const color = await this.getValueAsync(this.headerHighlightColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XModelPresenterCssVars.HeaderHighlighColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  private async applyContentColor() {
    //
    const color = await this.getValueAsync(this.contentColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XModelPresenterCssVars.ContentForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XModelPresenterCssVars.ContentBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XModelPresenterComponent } from './x-model-presenter.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XExpandablePanelModule } from '../x-expandable-panel/x-expandable-panel.module';
import { XModelPresenterItemComponent } from './x-model-presenter-item/x-model-presenter-item.component';

@NgModule({
  declarations: [XModelPresenterComponent, XModelPresenterItemComponent],
  imports: [
    XCardModule,
    CommonModule,
    XBaseComponentModule,
    XExpandablePanelModule,
  ],
  exports: [
    XCardModule,
    CommonModule,
    XBaseComponentModule,
    XExpandablePanelModule,
    XModelPresenterComponent,
    XModelPresenterItemComponent,
  ],
})
export class XModelPresenterModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XModelFieldsType,
  XColorIdentifier,
  XModelKeyProvider,
  XModelValueProvider,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';
import { XExpandablePanelActionModel } from '../x-expandable-panel/x-expandable-panel.typings';

export enum XModelPresenterStartupState {
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',
}

export type XModelPresenterStartupStateIdentifier =
  | XModelPresenterStartupState
  | string;

export enum XModelPresenterAction {
  //
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',

  //
  ClearProperty = 'clear_property',
  SetProperty = 'set_property',

  //
  SetModel = 'set_model',

  //
  Refresh = 'refresh',
}

export type XModelPresenterActionIdentifier = XModelPresenterAction | string;

export interface XModelPresenterActionModel
  extends XBaseActionModel<XModelPresenterActionIdentifier> {}

export enum XModelPresenterCssVars {
  //
  HeaderHighlighColor = 'header-highlight-color',

  //
  HeaderForegroundColor = 'header-foreground-color',
  HeaderBackgroundColor = 'header-background-color',

  //
  ContentForegroundColor = 'content-foreground-color',
  ContentBackgroundColor = 'content-background-color',

  //
  PanelForegroundColor = 'panel-foreground-color',
  PanelBackgroundColor = 'panel-background-color',
}

export type XModelPresenterCssVarsIdentifier = XModelPresenterCssVars | string;

//
//#region Inside Card Container ...
export interface IXModelPresenterContainer<T = any>
  extends IXComponentCardContainer {
  //
  actionProvider: Subject<XModelPresenterActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  headerColor: XStandardType<XColorIdentifier>;
  headerHighlightColor: XStandardType<XColorIdentifier>;
  contentColor: XStandardType<XColorIdentifier>;
  labelColor: XStandardType<XColorIdentifier>;
  valueColor: XStandardType<XColorIdentifier>;

  //
  showLabels: XStandardType<boolean>;
  propertyActionTemplate: TemplateRef<any>;
  startupState: XStandardType<XModelPresenterStartupStateIdentifier>;

  //
  panelCssClass: XStandardType<XOneOrManyType<string>>;
  panelActionProvider: Subject<XExpandablePanelActionModel>;

  //
  model: XStandardType<T>;

  //
  hideOptionalValues: XStandardType<boolean>;
  availableModelKeys: XModelFieldsType<T>;
  hiddenModelKeys: XModelFieldsType<T>;
  hiddenLabelModelKeys: XModelFieldsType<T>;
  sortOrderModelKeys: XModelFieldsType<T>;
  modelKeyProvider: XModelKeyProvider<T>;
  modelValueProvider: XModelValueProvider<T>;

  //
  onModelChanged: EventEmitter<T>;
}

@Injectable({
  providedIn: 'root',
})
export class XModelPresenterContainer<T = any>
  extends XComponentCardContainer
  implements Readonly<IXModelPresenterContainer<T>>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XModelPresenterActionModel> =
    new Subject<XModelPresenterActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly headerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly headerHighlightColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightShade;
  readonly contentColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly labelColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.TertiaryShade;
  readonly valueColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  //
  readonly showLabels: XStandardType<boolean> = true;
  readonly propertyActionTemplate: TemplateRef<any>;
  readonly startupState: XStandardType<XModelPresenterStartupStateIdentifier> =
    XModelPresenterStartupState.CollapseAll;

  //
  readonly panelCssClass: XStandardType<XOneOrManyType<string>>;
  readonly panelActionProvider: Subject<XExpandablePanelActionModel> =
    new Subject<XExpandablePanelActionModel>();

  //
  readonly model: XStandardType<T>;

  //
  readonly hideOptionalValues: XStandardType<boolean> = false;
  readonly availableModelKeys: XModelFieldsType<T>;
  readonly hiddenModelKeys: XModelFieldsType<T>;
  readonly hiddenLabelModelKeys: XModelFieldsType<T>;
  readonly sortOrderModelKeys: XModelFieldsType<T>;
  readonly modelKeyProvider: XModelKeyProvider<T>;
  readonly modelValueProvider: XModelValueProvider<T>;

  //
  readonly onModelChanged: EventEmitter<T> = new EventEmitter<T>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XModelPresenterContainerComponent<T = any>
  extends XComponentCardContainerComponent
  implements IXModelPresenterContainer<T>
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XModelPresenterActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  headerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerColor;

  @Input()
  headerHighlightColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerHighlightColor;

  @Input()
  contentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.contentColor;

  @Input()
  labelColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.labelColor;

  @Input()
  valueColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.valueColor;

  //
  @Input()
  showLabels: XStandardType<boolean> = this.propertyProvider.showLabels;

  @Input()
  propertyActionTemplate: TemplateRef<any> =
    this.propertyProvider.propertyActionTemplate;

  @Input()
  startupState: XStandardType<XModelPresenterStartupStateIdentifier> =
    this.propertyProvider.startupState;

  //
  @Input()
  panelCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.panelCssClass;

  @Input()
  panelActionProvider: Subject<XExpandablePanelActionModel> =
    this.propertyProvider.panelActionProvider;

  //
  @Input()
  model: XStandardType<T> = this.propertyProvider.model;

  //
  @Input()
  hideOptionalValues: XStandardType<boolean> =
    this.propertyProvider.hideOptionalValues;

  @Input()
  availableModelKeys: XModelFieldsType<T> =
    this.propertyProvider.availableModelKeys;

  @Input()
  hiddenModelKeys: XModelFieldsType<T> = this.propertyProvider.hiddenModelKeys;

  @Input()
  hiddenLabelModelKeys: XModelFieldsType<T> =
    this.propertyProvider.hiddenLabelModelKeys;

  @Input()
  sortOrderModelKeys: XModelFieldsType<T> =
    this.propertyProvider.sortOrderModelKeys;

  @Input()
  modelKeyProvider: XModelKeyProvider<T> =
    this.propertyProvider.modelKeyProvider;

  @Input()
  modelValueProvider: XModelValueProvider<T> =
    this.propertyProvider.modelValueProvider;

  //
  @Output()
  onModelChanged: EventEmitter<T> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XModelPresenterContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXModelPresenterContainer
  ): Observable<IXModelPresenterContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            headerColor: this.getValue(
              container ? container.headerColor : this.headerColor
            ),
            headerHighlightColor: this.getValue(
              container
                ? container.headerHighlightColor
                : this.headerHighlightColor
            ),
            contentColor: this.getValue(
              container ? container.contentColor : this.contentColor
            ),
            labelColor: this.getValue(
              container ? container.labelColor : this.labelColor
            ),
            valueColor: this.getValue(
              container ? container.valueColor : this.valueColor
            ),
            showLabels: this.getValue(
              container ? container.showLabels : this.showLabels
            ),
            propertyActionTemplate: this.getValue(
              container
                ? container.propertyActionTemplate
                : this.propertyActionTemplate
            ),
            startupState: this.getValue(
              container ? container.startupState : this.startupState
            ),
            panelCssClass: this.getValue(
              container ? container.panelCssClass : this.panelCssClass
            ),
            model: this.getValue(container ? container.model : this.model),
            hideOptionalValues: this.getValue(
              container ? container.hideOptionalValues : this.hideOptionalValues
            ),
            availableModelKeys: this.getValue(
              container ? container.availableModelKeys : this.availableModelKeys
            ),
            hiddenModelKeys: this.getValue(
              container ? container.hiddenModelKeys : this.hiddenModelKeys
            ),
            hiddenLabelModelKeys: this.getValue(
              container
                ? container.hiddenLabelModelKeys
                : this.hiddenLabelModelKeys
            ),
            sortOrderModelKeys: this.getValue(
              container ? container.sortOrderModelKeys : this.sortOrderModelKeys
            ),
            modelKeyProvider: this.getValue(
              container ? container.modelKeyProvider : this.modelKeyProvider
            ),
            modelValueProvider: this.getValue(
              container ? container.modelValueProvider : this.modelValueProvider
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            panelActionProvider: container
              ? container.panelActionProvider
              : this.panelActionProvider,

            //
            onModelChanged: this.onModelChanged,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter-item\x-model-presenter-item.component.html

<ng-template #content>
  <ng-content></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter-item\x-model-presenter-item.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter-item\x-model-presenter-item.component.ts

import {
  Input,
  Inject,
  NgZone,
  Renderer2,
  Component,
  ViewChild,
  ElementRef,
  TemplateRef,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XModelPresenterItemAction,
  XModelPresenterItemModel,
} from './x-model-presenter-item.typings';
import { Subject, Subscription } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseComponent } from '../../classes/x-base-component';
import { XStandardType, XOneOrManyType, nameof } from 'x-framework-core';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from './../../config/x-framework-components.config';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-model-presenter-item',
  templateUrl: './x-model-presenter-item.component.html',
  styleUrls: ['./x-model-presenter-item.component.scss'],
  // changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XModelPresenterItemComponent extends XBaseComponent {
  //
  //#region Props ...
  //
  @ViewChild('content', { static: false })
  content: TemplateRef<any>;

  //
  @Input()
  name: XStandardType<string>;

  @Input()
  value: XStandardType<any>;

  //
  @Input()
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  @Input()
  actionProvider = new Subject<XModelPresenterItemModel>();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XModelPresenterItemComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XModelPresenterItemModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XModelPresenterItemAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-model-presenter\x-model-presenter-item\x-model-presenter-item.typings.ts

import { XBaseActionModel } from '../../classes/x-base-action.model';

export enum XModelPresenterItemAction {
  Refresh = 'refresh',
}
export type XModelPresenterItemActionIdentifier = XModelPresenterItemAction | string;

export interface XModelPresenterItemModel
  extends XBaseActionModel<XModelPresenterItemActionIdentifier> {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-page.component.html

<!-- Component Content -->
<section
  class="x-page x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
>
  <!-- Screen Capture -->
  <x-screen-capture
    [uiDisabled]="uiDisabled"
    (imageCaptured)="imageCaptured.emit($event)"
    [enable]="getValue(enableCaptureMode) | async"
    [topOffset]="getValue(captureTopOffset) | async"
    [bottomOffset]="getValue(captureBottomOffset) | async"
    [downloadCapturedImage]="getValue(downloadCapturedImage) | async"
  >
    <!-- Toolbar -->
    <x-toolbar
      #toolbar
      [uiDisabled]="uiDisabled"
      *ngIf="getValue(showToolbar) | async"
      [color]="getValue(toolbarColor) | async"
      [title]="getValue(toolbarTitle) | async"
      [contentTemplate]="toolbarContentTemplate"
      [class.menu-opened]="isMenuOpen() | async"
      [logoUrl]="getValue(toolbarLogoUrl) | async"
      [hasMenu]="getValue(toolbarHasMenu) | async"
      [hasBack]="getValue(toolbarHasBack) | async"
      [logoSlot]="getValue(toolbarLogoSlot) | async"
      [showLogo]="getValue(toolbarShowLogo) | async"
      [showBack]="getValue(toolbarShowBack) | async"
      [subTitle]="getValue(toolbarSubTitle) | async"
      [showMenu]="getValue(toolbarShowMenu) | async"
      [cssClass]="getValue(toolbarCssClass) | async"
      [backSlot]="getValue(toolbarBackSlot) | async"
      [menuSlot]="getValue(toolbarMenuSlot) | async"
      [showTitle]="getValue(toolbarShowTitle) | async"
      [titleSlot]="getValue(toolbarTitleSlot) | async"
      [contentSlot]="getValue(toolbarContentSlot) | async"
      [defaultHref]="getValue(toolbarDefaultHref) | async"
      [backHandler]="getValue(toolbarBackHandler) | async"
      [autoHideMenu]="getValue(toolbarAutoHideMenu) | async"
      [showSubTitle]="getValue(toolbarShowSubTitle) | async"
      [endSlotButtonsTemplate]="toolbarEndSlotButtonsTemplate"
      [showProgressBar]="getValue(toolbarShowProgressBar) | async"
      [startSlotButtonsTemplate]="toolbarStartSlotButtonsTemplate"
      [progressBarColor]="getValue(toolbarProgressBarColor) | async"
    >
    </x-toolbar>

    <!-- Split Pane -->
    <ion-split-pane
      contentId="x-menu-wrapper"
      [when]="getValue(toggleMenuWhen) | async"
      [disabled]="emptyValue(toggleMenuWhen) | async"
      (ionSplitPaneVisible)="handleChangeSplitPane($event)"
    >
      <ng-container *ngTemplateOutlet="insideContentRef"></ng-container>
    </ion-split-pane>

    <!-- Footer -->
    <x-footer
      #footer
      [uiDisabled]="uiDisabled"
      *ngIf="getValue(showFooter) | async"
      [color]="getValue(footerColor) | async"
      [contentTemplate]="footerContentTemplate"
    ></x-footer>
  </x-screen-capture>
</section>

<!-- Page Inside Content Template -->
<ng-template #insideContentRef>
  <!-- Ion Menu -->
  <!-- [disabled]="uiDisabled" -->
  <ion-menu
    id="menu"
    class="x-menu"
    contentId="x-menu-wrapper"
    *ngIf="getValue(hasSide) | async"
    [side]="getValue(sideSlot) | async"
    [type]="getValue(sideType) | async"
    (ionDidOpen)="handleMenuStateChanged(MenuStates.Opened)"
    (ionDidClose)="handleMenuStateChanged(MenuStates.Closed)"
    (ionWillOpen)="handleMenuStateChanged(MenuStates.WillOpen)"
    (ionWillClose)="handleMenuStateChanged(MenuStates.WillClose)"
  >
    <!-- Menu Content -->
    <ion-content #menuContent [color]="getValue(sideColor) | async">
      <!-- Content -->
      <div class="x-page-side-wrapper">
        <ng-content select="[side]"></ng-content>
      </div>
    </ion-content>
  </ion-menu>

  <!-- Page Content -->
  <ion-content
    #pageContent
    id="x-menu-wrapper"
    [xColor]="getValue(color) | async"
    class="ion-no-margin ion-no-padding"
    [ngClass]="getValue(cssClass) | async"
    (scrolling)="handleScrollingEvent($event)"
    (scrollEnded)="scrolledEvent.emit($event)"
    (scrollStarted)="handleScrollStarted($event)"
    [scrollingActionProvider]="scrollEventActionProvider"
    [scrollEvents]="getValue(provideScrollEvents) | async"
    [xScrollBehaviour]="getValue(scrollBehaviour) | async"
    [scrollEndClasses]="getValue(scrollEndClasses) | async"
    [scrollStartClasses]="getValue(scrollStartClasses) | async"
  >
    <!-- Swipe Refresh -->
    <ion-refresher
      slot="fixed"
      [disabled]="uiDisabled"
      (ionRefresh)="handleRefresh($event)"
      *ngIf="getValue(showPageRefresher) | async"
    >
      <ion-refresher-content
        [refreshingText]="getValue(refreshingText) | async"
        [pullingText]="getValue(refreshPullingText) | async"
        [pullingIcon]="getValue(refreshPullingIcon) | async"
        [refreshingSpinner]="getValue(refreshingSpinner) | async"
      >
      </ion-refresher-content>
    </ion-refresher>

    <!-- Page Content -->
    <ng-content></ng-content>

    <!-- Infinite Scroll -->
    <ion-infinite-scroll
      (ionInfinite)="handleLoadMore($event)"
      *ngIf="getValue(showPageInfinityScroller) | async"
      [disabled]="getValue(infinityScrollDisabled) | async"
      [threshold]="getValue(infinityScrollThreshold) | async"
    >
      <ion-infinite-scroll-content
        [loadingText]="getValue(infinityScrollerLoadingText) | async"
        [loadingSpinner]="getValue(infinityScrollerLoadingSpinner) | async"
      >
      </ion-infinite-scroll-content>
    </ion-infinite-scroll>

    <!-- Fab Actions -->
    <ng-container *ngIf="getValue(actions) | async">
      <x-fab
        pageFab
        [loading]="loading"
        [uiDisabled]="uiDisabled"
        [item]="getValue(actions) | async"
        (xOnOutsideClicked)="closeActions()"
        [actionProvider]="actionsActionProvider"
      >
      </x-fab>
    </ng-container>
  </ion-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-page.component.scss

:host {
  height: 100%;

  --side-menu-width: 200px;
  --side-menu-min-width: 100px;
  --side-menu-max-width: 200px;

  --side-menu-content-height-gutter: 0px;
  --side-menu-content-offset-top: 0px;
  --side-menu-content-offset-bottom: 0px;

  --content-height-gutter: 0px;
  --content-offset-top: 0px;
  --content-offset-bottom: 0px;

  ion-refresher {
    padding-top: 90px;
  }

  ion-content {
    //
    height: calc(100% - var(--content-height-gutter));
    overflow: auto;

    //
    --offset-top: var(--content-offset-top) !important;
    --offset-bottom: var(--content-offset-bottom) !important;

    //
    --padding-top: 0;
    --padding-end: 0;
    --padding-start: 0;
    --padding-bottom: 0;
  }
}

:host::ng-deep {
  ion-header,
  ion-footer {
    z-index: 10000 !important;
  }
}

.x-menu {
  //
  --width: var(--side-menu-width) !important;
  --min-width: var(--side-menu-min-width) !important;
  --max-width: var(--side-menu-max-width) !important;

  //
  ion-content {
    //
    height: calc(100% - var(--side-menu-content-height-gutter)) !important;
    overflow: auto !important;

    //
    --offset-top: var(--side-menu-content-offset-top) !important;
    --offset-bottom: var(--side-menu-content-offset-bottom) !important;

    //
    --padding-top: 0;
    --padding-end: 0;
    --padding-start: 0;
    --padding-bottom: 0;
  }
}

.split-pane-visible > .split-pane-side {
  border: 0 !important;
  width: var(--side-menu-width) !important;
  min-width: var(--side-menu-min-width) !important;
  max-width: var(--side-menu-max-width) !important;
}

.x-footer-offset,
.x-toolbar-offset {
  visibility: hidden;
}

.x-page-side-wrapper {
  touch-action: pan-y;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-page.component.ts

import {
  XMenuState,
  XMenuAction,
  XPageCSSVars,
  XPageLoadingEvent,
} from './x-page.typings';
import { map } from 'rxjs/operators';
import { IonContent } from '@ionic/angular';
import { XFabItem } from '../x-fab/x-fab.typings';
import { isNullOrUndefined, nameof, XBehaviourEvent } from 'x-framework-core';
import { XPageContainerComponent } from './x-page.container';
import { XFooterComponent } from './x-footer/x-footer.component';
import { XToolbarComponent } from './x-toolbar/x-toolbar.component';
import { Component, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import { XToolbarAction } from './x-toolbar/x-toolbar.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-page',
  templateUrl: './x-page.component.html',
  styleUrls: ['./x-page.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XPageComponent extends XPageContainerComponent {
  //
  //#region Props ...
  //
  readonly MenuStates = Object.assign({}, XMenuState);

  //
  scrollYOffset = 0;
  scrollXOffset = 0;

  //
  //#region View Childs ...
  //
  //#region Toolbar ...
  private TOOLBAR: XToolbarComponent;

  @ViewChild('toolbar', { static: false })
  set toolbar(v: XToolbarComponent) {
    //
    if (!isNullOrUndefined(v)) {
      //
      this.TOOLBAR = v;
      this.applyContentSize();
    }
  }

  get toolbar() {
    return this.TOOLBAR;
  }
  //#endregion

  //
  //#region Footer ...
  private FOOTER: XFooterComponent;

  @ViewChild('footer', { static: false })
  set footer(v: XFooterComponent) {
    //
    if (!isNullOrUndefined(v)) {
      //
      this.FOOTER = v;
      this.applyContentSize();
    }
  }

  get footer() {
    return this.FOOTER;
  }
  //#endregion
  //#endregion

  //
  //#region Side Props ...
  //
  private IS_SIDE_OPENED = false;

  get isSideOpened() {
    return this.IS_SIDE_OPENED;
  }

  private CAN_CLOSE_MENU = true;

  get canCloseMenu() {
    return this.CAN_CLOSE_MENU;
  }
  //#endregion
  //#endregion

  //
  //#region LifeCycle ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region CssVars ...
    //
    //#region SideMenuWidth ...
    const isSideMenuWidthChanged = changeKeys.includes(
      nameof<XPageComponent>('sideMenuWidth')
    );
    const isSideMenuMinWidthChanged = changeKeys.includes(
      nameof<XPageComponent>('sideMenuMinWidth')
    );
    const isSideMenuMaxWidthChanged = changeKeys.includes(
      nameof<XPageComponent>('sideMenuMaxWidth')
    );
    if (
      isSideMenuWidthChanged ||
      isSideMenuMinWidthChanged ||
      isSideMenuMaxWidthChanged
    ) {
      await this.applySideMenuWidth();
    }
    //#endregion
    //#endregion
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    this.handleMenuStartState();
    await this.applyContentSize();
  }
  //#endregion

  //
  //#region Register Handlers ...
  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerResizeHandler();
    this.registerMenuActionProvider();
    this.registerToolbarActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //#endregion

  //
  //#region UI Handlers ...
  async handleChangeSplitPane(event: CustomEvent | any) {
    //
    const canUseMenu = await this.canUseMenu();
    if (!canUseMenu) {
      return;
    }

    //
    const isSideOpened = event.detail.visible;
    this.IS_SIDE_OPENED = isSideOpened;
    this.splitterStateChanged.emit(isSideOpened);

    //
    if (isSideOpened) {
      //
      this.CAN_CLOSE_MENU = false;
      this.menuState = XMenuState.Opened;
      await this.openMenu();
    } else {
      //
      this.CAN_CLOSE_MENU = true;
      this.menuState = XMenuState.Closed;
      await this.closeMenu();
    }
  }

  async handleMenuStateChanged(state: XMenuState) {
    //
    const canUseMenu = await this.canUseMenu();
    if (!canUseMenu) {
      return;
    }

    //
    this.menuState = state;

    //
    this.menuStateChange.emit(state);
  }

  async handleRefresh(event: any) {
    //
    const loaderEvent: XPageLoadingEvent = {
      type: 'RefreshLoader',
      event,
    };

    //
    this.refreshPage.emit(loaderEvent);
  }

  async handleLoadMore(event: any) {
    //
    const loaderEvent: XPageLoadingEvent = {
      type: 'InfinityScrollerLoader',
      event,
    };

    //
    this.loadMore.emit(loaderEvent);
  }

  async cancelLoading(event: any) {
    //
    if (!event) {
      return;
    }

    //
    event.target.complete();
  }

  async handleScrollStarted(event: XBehaviourEvent) {
    //
    this.scrolledEvent.emit(event);

    //
    this.closeActions();
  }

  async handleScrollingEvent(event: XBehaviourEvent) {
    //
    this.scrolledEvent.emit(event);

    //
    this.scrollYOffset = event.event.detail.scrollTop as number;
    this.scrollXOffset = event.event.detail.scrollLeft as number;

    //
    await this.applyCaptureOffsets();
  }

  isMenuOpen() {
    //
    return this.getValue(this.menuState).pipe(
      map((menuState) => {
        return (
          menuState === XMenuState.WillOpen || menuState === XMenuState.Opened
        );
      })
    );
  }
  //#endregion

  //
  //#region Actions ...
  async openMenu() {
    //
    const canUseMenu = await this.canUseMenu();
    if (!canUseMenu || this.IS_SIDE_OPENED) {
      return;
    }

    //
    this.menuController.open();
  }

  async closeMenu() {
    //
    const canUseMenu = await this.canUseMenu();
    if (!canUseMenu || !this.CAN_CLOSE_MENU) {
      return;
    }

    //
    this.menuController.close();
  }

  async toggleMenu() {
    //
    const canUseMenu = await this.canUseMenu();
    if (!canUseMenu) {
      return;
    }

    //
    if (this.isMenuOpen()) {
      await this.closeMenu();
    } else {
      await this.openMenu();
    }
  }

  async scrollToTop(duration?: number) {
    //
    await this.waitForElement('ion-content[id="x-menu-wrapper"]').then(
      (el: any) => {
        el.scrollToTop(duration);
      }
    );
  }

  async scrollToBottom(duration?: number) {
    //
    await this.waitForElement('ion-content[id="x-menu-wrapper"]').then(
      (el: any) => {
        el.scrollToBottom(duration);
      }
    );
  }

  async scrollByPoint(x?: number, y?: number, duration?: number) {
    //
    await this.waitForElement('ion-content[id="x-menu-wrapper"]').then(
      (el: any) => {
        el.scrollByPoint(x, y, duration);
      }
    );
  }

  async scrollToPoint(x?: number, y?: number, duration?: number) {
    //
    await this.waitForElement('ion-content[id="x-menu-wrapper"]').then(
      (el: any) => {
        el.scrollToPoint(x, y, duration);
      }
    );
  }

  closeActions(delay: number = 1000) {
    //
    if (!this.actions) {
      return;
    }

    //
    setTimeout(() => {
      //
      if (!this.actions) {
        return;
      }

      //
      this.actions = {
        ...this.actions,
        activated: false,
      } as XFabItem;

      //
      this.detectChanges();
    }, delay);
  }

  openActions(delay: number = 1000) {
    //
    if (!this.actions) {
      return;
    }

    //
    setTimeout(() => {
      //
      if (!this.actions) {
        return;
      }

      //
      this.actions = {
        ...this.actions,
        activated: true,
      } as XFabItem;

      //
      this.detectChanges();
    }, delay);
  }
  //#endregion

  //
  //#region Private ...
  private async canUseMenu() {
    //
    if (!this.toolbarHasMenu || !this.toolbarShowMenu) {
      return false;
    }

    //
    const hasMenu = await this.toPromise(this.getValue(this.toolbarHasMenu));
    const showMenu = await this.toPromise(this.getValue(this.toolbarShowMenu));

    //
    if (!hasMenu || !showMenu) {
      return false;
    }

    //
    return true;
  }

  private async handleMenuStartState() {
    //
    const canUseMenu = await this.canUseMenu();
    if (!canUseMenu || this.isSideOpened) {
      return;
    }

    //
    switch (this.menuState) {
      //
      case XMenuState.WillOpen:
      case XMenuState.Opened:
        await this.openMenu();
        break;

      //
      case XMenuState.WillClose:
      case XMenuState.Closed:
        await this.closeMenu();
        break;
    }
  }

  private registerMenuActionProvider() {
    //
    this.applyTakeUntilWrapper(
      this.menuActionProvider.asObservable()
    ).subscribe((action) => {
      //
      switch (action) {
        case XMenuAction.Open:
          this.openMenu();
          break;

        case XMenuAction.Close:
          this.closeMenu();
          break;

        case XMenuAction.Toggle:
          this.toggleMenu();
          break;
      }
    });
  }

  private registerToolbarActionProvider() {
    //
    if (isNullOrUndefined(this.toolbarActionProvider)) {
      return;
    }

    //
    this.applyTakeUntilWrapper(
      this.toolbarActionProvider.asObservable()
    ).subscribe(async (event) => {
      //
      if (isNullOrUndefined(event)) {
        return;
      }

      //
      if (event.action === XToolbarAction.ToggleMenu) {
        await this.toggleMenu();
      }
    });
  }

  private async registerResizeHandler() {
    //
    this.applyTakeUntilWrapper(this.ruler.change()).subscribe(async () => {
      await this.applyContentSize();
    });
  }

  private async getToolbarHeight() {
    //
    if (!this.toolbar) {
      return 0;
    }

    //
    const toolbarElm = this.toolbar.getElement();

    //
    if (!toolbarElm) {
      return 0;
    }

    //
    return toolbarElm.getBoundingClientRect().height;
  }

  private async getFooterHeight() {
    //
    if (!this.footer) {
      return 0;
    }

    //
    const footerElm = this.footer.getElement();

    //
    if (!footerElm) {
      return 0;
    }

    //
    return footerElm.getBoundingClientRect().height;
  }

  private async applyContentSize() {
    //
    const showToolbar = await this.getValueAsync(this.showToolbar);
    const toolbarHeight = await this.getToolbarHeight();

    //
    const showFooter = await this.getValueAsync(this.showFooter);
    const footerHeight = await this.getFooterHeight();

    //
    if (
      (showToolbar && toolbarHeight === 0) ||
      (showFooter && footerHeight === 0)
    ) {
      //
      setTimeout(() => this.applyContentSize(), 100);
      return;
    }

    //
    const topValue = showToolbar ? `-${toolbarHeight}px` : '0px';
    const bottomValue = showFooter ? `-${footerHeight}px` : '0px';

    //
    this.setCssVar(XPageCSSVars.ContentOffsetTop, topValue);
    this.setCssVar(XPageCSSVars.SideMenuContentOffsetTop, topValue);

    //
    this.setCssVar(XPageCSSVars.ContentOffsetBottom, bottomValue);
    this.setCssVar(XPageCSSVars.SideMenuContentOffsetBottom, bottomValue);

    //
    await this.applyCaptureOffsets();

    //
    this.detectChanges();
  }

  private async applySideMenuWidth() {
    //
    const sideMenuWidth = (await this.getValueAsync(this.sideMenuWidth)) || 200;
    const sideMenuMinWidth =
      (await this.getValueAsync(this.sideMenuMinWidth)) || 100;
    const sideMenuMaxWidth =
      (await this.getValueAsync(this.sideMenuMaxWidth)) || 200;

    //
    this.setCssVar(XPageCSSVars.SideMenuWidth, `${sideMenuWidth}px`);
    this.setCssVar(XPageCSSVars.SideMenuMinWidth, `${sideMenuMinWidth}px`);
    this.setCssVar(XPageCSSVars.SideMenuMaxWidth, `${sideMenuMaxWidth}px`);

    //
    this.detectChanges();
  }

  private async applyCaptureOffsets() {
    //
    const showToolbar = await this.getValueAsync(this.showToolbar);
    const toolbarHeight = await this.getToolbarHeight();

    //
    const showFooter = await this.getValueAsync(this.showFooter);
    const footerHeight = await this.getFooterHeight();

    //
    if (
      (showToolbar && toolbarHeight === 0) ||
      (showFooter && footerHeight === 0)
    ) {
      //
      setTimeout(() => this.applyCaptureOffsets(), 100);
      return;
    }

    //
    this.captureTopOffset = -1 * this.scrollYOffset + -1 * toolbarHeight;

    //
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-page.container.ts

import {
  Input,
  NgZone,
  Output,
  Inject,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XSpinner,
  XStandardType,
  XOneOrManyType,
  XBehaviourEvent,
  XColorIdentifier,
  XSpinnerIdentifier,
  XBehaviourEventType,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XPageSize,
  XSideSlot,
  XSideType,
  XMenuState,
  XMenuAction,
  XPageLoadingEvent,
  XMenuSlotIdentifier,
  XLogoSlotIdentifier,
  XPageSizeIdentifier,
  XSideSlotIdentifier,
  XSideTypeIdentifier,
  XBackSlotIdentifier,
  XTitleSlotIdentifier,
  XContentSlotIdentifier,
} from './x-page.typings';
import { forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';
import {
  XToolbarFooterContainer,
  IXToolbarFooterContainer,
} from '../classes/i-x-toolbar-footer';
import { Observable, Subject } from 'rxjs';
import { MenuController } from '@ionic/angular';
import { XBasePage } from '../classes/x-base-page';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { IXBaseContainer } from '../classes/i-x-base.container';
import { XFooterActionModel } from './x-footer/x-footer.typings';
import { XFabActionModel, XFabItem } from '../x-fab/x-fab.typings';
import { XToolbarActionModel } from './x-toolbar/x-toolbar.typings';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';

//
//#region Container ...
export interface IXPageContainer
  extends IXBaseContainer,
    IXToolbarFooterContainer {
  //
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;

  //
  enableCaptureMode: XStandardType<boolean>;
  downloadCapturedImage: XStandardType<boolean>;
  captureTopOffset: XStandardType<number>;
  captureBottomOffset: XStandardType<number>;
  imageCaptured: EventEmitter<string | Blob>;

  //
  provideScrollEvents: XStandardType<boolean>;
  scrollStartClasses: XOneOrManyType<string>;
  scrollEndClasses: XOneOrManyType<string>;
  scrollingClasses: XOneOrManyType<string>;
  scrollBehaviour: XOneOrManyType<string>;
  scrollEventActionProvider: Subject<XBehaviourEventType>;
  scrolledEvent: EventEmitter<XBehaviourEvent>;

  //
  toggleMenuWhen: XStandardType<XPageSizeIdentifier>;
  hasSide: XStandardType<boolean>;
  sideSlot: XStandardType<XSideSlotIdentifier>;
  sideType: XStandardType<XSideTypeIdentifier>;
  sideColor: XStandardType<XColorIdentifier>;
  sideMenuWidth: XStandardType<number>;
  sideMenuMinWidth: XStandardType<number>;
  sideMenuMaxWidth: XStandardType<number>;
  menuState: XStandardType<XMenuState>;
  menuActionProvider: Subject<XMenuAction>;
  splitterStateChanged: EventEmitter<boolean>;
  menuStateChange: EventEmitter<XMenuState>;

  //
  showPageRefresher: XStandardType<boolean>;
  refreshPage: EventEmitter<XPageLoadingEvent>;
  refreshingText: XStandardType<string>;
  refreshPullingText: XStandardType<string>;
  refreshPullingIcon: XStandardType<XIconIdentifier>;
  refreshingSpinner: XStandardType<XSpinnerIdentifier>;

  //
  showPageInfinityScroller: XStandardType<boolean>;
  infinityScrollDisabled: XStandardType<boolean>;
  infinityScrollThreshold: XStandardType<string>;
  infinityScrollerLoadingText: XStandardType<string>;
  infinityScrollerLoadingSpinner: XStandardType<XSpinnerIdentifier>;
  loadMore: EventEmitter<XPageLoadingEvent>;

  //
  actions: XStandardType<XFabItem>;
  actionsActionProvider: Subject<XFabActionModel>;
}

@Injectable({
  providedIn: 'root',
})
export class XPageContainer
  extends XToolbarFooterContainer
  implements Readonly<IXPageContainer>
{
  //
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightShade;

  //
  readonly enableCaptureMode: XStandardType<boolean> = false;
  readonly downloadCapturedImage: XStandardType<boolean> = true;
  readonly captureTopOffset: XStandardType<number> = 0;
  readonly captureBottomOffset: XStandardType<number> = 0;
  readonly imageCaptured: EventEmitter<string | Blob> = new EventEmitter<
    string | Blob
  >();

  //
  readonly provideScrollEvents: XStandardType<boolean>;
  readonly scrollStartClasses: XOneOrManyType<string>;
  readonly scrollEndClasses: XOneOrManyType<string>;
  readonly scrollingClasses: XOneOrManyType<string>;
  readonly scrollBehaviour: XOneOrManyType<string>;
  readonly scrollEventActionProvider: Subject<XBehaviourEventType> =
    new Subject<XBehaviourEventType>();
  readonly scrolledEvent: EventEmitter<XBehaviourEvent> =
    new EventEmitter<XBehaviourEvent>();

  //
  readonly toggleMenuWhen: XStandardType<XPageSizeIdentifier> =
    XPageSize.Medium;
  readonly hasSide: XStandardType<boolean> = true;
  readonly sideSlot: XStandardType<XSideSlotIdentifier> = XSideSlot.Start;
  readonly sideType: XStandardType<XSideTypeIdentifier> = XSideType.Overlay;
  readonly sideColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.PrimaryShade;
  readonly sideMenuWidth: XStandardType<number> = 200;
  readonly sideMenuMinWidth: XStandardType<number> = 100;
  readonly sideMenuMaxWidth: XStandardType<number> = 200;
  readonly menuState: XStandardType<XMenuState> = XMenuState.Opened;
  readonly menuActionProvider = new Subject<XMenuAction>();
  readonly splitterStateChanged = new EventEmitter<boolean>();
  readonly menuStateChange = new EventEmitter<XMenuState>();

  //
  readonly showPageRefresher: XStandardType<boolean> = false;
  readonly refreshPage: EventEmitter<XPageLoadingEvent> =
    new EventEmitter<XPageLoadingEvent>();
  readonly refreshingText: XStandardType<string>;
  readonly refreshPullingText: XStandardType<string>;
  readonly refreshPullingIcon: XStandardType<XIconIdentifier>;
  readonly refreshingSpinner: XStandardType<XSpinnerIdentifier> =
    XSpinner.Circular;

  //
  readonly showPageInfinityScroller: XStandardType<boolean> = false;
  readonly infinityScrollDisabled: XStandardType<boolean> = false;
  readonly infinityScrollThreshold: XStandardType<string> = '200px';
  readonly infinityScrollerLoadingText: XStandardType<string>;
  readonly infinityScrollerLoadingSpinner: XStandardType<XSpinnerIdentifier> =
    XSpinner.Circular;
  readonly loadMore: EventEmitter<XPageLoadingEvent> =
    new EventEmitter<XPageLoadingEvent>();

  //
  readonly actions: XStandardType<XFabItem>;
  readonly actionsActionProvider: Subject<XFabActionModel> =
    new Subject<XFabActionModel>();
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XPageContainerComponent
  extends XBasePage
  implements IXPageContainer
{
  //
  //#region Props ...
  //
  //#region Page ...
  //
  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  //
  @Input()
  enableCaptureMode: XStandardType<boolean> =
    this.propertyProvider.enableCaptureMode;

  @Input()
  downloadCapturedImage: XStandardType<boolean> =
    this.propertyProvider.downloadCapturedImage;

  @Input()
  captureTopOffset: XStandardType<number> =
    this.propertyProvider.captureTopOffset;

  @Input()
  captureBottomOffset: XStandardType<number> =
    this.propertyProvider.captureBottomOffset;

  @Output()
  imageCaptured: EventEmitter<string | Blob> = new EventEmitter();

  //
  @Input()
  provideScrollEvents: XStandardType<boolean> =
    this.propertyProvider.provideScrollEvents;

  @Input()
  scrollStartClasses: XOneOrManyType<string> =
    this.propertyProvider.scrollStartClasses;

  @Input()
  scrollEndClasses: XOneOrManyType<string> =
    this.propertyProvider.scrollEndClasses;

  @Input()
  scrollingClasses: XOneOrManyType<string> =
    this.propertyProvider.scrollingClasses;

  @Input()
  scrollBehaviour: XOneOrManyType<string> =
    this.propertyProvider.scrollBehaviour;

  @Input()
  scrollEventActionProvider: Subject<XBehaviourEventType> =
    this.propertyProvider.scrollEventActionProvider;

  @Output()
  scrolledEvent: EventEmitter<XBehaviourEvent> = new EventEmitter();

  //
  @Input()
  toggleMenuWhen: XStandardType<XPageSizeIdentifier> =
    this.propertyProvider.toggleMenuWhen;

  @Input()
  hasSide: XStandardType<boolean> = this.propertyProvider.hasSide;

  @Input()
  sideSlot: XStandardType<XSideSlotIdentifier> = this.propertyProvider.sideSlot;

  @Input()
  sideType: XStandardType<XSideTypeIdentifier> = this.propertyProvider.sideType;

  @Input()
  sideColor: XStandardType<XColorIdentifier> = this.propertyProvider.sideColor;

  @Input()
  sideMenuWidth: XStandardType<number> = this.propertyProvider.sideMenuWidth;

  @Input()
  sideMenuMinWidth: XStandardType<number> =
    this.propertyProvider.sideMenuMinWidth;

  @Input()
  sideMenuMaxWidth: XStandardType<number> =
    this.propertyProvider.sideMenuMaxWidth;

  @Input()
  menuState: XStandardType<XMenuState> = this.propertyProvider.menuState;

  @Input()
  menuActionProvider: Subject<XMenuAction> =
    this.propertyProvider.menuActionProvider;

  @Output()
  splitterStateChanged: EventEmitter<boolean> = new EventEmitter();

  @Output()
  menuStateChange: EventEmitter<XMenuState> = new EventEmitter();

  //
  @Input()
  showPageRefresher: XStandardType<boolean> =
    this.propertyProvider.showPageRefresher;

  @Output()
  refreshPage: EventEmitter<XPageLoadingEvent> = new EventEmitter();

  @Input()
  refreshingText: XStandardType<string> = this.propertyProvider.refreshingText;

  @Input()
  refreshPullingText: XStandardType<string> =
    this.propertyProvider.refreshPullingText;

  @Input()
  refreshPullingIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.refreshPullingIcon;

  @Input()
  refreshingSpinner: XStandardType<XSpinnerIdentifier> =
    this.propertyProvider.refreshingSpinner;

  //
  @Input()
  showPageInfinityScroller: XStandardType<boolean> =
    this.propertyProvider.showPageInfinityScroller;

  @Input()
  infinityScrollDisabled: XStandardType<boolean> =
    this.propertyProvider.infinityScrollDisabled;

  @Input()
  infinityScrollThreshold: XStandardType<string> =
    this.propertyProvider.infinityScrollThreshold;

  @Input()
  infinityScrollerLoadingText: XStandardType<string> =
    this.propertyProvider.infinityScrollerLoadingText;

  @Input()
  infinityScrollerLoadingSpinner: XStandardType<XSpinnerIdentifier> =
    this.propertyProvider.infinityScrollerLoadingSpinner;

  @Output()
  loadMore: EventEmitter<XPageLoadingEvent> = new EventEmitter();

  //
  @Input()
  actions: XStandardType<XFabItem> = this.propertyProvider.actions;

  @Input()
  actionsActionProvider: Subject<XFabActionModel> =
    this.propertyProvider.actionsActionProvider;
  //#endregion

  //
  //#region Toolbar ...
  //
  @Input()
  showToolbar: XStandardType<boolean> = this.propertyProvider.showToolbar;

  //
  @Input()
  toolbarActionProvider: Subject<XToolbarActionModel> =
    this.propertyProvider.toolbarActionProvider;

  @Input()
  toolbarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.toolbarCssClass;

  //
  @Input()
  toolbarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarColor;

  //
  @Input()
  toolbarShowProgressBar: XStandardType<boolean> =
    this.propertyProvider.toolbarShowProgressBar;

  @Input()
  toolbarProgressBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarProgressBarColor;

  //
  @Input()
  toolbarShowTitle: XStandardType<boolean> =
    this.propertyProvider.toolbarShowTitle;

  @Input()
  toolbarTitleSlot: XStandardType<XTitleSlotIdentifier> =
    this.propertyProvider.toolbarTitleSlot;

  @Input()
  toolbarTitle: XStandardType<string> = this.propertyProvider.toolbarTitle;

  //
  @Input()
  toolbarShowSubTitle: XStandardType<boolean> =
    this.propertyProvider.toolbarShowSubTitle;

  @Input()
  toolbarSubTitle: XStandardType<string> =
    this.propertyProvider.toolbarSubTitle;

  //
  @Input()
  toolbarShowLogo: XStandardType<boolean> =
    this.propertyProvider.toolbarShowLogo;

  @Input()
  toolbarLogoSlot: XStandardType<XLogoSlotIdentifier> =
    this.propertyProvider.toolbarLogoSlot;

  @Input()
  toolbarLogoUrl: XStandardType<string> = this.propertyProvider.toolbarLogoUrl;

  //
  @Input()
  toolbarHasBack: XStandardType<boolean> = this.propertyProvider.toolbarHasBack;

  @Input()
  toolbarShowBack: XStandardType<boolean> =
    this.propertyProvider.toolbarShowBack;

  @Input()
  toolbarBackSlot: XStandardType<XBackSlotIdentifier> =
    this.propertyProvider.toolbarBackSlot;

  @Input()
  toolbarBackHandler?: () => XStandardType<void> = () => {
    this.managerService.historyService.back();
  };

  @Input()
  toolbarDefaultHref: string = this.propertyProvider.toolbarDefaultHref;

  //
  @Input()
  toolbarHasMenu: XStandardType<boolean> = this.propertyProvider.toolbarHasMenu;

  @Input()
  toolbarShowMenu: XStandardType<boolean> =
    this.propertyProvider.toolbarShowMenu;

  @Input()
  toolbarAutoHideMenu: XStandardType<boolean> =
    this.propertyProvider.toolbarAutoHideMenu;

  @Input()
  toolbarMenuSlot: XStandardType<XMenuSlotIdentifier> =
    this.propertyProvider.toolbarMenuSlot;

  //
  @Input()
  toolbarStartSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarStartSlotButtonsTemplate;

  @Input()
  toolbarEndSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarEndSlotButtonsTemplate;

  //
  @Input()
  toolbarContentSlot: XStandardType<XContentSlotIdentifier> =
    this.propertyProvider.toolbarContentSlot;

  @Input()
  toolbarContentTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarContentTemplate;
  //#endregion

  //
  //#region Footer ...
  //
  @Input()
  showFooter: XStandardType<boolean> = this.propertyProvider.showFooter;

  //
  @Input()
  footerActionProvider: Subject<XFooterActionModel> =
    this.propertyProvider.footerActionProvider;

  @Input()
  footerCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.footerCssClass;

  //
  @Input()
  footerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.footerColor;

  @Input()
  footerContentTemplate: TemplateRef<any> =
    this.propertyProvider.footerContentTemplate;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    public menuController: MenuController,
    protected propertyProvider: XPageContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXPageContainer
  ): Observable<IXPageContainer> {
    return forkJoin({
      //
      //#region Page ...
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),

      //
      color: this.getValue(container ? container.color : this.color),

      //
      enableCaptureMode: this.getValue(
        container ? container.enableCaptureMode : this.enableCaptureMode
      ),
      downloadCapturedImage: this.getValue(
        container ? container.downloadCapturedImage : this.downloadCapturedImage
      ),
      captureTopOffset: this.getValue(
        container ? container.captureTopOffset : this.captureTopOffset
      ),
      captureBottomOffset: this.getValue(
        container ? container.captureBottomOffset : this.captureBottomOffset
      ),

      //
      provideScrollEvents: this.getValue(
        container ? container.provideScrollEvents : this.provideScrollEvents
      ),
      scrollStartClasses: this.getValue(
        container ? container.scrollStartClasses : this.scrollStartClasses
      ),
      scrollEndClasses: this.getValue(
        container ? container.scrollEndClasses : this.scrollEndClasses
      ),
      scrollingClasses: this.getValue(
        container ? container.scrollingClasses : this.scrollingClasses
      ),
      scrollBehaviour: this.getValue(
        container ? container.scrollBehaviour : this.scrollBehaviour
      ),

      //
      toggleMenuWhen: this.getValue(
        container ? container.toggleMenuWhen : this.toggleMenuWhen
      ),
      hasSide: this.getValue(container ? container.hasSide : this.hasSide),
      sideSlot: this.getValue(container ? container.sideSlot : this.sideSlot),
      sideType: this.getValue(container ? container.sideType : this.sideType),
      sideColor: this.getValue(
        container ? container.sideColor : this.sideColor
      ),
      sideMenuWidth: this.getValue(
        container ? container.sideMenuWidth : this.sideMenuWidth
      ),
      sideMenuMinWidth: this.getValue(
        container ? container.sideMenuMinWidth : this.sideMenuMinWidth
      ),
      sideMenuMaxWidth: this.getValue(
        container ? container.sideMenuMaxWidth : this.sideMenuMaxWidth
      ),
      menuState: this.getValue(
        container ? container.menuState : this.menuState
      ),

      //
      showPageRefresher: this.getValue(
        container ? container.showPageRefresher : this.showPageRefresher
      ),
      refreshingText: this.getValue(
        container ? container.refreshingText : this.refreshingText
      ),
      refreshPullingText: this.getValue(
        container ? container.refreshPullingText : this.refreshPullingText
      ),
      refreshPullingIcon: this.getValue(
        container ? container.refreshPullingIcon : this.refreshPullingIcon
      ),
      refreshingSpinner: this.getValue(
        container ? container.refreshingSpinner : this.refreshingSpinner
      ),

      //
      showPageInfinityScroller: this.getValue(
        container
          ? container.showPageInfinityScroller
          : this.showPageInfinityScroller
      ),
      infinityScrollDisabled: this.getValue(
        container
          ? container.infinityScrollDisabled
          : this.infinityScrollDisabled
      ),
      infinityScrollThreshold: this.getValue(
        container
          ? container.infinityScrollThreshold
          : this.infinityScrollThreshold
      ),
      infinityScrollerLoadingText: this.getValue(
        container
          ? container.infinityScrollerLoadingText
          : this.infinityScrollerLoadingText
      ),
      infinityScrollerLoadingSpinner: this.getValue(
        container
          ? container.infinityScrollerLoadingSpinner
          : this.infinityScrollerLoadingSpinner
      ),

      //
      actions: this.getValue(container ? container.actions : this.actions),
      //#endregion

      //
      //#region Toolbar ...
      showToolbar: this.getValue(
        container ? container.showToolbar : this.showToolbar
      ),
      toolbarCssClass: this.getValue(
        container ? container.toolbarCssClass : this.toolbarCssClass
      ),
      toolbarColor: this.getValue(
        container ? container.toolbarColor : this.toolbarColor
      ),
      toolbarShowProgressBar: this.getValue(
        container
          ? container.toolbarShowProgressBar
          : this.toolbarShowProgressBar
      ),
      toolbarProgressBarColor: this.getValue(
        container
          ? container.toolbarProgressBarColor
          : this.toolbarProgressBarColor
      ),
      toolbarShowTitle: this.getValue(
        container ? container.toolbarShowTitle : this.toolbarShowTitle
      ),
      toolbarTitleSlot: this.getValue(
        container ? container.toolbarTitleSlot : this.toolbarTitleSlot
      ),
      toolbarTitle: this.getValue(
        container ? container.toolbarTitle : this.toolbarTitle
      ),
      toolbarShowSubTitle: this.getValue(
        container ? container.toolbarShowSubTitle : this.toolbarShowSubTitle
      ),
      toolbarSubTitle: this.getValue(
        container ? container.toolbarSubTitle : this.toolbarSubTitle
      ),
      toolbarShowLogo: this.getValue(
        container ? container.toolbarShowLogo : this.toolbarShowLogo
      ),
      toolbarLogoSlot: this.getValue(
        container ? container.toolbarLogoSlot : this.toolbarLogoSlot
      ),
      toolbarLogoUrl: this.getValue(
        container ? container.toolbarLogoUrl : this.toolbarLogoUrl
      ),
      toolbarHasBack: this.getValue(
        container ? container.toolbarHasBack : this.toolbarHasBack
      ),
      toolbarShowBack: this.getValue(
        container ? container.toolbarShowBack : this.toolbarShowBack
      ),
      toolbarBackSlot: this.getValue(
        container ? container.toolbarBackSlot : this.toolbarBackSlot
      ),
      toolbarBackHandler: this.getValue(
        container ? container.toolbarBackHandler : this.toolbarBackHandler
      ),
      toolbarDefaultHref: this.getValue(
        container ? container.toolbarDefaultHref : this.toolbarDefaultHref
      ),
      toolbarHasMenu: this.getValue(
        container ? container.toolbarHasMenu : this.toolbarHasMenu
      ),
      toolbarShowMenu: this.getValue(
        container ? container.toolbarShowMenu : this.toolbarShowMenu
      ),
      toolbarAutoHideMenu: this.getValue(
        container ? container.toolbarAutoHideMenu : this.toolbarAutoHideMenu
      ),
      toolbarMenuSlot: this.getValue(
        container ? container.toolbarMenuSlot : this.toolbarMenuSlot
      ),
      toolbarStartSlotButtonsTemplate: this.getValue(
        container
          ? container.toolbarStartSlotButtonsTemplate
          : this.toolbarStartSlotButtonsTemplate
      ),
      toolbarEndSlotButtonsTemplate: this.getValue(
        container
          ? container.toolbarEndSlotButtonsTemplate
          : this.toolbarEndSlotButtonsTemplate
      ),
      toolbarContentSlot: this.getValue(
        container ? container.toolbarContentSlot : this.toolbarContentSlot
      ),
      toolbarContentTemplate: this.getValue(
        container
          ? container.toolbarContentTemplate
          : this.toolbarContentTemplate
      ),
      //#endregion

      //
      //#region Footer ...
      showFooter: this.getValue(
        container ? container.showFooter : this.showFooter
      ),
      footerCssClass: this.getValue(
        container ? container.footerCssClass : this.footerCssClass
      ),
      footerColor: this.getValue(
        container ? container.footerColor : this.footerColor
      ),
      footerContentTemplate: this.getValue(
        container ? container.footerContentTemplate : this.footerContentTemplate
      ),
      //#endregion
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          toolbarActionProvider: container
            ? container.toolbarActionProvider
            : this.toolbarActionProvider,

          //
          footerActionProvider: container
            ? container.footerActionProvider
            : this.footerActionProvider,

          //
          menuActionProvider: container
            ? container.menuActionProvider
            : this.menuActionProvider,

          //
          scrollEventActionProvider: container
            ? container.scrollEventActionProvider
            : this.scrollEventActionProvider,

          //
          actionsActionProvider: container
            ? container.actionsActionProvider
            : this.actionsActionProvider,

          //
          imageCaptured: this.imageCaptured,
          splitterStateChanged: this.splitterStateChanged,
          menuStateChange: this.menuStateChange,
          scrolledEvent: this.scrolledEvent,
          refreshPage: this.refreshPage,
          loadMore: this.loadMore,
        };
      })
    );
  }
  //#endregion
}

//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-page.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XPageComponent } from './x-page.component';
import { XFabModule } from './../x-fab/x-fab.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XButtonModule } from '../x-button/x-button.module';
import { XFooterComponent } from './x-footer/x-footer.component';
import { XToolbarComponent } from './x-toolbar/x-toolbar.component';
import { XNavigatorComponent } from './x-navigator/x-navigator.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XScreenCaptureModule } from '../x-screen-capture/x-screen-capture.module';
import { XExpandableListModule } from '../x-expandable-list/x-expandable-list.module';
import { XExpandablePanelModule } from '../x-expandable-panel/x-expandable-panel.module';

@NgModule({
  imports: [
    XFabModule,
    XIconModule,
    CommonModule,
    XButtonModule,
    XBaseComponentModule,
    XScreenCaptureModule,
    XExpandableListModule,
    XExpandablePanelModule,
  ],
  declarations: [
    XPageComponent,
    XFooterComponent,
    XToolbarComponent,
    XNavigatorComponent,
  ],
  exports: [
    XFabModule,
    XIconModule,
    CommonModule,
    XButtonModule,
    XPageComponent,
    XFooterComponent,
    XToolbarComponent,
    XNavigatorComponent,
    XBaseComponentModule,
    XScreenCaptureModule,
    XExpandableListModule,
    XExpandablePanelModule,
  ],
})
export class XPageModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-page.typings.ts


export enum XMenuState {
  WillOpen,
  Opened,
  WillClose,
  Closed,
}

export enum XMenuAction {
  Open,
  Close,
  Toggle,
}

export enum XPageSize {
  ExtraSmall = 'xs',
  Small = 'sm',
  Medium = 'md',
  Large = 'lg',
  ExtraLarge = 'xl',
}

export type XPageSizeIdentifier = XPageSize | string;

export enum XSideType {
  Overlay = 'overlay',
  Reveal = 'reveal',
  Push = 'push',
}

export type XSideTypeIdentifier = XSideType | string;

export enum XBackSlot {
  Start = 'start',
  End = 'end',
}

export type XBackSlotIdentifier = XBackSlot | string;

export enum XMenuSlot {
  Start = 'start',
  End = 'end',
}

export type XMenuSlotIdentifier = XMenuSlot | string;

export enum XLogoSlot {
  Start = 'start',
  End = 'end',
}

export type XLogoSlotIdentifier = XLogoSlot | string;

export enum XTitleSlot {
  Start = 'start',
  End = 'end',
}

export type XTitleSlotIdentifier = XTitleSlot | string;

export enum XContentSlot {
  Start = 'start',
  Middle = 'middle',
  End = 'end',
}

export type XContentSlotIdentifier = XContentSlot | string;

export enum XSideSlot {
  Start = 'start',
  End = 'end',
}

export type XSideSlotIdentifier = XSideSlot | string;

export interface XPageLoadingEvent {
  type: 'InfinityScrollerLoader' | 'RefreshLoader';
  event: any;
}

export enum XPageCSSVars {
  SideMenuWidth = 'side-menu-width',
  SideMenuMinWidth = 'side-menu-min-width',
  SideMenuMaxWidth = 'side-menu-max-width',
  SideMenuContentHeightGutter = 'side-menu-content-height-gutter',
  SideMenuContentOffsetTop = 'side-menu-content-offset-top',
  SideMenuContentOffsetBottom = 'side-menu-content-offset-bottom',
  ContentHeightGutter = 'content-height-gutter',
  ContentOffsetTop = 'content-offset-top',
  ContentOffsetBottom = 'content-offset-bottom',
}

export type XPageCSSVarsIdentifier = XPageCSSVars | string;


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-footer\x-footer.component.html

<ion-footer
  class="x-footer x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
>
  <ion-toolbar [color]="getValue(color) | async">
    <ion-title class="ion-text-center">
      <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
    </ion-title>
  </ion-toolbar>
</ion-footer>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-footer\x-footer.component.scss

:host {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-footer\x-footer.component.ts

import {
  XFooterAction,
  XFooterActionModel,
  XFooterContainerComponent,
} from './x-footer.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-footer',
  templateUrl: './x-footer.component.html',
  styleUrls: ['./x-footer.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XFooterComponent extends XFooterContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XFooterComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XFooterActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.actionProvider.asObservable()
    ).subscribe(async (model) => {
      //
      if (!model) {
        return;
      }

      //
      switch (model.action) {
        //
        case XFooterAction.Refresh:
          this.detectChanges();
          break;
      }
    });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-footer\x-footer.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
  XBaseComponentContainer,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XFooterAction {
  Refresh = 'refresh',
}

export type XFooterActionIdentifier = XFooterAction | string;

export interface XFooterActionModel
  extends XBaseActionModel<XFooterActionIdentifier> {}

//
//#region Container ...
export interface IXFooterContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XFooterActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  contentTemplate: TemplateRef<any>;
}

@Injectable({
  providedIn: 'root'
})
export class XFooterContainer
  extends XBaseContainer
  implements Readonly<IXFooterContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XFooterActionModel> =
    new Subject<XFooterActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly contentTemplate: TemplateRef<any>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XFooterContainerComponent
  extends XBaseContainerComponent
  implements IXFooterContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XFooterActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  contentTemplate: TemplateRef<any> = this.propertyProvider.contentTemplate;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XFooterContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXFooterContainer
  ): Observable<IXFooterContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      color: this.getValue(container ? container.color : this.color),
      contentTemplate: this.getValue(
        container ? container.contentTemplate : this.contentTemplate
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentFooterContainer = {
  [TKey in keyof IXFooterContainer as `footer${Capitalize<
    string & TKey
  >}`]: IXFooterContainer[TKey];
};

export interface IXComponentFooterContainer
  extends TXComponentFooterContainer,
    IXBaseComponentContainer {
  showFooter: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root'
})
export class XComponentFooterContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentFooterContainer>
{
  //
  //#region Props ...
  //
  readonly showFooter: XStandardType<boolean> = true;

  //
  readonly footerActionProvider: Subject<XFooterActionModel> =
    this.propertyProvider.actionProvider;
  readonly footerCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly footerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;
  readonly footerContentTemplate: TemplateRef<any> =
    this.propertyProvider.contentTemplate;
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XFooterContainer) {
    super();
  }
  //#endregion
}

@Injectable({
  providedIn: 'root'
})
@Component({
  template: `
    <!-- 
        <ng-container *ngIf="extractProps() | async as props">
            <ng-container *ngIf="props.showFooter">
              <x-footer
                [footerColor]="props.footerColor"
                [footerCssClass]="props.footerCssClass"
                [footerActionProvider]="props.footerActionProvider"
                [footerContentTemplate]="props.footerContentTemplate"              
              >
              </x-footer>
            </ng-container> 
        </ng-container> 
    -->
  `,
})
export abstract class XComponentFooterContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentFooterContainer
{
  //
  //#region Props ...
  //
  @Input()
  showFooter: XStandardType<boolean> = this.propertyProvider.showFooter;

  //
  @Input()
  footerActionProvider: Subject<XFooterActionModel> =
    this.propertyProvider.footerActionProvider;

  @Input()
  footerCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.footerCssClass;

  //
  @Input()
  footerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.footerColor;

  @Input()
  footerContentTemplate: TemplateRef<any> =
    this.propertyProvider.footerContentTemplate;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentFooterContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentFooterContainer
  ): Observable<IXComponentFooterContainer> {
    return forkJoin({
      //
      showFooter: this.getValue(
        container ? container.showFooter : this.showFooter
      ),
      footerCssClass: this.getValue(
        container ? container.footerCssClass : this.footerCssClass
      ),
      footerColor: this.getValue(
        container ? container.footerColor : this.footerColor
      ),
      footerContentTemplate: this.getValue(
        container ? container.footerContentTemplate : this.footerContentTemplate
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          footerActionProvider: container
            ? container.footerActionProvider
            : this.footerActionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-navigator\x-navigator.component.html

<x-expandable-list
  [loading]="loading"
  [supportRouting]="true"
  [uiDisabled]="uiDisabled"
  class="x-navigator x-wrapper"
  [childTemplate]="childTempRef"
  [parentTemplate]="parentTempRef"
  [class.mobile]="isMobileUi$ | async"
  [cssClass]="getValue(cssClass) | async"
  (itemSelected)="itemSelected.emit($event)"
  [childColor]="getValue(childColor) | async"
  [parentColor]="getValue(parentColor) | async"
  [items]="getArrayValue(preparedItems) | async"
  [startupState]="getValue(startupState) | async"
  [itemCssClass]="getValue(itemCssClass) | async"
  [dividerColor]="getValue(dividerColor) | async"
  [closeSiblings]="getValue(closeSiblings) | async"
  [highlightColor]="getValue(highlightColor) | async"
  [childActiveColor]="getValue(childActiveColor) | async"
  [parentActiveColor]="getValue(parentActiveColor) | async"
  [showChildDetailIcon]="getValue(showChildDetailIcon) | async"
  [showParentDetailIcon]="getValue(showParentDetailIcon) | async"
  [disableParentRouting]="getValue(disableParentRouting) | async"
>
</x-expandable-list>

<ng-template #parentTempRef let-item>
  <div
    fxLayout="row"
    fxLayoutAlign="start center"
    class="nav-item nav-item-parent"
    [ngClass]="getValue(itemCssClass) | async"
  >
    <div class="start">
      <ng-container *ngIf="isStartSlotted() | async">
        <ng-container
          *ngTemplateOutlet="iconRef; context: { $implicit: item }"
        ></ng-container>
      </ng-container>
    </div>
    <div fxFill class="center">
      <h4 class="x-nav-item-title ion-no-margin ion-no-padding text-truncate">
        {{ getResourceValue(item.title) | async }}
      </h4>
    </div>
    <div class="end">
      <ng-container *ngIf="isEndSlotted() | async">
        <ng-container
          *ngTemplateOutlet="iconRef; context: { $implicit: item }"
        ></ng-container>
      </ng-container>
    </div>
  </div>
</ng-template>

<ng-template #childTempRef let-item>
  <div
    fxLayout="row"
    fxLayoutAlign="start center"
    class="nav-item nav-item-child"
    [ngClass]="getValue(itemCssClass) | async"
  >
    <div class="start" *ngIf="getValue(showChildIcon) | async">
      <ng-container *ngIf="isStartSlotted() | async">
        <ng-container
          *ngTemplateOutlet="iconRef; context: { $implicit: item }"
        ></ng-container>
      </ng-container>
    </div>
    <div fxFill class="center">
      <h4 class="x-nav-item-title ion-no-margin ion-no-padding text-truncate">
        {{ getResourceValue(item.title) | async }}
      </h4>
    </div>
    <div class="end" *ngIf="getValue(showChildIcon) | async">
      <ng-container *ngIf="isEndSlotted() | async">
        <ng-container
          *ngTemplateOutlet="iconRef; context: { $implicit: item }"
        ></ng-container>
      </ng-container>
    </div>
  </div>
</ng-template>

<ng-template #iconRef let-item>
  <x-icon [cssClass]="'cb-inherit'" [name]="getValue(item.icon) | async">
  </x-icon>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-navigator\x-navigator.component.scss

.nav-item > * {
  display: inline-block;
  position: relative;
  margin: 0;
  padding: 0;
}

.x-nav-item-title {
  max-width: 150px;
  margin: 4px;
}

.nav-item-parent {
  .x-nav-item-title {
    font-size: 10pt;
    font-weight: bold;
  }
}

.nav-item-child {
  .x-nav-item-title {
    font-size: 9pt;
    font-weight: normal;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-navigator\x-navigator.component.ts

import {
  XNavigatorAction,
  XNavigatorActionModel,
  XNavigatorItemIconSlot,
  XNavigatorContainerComponent,
  XNavigatorListItem,
} from './x-navigator.typings';
import { map } from 'rxjs/operators';
import { Observable, Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XStandardType, XOneOrManyType, nameof } from 'x-framework-core';
import { XExpandableListItem } from '../../x-expandable-list/x-expandable-list.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-navigator',
  templateUrl: './x-navigator.component.html',
  styleUrls: ['./x-navigator.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XNavigatorComponent extends XNavigatorContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private EXPANDABLE_LIST_ITEMS: XOneOrManyType<XExpandableListItem<any>>;

  get preparedItems() {
    return this.EXPANDABLE_LIST_ITEMS;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.prepareItems();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XNavigatorComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Items Changed ...
    const isItemsChanged = changeKeys.includes(
      nameof<XNavigatorComponent>('items')
    );
    if (isItemsChanged) {
      await this.prepareItems();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  isEndSlotted() {
    return this.getValue(this.iconSlot).pipe(
      map((iconSlot) => iconSlot === XNavigatorItemIconSlot.End)
    );
  }

  isStartSlotted() {
    return this.getValue(this.iconSlot).pipe(
      map((iconSlot) => iconSlot === XNavigatorItemIconSlot.Start)
    );
  }
  //#endregion

  //
  //#region UI Actions ...
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XNavigatorActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.actionProvider.asObservable()
    ).subscribe(async (model) => {
      //
      if (!model) {
        return;
      }

      //
      switch (model.action) {
        //
        case XNavigatorAction.Refresh:
          this.detectChanges();
          break;
      }
    });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async prepareItems() {
    //
    this.EXPANDABLE_LIST_ITEMS = null;
    this.detectChanges();

    //
    const items = await this.getArrayValueAsync(this.items);
    const preparedItems = await Promise.all(
      items.map(async (i) => await this.toExpandableListItem(i))
    );

    //
    this.EXPANDABLE_LIST_ITEMS = [
      ...preparedItems
    ];

    //
    this.detectChanges();
  }

  private async toExpandableListItem(item: XNavigatorListItem) {
    //
    const result: XExpandableListItem<any> = {
      //
      ...item,
      data: '',
      childs: [
        ...(await Promise.all(
          (item.childs ? await this.getArrayValueAsync(item.childs) : []).map(
            async (c) => await this.toExpandableListItem(c)
          )
        )),
      ],
    };

    //
    return result;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-navigator\x-navigator.tools.ts

import {
  XPage,
  isArray,
  toArray,
  hasChild,
  mergeRoutes,
  XOneOrManyType,
} from 'x-framework-core';
import { XNavigatorListItem } from './x-navigator.typings';

export function extractParent(source: XOneOrManyType<XPage>): any {
  //
  if (!source) {
    return null;
  }

  //
  const isArrayOfPages = isArray(source);
  const page = source as XPage;
  const pages = source as XPage[];

  //
  if (isArrayOfPages) {
    //
    // Handle Array of Pages ...
    return pages.filter((p) => hasChild(p.childs));
  } else {
    //
    // Handle On Page ...
    return extractParent(page.childs);
  }
}

export function extractChilds(source: XOneOrManyType<XPage>) {
  //
  if (!source) {
    return null;
  }

  //
  const isArrayOfPages = isArray(source);
  const page = source as XPage;
  const pages = source as XPage[];

  //
  if (isArrayOfPages) {
    //
    // Handle Array of Pages ...
    return pages.filter((p) => !hasChild(p.childs));
  } else {
    //
    // Handle On Page ...
    return extractParent(page.childs);
  }
}

export function toFlat(pages: XOneOrManyType<XPage>) {
  //
  if (!pages || !hasChild(pages)) {
    return [];
  }

  //
  let result: XPage[] = [];

  //
  const pPages = toArray(pages);
  pPages.forEach((p) => {
    //
    result.push(p);

    //
    if (p.childs && hasChild(p.childs)) {
      //
      const childsFlat = toFlat(p.childs);
      result = result.concat(childsFlat);
    }
  });

  //
  return result;
}

export function toNavigatiorListItem(page: XPage) {
  //
  if (!page) {
    return null;
  }

  //
  const pageRoute = mergeRoutes(page.route);

  //
  const result: XNavigatorListItem = {
    id: page.id ? `${page.id}_(${pageRoute})` : `${page.name}_(${pageRoute})`,
    opened: false,
    hidden: false,
    disabled: false,
    icon: page.icon,
    href: page.route,
    replaceUrl: true,
    title: page.title,
    description: page.description,
    childs: hasChild(page.childs)
      ? toArray(page.childs).map((c) => toNavigatiorListItem(c))
      : null,
  };

  //
  return result;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-navigator\x-navigator.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../../x-icon/x-icon.typings';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';
import { XExpandableListItem } from '../../x-expandable-list/x-expandable-list.typings';

export type XNavigatorListItemBase = Pick<
  XExpandableListItem<any>,
  | 'id'
  | 'href'
  | 'title'
  | 'opened'
  | 'hidden'
  | 'disabled'
  | 'replaceUrl'
  | 'description'
>;
export interface XNavigatorListItem extends XNavigatorListItemBase {
  icon?: XStandardType<XIconIdentifier>;
  childs?: XStandardType<XOneOrManyType<XNavigatorListItem>>;
}

export enum XNavigatorItemIconSlot {
  Start = 'start',
  End = 'end',
}

export type XNavigatorItemIconSlotIdentifier = XNavigatorItemIconSlot | string;

export enum XNavigatorStartupState {
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',
}

export type XNavigatorStartupStateIdentifier = XNavigatorStartupState | string;

export enum XNavigatorAction {
  Refresh = 'refresh',
}

export type XNavigatorActionIdentifier = XNavigatorAction | string;

export interface XNavigatorActionModel
  extends XBaseActionModel<XNavigatorActionIdentifier> {}

//
//#region Container ...
export interface IXNavigatorContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XNavigatorActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  itemCssClass: XStandardType<XOneOrManyType<string>>;
  items: XStandardType<XOneOrManyType<XNavigatorListItem>>;

  //
  closeSiblings: XStandardType<boolean>;
  iconSlot: XStandardType<XNavigatorItemIconSlotIdentifier>;
  startupState: XStandardType<XNavigatorStartupStateIdentifier>;

  //
  showChildIcon: XStandardType<boolean>;
  showChildDetailIcon: XStandardType<boolean>;
  showParentIcon: XStandardType<boolean>;
  showParentDetailIcon: XStandardType<boolean>;
  disableParentRouting: XStandardType<boolean>;

  //
  childColor: XStandardType<XColorIdentifier>;
  childActiveColor: XStandardType<XColorIdentifier>;
  parentColor: XStandardType<XColorIdentifier>;
  parentActiveColor: XStandardType<XColorIdentifier>;
  highlightColor: XStandardType<XColorIdentifier>;
  dividerColor: XStandardType<XColorIdentifier>;

  //
  itemSelected: EventEmitter<XNavigatorListItem>;
}

@Injectable({
  providedIn: 'root',
})
export class XNavigatorContainer
  extends XBaseContainer
  implements Readonly<IXNavigatorContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XNavigatorActionModel> =
    new Subject<XNavigatorActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly itemCssClass: XStandardType<XOneOrManyType<string>>;
  readonly items: XStandardType<XOneOrManyType<XNavigatorListItem>>;

  //
  readonly closeSiblings: XStandardType<boolean> = true;
  readonly iconSlot: XStandardType<XNavigatorItemIconSlotIdentifier> =
    XNavigatorItemIconSlot.Start;
  readonly startupState: XStandardType<XNavigatorStartupStateIdentifier> =
    XNavigatorStartupState.ExpandAll;

  //
  readonly showChildIcon: XStandardType<boolean> = true;
  readonly showChildDetailIcon: XStandardType<boolean> = true;
  readonly showParentIcon: XStandardType<boolean> = true;
  readonly showParentDetailIcon: XStandardType<boolean> = true;
  readonly disableParentRouting: XStandardType<boolean> = true;

  //
  readonly childColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.PrimaryShade;
  readonly childActiveColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly parentColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.TertiaryTint;
  readonly parentActiveColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly highlightColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;
  readonly dividerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  //
  readonly itemSelected: EventEmitter<XNavigatorListItem> =
    new EventEmitter<XNavigatorListItem>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XNavigatorContainerComponent
  extends XBaseContainerComponent
  implements IXNavigatorContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XNavigatorActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  itemCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.itemCssClass;

  @Input()
  items: XStandardType<XOneOrManyType<XNavigatorListItem>> =
    this.propertyProvider.items;

  //
  @Input()
  closeSiblings: XStandardType<boolean> = this.propertyProvider.closeSiblings;

  @Input()
  iconSlot: XStandardType<XNavigatorItemIconSlotIdentifier> =
    this.propertyProvider.iconSlot;

  @Input()
  startupState: XStandardType<XNavigatorStartupStateIdentifier> =
    this.propertyProvider.startupState;

  //
  @Input()
  showChildIcon: XStandardType<boolean> = this.propertyProvider.showChildIcon;

  @Input()
  showChildDetailIcon: XStandardType<boolean> =
    this.propertyProvider.showChildDetailIcon;

  @Input()
  showParentIcon: XStandardType<boolean> = this.propertyProvider.showParentIcon;

  @Input()
  showParentDetailIcon: XStandardType<boolean> =
    this.propertyProvider.showParentDetailIcon;

  @Input()
  disableParentRouting: XStandardType<boolean> =
    this.propertyProvider.disableParentRouting;

  //
  @Input()
  childColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.childColor;

  @Input()
  childActiveColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.childActiveColor;

  @Input()
  parentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.parentColor;

  @Input()
  parentActiveColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.parentActiveColor;

  @Input()
  highlightColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.highlightColor;

  @Input()
  dividerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.dividerColor;

  //
  @Output()
  itemSelected: EventEmitter<XNavigatorListItem> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XNavigatorContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXNavigatorContainer
  ): Observable<IXNavigatorContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      itemCssClass: this.getValue(
        container ? container.itemCssClass : this.itemCssClass
      ),
      items: this.getValue(container ? container.items : this.items),
      closeSiblings: this.getValue(
        container ? container.closeSiblings : this.closeSiblings
      ),
      iconSlot: this.getValue(container ? container.iconSlot : this.iconSlot),
      startupState: this.getValue(
        container ? container.startupState : this.startupState
      ),
      showChildIcon: this.getValue(
        container ? container.showChildIcon : this.showChildIcon
      ),
      showChildDetailIcon: this.getValue(
        container ? container.showChildDetailIcon : this.showChildDetailIcon
      ),
      showParentIcon: this.getValue(
        container ? container.showParentIcon : this.showParentIcon
      ),
      showParentDetailIcon: this.getValue(
        container ? container.showParentDetailIcon : this.showParentDetailIcon
      ),
      disableParentRouting: this.getValue(
        container ? container.disableParentRouting : this.disableParentRouting
      ),
      childColor: this.getValue(
        container ? container.childColor : this.childColor
      ),
      childActiveColor: this.getValue(
        container ? container.childActiveColor : this.childActiveColor
      ),
      parentColor: this.getValue(
        container ? container.parentColor : this.parentColor
      ),
      parentActiveColor: this.getValue(
        container ? container.parentActiveColor : this.parentActiveColor
      ),
      highlightColor: this.getValue(
        container ? container.highlightColor : this.highlightColor
      ),
      dividerColor: this.getValue(
        container ? container.dividerColor : this.dividerColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          itemSelected: this.itemSelected,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-toolbar\x-toolbar.component.html

<ion-header>
  <!-- Toolbar -->
  <ion-toolbar
    matRipple
    [color]="getValue(color) | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <div class="x-toolbar-slot-container">
      <!-- Start Slot Content -->
      <div slot="start" class="x-toolbar-slot x-toolbar-start-slot">
        <!-- Start Slot Buttons -->
        <ion-buttons>
          <!-- Back Button -->
          <ng-container
            *ngIf="(getValue(showBack) | async) && (isStartSlotBack() | async)"
          >
            <ng-container *ngTemplateOutlet="backButtonRef"></ng-container>
          </ng-container>

          <!-- Menu Button -->
          <ng-container
            *ngIf="(getValue(showMenu) | async) && (isStartSlotMenu() | async)"
          >
            <ion-menu-button
              *ngIf="getValue(hasMenu) | async"
              (click)="handleMenuButtonClicked()"
              [autoHide]="getValue(autoHideMenu) | async"
            ></ion-menu-button>
          </ng-container>

          <!-- Custom Start Buttons -->
          <ng-container *ngIf="startSlotButtonsTemplate">
            <ng-container
              *ngTemplateOutlet="startSlotButtonsTemplate"
            ></ng-container>
          </ng-container>
        </ion-buttons>

        <!-- Logo -->
        <ion-thumbnail
          class="x-toolbar-thumbnail"
          *ngIf="(getValue(showLogo) | async) && (isStartSlotLogo() | async)"
        >
          <ion-img [src]="getValue(logoUrl) | async"></ion-img>
        </ion-thumbnail>

        <!-- Title and SubTitle -->
        <span
          class="x-toolbar-title-wrapper ion-text-start"
          *ngIf="(getValue(showTitle) | async) && (isStartSlotTitle() | async)"
        >
          <ng-container *ngTemplateOutlet="titleSubTitleRef"></ng-container>
        </span>

        <!-- Custom Content -->
        <ng-container *ngIf="contentTemplate && (isStartSlotContent() | async)">
          <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
        </ng-container>
      </div>

      <!-- Middle Content -->
      <div
        class="x-toolbar-slot x-toolbar-middle-slot"
        *ngIf="contentTemplate && (isMiddleSlotContent() | async)"
      >
        <!-- Custom Content -->
        <ng-container
          *ngIf="contentTemplate && (isMiddleSlotContent() | async)"
        >
          <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
        </ng-container>
      </div>

      <!-- End Slot Content -->
      <div class="x-toolbar-slot x-toolbar-end-slot" slot="end">
        <!-- Custom Content -->
        <ng-container *ngIf="contentTemplate && (isEndSlotContent() | async)">
          <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
        </ng-container>

        <!-- Title and SubTitle -->
        <span
          class="x-toolbar-title-wrapper ion-text-end"
          *ngIf="(getValue(showTitle) | async) && (isEndSlotTitle() | async)"
        >
          <ng-container *ngTemplateOutlet="titleSubTitleRef"></ng-container>
        </span>

        <!-- Logo -->
        <ion-thumbnail
          class="x-toolbar-thumbnail"
          *ngIf="(getValue(showLogo) | async) && (isEndSlotLogo() | async)"
        >
          <ion-img [src]="getValue(logoUrl) | async"></ion-img>
        </ion-thumbnail>

        <!-- End Slot Buttons -->
        <ion-buttons>
          <!-- Custom Start Buttons -->
          <ng-container *ngIf="endSlotButtonsTemplate">
            <ng-container
              *ngTemplateOutlet="endSlotButtonsTemplate"
            ></ng-container>
          </ng-container>

          <!-- Menu Button -->
          <ng-container
            *ngIf="(getValue(showMenu) | async) && (isEndSlotMenu() | async)"
          >
            <ion-menu-button
              *ngIf="getValue(hasMenu) | async"
              (click)="handleMenuButtonClicked()"
              [autoHide]="getValue(autoHideMenu) | async"
            ></ion-menu-button>
          </ng-container>

          <!-- Back Button -->
          <ng-container
            *ngIf="(getValue(showBack) | async) && (isEndSlotBack() | async)"
          >
            <ng-container *ngTemplateOutlet="backButtonRef"></ng-container>
          </ng-container>
        </ion-buttons>
      </div>
    </div>
  </ion-toolbar>

  <!-- Progressbar for Loading -->
  <ion-progress-bar
    type="indeterminate"
    *ngIf="getValue(showProgressBar) | async"
    [color]="getValue(progressBarColor) | async"
  ></ion-progress-bar>
</ion-header>

<!-- Back Bottun Template -->
<ng-template #backButtonRef>
  <x-button
    class="x-toolbar-back"
    [type]="ButtonTypes.Icon"
    [title]="ResourceIDs.back"
    [color]="getValue(color) | async"
    (clicked)="handleBackButtonClicked($event)"
  >
    <x-icon
      [foregroundColor]="false"
      [name]="IconNames.arrow_back"
      [color]="getValue(color) | async"
    ></x-icon>
  </x-button>
</ng-template>

<!-- Title/SubTitle Template -->
<ng-template #titleSubTitleRef>
  <!-- Title -->
  <h3
    [class.mobile]="managerService.isMobileUi$ | async"
    class="x-toolbar-title ion-no-padding ion-no-margin"
    [class.text-truncate]="managerService.isMobileUi$ | async"
  >
    {{ getValue(title) | async }}
  </h3>

  <!-- SubTitle -->
  <h5
    *ngIf="getValue(showSubTitle) | async"
    [matTooltip]="getValue(subTitle) | async"
    [class.mobile]="managerService.isMobileUi$ | async"
    class="x-toolbar-sub-title ion-no-margin ion-no-padding"
    [class.text-truncate]="managerService.isMobileUi$ | async"
  >
    {{ getValue(subTitle) | async }}
  </h5>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-toolbar\x-toolbar.component.scss

.x-toolbar-slot,
.x-toolbar-start-slot,
.x-toolbar-middle-slot,
.x-toolbar-end-slot {
  visibility: inherit;
}

.x-toolbar-start-slot,
.x-toolbar-middle-slot,
.x-toolbar-end-slot {
  margin: {
    left: 4px;
    right: 4px;
  }
}

.x-toolbar-slot-container {
  display: flex;
}

.x-toolbar-slot {
  display: flex;
  flex: 1;
}

.x-toolbar-slot > *,
.x-toolbar-slot-container > * {
  margin: {
    left: 2px;
    right: 2px;
  }

  width: max-content;
}

ion-thumbnail {
  width: 48px !important;
  height: 48px !important;
}

ion-img {
  object-fit: contain !important;
}

:host {
  --menu-button-transform: rotate(90deg);
  --menu-button-transform-duration: 500ms;
}

:host.menu-opened {
  ion-menu-button {
    //
    transform: var(--menu-button-transform);
    -ms-transform: var(--menu-button-transform);
    -webkit-transform: var(--menu-button-transform);

    //
    transition: transform;
    -webkit-transition: transform;

    //
    transition-duration: var(--menu-button-transform-duration);
    -webkit-transition-duration: var(--menu-button-transform-duration);
  }
}

ion-menu-button {
  //
  transition-duration: var(--menu-button-transform-duration);
  -webkit-transition-duration: var(--menu-button-transform-duration);
}

.x-toolbar-title-wrapper {
  padding-inline-end: 16px;
  padding-inline-start: 0;

  display: block;
  margin: {
    top: auto;
    bottom: auto;
  }
}

.x-toolbar-title,
.x-toolbar-sub-title {
  display: block !important;
}

.x-toolbar-back {
}

.mobile {
  max-width: 150px;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-toolbar\x-toolbar.component.ts

import {
  XParam,
  nameof,
  isNullOrEmptyString,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XToolbarAction,
  XToolbarActionModel,
  XToolbarContainerComponent,
} from './x-toolbar.typings';
import { map } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import { XIconNames } from '../../x-icon/x-icon.typings';
import { XMenuSlot, XBackSlot } from './../x-page.typings';
import { XButtonType } from '../../x-button/x-button.typings';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XLogoSlot, XTitleSlot, XContentSlot } from '../x-page.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-toolbar',
  templateUrl: './x-toolbar.component.html',
  styleUrls: ['./x-toolbar.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XToolbarComponent extends XToolbarContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly IconNames = Object.assign({}, XIconNames);
  readonly ButtonTypes = Object.assign({}, XButtonType);
  readonly ColorNames = Object.assign({}, XColorWithBrightness);
  //#endregion

  //
  //#region LifeCycle ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XToolbarComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }

  //#endregion

  //
  //#region Reading Query Params ...
  //#endregion

  //
  //#region Providers ...
  getBackHref() {
    //
    const isContainReturnUrl = this.managerService.isContainQueryParam(
      XParam.ReturnUrl
    );

    //
    return !isNullOrEmptyString(this.defaultHref)
      ? this.defaultHref
      : !isContainReturnUrl
      ? null
      : this.managerService.getQueryParamValOfType<string>(XParam.ReturnUrl);
  }

  canBack() {
    //
    return this.getValue(this.hasBack).pipe(
      map((hasBack) => {
        //
        if (
          !hasBack &&
          this.managerService.isContainQueryParam(XParam.HasBack)
        ) {
          hasBack =
            hasBack ||
            this.managerService.getQueryParamValOfType<boolean>(XParam.HasBack);
        }

        //
        return hasBack;
      })
    );
  }

  //
  //#region Slot Providers ...
  isEndSlotBack() {
    return this.getValue(this.backSlot).pipe(
      map((slot) => slot === XBackSlot.End)
    );
  }

  isStartSlotBack() {
    return this.getValue(this.backSlot).pipe(
      map((slot) => slot === XBackSlot.Start)
    );
  }

  isEndSlotMenu() {
    return this.getValue(this.menuSlot).pipe(
      map((slot) => slot === XMenuSlot.End)
    );
  }

  isStartSlotMenu() {
    return this.getValue(this.menuSlot).pipe(
      map((slot) => slot === XMenuSlot.Start)
    );
  }

  isEndSlotLogo() {
    return this.getValue(this.logoSlot).pipe(
      map((slot) => slot === XLogoSlot.End)
    );
  }

  isStartSlotLogo() {
    return this.getValue(this.logoSlot).pipe(
      map((slot) => slot === XLogoSlot.Start)
    );
  }

  isEndSlotTitle() {
    return this.getValue(this.titleSlot).pipe(
      map((slot) => slot === XTitleSlot.End)
    );
  }

  isStartSlotTitle() {
    return this.getValue(this.titleSlot).pipe(
      map((slot) => slot === XTitleSlot.Start)
    );
  }

  isEndSlotContent() {
    return this.getValue(this.contentSlot).pipe(
      map((slot) => slot === XContentSlot.End)
    );
  }

  isMiddleSlotContent() {
    return this.getValue(this.contentSlot).pipe(
      map((slot) => slot === XContentSlot.Middle)
    );
  }

  isStartSlotContent() {
    return this.getValue(this.contentSlot).pipe(
      map((slot) => slot === XContentSlot.Start)
    );
  }
  //#endregion
  //#endregion

  //
  //#region Handlers ...
  handleMenuButtonClicked() {
    this.actionProvider.next({ action: XToolbarAction.ToggleMenu });
  }

  async handleBackButtonClicked(event: any) {
    //
    if (this.backHandler) {
      //
      if (event && event.preventDefault) {
        event.preventDefault();
      }

      //
      await this.getValueAsync(this.backHandler());
      return;
    }

    //
    const hasReturnUrl = this.managerService.isContainQueryParam(
      XParam.ReturnUrl
    );
    const hasDefaultHref = !isNullOrEmptyString(this.defaultHref);
    const returnUrl = hasReturnUrl
      ? this.managerService.getQueryParamValOfType<string>(XParam.ReturnUrl)
      : hasDefaultHref
      ? this.defaultHref
      : '';

    //
    if (!isNullOrEmptyString(returnUrl)) {
      //
      await this.managerService.navigateByUrl(returnUrl);
      return;
    }

    //
    this.managerService.historyService.back();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XToolbarActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.actionProvider.asObservable()
    ).subscribe(async (model) => {
      //
      if (!model) {
        return;
      }

      //
      switch (model.action) {
        //
        case XToolbarAction.Refresh:
          this.detectChanges();
          break;
      }
    });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-page\x-toolbar\x-toolbar.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XBackSlot,
  XLogoSlot,
  XMenuSlot,
  XTitleSlot,
  XContentSlot,
  XBackSlotIdentifier,
  XLogoSlotIdentifier,
  XMenuSlotIdentifier,
  XTitleSlotIdentifier,
  XContentSlotIdentifier,
} from '../x-page.typings';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
  XBaseComponentContainer,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XToolbarAction {
  Refresh = 'refresh',
  ToggleMenu = 'ToggleMenu',
}

export type XToolbarActionIdentifier = XToolbarAction | string;

export interface XToolbarActionModel
  extends XBaseActionModel<XToolbarActionIdentifier> {}

//
//#region Container ...
export interface IXToolbarContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XToolbarActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;

  //
  showProgressBar: XStandardType<boolean>;
  progressBarColor: XStandardType<XColorIdentifier>;

  //
  showTitle: XStandardType<boolean>;
  titleSlot: XStandardType<XTitleSlotIdentifier>;
  title: XStandardType<string>;

  //
  showSubTitle: XStandardType<boolean>;
  subTitle: XStandardType<string>;

  //
  showLogo: XStandardType<boolean>;
  logoSlot: XStandardType<XLogoSlotIdentifier>;
  logoUrl: XStandardType<string>;

  //
  hasBack: XStandardType<boolean>;
  showBack: XStandardType<boolean>;
  backSlot: XStandardType<XBackSlotIdentifier>;
  backHandler?: () => XStandardType<void>;
  defaultHref: string;

  //
  hasMenu: XStandardType<boolean>;
  showMenu: XStandardType<boolean>;
  autoHideMenu: XStandardType<boolean>;
  menuSlot: XStandardType<XMenuSlotIdentifier>;

  //
  startSlotButtonsTemplate: TemplateRef<any>;
  endSlotButtonsTemplate: TemplateRef<any>;

  //
  contentSlot: XStandardType<XContentSlotIdentifier>;
  contentTemplate: TemplateRef<any>;
}

@Injectable({
  providedIn: 'root',
})
export class XToolbarContainer
  extends XBaseContainer
  implements Readonly<IXToolbarContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XToolbarActionModel> =
    new Subject<XToolbarActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Primary;

  //
  readonly showProgressBar: XStandardType<boolean> = false;
  readonly progressBarColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Warning;

  //
  readonly showTitle: XStandardType<boolean> = true;
  readonly titleSlot: XStandardType<XTitleSlotIdentifier> = XTitleSlot.End;
  readonly title: XStandardType<string>;

  //
  readonly showSubTitle: XStandardType<boolean> = false;
  readonly subTitle: XStandardType<string>;

  //
  readonly showLogo: XStandardType<boolean> = false;
  readonly logoSlot: XStandardType<XLogoSlotIdentifier> = XLogoSlot.Start;
  readonly logoUrl: XStandardType<string>;

  //
  readonly hasBack: XStandardType<boolean> = true;
  readonly showBack: XStandardType<boolean> = false;
  readonly backSlot: XStandardType<XBackSlotIdentifier> = XBackSlot.Start;
  readonly backHandler?: () => XStandardType<void>;
  readonly defaultHref: string;

  //
  readonly hasMenu: XStandardType<boolean> = true;
  readonly showMenu: XStandardType<boolean> = false;
  readonly autoHideMenu: XStandardType<boolean> = true;
  readonly menuSlot: XStandardType<XMenuSlotIdentifier> = XMenuSlot.Start;

  //
  readonly startSlotButtonsTemplate: TemplateRef<any>;
  readonly endSlotButtonsTemplate: TemplateRef<any>;

  //
  readonly contentSlot: XStandardType<XContentSlotIdentifier> =
    XContentSlot.End;
  readonly contentTemplate: TemplateRef<any>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XToolbarContainerComponent
  extends XBaseContainerComponent
  implements IXToolbarContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XToolbarActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  //
  @Input()
  showProgressBar: XStandardType<boolean> =
    this.propertyProvider.showProgressBar;

  @Input()
  progressBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.progressBarColor;

  //
  @Input()
  showTitle: XStandardType<boolean> = this.propertyProvider.showTitle;

  @Input()
  titleSlot: XStandardType<XTitleSlotIdentifier> =
    this.propertyProvider.titleSlot;

  @Input()
  title: XStandardType<string> = this.propertyProvider.title;

  //
  @Input()
  showSubTitle: XStandardType<boolean> = this.propertyProvider.showSubTitle;

  @Input()
  subTitle: XStandardType<string> = this.propertyProvider.subTitle;

  //
  @Input()
  showLogo: XStandardType<boolean> = this.propertyProvider.showLogo;

  @Input()
  logoSlot: XStandardType<XLogoSlotIdentifier> = this.propertyProvider.logoSlot;

  @Input()
  logoUrl: XStandardType<string> = this.propertyProvider.logoUrl;

  //
  @Input()
  hasBack: XStandardType<boolean> = this.propertyProvider.hasBack;

  @Input()
  showBack: XStandardType<boolean> = this.propertyProvider.showBack;

  @Input()
  backSlot: XStandardType<XBackSlotIdentifier> = this.propertyProvider.backSlot;

  @Input()
  backHandler?: () => XStandardType<void> = this.propertyProvider.backHandler;

  @Input()
  defaultHref: string = this.propertyProvider.defaultHref;

  //
  @Input()
  hasMenu: XStandardType<boolean> = this.propertyProvider.hasMenu;

  @Input()
  showMenu: XStandardType<boolean> = this.propertyProvider.showMenu;

  @Input()
  autoHideMenu: XStandardType<boolean> = this.propertyProvider.autoHideMenu;

  @Input()
  menuSlot: XStandardType<XMenuSlotIdentifier> = this.propertyProvider.menuSlot;

  //
  @Input()
  startSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.startSlotButtonsTemplate;

  @Input()
  endSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.endSlotButtonsTemplate;

  //
  @Input()
  contentSlot: XStandardType<XContentSlotIdentifier> =
    this.propertyProvider.contentSlot;

  @Input()
  contentTemplate: TemplateRef<any> = this.propertyProvider.contentTemplate;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XToolbarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXToolbarContainer
  ): Observable<IXToolbarContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      color: this.getValue(container ? container.color : this.color),
      showProgressBar: this.getValue(
        container ? container.showProgressBar : this.showProgressBar
      ),
      progressBarColor: this.getValue(
        container ? container.progressBarColor : this.progressBarColor
      ),
      showTitle: this.getValue(
        container ? container.showTitle : this.showTitle
      ),
      titleSlot: this.getValue(
        container ? container.titleSlot : this.titleSlot
      ),
      title: this.getValue(container ? container.title : this.title),
      showSubTitle: this.getValue(
        container ? container.showSubTitle : this.showSubTitle
      ),
      subTitle: this.getValue(container ? container.subTitle : this.subTitle),
      showLogo: this.getValue(container ? container.showLogo : this.showLogo),
      logoSlot: this.getValue(container ? container.logoSlot : this.logoSlot),
      logoUrl: this.getValue(container ? container.logoUrl : this.logoUrl),
      hasBack: this.getValue(container ? container.hasBack : this.hasBack),
      showBack: this.getValue(container ? container.showBack : this.showBack),
      backSlot: this.getValue(container ? container.backSlot : this.backSlot),
      backHandler: this.getValue(
        container ? container.backHandler : this.backHandler
      ),
      defaultHref: this.getValue(
        container ? container.defaultHref : this.defaultHref
      ),
      hasMenu: this.getValue(container ? container.hasMenu : this.hasMenu),
      showMenu: this.getValue(container ? container.showMenu : this.showMenu),
      autoHideMenu: this.getValue(
        container ? container.autoHideMenu : this.autoHideMenu
      ),
      menuSlot: this.getValue(container ? container.menuSlot : this.menuSlot),
      startSlotButtonsTemplate: this.getValue(
        container
          ? container.startSlotButtonsTemplate
          : this.startSlotButtonsTemplate
      ),
      endSlotButtonsTemplate: this.getValue(
        container
          ? container.endSlotButtonsTemplate
          : this.endSlotButtonsTemplate
      ),
      contentSlot: this.getValue(
        container ? container.contentSlot : this.contentSlot
      ),
      contentTemplate: this.getValue(
        container ? container.contentTemplate : this.contentTemplate
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentToolbarContainer = {
  [TKey in keyof IXToolbarContainer as `toolbar${Capitalize<
    string & TKey
  >}`]: IXToolbarContainer[TKey];
};

export interface IXComponentToolbarContainer
  extends TXComponentToolbarContainer,
    IXBaseComponentContainer {
  showToolbar: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentToolbarContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentToolbarContainer>
{
  //
  //#region Props ...
  //
  readonly showToolbar: XStandardType<boolean> = true;

  //
  readonly toolbarActionProvider: Subject<XToolbarActionModel> =
    this.propertyProvider.actionProvider;
  readonly toolbarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly toolbarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;

  //
  readonly toolbarShowProgressBar: XStandardType<boolean> =
    this.propertyProvider.showProgressBar;
  readonly toolbarProgressBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.progressBarColor;

  //
  readonly toolbarShowTitle: XStandardType<boolean> =
    this.propertyProvider.showTitle;
  readonly toolbarTitleSlot: XStandardType<XTitleSlotIdentifier> =
    this.propertyProvider.titleSlot;
  readonly toolbarTitle: XStandardType<string> = this.propertyProvider.title;

  //
  readonly toolbarShowSubTitle: XStandardType<boolean> =
    this.propertyProvider.showSubTitle;
  readonly toolbarSubTitle: XStandardType<string> =
    this.propertyProvider.subTitle;

  //
  readonly toolbarShowLogo: XStandardType<boolean> =
    this.propertyProvider.showLogo;
  readonly toolbarLogoSlot: XStandardType<XLogoSlotIdentifier> =
    this.propertyProvider.logoSlot;
  readonly toolbarLogoUrl: XStandardType<string> =
    this.propertyProvider.logoUrl;

  //
  readonly toolbarHasBack: XStandardType<boolean> =
    this.propertyProvider.hasBack;
  readonly toolbarShowBack: XStandardType<boolean> =
    this.propertyProvider.showBack;
  readonly toolbarBackSlot: XStandardType<XBackSlotIdentifier> =
    this.propertyProvider.backSlot;
  readonly toolbarBackHandler?: () => XStandardType<void> =
    this.propertyProvider.backHandler;
  readonly toolbarDefaultHref: string = this.propertyProvider.defaultHref;

  //
  readonly toolbarHasMenu: XStandardType<boolean> =
    this.propertyProvider.hasMenu;
  readonly toolbarShowMenu: XStandardType<boolean> =
    this.propertyProvider.showMenu;
  readonly toolbarAutoHideMenu: XStandardType<boolean> =
    this.propertyProvider.autoHideMenu;
  readonly toolbarMenuSlot: XStandardType<XMenuSlotIdentifier> =
    this.propertyProvider.menuSlot;

  //
  readonly toolbarStartSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.startSlotButtonsTemplate;
  readonly toolbarEndSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.endSlotButtonsTemplate;

  //
  readonly toolbarContentSlot: XStandardType<XContentSlotIdentifier> =
    this.propertyProvider.contentSlot;
  readonly toolbarContentTemplate: TemplateRef<any> =
    this.propertyProvider.contentTemplate;
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XToolbarContainer) {
    super();
  }
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
@Component({
  template: `
    <!--
        <ng-container *ngIf="extractProps() | async as props">
            <ng-container *ngIf="props.showToolbar">
              <x-toolbar
                [toolbarTitle]="props.toolbarTitle"
                [toolbarColor]="props.toolbarColor"
                [toolbarLogoUrl]="props.toolbarLogoUrl"
                [toolbarHasBack]="props.toolbarHasBack"
                [toolbarHasMenu]="props.toolbarHasMenu"
                [toolbarSubTitle]="props.toolbarSubTitle"
                [toolbarShowLogo]="props.toolbarShowLogo"
                [toolbarLogoSlot]="props.toolbarLogoSlot"
                [toolbarShowBack]="props.toolbarShowBack"
                [toolbarBackSlot]="props.toolbarBackSlot"
                [toolbarShowMenu]="props.toolbarShowMenu"
                [toolbarAutoHideMenu]="props.toolbarAutoHideMenu"
                [toolbarMenuSlot]="props.toolbarMenuSlot"
                [toolbarCssClass]="props.toolbarCssClass"
                [toolbarShowTitle]="props.toolbarShowTitle"
                [toolbarTitleSlot]="props.toolbarTitleSlot"
                [toolbarBackHandler]="props.toolbarBackHandler"
                [toolbarDefaultHref]="props.toolbarDefaultHref"
                [toolbarContentSlot]="props.toolbarContentSlot"
                [toolbarShowSubTitle]="props.toolbarShowSubTitle"
                [toolbarActionProvider]="props.toolbarActionProvider"
                [toolbarShowProgressBar]="props.toolbarShowProgressBar"
                [toolbarContentTemplate]="props.toolbarContentTemplate"
                [toolbarProgressBarColor]="props.toolbarProgressBarColor"
                [toolbarEndSlotButtonsTemplate]="props.toolbarEndSlotButtonsTemplate"
                [toolbarStartSlotButtonsTemplate]="props.toolbarStartSlotButtonsTemplate"
              >
              </x-toolbar>
            </ng-container>
        </ng-container>
    -->
  `,
})
export abstract class XComponentToolbarContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentToolbarContainer
{
  //
  //#region Props ...
  //
  @Input()
  showToolbar: XStandardType<boolean> = this.propertyProvider.showToolbar;

  //
  @Input()
  toolbarActionProvider: Subject<XToolbarActionModel> =
    this.propertyProvider.toolbarActionProvider;

  @Input()
  toolbarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.toolbarCssClass;

  //
  @Input()
  toolbarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarColor;

  //
  @Input()
  toolbarShowProgressBar: XStandardType<boolean> =
    this.propertyProvider.toolbarShowProgressBar;

  @Input()
  toolbarProgressBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.toolbarProgressBarColor;

  //
  @Input()
  toolbarShowTitle: XStandardType<boolean> =
    this.propertyProvider.toolbarShowTitle;

  @Input()
  toolbarTitleSlot: XStandardType<XTitleSlotIdentifier> =
    this.propertyProvider.toolbarTitleSlot;

  @Input()
  toolbarTitle: XStandardType<string> = this.propertyProvider.toolbarTitle;

  //
  @Input()
  toolbarShowSubTitle: XStandardType<boolean> =
    this.propertyProvider.toolbarShowSubTitle;

  @Input()
  toolbarSubTitle: XStandardType<string> =
    this.propertyProvider.toolbarSubTitle;

  //
  @Input()
  toolbarShowLogo: XStandardType<boolean> =
    this.propertyProvider.toolbarShowLogo;

  @Input()
  toolbarLogoSlot: XStandardType<XLogoSlotIdentifier> =
    this.propertyProvider.toolbarLogoSlot;

  @Input()
  toolbarLogoUrl: XStandardType<string> = this.propertyProvider.toolbarLogoUrl;

  //
  @Input()
  toolbarHasBack: XStandardType<boolean> = this.propertyProvider.toolbarHasBack;

  @Input()
  toolbarShowBack: XStandardType<boolean> =
    this.propertyProvider.toolbarShowBack;

  @Input()
  toolbarBackSlot: XStandardType<XBackSlotIdentifier> =
    this.propertyProvider.toolbarBackSlot;

  @Input()
  toolbarBackHandler?: () => XStandardType<void> =
    this.propertyProvider.toolbarBackHandler;

  @Input()
  toolbarDefaultHref: string = this.propertyProvider.toolbarDefaultHref;

  //
  @Input()
  toolbarHasMenu: XStandardType<boolean> = this.propertyProvider.toolbarHasMenu;

  @Input()
  toolbarShowMenu: XStandardType<boolean> =
    this.propertyProvider.toolbarShowMenu;

  @Input()
  toolbarAutoHideMenu: XStandardType<boolean> =
    this.propertyProvider.toolbarAutoHideMenu;

  @Input()
  toolbarMenuSlot: XStandardType<XMenuSlotIdentifier> =
    this.propertyProvider.toolbarMenuSlot;

  //
  @Input()
  toolbarStartSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarStartSlotButtonsTemplate;

  @Input()
  toolbarEndSlotButtonsTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarEndSlotButtonsTemplate;

  //
  @Input()
  toolbarContentSlot: XStandardType<XContentSlotIdentifier> =
    this.propertyProvider.toolbarContentSlot;

  @Input()
  toolbarContentTemplate: TemplateRef<any> =
    this.propertyProvider.toolbarContentTemplate;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentToolbarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentToolbarContainer
  ): Observable<IXComponentToolbarContainer> {
    return forkJoin({
      //
      showToolbar: this.getValue(
        container ? container.showToolbar : this.showToolbar
      ),
      //
      toolbarCssClass: this.getValue(
        container ? container.toolbarCssClass : this.toolbarCssClass
      ),
      toolbarColor: this.getValue(
        container ? container.toolbarColor : this.toolbarColor
      ),
      toolbarShowProgressBar: this.getValue(
        container
          ? container.toolbarShowProgressBar
          : this.toolbarShowProgressBar
      ),
      toolbarProgressBarColor: this.getValue(
        container
          ? container.toolbarProgressBarColor
          : this.toolbarProgressBarColor
      ),
      toolbarShowTitle: this.getValue(
        container ? container.toolbarShowTitle : this.toolbarShowTitle
      ),
      toolbarTitleSlot: this.getValue(
        container ? container.toolbarTitleSlot : this.toolbarTitleSlot
      ),
      toolbarTitle: this.getValue(
        container ? container.toolbarTitle : this.toolbarTitle
      ),
      toolbarShowSubTitle: this.getValue(
        container ? container.toolbarShowSubTitle : this.toolbarShowSubTitle
      ),
      toolbarSubTitle: this.getValue(
        container ? container.toolbarSubTitle : this.toolbarSubTitle
      ),
      toolbarShowLogo: this.getValue(
        container ? container.toolbarShowLogo : this.toolbarShowLogo
      ),
      toolbarLogoSlot: this.getValue(
        container ? container.toolbarLogoSlot : this.toolbarLogoSlot
      ),
      toolbarLogoUrl: this.getValue(
        container ? container.toolbarLogoUrl : this.toolbarLogoUrl
      ),
      toolbarHasBack: this.getValue(
        container ? container.toolbarHasBack : this.toolbarHasBack
      ),
      toolbarShowBack: this.getValue(
        container ? container.toolbarShowBack : this.toolbarShowBack
      ),
      toolbarBackSlot: this.getValue(
        container ? container.toolbarBackSlot : this.toolbarBackSlot
      ),
      toolbarBackHandler: this.getValue(
        container ? container.toolbarBackHandler : this.toolbarBackHandler
      ),
      toolbarDefaultHref: this.getValue(
        container ? container.toolbarDefaultHref : this.toolbarDefaultHref
      ),
      toolbarHasMenu: this.getValue(
        container ? container.toolbarHasMenu : this.toolbarHasMenu
      ),
      toolbarShowMenu: this.getValue(
        container ? container.toolbarShowMenu : this.toolbarShowMenu
      ),
      toolbarAutoHideMenu: this.getValue(
        container ? container.toolbarAutoHideMenu : this.toolbarAutoHideMenu
      ),
      toolbarMenuSlot: this.getValue(
        container ? container.toolbarMenuSlot : this.toolbarMenuSlot
      ),
      toolbarStartSlotButtonsTemplate: this.getValue(
        container
          ? container.toolbarStartSlotButtonsTemplate
          : this.toolbarStartSlotButtonsTemplate
      ),
      toolbarEndSlotButtonsTemplate: this.getValue(
        container
          ? container.toolbarEndSlotButtonsTemplate
          : this.toolbarEndSlotButtonsTemplate
      ),
      toolbarContentSlot: this.getValue(
        container ? container.toolbarContentSlot : this.toolbarContentSlot
      ),
      toolbarContentTemplate: this.getValue(
        container
          ? container.toolbarContentTemplate
          : this.toolbarContentTemplate
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          toolbarActionProvider: container
            ? container.toolbarActionProvider
            : this.toolbarActionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-progress-bar\x-progress-bar.component.html

<section class="x-progress-bar x-wrapper">
  <ion-progress-bar
    class="x-progress-bar-spinner"
    [type]="getValue(type) | async"
    [color]="getValue(color) | async"
    [value]="getValue(value) | async"
    [buffer]="getValue(buffer) | async"
  ></ion-progress-bar>
</section>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-progress-bar\x-progress-bar.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-progress-bar\x-progress-bar.component.ts

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { XProgressBarAction, XProgressBarActionModel, XProgressBarContainerComponent, XProgressBarType } from './x-progress-bar.typings';
import { Subject, Subscription } from 'rxjs';
import { nameof } from 'x-framework-core';

@Component({
    selector: 'x-progress-bar',
    templateUrl: './x-progress-bar.component.html',
    styleUrls: ['./x-progress-bar.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XProgressBarComponent extends XProgressBarContainerComponent {
    //
    //#region Props ...
    //
    private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

    //
    readonly PresentTypes = Object.assign({}, XProgressBarType);
    //#endregion

    //
    //#region LifeCycle ...
    onChange (changeKeys: string[]) {
        super.onChange(changeKeys);

        //
        //#region ActionProvider Changed ...
        //
        const isActionProviderChanged = changeKeys.includes(
            nameof<XProgressBarComponent>('actionProvider')
        );
        if (isActionProviderChanged) {
            this.registerActionProvider();
        }
        //#endregion
    }

    onDestroy () {
        super.onDestroy();

        //
        this.unregisterActionProvider();
    }
    //#endregion

    //
    //#region Handlers ...    
    registerViewHandlers () {
        super.registerViewHandlers();

        //
        this.registerActionProvider();
    }
    //#endregion

    //
    //#region Reading Query Params ...
    //#endregion

    //
    //#region Providers ...
    //#endregion

    //
    //#region Private ...
    //
    //#region Action Provider ...
    private registerActionProvider () {
        //
        this.unregisterActionProvider();

        //
        if (!this.actionProvider) {
            this.actionProvider = new Subject<XProgressBarActionModel>();
        }

        //
        // Handle Action Provider ...
        this.ACTION_PROVIDER_SUBSCRIPTION = this.applyTakeUntilWrapper(
            this.actionProvider.asObservable()
        ).subscribe(async (model) => {
            //
            if (!model) {
                return;
            }

            //
            switch (model.action) {
                //
                case XProgressBarAction.Refresh:
                    this.detectChanges();
                    break;
            }
        });
    }

    private unregisterActionProvider () {
        //
        if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
            return;
        }

        //
        this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
        this.ACTION_PROVIDER_SUBSCRIPTION = null;
    }
    //#endregion
    //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-progress-bar\x-progress-bar.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XProgressBarComponent } from './x-progress-bar.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XProgressBarComponent],
  imports: [CommonModule, XBaseComponentModule],
  exports: [XBaseComponentModule, XProgressBarComponent],
})
export class XProgressBarModule { }

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-progress-bar\x-progress-bar.typings.ts

import {
    Subject,
    forkJoin,
    Observable,
} from 'rxjs';
import {
    Input,
    NgZone,
    Inject,
    Renderer2,
    Component,
    Injectable,
    ElementRef,
    ChangeDetectorRef,
} from '@angular/core';
import {
    XStandardType,
    XOneOrManyType,
    XColorIdentifier,
    XColorWithBrightness,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
    XBaseContainer,
    IXBaseContainer,
    XBaseComponentContainer,
    XBaseContainerComponent,
    IXBaseComponentContainer,
} from '../classes/i-x-base.container';
import { XManagerService } from 'x-framework-services';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Prop Definitions ...
export enum XProgressBarType {
    Buffer = 'buffer',
    Determinate = 'determinate',
    Indeterminate = 'indeterminate',
}

export type XProgressBarTypeIdentifier = XProgressBarType | string;
//#endregion

//
//#region Action Provider
export enum XProgressBarAction {
    Refresh = 'refresh',
}

export type XProgressBarActionIdentifier = XProgressBarAction | string;


export interface XProgressBarActionModel
    extends XBaseActionModel<XProgressBarActionIdentifier> { }
//#endregion

//
//#region Container ...
export interface IXProgressBarContainer extends IXBaseContainer {
    //
    actionProvider: Subject<XProgressBarActionModel>;
    cssClass: XStandardType<XOneOrManyType<string>>;

    //
    color: XStandardType<XColorIdentifier>;

    //
    type: XStandardType<XProgressBarTypeIdentifier>;

    //
    value: XStandardType<number>;
    buffer: XStandardType<number>;
}

@Injectable({
    providedIn: 'root',
})
export class XProgressBarContainer
    extends XBaseContainer
    implements Readonly<IXProgressBarContainer>
{
    //
    //#region Props ...
    //
    readonly actionProvider = new Subject<XProgressBarActionModel>();
    readonly cssClass: XStandardType<XOneOrManyType<string>>;

    //
    readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Warning;

    //
    readonly type: XStandardType<XProgressBarTypeIdentifier> = XProgressBarType.Indeterminate;

    //
    readonly value: XStandardType<number>;
    readonly buffer: XStandardType<number>;
    //#endregion
}

@Injectable()
@Component({
    template: ``,
})
export abstract class XProgressBarContainerComponent
    extends XBaseContainerComponent
    implements IXProgressBarContainer {
    //
    //#region Props ...
    //
    @Input()
    actionProvider: Subject<XProgressBarActionModel> =
        this.propertyProvider.actionProvider;

    @Input()
    cssClass: XStandardType<XOneOrManyType<string>> =
        this.propertyProvider.cssClass;

    //
    @Input()
    color: XStandardType<XColorIdentifier> =
        this.propertyProvider.color;

    //
    @Input()
    type: XStandardType<XProgressBarTypeIdentifier> =
        this.propertyProvider.type;

    //
    @Input()
    value: XStandardType<number> =
        this.propertyProvider.value;

    @Input()
    buffer: XStandardType<number> =
        this.propertyProvider.buffer;
    //#endregion

    //
    //#region Constructor ...
    public constructor(
        @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
        public config: XFrameworkComponentsConfig,
        public managerService: XManagerService,
        public element: ElementRef,
        public renderer: Renderer2,
        public ruler: ViewportRuler,
        public zone: NgZone,
        public changeDetector: ChangeDetectorRef,
        protected propertyProvider: XProgressBarContainer
    ) {
        super(
            config,
            managerService,
            element,
            renderer,
            ruler,
            zone,
            changeDetector
        );
    }
    //#endregion

    //
    //#region Abstract ...
    public extractProps (
        container?: IXProgressBarContainer
    ): Observable<IXProgressBarContainer> {
        return forkJoin({
            //
            cssClass: this.getValue(container ? container.cssClass : this.cssClass),

            //
            color: this.getValue(container ? container.color : this.color),

            //
            type: this.getValue(container ? container.type : this.type),

            //
            value: this.getValue(container ? container.value : this.value),
            buffer: this.getValue(container ? container.buffer : this.buffer),
        }).pipe(
            map((data) => {
                return {
                    //
                    ...data,

                    //
                    actionProvider: container
                        ? container.actionProvider
                        : this.actionProvider,
                };
            })
        );
    }
    //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentProgressBarContainer = {
    [TKey in keyof IXProgressBarContainer as `progressBar${Capitalize<
        string & TKey
    >}`]: IXProgressBarContainer[TKey];
};

export interface IXComponentProgressBarContainer
    extends TXComponentProgressBarContainer,
    IXBaseComponentContainer {
    showProgressBar: XStandardType<boolean>;
}

@Injectable({
    providedIn: 'root',
})
export class XComponentProgressBarContainer
    extends XBaseComponentContainer
    implements Readonly<IXComponentProgressBarContainer> {
    //
    //#region Props ...
    //
    readonly showProgressBar: XStandardType<boolean> = false;

    //
    readonly progressBarActionProvider =
        this.propertyProvider.actionProvider;
    readonly progressBarCssClass: XStandardType<XOneOrManyType<string>> =
        this.propertyProvider.cssClass;

    //
    readonly progressBarColor: XStandardType<XColorIdentifier> =
        this.propertyProvider.color;

    //
    readonly progressBarType: XStandardType<XProgressBarTypeIdentifier> =
        this.propertyProvider.type;

    //
    readonly progressBarValue: XStandardType<number> =
        this.propertyProvider.value;
    readonly progressBarBuffer: XStandardType<number> =
        this.propertyProvider.buffer;
    //#endregion

    //
    //#region Constructor ...
    constructor(protected propertyProvider: XProgressBarContainer) {
        super();
    }
    //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.component.html

<div class="x-screen-capture x-wrapper" [class.mobile]="isMobileUi$ | async">
  <!-- Content -->
  <div #content class="x-content">
    <ng-content></ng-content>
  </div>

  <!-- Overlay -->
  <div
    #over
    xMouseHandler
    class="x-overlay"
    [class.enabled]="false"
    (moved)="handleMouseMoved($event)"
    (started)="handleMouseStarted($event)"
    [hidden]="!(getValue(enable) | async)"
    (finished)="handleMouseFinished($event)"
    [enableHandler]="getValue(enable) | async"
  >
    <div #rect class="x-rectangle" [class.drawing]="isDrawing"></div>
  </div>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.component.scss

.x-overlay {
  top: 0px;
  left: 0px;
  width: 100vw;
  height: 100vh;
  position: absolute;

  &.enabled {
    background-color: rgba(204, 204, 204, 0.6);
  }
}

.x-rectangle {
  position: absolute;

  &.drawing {
    border: 1px solid #ff0000;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.component.ts

import {
  ViewChild,
  Component,
  ElementRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import { nameof } from 'x-framework-core';
import { XScreenCaptureDimensions } from './x-screen-capture.typings';
import { XScreenCaptureContainerComponent } from './x-screen-capture.container';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-screen-capture',
  templateUrl: './x-screen-capture.component.html',
  styleUrls: ['./x-screen-capture.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XScreenCaptureComponent extends XScreenCaptureContainerComponent {
  //
  //#region Props ...
  //
  rect: HTMLElement;
  captureZone: HTMLElement;

  //
  isDrawing = false;

  //
  clippingDimensions: XScreenCaptureDimensions = {
    x: 0,
    y: 0,
    width: 0,
    height: 0,
  };

  //
  mouse = {
    x: 0,
    y: 0,
    startX: 0,
    startY: 0,
  };

  //
  //#region View Childs ...
  //
  private RECT: ElementRef;

  //
  @ViewChild('rect', { static: false })
  set rectangle(v: ElementRef) {
    //
    this.RECT = v;
    this.rect = v.nativeElement;
  }

  //
  get rectangle() {
    return this.RECT;
  }

  //
  private OVERLAY: ElementRef;

  //
  @ViewChild('over', { static: false })
  set overlay(v: ElementRef) {
    //
    this.OVERLAY = v;
    this.captureZone = v.nativeElement;
  }

  //
  get overlay() {
    return this.OVERLAY;
  }

  //
  private CONTENT: ElementRef;

  //
  @ViewChild('content', { static: false })
  set content(v: ElementRef) {
    this.CONTENT = v;
  }

  //
  get content() {
    return this.CONTENT;
  }
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const isEnableChanged = changeKeys.includes(
      nameof<XScreenCaptureComponent>('enable')
    );
    if (isEnableChanged) {
      //
      const enable = await this.getValueAsync(this.enable);
      this.reAssignElements(enable);
      this.detectChanges();
    }
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    this.reAssignElements();
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleMouseMoved(event: MouseEvent) {
    this.drawRect(event);
  }

  async handleMouseStarted(event: MouseEvent) {
    this.startCapture(event);
  }

  async handleMouseFinished(event: MouseEvent) {
    this.endCapture(event);
  }
  //#endregion

  //
  //#region Private ...
  private reAssignElements(enabled?: boolean) {
    //
    if (!this.OVERLAY || !this.RECT || !this.CONTENT) {
      return;
    }

    //
    this.rect = this.RECT.nativeElement;
    this.captureZone = this.OVERLAY.nativeElement;

    //
    if (enabled === true) {
      this.captureZone.style.cursor = 'crosshair';
    } else {
      this.captureZone.style.cursor = 'default';
    }
  }

  private setRectangle() {
    //
    this.rect.style.left = this.clippingDimensions.x + 'px';
    this.rect.style.top = this.clippingDimensions.y + 'px';
    this.rect.style.width = this.clippingDimensions.width + 'px';
    this.rect.style.height = this.clippingDimensions.height + 'px';
  }

  private startCapture(e: any) {
    //
    if (!this.captureZone) {
      //
      this.reAssignElements();
    }

    //
    this.setMousePosition(e);

    //
    this.isDrawing = true;
    this.detectChanges();

    //
    this.mouse.startX = this.mouse.x;
    this.mouse.startY = this.mouse.y;
    this.clippingDimensions = {
      x: this.mouse.x,
      y: this.mouse.y,
      width: 0,
      height: 0,
    };

    //
    this.setRectangle();
    this.captureZone.style.cursor = 'crosshair';
    this.detectChanges();
  }

  private drawRect(e: any) {
    //
    if (!this.captureZone) {
      return;
    }

    //
    if (this.isDrawing) {
      //
      this.setMousePosition(e);

      //
      this.clippingDimensions = {
        x:
          this.mouse.x - this.mouse.startX < 0
            ? this.mouse.x
            : this.mouse.startX,
        y:
          this.mouse.y - this.mouse.startY < 0
            ? this.mouse.y
            : this.mouse.startY,
        width: Math.abs(this.mouse.x - this.mouse.startX),
        height: Math.abs(this.mouse.y - this.mouse.startY),
      };

      //
      this.setRectangle();
    }
  }

  private setMousePosition(e: any) {
    //
    if (!this.captureZone) {
      return;
    }

    //
    // tslint:disable-next-line: deprecation
    const ev = e || window.event; // Moz || IE
    if (ev.pageX) {
      // Moz
      //
      this.mouse.x = ev.pageX + window.pageXOffset;
      this.mouse.y = ev.pageY + window.pageYOffset;
    } else if (ev.clientX) {
      // IE
      //
      this.mouse.x = ev.clientX + document.body.scrollLeft;
      this.mouse.y = ev.clientY + document.body.scrollTop;
    }
  }

  private async endCapture(ev: MouseEvent) {
    //
    if (!ev || !this.captureZone) {
      return;
    }

    //
    this.captureZone.style.cursor = 'default';
    this.isDrawing = false;
    this.detectChanges();

    //
    const captureDocument = document.body;

    //
    const topOffset = await this.getValueAsync(this.topOffset);
    if (topOffset !== 0) {
      this.clippingDimensions = {
        ...this.clippingDimensions,
        y: this.clippingDimensions.y + topOffset,
      };
    }

    //
    const bottomOffset = await this.getValueAsync(this.bottomOffset);
    if (bottomOffset !== 0) {
      this.clippingDimensions = {
        ...this.clippingDimensions,
        y: this.clippingDimensions.y + bottomOffset,
      };
    }

    //
    const canDownload = await this.getOptionalValueAsync(
      this.downloadCapturedImage
    );
    if (canDownload === true) {
      //
      const blob = await this.screenCaptureService.captureAndDownload(
        captureDocument,
        null,
        this.clippingDimensions
      );

      //
      this.imageCaptured.emit(blob);
    } else {
      //
      const blob = await this.screenCaptureService.capture(
        captureDocument,
        null,
        this.clippingDimensions
      );

      //
      this.imageCaptured.emit(blob);
    }

    //
    this.clippingDimensions = {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
    };

    //
    this.setRectangle();

    //
    this.reAssignElements();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.container.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Renderer2,
  Component,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { forkJoin, Observable } from 'rxjs';
import { XStandardType } from 'x-framework-core';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XScreenCaptureService } from './x-screen-capture.service';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

//
//#region Container ...
export interface IXScreenCaptureContainer extends IXBaseContainer {
  enable: XStandardType<boolean>;
  topOffset: XStandardType<number>;
  bottomOffset: XStandardType<number>;
  downloadCapturedImage: XStandardType<boolean>;
  imageCaptured: EventEmitter<string | Blob>;
}

@Injectable({
  providedIn: 'root',
})
export class XScreenCaptureContainer
  extends XBaseContainer
  implements Readonly<IXScreenCaptureContainer>
{
  //
  //#region Props ...
  readonly enable: XStandardType<boolean> = false;
  readonly topOffset: XStandardType<number> = 0;
  readonly bottomOffset: XStandardType<number> = 0;
  readonly downloadCapturedImage: XStandardType<boolean> = true;
  readonly imageCaptured: EventEmitter<string | Blob> = new EventEmitter<
    string | Blob
  >();
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
@Component({
  template: `
    <!-- 
          <ng-container *ngIf="extractProps() | async as props">
          </ng-container> 
      -->
  `,
})
export abstract class XScreenCaptureContainerComponent
  extends XBaseContainerComponent
  implements IXScreenCaptureContainer
{
  //
  //#region Props ...
  @Input()
  enable: XStandardType<boolean> = this.propertyProvider.enable;

  @Input()
  topOffset: XStandardType<number> = this.propertyProvider.topOffset;

  @Input()
  bottomOffset: XStandardType<number> = this.propertyProvider.bottomOffset;

  @Input()
  downloadCapturedImage: XStandardType<boolean> =
    this.propertyProvider.downloadCapturedImage;

  @Output()
  imageCaptured: EventEmitter<string | Blob> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    public screenCaptureService: XScreenCaptureService,
    protected propertyProvider: XScreenCaptureContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXScreenCaptureContainer
  ): Observable<IXScreenCaptureContainer> {
    return forkJoin({
      //
      enable: this.getValue(container ? container.enable : this.enable),
      topOffset: this.getValue(
        container ? container.topOffset : this.topOffset
      ),
      bottomOffset: this.getValue(
        container ? container.bottomOffset : this.bottomOffset
      ),
      downloadCapturedImage: this.getValue(
        container ? container.downloadCapturedImage : this.downloadCapturedImage
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          imageCaptured: this.imageCaptured,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XScreenCaptureComponent } from './x-screen-capture.component';

@NgModule({
  declarations: [XScreenCaptureComponent],
  imports: [CommonModule, XBaseComponentModule],
  exports: [XBaseComponentModule, XScreenCaptureComponent],
})
export class XScreenCaptureModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.service.ts

import {
  XMimeType,
  blobToImage,
  XBaseService,
  generateUuid,
  canvasToBlob,
  dataUrlToImage,
  canvasToDataUrl,
  clipImageToCanvas,
  downloadArrayBufferedFile,
} from 'x-framework-core';
import {
  XScreenCaptureType,
  XScreenCaptureOptions,
  XScreenCaptureDimensions,
  XScreenCaptureTypeIdentifier,
} from './x-screen-capture.typings';
import * as domToImage from 'dom-to-image';
import { Injectable, Inject } from '@angular/core';
import { XNotificationService } from 'x-framework-services';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

@Injectable({
  providedIn: 'root',
})
export class XScreenCaptureService extends XBaseService {
  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    private notificationService: XNotificationService
  ) {
    super(config);
  }
  //#endregion

  /**
   * Capture Element with Options as specified type ...
   *
   * @param screen an instance of HTMLElement ...
   */
  async capture(
    screen: HTMLElement,
    type?: XScreenCaptureTypeIdentifier,
    options?: XScreenCaptureOptions,
    clippingDimensions?: XScreenCaptureDimensions
  ) {
    //
    if (!screen) {
      return null;
    }

    //
    if (!type) {
      type = XScreenCaptureType.PNG;
    }

    //
    if (!options) {
      options = {};
    }

    //
    this.playCaptureNotification();

    //
    try {
      //
      switch (type) {
        //
        // PNG ...
        case XScreenCaptureType.PNG:
          //
          const pngDataUrl = await domToImage.default.toPng(screen, options);
          if (!clippingDimensions) {
            return pngDataUrl;
          } else {
            //
            const pngImage = await dataUrlToImage(pngDataUrl);
            if (!pngImage) {
              return null;
            }

            //
            const pngClippedCanvas = await clipImageToCanvas(
              pngImage,
              clippingDimensions.x,
              clippingDimensions.y,
              clippingDimensions.width,
              clippingDimensions.height
            );
            if (!pngClippedCanvas) {
              return null;
            }

            //
            return canvasToDataUrl(pngClippedCanvas, XMimeType.ImagePNG);
          }

        //
        // JPeg ...
        case XScreenCaptureType.JPeg:
          //
          const jPegDataUrl = await domToImage.default.toJpeg(screen, options);
          if (!clippingDimensions) {
            return jPegDataUrl;
          } else {
            //
            const jPegImage = await dataUrlToImage(jPegDataUrl);
            if (!jPegImage) {
              return null;
            }

            //
            const jPegClippedCanvas = await clipImageToCanvas(
              jPegImage,
              clippingDimensions.x,
              clippingDimensions.y,
              clippingDimensions.width,
              clippingDimensions.height
            );
            if (!jPegClippedCanvas) {
              return null;
            }

            //
            return canvasToDataUrl(jPegClippedCanvas, XMimeType.ImageJPEG);
          }

        //
        // SVG ...
        case XScreenCaptureType.SVG:
          //
          const svgDataUrl = await domToImage.default.toSvg(screen, options);
          if (!clippingDimensions) {
            return svgDataUrl;
          } else {
            //
            const svgImage = await dataUrlToImage(svgDataUrl);
            if (!svgImage) {
              return null;
            }

            //
            const svgClippedCanvas = await clipImageToCanvas(
              svgImage,
              clippingDimensions.x,
              clippingDimensions.y,
              clippingDimensions.width,
              clippingDimensions.height
            );
            if (!svgClippedCanvas) {
              return null;
            }

            //
            return canvasToDataUrl(svgClippedCanvas, XMimeType.ImageSVG);
          }

        //
        // To Blob ...
        case XScreenCaptureType.Blob:
          //
          const blob = await domToImage.default.toBlob(screen, options);
          if (!clippingDimensions) {
            return blob;
          } else {
            //
            const blobImage = await blobToImage(blob);
            if (!blobImage) {
              return null;
            }

            //
            const blobClippedCanvas = await clipImageToCanvas(
              blobImage,
              clippingDimensions.x,
              clippingDimensions.y,
              clippingDimensions.width,
              clippingDimensions.height
            );
            if (!blobClippedCanvas) {
              return null;
            }

            //
            return await canvasToBlob(blobClippedCanvas);
          }
      }
    } catch {}

    //
    return null;
  }

  /**
   * Capture Element with Options as Blob object and Download it ...
   *
   * @param screen an instance of HTMLElement ...
   * @param fileName downloaded file name ...
   */
  async captureAndDownload(
    screen: HTMLElement,
    options?: XScreenCaptureOptions,
    clippingDimensions?: XScreenCaptureDimensions,
    fileName?: string
  ) {
    //
    this.playCaptureNotification();

    //
    const fName = fileName ? fileName : `${generateUuid()}.png`;

    //
    try {
      //
      const blob = await domToImage.default.toBlob(screen, options);

      //
      if (!blob) {
        return null;
      }

      //
      if (!clippingDimensions) {
        //
        downloadArrayBufferedFile(fName, blob);
        return blob;
      } else {
        //
        const blobImage = await blobToImage(blob);
        if (!blobImage) {
          return null;
        }

        //
        const blobClippedCanvas = await clipImageToCanvas(
          blobImage,
          clippingDimensions.x,
          clippingDimensions.y,
          clippingDimensions.width,
          clippingDimensions.height
        );
        if (!blobClippedCanvas) {
          return null;
        }

        //
        const clippedBlob = await canvasToBlob(blobClippedCanvas);
        if (!clippedBlob) {
          return null;
        }

        //
        downloadArrayBufferedFile(fName, clippedBlob);
        return clippedBlob;
      }
    } catch {}

    //
    return null;
  }

  /**
   * play Capture Notification Sound ...
   */
  playCaptureNotification() {
    this.notificationService.playCaptureNotification();
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-screen-capture\x-screen-capture.typings.ts

import { Options } from 'dom-to-image';

// tslint:disable-next-line:no-empty-interface
export interface XScreenCaptureOptions extends Options {}

export enum XScreenCaptureType {
  SVG = 'svg',
  PNG = 'png',
  Blob = 'blob',
  JPeg = 'jpeg',
}

export type XScreenCaptureTypeIdentifier = XScreenCaptureType | string;

export interface XScreenCaptureDimensions {
  x: number;
  y: number;
  width: number;
  height: number;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-search-bar\x-search-bar.component.html

<ion-toolbar
  class="x-search-bar x-wrapper"
  [xColor]="getValue(color) | async"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
  [isForeground]="getValue(foregroundColor) | async"
>
  <ion-searchbar
    class="x-search-bar"
    [type]="getValue(type) | async"
    [value]="getValue(value) | async"
    [disabled]="uiDisabled || loading"
    [debounce]="getValue(debounce) | async"
    [spellcheck]="getValue(spellcheck) | async"
    [inputmode]="getValue(keyboardType) | async"
    [xColor]="getValue(searchInputColor) | async"
    (ionBlur)="onEvent.emit(SearchBarEvents.BLUR)"
    [clearIcon]="getValue(clearButtonIcon) | async"
    [enterkeyhint]="getValue(enterKeyHint) | async"
    [placeholder]="getResourceValue(title) | async"
    (ionFocus)="onEvent.emit(SearchBarEvents.FOCUS)"
    (ionClear)="onEvent.emit(SearchBarEvents.CLEAR)"
    (ionInput)="onEvent.emit(SearchBarEvents.INPUT)"
    [searchIcon]="getValue(searchButtonIcon) | async"
    (ionCancel)="onEvent.emit(SearchBarEvents.CANCEL)"
    (ionChange)="handleSearchQueryValueChange($event)"
    [showCancelButton]="getValue(showCancelButton) | async"
    [showClearButton]="getValue(showClearButton) | async"
    [cancelButtonIcon]="getValue(cancelButtonIcon) | async"
    [isForeground]="getValue(foregroundSearchInputColor) | async"
    [cancelButtonText]="getResourceValue(cancelButtonTitle) | async"
  ></ion-searchbar>
</ion-toolbar>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-search-bar\x-search-bar.component.scss

:host {
  --clear-icon-color: var(--x-color-danger);
  --cancel-icon-color: var(--x-color-primary);
  --search-icon-color: var(--x-color-primary);
}

:host::ng-deep {
  .searchbar-clear-icon {
    color: var(--clear-icon-color) !important;
  }

  .searchbar-search-icon {
    color: var(--search-icon-color) !important;
  }

  .searchbar-cancel-button {
    color: var(--cancel-icon-color) !important;
  }
}

.x-search-bar {
  padding: {
    left: 3px;
    right: 3px;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-search-bar\x-search-bar.component.ts

import {
  nameof,
  parseXColor,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XSearchBarEvent,
  XSearchBarAction,
  XSearchBarCssVars,
  XSearchBarActionModel,
  XSearchBarContainerComponent,
} from './x-search-bar.typings';
import { filter } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-search-bar',
  templateUrl: './x-search-bar.component.html',
  styleUrls: ['./x-search-bar.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSearchBarComponent extends XSearchBarContainerComponent {
  //
  //#region Props ...
  //
  protected applyThemeColors: boolean = true;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  readonly SearchBarEvents = Object.assign({}, XSearchBarEvent);
  //#endregion

  //
  //#region LifeCycles ...
  afterViewInit() {
    super.afterViewInit();

    //
    // Handle Start Value for start searching ...
    this.getValue(this.value)
      .pipe(filter((query) => !isNullOrEmptyString(query)))
      .subscribe((query) => {
        this.queryChange.emit(query);
      });
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region Action Provider ...
    const isActionProviderChaged = changeKeys.includes(
      nameof<XSearchBarComponent>('actionProvider')
    );
    if (isActionProviderChaged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Colors ...
    //
    const isCancelButtonIconColorChanged = changeKeys.includes(
      nameof<XSearchBarComponent>('cancelButtonIconColor')
    );
    if (isCancelButtonIconColorChanged) {
      await this.applyCancelButtonIconColor();
    }

    //
    const isClearButtonIconColorChanged = changeKeys.includes(
      nameof<XSearchBarComponent>('clearButtonIconColor')
    );
    if (isClearButtonIconColorChanged) {
      await this.applyClearButtonIconColor();
    }

    //
    const isSearchButtonIconColorChanged = changeKeys.includes(
      nameof<XSearchBarComponent>('searchButtonIconColor')
    );
    if (isSearchButtonIconColorChanged) {
      await this.applySearchButtonIconColor();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  //#endregion

  //
  //#region UI Handlers ...
  //
  handleSearchQueryValueChange(event: CustomEvent | any) {
    //
    const query =
      event && event.detail && event.detail.value
        ? event.detail.value.toString().trim().toLowerCase()
        : '';

    //
    this.queryChange.emit(query);
  }
  //#endregion

  //
  //#region Actions ...
  //#endregion

  //
  //#region Private ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XSearchBarActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XSearchBarAction.ClearValue:
            //
            this.value = '';
            this.detectChanges();
            break;

          //
          case XSearchBarAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }

  //
  //#region Color Appliers ...
  private async applyCancelButtonIconColor() {
    //
    const color = await this.getValueAsync(this.cancelButtonIconColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XSearchBarCssVars.CancelIconColor, `var(${bgColor})`);
    this.setCssVar(
      XSearchBarCssVars.CancelIconColor,
      `var(${bgColor})`,
      document.body
    );

    //
    this.detectChanges();
  }

  private async applyClearButtonIconColor() {
    //
    const color = await this.getValueAsync(this.clearButtonIconColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XSearchBarCssVars.ClearIconColor, `var(${bgColor})`);
    this.setCssVar(
      XSearchBarCssVars.ClearIconColor,
      `var(${bgColor})`,
      document.body
    );

    //
    this.detectChanges();
  }

  private async applySearchButtonIconColor() {
    //
    const color = await this.getValueAsync(this.searchButtonIconColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XSearchBarCssVars.SearchIconColor, `var(${bgColor})`);
    this.setCssVar(
      XSearchBarCssVars.SearchIconColor,
      `var(${bgColor})`,
      document.body
    );

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-search-bar\x-search-bar.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XSearchBarComponent } from './x-search-bar.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XSearchBarComponent],
  imports: [
    CommonModule,
    XBaseComponentModule,
  ],
  exports: [
    CommonModule,
    XSearchBarComponent,
    XBaseComponentModule,
  ],
})
export class XSearchBarModule { }

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-search-bar\x-search-bar.typings.ts

import {
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseComponentContainer,
  XBaseContainerComponent,
  IXBaseComponentContainer,
  XBaseComponentContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XSearchBarAction {
  ClearValue = 'clear_value',
  Refresh = 'refresh',
}

export type XSearchBarActionIdentifier = XSearchBarAction | string;

export interface XSearchBarActionModel
  extends XBaseActionModel<XSearchBarActionIdentifier> {}

export enum XSearchBarType {
  None = 'none',
  Text = 'text',
  Tel = 'tel',
  Url = 'url',
  Email = 'email',
  Numeric = 'numeric',
  Decimal = 'decimal',
  Search = 'search',
}

export type XSearchBarTypeIdentifier = XSearchBarType | string;

export enum XSearchBarEnterKeyHint {
  Done = 'done',
  Go = 'go',
  Next = 'next',
  Previous = 'previous',
  Search = 'search',
  Send = 'send',
  Enter = 'enter',
}

export type XSearchBarEnterKeyHintIdentifier = XSearchBarEnterKeyHint | string;

export enum XSearchBarShowButton {
  Always = 'always',
  Focus = 'focus',
  Never = 'never',
}

export type XSearchBarShowButtonIdentifier = XSearchBarShowButton | string;

export enum XSearchBarCssVars {
  //
  ClearIconColor = 'clear-icon-color',
  CancelIconColor = 'cancel-icon-color',
  SearchIconColor = 'search-icon-color',
}

export type XSearchBarCssVarsIdentifier = XSearchBarCssVars | string;

export enum XSearchBarEvent {
  FOCUS = 'focus',
  BLUR = 'blur',
  CANCEL = 'cancel',
  CLEAR = 'clear',
  INPUT = 'input',
}

export type XSearchBarEventIdentifier = XSearchBarEvent | string;

//
//#region Container ...
export interface IXSearchBarContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XSearchBarActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  //
  searchInputColor: XStandardType<XColorIdentifier>;
  foregroundSearchInputColor: XStandardType<boolean>;

  //
  type: XStandardType<XSearchBarTypeIdentifier>;
  title: XStandardType<XResourceIdentifier>;
  value: XStandardType<string>;
  enterKeyHint: XStandardType<XSearchBarEnterKeyHintIdentifier>;
  keyboardType: XStandardType<XSearchBarTypeIdentifier>;
  spellcheck: XStandardType<boolean>;
  debounce: XStandardType<number>;

  //
  showCancelButton: XStandardType<XSearchBarShowButtonIdentifier>;
  cancelButtonIcon: XStandardType<XIconIdentifier>;
  cancelButtonTitle: XStandardType<XResourceIdentifier>;
  cancelButtonIconColor: XStandardType<XColorIdentifier>;

  //
  showClearButton: XStandardType<XSearchBarShowButtonIdentifier>;
  clearButtonIcon: XStandardType<XIconIdentifier>;
  clearButtonIconColor: XStandardType<XColorIdentifier>;

  //
  searchButtonIcon: XStandardType<XIconIdentifier>;
  searchButtonIconColor: XStandardType<XColorIdentifier>;

  //
  queryChange: EventEmitter<string>;
  onEvent: EventEmitter<XSearchBarEventIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XSearchBarContainer
  extends XBaseContainer
  implements Readonly<IXSearchBarContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XSearchBarActionModel> =
    new Subject<XSearchBarActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  //
  readonly searchInputColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly foregroundSearchInputColor: XStandardType<boolean> = false;

  //
  readonly type: XStandardType<XSearchBarTypeIdentifier> =
    XSearchBarType.Search;
  readonly title: XStandardType<XResourceIdentifier> = XResourceIDs.search;
  readonly value: XStandardType<string>;
  readonly enterKeyHint: XStandardType<XSearchBarEnterKeyHintIdentifier> =
    XSearchBarEnterKeyHint.Search;
  readonly keyboardType: XStandardType<XSearchBarTypeIdentifier> =
    XSearchBarType.Search;
  readonly spellcheck: XStandardType<boolean> = false;
  readonly debounce: XStandardType<number> =
    this.config.defaultSearchDebounceTime || 500;

  //
  readonly showCancelButton: XStandardType<XSearchBarShowButtonIdentifier> =
    XSearchBarShowButton.Never;
  readonly cancelButtonIcon: XStandardType<XIconIdentifier>;
  readonly cancelButtonTitle: XStandardType<XResourceIdentifier>;
  readonly cancelButtonIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Primary;

  //
  readonly showClearButton: XStandardType<XSearchBarShowButtonIdentifier> =
    XSearchBarShowButton.Always;
  readonly clearButtonIcon: XStandardType<XIconIdentifier>;
  readonly clearButtonIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Danger;

  //
  readonly searchButtonIcon: XStandardType<XIconIdentifier>;
  readonly searchButtonIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Primary;

  //
  readonly queryChange: EventEmitter<string> = new EventEmitter<string>();
  readonly onEvent: EventEmitter<XSearchBarEventIdentifier> =
    new EventEmitter<XSearchBarEventIdentifier>();
  //#endregion

  //
  //#region Constructor ...
  constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig
  ) {
    super();
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XSearchBarContainerComponent
  extends XBaseContainerComponent
  implements IXSearchBarContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  @Input()
  searchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchInputColor;

  @Input()
  foregroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.foregroundSearchInputColor;

  //
  @Input()
  type: XStandardType<XSearchBarTypeIdentifier> = this.propertyProvider.type;

  @Input()
  title: XStandardType<XResourceIdentifier> = this.propertyProvider.title;

  @Input()
  value: XStandardType<string> = this.propertyProvider.value;

  @Input()
  enterKeyHint: XStandardType<XSearchBarEnterKeyHintIdentifier> =
    this.propertyProvider.enterKeyHint;

  @Input()
  keyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.keyboardType;

  @Input()
  spellcheck: XStandardType<boolean> = this.propertyProvider.spellcheck;

  @Input()
  debounce: XStandardType<number> = this.propertyProvider.debounce;

  //
  @Input()
  showCancelButton: XStandardType<XSearchBarShowButtonIdentifier> =
    this.propertyProvider.showCancelButton;

  @Input()
  cancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.cancelButtonIcon;

  @Input()
  cancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cancelButtonTitle;

  @Input()
  cancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.cancelButtonIconColor;

  //
  @Input()
  showClearButton: XStandardType<XSearchBarShowButtonIdentifier> =
    this.propertyProvider.showClearButton;

  @Input()
  clearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.clearButtonIcon;

  @Input()
  clearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.clearButtonIconColor;

  //
  @Input()
  searchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchButtonIcon;

  @Input()
  searchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchButtonIconColor;

  //
  @Output()
  queryChange: EventEmitter<string> = new EventEmitter();

  @Output()
  onEvent: EventEmitter<XSearchBarEventIdentifier> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XSearchBarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXSearchBarContainer
  ): Observable<IXSearchBarContainer> {
    return forkJoin({
      //
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),

      //
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),
      searchInputColor: this.getValue(
        container ? container.searchInputColor : this.searchInputColor
      ),
      foregroundSearchInputColor: this.getValue(
        container
          ? container.foregroundSearchInputColor
          : this.foregroundSearchInputColor
      ),
      type: this.getValue(container ? container.type : this.type),
      title: this.getValue(container ? container.title : this.title),
      value: this.getValue(container ? container.value : this.value),
      enterKeyHint: this.getValue(
        container ? container.enterKeyHint : this.enterKeyHint
      ),
      keyboardType: this.getValue(
        container ? container.keyboardType : this.keyboardType
      ),
      spellcheck: this.getValue(
        container ? container.spellcheck : this.spellcheck
      ),
      debounce: this.getValue(container ? container.debounce : this.debounce),
      showCancelButton: this.getValue(
        container ? container.showCancelButton : this.showCancelButton
      ),
      cancelButtonIcon: this.getValue(
        container ? container.cancelButtonIcon : this.cancelButtonIcon
      ),
      cancelButtonTitle: this.getValue(
        container ? container.cancelButtonTitle : this.cancelButtonTitle
      ),
      cancelButtonIconColor: this.getValue(
        container ? container.cancelButtonIconColor : this.cancelButtonIconColor
      ),
      showClearButton: this.getValue(
        container ? container.showCancelButton : this.showCancelButton
      ),
      clearButtonIcon: this.getValue(
        container ? container.clearButtonIcon : this.clearButtonIcon
      ),
      clearButtonIconColor: this.getValue(
        container ? container.clearButtonIconColor : this.clearButtonIconColor
      ),
      searchButtonIcon: this.getValue(
        container ? container.searchButtonIcon : this.searchButtonIcon
      ),
      searchButtonIconColor: this.getValue(
        container ? container.searchButtonIconColor : this.searchButtonIconColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          onEvent: this.onEvent,
          queryChange: this.queryChange,
        };
      })
    );
  }
  //#endregion
}
//#endregion

//
//#region ComponentContainer ...
type TXComponentSearchBarContainer = {
  [TKey in keyof IXSearchBarContainer as `searchBar${Capitalize<
    string & TKey
  >}`]: IXSearchBarContainer[TKey];
};

export interface IXComponentSearchBarContainer
  extends TXComponentSearchBarContainer,
    IXBaseComponentContainer {
  //
  showSearchBar: XStandardType<boolean>;

  //
  searchBarActionProvider: Subject<XSearchBarActionModel>;
  searchBarCssClass: XStandardType<XOneOrManyType<string>>;

  //
  searchBarColor: XStandardType<XColorIdentifier>;
  searchBarForegroundColor: XStandardType<boolean>;

  //
  searchBarSearchInputColor: XStandardType<XColorIdentifier>;
  searchBarForegroundSearchInputColor: XStandardType<boolean>;

  //
  searchBarType: XStandardType<XSearchBarTypeIdentifier>;
  searchBarTitle: XStandardType<XResourceIdentifier>;
  searchBarValue: XStandardType<string>;
  searchBarEnterKeyHint: XStandardType<XSearchBarEnterKeyHintIdentifier>;
  searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier>;
  searchBarSpellcheck: XStandardType<boolean>;
  searchBarDebounce: XStandardType<number>;

  //
  searchBarShowCancelButton: XStandardType<XSearchBarShowButtonIdentifier>;
  searchBarCancelButtonIcon: XStandardType<XIconIdentifier>;
  searchBarCancelButtonTitle: XStandardType<XResourceIdentifier>;
  searchBarCancelButtonIconColor: XStandardType<XColorIdentifier>;

  //
  searchBarShowClearButton: XStandardType<XSearchBarShowButtonIdentifier>;
  searchBarClearButtonIcon: XStandardType<XIconIdentifier>;
  searchBarClearButtonIconColor: XStandardType<XColorIdentifier>;

  //
  searchBarSearchButtonIcon: XStandardType<XIconIdentifier>;
  searchBarSearchButtonIconColor: XStandardType<XColorIdentifier>;

  //
  searchBarQueryChange: EventEmitter<string>;
  searchBarOnEvent: EventEmitter<XSearchBarEventIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XComponentSearchBarContainer
  extends XBaseComponentContainer
  implements Readonly<IXComponentSearchBarContainer>
{
  //
  //#region Props ...
  //
  readonly showSearchBar: XStandardType<boolean> = true;

  //
  readonly searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.actionProvider;
  readonly searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  readonly searchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.color;
  readonly searchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  //
  readonly searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchInputColor;
  readonly searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.foregroundSearchInputColor;

  //
  readonly searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.type;
  readonly searchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.title;
  readonly searchBarValue: XStandardType<string> = this.propertyProvider.value;
  readonly searchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.enterKeyHint;
  readonly searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.keyboardType;
  readonly searchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.spellcheck;
  readonly searchBarDebounce: XStandardType<number> =
    this.propertyProvider.debounce;

  //
  readonly searchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.showCancelButton;
  readonly searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.cancelButtonIcon;
  readonly searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.cancelButtonTitle;
  readonly searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.cancelButtonIconColor;

  //
  readonly searchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.showClearButton;
  readonly searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.clearButtonIcon;
  readonly searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.clearButtonIconColor;

  //
  readonly searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchButtonIcon;
  readonly searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchButtonIconColor;

  //
  readonly searchBarQueryChange: EventEmitter<string> =
    this.propertyProvider.queryChange;
  readonly searchBarOnEvent: EventEmitter<XSearchBarEventIdentifier> =
    this.propertyProvider.onEvent;
  //#endregion

  //
  //#region Constructor ...
  constructor(protected propertyProvider: XSearchBarContainer) {
    super();
  }
  //#endregion
}

@Component({
  template: ``,
})
export abstract class XComponentSearchBarContainerComponent
  extends XBaseComponentContainerComponent
  implements IXComponentSearchBarContainer
{
  //
  //#region Props ...
  //
  @Input()
  showSearchBar: XStandardType<boolean> = this.propertyProvider.showSearchBar;

  //
  @Input()
  searchBarActionProvider: Subject<XSearchBarActionModel> =
    this.propertyProvider.searchBarActionProvider;

  @Input()
  searchBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.searchBarCssClass;

  //
  @Input()
  searchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarColor;

  @Input()
  searchBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundColor;

  //
  @Input()
  searchBarSearchInputColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchInputColor;

  @Input()
  searchBarForegroundSearchInputColor: XStandardType<boolean> =
    this.propertyProvider.searchBarForegroundSearchInputColor;

  //
  @Input()
  searchBarType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarType;

  @Input()
  searchBarTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarTitle;

  @Input()
  searchBarValue: XStandardType<string> = this.propertyProvider.searchBarValue;

  @Input()
  searchBarEnterKeyHint: XStandardType<string> =
    this.propertyProvider.searchBarEnterKeyHint;

  @Input()
  searchBarKeyboardType: XStandardType<XSearchBarTypeIdentifier> =
    this.propertyProvider.searchBarKeyboardType;

  @Input()
  searchBarSpellcheck: XStandardType<boolean> =
    this.propertyProvider.searchBarSpellcheck;

  @Input()
  searchBarDebounce: XStandardType<number> =
    this.propertyProvider.searchBarDebounce;

  //
  @Input()
  searchBarShowCancelButton: XStandardType<string> =
    this.propertyProvider.searchBarShowCancelButton;

  @Input()
  searchBarCancelButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarCancelButtonIcon;

  @Input()
  searchBarCancelButtonTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchBarCancelButtonTitle;

  @Input()
  searchBarCancelButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarCancelButtonIconColor;

  //
  @Input()
  searchBarShowClearButton: XStandardType<string> =
    this.propertyProvider.searchBarShowClearButton;

  @Input()
  searchBarClearButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarClearButtonIcon;

  @Input()
  searchBarClearButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarClearButtonIconColor;

  //
  @Input()
  searchBarSearchButtonIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.searchBarSearchButtonIcon;

  @Input()
  searchBarSearchButtonIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarSearchButtonIconColor;

  //
  @Output()
  searchBarQueryChange: EventEmitter<string> = new EventEmitter();

  @Output()
  searchBarOnEvent: EventEmitter<string> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XComponentSearchBarContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXComponentSearchBarContainer
  ): Observable<IXComponentSearchBarContainer> {
    return forkJoin({
      //
      showSearchBar: this.getValue(
        container ? container.showSearchBar : this.showSearchBar
      ),

      //
      searchBarCssClass: this.getArrayValue(
        container ? container.searchBarCssClass : this.searchBarCssClass
      ),

      //
      searchBarColor: this.getValue(
        container ? container.searchBarColor : this.searchBarColor
      ),
      searchBarForegroundColor: this.getValue(
        container
          ? container.searchBarForegroundColor
          : this.searchBarForegroundColor
      ),
      searchBarSearchInputColor: this.getValue(
        container
          ? container.searchBarSearchInputColor
          : this.searchBarSearchInputColor
      ),
      searchBarForegroundSearchInputColor: this.getValue(
        container
          ? container.searchBarForegroundSearchInputColor
          : this.searchBarForegroundSearchInputColor
      ),
      searchBarType: this.getValue(
        container ? container.searchBarType : this.searchBarType
      ),
      searchBarTitle: this.getValue(
        container ? container.searchBarTitle : this.searchBarTitle
      ),
      searchBarValue: this.getValue(
        container ? container.searchBarValue : this.searchBarValue
      ),
      searchBarEnterKeyHint: this.getValue(
        container ? container.searchBarEnterKeyHint : this.searchBarEnterKeyHint
      ),
      searchBarKeyboardType: this.getValue(
        container ? container.searchBarKeyboardType : this.searchBarKeyboardType
      ),
      searchBarSpellcheck: this.getValue(
        container ? container.searchBarSpellcheck : this.searchBarSpellcheck
      ),
      searchBarDebounce: this.getValue(
        container ? container.searchBarDebounce : this.searchBarDebounce
      ),
      searchBarShowCancelButton: this.getValue(
        container
          ? container.searchBarShowCancelButton
          : this.searchBarShowCancelButton
      ),
      searchBarCancelButtonIcon: this.getValue(
        container
          ? container.searchBarCancelButtonIcon
          : this.searchBarCancelButtonIcon
      ),
      searchBarCancelButtonTitle: this.getValue(
        container
          ? container.searchBarCancelButtonTitle
          : this.searchBarCancelButtonTitle
      ),
      searchBarCancelButtonIconColor: this.getValue(
        container
          ? container.searchBarCancelButtonIconColor
          : this.searchBarCancelButtonIconColor
      ),
      searchBarShowClearButton: this.getValue(
        container
          ? container.searchBarShowClearButton
          : this.searchBarShowClearButton
      ),
      searchBarClearButtonIcon: this.getValue(
        container
          ? container.searchBarClearButtonIcon
          : this.searchBarClearButtonIcon
      ),
      searchBarClearButtonIconColor: this.getValue(
        container
          ? container.searchBarClearButtonIconColor
          : this.searchBarClearButtonIconColor
      ),
      searchBarSearchButtonIcon: this.getValue(
        container
          ? container.searchBarSearchButtonIcon
          : this.searchBarSearchButtonIcon
      ),
      searchBarSearchButtonIconColor: this.getValue(
        container
          ? container.searchBarSearchButtonIconColor
          : this.searchBarSearchButtonIconColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          searchBarActionProvider: container
            ? container.searchBarActionProvider
            : this.searchBarActionProvider,

          //
          searchBarOnEvent: this.searchBarOnEvent,
          searchBarQueryChange: this.searchBarQueryChange,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slide.animation.providers.ts

import { XSlideAnimationProvider } from './x-slider.typings';

//
//#region Animations ...
export const Coverflow: XSlideAnimationProvider = {
  coverflowEffect: {
    rotate: 50,
    stretch: 0,
    depth: 100,
    modifier: 1,
    slideShadows: true
  },
  on: {
    beforeInit() {
      const swiper = this;
      swiper.classNames.push(
        `${swiper.params.containerModifierClass}coverflow`
      );
      swiper.classNames.push(`${swiper.params.containerModifierClass}3d`);

      swiper.params.watchSlidesProgress = true;
      swiper.originalParams.watchSlidesProgress = true;
    },
    setTranslate() {
      const swiper = this;
      const {
        width: swiperWidth,
        height: swiperHeight,
        slides,
        $wrapperEl,
        slidesSizesGrid
      } = swiper;
      const params = swiper.params.coverflowEffect;
      const isHorizontal = swiper.isHorizontal();
      const transform$$1 = swiper.translate;
      const center = isHorizontal
        ? -transform$$1 + swiperWidth / 2
        : -transform$$1 + swiperHeight / 2;
      const rotate = isHorizontal ? params.rotate : -params.rotate;
      const translate = params.depth;
      // Each slide offset from center
      for (let i = 0, length = slides.length; i < length; i += 1) {
        const $slideEl = slides.eq(i);
        const slideSize = slidesSizesGrid[i];
        const slideOffset = $slideEl[0].swiperSlideOffset;
        const offsetMultiplier =
          ((center - slideOffset - slideSize / 2) / slideSize) *
          params.modifier;

        let rotateY = isHorizontal ? rotate * offsetMultiplier : 0;
        let rotateX = isHorizontal ? 0 : rotate * offsetMultiplier;
        // var rotateZ = 0
        let translateZ = -translate * Math.abs(offsetMultiplier);

        let translateY = isHorizontal ? 0 : params.stretch * offsetMultiplier;
        let translateX = isHorizontal ? params.stretch * offsetMultiplier : 0;

        // Fix for ultra small values
        if (Math.abs(translateX) < 0.001) {
          translateX = 0;
        }
        if (Math.abs(translateY) < 0.001) {
          translateY = 0;
        }
        if (Math.abs(translateZ) < 0.001) {
          translateZ = 0;
        }
        if (Math.abs(rotateY) < 0.001) {
          rotateY = 0;
        }
        if (Math.abs(rotateX) < 0.001) {
          rotateX = 0;
        }

        const slideTransform =
          `translate3d(${translateX}px,${translateY}px,${translateZ}px)` +
          `  rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;

        $slideEl.transform(slideTransform);
        $slideEl[0].style.zIndex = -Math.abs(Math.round(offsetMultiplier)) + 1;
        if (params.slideShadows) {
          // Set shadows
          let $shadowBeforeEl = isHorizontal
            ? $slideEl.find('.swiper-slide-shadow-left')
            : $slideEl.find('.swiper-slide-shadow-top');
          let $shadowAfterEl = isHorizontal
            ? $slideEl.find('.swiper-slide-shadow-right')
            : $slideEl.find('.swiper-slide-shadow-bottom');
          if ($shadowBeforeEl.length === 0) {
            $shadowBeforeEl = swiper.$(
              `<div class="swiper-slide-shadow-${
                isHorizontal ? 'left' : 'top'
              }"></div>`
            );
            // $slideEl.append($shadowBeforeEl);
          }
          if ($shadowAfterEl.length === 0) {
            $shadowAfterEl = swiper.$(
              `<div class="swiper-slide-shadow-${
                isHorizontal ? 'right' : 'bottom'
              }"></div>`
            );
            // $slideEl.append($shadowAfterEl);
          }
          if ($shadowBeforeEl.length) {
            $shadowBeforeEl[0].style.opacity =
              offsetMultiplier > 0 ? offsetMultiplier : 0;
          }
          if ($shadowAfterEl.length) {
            $shadowAfterEl[0].style.opacity =
              -offsetMultiplier > 0 ? -offsetMultiplier : 0;
          }
        }
      }

      // Set correct perspective for IE10
      if (
        swiper.support.pointerEvents ||
        swiper.support.prefixedPointerEvents
      ) {
        const ws = $wrapperEl[0].style;
        ws.perspectiveOrigin = `${center}px 50%`;
      }
    },
    setTransition(duration) {
      const swiper = this;
      swiper.slides
        .transition(duration)
        .find(
          '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left'
        )
        .transition(duration);
    }
  }
};

//
export const Cube: XSlideAnimationProvider = {
  cubeEffect: {
    shadow: true,
    slideShadows: true,
    shadowOffset: 20,
    shadowScale: 0.94
  },
  on: {
    beforeInit() {
      const swiper = this;
      swiper.classNames.push(`${swiper.params.containerModifierClass}cube`);
      swiper.classNames.push(`${swiper.params.containerModifierClass}3d`);

      const overwriteParams = {
        slidesPerView: 1,
        slidesPerColumn: 1,
        slidesPerGroup: 1,
        watchSlidesProgress: true,
        resistanceRatio: 0,
        spaceBetween: 0,
        centeredSlides: false,
        virtualTranslate: true
      };

      this.params = Object.assign(this.params, overwriteParams);
      this.originalParams = Object.assign(this.originalParams, overwriteParams);
    },
    setTranslate() {
      const swiper = this;
      const {
        $el,
        $wrapperEl,
        slides,
        width: swiperWidth,
        height: swiperHeight,
        rtlTranslate: rtl,
        size: swiperSize
      } = swiper;
      const params = swiper.params.cubeEffect;
      const isHorizontal = swiper.isHorizontal();
      const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
      let wrapperRotate = 0;
      let $cubeShadowEl;
      if (params.shadow) {
        if (isHorizontal) {
          $cubeShadowEl = $wrapperEl.find('.swiper-cube-shadow');
          if ($cubeShadowEl.length === 0) {
            $cubeShadowEl = swiper.$('<div class="swiper-cube-shadow"></div>');
            $wrapperEl.append($cubeShadowEl);
          }
          $cubeShadowEl.css({ height: `${swiperWidth}px` });
        } else {
          $cubeShadowEl = $el.find('.swiper-cube-shadow');
          if ($cubeShadowEl.length === 0) {
            $cubeShadowEl = swiper.$('<div class="swiper-cube-shadow"></div>');
            $el.append($cubeShadowEl);
          }
        }
      }

      for (let i = 0; i < slides.length; i += 1) {
        const $slideEl = slides.eq(i);
        let slideIndex = i;
        if (isVirtual) {
          slideIndex = parseInt($slideEl.attr('data-swiper-slide-index'), 10);
        }
        let slideAngle = slideIndex * 90;
        let round = Math.floor(slideAngle / 360);
        if (rtl) {
          slideAngle = -slideAngle;
          round = Math.floor(-slideAngle / 360);
        }
        const progress = Math.max(Math.min($slideEl[0].progress, 1), -1);
        let tx = 0;
        let ty = 0;
        let tz = 0;
        if (slideIndex % 4 === 0) {
          tx = -round * 4 * swiperSize;
          tz = 0;
        } else if ((slideIndex - 1) % 4 === 0) {
          tx = 0;
          tz = -round * 4 * swiperSize;
        } else if ((slideIndex - 2) % 4 === 0) {
          tx = swiperSize + round * 4 * swiperSize;
          tz = swiperSize;
        } else if ((slideIndex - 3) % 4 === 0) {
          tx = -swiperSize;
          tz = 3 * swiperSize + swiperSize * 4 * round;
        }
        if (rtl) {
          tx = -tx;
        }

        if (!isHorizontal) {
          ty = tx;
          tx = 0;
        }

        const transform$$1 = `rotateX(${
          isHorizontal ? 0 : -slideAngle
        }deg) rotateY(${
          isHorizontal ? slideAngle : 0
        }deg) translate3d(${tx}px, ${ty}px, ${tz}px)`;
        if (progress <= 1 && progress > -1) {
          wrapperRotate = slideIndex * 90 + progress * 90;
          if (rtl) {
            wrapperRotate = -slideIndex * 90 - progress * 90;
          }
        }
        $slideEl.transform(transform$$1);
        if (params.slideShadows) {
          // Set shadows
          let shadowBefore = isHorizontal
            ? $slideEl.find('.swiper-slide-shadow-left')
            : $slideEl.find('.swiper-slide-shadow-top');
          let shadowAfter = isHorizontal
            ? $slideEl.find('.swiper-slide-shadow-right')
            : $slideEl.find('.swiper-slide-shadow-bottom');
          if (shadowBefore.length === 0) {
            shadowBefore = swiper.$(
              `<div class="swiper-slide-shadow-${
                isHorizontal ? 'left' : 'top'
              }"></div>`
            );
            $slideEl.append(shadowBefore);
          }
          if (shadowAfter.length === 0) {
            shadowAfter = swiper.$(
              `<div class="swiper-slide-shadow-${
                isHorizontal ? 'right' : 'bottom'
              }"></div>`
            );
            $slideEl.append(shadowAfter);
          }
          if (shadowBefore.length) {
            shadowBefore[0].style.opacity = Math.max(-progress, 0);
          }
          if (shadowAfter.length) {
            shadowAfter[0].style.opacity = Math.max(progress, 0);
          }
        }
      }
      $wrapperEl.css({
        '-webkit-transform-origin': `50% 50% -${swiperSize / 2}px`,
        '-moz-transform-origin': `50% 50% -${swiperSize / 2}px`,
        '-ms-transform-origin': `50% 50% -${swiperSize / 2}px`,
        'transform-origin': `50% 50% -${swiperSize / 2}px`
      });

      if (params.shadow) {
        if (isHorizontal) {
          $cubeShadowEl.transform(
            `translate3d(0px, ${swiperWidth / 2 +
              params.shadowOffset}px, ${-swiperWidth /
              2}px) rotateX(90deg) rotateZ(0deg) scale(${params.shadowScale})`
          );
        } else {
          const shadowAngle =
            Math.abs(wrapperRotate) -
            Math.floor(Math.abs(wrapperRotate) / 90) * 90;
          const multiplier =
            1.5 -
            (Math.sin((shadowAngle * 2 * Math.PI) / 360) / 2 +
              Math.cos((shadowAngle * 2 * Math.PI) / 360) / 2);
          const scale1 = params.shadowScale;
          const scale2 = params.shadowScale / multiplier;
          const offset$$1 = params.shadowOffset;
          $cubeShadowEl.transform(
            `scale3d(${scale1}, 1, ${scale2}) translate3d(0px, ${swiperHeight /
              2 +
              offset$$1}px, ${-swiperHeight / 2 / scale2}px) rotateX(-90deg)`
          );
        }
      }

      const zFactor =
        swiper.browser.isSafari || swiper.browser.isUiWebView
          ? -swiperSize / 2
          : 0;
      $wrapperEl.transform(
        `translate3d(0px,0,${zFactor}px) rotateX(${
          swiper.isHorizontal() ? 0 : wrapperRotate
        }deg) rotateY(${swiper.isHorizontal() ? -wrapperRotate : 0}deg)`
      );
    },
    setTransition(duration) {
      const swiper = this;
      const { $el, slides } = swiper;
      slides
        .transition(duration)
        .find(
          '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left'
        )
        .transition(duration);
      if (swiper.params.cubeEffect.shadow && !swiper.isHorizontal()) {
        $el.find('.swiper-cube-shadow').transition(duration);
      }
    }
  }
};

//
export const Fade: XSlideAnimationProvider = {
  on: {
    beforeInit() {
      const swiper = this;
      swiper.classNames.push(`${swiper.params.containerModifierClass}fade`);
      const overwriteParams = {
        slidesPerView: 1,
        slidesPerColumn: 1,
        slidesPerGroup: 1,
        watchSlidesProgress: true,
        spaceBetween: 0,
        virtualTranslate: true
      };
      swiper.params = Object.assign(swiper.params, overwriteParams);
      swiper.params = Object.assign(swiper.originalParams, overwriteParams);
    },
    setTranslate() {
      const swiper = this;
      const { slides } = swiper;
      for (let i = 0; i < slides.length; i += 1) {
        const $slideEl = swiper.slides.eq(i);
        const offset$$1 = $slideEl[0].swiperSlideOffset;
        let tx = -offset$$1;
        if (!swiper.params.virtualTranslate) {
          tx -= swiper.translate;
        }
        let ty = 0;
        if (!swiper.isHorizontal()) {
          ty = tx;
          tx = 0;
        }
        const slideOpacity = swiper.params.fadeEffect.crossFade
          ? Math.max(1 - Math.abs($slideEl[0].progress), 0)
          : 1 + Math.min(Math.max($slideEl[0].progress, -1), 0);
        $slideEl
          .css({
            opacity: slideOpacity
          })
          .transform(`translate3d(${tx}px, ${ty}px, 0px)`);
      }
    },
    setTransition(duration) {
      const swiper = this;
      const { slides, $wrapperEl } = swiper;
      slides.transition(duration);
      if (swiper.params.virtualTranslate && duration !== 0) {
        let eventTriggered = false;
        slides.transitionEnd(() => {
          if (eventTriggered) {
            return;
          }
          if (!swiper || swiper.destroyed) {
            return;
          }
          eventTriggered = true;
          swiper.animating = false;
          const triggerEvents = ['webkitTransitionEnd', 'transitionend'];
          // tslint:disable-next-line:prefer-for-of
          for (let i = 0; i < triggerEvents.length; i += 1) {
            $wrapperEl.trigger(triggerEvents[i]);
          }
        });
      }
    }
  }
};

//
export const Flip: XSlideAnimationProvider = {
  on: {
    beforeInit() {
      const swiper = this;
      swiper.classNames.push(`${swiper.params.containerModifierClass}flip`);
      swiper.classNames.push(`${swiper.params.containerModifierClass}3d`);
      const overwriteParams = {
        slidesPerView: 1,
        slidesPerColumn: 1,
        slidesPerGroup: 1,
        watchSlidesProgress: true,
        spaceBetween: 0,
        virtualTranslate: true
      };
      swiper.params = Object.assign(swiper.params, overwriteParams);
      swiper.originalParams = Object.assign(
        swiper.originalParams,
        overwriteParams
      );
    },
    setTranslate() {
      const swiper = this;
      const { $, slides, rtlTranslate: rtl } = swiper;
      for (let i = 0; i < slides.length; i += 1) {
        const $slideEl = slides.eq(i);
        let progress = $slideEl[0].progress;
        if (swiper.params.flipEffect.limitRotation) {
          progress = Math.max(Math.min($slideEl[0].progress, 1), -1);
        }
        const offset$$1 = $slideEl[0].swiperSlideOffset;
        const rotate = -180 * progress;
        let rotateY = rotate;
        let rotateX = 0;
        let tx = -offset$$1;
        let ty = 0;
        if (!swiper.isHorizontal()) {
          ty = tx;
          tx = 0;
          rotateX = -rotateY;
          rotateY = 0;
        } else if (rtl) {
          rotateY = -rotateY;
        }

        $slideEl[0].style.zIndex =
          -Math.abs(Math.round(progress)) + slides.length;

        if (swiper.params.flipEffect.slideShadows) {
          // Set shadows
          let shadowBefore = swiper.isHorizontal()
            ? $slideEl.find('.swiper-slide-shadow-left')
            : $slideEl.find('.swiper-slide-shadow-top');
          let shadowAfter = swiper.isHorizontal()
            ? $slideEl.find('.swiper-slide-shadow-right')
            : $slideEl.find('.swiper-slide-shadow-bottom');
          if (shadowBefore.length === 0) {
            shadowBefore = swiper.$(
              `<div class="swiper-slide-shadow-${
                swiper.isHorizontal() ? 'left' : 'top'
              }"></div>`
            );
            $slideEl.append(shadowBefore);
          }
          if (shadowAfter.length === 0) {
            shadowAfter = swiper.$(
              `<div class="swiper-slide-shadow-${
                swiper.isHorizontal() ? 'right' : 'bottom'
              }"></div>`
            );
            $slideEl.append(shadowAfter);
          }
          if (shadowBefore.length) {
            shadowBefore[0].style.opacity = Math.max(-progress, 0);
          }
          if (shadowAfter.length) {
            shadowAfter[0].style.opacity = Math.max(progress, 0);
          }
        }
        $slideEl.transform(
          `translate3d(${tx}px, ${ty}px, 0px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
        );
      }
    },
    setTransition(duration) {
      const swiper = this;
      const { slides, activeIndex, $wrapperEl } = swiper;
      slides
        .transition(duration)
        .find(
          '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left'
        )
        .transition(duration);
      if (swiper.params.virtualTranslate && duration !== 0) {
        let eventTriggered = false;
        // eslint-disable-next-line
        slides.eq(activeIndex).transitionEnd(function onTransitionEnd() {
          if (eventTriggered) {
            return;
          }
          if (!swiper || swiper.destroyed) {
            return;
          }

          eventTriggered = true;
          swiper.animating = false;
          const triggerEvents = ['webkitTransitionEnd', 'transitionend'];
          // tslint:disable-next-line:prefer-for-of
          for (let i = 0; i < triggerEvents.length; i += 1) {
            $wrapperEl.trigger(triggerEvents[i]);
          }
        });
      }
    }
  }
};
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slider.component.html

<!-- WrapWithCard Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component Template -->
<ng-template #componentRef>
  <section
    class="x-slider x-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <ion-slides
      #slider
      class="slider"
      [options]="sliderConfig"
      [pager]="hasPager() | async"
      [class.no-pager]="hasPager() | async"
      (ionSlideTap)="handleSlideChanged()"
      (ionSlideNextEnd)="handleSlideChanged()"
      (ionSlidePrevEnd)="handleSlideChanged()"
      (ionSlideTouchEnd)="handleSlideChanged()"
      (ionSlideDidChange)="handleSlideChanged()"
      (ionSlideTransitionEnd)="handleSlideChanged()"
    >
      <ng-container *ngFor="let slide of slides">
        <ng-container *ngTemplateOutlet="slide.template"></ng-container>
      </ng-container>
    </ion-slides>
  </section>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slider.component.scss

:host {
  --paginator: var(--x-color-dark-tint);
  --paginator-active: var(--x-color-secondary);
}

ion-slides {
  --bullet-background: var(--paginator);
  --bullet-background-active: var(--paginator-active);
}

.no-pager {
  padding-bottom: 0 !important;
}

.slider {
  padding-bottom: 30px !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slider.component.ts

import {
  Component,
  QueryList,
  ViewChild,
  ContentChildren,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  XSliderAction,
  XSliderConfig,
  XSliderAnimation,
  XSliderActionModel,
  defaultXSliderConfig,
  XSliderContainerComponent,
  XSliderAnimationIdentifier,
  defaultXSliderPaginationConfig,
} from './x-slider.typings';
import { map } from 'rxjs/operators';
import { IonSlides } from '@ionic/angular';
import { Subject, Subscription } from 'rxjs';
import { XSlideComponent } from './x-slide/x-slide.component';
import { Coverflow, Cube, Fade, Flip } from './x-slide.animation.providers';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-slider',
  templateUrl: './x-slider.component.html',
  styleUrls: ['./x-slider.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSliderComponent extends XSliderContainerComponent {
  //
  //#region Props ...
  //
  private LAST_ACTIVE_INDEX = 0;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  //#region Slider Config ...
  //
  private SLIDER_CONFIG: any;

  //
  get sliderConfig() {
    //
    if (!this.SLIDER_CONFIG) {
      this.SLIDER_CONFIG = this.getConfiguration();
    }

    //
    return this.SLIDER_CONFIG;
  }
  //#endregion

  //
  //#region Slider ...
  //
  private SLIDER: IonSlides;

  //
  @ViewChild('slider', { static: false })
  set slider(s: IonSlides) {
    this.SLIDER = s;
    this.handleConfigurationChanged();
  }

  //
  get slider() {
    return this.SLIDER;
  }
  //#endregion

  //
  //#region Slides ...
  //
  private SLIDES: QueryList<XSlideComponent>;

  //
  @ContentChildren(XSlideComponent)
  set slides(s: QueryList<XSlideComponent>) {
    this.SLIDES = s;
    this.handleConfigurationChanged();
  }

  //
  get slides() {
    return this.SLIDES;
  }
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XSliderComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    const isConfigChanged = changeKeys.includes(
      nameof<XSliderComponent>('options')
    );
    const isAnimationChanged = changeKeys.includes(
      nameof<XSliderComponent>('animation')
    );

    //
    if (isConfigChanged || isAnimationChanged) {
      await this.handleConfigurationChanged();
    }
  }

  afterContentInit() {
    super.afterContentInit();

    //
    if (this.slider) {
      this.update();
    }

    //
    this.managerService.dispatchResizeEvent();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Ui Action Providers ...
  hasPager() {
    return this.getValue(this.options).pipe(
      map((options) => options.pagination)
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleSlideChanged() {
    //
    if (isNullOrUndefined(this.slider)) {
      return;
    }

    //
    const index = await this.slider.getActiveIndex();
    if (index === this.LAST_ACTIVE_INDEX) {
      return;
    }

    //
    this.LAST_ACTIVE_INDEX = index;
    this.onSlideChange.emit(index);
  }
  //#endregion

  //
  //#region Provided Actions ...
  length() {
    return this.slider.length();
  }

  getActiveIndex() {
    return this.slider.getActiveIndex();
  }

  getPreviousIndex() {
    return this.slider.getPreviousIndex();
  }

  getSwiper() {
    return this.slider.getSwiper();
  }

  isBeginning() {
    return this.slider.isBeginning();
  }

  isEnd() {
    return this.slider.isEnd();
  }

  lockSwipeToNext(lock: boolean) {
    return this.slider.lockSwipeToNext(lock);
  }

  lockSwipeToPrev(lock: boolean) {
    return this.slider.lockSwipeToPrev(lock);
  }

  lockSwipes(lock: boolean) {
    return this.slider.lockSwipes(lock);
  }

  slideNext(speed?: number, runCallbacks?: boolean) {
    return this.slider.slideNext(speed, runCallbacks);
  }

  slidePrev(speed?: number, runCallbacks?: boolean) {
    return this.slider.slidePrev(speed, runCallbacks);
  }

  slideTo(index: number, speed?: number, runCallbacks?: boolean) {
    return this.slider.slideTo(index, speed, runCallbacks);
  }

  startAutoplay() {
    return this.slider.startAutoplay();
  }

  stopAutoplay() {
    return this.slider.stopAutoplay();
  }

  update() {
    return this.slider.update();
  }

  updateAutoHeight() {
    return this.slider.updateAutoHeight();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XSliderActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XSliderAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async handleConfigurationChanged() {
    //
    const anim = await this.getValueAsync(this.animation);
    const options = await this.getValueAsync(this.options);

    //
    this.SLIDER_CONFIG = this.getConfiguration(anim, options);
  }

  private getConfiguration(
    animation?: XSliderAnimationIdentifier,
    config?: XSliderConfig
  ) {
    //
    let result: XSliderConfig;

    //
    if (isNullOrEmptyString(animation)) {
      animation = XSliderAnimation.Coverflow;
    }

    //
    if (!config) {
      config = defaultXSliderConfig;
    }

    //
    result = {
      ...defaultXSliderConfig,
      ...config,
      speed:
        config.speed && config.speed <= 15000 && config.speed >= 500
          ? config.speed
          : defaultXSliderConfig.speed,
      initialSlide:
        this.slides &&
        config.initialSlide <= this.slides.length &&
        config.initialSlide >= 0
          ? config.initialSlide
          : defaultXSliderConfig.initialSlide,
    };

    //
    switch (animation) {
      case XSliderAnimation.Coverflow:
        result = {
          ...result,
          ...Coverflow,
        };
        break;

      case XSliderAnimation.Cube:
        result = {
          ...result,
          ...Cube,
        };
        break;

      case XSliderAnimation.Fade:
        result = {
          ...result,
          ...Fade,
        };
        break;

      case XSliderAnimation.Flip:
        result = {
          ...result,
          ...Flip,
        };
        break;
    }

    //
    if (result.pagination) {
      result.pagination = defaultXSliderPaginationConfig;
    }

    //
    return result;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slider.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XSliderComponent } from './x-slider.component';
import { XSlideComponent } from './x-slide/x-slide.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XSliderComponent, XSlideComponent],
  imports: [XCardModule, CommonModule, XBaseComponentModule, XIconModule],
  exports: [
    XIconModule,
    XCardModule,
    XSlideComponent,
    XSliderComponent,
    XBaseComponentModule,
  ],
})
export class XSliderModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slider.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Renderer2,
  Component,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XOneOrManyType, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XSliderAnimation {
  Coverflow = 'coverflow',
  Cube = 'cube',
  Fade = 'fade',
  Flip = 'flip',
}

export type XSliderAnimationIdentifier = XSliderAnimation | string;

export interface XSlideAnimationProvider {
  [key: string]: any;
  on: {
    [key: string]: any;
    beforeInit?: () => void;
    setTranslate?: () => void;
    setTransition?: (duration?: number) => void;
  };
}

export interface XSliderPaginationConfig {
  el: string;
  type: string;
  clickable: boolean;
}

export type XSliderPagination = XSliderPaginationConfig | boolean;

export const defaultXSliderPaginationConfig: XSliderPaginationConfig = {
  el: '.swiper-pagination',
  type: 'bullets',
  clickable: true,
};

export interface XSliderConfig {
  lazy: boolean;
  zoom: boolean;
  autoplay: boolean;
  initialSlide: number;
  grabCursor: boolean;
  autoHeight: boolean;
  slidesPerView: number;
  loadPrevNext: boolean;
  preventClicks: boolean;
  pagination: XSliderPagination;
  speed: number;
  on?: any;
}

export const defaultXSliderConfig: XSliderConfig = {
  lazy: true,
  zoom: true,
  autoplay: true,
  initialSlide: 0,
  grabCursor: true,
  autoHeight: true,
  slidesPerView: 1,
  loadPrevNext: true,
  preventClicks: false,
  speed: 400,
  pagination: true,
};

export enum XSliderAction {
  Refresh = 'refresh',
}

export type XSliderActionIdentifier = XSliderAction | string;

export interface XSliderActionModel
  extends XBaseActionModel<XSliderActionIdentifier> {}

//
//#region Inside Card Container ...
export interface IXSliderContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XSliderActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  options: XStandardType<XSliderConfig>;
  animation: XStandardType<XSliderAnimationIdentifier>;

  //
  onSlideChange: EventEmitter<number>;
}

@Injectable({
  providedIn: 'root',
})
export class XSliderContainer
  extends XComponentCardContainer
  implements Readonly<IXSliderContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XSliderActionModel> =
    new Subject<XSliderActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly options: XStandardType<XSliderConfig> = defaultXSliderConfig;
  readonly animation: XStandardType<XSliderAnimationIdentifier> =
    XSliderAnimation.Coverflow;

  //
  readonly onSlideChange = new EventEmitter<number>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XSliderContainerComponent
  extends XComponentCardContainerComponent
  implements IXSliderContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XSliderActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  options: XStandardType<XSliderConfig> = this.propertyProvider.options;

  @Input()
  animation: XStandardType<XSliderAnimationIdentifier> =
    this.propertyProvider.animation;

  //
  @Output()
  onSlideChange: EventEmitter<number> = this.propertyProvider.onSlideChange;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XSliderContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXSliderContainer
  ): Observable<IXSliderContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            options: this.getValue(
              container ? container.options : this.options
            ),
            animation: this.getValue(
              container ? container.animation : this.animation
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            onSlideChange: container
              ? container.onSlideChange
              : this.onSlideChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slide\x-slide.component.html

<ng-template #content>
  <ion-slide>
    <div class="full-width">
      <ng-content></ng-content>
    </div>
  </ion-slide>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slide\x-slide.component.scss

.full-width {
  width: 100%;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slide\x-slide.component.ts

import {
  Component,
  ViewChild,
  TemplateRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XSlideAction,
  XSlideActionModel,
  XSlideContainerComponent,
} from './x-slide.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-slide',
  templateUrl: './x-slide.component.html',
  styleUrls: ['./x-slide.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSlideComponent extends XSlideContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  @ViewChild('content', { static: true })
  template!: TemplateRef<any>;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XSlideComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XSlideActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XSlideAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slider\x-slide\x-slide.typings.ts

import {
  Input,
  NgZone,
  Inject,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../../classes/i-x-base.container';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XSlideAction {
  Refresh = 'refresh',
}

export type XSlideActionIdentifier = XSlideAction | string;

export interface XSlideActionModel
  extends XBaseActionModel<XSlideActionIdentifier> {}

//
//#region Container ...
export interface IXSlideContainer extends IXBaseContainer {
  actionProvider: Subject<XSlideActionModel>;
}

@Injectable({
  providedIn: 'root'
})
export class XSlideContainer
  extends XBaseContainer
  implements Readonly<IXSlideContainer>
{
  //
  //#region Props ...
  actionProvider: Subject<XSlideActionModel> = new Subject<XSlideActionModel>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XSlideContainerComponent
  extends XBaseContainerComponent
  implements IXSlideContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XSlideActionModel> =
    this.propertyProvider.actionProvider;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XSlideContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXSlideContainer
  ): Observable<IXSlideContainer> {
    return forkJoin({
      //
    }).pipe(
      map((data) => {
        return {
          // //
          // ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slotter.component.html

<div
  class="x-slotter x-wrapper"
  [fxLayout]="getLayout() | async"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
>
  <!-- Chack Has Slots -->
  <ng-container *ngIf="hasSlots()">
    <div
      fxFlex
      class="x-start-top-slot"
      *ngIf="getHasStartOrTopSlot() | async"
      [ngClass]="getValue(startTopSlotCssClass) | async"
    >
      <!-- Start / Top -->
      <ng-container *ngFor="let slot of startOrTopSlots">
        <div
          class="x-start-top-slot-inner-container"
          [ngClass]="getValue(slot.cssClass) | async"
          *ngIf="notNullOrUndefinedValue(slot.content) | async"
        >
          <ng-container *ngTemplateOutlet="slot.content"></ng-container>
        </div>
      </ng-container>
    </div>

    <!-- Center -->
    <div
      fxFlex
      class="x-center-slot"
      *ngIf="getHasCenterSlot() | async"
      [ngClass]="getValue(centerSlotCssClass) | async"
    >
      <ng-container *ngFor="let slot of cneterSlots">
        <div
          class="x-center-slot-inner-container"
          [ngClass]="getValue(slot.cssClass) | async"
          *ngIf="notNullOrUndefinedValue(slot.content) | async"
        >
          <ng-container *ngTemplateOutlet="slot.content"></ng-container>
        </div>
      </ng-container>
    </div>

    <!-- end / Bottom -->
    <div
      fxFlex
      class="x-end-bottom-slot"
      *ngIf="getHasEndOrBottomSlot() | async"
      [ngClass]="getValue(endBottomSlotCssClass) | async"
    >
      <ng-container *ngFor="let slot of endOrBottomSlots">
        <div
          class="x-end-bottom-slot-inner-container"
          [ngClass]="getValue(slot.cssClass) | async"
          *ngIf="notNullOrUndefinedValue(slot.content) | async"
        >
          <ng-container *ngTemplateOutlet="slot.content"></ng-container>
        </div>
      </ng-container>
    </div>
  </ng-container>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slotter.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slotter.component.ts

import {
  Component,
  QueryList,
  ContentChildren,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XSlotName,
  XSlotLayout,
  XSlotterAction,
  XSlotterActionModel,
  XSlotterContainerComponent,
} from './x-slotter.typings';
import { map } from 'rxjs/operators';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';
import { XSlotComponent } from './x-slot/x-slot.component';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-slotter',
  templateUrl: './x-slotter.component.html',
  styleUrls: ['./x-slotter.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSlotterComponent extends XSlotterContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  //#region Slots Props ...
  //
  private SLOTS: QueryList<XSlotComponent>;

  //
  @ContentChildren(XSlotComponent)
  set slots(s: QueryList<XSlotComponent>) {
    //
    this.SLOTS = s;
    this.prepareSlots();
  }

  //
  get slots() {
    return this.SLOTS;
  }

  //
  startOrTopSlots: XSlotComponent[] = [];
  cneterSlots: XSlotComponent[] = [];
  endOrBottomSlots: XSlotComponent[] = [];
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XSlotterComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Ui Providers ...
  hasSlots() {
    return this.slots && this.slots.length > 0;
  }

  getHasCenterSlot() {
    return this.applyTakeUntilWrapper(this.getValue(this.hasCenterSlot));
  }

  getHasStartOrTopSlot() {
    return this.applyTakeUntilWrapper(this.getValue(this.hasStartOrTopSlot));
  }

  getHasEndOrBottomSlot() {
    return this.applyTakeUntilWrapper(this.getValue(this.hasEndOrBottomSlot));
  }

  isLayoutHorizontal() {
    return this.getValue(this.layout).pipe(
      map((layout) => layout === XSlotLayout.HORIZONTAL)
    );
  }

  isLayoutVertical() {
    return this.getValue(this.layout).pipe(
      map((layout) => layout === XSlotLayout.VERTICAL)
    );
  }

  getLayout() {
    return this.getValue(this.layout).pipe(
      map((layout) => {
        return layout === XSlotLayout.VERTICAL ? 'column' : 'row';
      })
    );
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XSlotterActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XSlotterAction.Refresh:
            await this.prepareSlots();
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async prepareSlots() {
    //
    this.startOrTopSlots = [];
    this.cneterSlots = [];
    this.endOrBottomSlots = [];

    //
    this.detectChanges();

    //
    if (!this.hasSlots()) {
      return;
    }

    //
    this.slots.forEach(async (slot) => {
      //
      const name = await this.getValueAsync(slot.name);
      switch (name) {
        case XSlotName.START:
          this.startOrTopSlots.push(slot);
          break;

        case XSlotName.TOP:
          this.startOrTopSlots.push(slot);
          break;

        case XSlotName.CENTER:
          this.cneterSlots.push(slot);
          break;

        case XSlotName.END:
          this.endOrBottomSlots.push(slot);
          break;

        case XSlotName.BOTTOM:
          this.endOrBottomSlots.push(slot);
          break;
      }

      //
      this.detectChanges();
    });

    //
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slotter.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XSlotterComponent } from './x-slotter.component';
import { XSlotComponent } from './x-slot/x-slot.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XSlotterComponent, XSlotComponent],
  imports: [CommonModule, XBaseComponentModule],
  exports: [XBaseComponentModule, XSlotterComponent, XSlotComponent]
})
export class XSlotterModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slotter.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XOneOrManyType, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XSlotName {
  START = 'start',
  END = 'end',
  CENTER = 'center',
  TOP = 'top',
  BOTTOM = 'bottom',
}

export type XSlotNameIdentifier = XSlotName | string;

export enum XSlotLayout {
  HORIZONTAL = 'horizontal',
  VERTICAL = 'vertical',
}

export type XSlotLayoutIdentifier = XSlotLayout | string;

export enum XSlotterAction {
  Refresh = 'refresh',
}

export type XSlotterActionIdentifier = XSlotterAction | string;

export interface XSlotterActionModel
  extends XBaseActionModel<XSlotterActionIdentifier> {}

//
//#region Container ...
export interface IXSlotterContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XSlotterActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  layout: XStandardType<XSlotLayoutIdentifier>;

  //
  hasStartOrTopSlot: XStandardType<boolean>;
  startTopSlotCssClass: XStandardType<XOneOrManyType<string>>;

  //
  hasCenterSlot: XStandardType<boolean>;
  centerSlotCssClass: XStandardType<XOneOrManyType<string>>;

  //
  hasEndOrBottomSlot: XStandardType<boolean>;
  endBottomSlotCssClass: XStandardType<XOneOrManyType<string>>;
}

@Injectable({
  providedIn: 'root'
})
export class XSlotterContainer
  extends XBaseContainer
  implements Readonly<IXSlotterContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XSlotterActionModel>;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly layout: XStandardType<XSlotLayoutIdentifier> =
    XSlotLayout.HORIZONTAL;

  //
  readonly hasStartOrTopSlot: XStandardType<boolean> = true;
  readonly startTopSlotCssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly hasCenterSlot: XStandardType<boolean> = true;
  readonly centerSlotCssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly hasEndOrBottomSlot: XStandardType<boolean> = true;
  readonly endBottomSlotCssClass: XStandardType<XOneOrManyType<string>>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XSlotterContainerComponent
  extends XBaseContainerComponent
  implements IXSlotterContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XSlotterActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  layout: XStandardType<XSlotLayoutIdentifier> = this.propertyProvider.layout;

  //
  @Input()
  hasStartOrTopSlot: XStandardType<boolean> =
    this.propertyProvider.hasStartOrTopSlot;

  @Input()
  startTopSlotCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.startTopSlotCssClass;

  //
  @Input()
  hasCenterSlot: XStandardType<boolean> = this.propertyProvider.hasCenterSlot;

  @Input()
  centerSlotCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.centerSlotCssClass;

  //
  @Input()
  hasEndOrBottomSlot: XStandardType<boolean> =
    this.propertyProvider.hasEndOrBottomSlot;

  @Input()
  endBottomSlotCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.endBottomSlotCssClass;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XSlotterContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXSlotterContainer
  ): Observable<IXSlotterContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      layout: this.getValue(container ? container.layout : this.layout),
      hasStartOrTopSlot: this.getValue(
        container ? container.hasStartOrTopSlot : this.hasStartOrTopSlot
      ),
      startTopSlotCssClass: this.getValue(
        container ? container.startTopSlotCssClass : this.startTopSlotCssClass
      ),
      hasCenterSlot: this.getValue(
        container ? container.hasCenterSlot : this.hasCenterSlot
      ),
      centerSlotCssClass: this.getValue(
        container ? container.centerSlotCssClass : this.centerSlotCssClass
      ),
      hasEndOrBottomSlot: this.getValue(
        container ? container.hasEndOrBottomSlot : this.hasEndOrBottomSlot
      ),
      endBottomSlotCssClass: this.getValue(
        container ? container.endBottomSlotCssClass : this.endBottomSlotCssClass
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slot\x-slot.component.html

<ng-template #content>
  <ng-content></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slot\x-slot.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slot\x-slot.component.ts

import {
  XSlotAction,
  XSlotActionModel,
  XSlotContainerComponent,
} from './x-slot.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';
import { Component, ViewChild, TemplateRef } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-slot',
  templateUrl: './x-slot.component.html',
  styleUrls: ['./x-slot.component.scss'],
  // changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSlotComponent extends XSlotContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  @ViewChild('content', { static: false })
  content: TemplateRef<any>;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const isActionProviderChanged = changeKeys.includes(
      nameof<XSlotComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XSlotActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XSlotAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-slotter\x-slot\x-slot.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XSlotNameIdentifier } from '../x-slotter.typings';
import { XOneOrManyType, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XSlotAction {
  Refresh = 'refresh',
}

export type XSlotActionIdentifier = XSlotAction | string;

export interface XSlotActionModel
  extends XBaseActionModel<XSlotActionIdentifier> {}

//
//#region Container ...
export interface IXSlotContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XSlotActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  name: XStandardType<XSlotNameIdentifier>;
}

@Injectable({
  providedIn: 'root'
})
export class XSlotContainer
  extends XBaseContainer
  implements Readonly<IXSlotContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider = new Subject<XSlotActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly name: XStandardType<XSlotNameIdentifier>;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XSlotContainerComponent
  extends XBaseContainerComponent
  implements IXSlotContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XSlotActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  name: XStandardType<XSlotNameIdentifier> = this.propertyProvider.name;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XSlotContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXSlotContainer
  ): Observable<IXSlotContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      name: this.getValue(container ? container.name : this.name),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-spinner\x-spinner.component.html

<ion-spinner
  [isForeground]="true"
  [name]="getValue(name) | async"
  [xColor]="getValue(color) | async"
  [paused]="getValue(paused) | async"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
  [duration]="getValue(duration) | async"
  class="x-spinner x-wrapper ion-no-margin ion-no-padding ion-no-border"
></ion-spinner>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-spinner\x-spinner.component.scss

:host {
  display: inline-block;
  position: relative;
  width: 28px;
  height: 28px;
}

ion-spinner {
  display: inline-block;
  position: relative;
  width: 28px;
  height: 28px;
}

.x-spinner {
  direction: ltr !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-spinner\x-spinner.component.ts

import {
  XSpinnerAction,
  XSpinnerActionModel,
  XSpinnerContainerComponent,
} from './x-spinner.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-spinner',
  templateUrl: './x-spinner.component.html',
  styleUrls: ['./x-spinner.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XSpinnerComponent extends XSpinnerContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XSpinnerComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XSpinnerActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XSpinnerAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-spinner\x-spinner.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XSpinnerComponent } from './x-spinner.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XSpinnerComponent],
  imports: [CommonModule, XBaseComponentModule],
  exports: [XBaseComponentModule, XSpinnerComponent]
})
export class XSpinnerModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-spinner\x-spinner.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XSpinner,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XSpinnerIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XSpinnerAction {
  Refresh = 'refresh',
}

export type XSpinnerActionIdentifier = XSpinnerAction | string;

export interface XSpinnerActionModel
  extends XBaseActionModel<XSpinnerActionIdentifier> {}

//
//#region Container ...
export interface IXSpinnerContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XSpinnerActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  name: XStandardType<XSpinnerIdentifier>;
  duration: XStandardType<number>;
  color: XStandardType<XColorIdentifier>;
  paused: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root'
})
export class XSpinnerContainer
  extends XBaseContainer
  implements Readonly<IXSpinnerContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XSpinnerActionModel>;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly name: XStandardType<XSpinnerIdentifier> = XSpinner.Circular;
  readonly duration: XStandardType<number> = undefined;
  readonly color: XStandardType<XColorIdentifier> =
    XColorWithBrightness.SecondaryShade;
  readonly paused: XStandardType<boolean> = false;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XSpinnerContainerComponent
  extends XBaseContainerComponent
  implements IXSpinnerContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XSpinnerActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  name: XStandardType<XSpinnerIdentifier> = this.propertyProvider.name;

  @Input()
  duration: XStandardType<number> = this.propertyProvider.duration;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  paused: XStandardType<boolean> = this.propertyProvider.paused;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XSpinnerContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXSpinnerContainer
  ): Observable<IXSpinnerContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),

      //
      name: this.getValue(container ? container.name : this.name),
      duration: this.getValue(container ? container.duration : this.duration),
      color: this.getValue(container ? container.color : this.color),
      paused: this.getValue(container ? container.paused : this.paused),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-stepper.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else wrapperRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="wrapperRef"></ng-container>

    <!-- Actions -->
    <div class="x-actions" actions>
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Components itSelf Template -->
<ng-template #wrapperRef>
  <!-- Stepper Element -->
  <div
    class="x-stepper x-wrapper"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [class.disableHeader]="!(getValue(selectableHeaders) | async)"
  >
    <!-- Mat Stepper -->
    <mat-stepper
      #stepper
      [orientation]="getOrientation() | async"
      [selectedIndex]="getValue(selectedIndex) | async"
      [labelPosition]="getLabelPositionValue() | async"
      (selectionChange)="handleSelectionChanged($event)"
    >
      <!-- Icons Templates -->
      <ng-template matStepperIcon="number" let-index="index">
        {{ applyLocale(index + 1) }}
      </ng-template>
      <ng-template matStepperIcon="edit" let-index="index">
        {{ applyLocale(index + 1) }}
      </ng-template>

      <!-- Steps -->
      <!-- Check Has Steps -->
      <ng-container *ngIf="hasSteps()">
        <ng-container *ngFor="let step of steps">
          <!-- Step Implementation -->
          <mat-step [attr.stepperId]="step.id">
            <!-- Step Label -->
            <ng-template matStepLabel *ngIf="getValue(showLabel) | async">
              <ng-container *ngTemplateOutlet="step.label"></ng-container>
            </ng-template>

            <!-- Step Content Representation -->
            <ng-container *ngTemplateOutlet="step.content"></ng-container>

            <!-- Step Buttons -->
            <ng-container
              *ngTemplateOutlet="
                stepperActionsRef;
                context: { stepper: stepper, step: step }
              "
            ></ng-container>
          </mat-step>
        </ng-container>
      </ng-container>
    </mat-stepper>
  </div>
</ng-template>

<!-- Actions Template -->
<ng-template #stepperActionsRef let-stepper="stepper" let-step="step">
  <!-- Step Buttons -->
  <div class="x-step-actions">
    <x-slotter *ngIf="hasActions(step) | async">
      <!-- Next -->
      <x-slot
        [name]="getValue(step.nextPosition) | async"
        [cssClass]="getValue(step.nextCssClass) | async"
      >
        <ng-container *ngIf="hasStepAction(step, NPRButtonTypes.Next) | async">
          <ng-container
            *ngTemplateOutlet="
              buttonRef;
              context: {
                stepper: stepper,
                step: step,
                type: NPRButtonTypes.Next
              }
            "
          ></ng-container>
        </ng-container>
      </x-slot>

      <!-- Reset -->
      <x-slot
        [name]="getValue(step.resetPosition) | async"
        [cssClass]="getValue(step.resetCssClass) | async"
      >
        <ng-container *ngIf="hasStepAction(step, NPRButtonTypes.Reset) | async">
          <ng-container
            *ngTemplateOutlet="
              buttonRef;
              context: {
                stepper: stepper,
                step: step,
                type: NPRButtonTypes.Reset
              }
            "
          ></ng-container>
        </ng-container>
      </x-slot>

      <!-- Prev -->
      <x-slot
        [name]="getValue(step.prevPosition) | async"
        [cssClass]="getValue(step.prevCssClass) | async"
      >
        <ng-container *ngIf="hasStepAction(step, NPRButtonTypes.Prev) | async">
          <ng-container
            *ngTemplateOutlet="
              buttonRef;
              context: {
                stepper: stepper,
                step: step,
                type: NPRButtonTypes.Prev
              }
            "
          ></ng-container>
        </ng-container>
      </x-slot>
    </x-slotter>

    <!-- Specified Actions -->
    <div
      class="x-step-custom-actions-wrapper"
      *ngIf="getValue(step.hasCustomActions) | async"
    >
      <ng-container *ngTemplateOutlet="step.actions"></ng-container>
    </div>
  </div>
</ng-template>

<!-- Button Template -->
<ng-template #buttonRef let-stepper="stepper" let-step="step" let-type="type">
  <x-button
    [icon]="getButtonIcon(step, type) | async"
    [type]="getButtonType(step, type) | async"
    [title]="getButtonTitle(step, type) | async"
    [color]="getButtonColor(step, type) | async"
    (clicked)="handleNPRStep(stepper, step, type)"
    [cssClass]="getButtonCssClass(step, type) | async"
    [uiDisabled]="getButtonDisabled(step, type) | async"
    [iconColor]="getButtonIconColor(step, type) | async"
    [class.disabled]="getButtonDisabled(step, type) | async"
    [iconPosition]="getButtonIconPosition(step, type) | async"
    [foregroundColor]="getButtonForegroundColor(step, type) | async"
  ></x-button>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-stepper.component.scss

:host {
  //
  // TODO: add and handle cssVariable and color applier ...
  --icon-background-color: var(--x-color-tertiary);
  --icon-foreground-color: var(--x-color-tertiary-contrast);
  --title-foreground-color: var(--x-color-secondary);
  --header-background-color: var(--x-color-light);
  --header-foreground-color: var(--x-color-light-contrast);
}

:host(::ng-deep.disableHeader) {
  .mat-step-header {
    pointer-events: none;
  }
}

:host ::ng-deep .mat-step-header,
:host ::ng-deep .mat-stepper-vertical,
:host ::ng-deep .mat-stepper-horizontal,
:host ::ng-deep .mat-horizontal-stepper-header-container {
  background-color: transparent !important;
}

:host ::ng-deep .mat-step-icon {
  color: var(--icon-foreground-color) !important;
  background-color: var(--icon-background-color) !important;
}

:host ::ng-deep .mat-step-label,
:host ::ng-deep .mat-step-header {
  color: var(--header-foreground-color) !important;
  background-color: var(--header-background-color) !important;
}

:host ::ng-deep .mat-step-label {
  color: var(--title-foreground-color) !important;
}

:host ::ng-deep .mat-vertical-stepper-header {
  padding: {
    top: 4px;
    bottom: 4px;
  }
}

//
$transition: opacity;
$duration: 500ms;
$delay: 0;
button {
  //
  transition: $transition;
  -webkit-transition: $transition;

  //
  transition-delay: $delay;
  -webkit-transition-delay: $delay;

  //
  transition-duration: $duration;
  -webkit-transition-duration: $duration;
}

.x-step-actions {
  button {
    margin: {
      left: 4px;
      right: 4px;
    }
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-stepper.component.ts

import {
  ViewChild,
  Component,
  QueryList,
  ContentChildren,
} from '@angular/core';
import {
  nameof,
  mergeList,
  parseXColor,
  XStandardType,
  isNullOrEmptyString,
  XColorWithBrightness,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XStepperType,
  XStepperAction,
  XStepperCssVars,
  XStepperActionModel,
  XStepperLabelPosition,
  XStepperContainerComponent,
} from './x-stepper.typings';
import {
  StepperOrientation,
  StepperSelectionEvent,
  STEPPER_GLOBAL_OPTIONS,
} from '@angular/cdk/stepper';
import { concatMap, map } from 'rxjs/operators';
import { MatStepper } from '@angular/material/stepper';
import { Observable, Subject, Subscription } from 'rxjs';
import { XStepComponent } from './x-step/x-step.component';
import { XButtonType } from '../x-button/x-button.typings';
import { XSlotName } from '../x-slotter/x-slotter.typings';
import { XNPRButtonType } from '../classes/i-x-next-prev-reset.container';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-stepper',
  templateUrl: './x-stepper.component.html',
  styleUrls: ['./x-stepper.component.scss'],
  providers: [
    {
      provide: STEPPER_GLOBAL_OPTIONS,
      useValue: { showError: true },
    },
  ],
})
export class XStepperComponent extends XStepperContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly NPRButtonTypes = Object.assign({}, XNPRButtonType);

  //
  @ViewChild('stepper', { static: false })
  private stepper: MatStepper;

  //
  selected: string;
  // applyThemeColors = true;

  //
  //#region Steps Props ...
  //
  private STEPS: QueryList<XStepComponent>;

  //
  @ContentChildren(XStepComponent)
  set steps(s: QueryList<XStepComponent>) {
    //
    if (!s) {
      return;
    }

    //
    this.STEPS = s;
    this.detectChanges();
  }

  //
  get steps() {
    return this.STEPS;
  }
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XStepperComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Colors Changed ...
    //
    const isIconColorChanged = changeKeys.includes(
      nameof<XStepperComponent>('iconColor')
    );
    if (isIconColorChanged) {
      await this.applyIconColor();
    }

    //
    const isTitleColorChanged = changeKeys.includes(
      nameof<XStepperComponent>('titleColor')
    );
    if (isTitleColorChanged) {
      await this.applyTitleColor();
    }

    //
    const isHeaderColorChanged = changeKeys.includes(
      nameof<XStepperComponent>('headerColor')
    );
    if (isHeaderColorChanged) {
      await this.applyeaderColor();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerInstanceHandlers() {
    super.registerInstanceHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Task Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  //
  //#region TypeDetectors ...
  getOrientation() {
    return this.getValue(this.type).pipe(
      map((result) => result as StepperOrientation)
    );
  }

  isTypeHorizontal() {
    return this.getValue(this.type).pipe(
      map((type) => type === XStepperType.HORIZONTAL)
    );
  }

  isTypeVertical() {
    return this.getValue(this.type).pipe(
      map((type) => type === XStepperType.VERTICAL)
    );
  }
  //#endregion

  //
  getLabelPositionValue(): Observable<'bottom' | 'end'> {
    return this.getValue(this.labelPosition).pipe(
      map((position) =>
        position === XStepperLabelPosition.END ? 'end' : 'bottom'
      )
    );
  }

  //
  hasSteps() {
    return this.steps && this.steps.length > 0 ? true : false;
  }

  hasActions(step: XStepComponent) {
    return this.getValue(step.hasNext).pipe(
      concatMap((hasNext) =>
        this.getValue(step.hasPrev).pipe(
          map((hasPrev) => {
            return {
              hasNext,
              hasPrev,
            };
          })
        )
      ),
      concatMap((hasNextPrev) =>
        this.getValue(step.hasReset).pipe(
          map((hasReset) => {
            return {
              ...hasNextPrev,
              hasReset,
            };
          })
        )
      ),
      map((info) => info.hasNext || info.hasPrev || info.hasReset)
    );
  }

  //
  //#region Button Providers ...
  hasStepAction(step: XStepComponent, type: XNPRButtonType) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        return this.getOptionalValue(this.hasNext).pipe(
          concatMap((cHas) =>
            this.getOptionalValue(step.hasNext).pipe(
              map((sHas) => cHas || sHas)
            )
          )
        );

      //
      case this.NPRButtonTypes.Prev:
        return this.getOptionalValue(this.hasPrev).pipe(
          concatMap((cHas) =>
            this.getOptionalValue(step.hasPrev).pipe(
              map((sHas) => cHas || sHas)
            )
          )
        );

      //
      case this.NPRButtonTypes.Reset:
        return this.getOptionalValue(this.hasReset).pipe(
          concatMap((cHas) =>
            this.getOptionalValue(step.hasReset).pipe(
              map((sHas) => cHas || sHas)
            )
          )
        );
    }
  }

  getButtonIcon(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.icon
          ? this.getValue(btns.s.icon)
          : this.getValue(btns.c.icon)
      )
    );
  }

  getButtonType(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.type
          ? this.getValue(btns.s.type)
          : this.getValue(btns.c.type)
      ),
      map((btnType) =>
        !isNullOrEmptyString(btnType) ? btnType : XButtonType.Raised
      )
    );
  }

  getButtonTitle(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.title
          ? this.getResourceValue(btns.s.title)
          : this.getResourceValue(btns.c.title)
      )
    );
  }

  getButtonColor(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.color
          ? this.getValue(btns.s.color)
          : this.getValue(btns.c.color)
      ),
      map((btnColor) =>
        !isNullOrEmptyString(btnColor) ? btnColor : XColorWithBrightness.Primary
      )
    );
  }

  getButtonIconColor(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.iconColor
          ? this.getValue(btns.s.iconColor)
          : this.getValue(btns.c.iconColor)
      ),
      map((btnColor) =>
        !isNullOrEmptyString(btnColor) ? btnColor : XColorWithBrightness.Primary
      )
    );
  }

  getButtonDisabled(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtonDisabled(step, type);
  }

  getButtonCssClass(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        this.getArrayValue(btns.s.cssClass).pipe(
          concatMap((sCss) =>
            this.getArrayValue(btns.c.cssClass).pipe(
              map((cCss) => mergeList(sCss, cCss))
            )
          )
        )
      )
    );
  }

  getButtonIconPosition(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.iconPosition
          ? this.getValue(btns.s.iconPosition)
          : this.getValue(btns.c.iconPosition)
      )
    );
  }

  getButtonForegroundColor(step: XStepComponent, type: XNPRButtonType) {
    return this.getActionButtons(step, type).pipe(
      concatMap((btns) =>
        btns.s && btns.s.foregroundColor
          ? this.getOptionalValue(btns.s.foregroundColor)
          : this.getOptionalValue(btns.c.foregroundColor)
      )
    );
  }
  //#endregion
  //#endregion

  //
  //#region UI Handlers ...
  async handleSelectionChanged(event: StepperSelectionEvent) {
    //
    if (this.uiDisabled || this.loading || !this.hasSteps()) {
      return;
    }

    const index = event.selectedIndex;
    const stepId = await this.getValueAsync(this.steps.toArray()[index].id);
    this.selectedChange.emit(stepId);
  }

  async handleNPRStep(
    stepper: MatStepper,
    step: XStepComponent,
    type: XNPRButtonType
  ) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        await this.handleNextStep(stepper, step);
        break;

      //
      case this.NPRButtonTypes.Prev:
        await this.handlePrevStep(stepper, step);
        break;

      //
      case this.NPRButtonTypes.Reset:
        await this.handleReset(stepper, step);
        break;
    }
  }

  async handleNextStep(
    stepper: MatStepper,
    step: XStepComponent,
    force?: boolean
  ) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !stepper ||
      !step ||
      step.loading ||
      step.uiDisabled ||
      !this.hasSteps()
    ) {
      return;
    }

    //
    let nextId: string;
    const stepId = await this.getValueAsync(step.id);
    const stepIndex = await this.getStepIndex(stepId);
    if (stepIndex === -1) {
      return;
    }
    if (stepIndex < this.steps.length - 1) {
      nextId = await this.getValueAsync(this.steps.toArray()[stepIndex + 1].id);
    }

    //
    const button = await this.getValueAsync(step.nextButton);
    if (button.clicked) {
      button.clicked.emit();
    }

    if (isNullOrEmptyString(nextId)) {
      return;
    }

    //
    if (!force) {
      //
      const isUiBusy = this.uiDisabled || this.loading;
      const canNextStep = await this.getValueAsync(this.canNext);
      const disableNextStep = await this.getValueAsync(step.disableNext);

      //
      if (isUiBusy || disableNextStep || !canNextStep) {
        return;
      }

      //
      const isConfirmedType = this.nextConfirmer
        ? this.nextConfirmer()
        : this.getValue(true);
      const isConfirmed = await this.getValueAsync(isConfirmedType);

      //
      if (!isConfirmed) {
        return;
      }

      //
      const isStepNextConfitmedType = step.nextConfirmer
        ? step.nextConfirmer()
        : this.getValue(true);
      const isStepNextConfirmed = await this.getValueAsync(
        isStepNextConfitmedType
      );
      if (!isStepNextConfirmed) {
        return;
      }
    }

    //
    this.selected = nextId;
    this.selectedChange.emit(nextId);
    stepper.next();
    this.onNext.emit(stepId);
    this.detectChanges();
  }

  async handlePrevStep(
    stepper: MatStepper,
    step: XStepComponent,
    force?: boolean
  ) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !stepper ||
      !step ||
      step.loading ||
      step.uiDisabled ||
      !this.hasSteps()
    ) {
      return;
    }

    //
    let prevId: string;
    const stepId = await this.getValueAsync(step.id);
    const stepIndex = await this.getStepIndex(stepId);
    if (stepIndex === -1) {
      return;
    }
    prevId = await this.getValueAsync(this.steps.toArray()[stepIndex].id);

    //
    const button = await this.getValueAsync(step.prevButton);
    if (button.clicked) {
      button.clicked.emit();
    }

    if (isNullOrEmptyString(prevId)) {
      return;
    }

    //
    if (!force) {
      const isUiBusy = this.uiDisabled || this.loading;
      const canPrevStep = await this.getValueAsync(this.canPrev);
      const disablePrevStep = await this.getValueAsync(step.disablePrev);
      if (isUiBusy || disablePrevStep || !canPrevStep) {
        return;
      }

      //
      const isConfirmedType = this.prevConfirmer
        ? this.prevConfirmer()
        : this.getValue(true);
      const isConfirmed = await this.getValueAsync(isConfirmedType);

      //
      if (!isConfirmed) {
        return;
      }

      //
      const isStepPrevConfitmedType = step.prevConfirmer
        ? step.prevConfirmer()
        : this.getValue(true);
      const isStepPrevConfirmed = await this.getValueAsync(
        isStepPrevConfitmedType
      );
      if (!isStepPrevConfirmed) {
        return;
      }
    }

    //
    this.selected = prevId;
    this.selectedChange.emit(prevId);
    stepper.previous();
    this.onPrev.emit(stepId);
    this.detectChanges();
  }

  async handleReset(
    stepper: MatStepper,
    step: XStepComponent,
    force?: boolean
  ) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !stepper ||
      !step ||
      step.loading ||
      step.uiDisabled ||
      !this.hasSteps()
    ) {
      return;
    }

    //
    const stepId = await this.getValueAsync(step.id);
    const firstId = await this.getValueAsync(this.steps.toArray()[0].id);

    //
    const button = await this.getValueAsync(step.resetButton);
    if (button.clicked) {
      button.clicked.emit();
    }

    //
    if (!force) {
      const isUiBusy = this.uiDisabled || this.loading;
      const canResetStep = await this.getValueAsync(this.canReset);
      const disableResetStep = await this.getValueAsync(step.disableReset);
      if (isUiBusy || disableResetStep || !canResetStep) {
        return;
      }

      //
      const isConfirmedType = this.resetConfirmer
        ? this.resetConfirmer()
        : this.getValue(true);
      const isConfirmed = await this.getValueAsync(isConfirmedType);

      //
      if (!isConfirmed) {
        return;
      }

      //
      const isStepResetConfitmedType = step.resetConfirmer
        ? step.resetConfirmer()
        : this.getValue(true);
      const isStepResetConfirmed = await this.getValueAsync(
        isStepResetConfitmedType
      );
      if (!isStepResetConfirmed) {
        return;
      }
    }

    //
    this.selected = firstId;
    this.selectedChange.emit(firstId);
    stepper.reset();
    this.onReset.emit(stepId);
    this.detectChanges();
  }

  async handleToStep(stepper: MatStepper, step: XStepComponent) {
    //
    if (
      this.uiDisabled ||
      this.loading ||
      !step ||
      step.loading ||
      step.uiDisabled ||
      !this.hasSteps() ||
      this.selected === (await this.getValueAsync(step.id))
    ) {
      return;
    }

    //
    const stepId = await this.getValueAsync(step.id);
    const stepIndex = await this.getStepIndex(stepId);

    //
    this.selected = stepId;
    this.selectedChange.emit(stepId);
    stepper.selectedIndex = stepIndex;
    this.detectChanges();
  }
  //#endregion

  //
  //#region Actions ...
  async next(force?: boolean) {
    //
    if (!this.selected || !this.stepper || !this.hasSteps()) {
      return;
    }

    //
    const stepId = await this.getValueAsync(this.selected);
    if (!stepId) {
      return;
    }

    //
    const step = await this.getStep(stepId);
    if (!step) {
      return;
    }

    //
    await this.handleNextStep(this.stepper, step, force);
  }

  async prev(force?: boolean) {
    //
    if (!this.selected || !this.stepper || !this.hasSteps()) {
      return;
    }

    //
    const stepId = await this.getValueAsync(this.selected);
    if (!stepId) {
      return;
    }

    //
    const step = await this.getStep(stepId);
    if (!step) {
      return;
    }

    //
    await this.handlePrevStep(this.stepper, step, force);
  }

  async reset(force?: boolean) {
    //
    if (!this.selected || !this.stepper || !this.hasSteps()) {
      return;
    }

    //
    const stepId = await this.getValueAsync(this.selected);
    if (!stepId) {
      return;
    }

    //
    const step = await this.getStep(stepId);
    if (!step) {
      return;
    }

    //
    await this.handleReset(this.stepper, step, force);
  }

  async to(id: XStandardType<string>, force?: boolean) {
    //
    if (!id || !this.hasSteps() || !this.stepper) {
      return;
    }

    //
    const stepId = await this.getValueAsync(id);
    if (!stepId) {
      return;
    }

    //
    const stepIndex = await this.getStepIndex(stepId);
    if (stepIndex === -1) {
      return;
    }

    //
    const step = await this.getStep(stepId);
    if (!step) {
      return;
    }

    //
    const selectedIndex = await this.getStepIndex(this.selected);
    if (selectedIndex === -1 || selectedIndex === stepIndex) {
      return;
    }

    //
    if (stepIndex > selectedIndex) {
      //
      // next confirm ...
      //
      if (!force) {
        //
        const isUiBusy = this.uiDisabled || this.loading;
        const canNextStep = await this.getValueAsync(this.canNext);
        const disableNextStep = await this.getValueAsync(step.disableNext);

        //
        if (isUiBusy || disableNextStep || !canNextStep) {
          return;
        }

        //
        const isStepNextConfitmedType = step.nextConfirmer
          ? step.nextConfirmer()
          : this.getValue(true);
        const isStepNextConfirmed = await this.getValueAsync(
          isStepNextConfitmedType
        );
        if (!isStepNextConfirmed) {
          return;
        }
      }

      //
      this.selected = stepId;
      this.selectedChange.emit(stepId);
    } else if (stepIndex < selectedIndex) {
      //
      // prev Confirm ...
      //
      if (!force) {
        const isUiBusy = this.uiDisabled || this.loading;
        const canPrevStep = await this.getValueAsync(this.canPrev);
        const disablePrevStep = await this.getValueAsync(step.disablePrev);
        if (isUiBusy || disablePrevStep || !canPrevStep) {
          return;
        }

        //
        const isStepPrevConfitmedType = step.prevConfirmer
          ? step.prevConfirmer()
          : this.getValue(true);
        const isStepPrevConfirmed = await this.getValueAsync(
          isStepPrevConfitmedType
        );
        if (!isStepPrevConfirmed) {
          return;
        }
      }

      //
      this.selected = stepId;
      this.selectedChange.emit(stepId);
    }
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XStepperActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XStepperAction.NEXT:
            await this.next(model.force);
            break;

          //
          case XStepperAction.PREV:
            await this.prev(model.force);
            break;

          //
          case XStepperAction.RESET:
            await this.reset(model.force);
            break;

          //
          case XStepperAction.TO:
            if (model.payload) {
              await this.to(model.payload, model.force);
            }
            break;

          //
          case XStepperAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private getActionButtons(step: XStepComponent, type: XNPRButtonType) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        return this.getValue(this.nextButton).pipe(
          concatMap((cButton) =>
            this.getValue(step.nextButton).pipe(
              map((sButton) => ({ c: cButton, s: sButton }))
            )
          )
        );

      //
      case this.NPRButtonTypes.Prev:
        return this.getValue(this.prevButton).pipe(
          concatMap((cButton) =>
            this.getValue(step.prevButton).pipe(
              map((sButton) => ({ c: cButton, s: sButton }))
            )
          )
        );

      //
      case this.NPRButtonTypes.Reset:
        return this.getValue(this.resetButton).pipe(
          concatMap((cButton) =>
            this.getValue(step.resetButton).pipe(
              map((sButton) => ({ c: cButton, s: sButton }))
            )
          )
        );
    }
  }

  private getActionButtonDisabled(step: XStepComponent, type: XNPRButtonType) {
    //
    switch (type) {
      //
      case this.NPRButtonTypes.Next:
        return this.getOptionalValue(this.disableNext).pipe(
          concatMap((cDisable) =>
            this.getOptionalValue(step.disableNext).pipe(
              map(
                (sDisable) =>
                  this.uiDisabled || cDisable || step.uiDisabled || sDisable
              )
            )
          )
        );

      //
      case this.NPRButtonTypes.Prev:
        return this.getOptionalValue(this.disablePrev).pipe(
          concatMap((cDisable) =>
            this.getOptionalValue(step.disablePrev).pipe(
              map(
                (sDisable) =>
                  this.uiDisabled || cDisable || step.uiDisabled || sDisable
              )
            )
          )
        );

      //
      case this.NPRButtonTypes.Reset:
        return this.getOptionalValue(this.disableReset).pipe(
          concatMap((cDisable) =>
            this.getOptionalValue(step.disableReset).pipe(
              map(
                (sDisable) =>
                  this.uiDisabled || cDisable || step.uiDisabled || sDisable
              )
            )
          )
        );
    }
  }

  private async getStep(id: string) {
    //
    if (!id || !this.hasSteps()) {
      return null;
    }

    //
    return this.steps.toArray().find(async (p) => {
      const pId = await this.getValueAsync(p.id);
      return pId === id;
    });
  }

  private async getStepIndex(id: string) {
    //
    if (!id || !this.hasSteps()) {
      return -1;
    }

    //
    let result = -1;
    const steps = this.steps.toArray();
    if (!this.hasChild(steps)) {
      return result;
    }

    //
    for (let i = 0; i < steps.length; i++) {
      //
      const step = steps[i];
      const stepId = await this.getValueAsync(step.id);
      if (stepId === id) {
        result = i;
      }
    }

    //
    return result;
  }

  //
  //#region Color Appliers ...
  private async applyIconColor() {
    //
    const color = await this.getValueAsync(this.iconColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XStepperCssVars.IconForegroundColor, `var(${fgColor})`);
    this.setCssVar(XStepperCssVars.IconBackgroundColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyTitleColor() {
    //
    const color = await this.getValueAsync(this.titleColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XStepperCssVars.TitleForegroundColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyeaderColor() {
    //
    const color = await this.getValueAsync(this.headerColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XStepperCssVars.HeaderForegroundColor, `var(${fgColor})`);
    this.setCssVar(XStepperCssVars.HeaderBackgroundColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-stepper.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { XIconModule } from '../x-icon/x-icon.module';
import { XCardModule } from '../x-card/x-card.module';
import { XFormModule } from './../x-form/x-form.module';
import { XStepperComponent } from './x-stepper.component';
import { XStepComponent } from './x-step/x-step.component';
import { XButtonModule } from '../x-button/x-button.module';
import { XSlotterModule } from '../x-slotter/x-slotter.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XStepperComponent, XStepComponent],
  imports: [
    XIconModule,
    XCardModule,
    XFormModule,
    CommonModule,
    XButtonModule,
    XSlotterModule,
    ReactiveFormsModule,
    XBaseComponentModule,
  ],
  exports: [
    XIconModule,
    XCardModule,
    XFormModule,
    CommonModule,
    XButtonModule,
    XStepComponent,
    XSlotterModule,
    XStepperComponent,
    ReactiveFormsModule,
    XBaseComponentModule,
  ],
})
export class XStepperModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-stepper.typings.ts

import {
  Input,
  NgZone,
  Inject,
  Output,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import { concatMap, map } from 'rxjs/operators';
import {
  XNextPrevResetAndHostContainer,
  IXNextPrevResetAndHostContainer,
  XNextPrevResetAndHostContainerComponent,
} from '../classes/i-x-next-prev-reset.container';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XStepperType {
  HORIZONTAL = 'horizontal',
  VERTICAL = 'vertical',
}

export type XStepperTypeIdentifier = XStepperType | string;

export enum XStepperLabelPosition {
  END = 'end',
  BOTTOM = 'bottom',
}

export type XStepperLabelPositionIdentifier = XStepperLabelPosition | string;

export enum XStepperAction {
  NEXT = 'next',
  PREV = 'prev',
  RESET = 'reset',
  TO = 'to',

  //
  Refresh = 'refresh',
}

export type XStepperActionIdentifier = XStepperAction | string;

export interface XStepperActionModel
  extends XBaseActionModel<XStepperActionIdentifier> {
  force?: boolean;
}

export enum XStepperCssVars {
  //
  IconForegroundColor = 'icon-foreground-color',
  IconBackgroundColor = 'icon-background-color',

  //
  TitleForegroundColor = 'title-foreground-color',

  //
  HeaderForegroundColor = 'header-foreground-color',
  HeaderBackgroundColor = 'header-background-color',
}

export type XStepperCssVarsIdentifier = XStepperCssVars | string;

export function defaultActionConfirmer() {
  return true;
}

//
//#region Inside Card Container ...
export interface IXStepperContainer extends IXNextPrevResetAndHostContainer {
  //
  actionProvider: Subject<XStepperActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  type: XStandardType<XStepperTypeIdentifier>;
  showLabel: XStandardType<boolean>;
  labelPosition: XStandardType<XStepperLabelPositionIdentifier>;

  //
  selectableHeaders: XStandardType<boolean>;

  //
  selectedIndex: XStandardType<number>;
  selectedChange: EventEmitter<string>;

  //
  iconColor: XStandardType<XColorIdentifier>;
  titleColor: XStandardType<XColorIdentifier>;
  headerColor: XStandardType<XColorIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XStepperContainer
  extends XNextPrevResetAndHostContainer
  implements Readonly<IXStepperContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XStepperActionModel> =
    new Subject<XStepperActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly type: XStandardType<XStepperTypeIdentifier> =
    XStepperType.HORIZONTAL;
  readonly showLabel: XStandardType<boolean> = true;
  readonly labelPosition: XStandardType<XStepperLabelPositionIdentifier> =
    XStepperLabelPosition.END;

  //
  readonly selectableHeaders: XStandardType<boolean> = false;

  //
  readonly selectedIndex: XStandardType<number>;
  readonly selectedChange: EventEmitter<string> = new EventEmitter<string>();

  //
  readonly iconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Tertiary;
  readonly titleColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Secondary;
  readonly headerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XStepperContainerComponent
  extends XNextPrevResetAndHostContainerComponent
  implements IXStepperContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XStepperActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  type: XStandardType<XStepperTypeIdentifier> = this.propertyProvider.type;

  @Input()
  showLabel: XStandardType<boolean> = this.propertyProvider.showLabel;

  @Input()
  labelPosition: XStandardType<XStepperLabelPositionIdentifier> =
    this.propertyProvider.labelPosition;

  //
  @Input()
  selectableHeaders: XStandardType<boolean> =
    this.propertyProvider.selectableHeaders;

  //
  @Input()
  selectedIndex: XStandardType<number> = this.propertyProvider.selectedIndex;

  @Output()
  selectedChange: EventEmitter<string> = new EventEmitter();

  //
  @Input()
  iconColor: XStandardType<XColorIdentifier> = this.propertyProvider.iconColor;

  @Input()
  titleColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.titleColor;

  @Input()
  headerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerColor;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XStepperContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXStepperContainer
  ): Observable<IXStepperContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            type: this.getValue(container ? container.type : this.type),
            showLabel: this.getValue(
              container ? container.showLabel : this.showLabel
            ),
            labelPosition: this.getValue(
              container ? container.labelPosition : this.labelPosition
            ),
            selectableHeaders: this.getValue(
              container ? container.selectableHeaders : this.selectableHeaders
            ),
            selectedIndex: this.getValue(
              container ? container.selectedIndex : this.selectedIndex
            ),
            iconColor: this.getValue(
              container ? container.iconColor : this.iconColor
            ),
            titleColor: this.getValue(
              container ? container.titleColor : this.titleColor
            ),
            headerColor: this.getValue(
              container ? container.headerColor : this.headerColor
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            selectedChange: this.selectedChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-step\x-step.component.html

<!-- Label -->
<ng-template #label>
  <ng-content select="[x-step-label]"></ng-content>
</ng-template>

<!-- Content -->
<ng-template #content>
  <ng-content></ng-content>
</ng-template>

<!-- Actions -->
<ng-template #actions>
  <ng-content select="[x-step-actions]"></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-step\x-step.component.scss


### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-step\x-step.component.ts

import {
  XStepAction,
  XStepActionModel,
  XStepContainerComponent,
} from './x-step.typings';
import { Subject, Subscription } from 'rxjs';
import { XModalButtonRole, nameof } from 'x-framework-core';
import { Component, ViewChild, TemplateRef } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-step',
  templateUrl: './x-step.component.html',
  styleUrls: ['./x-step.component.scss'],
})
export class XStepComponent extends XStepContainerComponent {
  //
  //#region Props ...
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  //#region ViewChilds ...
  //
  @ViewChild('label', { static: false })
  label!: TemplateRef<any>;

  //
  @ViewChild('content', { static: false })
  content!: TemplateRef<any>;

  //
  @ViewChild('actions', { static: false })
  actions!: TemplateRef<any>;
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XStepComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerInstanceHandlers() {
    super.registerInstanceHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XStepActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XStepAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private defaultStepperResetConfirmer() {
    //
    return new Promise<boolean>((resolver) => {
      //
      this.managerService.dialogService.presentAreYouSureYesNoDialog(
        () => resolver(true),
        XModalButtonRole.Destructive,
        () => resolver(false)
      );
    });
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-stepper\x-step\x-step.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import { concatMap, map } from 'rxjs/operators';
import {
  XNextPrevResetContainer,
  IXNextPrevResetContainer,
  XNextPrevResetContainerComponent,
} from '../../classes/i-x-next-prev-reset.container';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Subject, Observable } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { generateUuid, XStandardType } from 'x-framework-core';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XStepAction {
  Refresh = 'refresh',
}

export type XStepActionIdentifier = XStepAction | string;

export interface XStepActionModel
  extends XBaseActionModel<XStepActionIdentifier> {
  force?: boolean;
}

//
//#region Container ...
export interface IXStepContainer extends IXNextPrevResetContainer {
  actionProvider: Subject<XStepActionModel>;
  id: XStandardType<string>;
  hasCustomActions: XStandardType<boolean>;
}

@Injectable({
  providedIn: 'root',
})
export class XStepContainer
  extends XNextPrevResetContainer
  implements Readonly<IXStepContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XStepActionModel> =
    new Subject<XStepActionModel>();
  readonly id: XStandardType<string>; // = generateUuid('XStepComponent');
  readonly hasCustomActions: XStandardType<boolean> = false;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XStepContainerComponent
  extends XNextPrevResetContainerComponent
  implements IXStepContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XStepActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  id: XStandardType<string> = generateUuid('XStepComponent');

  @Input()
  hasCustomActions: XStandardType<boolean> =
    this.propertyProvider.hasCustomActions;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XStepContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXStepContainer
  ): Observable<IXStepContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            id: this.getValue(container ? container.id : this.id),
            hasCustomActions: this.getValue(
              container ? container.hasCustomActions : this.hasCustomActions
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tab-navigator\x-tab-navigator.component.html

<div
  class="x-tab-navigator x-wrapper"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
>
  <ion-tabs>
    <ion-tab-bar
      [xColor]="getValue(color) | async"
      [slot]="getValue(navigatorSlot) | async"
      [isForeground]="getValue(foregroundColor) | async"
    >
      <ng-container *ngFor="let item of preparedItems">
        <ion-tab-button
          *ngIf="item"
          class="x-nav-button"
          [tab]="getItemRoute(item) | async"
          [href]="getItemRoute(item) | async"
          [disabled]="isDisabled(item) | async"
          [layout]="getValue(item.layout) | async"
          [matTooltip]="getItemTooltip(item) | async"
          [download]="getOptionalValue(item.download) | async"
          [target]="getOptionalValue(item.routerTarget) | async"
        >
          <!-- Icon -->
          <x-icon
            class="x-icon"
            [color]="'cb-inherit'"
            [name]="getValue(item.icon) | async"
            *ngIf="notEmptyValue(item.icon) | async"
            [cssClass]="'ion-no-padding ion-no-margin'"
          >
          </x-icon>

          <!-- Label -->
          <ion-label class="x-tab-label" *ngIf="!(hideTabTitle(item) | async)">
            {{ getResourceValue(item.title) | async }}
          </ion-label>

          <!-- Badge Number -->
          <ion-badge *ngIf="hasBadgeNumber(item) | async">
            {{ getItemBadgeNumber(item) | async }}
          </ion-badge>
        </ion-tab-button>
      </ng-container>
    </ion-tab-bar>
  </ion-tabs>
</div>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tab-navigator\x-tab-navigator.component.scss

:host {
  //
  --highlight-foreground-color: var(--x-color-success-contrast);
  --highlight-background-color: var(--x-color-success);

  //
  --selected-foreground-color: var(--x-color-secondary-contrast);
  --selected-background-color: var(--x-color-secondary);

  //
  --badge-foreground-color: var(--x-color-dark-contrast);
  --badge-background-color: var(--x-color-dark);

  //
  --icon-color: var(--x-color-dark);

  //
  --foreground-color: var(--x-color-light-contrast);
  --background-color: var(--x-color-light);
}

x-icon {
  font-size: 12px;
  color: var(--icon-color);
}

ion-tab-button {
  --color: var(--foreground-color);
  --color-focused: var(--highlight-foreground-color);
  --color-selected: var(--selected-foreground-color);
}

ion-tab-button::part(native) {
  color: var(--foreground-color);

  &:hover {
    color: var(--highlight-foreground-color);
  }
}

ion-badge {
  color: var(--badge-foreground-color);
  background-color: var(--badge-background-color);
}

.tab-selected {
  background-color: var(--selected-background-color) !important;

  x-icon {
    color: var(--selected-foreground-color) !important;
  }
}

.x-tab-label {
  font-size: 9px !important;

  &:hover {
    color: var(--highlight-foreground-color);
  }
}

.x-nav-button:hover {
  color: var(--highlight-foreground-color);
  background-color: var(--highlight-background-color);

  x-icon {
    color: var(--highlight-foreground-color) !important;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tab-navigator\x-tab-navigator.component.ts

import {
  nameof,
  parseXColor,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XTabNavigatorItem,
  XTabNavigatorAction,
  XTabNavigatorCssVars,
  XTabNavigatorItemLayout,
  XTabNavigatorActionModel,
  XTabNavigatorContainerComponent,
} from './x-tab-navigator.typings';
import { map, concatMap } from 'rxjs/operators';
import { of, Observable, Subject, Subscription } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-tab-navigator',
  templateUrl: './x-tab-navigator.component.html',
  styleUrls: ['./x-tab-navigator.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XTabNavigatorComponent extends XTabNavigatorContainerComponent {
  //
  //#region Props ...
  //
  private PREPARED_ITEMS: XTabNavigatorItem[];
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  get preparedItems() {
    return this.PREPARED_ITEMS;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.prepareItems();
    await this.applyDefaultColors();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Items Changed ...
    const isItemsChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('items')
    );
    if (isItemsChanged) {
      await this.prepareItems();
    }
    //#endregion

    //
    //#region Colors Changed ...
    //
    // Color ...
    const isColorChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('color')
    );
    if (isColorChanged) {
      await this.applyColor();
    }

    //
    // Icon Color ...
    const isIconColorChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('iconColor')
    );
    if (isIconColorChanged) {
      await this.applyIconColor();
    }

    //
    // HighLight Color ...
    const isHighLightColorChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('highlightColor')
    );
    if (isHighLightColorChanged) {
      await this.applyHighLightedColor();
    }

    //
    // Selected Color ...
    const isSelectedColorChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('selectedColor')
    );
    if (isSelectedColorChanged) {
      await this.applySelectedColor();
    }

    //
    // Badge Color ...
    const isBadgeColorChanged = changeKeys.includes(
      nameof<XTabNavigatorComponent>('badgeColor')
    );
    if (isBadgeColorChanged) {
      await this.applyBadgeColor();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Ui Providers ...
  isDisabled(item: XTabNavigatorItem) {
    return (this.getOptionalValue(item.disabled) as Observable<boolean>).pipe(
      map((disabled) => disabled || this.uiDisabled)
    );
  }

  getItemRoute(item: XTabNavigatorItem) {
    return this.getArrayValue(item.href).pipe(
      map((href) => {
        //
        if (!this.hasChild(href)) {
          return undefined;
        }

        //
        if (href.length === 1) {
          return href[0];
        }

        //
        const result = href.join('/').replace('//', '/');
        return result;
      })
    );
  }

  getItemTooltip(item: XTabNavigatorItem) {
    return this.getResourceValue(item.description).pipe(
      concatMap((val) => {
        if (isNullOrEmptyString(val.toString())) {
          return this.getResourceValue(item.title);
        } else {
          return of(val);
        }
      })
    );
  }

  hideTabTitle(item: XTabNavigatorItem) {
    return this.getValue(this.hideTabLabelsOnSmallScreens).pipe(
      concatMap((hideOnMobile) => {
        return hideOnMobile
          ? this.isMobileUi$
          : this.getValue(item.layout).pipe(
              map((layout) => layout === XTabNavigatorItemLayout.LABEL_HIDE)
            );
      })
    );
  }

  hasBadgeNumber(item: XTabNavigatorItem) {
    return this.getValue(item.badge).pipe(map((badge) => badge > 0));
  }

  getItemBadgeNumber(item: XTabNavigatorItem) {
    return (this.getOptionalValue(item.badge) as Observable<number>).pipe(
      map((badge) => {
        if (!badge) {
          return this.applyLocale(0);
        } else {
          return this.applyLocale(badge);
        }
      })
    );
  }
  //#endregion

  //
  //#region Private ...
  private async prepareItems() {
    //
    const items = await this.getValueAsync(this.items);
    this.PREPARED_ITEMS = items;

    //
    this.detectChanges();
  }

  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XTabNavigatorActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XTabNavigatorAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region Color Appliers ...
  private async applyDefaultColors() {
    //
    await this.applyColor();
    await this.applyIconColor();
    await this.applyBadgeColor();
    await this.applySelectedColor();
    await this.applyHighLightedColor();
  }

  //
  // Color ...
  private async applyColor() {
    //
    const color = await this.getValueAsync(this.color);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XTabNavigatorCssVars.ForegroundColor, `var(${fgColor})`);
    this.setCssVar(XTabNavigatorCssVars.BackgroundColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  //
  // Icon Color ...
  private async applyIconColor() {
    //
    const color = await this.getValueAsync(this.iconColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XTabNavigatorCssVars.IconColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  //
  // HighLight Color ...
  private async applyHighLightedColor() {
    //
    const color = await this.getValueAsync(this.highlightColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XTabNavigatorCssVars.HighlightedForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XTabNavigatorCssVars.HighlightedBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  //
  // Selected Color ...
  private async applySelectedColor() {
    //
    const color = await this.getValueAsync(this.selectedColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XTabNavigatorCssVars.SelectedForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XTabNavigatorCssVars.SelectedBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }

  //
  // Badge Color ...
  private async applyBadgeColor() {
    //
    const color = await this.getValueAsync(this.badgeColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(
      XTabNavigatorCssVars.BadgeForegroundColor,
      `var(${fgColor})`
    );
    this.setCssVar(
      XTabNavigatorCssVars.BadgeBackgroundColor,
      `var(${bgColor})`
    );

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tab-navigator\x-tab-navigator.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XTabNavigatorComponent } from './x-tab-navigator.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XTabNavigatorComponent],
  imports: [CommonModule, XBaseComponentModule, XIconModule],
  exports: [XIconModule, XBaseComponentModule, XTabNavigatorComponent]
})
export class XTabNavigatorModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tab-navigator\x-tab-navigator.tools.ts

import {
  XTabNavigatorItem,
  XTabNavigatorItemLayout,
} from './x-tab-navigator.typings';
import { XPage } from 'x-framework-core';

export function toTabNavigatiorItem(page: XPage) {
  //
  if (!page) {
    return null;
  }

  //
  const result: XTabNavigatorItem = {
    id: page.id ? page.id : page.name,
    title: page.title,
    description: page.description,
    href: page.baseRoute,
    disabled: false,
    icon: page.icon,
    badge: 0,
    layout: XTabNavigatorItemLayout.ICON_TOP,
  };

  //
  return result;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tab-navigator\x-tab-navigator.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
  XRouterTargetIdentifier,
} from 'x-framework-core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XNavigatorListItem } from '../x-page/x-navigator/x-navigator.typings';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export interface XTabNavigatorItem
  extends Pick<
    XNavigatorListItem,
    'id' | 'title' | 'description' | 'href' | 'disabled'
  > {
  //
  icon: XStandardType<XIconIdentifier>;
  badge?: XStandardType<number>;
  //
  download?: XStandardType<string>;
  routerTarget?: XStandardType<XRouterTargetIdentifier>;
  //
  layout: XStandardType<XTabNavigatorItemLayout>;
}

export enum XTabNavigatorItemLayout {
  ICON_TOP = 'icon-top',
  ICON_END = 'icon-end',
  ICON_HIDE = 'icon-hide',
  ICON_START = 'icon-start',
  LABEL_HIDE = 'label-hide',
  ICON_BOTTOM = 'icon-bottom',
}

export type XTabNavigatorItemLayoutIdentifier =
  | XTabNavigatorItemLayout
  | string;

export enum XTabNavigatorNavigatorSlot {
  TOP = 'top',
  BOTTOM = 'bottom',
}

export type XTabNavigatorNavigatorSlotIdentifier =
  | XTabNavigatorNavigatorSlot
  | string;

export enum XTabNavigatorAction {
  Refresh = 'refresh',
}

export type XTabNavigatorActionIdentifier = XTabNavigatorAction | string;

export interface XTabNavigatorActionModel
  extends XBaseActionModel<XTabNavigatorActionIdentifier> {}

export enum XTabNavigatorCssVars {
  //
  HighlightedForegroundColor = 'highlight-foreground-color',
  HighlightedBackgroundColor = 'highlight-background-color',

  //
  SelectedForegroundColor = 'selected-foreground-color',
  SelectedBackgroundColor = 'selected-background-color',

  //
  BadgeForegroundColor = 'badge-foreground-color',
  BadgeBackgroundColor = 'badge-background-color',

  //
  IconColor = 'icon-color',

  //
  ForegroundColor = 'foreground-color',
  BackgroundColor = 'background-color',
}

//
//#region Container ...
export interface IXTabNavigatorContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XTabNavigatorActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  navigatorSlot: XStandardType<XTabNavigatorNavigatorSlotIdentifier>;
  hideTabLabelsOnSmallScreens: XStandardType<boolean>;
  items: XStandardType<XTabNavigatorItem[]>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;
  iconColor: XStandardType<XColorIdentifier>;
  badgeColor: XStandardType<XColorIdentifier>;
  highlightColor: XStandardType<XColorIdentifier>;
  selectedColor: XStandardType<XColorIdentifier>;
}

@Injectable({
  providedIn: 'root'
})
export class XTabNavigatorContainer
  extends XBaseContainer
  implements Readonly<IXTabNavigatorContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XTabNavigatorActionModel> =
    new Subject<XTabNavigatorActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly navigatorSlot: XStandardType<XTabNavigatorNavigatorSlotIdentifier> =
    XTabNavigatorNavigatorSlot.BOTTOM;
  readonly hideTabLabelsOnSmallScreens: XStandardType<boolean> = true;
  readonly items: XStandardType<XTabNavigatorItem[]> = [];

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;
  readonly iconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly badgeColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.WarningTint;
  readonly highlightColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.WarningShade;
  readonly selectedColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.TertiaryShade;
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XTabNavigatorContainerComponent
  extends XBaseContainerComponent
  implements IXTabNavigatorContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XTabNavigatorActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  navigatorSlot: XStandardType<XTabNavigatorNavigatorSlotIdentifier> =
    this.propertyProvider.navigatorSlot;

  @Input()
  hideTabLabelsOnSmallScreens: XStandardType<boolean> =
    this.propertyProvider.hideTabLabelsOnSmallScreens;

  @Input()
  items: XStandardType<XTabNavigatorItem[]> = this.propertyProvider.items;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  @Input()
  iconColor: XStandardType<XColorIdentifier> = this.propertyProvider.iconColor;

  @Input()
  badgeColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.badgeColor;

  @Input()
  highlightColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.highlightColor;

  @Input()
  selectedColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.selectedColor;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XTabNavigatorContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXTabNavigatorContainer
  ): Observable<IXTabNavigatorContainer> {
    return forkJoin({
      //
      cssClass: this.getValue(container ? container.cssClass : this.cssClass),
      navigatorSlot: this.getValue(
        container ? container.navigatorSlot : this.navigatorSlot
      ),
      hideTabLabelsOnSmallScreens: this.getValue(
        container
          ? container.hideTabLabelsOnSmallScreens
          : this.hideTabLabelsOnSmallScreens
      ),
      items: this.getValue(container ? container.items : this.items),
      color: this.getValue(container ? container.color : this.color),
      foregroundColor: this.getValue(
        container ? container.foregroundColor : this.foregroundColor
      ),
      iconColor: this.getValue(
        container ? container.iconColor : this.iconColor
      ),
      badgeColor: this.getValue(
        container ? container.badgeColor : this.badgeColor
      ),
      highlightColor: this.getValue(
        container ? container.highlightColor : this.highlightColor
      ),
      selectedColor: this.getValue(
        container ? container.selectedColor : this.selectedColor
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-table\x-table.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else tableRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="tableRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Table ItSelf Template -->
<ng-template #tableRef>
  <!-- Wrapper -->
  <div class="x-table x-wrapper" [class.mobile]="isMobileUi$ | async">
    <!-- SearchBar -->
    <ng-container *ngIf="isSearchbarTopPositioned() | async">
      <ng-container *ngTemplateOutlet="searchBarRef"></ng-container>
    </ng-container>

    <!-- Table Actions -->
    <ng-container
      *ngIf="
        (getValue(showActionBar) | async) &&
        (isActionBarTopPositioned() | async)
      "
    >
      <ng-container *ngTemplateOutlet="actionsRef"></ng-container>
    </ng-container>

    <!-- Table Paginator -->
    <ng-container
      *ngIf="
        (hasChildValue(data) | async) &&
        (getValue(showPaginator) | async) &&
        (isPaginatorTopPositioned() | async)
      "
    >
      <ng-container *ngTemplateOutlet="paginatorRef"></ng-container>
    </ng-container>

    <!-- Table if there is Data -->
    <mat-table
      matSort
      class="x-table"
      [dataSource]="dataSource"
      [xColor]="getValue(color) | async"
      *ngIf="hasChildValue(data) | async"
      [ngClass]="getValue(cssClass) | async"
      [isForeground]="getValue(foregroundColor) | async"
      [matSortDisableClear]="!(getValue(canClearSort) | async)"
    >
      <!-- Columns -->
      <ng-container *ngFor="let col of columnsData">
        <!-- Selection Column -->
        <ng-container
          [sticky]="true"
          *ngIf="isSelectionRow(col) | async"
          [matColumnDef]="getColumnName(col) | async"
        >
          <!-- Selection Header Cell -->
          <mat-header-cell
            *matHeaderCellDef
            [ngClass]="getHeaderClass(col) | async"
            class="x-table-header x-table-selection"
            [xColor]="getValue(headerColor) | async"
            [isForeground]="getValue(headerForegroundColor) | async"
          >
            <ion-checkbox
              [disabled]="!selection"
              [class.hydrated]="false"
              [hidden]="!(getValue(allowMultiSelect) | async)"
              class="x-check-box x-selection-master ion-margin-end"
            >
            </ion-checkbox>
          </mat-header-cell>

          <!-- Selection Table Cell -->
          <mat-cell
            *matCellDef="let row"
            [ngClass]="getCellClass(col) | async"
            class="x-table-cell x-table-selection"
          >
            <ion-checkbox
              [checked]="isSelected(row)"
              class="x-check-box ion-margin-end"
              [disabled]="!isRowsSelectable || !selection"
              (ionChange)="handleRowSelected($event, row)"
            >
            </ion-checkbox>
          </mat-cell>
        </ng-container>

        <!-- Model Displayed Columns -->
        <ng-container
          *ngIf="!(isSelectionRow(col) | async)"
          [matColumnDef]="getColumnName(col) | async"
        >
          <!-- Header Cell -->
          <mat-header-cell
            mat-sort-header
            *matHeaderCellDef
            class="x-table-header"
            [ngClass]="getHeaderClass(col) | async"
            [xColor]="getValue(headerColor) | async"
            [hidden]="getOptionalValue(col.hidden) | async"
            [disabled]="!(getOptionalValue(col.sortable) | async)"
            [isForeground]="getValue(headerForegroundColor) | async"
            [matTooltip]="getResourceValue(col.description) | async"
          >
            <ng-container
              *ngTemplateOutlet="
                headerCellRef;
                context: { $implicit: getResourceValue(col.title) | async }
              "
            ></ng-container>
          </mat-header-cell>

          <!-- Table Cell -->
          <mat-cell
            class="x-table-cell"
            *matCellDef="let element"
            [ngClass]="getCellClass(col) | async"
            [hidden]="getOptionalValue(col.hidden) | async"
          >
            <!-- Determine which Template to used based on Custom Column or Displayed Columns and pass Proper Data to it -->
            <ng-container
              *ngTemplateOutlet="
                (getOptionalValue(col.customColumn) | async)
                  ? customCellTemplate || defaultCustomCellRef
                  : cellTemplate || defaultCellRef;
                context: {
                  $implicit:
                    (getOptionalValue(col.customColumn) | async)
                      ? element
                      : element[col.name],
                  name: col.name
                }
              "
            ></ng-container>
          </mat-cell>
        </ng-container>
      </ng-container>

      <!-- Header Rows -->
      <ng-container *ngIf="getValue(showHeader) | async">
        <!-- Header Row -->
        <mat-header-row
          mat-header-row
          class="x-table-header-row"
          *matHeaderRowDef="
            displayedColumns;
            sticky: getValue(stickyHeader) | async
          "
        ></mat-header-row>
      </ng-container>

      <!-- Items Row -->
      <mat-row
        matRipple
        class="x-table-row"
        (click)="handleRowClicked(row)"
        [xColor]="getValue(color) | async"
        [matRippleDisabled]="
          !(getValue(clickableRows) | async) &&
          !(getValue(selectableRows) | async)
        "
        *matRowDef="let row; columns: displayedColumns"
        [isForeground]="getValue(foregroundColor) | async"
      ></mat-row>
    </mat-table>

    <!-- SearchBar -->
    <ng-container *ngIf="!(isSearchbarTopPositioned() | async)">
      <ng-container *ngTemplateOutlet="searchBarRef"></ng-container>
    </ng-container>

    <!-- Table Actions -->
    <ng-container
      *ngIf="
        (getValue(showActionBar) | async) &&
        !(isActionBarTopPositioned() | async)
      "
    >
      <ng-container *ngTemplateOutlet="actionsRef"></ng-container>
    </ng-container>

    <!-- Table Paginator -->
    <ng-container
      *ngIf="
        (hasChildValue(data) | async) &&
        (getValue(showPaginator) | async) &&
        !(isPaginatorTopPositioned() | async)
      "
    >
      <ng-container *ngTemplateOutlet="paginatorRef"></ng-container>
    </ng-container>

    <!-- Empty -->
    <ng-container
      *ngIf="(getValue(showEmpty) | async) && !(hasChildValue(data) | async)"
    >
      <ng-container *ngTemplateOutlet="defaultEmptyListRef"></ng-container>
    </ng-container>
  </div>
</ng-template>

<!-- Header Cell Ref -->
<ng-template #headerCellRef let-title>
  <div class="x-header-title text-truncate">
    {{ title }}
  </div>
</ng-template>

<!-- Default Empty Content -->
<ng-template #defaultEmptyListRef>
  <x-empty
    (refresh)="emptyRefresh.emit($event)"
    [color]="getValue(emptyColor) | async"
    [message]="getResourceValue(emptyMessage) | async"
    [refreshIcon]="getValue(emptyRefreshIcon) | async"
    [messageColor]="getValue(emptyMessageColor) | async"
    [hasRefresh]="getValue(emptyHasRefresh) | async"
    [foregroundRefreshButtonColor]="
      getValue(emptyForegroundRefreshButtonColor) | async
    "
    [foregroundColor]="getValue(emptyForegroundColor) | async"
    [refreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
    [foregroundMessageColor]="getValue(emptyForegroundMessageColor) | async"
  ></x-empty>
</ng-template>

<!-- Default Element Template -->
<ng-template #defaultCellRef let-value let-name="name">
  <div class="text-truncate">
    {{ value }}
  </div>
</ng-template>

<!-- Default Custom Template -->
<ng-template #defaultCustomCellRef let-item> </ng-template>

<!-- SearchBar Ref -->
<ng-template #searchBarRef>
  <ion-toolbar
    class="x-search-bar"
    *ngIf="getValue(showSearchBar) | async"
    [xColor]="getValue(searchBarColor) | async"
  >
    <ion-searchbar
      class="x-list-actions"
      [xColor]="getValue(searchBarColor) | async"
      [placeholder]="getResourceValue(searchTitle) | async"
      (ionChange)="handleFilterData($event)"
    ></ion-searchbar>
  </ion-toolbar>
</ng-template>

<!-- PaginatorRef -->
<ng-template #paginatorRef>
  <ion-toolbar
    class="x-paginator-bar"
    *ngIf="getValue(showPaginator) | async"
    [xColor]="getValue(paginatorColor) | async"
  >
    <!-- Mat Paginator Component -->
    <mat-paginator
      #matPaginator
      [hidden]="true"
      *ngIf="preparedPaginatorOptions"
      [pageSize]="getValue(preparedPaginatorOptions.pageSize) | async"
    ></mat-paginator>

    <!-- Start Slot Contents -->
    <ion-buttons slot="start" *ngIf="preparedPaginatorOptions">
      <!-- First -->
      <ion-button
        #first
        (click)="firstPage()"
        *ngIf="preparedPaginatorOptions.first"
        [hidden]="getValue(preparedPaginatorOptions.first.hidden) | async"
        [disabled]="
          uiDisabled ||
          loading ||
          (getValue(preparedPaginatorOptions.first.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.first.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.first.title) | async,
              color: getValue(preparedPaginatorOptions.first.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>

      <!-- Previous -->
      <ion-button
        #previous
        (click)="previousPage()"
        *ngIf="preparedPaginatorOptions.prev"
        [hidden]="getValue(preparedPaginatorOptions.prev.hidden) | async"
        [disabled]="
          uiDisabled ||
          loading ||
          (getValue(preparedPaginatorOptions.prev.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.prev.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.prev.title) | async,
              color: getValue(preparedPaginatorOptions.prev.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>
    </ion-buttons>

    <!-- Pagination Info -->
    <div class="x-table-paginator-wrapper">
      <div fxLayout="row" fxLayoutAlign="center stretch">
        <!-- Current Page -->
        <div fxFlex="auto" *ngIf="currentPage">
          <ion-button
            fill="clear"
            expand="full"
            class="cb-inherit"
            *ngIf="!pageNumberInput"
            [disabled]="uiDisabled || loading"
            (click)="pageNumberInput = true"
          >
            {{
              preparedPaginatorOptions.pageInfoProvider(
                managerService.currentLocale,
                currentPage,
                pageSize,
                totalPages,
                totalItems
              )
            }}
          </ion-button>

          <!-- Change Current Page -->
          <ion-input
            step="1"
            [min]="1"
            type="number"
            debounce="500"
            required="true"
            autofocus="true"
            [max]="totalPages"
            class="cb-inherit"
            inputmode="numeric"
            [value]="currentPage"
            *ngIf="pageNumberInput"
            (ionBlur)="pageNumberInput = false"
            [disabled]="uiDisabled || loading"
            (ionChange)="handlePageChange($event)"
          ></ion-input>
        </div>

        <!-- Page Size -->
        <div fxFlex="auto">
          <ion-button
            fill="clear"
            expand="full"
            *ngIf="pageSize"
            class="cb-inherit"
            (click)="handlePageSizeChange()"
            [disabled]="uiDisabled || loading"
          >
            {{ applyLocale(pageSize) }}
          </ion-button>
        </div>
      </div>
    </div>

    <!-- End Slot Content -->
    <ion-buttons slot="end" *ngIf="preparedPaginatorOptions">
      <!-- Noxt -->
      <ion-button
        #next
        (click)="nextPage()"
        *ngIf="preparedPaginatorOptions.next"
        [hidden]="getValue(preparedPaginatorOptions.next.hidden) | async"
        [disabled]="
          uiDisabled ||
          loading ||
          (getValue(preparedPaginatorOptions.next.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.next.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.next.title) | async,
              color: getValue(preparedPaginatorOptions.next.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>

      <!-- Last -->
      <ion-button
        #last
        (click)="lastPage()"
        *ngIf="preparedPaginatorOptions.last"
        [hidden]="getValue(preparedPaginatorOptions.last.hidden) | async"
        [disabled]="
          uiDisabled ||
          loading ||
          (getValue(preparedPaginatorOptions.last.disabled) | async)
        "
      >
        <ng-container
          *ngTemplateOutlet="
            iconRef;
            context: {
              name: getValue(preparedPaginatorOptions.last.icon) | async,
              tooltip:
                getResourceValue(preparedPaginatorOptions.last.title) | async,
              color: getValue(preparedPaginatorOptions.prev.color) | async,
              applyDirection: true
            }
          "
        ></ng-container>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ng-template>

<!-- ActionsRef -->
<ng-template #actionsRef>
  <ion-toolbar
    class="x-actions-bar"
    [xColor]="getValue(actionBarColor) | async"
    *ngIf="preparedActionOptions && getValue(showActionBar) | async"
  >
    <!-- Start Slot Content -->
    <ion-buttons slot="start">
      <ng-container *ngFor="let action of preparedActionOptions.start">
        <ng-container
          *ngTemplateOutlet="actionButtonRef; context: { $implicit: action }"
        ></ng-container>
      </ng-container>
    </ion-buttons>

    <!-- Custom Content -->
    <div
      fxLayout="row"
      class="x-table-paginator-wrapper"
      fxLayoutAlign="center stretch"
    >
      <ng-content select="[actions]"></ng-content>
    </div>

    <!-- End Slot Content -->
    <ion-buttons slot="end">
      <ng-container *ngFor="let action of preparedActionOptions.end">
        <ng-container
          *ngTemplateOutlet="actionButtonRef; context: { $implicit: action }"
        ></ng-container>
      </ng-container>
    </ion-buttons>
  </ion-toolbar>
</ng-template>

<!-- Icon Template -->
<ng-template
  #iconRef
  let-name="name"
  let-tooltip="tooltip"
  let-color="color"
  let-applyDirection="applyDirection"
>
  <x-icon
    [name]="name"
    [color]="color"
    [matTooltip]="tooltip"
    [applyDirection]="applyDirection"
    [foregroundColor]="true"
    [cssClass]="!color ? 'cb-inherit' : null"
  ></x-icon>
</ng-template>

<!-- Action Button Ref -->
<ng-template #actionButtonRef let-action>
  <ion-button
    *ngIf="action"
    [hidden]="getValue(action.hidden) | async"
    [disabled]="getValue(action.disabled) | async"
    (click)="handleActionBarItemClick(action, selection.selected)"
  >
    <ng-container
      *ngTemplateOutlet="
        iconRef;
        context: {
          name: getValue(action.icon) | async,
          tooltip: getResourceValue(action.title) | async,
          color: getValue(action.color) | async,
          applyDirection: false
        }
      "
    ></ng-container>
  </ion-button>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-table\x-table.component.scss

::ng-deep :root,
:host {
  //
  --divider-color: var(--x-color-dark);

  //
  --highlight-color: var(--x-color-warning-shade-contrast);
  --highlight-background-color: var(--x-color-warning-shade);

  //
  --odd-rows-background-color: inherit;
  --even-rows-background-color: inherit;

  //
  --check-box-border-color: var(--x-color-dark);
  --check-box-check-mark-color: var(--x-color-dark);
}

x-card {
  width: inherit;
  height: inherit;
}

.x-table {
  width: inherit;
  height: inherit;
}

mat-row,
td.mat-cell,
mat-header-row,
mat-footer-row,
th.mat-header-cell,
td.mat-footer-cell {
  border-bottom-color: var(--divider-color) !important;
}

.mat-paginator,
.mat-paginator-page-size .mat-select-trigger {
  color: unset !important;
}

.mat-paginator {
  background-color: unset !important;
}

.x-table-paginator-wrapper {
  width: 100%;
}

.x-page-select-icon {
  margin: {
    left: 4px;
    right: 4px;
  }
}

.x-table-cell,
.x-table-header {
  flex: 1 !important;
}

.x-header-title {
  width: fit-content;
}

.x-table-selection {
  flex: none !important;
}

.x-table-cell {
  color: inherit !important;
  background-color: inherit !important;
}

.x-table-row {
  &:hover {
    color: var(--highlight-color) !important;
    background-color: var(--highlight-background-color) !important;
  }

  &:not(:hover):nth-child(odd) {
    background-color: var(--odd-rows-background-color) !important;
  }

  &:not(:hover):nth-child(even) {
    background-color: var(--even-rows-background-color) !important;
  }
}

.x-check-box {
  --border-color: var(--check-box-border-color, inherit) !important;
  --checkmark-color: var(--check-box-check-mark-color, inherit) !important;
  --background-checked: auto !important;
  --border-color-checked: auto !important;
}

.x-selection-master {
  visibility: hidden !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-table\x-table.component.ts

import {
  nameof,
  XColor,
  toArray,
  XLocale,
  isNumber,
  hasChild,
  isFunction,
  applyLocale,
  XResourceIDs,
  XModalButtonRole,
  XPickerColumnOption,
} from 'x-framework-core';
import {
  XTableAction,
  XTableActionModel,
  XTableColumnOption,
  XTableItemPosition,
  XTableActionButton,
  XTableActionOptions,
  XTablePaginatorOptions,
  XTableContainerComponent,
  X_TABLE_SELECTION_HEADER_IDENTIFIER,
} from './x-table.typings';
import { Subject, Subscription } from 'rxjs';
import { map, concatMap } from 'rxjs/operators';
import { MatSort } from '@angular/material/sort';
import { XIconNames } from '../x-icon/x-icon.typings';
import { SelectionModel } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { Component, ViewChild, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-table',
  templateUrl: './x-table.component.html',
  styleUrls: ['./x-table.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XTableComponent extends XTableContainerComponent {
  //
  //#region Props ...
  private MODAL: any;
  private SORT: MatSort;
  private DATA: any[] = [];
  private PAGE_SIZE: number;
  private TOTAL_PAGES: number;
  private TOTAL_ITEMS: number;
  private CURRENT_PAGE: number;
  private PAGINATOR: MatPaginator;
  private IS_ROWS_SELECTABLE: boolean;
  private DISPLAYED_COLUMNS: string[] = [];
  private ACTION_OPTIONS: XTableActionOptions;
  private SELECTION_SUBSCRIPTION: Subscription;
  private SELECTION = new SelectionModel<any>();
  private COLUMNS_DATA: XTableColumnOption<any>[];
  private PAGINATOR_OPTIONS: XTablePaginatorOptions;
  private PAGINATOR_INIT_SUBSCRIPTION: Subscription;
  private PAGINATOR_PAGE_SUBSCRIPTION: Subscription;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private DATA_SOURCE = new MatTableDataSource(this.DATA);

  //
  pageNumberInput = false;
  selectionRowTitle = X_TABLE_SELECTION_HEADER_IDENTIFIER;

  //
  //#region Getters ...
  get data() {
    return this.DATA;
  }

  get isRowsSelectable() {
    return this.IS_ROWS_SELECTABLE;
  }

  get columnsData() {
    return this.COLUMNS_DATA;
  }

  get dataSource() {
    return this.DATA_SOURCE;
  }

  get displayedColumns() {
    return this.DISPLAYED_COLUMNS;
  }

  get selection() {
    return this.SELECTION;
  }

  get preparedPaginatorOptions() {
    return this.PAGINATOR_OPTIONS;
  }

  get pageSize() {
    return this.PAGE_SIZE;
  }

  get currentPage() {
    return this.CURRENT_PAGE;
  }

  get totalPages() {
    return this.TOTAL_PAGES;
  }

  get totalItems() {
    return this.TOTAL_ITEMS;
  }

  get selectedItems() {
    return this.isRowsSelectable && this.selection
      ? this.selection.selected
      : [];
  }

  get preparedActionOptions() {
    return this.ACTION_OPTIONS;
  }
  //#endregion

  //
  //#region Sort Props ...
  @ViewChild(MatSort, { static: false })
  set sort(v: MatSort) {
    if (!v) {
      return;
    }

    //
    this.SORT = v;
    this.DATA_SOURCE.sort = v;
  }

  get sort() {
    return this.SORT;
  }
  //#endregion

  //
  //#region Paginator Props ...
  @ViewChild(MatPaginator, { static: false })
  set paginator(v: MatPaginator) {
    if (!v) {
      return;
    }

    //
    this.PAGINATOR = v;
    this.DATA_SOURCE.paginator = v;
    this.registerPaginatorHandlers();
  }

  get paginator() {
    return this.PAGINATOR;
  }
  //#endregion
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.preparePaginatorOptions();
  }

  async afterViewInit() {
    super.afterViewInit();

    //
    await this.prepareUIData();
    await this.prepareSelection();
    await this.prepareActionOptions();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    let isLoadingChanged = changeKeys.includes(
      nameof<XTableComponent>('loading')
    );
    let isUiDisabledChanged = changeKeys.includes(
      nameof<XTableComponent>('uiDisabled')
    );
    if (isLoadingChanged || isUiDisabledChanged) {
      await this.prepareActionOptions();
    }

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XTableComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    // Handle Prepare Data Source ...
    if (
      changeKeys.includes(nameof<XTableComponent>('properties')) ||
      changeKeys.includes(nameof<XTableComponent>('selectableRows'))
    ) {
      await this.prepareDataSource();
    }

    //
    // Handle Prepare Selection ...
    if (
      changeKeys.includes(nameof<XTableComponent>('allowMultiSelect')) ||
      changeKeys.includes(nameof<XTableComponent>('initialSelectedValues'))
    ) {
      await this.prepareSelection();
    }

    //
    // Handle Paginator Options ...
    if (changeKeys.includes(nameof<XTableComponent>('paginatorOptions'))) {
      await this.preparePaginatorOptions();
    }

    //
    // Handle Action Options ...
    if (changeKeys.includes(nameof<XTableComponent>('actionOptions'))) {
      await this.prepareActionOptions();
    }

    //
    // Handle UI Data ...
    if (changeKeys.includes(nameof<XTableComponent>('selectableRows'))) {
      await this.prepareUIData();
    }
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Providers ...
  isSelectionRow(col: XTableColumnOption<any>) {
    return this.getValue(col.name).pipe(
      map((colName) => colName === X_TABLE_SELECTION_HEADER_IDENTIFIER)
    );
  }

  isSearchbarTopPositioned() {
    return this.getValue(this.searchBarPosition).pipe(
      map((pos) => pos === XTableItemPosition.TOP)
    );
  }

  isPaginatorTopPositioned() {
    return this.getValue(this.paginatorPosition).pipe(
      map((pos) => pos === XTableItemPosition.TOP)
    );
  }

  isActionBarTopPositioned() {
    return this.getValue(this.actionBarPosition).pipe(
      map((pos) => pos === XTableItemPosition.TOP)
    );
  }

  getHeaderClass(col: XTableColumnOption<any>) {
    return this.getValue(col.name).pipe(
      concatMap((colName) =>
        this.getValue(this.headerCellCssClass).pipe(
          map((cssClass) => {
            return { colName, cssClass };
          })
        )
      ),
      map((info) => {
        //
        let result: string[] = [];

        //
        const className = `x-table-header-${info.colName.toString()}`;
        const cssClasses = toArray(info.cssClass);

        //
        result = [className].concat(cssClasses);

        //
        return result;
      })
    );
  }

  getCellClass(col: XTableColumnOption<any>) {
    return this.getValue(col.name).pipe(
      concatMap((colName) =>
        this.getValue(this.cellCssClass).pipe(
          map((cssClass) => {
            return { colName, cssClass };
          })
        )
      ),
      map((info) => {
        //
        let result: string[] = [];

        //
        const className = `x-table-cell-${info.colName.toString()}`;
        const cssClasses = toArray(info.cssClass);

        //
        result = [className].concat(cssClasses);

        //
        return result;
      })
    );
  }

  getColumnName(col: XTableColumnOption<any>) {
    return this.getValue(col.name).pipe(map(name => name.toString()));
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleRowClicked(row: any) {
    //
    const isClickable = await this.getValueAsync(this.clickableRows);
    const isSelectable = await this.getValueAsync(this.selectableRows);

    //
    if (!isClickable && !this.selectableRows) {
      return;
    }

    //
    if (isSelectable && this.selection) {
      //
      if (this.uiDisabled || this.loading) {
        return;
      }

      //
      this.selection.toggle(row);
    }

    //
    if (isClickable) {
      //
      if (this.uiDisabled || this.loading) {
        return;
      }

      //
      this.rowClicked.emit(row);
    }
  }

  async handleRowSelected(event: CustomEvent | any, row: any) {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const isSelectable = await this.getValueAsync(this.selectableRows);
    if (!isSelectable) {
      return;
    }

    //
    const selected = event.detail.checked as boolean;

    //
    if (!selected) {
      return;
    }

    //
    this.rowSelected.emit(row);
  }

  async handleFilterData(event: CustomEvent | any) {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const query =
      event && event.detail && event.detail.value
        ? event.detail.value.toString().trim().toLowerCase()
        : '';

    //
    await this.filterData(query);
  }

  async handlePageChange(event: CustomEvent | any) {
    //
    const selectedPage = (event.detail.value as number) || undefined;
    if (!selectedPage) {
      return;
    }

    //
    await this.toPage(selectedPage);
  }

  async handlePageSizeChange() {
    //
    if (!this.preparedPaginatorOptions || !this.pageSize) {
      return;
    }

    //
    const pageSizes = await this.getValueAsync(
      this.preparedPaginatorOptions.pageSizes
    );
    if (!hasChild(pageSizes)) {
      return;
    }

    const options: XPickerColumnOption[] = pageSizes.map((ps) => {
      //
      const opt: XPickerColumnOption = {
        text: this.applyLocale(ps.toString()),
        value: ps,
        disabled: ps === this.pageSize,
      };

      //
      return opt;
    });

    //
    if (this.MODAL) {
      await this.managerService.dialogService.pickerController.dismiss();
      this.MODAL = null;
    }

    //
    this.MODAL = await this.managerService.dialogService.presentPicker({
      buttons: [
        {
          text: this.resourceProvider('accept'),
          role: XModalButtonRole.Selected,
          handler: (value) => {
            if (!value) {
              return;
            }

            //
            const pageSize = value.pageSize.value;
            this.changePageSize(pageSize);
            this.detectChanges();
          },
        },
        {
          text: this.resourceProvider('cancel'),
          role: XModalButtonRole.Cancel,
        },
      ],
      columns: [{ name: 'pageSize', options }],
    });

    //
    await this.MODAL.onWillDismiss();
    this.MODAL = null;
  }

  async handleActionBarItemClick(
    action: XTableActionButton,
    selectedItems: any[]
  ) {
    //
    if (action && action.handler && isFunction(action.handler)) {
      await this.getValueAsync(action.handler(selectedItems));
    }
  }
  //#endregion

  //
  //#region Actions ...
  isAllSelected() {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.data.length;
    return numSelected === numRows;
  }

  isAllFilteredSelected() {
    //
    if (
      !this.dataSource ||
      !hasChild(this.dataSource.filteredData) ||
      this.selection.isEmpty() ||
      !hasChild(this.selection.selected)
    ) {
      return false;
    }

    //
    const filteredData = this.dataSource.filteredData;
    const result = this.selection.selected.every((s) =>
      filteredData.includes(s)
    );

    //
    return result;
  }

  isSelected(item: any) {
    //
    if (
      !item ||
      !this.data ||
      !this.selection ||
      !this.isRowsSelectable ||
      !this.data.includes(item)
    ) {
      return false;
    }

    //
    return this.selection.isSelected(item);
  }

  toggleSelectAll() {
    this.isAllSelected()
      ? this.selection.clear()
      : this.dataSource.data.forEach((row) => this.selection.select(row));
  }

  selectAll() {
    //
    if (
      !this.isRowsSelectable ||
      !this.selection ||
      !this.dataSource ||
      !this.dataSource.data
    ) {
      return;
    }

    //
    this.dataSource.data.forEach((i) => this.selection.select(i));
  }

  selectAllFilteredData() {
    //
    if (
      !this.isRowsSelectable ||
      !this.selection ||
      !this.dataSource ||
      !this.dataSource.data ||
      !this.dataSource.filteredData
    ) {
      return;
    }

    //
    this.dataSource.filteredData.forEach((i) => this.selection.select(i));
  }

  deselectAll() {
    //
    if (
      !this.isRowsSelectable ||
      !this.selection ||
      !this.dataSource ||
      !this.dataSource.data ||
      !this.dataSource.filteredData
    ) {
      return;
    }

    //
    this.dataSource.filteredData.forEach((i) => this.selection.deselect(i));
  }

  deselectAllFilteredData() {
    //
    if (!this.isRowsSelectable || !this.selection) {
      return;
    }

    //
    this.selection.clear();
  }

  inverseSelection() {
    //
    if (
      !this.isRowsSelectable ||
      !this.selection ||
      !this.dataSource ||
      !this.dataSource.data
    ) {
      return;
    }

    //
    this.dataSource.data.forEach((i) => this.selection.toggle(i));
  }

  async filterData(query: string) {
    //
    this.dataSource.filter = query.trim().toLowerCase();
    this.prepareActionOptions();

    //
    await this.firstPage();
  }

  async firstPage() {
    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.firstPage();

    //
    this.detectChanges();
  }

  async lastPage() {
    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.lastPage();

    //
    this.detectChanges();
  }

  async nextPage() {
    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.nextPage();

    //
    this.detectChanges();
  }

  async previousPage() {
    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    this.paginator.previousPage();

    //
    this.detectChanges();
  }

  async toPage(index: number) {
    //
    const hasPaginator = await this.getValueAsync(this.showPaginator);
    if (!hasPaginator || !this.paginator) {
      return;
    }

    //
    index = index === 0 ? 1 : index;

    //
    this.paginator.pageIndex = index - 1;
    this.paginator.page.next({
      pageIndex: index - 1,
      pageSize: this.pageSize,
      length: this.totalItems,
    });

    //
    this.detectChanges();
  }

  async changePageSize(size: number) {
    //
    if (!size || !this.paginator) {
      return;
    }

    //
    this.paginator.pageSize = size;
    this.paginator.page.emit({
      pageIndex: 0,
      pageSize: size,
      length: this.totalItems,
    });

    //
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Action Provider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XTableActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XTableAction.Refresh:
            this.detectChanges();
            break;

          //
          case XTableAction.DE_SELECT_ALL:
            this.deselectAll();
            break;

          //
          case XTableAction.INVERSE_SELECTION:
            this.inverseSelection();
            break;

          //
          case XTableAction.SELECT_ALL:
            this.selectAll();
            break;

          //
          case XTableAction.TOGGLE_SELECT_ALL:
            this.toggleSelectAll();
            break;

          //
          case XTableAction.FIRST_PAGE:
            await this.firstPage();
            break;

          //
          case XTableAction.LAST_PAGE:
            await this.lastPage();
            break;

          //
          case XTableAction.NEXT_PAGE:
            await this.nextPage();
            break;

          //
          case XTableAction.PREVIOUS_PAGE:
            await this.previousPage();
            break;

          //
          case XTableAction.TO_PAGE:
            if (model.payload && isNumber(model.payload)) {
              await this.toPage(model.payload as number);
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private async prepareUIData() {
    //
    this.IS_ROWS_SELECTABLE = await this.getValueAsync(this.selectableRows);

    //
    this.detectChanges();
  }

  private async prepareDataSource() {
    //
    this.DATA = [];
    this.COLUMNS_DATA = [];
    this.DISPLAYED_COLUMNS = [];
    if (!this.properties) {
      return;
    }

    //
    const dataSource = await this.getValueAsync(this.properties.items);
    this.DATA = dataSource;

    //
    const isSelectableRows = await this.getValueAsync(this.selectableRows);
    const baseColumnsData = !isSelectableRows
      ? []
      : [
          {
            name: X_TABLE_SELECTION_HEADER_IDENTIFIER,
          } as XTableColumnOption<any>,
        ];
    this.COLUMNS_DATA = baseColumnsData.concat(this.properties.columns);

    //
    const displayedColumns = await Promise.all(
      this.COLUMNS_DATA.map(
        async (cd) => (await this.getValueAsync(cd.name)) as string
      )
    );
    this.DISPLAYED_COLUMNS = displayedColumns;

    //
    this.DATA_SOURCE.data = this.DATA;

    //
    this.detectChanges();
  }

  private async prepareSelection() {
    //
    const isMultipleSelection = await this.getValueAsync(this.allowMultiSelect);
    const initialSelectedValues = await this.getValueAsync(
      this.initialSelectedValues
    );

    //
    this.SELECTION = new SelectionModel<any>(
      isMultipleSelection,
      initialSelectedValues
    );

    //
    // notify event emitter when has initial selected values ...
    if (hasChild(initialSelectedValues)) {
      this.rowSelectionChanged.emit(initialSelectedValues);
    }

    //
    this.registerSelectionChangeHandler();

    //
    this.detectChanges();
  }

  private registerSelectionChangeHandler() {
    //
    // Unsubscribe Last ...
    if (this.SELECTION_SUBSCRIPTION) {
      this.SELECTION_SUBSCRIPTION.unsubscribe();
      this.SELECTION_SUBSCRIPTION = null;
    }

    //
    // Subscribe New ...
    this.SELECTION_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.SELECTION.changed.asObservable()
    ).subscribe(async () => {
      //
      await this.prepareActionOptions();
      this.rowSelectionChanged.emit(this.SELECTION.selected);
    });
  }

  private async preparePaginatorOptions() {
    //
    const options = await this.getValueAsync(this.paginatorOptions);
    const defaultOptions = this.defaultPaginatorOptions();
    const preparedOptions: XTablePaginatorOptions = {
      ...defaultOptions,
      ...options,
    };

    //
    if (options) {
      //
      if (!options.first) {
        preparedOptions.first = undefined;
      } else {
        //
        const first = options.first;

        //
        if (first.disabled) {
          const firstDisabled = await this.getValueAsync(first.disabled);
          preparedOptions.first.disabled =
            defaultOptions.first.disabled && firstDisabled;
        } else {
          preparedOptions.first.disabled = defaultOptions.first.disabled;
        }

        //
        if (!first.icon) {
          preparedOptions.first.icon = defaultOptions.first.icon;
        }

        //
        if (!first.title) {
          preparedOptions.first.title = defaultOptions.first.title;
        }
      }

      //
      if (!options.last) {
        preparedOptions.last = undefined;
      } else {
        //
        const last = options.last;

        //
        if (last.disabled) {
          const lastDisabled = await this.getValueAsync(last.disabled);
          preparedOptions.last.disabled =
            defaultOptions.last.disabled && lastDisabled;
        } else {
          preparedOptions.last.disabled = defaultOptions.last.disabled;
        }

        //
        if (!last.icon) {
          preparedOptions.last.icon = defaultOptions.last.icon;
        }

        //
        if (!last.title) {
          preparedOptions.last.title = defaultOptions.last.title;
        }
      }

      //
      if (!options.next) {
        preparedOptions.next = undefined;
      } else {
        //
        const next = options.next;

        //
        if (next.disabled) {
          const nextDisabled = await this.getValueAsync(next.disabled);
          preparedOptions.next.disabled =
            defaultOptions.next.disabled && nextDisabled;
        } else {
          preparedOptions.next.disabled = defaultOptions.next.disabled;
        }

        //
        if (!next.icon) {
          preparedOptions.next.icon = defaultOptions.next.icon;
        }

        //
        if (!next.title) {
          preparedOptions.next.title = defaultOptions.next.title;
        }
      }

      //
      if (!options.prev) {
        preparedOptions.prev = undefined;
      } else {
        //
        const prev = options.prev;

        //
        if (prev.disabled) {
          const prevDisabled = await this.getValueAsync(prev.disabled);
          preparedOptions.prev.disabled =
            defaultOptions.prev.disabled && prevDisabled;
        } else {
          preparedOptions.prev.disabled = defaultOptions.prev.disabled;
        }

        //
        if (!prev.icon) {
          preparedOptions.prev.icon = defaultOptions.prev.icon;
        }

        //
        if (!prev.title) {
          preparedOptions.prev.title = defaultOptions.prev.title;
        }
      }
    }

    //
    this.PAGINATOR_OPTIONS = preparedOptions;

    //
    this.detectChanges();
  }

  private async registerPaginatorHandlers() {
    //
    if (!this.paginator) {
      return;
    }

    //
    //#region Handle Paginator Ready ...
    //
    // Unsubscribe Last ...
    if (this.PAGINATOR_INIT_SUBSCRIPTION) {
      this.PAGINATOR_INIT_SUBSCRIPTION.unsubscribe();
      this.PAGINATOR_INIT_SUBSCRIPTION = null;
    }

    //
    // Subscribe New ...
    this.PAGINATOR_INIT_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.paginator.initialized
    ).subscribe(async () => {
      //
      await this.preparePaginatorOptions();

      //
      //#region Initial Page Info Reciever ...
      //
      this.PAGE_SIZE = this.paginator.pageSize;
      this.TOTAL_ITEMS = this.paginator.length;
      this.PAGE_SIZE = this.paginator.pageSize;
      this.CURRENT_PAGE = this.paginator.pageIndex + 1;

      //
      const totalPagesFloat = this.paginator.length / this.paginator.pageSize;
      this.TOTAL_PAGES = Math.floor(totalPagesFloat);

      //
      if (
        totalPagesFloat > this.TOTAL_PAGES &&
        totalPagesFloat < this.TOTAL_PAGES + 1
      ) {
        this.TOTAL_PAGES++;
      }
      //#endregion

      //
      this.detectChanges();
    });
    //#endregion

    //
    //#region Handle Page Changes ...
    //
    // Unsubscribe last ...
    if (this.PAGINATOR_PAGE_SUBSCRIPTION) {
      this.PAGINATOR_PAGE_SUBSCRIPTION.unsubscribe();
      this.PAGINATOR_PAGE_SUBSCRIPTION = null;
    }

    //
    // Subscribe New ...
    this.PAGINATOR_PAGE_SUBSCRIPTION = this.applyTakeUntilWrapper(
      this.paginator.page.asObservable()
    ).subscribe(async (res) => {
      //
      // ReNew Pagination Options ...
      await this.preparePaginatorOptions();

      //
      // Handle Current Page ...
      this.PAGE_SIZE = res.pageSize;
      this.TOTAL_ITEMS = res.length;
      this.PAGE_SIZE = res.pageSize;
      this.CURRENT_PAGE = res.pageIndex + 1;
      this.pageChanged.emit(this.CURRENT_PAGE);

      //
      //#region Handle Calculation of Total pages ...
      const totalPagesFloat = res.length / res.pageSize;
      this.TOTAL_PAGES = Math.floor(totalPagesFloat);

      //
      if (
        totalPagesFloat > this.TOTAL_PAGES &&
        totalPagesFloat < this.TOTAL_PAGES + 1
      ) {
        this.TOTAL_PAGES++;
      }
      //#endregion

      //
      this.detectChanges();
    });
    //#endregion
  }

  private defaultPaginatorOptions() {
    //
    const options: XTablePaginatorOptions = {
      pageSizes: this.config.pageSizes,
      pageSize: this.config.defaultPageSize,
      first: {
        title: 'first',
        icon: XIconNames.first,
        disabled: !this.paginator || !this.paginator.hasPreviousPage(),
      },
      last: {
        title: 'last',
        icon: XIconNames.last,
        disabled: !this.paginator || !this.paginator.hasNextPage(),
      },
      prev: {
        title: 'previous',
        icon: XIconNames.previous,
        disabled: !this.paginator || !this.paginator.hasPreviousPage(),
      },
      next: {
        title: 'next',
        icon: XIconNames.next,
        disabled: !this.paginator || !this.paginator.hasNextPage(),
      },
      pageInfoProvider: (
        locale: XLocale,
        currentPage: number,
        pageSize: number,
        totalPages: number
      ) => {
        //
        const info = `${currentPage}/${totalPages}`;
        return applyLocale(info, locale);
      },
    };

    //
    return options;
  }

  private async prepareActionOptions() {
    //
    let preparedOptions: XTableActionOptions = {};
    const options = await this.getValueAsync(this.actionOptions);
    const defaultOptions = this.defaultActionOptions();

    //
    if (!options) {
      preparedOptions = defaultOptions;
    } else {
      //
      if (options.start) {
        preparedOptions.start = [...options.start, ...defaultOptions.start];
      } else {
        preparedOptions.start = [...defaultOptions.start];
      }

      //
      if (options.end) {
        preparedOptions.end = [...defaultOptions.end, ...options.end];
      } else {
        preparedOptions.end = [...defaultOptions.end];
      }
    }

    //
    this.ACTION_OPTIONS = preparedOptions;

    //
    this.detectChanges();
  }

  private defaultActionOptions() {
    //
    const options: XTableActionOptions = {
      start: [
        {
          title: XResourceIDs.delete,
          icon: XIconNames.remove,
          color: XColor.Danger,
          disabled:
            this.uiDisabled ||
            this.loading ||
            !this.selection ||
            !hasChild(this.data) ||
            !hasChild(this.selection.selected),
          hidden: !this.isRowsSelectable,
          handler: (items: any) => {
            //
            this.managerService.dialogService.presentActionModal({
              header: XResourceIDs.are_you_sure,
              buttons: [],
              keyboardClose: true,
              backdropDismiss: true,
              hasOk: true,
              okHandler: () => {
                this.removeItems.emit(items);
              },
              hasCancel: true,
            });
          },
        },
      ],
      end: [
        {
          title: XResourceIDs.select_all,
          icon: XIconNames.select_all,
          color: XColor.Tertiary,
          disabled:
            this.uiDisabled ||
            this.loading ||
            !this.selection ||
            !hasChild(this.data) ||
            this.isAllSelected(),
          hidden: !this.isRowsSelectable,
          handler: () => {
            this.selectAll();
          },
        },
        {
          title: XResourceIDs.select_all_group,
          icon: XIconNames.select_all_group,
          color: XColor.Tertiary,
          disabled:
            this.uiDisabled ||
            this.loading ||
            !hasChild(this.dataSource.filteredData) ||
            this.isAllFilteredSelected(),
          hidden:
            !this.dataSource ||
            !this.isRowsSelectable ||
            !this.dataSource.filter,
          handler: () => {
            this.selectAllFilteredData();
          },
        },
        {
          title: XResourceIDs.deselect_all,
          icon: XIconNames.deselect_all,
          color: XColor.Tertiary,
          disabled:
            this.uiDisabled ||
            this.loading ||
            !this.selection ||
            !hasChild(this.data) ||
            !hasChild(this.selection.selected),
          hidden: !this.isRowsSelectable,
          handler: () => {
            this.deselectAll();
          },
        },
        {
          title: XResourceIDs.deselect_all_group,
          icon: XIconNames.deselect_all_group,
          color: XColor.Tertiary,
          disabled:
            this.uiDisabled ||
            this.loading ||
            !this.selection ||
            !hasChild(this.data) ||
            !this.isAllFilteredSelected() ||
            !hasChild(this.selection.selected),
          hidden:
            !this.isRowsSelectable ||
            !this.dataSource.filter ||
            !hasChild(this.dataSource.filteredData),
          handler: () => {
            this.deselectAllFilteredData();
          },
        },
        {
          title: XResourceIDs.inverse_selection,
          icon: XIconNames.inverse_selection,
          color: XColor.Tertiary,
          disabled:
            this.uiDisabled ||
            this.loading ||
            !this.selection ||
            !hasChild(this.data) ||
            !hasChild(this.selection.selected),
          hidden: !this.isRowsSelectable,
          handler: () => {
            this.inverseSelection();
          },
        },
      ],
    };

    //
    return options;
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-table\x-table.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XTableComponent } from './x-table.component';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XEmptyModule } from '../x-empty/x-empty.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XTableComponent],
  imports: [
    XCardModule,
    XIconModule,
    XEmptyModule,
    CommonModule,
    XBaseComponentModule,
  ],
  exports: [
    XCardModule,
    XIconModule,
    XEmptyModule,
    XTableComponent,
    XBaseComponentModule,
  ],
})
export class XTableModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-table\x-table.typings.ts

import {
  Inject,
  Input,
  NgZone,
  Output,
  Component,
  Renderer2,
  Injectable,
  ElementRef,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XLocale,
  XResourceIDs,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XCardEmptyContainer,
  IXCardEmptyContainer,
  XCardEmptyContainerComponent,
} from '../classes/i-x-card-empty.container';
import { concatMap, map } from 'rxjs/operators';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { XIconIdentifier } from '../x-icon/x-icon.typings';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export type XTableColumn<T> = keyof T;

export type XTableColumnIdentifier<T> = XTableColumn<T> | string;

export interface XTableColumnOption<T> {
  //
  hidden?: XStandardType<boolean>;
  name: XTableColumnIdentifier<T>;
  sortable?: XStandardType<boolean>;
  icon?: XStandardType<XIconIdentifier>;
  customColumn?: XStandardType<boolean>;
  title: XStandardType<XResourceIdentifier>;
  description?: XStandardType<XResourceIdentifier>;
}

export interface XTableConfig<T> {
  //
  items: XStandardType<T[]>;
  columns: XTableColumnOption<T>[];
}

export type XSortDirection = 'asc' | 'desc';

export enum XTableItemPosition {
  TOP = 'top',
  BOTTOM = 'bottom',
}

export type XTableItemPositionIdentifier = XTableItemPosition | string;

export const X_TABLE_SELECTION_HEADER_IDENTIFIER = 'selection';

export interface XTableActionButton {
  hidden?: XStandardType<boolean>;
  disabled?: XStandardType<boolean>;
  icon?: XStandardType<XIconIdentifier>;
  color?: XStandardType<XColorIdentifier>;
  title?: XStandardType<XResourceIdentifier>;
  handler?: (item?: any) => XStandardType<void>;
}

export interface XTablePaginatorOptions {
  pageSizes?: XStandardType<number[]>;
  pageSize?: XStandardType<number>;
  first?: XTableActionButton;
  last?: XTableActionButton;
  next?: XTableActionButton;
  prev?: XTableActionButton;
  pageInfoProvider?: (
    locale: XLocale,
    currentPage: number,
    pageSize: number,
    totalPages: number,
    totalItems: number
  ) => string;
}

export interface XTableActionOptions {
  end?: XTableActionButton[];
  start?: XTableActionButton[];
}

export enum XTableAction {
  //
  Refresh = 'refresh',

  //
  TOGGLE_SELECT_ALL = 'toggle-select-all',
  SELECT_ALL = 'select-all',
  DE_SELECT_ALL = 'deselect-all',
  INVERSE_SELECTION = 'inverse-selection',
  FIRST_PAGE = 'first-page',
  LAST_PAGE = 'last-page',
  NEXT_PAGE = 'next-page',
  PREVIOUS_PAGE = 'previous-page',
  TO_PAGE = 'to-page',
}

export type XTableActionIdentifier = XTableAction | string;

export interface XTableActionModel
  extends XBaseActionModel<XTableActionIdentifier> {}

//
//#region Inside Card/Empty Container ...
export interface IXTableContainer extends IXCardEmptyContainer {
  //
  //#region Other Props ...
  //
  actionProvider: Subject<XTableActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;
  foregroundColor: XStandardType<boolean>;

  canClearSort: XStandardType<boolean>;
  cellCssClass: XStandardType<XOneOrManyType<string>>;
  properties: XTableConfig<any>;
  cellTemplate: TemplateRef<any>;
  customCellTemplate: TemplateRef<any>;
  //#endregion

  //
  //#region Header Props ...
  //
  showHeader: XStandardType<boolean>;
  stickyHeader: XStandardType<boolean>;
  headerCellCssClass: XStandardType<XOneOrManyType<string>>;

  //
  headerColor: XStandardType<XColorIdentifier>;
  headerForegroundColor: XStandardType<boolean>;
  //#endregion

  //
  //#region Click Props ...
  clickableRows: XStandardType<boolean>;
  rowClicked: EventEmitter<any>;
  //#endregion

  //
  //#region Selection props ...
  selectableRows: XStandardType<boolean>;
  rowSelected: EventEmitter<any>;
  rowSelectionChanged: EventEmitter<any[]>;
  allowMultiSelect: XStandardType<boolean>;
  initialSelectedValues: XStandardType<any[]>;
  //#endregion

  //
  //#region Filter/Search Props ...
  showSearchBar: XStandardType<boolean>;
  searchBarPosition: XStandardType<XTableItemPositionIdentifier>;
  searchBarColor: XStandardType<XColorIdentifier>;
  searchTitle: XStandardType<XResourceIdentifier>;
  //#endregion

  //
  //#region Paginator Props ...
  showPaginator: XStandardType<boolean>;
  paginatorPosition: XStandardType<XTableItemPositionIdentifier>;
  paginatorColor: XStandardType<XColorIdentifier>;
  paginatorOptions: XStandardType<XTablePaginatorOptions>;
  pageChanged: EventEmitter<number>;
  //#endregion

  //
  //#region Action Props ...
  showActionBar: XStandardType<boolean>;
  actionBarPosition: XStandardType<XTableItemPositionIdentifier>;
  actionBarColor: XStandardType<XColorIdentifier>;
  actionOptions: XStandardType<XTableActionOptions>;
  removeItems: EventEmitter<any[]>;
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
export class XTableContainer
  extends XCardEmptyContainer
  implements Readonly<IXTableContainer>
{
  //
  //#region Props ...
  //
  //#region Other Props ...
  //
  readonly actionProvider: Subject<XTableActionModel> =
    new Subject<XTableActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;
  readonly foregroundColor: XStandardType<boolean> = false;

  readonly canClearSort: XStandardType<boolean> = true;
  readonly cellCssClass: XStandardType<XOneOrManyType<string>>;
  readonly properties: XTableConfig<any>;
  readonly cellTemplate: TemplateRef<any>;
  readonly customCellTemplate: TemplateRef<any>;
  //#endregion

  //
  //#region Header Props ...
  //
  readonly showHeader: XStandardType<boolean> = true;
  readonly stickyHeader: XStandardType<boolean> = true;
  readonly headerCellCssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly headerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly headerForegroundColor: XStandardType<boolean> = false;
  //#endregion

  //
  //#region Click Props ...
  readonly clickableRows: XStandardType<boolean> = false;
  readonly rowClicked: EventEmitter<any> = new EventEmitter<any>();
  //#endregion

  //
  //#region Selection props ...
  readonly selectableRows: XStandardType<boolean> = false;
  readonly rowSelected: EventEmitter<any> = new EventEmitter<any>();
  readonly rowSelectionChanged: EventEmitter<any[]> = new EventEmitter<any[]>();
  readonly allowMultiSelect: XStandardType<boolean> = true;
  readonly initialSelectedValues: XStandardType<any[]> = [];
  //#endregion

  //
  //#region Filter/Search Props ...
  readonly showSearchBar: XStandardType<boolean> = false;
  readonly searchBarPosition: XStandardType<XTableItemPositionIdentifier> =
    XTableItemPosition.TOP;
  readonly searchBarColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly searchTitle: XStandardType<XResourceIdentifier> =
    XResourceIDs.search;
  //#endregion

  //
  //#region Paginator Props ...
  readonly showPaginator: XStandardType<boolean>;
  readonly paginatorPosition: XStandardType<XTableItemPositionIdentifier> =
    XTableItemPosition.BOTTOM;
  readonly paginatorColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly paginatorOptions: XStandardType<XTablePaginatorOptions>;
  readonly pageChanged: EventEmitter<number> = new EventEmitter<number>();
  //#endregion

  //
  //#region Action Props ...
  readonly showActionBar: XStandardType<boolean>;
  readonly actionBarPosition: XStandardType<XTableItemPositionIdentifier> =
    XTableItemPosition.TOP;
  readonly actionBarColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly actionOptions: XStandardType<XTableActionOptions>;
  readonly removeItems: EventEmitter<any[]> = new EventEmitter<any[]>();
  //#endregion
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XTableContainerComponent
  extends XCardEmptyContainerComponent
  implements IXTableContainer
{
  //
  //#region Props ...
  //
  //#region Other Props ...
  @Input()
  actionProvider: Subject<XTableActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  canClearSort: XStandardType<boolean> = this.propertyProvider.canClearSort;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  foregroundColor: XStandardType<boolean> =
    this.propertyProvider.foregroundColor;

  @Input()
  cellCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cellCssClass;

  @Input()
  properties: XTableConfig<any> = this.propertyProvider.properties;

  @Input()
  cellTemplate: TemplateRef<any> = this.propertyProvider.cellTemplate;

  @Input()
  customCellTemplate: TemplateRef<any> =
    this.propertyProvider.customCellTemplate;
  //#endregion

  //
  //#region Header Props ...
  @Input()
  showHeader: XStandardType<boolean> = this.propertyProvider.showHeader;

  @Input()
  stickyHeader: XStandardType<boolean> = this.propertyProvider.stickyHeader;

  @Input()
  headerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerColor;

  @Input()
  headerForegroundColor: XStandardType<boolean> =
    this.propertyProvider.headerForegroundColor;

  @Input()
  headerCellCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.headerCellCssClass;
  //#endregion

  //
  //#region Click Props ...
  @Input()
  clickableRows: XStandardType<boolean> = this.propertyProvider.clickableRows;

  @Output()
  rowClicked: EventEmitter<any> = new EventEmitter();
  //#endregion

  //
  //#region Selection props ...
  @Input()
  selectableRows: XStandardType<boolean> = this.propertyProvider.selectableRows;

  @Output()
  rowSelected: EventEmitter<any> = new EventEmitter();

  @Output()
  rowSelectionChanged: EventEmitter<any[]> = new EventEmitter();

  @Input()
  allowMultiSelect: XStandardType<boolean> =
    this.propertyProvider.allowMultiSelect;

  @Input()
  initialSelectedValues: XStandardType<any[]> =
    this.propertyProvider.initialSelectedValues;
  //#endregion

  //
  //#region Filter/Search Props ...
  @Input()
  showSearchBar: XStandardType<boolean> = this.propertyProvider.showSearchBar;

  @Input()
  searchBarPosition: XStandardType<XTableItemPositionIdentifier> =
    this.propertyProvider.searchBarPosition;

  @Input()
  searchBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.searchBarColor;

  @Input()
  searchTitle: XStandardType<XResourceIdentifier> =
    this.propertyProvider.searchTitle;
  //#endregion

  //
  //#region Paginator Props ...
  @Input()
  showPaginator: XStandardType<boolean> = this.propertyProvider.showPaginator;

  @Input()
  paginatorPosition: XStandardType<XTableItemPositionIdentifier> =
    this.propertyProvider.paginatorPosition;

  @Input()
  paginatorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.paginatorColor;

  @Input()
  paginatorOptions: XStandardType<XTablePaginatorOptions> =
    this.propertyProvider.paginatorOptions;

  @Output()
  pageChanged: EventEmitter<number> = new EventEmitter();
  //#endregion

  //
  //#region Action Props ...
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  @Input()
  actionBarPosition: XStandardType<XTableItemPositionIdentifier> =
    this.propertyProvider.actionBarPosition;

  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionOptions: XStandardType<XTableActionOptions> =
    this.propertyProvider.actionOptions;

  @Output()
  removeItems: EventEmitter<any[]> = new EventEmitter();
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XTableContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXTableContainer
  ): Observable<IXTableContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            canClearSort: this.getValue(
              container ? container.canClearSort : this.canClearSort
            ),
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            color: this.getValue(container ? container.color : this.color),
            foregroundColor: this.getValue(
              container ? container.foregroundColor : this.foregroundColor
            ),
            cellCssClass: this.getValue(
              container ? container.cellCssClass : this.cellCssClass
            ),
            properties: this.getValue(
              container ? container.properties : this.properties
            ),
            cellTemplate: this.getValue(
              container ? container.cellTemplate : this.cellTemplate
            ),
            customCellTemplate: this.getValue(
              container ? container.customCellTemplate : this.customCellTemplate
            ),
            showHeader: this.getValue(
              container ? container.showHeader : this.showHeader
            ),
            stickyHeader: this.getValue(
              container ? container.stickyHeader : this.stickyHeader
            ),
            headerColor: this.getValue(
              container ? container.headerColor : this.headerColor
            ),
            headerForegroundColor: this.getValue(
              container
                ? container.headerForegroundColor
                : this.headerForegroundColor
            ),
            headerCellCssClass: this.getValue(
              container ? container.headerCellCssClass : this.headerCellCssClass
            ),
            clickableRows: this.getValue(
              container ? container.clickableRows : this.clickableRows
            ),
            selectableRows: this.getValue(
              container ? container.selectableRows : this.selectableRows
            ),
            allowMultiSelect: this.getValue(
              container ? container.allowMultiSelect : this.allowMultiSelect
            ),
            initialSelectedValues: this.getValue(
              container
                ? container.initialSelectedValues
                : this.initialSelectedValues
            ),
            showSearchBar: this.getValue(
              container ? container.showSearchBar : this.showSearchBar
            ),
            searchBarPosition: this.getValue(
              container ? container.searchBarPosition : this.searchBarPosition
            ),
            searchBarColor: this.getValue(
              container ? container.searchBarColor : this.searchBarColor
            ),
            searchTitle: this.getValue(
              container ? container.searchTitle : this.searchTitle
            ),
            showPaginator: this.getValue(
              container ? container.showPaginator : this.showPaginator
            ),
            paginatorPosition: this.getValue(
              container ? container.paginatorPosition : this.paginatorPosition
            ),
            paginatorColor: this.getValue(
              container ? container.paginatorColor : this.paginatorColor
            ),
            paginatorOptions: this.getValue(
              container ? container.paginatorOptions : this.paginatorOptions
            ),
            showActionBar: this.getValue(
              container ? container.showActionBar : this.showActionBar
            ),
            actionBarPosition: this.getValue(
              container ? container.actionBarPosition : this.actionBarPosition
            ),
            actionBarColor: this.getValue(
              container ? container.actionBarColor : this.actionBarColor
            ),
            actionOptions: this.getValue(
              container ? container.actionOptions : this.actionOptions
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            rowSelectionChanged: this.rowSelectionChanged,
            pageChanged: this.pageChanged,
            rowClicked: this.rowClicked,
            rowSelected: this.rowSelected,
            removeItems: this.removeItems,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tabs.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else tabsRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="tabsRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!--  Component Ref -->
<ng-template #tabsRef>
  <div
    xSwipable
    class="x-tabs x-wrapper"
    [supportedDirections]="2"
    (swipeEnd)="handleSwipeEnd()"
    (swipeStart)="handleSwipeStart()"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
    [swipeDisabled]="swipeDisabled() | async"
    [class.swipable]="!(swipeDisabled() | async)"
    [class.hide-header]="!(getValue(showHeader) | async)"
  >
    <mat-tab-group
      #group
      [(selectedIndex)]="selectedIndex"
      [color]="getValue(color) | async"
      (animationDone)="animationDone.emit()"
      [dynamicHeight]="isDynamicHeight() | async"
      [headerPosition]="getHeaderPosition() | async"
      [attr.mat-stretch-tabs]="isStretched() | async"
      [disableRipple]="getValue(disableRipple) | async"
      [attr.mat-align-tabs]="getTabAlignValue() | async"
      (selectedTabChange)="handleSelectedTabChanged($event)"
      [disablePagination]="getValue(disablePagination) | async"
      [animationDuration]="getValue(animationDuration) | async"
    >
      <mat-tab
        *ngFor="let tab of tabs"
        [disabled]="uiDisabled || (getValueStream(disablePagination) | async)"
      >
        <ng-template mat-tab-label>
          <div [xColor]="getValue(headerColor) | async" [isForeground]="false">
            <ng-container *ngTemplateOutlet="tab.label"></ng-container>
          </div>
        </ng-template>

        <ng-template matTabContent>
          <ng-container *ngTemplateOutlet="tab.content"></ng-container>
        </ng-template>
      </mat-tab>
    </mat-tab-group>
  </div>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tabs.component.scss

:host {
  //
  --tab-header-color: var(--x-color-primary);

  //
  --tab-indicator-color: var(--x-color-secondary);

  //
  --tab-paginator-foreground-color: var(--x-color-secondary-contrast);
  --tab-paginator-background-color: var(--x-color-secondary);

  //
  --active-tab-foreground-color: var(--x-color-secondary-contrast);
  --active-tab-background-color: var(--x-color-secondary);
}

:host ::ng-deep.mat-ink-bar {
  background-color: var(--tab-indicator-color) !important;
}

.x-wrapper {
  //
  width: inherit;
  height: inherit;
}

x-card {
  //
  width: inherit;
  height: inherit;
}

:host ::ng-deep .x-wrapper {
  .mat-tab-header {
    display: flex;
  }

  &.hide-header {
    .mat-tab-header {
      display: none;
    }
  }
}

:host ::ng-deep.swipable {
  //
  cursor: grab;

  //
  -webkit-user-select: none; /* Chrome all / Safari all */
  -moz-user-select: none; /* Firefox all */
  -ms-user-select: none; /* IE 10+ */
  user-select: none; /* Likely future */
}

:host ::ng-deep.mat-tab-label-active {
  opacity: 1 !important;

  //
  color: var(--active-tab-foreground-color) !important;
  background-color: var(--active-tab-background-color) !important;

  .mat-tab-label-content {
    //
    color: var(--active-tab-foreground-color) !important;
    background-color: var(--active-tab-background-color) !important;

    * {
      //
      color: var(--active-tab-foreground-color) !important;
      background-color: var(--active-tab-background-color) !important;
    }
  }
}

:host ::ng-deep .mat-tab-header-pagination {
  //
  background-color: var(--tab-paginator-background-color) !important;
}

:host ::ng-deep.mat-tab-header-pagination-chevron {
  border-color: var(--tab-paginator-foreground-color) !important;
}

:host ::ng-deep .mat-tab-labels {
  //
  background-color: var(--tab-header-color) !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tabs.component.ts

import {
  QueryList,
  Component,
  ContentChildren,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  nameof,
  parseXColor,
  isNullOrEmptyString,
  getXColorCssVariableName,
} from 'x-framework-core';
import {
  XTabAlign,
  XTabsAction,
  XTabsCssVars,
  XTabChangeEvent,
  XTabsActionModel,
  XTabsHeaderPosition,
  XTabsContainerComponent,
} from './x-tabs.typings';
import { map, concatMap } from 'rxjs/operators';
import { XTabComponent } from './x-tab/x-tab.component';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { of, Subject, Subscription, Observable } from 'rxjs';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-tabs',
  templateUrl: './x-tabs.component.html',
  styleUrls: ['./x-tabs.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XTabsComponent extends XTabsContainerComponent {
  //
  //#region Props ...
  //
  private TABS: QueryList<XTabComponent>;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  canPrev$ = new Subject<boolean>();
  canNext$ = new Subject<boolean>();

  //
  get tabs() {
    return this.TABS;
  }

  //
  @ContentChildren(XTabComponent)
  set tabs(s: QueryList<XTabComponent>) {
    this.TABS = s;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onInit() {
    super.onInit();

    //
    await this.handleSelectedIndex();
    await this.applyDefaultColors();
  }

  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    const hasSelectedIndex = changeKeys.includes(
      nameof<XTabsComponent>('selectedIndex')
    );
    if (hasSelectedIndex) {
      await this.handleSelectedIndex();
    }

    //
    //#region Color Changed ...
    //
    // Tab Header Color ...
    const isTabHeaderColorChanged = changeKeys.includes(
      nameof<XTabsComponent>('headerColor')
    );
    if (isTabHeaderColorChanged) {
      await this.applyTabHeaderColor();
    }

    //
    // Active Tab Color ...
    const isActiveTabColorChanged = changeKeys.includes(
      nameof<XTabsComponent>('activeTabColor')
    );
    if (isActiveTabColorChanged) {
      await this.applyActiveTabColor();
    }

    //
    // Tab Indicator Color ...
    const isTabIndicatorColorChanged = changeKeys.includes(
      nameof<XTabsComponent>('tabIndicatorColor')
    );
    if (isTabIndicatorColorChanged) {
      await this.applyTabIndicatorColor();
    }

    //
    // Tab Paginator Color ...
    const isTabPaginatorColorChanged = changeKeys.includes(
      nameof<XTabsComponent>('tabPaginatorColor')
    );
    if (isTabPaginatorColorChanged) {
      await this.applyTabPaginatorColor();
    }
    //#endregion

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XTabsComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Task Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  isDynamicHeight() {
    return this.getValue(this.dynamicHeight).pipe(
      map((isDynamic) => (isDynamic ? '' : undefined))
    );
  }

  isStretched() {
    return this.getValue(this.alignTabs).pipe(
      map((align) => (align === XTabAlign.STRETCH ? '' : undefined))
    );
  }

  getTabAlignValue() {
    return this.isStretched().pipe(
      concatMap((isStretched) => {
        if (isStretched) {
          return of(undefined);
        } else {
          return this.getValue(this.alignTabs);
        }
      })
    );
  }

  swipeDisabled() {
    return this.getValue(this.swipable).pipe(
      map((swipable) => !swipable || this.uiDisabled)
    );
  }

  getHeaderPosition(): Observable<'above' | 'below'> {
    return this.getValue(this.headerPosition).pipe(
      map((position) =>
        position === XTabsHeaderPosition.ABOVE ? 'above' : 'below'
      )
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleSelectedTabChanged(event: MatTabChangeEvent) {
    //
    const mEvent: XTabChangeEvent = {
      index: event.index,
      tab: this.tabs.toArray()[event.index],
    };

    //
    const canLoop = await this.canLoop();

    //
    if (event.index === 0) {
      //
      this.canPrev$.next(false || canLoop);
      this.canNext$.next(true);
    } else if (event.index === this.tabs.length - 1) {
      //
      this.canPrev$.next(true);
      this.canNext$.next(false || canLoop);
    } else if (event.index > 0 && event.index < this.tabs.length) {
      //
      this.canPrev$.next(true);
      this.canNext$.next(true);
    }

    //
    this.selectedTabChange.emit(mEvent);
    this.selectedIndexChange.emit(mEvent.index);
  }

  async handleSwipeEnd() {
    await this.prev();
  }

  async handleSwipeStart() {
    await this.next();
  }
  //#endregion

  //
  //#region Actions ...
  length() {
    return this.tabs ? this.tabs.length : 0;
  }

  getTab(index: number) {
    return this.checkIndex(index) ? this.tabs.toArray()[index] : null;
  }

  async next() {
    //
    const isLocked = await this.isLocked();
    if (isLocked) {
      return;
    }

    //
    const current = this.selectedIndex;
    const canLoop = await this.canLoop();
    const next = this.checkIndex(current + 1)
      ? current + 1
      : canLoop
      ? 0
      : current;

    //
    this.selectedIndex = next;
    this.detectChanges();
  }

  async prev() {
    //
    const isLocked = await this.isLocked();
    if (isLocked) {
      return;
    }

    //
    const current = this.selectedIndex;
    const canLoop = await this.canLoop();
    const prev = this.checkIndex(current - 1)
      ? current - 1
      : canLoop
      ? this.tabs.length - 1
      : current;

    //
    this.selectedIndex = prev;
    this.detectChanges();
  }

  async first() {
    //
    const isLocked = await this.isLocked();
    if (isLocked) {
      return;
    }

    //
    this.selectedIndex = 0;
    this.detectChanges();
  }

  async last() {
    //
    const isLocked = await this.isLocked();
    if (isLocked) {
      return;
    }

    //
    this.selectedIndex = this.tabs.length - 1;
    this.detectChanges();
  }

  async to(index: number) {
    //
    const isLocked = await this.isLocked();
    if (isLocked) {
      return;
    }

    //
    if (!this.checkIndex(index)) {
      return;
    }

    //
    this.selectedIndex = index;
    this.detectChanges();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XTabsActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XTabsAction.FIRST:
            await this.first();
            break;

          //
          case XTabsAction.LAST:
            await this.last();
            break;

          //
          case XTabsAction.NEXT:
            await this.next();
            break;

          //
          case XTabsAction.PREV:
            await this.prev();
            break;

          //
          case XTabsAction.TO:
            if (model.payload) {
              await this.to(model.payload);
            }
            break;

          //
          case XTabsAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private checkIndex(index: number) {
    return this.tabs && index < this.tabs.length && index >= 0;
  }

  private async canLoop() {
    //
    const result = await this.getValueAsync(this.loopTabs);
    return result || false;
  }

  private async isLocked() {
    //
    const result = await this.getValueAsync(this.lock);
    return result || false;
  }

  private async handleSelectedIndex() {
    //
    const startIndex = await this.getValueAsync(this.selectedIndex);

    //
    if (!this.checkIndex(startIndex)) {
      //
      this.selectedIndex = 0;
      return;
    }

    //
    this.selectedIndex = startIndex;
  }

  //
  //#region Color Appliers ...
  private async applyDefaultColors() {
    //
    await this.applyActiveTabColor();
    await this.applyTabHeaderColor();
    await this.applyTabPaginatorColor();
    await this.applyTabIndicatorColor();
  }

  private async applyTabHeaderColor() {
    //
    const color = await this.getValueAsync(this.headerColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XTabsCssVars.TabHeaderColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyActiveTabColor() {
    //
    const color = await this.getValueAsync(this.activeTabColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XTabsCssVars.ActiveTabForegroundColor, `var(${fgColor})`);
    this.setCssVar(XTabsCssVars.ActiveTabBackgroundColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyTabIndicatorColor() {
    //
    const color = await this.getValueAsync(this.tabIndicatorColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XTabsCssVars.TabIndicatorColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }

  private async applyTabPaginatorColor() {
    //
    const color = await this.getValueAsync(this.tabPaginatorColor);
    if (!color) {
      return;
    }

    //
    const colorDescriptor = parseXColor(color);
    if (!colorDescriptor) {
      return;
    }

    //
    const fgColor = getXColorCssVariableName(
      colorDescriptor.name,
      true,
      colorDescriptor.brightnessLevel
    );
    const bgColor = getXColorCssVariableName(
      colorDescriptor.name,
      false,
      colorDescriptor.brightnessLevel
    );
    if (isNullOrEmptyString(fgColor) || isNullOrEmptyString(bgColor)) {
      return;
    }

    //
    this.setCssVar(XTabsCssVars.TabPaginatorForegroundColor, `var(${fgColor})`);
    this.setCssVar(XTabsCssVars.TabPaginatorBackgroundColor, `var(${bgColor})`);

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tabs.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XTabsComponent } from './x-tabs.component';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XTabComponent } from './x-tab/x-tab.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XTabsContainerComponent } from './x-tabs.typings';

@NgModule({
  declarations: [XTabsComponent, XTabComponent, XTabsContainerComponent],
  imports: [CommonModule, XBaseComponentModule, XIconModule, XCardModule],
  exports: [
    XIconModule,
    XCardModule,
    XTabComponent,
    XTabsComponent,
    XBaseComponentModule
  ]
})
export class XTabsModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tabs.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Renderer2,
  Component,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { XTabComponent } from './x-tab/x-tab.component';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XTabsHeaderPosition {
  ABOVE = 'above',
  BELOW = 'below',
}

export type XTabsHeaderPositionIdentifier = XTabsHeaderPosition | string;

export interface XTabChangeEvent extends Pick<MatTabChangeEvent, 'index'> {
  tab: XTabComponent;
}

export enum XTabAlign {
  START = 'start',
  CENTER = 'center',
  END = 'end',
  STRETCH = 'stretch',
}

export type XTabAlignIdentifier = XTabAlign | string;

export enum XTabsAction {
  FIRST = 'first',
  LAST = 'last',
  PREV = 'prev',
  NEXT = 'next',
  TO = 'to',

  //
  Refresh = 'refresh',
}

export type XTabsActionIdentifier = XTabsAction | string;

export interface XTabsActionModel
  extends XBaseActionModel<XTabsActionIdentifier> {}

export enum XTabsCssVars {
  //
  TabHeaderColor = 'tab-header-color',

  //
  TabIndicatorColor = 'tab-indicator-color',

  //
  ActiveTabForegroundColor = 'active-tab-foreground-color',
  ActiveTabBackgroundColor = 'active-tab-background-color',

  //
  TabPaginatorForegroundColor = 'tab-paginator-foreground-color',
  TabPaginatorBackgroundColor = 'tab-paginator-background-color',
}

export type XTabsCssVarsIdentifier = XTabsCssVars | string;

//
//#region Inside Card Container ...
export interface IXTabsContainer extends IXComponentCardContainer {
  //
  actionProvider: Subject<XTabsActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  selectedIndex: number;
  selectedIndexChange: EventEmitter<number>;

  //
  showHeader: XStandardType<boolean>;
  disablePagination: XStandardType<boolean>;
  alignTabs: XStandardType<XTabAlignIdentifier>;
  dynamicHeight: XStandardType<boolean>;
  animationDuration: XStandardType<string>;
  disableRipple: XStandardType<boolean>;
  headerPosition: XStandardType<XTabsHeaderPositionIdentifier>;

  //
  loopTabs: XStandardType<boolean>;
  swipable: XStandardType<boolean>;
  animationDone: EventEmitter<void>;

  //
  lock: XStandardType<boolean>;

  //
  color: XStandardType<XColorIdentifier>;
  headerColor: XStandardType<XColorIdentifier>;
  activeTabColor: XStandardType<XColorIdentifier>;
  tabIndicatorColor: XStandardType<XColorIdentifier>;
  tabPaginatorColor: XStandardType<XColorIdentifier>;

  //
  selectedTabChange: EventEmitter<XTabChangeEvent>;
}

@Injectable({
  providedIn: 'root',
})
export class XTabsContainer
  extends XComponentCardContainer
  implements Readonly<IXTabsContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XTabsActionModel> =
    new Subject<XTabsActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly selectedIndex: number = 0;
  readonly selectedIndexChange: EventEmitter<number> =
    new EventEmitter<number>();

  //
  readonly showHeader: XStandardType<boolean> = true;
  readonly disablePagination: XStandardType<boolean> = false;
  readonly alignTabs: XStandardType<XTabAlignIdentifier> = XTabAlign.START;
  readonly dynamicHeight: XStandardType<boolean> = false;
  readonly disableRipple: XStandardType<boolean> = false;
  readonly headerPosition: XStandardType<XTabsHeaderPositionIdentifier> =
    XTabsHeaderPosition.ABOVE;

  //
  readonly loopTabs: XStandardType<boolean> = true;
  readonly swipable: XStandardType<boolean> = false;
  readonly animationDuration: XStandardType<string> = '500ms';

  //
  readonly lock: XStandardType<boolean> = false;

  //
  readonly color: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Transparent;
  readonly headerColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Primary;
  readonly activeTabColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Secondary;
  readonly tabIndicatorColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.SecondaryTint;
  readonly tabPaginatorColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.SecondaryShade;

  //
  readonly animationDone: EventEmitter<void> = new EventEmitter();
  readonly selectedTabChange: EventEmitter<XTabChangeEvent> =
    new EventEmitter<XTabChangeEvent>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export class XTabsContainerComponent
  extends XComponentCardContainerComponent
  implements IXTabsContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XTabsActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  selectedIndex: number = this.propertyProvider.selectedIndex;

  @Output()
  selectedIndexChange: EventEmitter<number> = new EventEmitter();

  //
  @Input()
  showHeader: XStandardType<boolean> = this.propertyProvider.showHeader;

  @Input()
  disablePagination: XStandardType<boolean> =
    this.propertyProvider.disablePagination;

  @Input()
  alignTabs: XStandardType<XTabAlignIdentifier> =
    this.propertyProvider.alignTabs;

  @Input()
  dynamicHeight: XStandardType<boolean> = this.propertyProvider.dynamicHeight;

  @Input()
  disableRipple: XStandardType<boolean> = this.propertyProvider.disableRipple;

  @Input()
  headerPosition: XStandardType<XTabsHeaderPositionIdentifier> =
    this.propertyProvider.headerPosition;

  //
  @Input()
  loopTabs: XStandardType<boolean> = this.propertyProvider.loopTabs;

  @Input()
  swipable: XStandardType<boolean> = this.propertyProvider.swipable;

  @Input()
  animationDuration: XStandardType<string> =
    this.propertyProvider.animationDuration;

  //
  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  @Input()
  headerColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.headerColor;

  @Input()
  activeTabColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.activeTabColor;

  @Input()
  tabIndicatorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.tabIndicatorColor;

  @Input()
  tabPaginatorColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.tabPaginatorColor;

  //
  @Output()
  animationDone: EventEmitter<void> = new EventEmitter();

  @Output()
  selectedTabChange: EventEmitter<XTabChangeEvent> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XTabsContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXTabsContainer
  ): Observable<IXTabsContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),
            selectedIndex: this.getValue(
              container ? container.selectedIndex : this.selectedIndex
            ),
            showHeader: this.getValue(
              container ? container.showHeader : this.showHeader
            ),
            disablePagination: this.getValue(
              container ? container.disablePagination : this.disablePagination
            ),
            alignTabs: this.getValue(
              container ? container.alignTabs : this.alignTabs
            ),
            dynamicHeight: this.getValue(
              container ? container.dynamicHeight : this.dynamicHeight
            ),
            animationDuration: this.getValue(
              container ? container.animationDuration : this.animationDuration
            ),
            disableRipple: this.getValue(
              container ? container.disableRipple : this.disableRipple
            ),
            headerPosition: this.getValue(
              container ? container.headerPosition : this.headerPosition
            ),
            loopTabs: this.getValue(
              container ? container.loopTabs : this.loopTabs
            ),
            swipable: this.getValue(
              container ? container.swipable : this.swipable
            ),
            lock: this.getValue(container ? container.lock : this.lock),
            color: this.getValue(container ? container.color : this.color),
            headerColor: this.getValue(
              container ? container.headerColor : this.headerColor
            ),
            activeTabColor: this.getValue(
              container ? container.activeTabColor : this.activeTabColor
            ),
            tabIndicatorColor: this.getValue(
              container ? container.tabIndicatorColor : this.tabIndicatorColor
            ),
            tabPaginatorColor: this.getValue(
              container ? container.tabPaginatorColor : this.tabPaginatorColor
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            animationDone: this.animationDone,
            selectedTabChange: this.selectedTabChange,
            selectedIndexChange: this.selectedIndexChange,
          };
        })
      );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tab\x-tab.component.html

<ng-template #label>
  <ng-content select="[x-tab-label]"></ng-content>
</ng-template>

<ng-template #content>
  <ng-content></ng-content>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tab\x-tab.component.scss

.full-width {
  width: 100%;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tab\x-tab.component.ts

import {
  Component,
  ViewChild,
  TemplateRef,
  ChangeDetectionStrategy,
} from '@angular/core';
import {
  XTabAction,
  XTabActionModel,
  XTabContainerComponent,
} from './x-tab.typings';
import { nameof } from 'x-framework-core';
import { Subject, Subscription } from 'rxjs';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-tab',
  templateUrl: './x-tab.component.html',
  styleUrls: ['./x-tab.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XTabComponent extends XTabContainerComponent {
  //
  //#region Props ...
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  @ViewChild('label', { static: false })
  label!: TemplateRef<any>;

  //
  @ViewChild('content', { static: false })
  content!: TemplateRef<any>;
  //#endregion

  //
  //#region LifeCycles ...
  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XTabComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XTabActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XTabAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tabs\x-tab\x-tab.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Renderer2,
  ElementRef,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../../classes/i-x-base.container';
import { forkJoin, Observable, Subject } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { Component, Injectable } from '@angular/core';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';

export enum XTabAction {
  Refresh = 'refresh',
}

export type XTabActionIdentifier = XTabAction | string;

export interface XTabActionModel
  extends XBaseActionModel<XTabActionIdentifier> {}

//
//#region Container ...
export interface IXTabContainer extends IXBaseContainer {
  actionProvider: Subject<XTabActionModel>;
}

@Injectable({
  providedIn: 'root'
})
export class XTabContainer
  extends XBaseContainer
  implements Readonly<IXTabContainer>
{
  //
  //#region Props ...
  readonly actionProvider: Subject<XTabActionModel> =
    new Subject<XTabActionModel>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XTabContainerComponent
  extends XBaseContainerComponent
  implements IXTabContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XTabActionModel> =
    this.propertyProvider.actionProvider;
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XTabContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(container?: IXTabContainer): Observable<IXTabContainer> {
    return forkJoin({
      //
    }).pipe(
      map((data) => {
        return {
          //
          // ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme-typings.ts

//
//#region Form Interfaces ...
export interface XThemeBaseFormModel {
    name: string;
  }
  
  export interface XThemeDarkFormModel {
    //
    //#region Dark ...
    darkXColorPrimary: string;
    darkXColorPrimaryContrast: string;
    darkXColorPrimaryShade: string;
    darkXColorPrimaryShadeContrast: string;
    darkXColorPrimaryTint: string;
    darkXColorPrimaryTintContrast: string;
    darkXColorSecondary: string;
    darkXColorSecondaryContrast: string;
    darkXColorSecondaryShade: string;
    darkXColorSecondaryShadeContrast: string;
    darkXColorSecondaryTint: string;
    darkXColorSecondaryTintContrast: string;
    darkXColorTertiary: string;
    darkXColorTertiaryContrast: string;
    darkXColorTertiaryShade: string;
    darkXColorTertiaryShadeContrast: string;
    darkXColorTertiaryTint: string;
    darkXColorTertiaryTintContrast: string;
    darkXColorSuccess: string;
    darkXColorSuccessContrast: string;
    darkXColorSuccessShade: string;
    darkXColorSuccessShadeContrast: string;
    darkXColorSuccessTint: string;
    darkXColorSuccessTintContrast: string;
    darkXColorWarning: string;
    darkXColorWarningContrast: string;
    darkXColorWarningShade: string;
    darkXColorWarningShadeContrast: string;
    darkXColorWarningTint: string;
    darkXColorWarningTintContrast: string;
    darkXColorDanger: string;
    darkXColorDangerContrast: string;
    darkXColorDangerShade: string;
    darkXColorDangerShadeContrast: string;
    darkXColorDangerTint: string;
    darkXColorDangerTintContrast: string;
    darkXColorDark: string;
    darkXColorDarkContrast: string;
    darkXColorDarkShade: string;
    darkXColorDarkShadeContrast: string;
    darkXColorDarkTint: string;
    darkXColorDarkTintContrast: string;
    darkXColorMedium: string;
    darkXColorMediumContrast: string;
    darkXColorMediumShade: string;
    darkXColorMediumShadeContrast: string;
    darkXColorMediumTint: string;
    darkXColorMediumTintContrast: string;
    darkXColorLight: string;
    darkXColorLightContrast: string;
    darkXColorLightShade: string;
    darkXColorLightShadeContrast: string;
    darkXColorLightTint: string;
    darkXColorLightTintContrast: string;
    //#endregion
  }
  
  export interface XThemeLightFormModel {
    //
    //#region Light ...
    lightXColorPrimary: string;
    lightXColorPrimaryContrast: string;
    lightXColorPrimaryShade: string;
    lightXColorPrimaryShadeContrast: string;
    lightXColorPrimaryTint: string;
    lightXColorPrimaryTintContrast: string;
    lightXColorSecondary: string;
    lightXColorSecondaryContrast: string;
    lightXColorSecondaryShade: string;
    lightXColorSecondaryShadeContrast: string;
    lightXColorSecondaryTint: string;
    lightXColorSecondaryTintContrast: string;
    lightXColorTertiary: string;
    lightXColorTertiaryContrast: string;
    lightXColorTertiaryShade: string;
    lightXColorTertiaryShadeContrast: string;
    lightXColorTertiaryTint: string;
    lightXColorTertiaryTintContrast: string;
    lightXColorSuccess: string;
    lightXColorSuccessContrast: string;
    lightXColorSuccessShade: string;
    lightXColorSuccessShadeContrast: string;
    lightXColorSuccessTint: string;
    lightXColorSuccessTintContrast: string;
    lightXColorWarning: string;
    lightXColorWarningContrast: string;
    lightXColorWarningShade: string;
    lightXColorWarningShadeContrast: string;
    lightXColorWarningTint: string;
    lightXColorWarningTintContrast: string;
    lightXColorDanger: string;
    lightXColorDangerContrast: string;
    lightXColorDangerShade: string;
    lightXColorDangerShadeContrast: string;
    lightXColorDangerTint: string;
    lightXColorDangerTintContrast: string;
    lightXColorDark: string;
    lightXColorDarkContrast: string;
    lightXColorDarkShade: string;
    lightXColorDarkShadeContrast: string;
    lightXColorDarkTint: string;
    lightXColorDarkTintContrast: string;
    lightXColorMedium: string;
    lightXColorMediumContrast: string;
    lightXColorMediumShade: string;
    lightXColorMediumShadeContrast: string;
    lightXColorMediumTint: string;
    lightXColorMediumTintContrast: string;
    lightXColorLight: string;
    lightXColorLightContrast: string;
    lightXColorLightShade: string;
    lightXColorLightShadeContrast: string;
    lightXColorLightTint: string;
    lightXColorLightTintContrast: string;
    //#endregion
  }
  
  export interface XThemeFormModel
    extends XThemeBaseFormModel,
      XThemeDarkFormModel,
      XThemeLightFormModel {}
  //#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XTabsModule } from '../x-tabs/x-tabs.module';
import { XListModule } from '../x-list/x-list.module';
import { XFormModule } from '../x-form/x-form.module';
import { XColorModule } from '../x-color/x-color.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';
import { XColorPickerModule } from '../x-color-picker/x-color-picker.module';
import { XThemeManagerComponent } from './x-theme-manager/x-theme-manager.component';
import { XModelPresenterModule } from '../x-model-presenter/x-model-presenter.module';

@NgModule({
  declarations: [XThemeManagerComponent],
  imports: [
    XFormModule,
    XIconModule,
    XCardModule,
    XTabsModule,
    XListModule,
    XColorModule,
    CommonModule,
    XColorPickerModule,
    XBaseComponentModule,
    XModelPresenterModule,
  ],
  exports: [
    XFormModule,
    XIconModule,
    XCardModule,
    XTabsModule,
    XListModule,
    XColorModule,
    CommonModule,
    XColorPickerModule,
    XBaseComponentModule,
    XModelPresenterModule,
    XThemeManagerComponent,
  ],
  providers: [],
})
export class XThemeModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme.tools.ts

import { XThemePack, XThemeType } from 'x-framework-core';
import { XThemeBaseFormModel, XThemeDarkFormModel, XThemeFormModel, XThemeLightFormModel } from './x-theme-typings';

/**
 * convert to XThemeFormModel
 *
 * @param theme an instance of XThemePack ...
 */
export function themeToForm(theme: XThemePack) {
  //
  if (!theme) {
    return getEmptyThemeFormModel();
  }

  //
  return {
    name: theme.name,

    //
    //#region Dark ...
    darkXColorPrimary: theme.dark.colors.XColorPrimary || '',
    darkXColorPrimaryContrast: theme.dark.colors.XColorPrimaryContrast || '',
    darkXColorPrimaryShade: theme.dark.colors.XColorPrimaryShade || '',
    darkXColorPrimaryShadeContrast:
      theme.dark.colors.XColorPrimaryShadeContrast || '',
    darkXColorPrimaryTint: theme.dark.colors.XColorPrimaryTint || '',
    darkXColorPrimaryTintContrast:
      theme.dark.colors.XColorPrimaryTintContrast || '',
    darkXColorSecondary: theme.dark.colors.XColorSecondary || '',
    darkXColorSecondaryContrast:
      theme.dark.colors.XColorSecondaryContrast || '',
    darkXColorSecondaryShade: theme.dark.colors.XColorSecondaryShade || '',
    darkXColorSecondaryShadeContrast:
      theme.dark.colors.XColorSecondaryShadeContrast || '',
    darkXColorSecondaryTint: theme.dark.colors.XColorSecondaryTint || '',
    darkXColorSecondaryTintContrast:
      theme.dark.colors.XColorSecondaryTintContrast || '',
    darkXColorTertiary: theme.dark.colors.XColorTertiary || '',
    darkXColorTertiaryContrast: theme.dark.colors.XColorTertiaryContrast || '',
    darkXColorTertiaryShade: theme.dark.colors.XColorTertiaryShade || '',
    darkXColorTertiaryShadeContrast:
      theme.dark.colors.XColorTertiaryShadeContrast || '',
    darkXColorTertiaryTint: theme.dark.colors.XColorTertiaryTint || '',
    darkXColorTertiaryTintContrast:
      theme.dark.colors.XColorTertiaryTintContrast || '',
    darkXColorSuccess: theme.dark.colors.XColorSuccess || '',
    darkXColorSuccessContrast: theme.dark.colors.XColorSuccessContrast || '',
    darkXColorSuccessShade: theme.dark.colors.XColorSuccessShade || '',
    darkXColorSuccessShadeContrast:
      theme.dark.colors.XColorSuccessShadeContrast || '',
    darkXColorSuccessTint: theme.dark.colors.XColorSuccessTint || '',
    darkXColorSuccessTintContrast:
      theme.dark.colors.XColorSuccessTintContrast || '',
    darkXColorWarning: theme.dark.colors.XColorWarning || '',
    darkXColorWarningContrast: theme.dark.colors.XColorWarningContrast || '',
    darkXColorWarningShade: theme.dark.colors.XColorWarningShade || '',
    darkXColorWarningShadeContrast:
      theme.dark.colors.XColorWarningShadeContrast || '',
    darkXColorWarningTint: theme.dark.colors.XColorWarningTint || '',
    darkXColorWarningTintContrast:
      theme.dark.colors.XColorWarningTintContrast || '',
    darkXColorDanger: theme.dark.colors.XColorDanger || '',
    darkXColorDangerContrast: theme.dark.colors.XColorDangerContrast || '',
    darkXColorDangerShade: theme.dark.colors.XColorDangerShade || '',
    darkXColorDangerShadeContrast:
      theme.dark.colors.XColorDangerShadeContrast || '',
    darkXColorDangerTint: theme.dark.colors.XColorDangerTint || '',
    darkXColorDangerTintContrast:
      theme.dark.colors.XColorDangerTintContrast || '',
    darkXColorDark: theme.dark.colors.XColorDark || '',
    darkXColorDarkContrast: theme.dark.colors.XColorDarkContrast || '',
    darkXColorDarkShade: theme.dark.colors.XColorDarkShade || '',
    darkXColorDarkShadeContrast:
      theme.dark.colors.XColorDarkShadeContrast || '',
    darkXColorDarkTint: theme.dark.colors.XColorDarkTint || '',
    darkXColorDarkTintContrast: theme.dark.colors.XColorDarkTintContrast || '',
    darkXColorMedium: theme.dark.colors.XColorMedium || '',
    darkXColorMediumContrast: theme.dark.colors.XColorMediumContrast || '',
    darkXColorMediumShade: theme.dark.colors.XColorMediumShade || '',
    darkXColorMediumShadeContrast:
      theme.dark.colors.XColorMediumShadeContrast || '',
    darkXColorMediumTint: theme.dark.colors.XColorMediumTint || '',
    darkXColorMediumTintContrast:
      theme.dark.colors.XColorMediumTintContrast || '',
    darkXColorLight: theme.dark.colors.XColorLight || '',
    darkXColorLightContrast: theme.dark.colors.XColorLightContrast || '',
    darkXColorLightShade: theme.dark.colors.XColorLightShade || '',
    darkXColorLightShadeContrast:
      theme.dark.colors.XColorLightShadeContrast || '',
    darkXColorLightTint: theme.dark.colors.XColorLightTint || '',
    darkXColorLightTintContrast:
      theme.dark.colors.XColorLightTintContrast || '',
    //#endregion

    //
    //#region Light ...
    lightXColorPrimary: theme.light.colors.XColorPrimary || '',
    lightXColorPrimaryContrast: theme.light.colors.XColorPrimaryContrast || '',
    lightXColorPrimaryShade: theme.light.colors.XColorPrimaryShade || '',
    lightXColorPrimaryShadeContrast:
      theme.light.colors.XColorPrimaryShadeContrast || '',
    lightXColorPrimaryTint: theme.light.colors.XColorPrimaryTint || '',
    lightXColorPrimaryTintContrast:
      theme.light.colors.XColorPrimaryTintContrast || '',
    lightXColorSecondary: theme.light.colors.XColorSecondary || '',
    lightXColorSecondaryContrast:
      theme.light.colors.XColorSecondaryContrast || '',
    lightXColorSecondaryShade: theme.light.colors.XColorSecondaryShade || '',
    lightXColorSecondaryShadeContrast:
      theme.light.colors.XColorSecondaryShadeContrast || '',
    lightXColorSecondaryTint: theme.light.colors.XColorSecondaryTint || '',
    lightXColorSecondaryTintContrast:
      theme.light.colors.XColorSecondaryTintContrast || '',
    lightXColorTertiary: theme.light.colors.XColorTertiary || '',
    lightXColorTertiaryContrast:
      theme.light.colors.XColorTertiaryContrast || '',
    lightXColorTertiaryShade: theme.light.colors.XColorTertiaryShade || '',
    lightXColorTertiaryShadeContrast:
      theme.light.colors.XColorTertiaryShadeContrast || '',
    lightXColorTertiaryTint: theme.light.colors.XColorTertiaryTint || '',
    lightXColorTertiaryTintContrast:
      theme.light.colors.XColorTertiaryTintContrast || '',
    lightXColorSuccess: theme.light.colors.XColorSuccess || '',
    lightXColorSuccessContrast: theme.light.colors.XColorSuccessContrast || '',
    lightXColorSuccessShade: theme.light.colors.XColorSuccessShade || '',
    lightXColorSuccessShadeContrast:
      theme.light.colors.XColorSuccessShadeContrast || '',
    lightXColorSuccessTint: theme.light.colors.XColorSuccessTint || '',
    lightXColorSuccessTintContrast:
      theme.light.colors.XColorSuccessTintContrast || '',
    lightXColorWarning: theme.light.colors.XColorWarning || '',
    lightXColorWarningContrast: theme.light.colors.XColorWarningContrast || '',
    lightXColorWarningShade: theme.light.colors.XColorWarningShade || '',
    lightXColorWarningShadeContrast:
      theme.light.colors.XColorWarningShadeContrast || '',
    lightXColorWarningTint: theme.light.colors.XColorWarningTint || '',
    lightXColorWarningTintContrast:
      theme.light.colors.XColorWarningTintContrast || '',
    lightXColorDanger: theme.light.colors.XColorDanger || '',
    lightXColorDangerContrast: theme.light.colors.XColorDangerContrast || '',
    lightXColorDangerShade: theme.light.colors.XColorDangerShade || '',
    lightXColorDangerShadeContrast:
      theme.light.colors.XColorDangerShadeContrast || '',
    lightXColorDangerTint: theme.light.colors.XColorDangerTint || '',
    lightXColorDangerTintContrast:
      theme.light.colors.XColorDangerTintContrast || '',
    lightXColorDark: theme.light.colors.XColorDark || '',
    lightXColorDarkContrast: theme.light.colors.XColorDarkContrast || '',
    lightXColorDarkShade: theme.light.colors.XColorDarkShade || '',
    lightXColorDarkShadeContrast:
      theme.light.colors.XColorDarkShadeContrast || '',
    lightXColorDarkTint: theme.light.colors.XColorDarkTint || '',
    lightXColorDarkTintContrast:
      theme.light.colors.XColorDarkTintContrast || '',
    lightXColorMedium: theme.light.colors.XColorMedium || '',
    lightXColorMediumContrast: theme.light.colors.XColorMediumContrast || '',
    lightXColorMediumShade: theme.light.colors.XColorMediumShade || '',
    lightXColorMediumShadeContrast:
      theme.light.colors.XColorMediumShadeContrast || '',
    lightXColorMediumTint: theme.light.colors.XColorMediumTint || '',
    lightXColorMediumTintContrast:
      theme.light.colors.XColorMediumTintContrast || '',
    lightXColorLight: theme.light.colors.XColorLight || '',
    lightXColorLightContrast: theme.light.colors.XColorLightContrast || '',
    lightXColorLightShade: theme.light.colors.XColorLightShade || '',
    lightXColorLightShadeContrast:
      theme.light.colors.XColorLightShadeContrast || '',
    lightXColorLightTint: theme.light.colors.XColorLightTint || '',
    lightXColorLightTintContrast:
      theme.light.colors.XColorLightTintContrast || '',
    //#endregion
  } as XThemeFormModel;
}

/**
 * convert to XThemePack
 *
 * @param form an instance of XThemeFormModel ...
 */
export function formToTheme(form: XThemeFormModel) {
  //
  if (!form) {
    return getEmptyThemePack();
  }

  //
  return {
    //
    name: form.name || '',

    //
    // Dark ...
    dark: {
      type: XThemeType.Dark,
      colors: {
        XColorPrimary: form.darkXColorPrimary || '',
        XColorPrimaryContrast: form.darkXColorPrimaryContrast || '',
        XColorPrimaryShade: form.darkXColorPrimaryShade || '',
        XColorPrimaryShadeContrast: form.darkXColorPrimaryShadeContrast || '',
        XColorPrimaryTint: form.darkXColorPrimaryTint || '',
        XColorPrimaryTintContrast: form.darkXColorPrimaryTintContrast || '',
        XColorSecondary: form.darkXColorSecondary || '',
        XColorSecondaryContrast: form.darkXColorSecondaryContrast || '',
        XColorSecondaryShade: form.darkXColorSecondaryShade || '',
        XColorSecondaryShadeContrast:
          form.darkXColorSecondaryShadeContrast || '',
        XColorSecondaryTint: form.darkXColorSecondaryTint || '',
        XColorSecondaryTintContrast: form.darkXColorSecondaryTintContrast || '',
        XColorTertiary: form.darkXColorTertiary || '',
        XColorTertiaryContrast: form.darkXColorTertiaryContrast || '',
        XColorTertiaryShade: form.darkXColorTertiaryShade || '',
        XColorTertiaryShadeContrast: form.darkXColorTertiaryShadeContrast || '',
        XColorTertiaryTint: form.darkXColorTertiaryTint || '',
        XColorTertiaryTintContrast: form.darkXColorTertiaryTintContrast || '',
        XColorSuccess: form.darkXColorSuccess || '',
        XColorSuccessContrast: form.darkXColorSuccessContrast || '',
        XColorSuccessShade: form.darkXColorSuccessShade || '',
        XColorSuccessShadeContrast: form.darkXColorSuccessShadeContrast || '',
        XColorSuccessTint: form.darkXColorSuccessTint || '',
        XColorSuccessTintContrast: form.darkXColorSuccessTintContrast || '',
        XColorWarning: form.darkXColorWarning || '',
        XColorWarningContrast: form.darkXColorWarningContrast || '',
        XColorWarningShade: form.darkXColorWarningShade || '',
        XColorWarningShadeContrast: form.darkXColorWarningShadeContrast || '',
        XColorWarningTint: form.darkXColorWarningTint || '',
        XColorWarningTintContrast: form.darkXColorWarningTintContrast || '',
        XColorDanger: form.darkXColorDanger || '',
        XColorDangerContrast: form.darkXColorDangerContrast || '',
        XColorDangerShade: form.darkXColorDangerShade || '',
        XColorDangerShadeContrast: form.darkXColorDangerShadeContrast || '',
        XColorDangerTint: form.darkXColorDangerTint || '',
        XColorDangerTintContrast: form.darkXColorDangerTintContrast || '',
        XColorDark: form.darkXColorDark || '',
        XColorDarkContrast: form.darkXColorDarkContrast || '',
        XColorDarkShade: form.darkXColorDarkShade || '',
        XColorDarkShadeContrast: form.darkXColorDarkShadeContrast || '',
        XColorDarkTint: form.darkXColorDarkTint || '',
        XColorDarkTintContrast: form.darkXColorDarkTintContrast || '',
        XColorMedium: form.darkXColorMedium || '',
        XColorMediumContrast: form.darkXColorMediumContrast || '',
        XColorMediumShade: form.darkXColorMediumShade || '',
        XColorMediumShadeContrast: form.darkXColorMediumShadeContrast || '',
        XColorMediumTint: form.darkXColorMediumTint || '',
        XColorMediumTintContrast: form.darkXColorMediumTintContrast || '',
        XColorLight: form.darkXColorLight || '',
        XColorLightContrast: form.darkXColorLightContrast || '',
        XColorLightShade: form.darkXColorLightShade || '',
        XColorLightShadeContrast: form.darkXColorLightShadeContrast || '',
        XColorLightTint: form.darkXColorLightTint || '',
        XColorLightTintContrast: form.darkXColorLightTintContrast || '',
      },
    },

    //
    // Light ...
    light: {
      type: XThemeType.Light,
      colors: {
        XColorPrimary: form.lightXColorPrimary || '',
        XColorPrimaryContrast: form.lightXColorPrimaryContrast || '',
        XColorPrimaryShade: form.lightXColorPrimaryShade || '',
        XColorPrimaryShadeContrast: form.lightXColorPrimaryShadeContrast || '',
        XColorPrimaryTint: form.lightXColorPrimaryTint || '',
        XColorPrimaryTintContrast: form.lightXColorPrimaryTintContrast || '',
        XColorSecondary: form.lightXColorSecondary || '',
        XColorSecondaryContrast: form.lightXColorSecondaryContrast || '',
        XColorSecondaryShade: form.lightXColorSecondaryShade || '',
        XColorSecondaryShadeContrast:
          form.lightXColorSecondaryShadeContrast || '',
        XColorSecondaryTint: form.lightXColorSecondaryTint || '',
        XColorSecondaryTintContrast:
          form.lightXColorSecondaryTintContrast || '',
        XColorTertiary: form.lightXColorTertiary || '',
        XColorTertiaryContrast: form.lightXColorTertiaryContrast || '',
        XColorTertiaryShade: form.lightXColorTertiaryShade || '',
        XColorTertiaryShadeContrast:
          form.lightXColorTertiaryShadeContrast || '',
        XColorTertiaryTint: form.lightXColorTertiaryTint || '',
        XColorTertiaryTintContrast: form.lightXColorTertiaryTintContrast || '',
        XColorSuccess: form.lightXColorSuccess || '',
        XColorSuccessContrast: form.lightXColorSuccessContrast || '',
        XColorSuccessShade: form.lightXColorSuccessShade || '',
        XColorSuccessShadeContrast: form.lightXColorSuccessShadeContrast || '',
        XColorSuccessTint: form.lightXColorSuccessTint || '',
        XColorSuccessTintContrast: form.lightXColorSuccessTintContrast || '',
        XColorWarning: form.lightXColorWarning || '',
        XColorWarningContrast: form.lightXColorWarningContrast || '',
        XColorWarningShade: form.lightXColorWarningShade || '',
        XColorWarningShadeContrast: form.lightXColorWarningShadeContrast || '',
        XColorWarningTint: form.lightXColorWarningTint || '',
        XColorWarningTintContrast: form.lightXColorWarningTintContrast || '',
        XColorDanger: form.lightXColorDanger || '',
        XColorDangerContrast: form.lightXColorDangerContrast || '',
        XColorDangerShade: form.lightXColorDangerShade || '',
        XColorDangerShadeContrast: form.lightXColorDangerShadeContrast || '',
        XColorDangerTint: form.lightXColorDangerTint || '',
        XColorDangerTintContrast: form.lightXColorDangerTintContrast || '',
        XColorDark: form.lightXColorDark || '',
        XColorDarkContrast: form.lightXColorDarkContrast || '',
        XColorDarkShade: form.lightXColorDarkShade || '',
        XColorDarkShadeContrast: form.lightXColorDarkShadeContrast || '',
        XColorDarkTint: form.lightXColorDarkTint || '',
        XColorDarkTintContrast: form.lightXColorDarkTintContrast || '',
        XColorMedium: form.lightXColorMedium || '',
        XColorMediumContrast: form.lightXColorMediumContrast || '',
        XColorMediumShade: form.lightXColorMediumShade || '',
        XColorMediumShadeContrast: form.lightXColorMediumShadeContrast || '',
        XColorMediumTint: form.lightXColorMediumTint || '',
        XColorMediumTintContrast: form.lightXColorMediumTintContrast || '',
        XColorLight: form.lightXColorLight || '',
        XColorLightContrast: form.lightXColorLightContrast || '',
        XColorLightShade: form.lightXColorLightShade || '',
        XColorLightShadeContrast: form.lightXColorLightShadeContrast || '',
        XColorLightTint: form.lightXColorLightTint || '',
        XColorLightTintContrast: form.lightXColorLightTintContrast || '',
      },
    },
  } as XThemePack;
}

/**
 * prepare an empty XThemePack instance ...
 */
export function getEmptyThemePack() {
  return {
    name: '',
    dark: {
      type: XThemeType.Dark,
      colors: {
        XColorPrimary: '',
        XColorPrimaryContrast: '',
        XColorPrimaryShade: '',
        XColorPrimaryShadeContrast: '',
        XColorPrimaryTint: '',
        XColorPrimaryTintContrast: '',
        XColorSecondary: '',
        XColorSecondaryContrast: '',
        XColorSecondaryShade: '',
        XColorSecondaryShadeContrast: '',
        XColorSecondaryTint: '',
        XColorSecondaryTintContrast: '',
        XColorTertiary: '',
        XColorTertiaryContrast: '',
        XColorTertiaryShade: '',
        XColorTertiaryShadeContrast: '',
        XColorTertiaryTint: '',
        XColorTertiaryTintContrast: '',
        XColorSuccess: '',
        XColorSuccessContrast: '',
        XColorSuccessShade: '',
        XColorSuccessShadeContrast: '',
        XColorSuccessTint: '',
        XColorSuccessTintContrast: '',
        XColorWarning: '',
        XColorWarningContrast: '',
        XColorWarningShade: '',
        XColorWarningShadeContrast: '',
        XColorWarningTint: '',
        XColorWarningTintContrast: '',
        XColorDanger: '',
        XColorDangerContrast: '',
        XColorDangerShade: '',
        XColorDangerShadeContrast: '',
        XColorDangerTint: '',
        XColorDangerTintContrast: '',
        XColorDark: '',
        XColorDarkContrast: '',
        XColorDarkShade: '',
        XColorDarkShadeContrast: '',
        XColorDarkTint: '',
        XColorDarkTintContrast: '',
        XColorMedium: '',
        XColorMediumContrast: '',
        XColorMediumShade: '',
        XColorMediumShadeContrast: '',
        XColorMediumTint: '',
        XColorMediumTintContrast: '',
        XColorLight: '',
        XColorLightContrast: '',
        XColorLightShade: '',
        XColorLightShadeContrast: '',
        XColorLightTint: '',
        XColorLightTintContrast: '',
      },
    },
    light: {
      type: XThemeType.Light,
      colors: {
        XColorPrimary: '',
        XColorPrimaryContrast: '',
        XColorPrimaryShade: '',
        XColorPrimaryShadeContrast: '',
        XColorPrimaryTint: '',
        XColorPrimaryTintContrast: '',
        XColorSecondary: '',
        XColorSecondaryContrast: '',
        XColorSecondaryShade: '',
        XColorSecondaryShadeContrast: '',
        XColorSecondaryTint: '',
        XColorSecondaryTintContrast: '',
        XColorTertiary: '',
        XColorTertiaryContrast: '',
        XColorTertiaryShade: '',
        XColorTertiaryShadeContrast: '',
        XColorTertiaryTint: '',
        XColorTertiaryTintContrast: '',
        XColorSuccess: '',
        XColorSuccessContrast: '',
        XColorSuccessShade: '',
        XColorSuccessShadeContrast: '',
        XColorSuccessTint: '',
        XColorSuccessTintContrast: '',
        XColorWarning: '',
        XColorWarningContrast: '',
        XColorWarningShade: '',
        XColorWarningShadeContrast: '',
        XColorWarningTint: '',
        XColorWarningTintContrast: '',
        XColorDanger: '',
        XColorDangerContrast: '',
        XColorDangerShade: '',
        XColorDangerShadeContrast: '',
        XColorDangerTint: '',
        XColorDangerTintContrast: '',
        XColorDark: '',
        XColorDarkContrast: '',
        XColorDarkShade: '',
        XColorDarkShadeContrast: '',
        XColorDarkTint: '',
        XColorDarkTintContrast: '',
        XColorMedium: '',
        XColorMediumContrast: '',
        XColorMediumShade: '',
        XColorMediumShadeContrast: '',
        XColorMediumTint: '',
        XColorMediumTintContrast: '',
        XColorLight: '',
        XColorLightContrast: '',
        XColorLightShade: '',
        XColorLightShadeContrast: '',
        XColorLightTint: '',
        XColorLightTintContrast: '',
      },
    },
  } as XThemePack;
}

/**
 * prepare an empty XThemeBaseFormModel instance ...
 */
export function getEmptyThemeBaseFormModel() {
  return {
    name: '',
  } as XThemeBaseFormModel;
}

/**
 * prepare an empty XThemeLightFormModel instance ...
 */
export function getEmptyThemeLightFormModel() {
  return {
    lightXColorPrimary: '',
    lightXColorPrimaryContrast: '',
    lightXColorPrimaryShade: '',
    lightXColorPrimaryShadeContrast: '',
    lightXColorPrimaryTint: '',
    lightXColorPrimaryTintContrast: '',
    lightXColorSecondary: '',
    lightXColorSecondaryContrast: '',
    lightXColorSecondaryShade: '',
    lightXColorSecondaryShadeContrast: '',
    lightXColorSecondaryTint: '',
    lightXColorSecondaryTintContrast: '',
    lightXColorTertiary: '',
    lightXColorTertiaryContrast: '',
    lightXColorTertiaryShade: '',
    lightXColorTertiaryShadeContrast: '',
    lightXColorTertiaryTint: '',
    lightXColorTertiaryTintContrast: '',
    lightXColorSuccess: '',
    lightXColorSuccessContrast: '',
    lightXColorSuccessShade: '',
    lightXColorSuccessShadeContrast: '',
    lightXColorSuccessTint: '',
    lightXColorSuccessTintContrast: '',
    lightXColorWarning: '',
    lightXColorWarningContrast: '',
    lightXColorWarningShade: '',
    lightXColorWarningShadeContrast: '',
    lightXColorWarningTint: '',
    lightXColorWarningTintContrast: '',
    lightXColorDanger: '',
    lightXColorDangerContrast: '',
    lightXColorDangerShade: '',
    lightXColorDangerShadeContrast: '',
    lightXColorDangerTint: '',
    lightXColorDangerTintContrast: '',
    lightXColorDark: '',
    lightXColorDarkContrast: '',
    lightXColorDarkShade: '',
    lightXColorDarkShadeContrast: '',
    lightXColorDarkTint: '',
    lightXColorDarkTintContrast: '',
    lightXColorMedium: '',
    lightXColorMediumContrast: '',
    lightXColorMediumShade: '',
    lightXColorMediumShadeContrast: '',
    lightXColorMediumTint: '',
    lightXColorMediumTintContrast: '',
    lightXColorLight: '',
    lightXColorLightContrast: '',
    lightXColorLightShade: '',
    lightXColorLightShadeContrast: '',
    lightXColorLightTint: '',
    lightXColorLightTintContrast: '',
  } as XThemeLightFormModel;
}

/**
 * prepare an empty XThemeDarkFormModel instance ...
 */
export function getEmptyThemeDarkFormModel() {
  return {
    darkXColorPrimary: '',
    darkXColorPrimaryContrast: '',
    darkXColorPrimaryShade: '',
    darkXColorPrimaryShadeContrast: '',
    darkXColorPrimaryTint: '',
    darkXColorPrimaryTintContrast: '',
    darkXColorSecondary: '',
    darkXColorSecondaryContrast: '',
    darkXColorSecondaryShade: '',
    darkXColorSecondaryShadeContrast: '',
    darkXColorSecondaryTint: '',
    darkXColorSecondaryTintContrast: '',
    darkXColorTertiary: '',
    darkXColorTertiaryContrast: '',
    darkXColorTertiaryShade: '',
    darkXColorTertiaryShadeContrast: '',
    darkXColorTertiaryTint: '',
    darkXColorTertiaryTintContrast: '',
    darkXColorSuccess: '',
    darkXColorSuccessContrast: '',
    darkXColorSuccessShade: '',
    darkXColorSuccessShadeContrast: '',
    darkXColorSuccessTint: '',
    darkXColorSuccessTintContrast: '',
    darkXColorWarning: '',
    darkXColorWarningContrast: '',
    darkXColorWarningShade: '',
    darkXColorWarningShadeContrast: '',
    darkXColorWarningTint: '',
    darkXColorWarningTintContrast: '',
    darkXColorDanger: '',
    darkXColorDangerContrast: '',
    darkXColorDangerShade: '',
    darkXColorDangerShadeContrast: '',
    darkXColorDangerTint: '',
    darkXColorDangerTintContrast: '',
    darkXColorDark: '',
    darkXColorDarkContrast: '',
    darkXColorDarkShade: '',
    darkXColorDarkShadeContrast: '',
    darkXColorDarkTint: '',
    darkXColorDarkTintContrast: '',
    darkXColorMedium: '',
    darkXColorMediumContrast: '',
    darkXColorMediumShade: '',
    darkXColorMediumShadeContrast: '',
    darkXColorMediumTint: '',
    darkXColorMediumTintContrast: '',
    darkXColorLight: '',
    darkXColorLightContrast: '',
    darkXColorLightShade: '',
    darkXColorLightShadeContrast: '',
    darkXColorLightTint: '',
    darkXColorLightTintContrast: '',
  } as XThemeDarkFormModel;
}

/**
 * prepare an empty XThemeFormModel instance ...
 */
export function getEmptyThemeFormModel() {
  return {
    ...getEmptyThemeBaseFormModel(),
    ...getEmptyThemeDarkFormModel(),
    ...getEmptyThemeLightFormModel(),
  } as XThemeFormModel;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme-manager\x-theme-manager.component.html

<!-- Card Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component Ref -->
<ng-template #componentRef>
  <div
    class="x-theme-manager x-wrapper"
    [xColor]="getValue(color) | async"
    [class.mobile]="isMobileUi$ | async"
    [ngClass]="getValue(cssClass) | async"
  >
    <!-- Tabs -->
    <x-tabs
      #xTabs
      [swipable]="false"
      [loading]="loading"
      [showHeader]="false"
      [dynamicHeight]="true"
      [color]="'transparent'"
      [uiDisabled]="uiDisabled"
      [disablePagination]="true"
      [(selectedIndex)]="currentTab"
      [actionProvider]="tabsActionProvider"
      (selectedTabChange)="handleSelectedTabChanged($event)"
    >
      <!-- List Tab -->
      <x-tab>
        <!-- Theme List -->
        <x-list
          #listPresenter
          [items]="listItems"
          [loading]="loading"
          [showToolbar]="true"
          [stickyToolbar]="true"
          [selectableItems]="false"
          [foregroundColor]="false"
          [uiDisabled]="uiDisabled"
          [enableReordering]="false"
          [itemTemplate]="themeListItemRef"
          [color]="getValue(color) | async"
          (emptyRefresh)="handleListRefresh()"
          [actionProvider]="listActionProvider"
          [filteringHandler]="listFilteringHandler"
          [actionBarColor]="getValue(actionBarColor)"
          *ngIf="
            (getValue(listShowSearchBar) | async) &&
            (hasChildValue(listItems) | async)
          "
          [actionBarForegroundColor]="
            getValue(actionBarForegroundColor) | async
          "
          [showEmpty]="getValue(listShowEmpty) | async"
          [emptyColor]="getValue(listEmptyColor) | async"
          [emptyActionProvider]="listEmptyActionProvider"
          [emptyRefreshButtonColor]="
            getValue(listEmptyRefreshButtonColor) | async
          "
          [searchBarForegroundColor]="
            getValue(listSearchBarForegroundColor) | async
          "
          [cssClass]="getArrayValue(listCssClass) | async"
          [searchBarCancelButtonIcon]="
            getValue(listSearchBarCancelButtonIcon) | async
          "
          [searchBarSearchInputColor]="
            getValue(listSearchBarSearchInputColor) | async
          "
          [showDivider]="getValue(listShowDivider) | async"
          [showActionBar]="getValue(showActionBar) | async"
          [searchBarType]="getValue(searchBarType) | async"
          [searchBarShowCancelButton]="
            getValue(listSearchBarShowCancelButton) | async
          "
          [searchBarCancelButtonTitle]="
            getValue(listSearchBarCancelButtonTitle) | async
          "
          [searchBarSpellcheck]="
            getOptionalValue(listSearchBarSpellcheck) | async
          "
          [emptyForegroundMessageColor]="
            getValue(listEmptyForegroundMessageColor) | async
          "
          [searchBarActionProvider]="searchBarActionProvider"
          [emptyCssClass]="getValue(listEmptyCssClass) | async"
          [searchBarClearButtonIconColor]="
            getValue(listSearchBarClearButtonIconColor) | async
          "
          [searchBarSearchButtonIconColor]="
            getValue(listSearchBarSearchButtonIconColor) | async
          "
          (searchBarOnEvent)="handleListSearchBarOnEvent($event)"
          [searchBarColor]="getValue(listSearchBarColor) | async"
          [searchBarValue]="getValue(listSearchBarValue) | async"
          [emptyForegroundRefreshButtonColor]="
            getValue(listEmptyForegroundRefreshButtonColor) | async
          "
          [emptyHasRefresh]="getValue(listEmptyHasRefresh) | async"
          [emptyMessage]="getResourceValue(listEmptyMessage) | async"
          [emptyRefreshIcon]="getValue(listEmptyRefreshIcon) | async"
          [showSearchBar]="(countChildsValue(listItems) | async) > 2"
          [searchBarForegroundSearchInputColor]="
            getValue(listSearchBarForegroundSearchInputColor) | async
          "
          [actionBarActions]="getArrayValue(actionBarActions) | async"
          [searchBarDebounce]="getValue(listSearchBarDebounce) | async"
          [emptyMessageColor]="getValue(listEmptyMessageColor) | async"
          [actionBarCssClass]="getArrayValue(actionBarCssClass) | async"
          [searchBarTitle]="getResourceValue(listSearchBarTitle) | async"
          (actionBarActionFired)="handleListActionBarActionFired($event)"
          (searchBarQueryChange)="handleListSearchBarQueryChange($event)"
          [searchBarCssClass]="getArrayValue(listSearchBarCssClass) | async"
          [emptyForegroundColor]="getValue(listEmptyForegroundColor) | async"
          [searchBarKeyboardType]="getValue(listSearchBarKeyboardType) | async"
          [searchBarEnterKeyHint]="getValue(listSearchBarEnterKeyHint) | async"
          [searchBarClearButtonIcon]="
            getValue(listSearchBarClearButtonIcon) | async
          "
          [searchBarSearchButtonIcon]="
            getValue(listSearchBarSearchButtonIcon) | async
          "
        >
        </x-list>
      </x-tab>

      <!-- Preview/Details Tab -->
      <x-tab>
        <!-- Theme Preview -->
        <x-model-presenter
          [color]="getValue(color) | async"
          [modelKeyProvider]="themeKeyProvider"
          [hiddenModelKeys]="hiddenThemModelKeys"
          [model]="getValue(selectedTheme) | async"
          [cardColor]="getValue(cardColor) | async"
          [propertyActionTemplate]="previewActionRef"
          [labelColor]="getValue(propertyLabelColor) | async"
          [valueColor]="getValue(propertyValueColor) | async"
          *ngIf="notNullOrUndefinedValue(selectedTheme) | async"
          [headerHighlightColor]="
            getValue(presenterHeaderHighlightColor) | async
          "
          [headerColor]="getValue(presenterHeaderColor) | async"
          [contentColor]="getValue(presenterContentColor) | async"
          [cardForegroundColor]="getValue(cardForegroundColor) | async"
        >
        </x-model-presenter>
      </x-tab>

      <!-- Edit -->
      <x-tab>
        <x-action-bar
          *ngIf="getValue(showActionBar) | async"
          [actionProvider]="actionBarActionProvider"
          [color]="getValue(actionBarColor) | async"
          [actions]="themeModelPresenterActionActions"
          [cssClass]="getValue(actionBarCssClass) | async"
          [foregroundColor]="getValue(actionBarForegroundColor) | async"
        >
        </x-action-bar>

        <!-- Model Presenter -->
        <x-model-presenter
          [color]="getValue(color) | async"
          [modelKeyProvider]="themeKeyProvider"
          [hiddenModelKeys]="hiddenThemModelKeys"
          [model]="getValue(selectedTheme) | async"
          [cardColor]="getValue(cardColor) | async"
          [propertyActionTemplate]="themeEditActionRef"
          [labelColor]="getValue(propertyLabelColor) | async"
          [valueColor]="getValue(propertyValueColor) | async"
          [actionProvider]="themeModelPresenterActionProvider"
          (onModelChanged)="handleThemeContentChanged($event)"
          *ngIf="notNullOrUndefinedValue(selectedTheme) | async"
          [headerHighlightColor]="
            getValue(presenterHeaderHighlightColor) | async
          "
          [headerColor]="getValue(presenterHeaderColor) | async"
          [contentColor]="getValue(presenterContentColor) | async"
          [cardForegroundColor]="getValue(cardForegroundColor) | async"
        >
        </x-model-presenter>

        <!-- Action -->
        <div class="actions">
          <x-slotter [layout]="SlotLayouts.HORIZONTAL">
            <!-- Center -->
            <x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
              <x-button
                [loading]="loading"
                [type]="ButtonTypes.Raised"
                class="ion-margin-horizontal"
                (clicked)="handleUpdateTheme()"
                [color]="getValue(saveColor) | async"
                [title]="getResourceValue(saveLabel) | async"
                [uiDisabled]="!(getValue(canSaveTheme) | async)"
              ></x-button>
            </x-slot>
          </x-slotter>
        </div>
      </x-tab>

      <!-- Import -->
      <x-tab>
        <x-file-upload
          [showEmpty]="true"
          [wrapWithCard]="false"
          [showActionBar]="true"
          [listShowToolbar]="true"
          [emptyHasRefresh]="false"
          [listStickyToolbar]="true"
          [listEnableReordering]="false"
          [actionProvider]="themeUploadActionProvider"
          [allowedExtensions]="allowedThemeExtensions"
          [color]="getValue(themeUploadColor) | async"
          [emptyColor]="getValue(listEmptyColor) | async"
          [actionBarColor]="getValue(actionBarColor) | async"
          (fileChange)="handleThemeUploadFileChanged($event)"
          [maxAllowedSize]="getValue(maxAllowedSize) | async"
          [listActionProvider]="themeUploadListActionProvider"
          [actionBarActions]="themeUploadListActionbarActions"
          [listCssClass]="getArrayValue(listCssClass) | async"
          [listShowDivider]="getValue(listShowDivider) | async"
          [maxAllowedFiles]="getValue(maxAllowedFiles) | async"
          [cssClass]="getArrayValue(themeUploadCssClass) | async"
          [searchBarColor]="getValue(listSearchBarColor) | async"
          [fileDropAreaAllowedExtensions]="allowedThemeExtensions"
          [emptyMessage]="getValue(themeUploadEmptyMessage) | async"
          [emptyCssClass]="getArrayValue(listEmptyCssClass) | async"
          [minAllowedFileSize]="getValue(minAllowedFileSize) | async"
          [maxAllowedFileSize]="getValue(maxAllowedFileSize) | async"
          [actionBarHideActions]="themeUploadListActionBarHideActions"
          (actionBarActionFired)="handleThemeUploadActionFired($event)"
          [emptyMessageColor]="getValue(listEmptyMessageColor) | async"
          [showSearchBar]="(countChildsValue(uploadThemes) | async) > 1"
          [actionBarCssClass]="getArrayValue(actionBarCssClass) | async"
          [foregroundColor]="getValue(themeUploadForegroundColor) | async"
          [fileDropAreaSupportsMultipleFiles]="
            getValue(themeUploadFileDropAreaSupportsMultipleFiles) | async
          "
          [searchBarCssClass]="getArrayValue(listSearchBarCssClass) | async"
          [fileDropAreaIcon]="getValue(themeUploadFileDropAreaIcon) | async"
          [emptyForegroundColor]="getValue(listEmptyForegroundColor) | async"
          [fileDropAreaColor]="getValue(themeUploadFileDropAreaColor) | async"
          [fileDropAreaActionProvider]="themeUploadFileDropAreaActionProvider"
          [searchBarSearchInputColor]="
            getValue(listSearchBarSearchInputColor) | async
          "
          [fileDropAreaErrorColor]="
            getValue(themeUploadFileDropAreaErrorColor) | async
          "
          [fileDropAreaAccentColor]="
            getValue(themeUploadFileDropAreaAccentColor) | async
          "
          [fileDropAreaSuccessColor]="
            getValue(themeUploadFileDropAreaSuccessColor) | async
          "
          [fileDropAreaForegroundColor]="
            getValue(themeUploadFileDropAreaForegroundColor) | async
          "
          [emptyForegroundMessageColor]="
            getValue(themeUploadEmptyForegroundMessageColor) | async
          "
          [fileDropAreaMessage]="
            getValue(themeUploadFileDropAreaMessage) | async
          "
          [fileDropAreaCssClass]="
            getArrayValue(themeUploadFileDropAreaCssClass) | async
          "
          [showFileDropArea]="
            (isNotMobileUi$ | async) && getValue(themeUploadShowFileDropArea)
              | async
          "
        ></x-file-upload>
      </x-tab>
    </x-tabs>
  </div>
</ng-template>

<!-- pack List Item Template -->
<ng-template #themeListItemRef let-item="item" let-index="index">
  {{ item.data.name }}
</ng-template>

<!-- previewActionRef -->
<ng-template
  #previewActionRef
  let-key="key"
  let-prefix="prefix"
  let-value="value"
>
  <x-color
    [showLabel]="false"
    class="x-color-item"
    *ngIf="key !== 'name'"
    [value]="getColorValue(key, value)"
    [borderColor]="getValue(colorViewBorderColor) | async"
    [borderWidth]="getValue(colorViewBorderWidth) | async"
    [borderStyle]="getValue(colorViewBorderStyle) | async"
  ></x-color>
</ng-template>

<!-- ThemeEditActionRef -->
<ng-template
  #themeEditActionRef
  let-key="key"
  let-prefix="prefix"
  let-value="value"
>
  <ng-container *ngIf="key !== 'name'">
    <!-- Color Viewer -->
    <x-color
      [showLabel]="false"
      class="x-color-item"
      [value]="getColorValue(key, value)"
      [borderColor]="getValue(colorViewBorderColor) | async"
      [borderWidth]="getValue(colorViewBorderWidth) | async"
      [borderStyle]="getValue(colorViewBorderStyle) | async"
    ></x-color>

    <!-- is Small Screen -->
    <ng-container *ngIf="isMobileUi$ | async">
      <!-- Property Selector -->
      <x-icon
        [clickable]="true"
        [foregroundColor]="true"
        [name]="getValue(propertyActionMenuIcon) | async"
        [color]="getValue(propertyActionMenuIconColor) | async"
        (clicked)="handleThemePropertyActionMenuClicked(key, prefix, value)"
      ></x-icon>
    </ng-container>

    <!-- is Big Screen -->
    <ng-container *ngIf="isNotMobileUi$ | async">
      <!-- Property Selector -->
      <x-icon
        [clickable]="true"
        [foregroundColor]="true"
        [name]="getValue(propertyEditIcon) | async"
        (clicked)="handleThemePropertyEdit(key, prefix, value)"
        [color]="
          (notNullOrUndefinedValue(value) | async)
            ? (getValue(propertyEditIconColor) | async)
            : (getValue(emptyPropertyEditIconColor) | async)
        "
      ></x-icon>
    </ng-container>
  </ng-container>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme-manager\x-theme-manager.component.scss

:host ::ng-deep x-color {
  --size: 24px !important;
  display: inline-block;
}

.actions {
  margin: {
    top: 8px;
    bottom: 8px;
  }
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme-manager\x-theme-manager.component.ts

import {
  nameof,
  assign,
  XParam,
  toArray,
  hasChild,
  XUiEvent,
  cloneJson,
  parseJson,
  isFunction,
  isSameType,
  XThemeType,
  XThemePack,
  isSameObject,
  XResourceIDs,
  XDialogResult,
  XExceptionIDs,
  XBaseKeyValue,
  XStandardType,
  XPickerOptions,
  toNormalString,
  XOneOrManyType,
  XModelProperty,
  XThemeDescriptor,
  XModalButtonRole,
  XModelKeyProvider,
  isNullOrUndefined,
  isNullOrEmptyString,
  XThemeTypeIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XThemeManagerTabs,
  XThemeManagerAction,
  XThemeManagerActionModel,
  XThemeManagerContainerComponent,
} from './x-theme-manager.typings';
import {
  XTabsAction,
  XTabChangeEvent,
  XTabsActionModel,
} from '../../x-tabs/x-tabs.typings';
import {
  XListItem,
  XListAction,
  XListActionModel,
  XListItemSlideOption,
  XListFilteringHandler,
  defaultListFilteringHandler,
} from '../../x-list/x-list.typings';
import { Subject, Subscription } from 'rxjs';
import {
  XActionBarItem,
  XActionBarAction,
  XActionBarActionModel,
  DefaultActionBarItemIds,
  defaultActionBarSetItem,
  defaultActionBarUploadItem,
  XActionBarActionFiredModel,
} from '../../x-action-bar/x-action-bar.typings';
import {
  XSearchBarType,
  XSearchBarAction,
  XSearchBarActionModel,
  XSearchBarEventIdentifier,
} from '../../x-search-bar/x-search-bar.typings';
import {
  XFileUploadAction,
  XFileUploadActionModel,
} from '../../x-file-upload/x-file-upload.typings';
import { getEmptyThemePack } from '../x-theme.tools';
import { XIconNames } from '../../x-icon/x-icon.typings';
import {
  XModelPresenterAction,
  XModelPresenterActionModel,
} from '../../x-model-presenter/x-model-presenter.typings';
import { XButtonType } from '../../x-button/x-button.typings';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XSlotLayout, XSlotName } from '../../x-slotter/x-slotter.typings';
import { XPopoverComponent } from '../../dialogs/x-popover/x-popover.component';
import { XColorPickerOutputFormat } from '../../x-color-picker/x-color-picker.typings';
import { XFileDropAreaActionModel } from '../../x-file-drop-area/x-file-drop-area.typings';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-theme-manager',
  templateUrl: './x-theme-manager.component.html',
  styleUrls: ['./x-theme-manager.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XThemeManagerComponent extends XThemeManagerContainerComponent {
  //
  //#region Props ...
  //
  private loadingObject: HTMLIonLoadingElement;
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  readonly SlotNames = Object.assign({}, XSlotName);
  readonly Tabs = Object.assign({}, XThemeManagerTabs);
  readonly SlotLayouts = Object.assign({}, XSlotLayout);
  readonly ButtonTypes = Object.assign({}, XButtonType);
  readonly ColorNames = Object.assign({}, XColorWithBrightness);
  readonly themeUploadListActionProvider = new Subject<XListActionModel>();
  readonly allowedThemeExtensions: XStandardType<XOneOrManyType<string>> = [
    '.json',
  ];
  readonly themeUploadActionProvider = new Subject<XFileUploadActionModel>();
  readonly themeUploadListActionBarHideActions: XStandardType<
    XOneOrManyType<string>
  >;
  readonly themeModelPresenterActionProvider =
    new Subject<XModelPresenterActionModel>();
  readonly themeUploadListActionbarActions: XStandardType<
    XOneOrManyType<XActionBarItem>
  >;
  readonly themeUploadFileDropAreaActionProvider =
    new Subject<XFileDropAreaActionModel>();

  //
  canSaveTheme = false;
  listItems: XListItem<any>[];
  selectedTheme: XThemePack = null;
  uploadThemes: XOneOrManyType<File>;
  currentTab: number = this.Tabs.List;
  baseTheme: Readonly<XThemePack> = null;
  showList: XStandardType<boolean> = true;
  hiddenThemModelKeys: XOneOrManyType<string> = [
    `${nameof<XThemePack>('dark')}.${nameof<XThemeDescriptor>('type')}`,
    `${nameof<XThemePack>('light')}.${nameof<XThemeDescriptor>('type')}`,
  ];
  themeKeyProvider: XModelKeyProvider = (key) => {
    //
    let result = key as string;

    //
    switch (key) {
      //
      case nameof<XThemePack>('name'):
        result = this.resourceProvider(this.ResourceIDs.theme);
        break;

      //
      case nameof<XThemePack>('dark'):
        result = this.resourceProvider(this.ResourceIDs.theme_dark);
        break;

      //
      case nameof<XThemePack>('light'):
        result = this.resourceProvider(this.ResourceIDs.theme_light);
        break;

      //
      case nameof<XThemeDescriptor>('colors'):
        result = this.resourceProvider(this.ResourceIDs.colors);
        break;

      //
      default:
        result = key as string;
        break;
    }

    //
    return result;
  };
  listActionProvider = new Subject<XListActionModel>();
  tabsActionProvider = new Subject<XTabsActionModel>();
  searchBarType: XStandardType<string> = XSearchBarType.Search;
  searchBarActionProvider = new Subject<XSearchBarActionModel>();
  actionBarActionProvider = new Subject<XActionBarActionModel>();
  actionBarActions: XStandardType<XOneOrManyType<XActionBarItem>>;
  themeModelPresenterActionActions: XOneOrManyType<XActionBarItem> = [
    {
      //
      ...defaultActionBarSetItem,

      //
      title: this.ResourceIDs.save,
      disabled: true,

      //
      handler: async (id) => {
        await this.handleUpdateTheme();
      },
    },
  ];
  listFilteringHandler: XListFilteringHandler = defaultListFilteringHandler;
  //#endregion

  //
  //#region LifeCycles ...
  async afterViewInit() {
    super.afterViewInit();

    //
    await this.startUp();
  }

  onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XThemeManagerComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region UI Provider ...
  getColorValue(key: string, value: string) {
    return {
      key,
      value,
    } as XBaseKeyValue<string>;
  }
  //#endregion

  //
  //#region UI Handler ...
  //
  async handleSelectedTabChanged(event: XTabChangeEvent) {
    this.currentTab = event.index;
  }

  async handleListRefresh() {
    //
    await this.startUp();

    //
    if (!isNullOrUndefined(this.listEmptyRefresh)) {
      this.listEmptyRefresh.emit();
    }
  }

  async handleListActionBarActionFired(model: XActionBarActionFiredModel) {
    //
    if (isNullOrUndefined(model) || isNullOrEmptyString(model.action)) {
      return;
    }

    //
    switch (model.action) {
      //
      case DefaultActionBarItemIds.Upload:
        await this.handleUploadTheme();
        break;
    }

    //
    if (!isNullOrUndefined(this.listActionBarActionFired)) {
      this.listActionBarActionFired.emit(model);
    }
  }

  async handleListSearchBarOnEvent(event: XSearchBarEventIdentifier) {
    //
    if (!isNullOrUndefined(this.listSearchBarOnEvent)) {
      this.listSearchBarOnEvent.emit(event);
    }
  }

  async handleListSearchBarQueryChange(event: string) {
    //
    if (!isNullOrUndefined(this.listSearchBarQueryChange)) {
      this.listSearchBarQueryChange.emit(event);
    }
  }

  async handleThemeUploadActionFired(model: XActionBarActionFiredModel) {
    //
    // Validate Args ...
    if (isNullOrUndefined(model) || isNullOrEmptyString(model.action)) {
      return;
    }

    //
    switch (model.action) {
      //
      case DefaultActionBarItemIds.Upload:
        await this.handleThemesImport();
        break;
    }
  }

  async handleThemeUploadFileChanged(files: XOneOrManyType<File>) {
    this.uploadThemes = files;
  }

  //
  //#region ModelPresenter Handlers ...
  async handleThemePropertyActionMenuClicked(
    key: string,
    prefix: string,
    value: any
  ) {
    //
    if (isNullOrEmptyString(key)) {
      return;
    }

    //
    const onlyIcon = false;
    const slideOptions: XListItemSlideOption[] = [];

    //
    // Edit ...
    slideOptions.push({
      id: 'property_edit',
      icon: await this.getValueAsync(this.propertyEditIcon),
      title: this.ResourceIDs.edit,
      color: isNullOrUndefined(value)
        ? await this.getValueAsync(this.emptyPropertyEditIconColor)
        : await this.getValueAsync(this.propertyEditIconColor),
      slot: 'start',
      onlyIcon,
      handler: async () => {
        await this.handleThemePropertyEdit(key, prefix, value);
      },
    });

    //
    // Menu Items ...
    const item: XListItem<any> = {
      data: '',
      slideOptions,
    };

    //
    const popOver = await this.managerService.dialogService.presentPopover({
      component: XPopoverComponent,
      componentProps: assign({}, { key: XParam.Item, value: item }),
      translucent: true,
      showBackdrop: true,
      event,
    });

    //
    const { data } = await popOver.onWillDismiss();
    if (!data || !data.id || !data.data) {
      return;
    }

    //
    const optionId = data.id.toString() as string;
    const option =
      item && item.slideOptions
        ? item.slideOptions.find((so) => so.id === optionId)
        : null;
    if (!option) {
      return;
    }

    //
    const handler = option.handler;
    if (!handler || !isFunction(handler)) {
      return;
    }

    //
    // Run Handler ...
    await handler();
  }

  async handleThemePropertyEdit(key: string, prefix: string, value: any) {
    //
    if (isNullOrEmptyString(key)) {
      return;
    }

    //
    await this.colorPickerDialogService.openDialog({
      //
      title: key,

      //
      selectedColor: value,

      //
      showColorInput: true,
      showPresetColors: false,
      showCopyToClipboard: true,

      //
      cardColor: await this.getValueAsync(this.cardColor),
      cardForegroundColor: await this.getValueAsync(this.cardForegroundColor),

      //
      representColorFormats: [XColorPickerOutputFormat.XHEX],
    });

    //
    const subscription = this.colorPickerDialogService.onResult
      .asObservable()
      .subscribe((result) => {
        //
        if (
          result.result !== XDialogResult.Ok ||
          !result.payload ||
          isNullOrEmptyString(result.payload)
        ) {
          return;
        }

        //
        const value = result.payload as string;
        this.themeModelPresenterActionProvider.next({
          action: XModelPresenterAction.SetProperty,
          payload: {
            key,
            value,
            prefix,
          } as XModelProperty,
        });

        //
        subscription.unsubscribe();
      });
  }

  async handleThemeContentChanged(event: any) {
    //
    const isTheme = isSameType(event, this.selectedTheme);
    if (!isTheme) {
      return;
    }

    //
    const isThemeChanged = !isSameObject(this.baseTheme, this.selectedTheme);
    this.canSaveTheme = isThemeChanged;

    //
    toArray(this.themeModelPresenterActionActions)[0].disabled =
      !this.canSaveTheme;

    //
    this.themeChange.emit(isThemeChanged);
    this.handleChangeDetection();
  }

  async handleUpdateTheme() {
    //
    if (!this.selectedTheme || !this.baseTheme || !this.canSaveTheme) {
      return;
    }

    //
    await this.managerService.dialogService.presentAreYouSureYesNoDialog(
      async () => {
        await this.updateTheme(this.selectedTheme);
      }
    );
  }
  //#endregion
  //#endregion

  //
  //#region Actions ...
  async back() {
    //
    switch (this.currentTab) {
      //
      case this.Tabs.List:
        this.baseTheme = null;
        this.selectedTheme = null;
        break;

      //
      case this.Tabs.Details:
        await this.changeTab(this.Tabs.List);
        break;

      //
      case this.Tabs.Import:
        //
        this.uploadThemes = [];
        this.themeUploadActionProvider.next({
          action: XFileUploadAction.CLEAR_LIST,
        });
        await this.changeTab(this.Tabs.List);
        break;

      //
      case this.Tabs.Edit:
        this.baseTheme = null;
        this.selectedTheme = null;
        this.canSaveTheme = false;
        this.themeChange.emit(false);
        await this.changeTab(this.Tabs.List);
        break;
    }
  }

  async clearList() {
    //
    this.listItems = [];
    this.handleChangeDetection();
  }

  async setTheme(theme: XThemePack, whichType: XThemeTypeIdentifier) {
    //
    if (!theme && isNullOrEmptyString(theme.name)) {
      return;
    }

    //
    const restart = await this.getOptionalValueAsync(this.restartAfterSetTheme);

    //
    const isSetted = await this.managerService.themeManagerService.setTheme(
      theme,
      whichType,
      true,
      restart
    );
    if (isSetted) {
      //
      await this.prepareListItems();

      //
      this.managerService.settingsService.restart();
    }
  }

  async editTheme(theme: XThemePack) {
    //
    if (!theme || !isNullOrUndefined(this.loadingObject)) {
      return;
    }

    //
    await this.presentLoading();

    //
    this.baseTheme = cloneJson(theme);
    this.selectedTheme = {
      ...theme,
    };
    await this.changeTab(this.Tabs.Edit);
    this.handleChangeDetection();
  }

  async cloneTheme(theme: XThemePack) {
    //
    if (!theme) {
      return;
    }

    //
    await this.managerService.dialogService.presentAlert({
      subHeader: XResourceIDs.theme,
      message: XResourceIDs.theme_clone_title,
      inputs: [
        {
          type: 'text',
          name: 'name',
          placeholder: XResourceIDs.theme_name_description,
        },
      ],
      buttons: [
        {
          text: XResourceIDs.clone,
          role: XModalButtonRole.Selected,
          cssClass: ['btn-selected'],
          handler: async (value) => {
            //
            if (value && value.name && !isNullOrEmptyString(value.name)) {
              //
              const themeName = toNormalString(theme.name);
              const clonedThemeName = toNormalString(value.name as string);
              const isCloned =
                await this.managerService.themeManagerService.cloneTheme(
                  themeName,
                  clonedThemeName,
                  false
                );

              //
              if (isCloned) {
                //
                this.showSuccess();
                await this.prepareListItems();
              } else {
                this.showError(XExceptionIDs.ActionFailed);
              }
            } else {
              this.showError(XExceptionIDs.InvalidData);
            }
          },
        },
        {
          text: XResourceIDs.cancel,
          role: XModalButtonRole.Cancel,
          cssClass: ['btn-cancel'],
          handler: () => {},
        },
      ],
    });
  }

  async downloadTheme(theme: XThemePack) {
    //
    if (!theme) {
      return;
    }

    //
    await this.managerService.themeManagerService.exportTheme(theme.name);
  }

  async removeTheme(theme: XThemePack) {
    //
    if (!theme) {
      return;
    }

    //
    await this.managerService.dialogService.presentAreYouSureYesNoDialog(() => {
      this.managerService.themeManagerService
        .removeTheme(toNormalString(theme.name), false)
        .then((isRemoved) => {
          if (isRemoved) {
            //
            this.showSuccess();
            this.prepareListItems();
          } else {
            this.showError();
          }
        });
    });
  }

  async previewTheme(theme: XThemePack) {
    //
    if (!theme || !isNullOrUndefined(this.loadingObject)) {
      return;
    }

    //
    await this.presentLoading();

    //
    this.selectedTheme = {
      ...theme,
    };
    await this.changeTab(this.Tabs.Details);
    this.handleChangeDetection();
  }

  async saveTheme(theme: XThemePack) {
    //
    if (!theme) {
      return false;
    }

    //
    if (
      isNullOrEmptyString(theme.name) ||
      (await this.managerService.themeManagerService.isThemeExists(theme.name))
    ) {
      this.showError(XExceptionIDs.Duplicate);
      return false;
    }

    //
    const isSaved = await this.managerService.themeManagerService.saveTheme(
      theme,
      false
    );

    //
    if (isSaved) {
      //
      this.themeChange.emit(false);
      this.managerService.settingsService.restart();
    }

    return isSaved;
  }

  async updateTheme(theme: XThemePack) {
    //
    if (!theme || isNullOrEmptyString(theme.name)) {
      return false;
    }

    //
    const isExists =
      await this.managerService.themeManagerService.isThemeExists(theme.name);
    if (!isExists) {
      this.showError(XExceptionIDs.NotFound);
      return false;
    }

    //
    const isUpdated = await this.managerService.themeManagerService.updateTheme(
      theme.name,
      theme,
      false,
      true
    );

    //
    if (isUpdated) {
      //
      this.themeChange.emit(false);
      this.managerService.settingsService.restart();
    }

    //
    return isUpdated;
  }
  //#endregion

  //
  //#region Private ...
  //
  //#region Handler Registerrers ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XThemeManagerActionModel>();
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        switch (model.action) {
          //
          case XThemeManagerAction.Refresh:
            this.detectChanges();
            break;

          //
          case XThemeManagerAction.Back:
            await this.back();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region Private Actions ...
  private async startUp() {
    //
    this.tabChange.emit(this.currentTab);
    await this.prepareActionBarActions();
    await this.prepareListItems();
  }

  private async prepareListItems() {
    //
    this.listItems = null;

    //
    const loadingDialog =
      await this.managerService.dialogService.presentLoading({
        showBackdrop: true,
        backdropDismiss: false,
        message: this.resourceProvider(XResourceIDs.loading),
      });

    //
    const currentTheme =
      await this.managerService.themeManagerService.getCurrentTheme();
    const themes = await this.managerService.themeManagerService.getThemes();
    if (!currentTheme || !hasChild(themes)) {
      //
      await this.clearList();
      await loadingDialog.dismiss();
      return;
    }

    //
    // Prepare List Items ...
    this.listItems = themes.map((theme) => {
      //
      const isCurrent =
        toNormalString(theme.name) === toNormalString(currentTheme.name);

      //
      return {
        data: theme,
        slideOptions: [
          //
          // Remove ...
          {
            id: 'delete_theme',
            icon: XIconNames.remove,
            title: this.ResourceIDs.delete,
            color: XColorWithBrightness.Danger,
            onlyIcon: true,
            disabled: this.uiDisabled || this.loading || isCurrent,
            slot: 'end',
            handler: () => {
              this.removeTheme(theme);
            },
          },
          //
          // Set As Current ...
          {
            id: 'set_theme',
            icon: XIconNames.checked,
            title: this.ResourceIDs.set,
            color: XColorWithBrightness.Success,
            onlyIcon: true,
            disabled:
              this.uiDisabled ||
              this.loading ||
              toNormalString(theme.name) === toNormalString(currentTheme.name),
            slot: 'start',
            handler: () => {
              //
              // Prepare Picker Options for Theme Setting ...
              const options: XPickerOptions = {
                buttons: [
                  {
                    text: XResourceIDs.set,
                    role: XModalButtonRole.Selected,
                    cssClass: ['btn-selected'],
                    handler: async (value) => {
                      //
                      if (!value || !value.type || !value.type.value) {
                        return;
                      }

                      //
                      const type = value.type.value as XThemeTypeIdentifier;
                      await this.setTheme(theme, type);
                    },
                  },
                  {
                    text: XResourceIDs.cancel,
                    role: XModalButtonRole.Cancel,
                    cssClass: ['btn-cancel'],
                  },
                ],
                columns: [
                  {
                    name: 'type',
                    options: [
                      {
                        text: XResourceIDs.theme_light,
                        value: XThemeType.Light,
                      },
                      {
                        text: XResourceIDs.theme_dark,
                        value: XThemeType.Dark,
                      },
                    ],
                  },
                ],
              };

              //
              // Present Theme  Picker ...
              this.managerService.dialogService.presentPicker(options);
            },
          },
          //
          // Check For Change Mode ...
          ...(isCurrent
            ? [
                //
                // Change Mode ...
                {
                  id: 'change_theme_mode',
                  icon: XIconNames.contrast,
                  title: this.ResourceIDs.theme_mode,
                  color: XColorWithBrightness.Success,
                  onlyIcon: true,
                  disabled: this.uiDisabled || this.loading,
                  slot: 'start',
                  handler: async () => {
                    //
                    const currentType =
                      await this.managerService.themeManagerService.getCurrentThemeType();
                    const type =
                      currentType === XThemeType.Light
                        ? XThemeType.Dark
                        : XThemeType.Light;
                    await this.setTheme(theme, type);
                  },
                },
              ]
            : []),
          //
          // Preview ...
          {
            id: 'preview_theme',
            icon: XIconNames.preview,
            title: this.ResourceIDs.details,
            color: XColorWithBrightness.Secondary,
            onlyIcon: true,
            disabled: this.uiDisabled || this.loading,
            slot: 'start',
            handler: async () => {
              await this.previewTheme(theme);
            },
          },
          //
          // Edit ...
          {
            id: 'edit_theme',
            icon: XIconNames.edit,
            title: this.ResourceIDs.edit,
            color: XColorWithBrightness.Tertiary,
            onlyIcon: true,
            disabled: this.uiDisabled || this.loading, // || isCurrent,
            slot: 'start',
            handler: () => {
              this.editTheme(theme);
            },
          },
          //
          // Clone ...
          {
            id: 'clone_theme',
            icon: XIconNames.copy,
            title: this.ResourceIDs.clone,
            color: XColorWithBrightness.Success,
            onlyIcon: true,
            disabled: this.uiDisabled || this.loading,
            slot: 'start',
            handler: async () => {
              await this.cloneTheme(theme);
            },
          },
          //
          // Download ...
          {
            id: 'download_theme',
            icon: XIconNames.download,
            title: this.ResourceIDs.download,
            color: XColorWithBrightness.SecondaryShade,
            onlyIcon: true,
            disabled: this.uiDisabled || this.loading,
            slot: 'start',
            handler: async () => {
              await this.downloadTheme(theme);
            },
          },
        ],
      } as XListItem<XThemePack>;
    });

    //
    await loadingDialog.dismiss();

    //
    this.handleChangeDetection();
  }

  private handleChangeDetection() {
    //
    this.tabsActionProvider.next({
      action: XTabsAction.Refresh,
    });

    //
    this.listActionProvider.next({
      action: XListAction.Refresh,
    });

    //
    this.actionBarActionProvider.next({
      action: XActionBarAction.Refresh,
    });

    //
    this.searchBarActionProvider.next({
      action: XSearchBarAction.Refresh,
    });

    //
    this.themeUploadActionProvider.next({
      action: XFileUploadAction.Refresh,
    });

    //
    this.themeUploadListActionProvider.next({
      action: XListAction.Refresh,
    });

    //
    this.themeModelPresenterActionProvider.next({
      action: XModelPresenterAction.Refresh,
    });

    //
    this.themeUploadFileDropAreaActionProvider.next({
      action: XFileUploadAction.Refresh,
    });

    //
    this.managerService.dispatchResizeEvent();

    //
    this.detectChanges();
  }

  private async presentLoading() {
    //
    this.loadingObject = await this.managerService.dialogService.presentLoading(
      {
        message: this.resourceProvider(this.ResourceIDs.loading),
      }
    );
  }

  private async changeTab(tab?: XThemeManagerTabs) {
    //
    if (!tab) {
      tab = this.Tabs.List;
    }

    //
    //
    // Prevent go forward if args is not ready ...
    if (this.loading || this.uiDisabled || this.currentTab === tab) {
      return;
    }

    //
    //#region Pre Change Actions ...
    //
    // Based On Current Tab ...
    switch (this.currentTab) {
      //
      case this.Tabs.List:
        break;

      //
      case this.Tabs.Details:
        break;

      //
      case this.Tabs.Import:
        break;

      //
      case this.Tabs.Edit:
        break;
    }

    //
    // Based On Dest Tab ...
    switch (tab) {
      //
      case this.Tabs.List:
        this.baseTheme = null;
        this.selectedTheme = null;
        break;

      //
      case this.Tabs.Details:
        //
        if (isNullOrUndefined(this.selectedTheme)) {
          return;
        }
        break;

      //
      case this.Tabs.Import:
        break;

      //
      case this.Tabs.Edit:
        //
        if (isNullOrUndefined(this.selectedTheme)) {
          return;
        }
        break;
    }
    //#endregion

    //
    // Change Current Tab ...
    this.currentTab = tab;
    this.detectChanges();

    //
    //#region Post Change Actions ...
    switch (tab) {
      //
      case this.Tabs.List:
        break;

      //
      case this.Tabs.Details:
        break;

      //
      case this.Tabs.Import:
        break;

      //
      case this.Tabs.Edit:
        break;
    }
    //#endregion

    //
    this.tabChange.emit(tab);
    this.handleChangeDetection();
  }

  private async handleUploadTheme() {
    await this.changeTab(this.Tabs.Import);
  }

  private async handleThemesImport() {
    //
    if (!this.hasChild(this.uploadThemes)) {
      return;
    }

    //
    const emptyTheme = getEmptyThemePack();
    const importedThemes = await this.getArrayValueAsync(this.uploadThemes);

    //
    importedThemes.forEach(async (themeFile) => {
      //
      const fileReader = new FileReader();
      fileReader.readAsText(themeFile);
      fileReader.onloadend = async (e) => {
        //
        if (!e || !e.target || !e.target.result) {
          return;
        }

        //
        try {
          //
          // Check Theme Valid ...
          const themePack = parseJson<XThemePack>(e.target.result.toString());
          if (!themePack) {
            //
            await this.issueThemeError(
              themeFile.name,
              XExceptionIDs.InavlidFile
            );

            //
            return;
          }

          //
          // Check Theme Type ...
          const isCorrectThemeType = isSameType(themePack, emptyTheme);
          if (!isCorrectThemeType) {
            //
            await this.issueThemeError(
              themeFile.name,
              XExceptionIDs.UnsupportedFileType
            );

            //
            return;
          }

          //
          // Check Theme Name and Existance ...
          if (
            isNullOrEmptyString(themePack.name) ||
            (await this.managerService.themeManagerService.isThemeExists(
              themePack.name
            ))
          ) {
            //
            this.issueThemeError(themeFile.name, XExceptionIDs.Duplicate);

            //
            return;
          }

          //
          const isSaved = await this.saveTheme(themePack);
          if (isSaved) {
            await this.prepareListItems();
          } else {
            //
            await this.issueThemeError(
              themeFile.name,
              XExceptionIDs.ActionFailed
            );

            //
            return;
          }
        } catch {
          await this.issueThemeError(themeFile.name, XExceptionIDs.InavlidFile);
        }
      };
    });

    //
    await this.back();
  }

  private async issueThemeError(fileName: string, exception: XExceptionIDs) {
    //
    if (isNullOrEmptyString(fileName)) {
      return;
    }

    //
    const message = `${this.managerService.localizationService.getErrorMessage(
      exception
    )}_${fileName}`;

    //
    await this.managerService.notificationService.presentDangerNotification({
      dissmissable: true,
      message,
    });
  }
  //#endregion

  //
  //#region ActionBar Actions ...
  private async prepareActionBarActions() {
    //
    //#region Prepare Default Actions ...
    const hideActions: string | string[] = [];

    //
    const defaultActions: XOneOrManyType<XActionBarItem> = [];

    //
    // Import ...
    const importId = DefaultActionBarItemIds.Upload.toString();
    if (!hideActions.includes(importId)) {
      defaultActions.push({
        //
        ...defaultActionBarUploadItem,

        //
        title: XResourceIDs.import,
        color: XColorWithBrightness.Success,

        //
        index: 1,
        disabled: this.uiDisabled,
      });
    }
    //#endregion

    //
    if (!this.actionBarActions) {
      //
      this.actionBarActions = defaultActions;
      this.handleChangeDetection();
      return;
    }

    //
    const actions = await this.getArrayValueAsync(this.actionBarActions);
    if (!hasChild(actions)) {
      //
      this.actionBarActions = defaultActions;
      this.handleChangeDetection();
      return;
    }

    //
    this.actionBarActions = actions;
    this.handleChangeDetection();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-theme\x-theme-manager\x-theme-manager.typings.ts

import {
  Input,
  Inject,
  NgZone,
  Output,
  Renderer2,
  Component,
  Injectable,
  ElementRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XResourceIDs,
  XBorderStyle,
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
  XBorderStyleIdentifier,
} from 'x-framework-core';
import {
  XCardContainer,
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../../x-card/x-card.typings';
import {
  XComponentListContainer,
  IXComponentListContainer,
} from '../../x-list/x-list.typings';
import {
  XFileContainer,
  IXFileContainer,
} from '../../classes/i-x-file.container';
import { concatMap, map } from 'rxjs/operators';
import {
  XActionBarActionFiredModel,
  XComponentActionBarContainer,
  IXComponentActionBarContainer,
} from '../../x-action-bar/x-action-bar.typings';
import { ViewportRuler } from '@angular/cdk/overlay';
import { forkJoin, Observable, Subject } from 'rxjs';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../../classes/x-base-action.model';
import { XIconIdentifier, XIconNames } from '../../x-icon/x-icon.typings';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../../tokens/x-injectable-tokens';
import { IXFileUploadContainer } from '../../x-file-upload/x-file-upload.typings';
import { XFileUploadContainer } from './../../x-file-upload/x-file-upload.typings';
import { XSearchBarEventIdentifier } from '../../x-search-bar/x-search-bar.typings';
import { XFrameworkComponentsConfig } from '../../config/x-framework-components.config';
import { XColorPickerDialogService } from '../../x-color-picker/x-color-picker-dialog/x-color-picker-dialog.service';

export enum XThemeManagerAction {
  //
  Back = 'back',
  Refresh = 'refresh',
}

export type XThemeManagerActionIdentifier = XThemeManagerAction | string;

export interface XThemeManagerActionModel
  extends XBaseActionModel<XThemeManagerActionIdentifier> {}

export enum XThemeManagerTabs {
  List,
  Details,
  Edit,
  Import,
}

//
//#region Inside Card Container ...
//
type TXSelectedComponentListContainer = Pick<
  IXComponentListContainer,
  | 'listCssClass'
  | 'listShowDivider'
  //
  // List Empty ...
  | 'listShowEmpty'
  | 'listEmptyActionProvider'
  | 'listEmptyColor'
  | 'listEmptyCssClass'
  | 'listEmptyForegroundColor'
  | 'listEmptyForegroundMessageColor'
  | 'listEmptyForegroundRefreshButtonColor'
  | 'listEmptyHasRefresh'
  | 'listEmptyMessage'
  | 'listEmptyMessageColor'
  | 'listEmptyRefresh'
  | 'listEmptyRefreshButtonColor'
  | 'listEmptyRefreshIcon'
  //
  // List Search ...
  | 'listSearchBarValue'
  | 'listSearchBarColor'
  | 'listSearchBarDebounce'
  | 'listSearchBarSearchInputColor'
  | 'listSearchBarForegroundSearchInputColor'
  | 'listSearchBarCancelButtonIcon'
  | 'listSearchBarCancelButtonTitle'
  | 'listSearchBarClearButtonIcon'
  | 'listSearchBarClearButtonIconColor'
  | 'listSearchBarCssClass'
  | 'listSearchBarEnterKeyHint'
  | 'listSearchBarForegroundColor'
  | 'listSearchBarKeyboardType'
  | 'listActionBarActionFired'
  | 'listSearchBarOnEvent'
  | 'listSearchBarQueryChange'
  | 'listSearchBarSearchButtonIcon'
  | 'listSearchBarSearchButtonIconColor'
  | 'listSearchBarShowCancelButton'
  | 'listSearchBarSpellcheck'
  | 'listSearchBarTitle'
  | 'listShowSearchBar'
>;

//
type TXSelectedComponentActionBarContainer = Pick<
  IXComponentActionBarContainer,
  | 'showActionBar'
  | 'actionBarColor'
  | 'actionBarCssClass'
  | 'actionBarForegroundColor'
>;

//
type TXSelectedComponentFileUploadContainer = Pick<
  {
    [TKey in keyof IXFileUploadContainer as `themeUpload${Capitalize<
      string & TKey
    >}`]: IXFileUploadContainer[TKey];
  },
  //
  | 'themeUploadColor'
  | 'themeUploadCssClass'
  | 'themeUploadEmptyMessage'
  | 'themeUploadForegroundColor'
  | 'themeUploadFileDropAreaIcon'
  | 'themeUploadShowFileDropArea'
  | 'themeUploadFileDropAreaColor'
  | 'themeUploadFileDropAreaMessage'
  | 'themeUploadFileDropAreaCssClass'
  | 'themeUploadFileDropAreaErrorColor'
  | 'themeUploadFileDropAreaAccentColor'
  | 'themeUploadFileDropAreaSuccessColor'
  | 'themeUploadFileDropAreaForegroundColor'
  | 'themeUploadEmptyForegroundMessageColor'
  | 'themeUploadFileDropAreaSupportsMultipleFiles'
>;

//
type TXSelectedFileContainer = Pick<
  IXFileContainer,
  | 'maxAllowedSize'
  | 'minAllowedFileSize'
  | 'maxAllowedFileSize'
  | 'maxAllowedFiles'
>;

//
type TXThemeManagerContainer = IXComponentCardContainer &
  TXSelectedComponentActionBarContainer &
  TXSelectedComponentListContainer &
  TXSelectedComponentFileUploadContainer &
  TXSelectedFileContainer & {};

//
export interface IXThemeManagerContainer extends TXThemeManagerContainer {
  //
  actionProvider: Subject<XThemeManagerActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  color: XStandardType<XColorIdentifier>;

  //
  themeChange: EventEmitter<boolean>;
  tabChange: EventEmitter<XStandardType<number>>;

  //
  restartAfterSetTheme: XStandardType<boolean>;
  colorViewBorderWidth: XStandardType<number>;
  colorViewBorderStyle: XStandardType<XBorderStyleIdentifier>;

  //
  propertyLabelColor: XStandardType<XColorIdentifier>;
  propertyValueColor: XStandardType<XColorIdentifier>;
  colorViewBorderColor: XStandardType<XColorIdentifier>;

  //
  propertyActionMenuIconColor: XStandardType<XColorIdentifier>;
  propertyActionMenuIcon: XStandardType<XIconIdentifier>;
  propertyEditIconColor: XStandardType<XColorIdentifier>;
  propertyEditIcon: XStandardType<XIconIdentifier>;
  propertyClearIconColor: XStandardType<XColorIdentifier>;
  propertyClearIcon: XStandardType<XIconIdentifier>;
  emptyPropertyEditIconColor: XStandardType<XColorIdentifier>;

  //
  presenterHeaderColor: XStandardType<XColorIdentifier>;
  presenterContentColor: XStandardType<XColorIdentifier>;
  presenterHeaderHighlightColor: XStandardType<XColorIdentifier>;

  //
  saveColor: XStandardType<XColorIdentifier>;
  saveLabel: XStandardType<XResourceIdentifier>;
  resetColor: XStandardType<XColorIdentifier>;
  resetLabel: XStandardType<XResourceIdentifier>;
}

@Injectable({
  providedIn: 'root',
})
export class XThemeManagerContainer
  extends XComponentCardContainer
  implements Readonly<IXThemeManagerContainer>
{
  //
  //#region Props ...
  //
  //#region Self ...
  readonly actionProvider: Subject<XThemeManagerActionModel> =
    new Subject<XThemeManagerActionModel>();
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly color: XStandardType<XColorIdentifier> = XColorWithBrightness.Light;

  //
  readonly themeChange: EventEmitter<boolean> = new EventEmitter<boolean>();
  readonly tabChange: EventEmitter<XStandardType<number>> = new EventEmitter<
    XStandardType<number>
  >();

  //
  readonly restartAfterSetTheme: XStandardType<boolean> = true;
  readonly colorViewBorderWidth: XStandardType<number> = 1;
  readonly colorViewBorderStyle: XStandardType<XBorderStyleIdentifier> =
    XBorderStyle.Solid;

  //
  readonly propertyLabelColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Tertiary;
  readonly propertyValueColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly colorViewBorderColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightShade;

  //
  readonly propertyActionMenuIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly propertyActionMenuIcon: XStandardType<XIconIdentifier> =
    XIconNames.menu_vertical;
  readonly propertyEditIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Tertiary;
  readonly propertyEditIcon: XStandardType<XIconIdentifier> = XIconNames.edit;
  readonly propertyClearIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Danger;
  readonly propertyClearIcon: XStandardType<XIconIdentifier> = XIconNames.clear;
  readonly emptyPropertyEditIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Danger;

  //
  readonly presenterHeaderColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Light;
  readonly presenterContentColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;
  readonly presenterHeaderHighlightColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.LightShade;

  //
  readonly saveColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Tertiary;
  readonly saveLabel: XStandardType<XResourceIdentifier> = XResourceIDs.save;

  //
  readonly resetColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.WarningShade;
  readonly resetLabel: XStandardType<XResourceIdentifier> = XResourceIDs.reset;
  //#endregion

  //
  //#region ActionBar ...
  readonly showActionBar = this.actionBarPropertyProvider.showActionBar;
  readonly actionBarColor = this.actionBarPropertyProvider.actionBarColor;
  readonly actionBarCssClass = this.actionBarPropertyProvider.actionBarCssClass;
  readonly actionBarForegroundColor =
    this.actionBarPropertyProvider.actionBarForegroundColor;
  //#endregion

  //
  //#region List ...
  readonly listCssClass = this.listPropertyProvider.listCssClass;
  readonly listShowDivider = this.listPropertyProvider.listShowDivider;

  //
  //#region List Empty ...
  readonly listShowEmpty = this.listPropertyProvider.listShowEmpty;
  readonly listEmptyActionProvider =
    this.listPropertyProvider.listEmptyActionProvider;
  readonly listEmptyColor = this.listPropertyProvider.listEmptyColor;
  readonly listEmptyCssClass = this.listPropertyProvider.listEmptyCssClass;
  readonly listEmptyForegroundColor =
    this.listPropertyProvider.listEmptyForegroundColor;
  readonly listEmptyForegroundMessageColor =
    this.listPropertyProvider.listEmptyForegroundMessageColor;
  readonly listEmptyForegroundRefreshButtonColor =
    this.listPropertyProvider.listEmptyForegroundRefreshButtonColor;
  readonly listEmptyHasRefresh = this.listPropertyProvider.listEmptyHasRefresh;
  readonly listEmptyMessage = this.listPropertyProvider.listEmptyMessage;
  readonly listEmptyMessageColor =
    this.listPropertyProvider.listEmptyMessageColor;
  readonly listEmptyRefresh = this.listPropertyProvider.listEmptyRefresh;
  readonly listEmptyRefreshButtonColor =
    this.listPropertyProvider.listEmptyRefreshButtonColor;
  readonly listEmptyRefreshIcon =
    this.listPropertyProvider.listEmptyRefreshIcon;
  //#endregion

  //
  //#region List Search ...
  readonly listSearchBarValue = this.listPropertyProvider.listSearchBarValue;
  readonly listSearchBarColor = this.listPropertyProvider.listSearchBarColor;
  readonly listSearchBarDebounce =
    this.listPropertyProvider.listSearchBarDebounce;
  readonly listSearchBarSearchInputColor =
    this.listPropertyProvider.listSearchBarSearchInputColor;
  readonly listSearchBarForegroundSearchInputColor =
    this.listPropertyProvider.listSearchBarForegroundSearchInputColor;
  readonly listSearchBarCancelButtonIcon =
    this.listPropertyProvider.listSearchBarCancelButtonIcon;
  readonly listSearchBarCancelButtonTitle =
    this.listPropertyProvider.listSearchBarCancelButtonTitle;
  readonly listSearchBarClearButtonIcon =
    this.listPropertyProvider.listSearchBarClearButtonIcon;
  readonly listSearchBarClearButtonIconColor =
    this.listPropertyProvider.listSearchBarClearButtonIconColor;
  readonly listSearchBarCssClass =
    this.listPropertyProvider.listSearchBarCssClass;
  readonly listSearchBarEnterKeyHint =
    this.listPropertyProvider.listSearchBarEnterKeyHint;
  readonly listSearchBarForegroundColor =
    this.listPropertyProvider.listSearchBarForegroundColor;
  readonly listSearchBarKeyboardType =
    this.listPropertyProvider.listSearchBarKeyboardType;
  readonly listActionBarActionFired =
    this.listPropertyProvider.listActionBarActionFired;
  readonly listSearchBarOnEvent =
    this.listPropertyProvider.listSearchBarOnEvent;
  readonly listSearchBarQueryChange =
    this.listPropertyProvider.listSearchBarQueryChange;
  readonly listSearchBarSearchButtonIcon =
    this.listPropertyProvider.listSearchBarSearchButtonIcon;
  readonly listSearchBarSearchButtonIconColor =
    this.listPropertyProvider.listSearchBarSearchButtonIconColor;
  readonly listSearchBarShowCancelButton =
    this.listPropertyProvider.listSearchBarShowCancelButton;
  readonly listSearchBarSpellcheck =
    this.listPropertyProvider.listSearchBarSpellcheck;
  readonly listSearchBarTitle = this.listPropertyProvider.listSearchBarTitle;
  readonly listShowSearchBar = this.listPropertyProvider.listShowSearchBar;
  //#endregion
  //#endregion

  //
  //#region File Upload ...
  readonly themeUploadColor = this.fileUploadPropertyProvider.color;
  readonly themeUploadCssClass = this.fileUploadPropertyProvider.cssClass;
  readonly themeUploadEmptyMessage =
    this.fileUploadPropertyProvider.emptyMessage;
  readonly themeUploadForegroundColor =
    this.fileUploadPropertyProvider.foregroundColor;
  readonly themeUploadFileDropAreaIcon =
    this.fileUploadPropertyProvider.fileDropAreaIcon;
  readonly themeUploadShowFileDropArea =
    this.fileUploadPropertyProvider.showFileDropArea;
  readonly themeUploadFileDropAreaColor =
    this.fileUploadPropertyProvider.fileDropAreaColor;
  readonly themeUploadFileDropAreaMessage =
    this.fileUploadPropertyProvider.fileDropAreaMessage;
  readonly themeUploadFileDropAreaCssClass =
    this.fileUploadPropertyProvider.fileDropAreaCssClass;
  readonly themeUploadFileDropAreaErrorColor =
    this.fileUploadPropertyProvider.fileDropAreaErrorColor;
  readonly themeUploadFileDropAreaAccentColor =
    this.fileUploadPropertyProvider.fileDropAreaAccentColor;
  readonly themeUploadFileDropAreaSuccessColor =
    this.fileUploadPropertyProvider.fileDropAreaSuccessColor;
  readonly themeUploadFileDropAreaForegroundColor =
    this.fileUploadPropertyProvider.fileDropAreaForegroundColor;
  readonly themeUploadEmptyForegroundMessageColor =
    this.fileUploadPropertyProvider.emptyForegroundMessageColor;
  readonly themeUploadFileDropAreaSupportsMultipleFiles =
    this.fileUploadPropertyProvider.fileDropAreaSupportsMultipleFiles;
  //#endregion

  //
  //#region File ...
  readonly maxAllowedSize = this.filePropertyProvider.maxAllowedSize;
  readonly minAllowedFileSize = this.filePropertyProvider.minAllowedFileSize;
  readonly maxAllowedFileSize = this.filePropertyProvider.maxAllowedFileSize;
  readonly maxAllowedFiles = this.filePropertyProvider.maxAllowedFiles;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  constructor(
    protected filePropertyProvider: XFileContainer,
    protected cardPropertyProvider: XCardContainer,
    protected listPropertyProvider: XComponentListContainer,
    protected fileUploadPropertyProvider: XFileUploadContainer,
    protected actionBarPropertyProvider: XComponentActionBarContainer
  ) {
    super(cardPropertyProvider);
  }
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XThemeManagerContainerComponent
  extends XComponentCardContainerComponent
  implements IXThemeManagerContainer
{
  //
  //#region Props ...
  //
  //#region Self ...
  @Input()
  actionProvider: Subject<XThemeManagerActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  color: XStandardType<XColorIdentifier> = this.propertyProvider.color;

  //
  @Output()
  themeChange: EventEmitter<boolean> = new EventEmitter();

  @Output()
  tabChange: EventEmitter<XStandardType<number>> = new EventEmitter();

  //
  @Input()
  restartAfterSetTheme: XStandardType<boolean> =
    this.propertyProvider.restartAfterSetTheme;

  @Input()
  colorViewBorderWidth: XStandardType<number> =
    this.propertyProvider.colorViewBorderWidth;

  @Input()
  colorViewBorderStyle: XStandardType<XBorderStyleIdentifier> =
    this.propertyProvider.colorViewBorderStyle;

  //
  @Input()
  propertyLabelColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.propertyLabelColor;

  @Input()
  propertyValueColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.propertyValueColor;

  @Input()
  colorViewBorderColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.colorViewBorderColor;

  //
  @Input()
  propertyActionMenuIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.propertyActionMenuIconColor;

  @Input()
  propertyActionMenuIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.propertyActionMenuIcon;

  @Input()
  propertyEditIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.propertyEditIconColor;

  @Input()
  propertyEditIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.propertyEditIcon;

  @Input()
  propertyClearIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.propertyClearIconColor;

  @Input()
  propertyClearIcon: XStandardType<XIconIdentifier> =
    this.propertyProvider.propertyClearIcon;

  @Input()
  emptyPropertyEditIconColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.emptyPropertyEditIconColor;

  //
  @Input()
  presenterHeaderColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.presenterHeaderColor;

  @Input()
  presenterContentColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.presenterContentColor;

  @Input()
  presenterHeaderHighlightColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.presenterHeaderHighlightColor;

  //
  @Input()
  saveColor: XStandardType<XColorIdentifier> = this.propertyProvider.saveColor;

  @Input()
  saveLabel: XStandardType<XResourceIdentifier> =
    this.propertyProvider.saveLabel;

  //
  @Input()
  resetColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.resetColor;

  @Input()
  resetLabel: XStandardType<XResourceIdentifier> =
    this.propertyProvider.resetLabel;
  //#endregion

  //
  //#region ActionBar ...
  @Input()
  showActionBar: XStandardType<boolean> = this.propertyProvider.showActionBar;

  @Input()
  actionBarColor: XStandardType<XColorIdentifier> =
    this.propertyProvider.actionBarColor;

  @Input()
  actionBarCssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.actionBarCssClass;

  @Input()
  actionBarForegroundColor: XStandardType<boolean> =
    this.propertyProvider.actionBarForegroundColor;
  //#endregion

  //
  //#region List ...
  @Input()
  listCssClass = this.propertyProvider.listCssClass;

  @Input()
  listShowDivider = this.propertyProvider.listShowDivider;

  //
  //#region List Empty ...
  @Input()
  listShowEmpty = this.propertyProvider.listShowEmpty;

  @Input()
  listEmptyActionProvider = this.propertyProvider.listEmptyActionProvider;

  @Input()
  listEmptyColor = this.propertyProvider.listEmptyColor;

  @Input()
  listEmptyCssClass = this.propertyProvider.listEmptyCssClass;

  @Input()
  listEmptyForegroundColor = this.propertyProvider.listEmptyForegroundColor;

  @Input()
  listEmptyForegroundMessageColor =
    this.propertyProvider.listEmptyForegroundMessageColor;

  @Input()
  listEmptyForegroundRefreshButtonColor =
    this.propertyProvider.listEmptyForegroundRefreshButtonColor;

  @Input()
  listEmptyHasRefresh = this.propertyProvider.listEmptyHasRefresh;

  @Input()
  listEmptyMessage = this.propertyProvider.listEmptyMessage;

  @Input()
  listEmptyMessageColor = this.propertyProvider.listEmptyMessageColor;

  @Output()
  listEmptyRefresh: EventEmitter<void> = new EventEmitter();

  @Input()
  listEmptyRefreshButtonColor =
    this.propertyProvider.listEmptyRefreshButtonColor;

  @Input()
  listEmptyRefreshIcon = this.propertyProvider.listEmptyRefreshIcon;
  //#endregion

  //
  //#region List Search ...
  @Input()
  listSearchBarValue = this.propertyProvider.listSearchBarValue;

  @Input()
  listSearchBarColor = this.propertyProvider.listSearchBarColor;

  @Input()
  listSearchBarDebounce = this.propertyProvider.listSearchBarDebounce;

  @Input()
  listSearchBarSearchInputColor =
    this.propertyProvider.listSearchBarSearchInputColor;

  @Input()
  listSearchBarForegroundSearchInputColor =
    this.propertyProvider.listSearchBarForegroundSearchInputColor;

  @Input()
  listSearchBarCancelButtonIcon =
    this.propertyProvider.listSearchBarCancelButtonIcon;

  @Input()
  listSearchBarCancelButtonTitle =
    this.propertyProvider.listSearchBarCancelButtonTitle;

  @Input()
  listSearchBarClearButtonIcon =
    this.propertyProvider.listSearchBarClearButtonIcon;

  @Input()
  listSearchBarClearButtonIconColor =
    this.propertyProvider.listSearchBarClearButtonIconColor;

  @Input()
  listSearchBarCssClass = this.propertyProvider.listSearchBarCssClass;

  @Input()
  listSearchBarEnterKeyHint = this.propertyProvider.listSearchBarEnterKeyHint;

  @Input()
  listSearchBarForegroundColor =
    this.propertyProvider.listSearchBarForegroundColor;

  @Input()
  listSearchBarKeyboardType = this.propertyProvider.listSearchBarKeyboardType;

  @Output()
  listActionBarActionFired: EventEmitter<XActionBarActionFiredModel> = new EventEmitter<XActionBarActionFiredModel>();

  @Output()
  listSearchBarOnEvent: EventEmitter<XSearchBarEventIdentifier> = new EventEmitter();

  @Output()
  listSearchBarQueryChange: EventEmitter<string> = new EventEmitter();

  @Input()
  listSearchBarSearchButtonIcon =
    this.propertyProvider.listSearchBarSearchButtonIcon;

  @Input()
  listSearchBarSearchButtonIconColor =
    this.propertyProvider.listSearchBarSearchButtonIconColor;

  @Input()
  listSearchBarShowCancelButton =
    this.propertyProvider.listSearchBarShowCancelButton;

  @Input()
  listSearchBarSpellcheck = this.propertyProvider.listSearchBarSpellcheck;

  @Input()
  listSearchBarTitle = this.propertyProvider.listSearchBarTitle;

  @Input()
  listShowSearchBar = this.propertyProvider.listShowSearchBar;
  //#endregion
  //#endregion

  //
  //#region File Upload ...
  @Input()
  themeUploadColor = this.propertyProvider.themeUploadColor;

  @Input()
  themeUploadCssClass = this.propertyProvider.themeUploadCssClass;

  @Input()
  themeUploadEmptyMessage = this.propertyProvider.themeUploadEmptyMessage;

  @Input()
  themeUploadForegroundColor = this.propertyProvider.themeUploadForegroundColor;

  @Input()
  themeUploadFileDropAreaIcon =
    this.propertyProvider.themeUploadFileDropAreaIcon;

  @Input()
  themeUploadShowFileDropArea =
    this.propertyProvider.themeUploadShowFileDropArea;

  @Input()
  themeUploadFileDropAreaColor =
    this.propertyProvider.themeUploadFileDropAreaColor;

  @Input()
  themeUploadFileDropAreaMessage =
    this.propertyProvider.themeUploadFileDropAreaMessage;

  @Input()
  themeUploadFileDropAreaCssClass =
    this.propertyProvider.themeUploadFileDropAreaCssClass;

  @Input()
  themeUploadFileDropAreaErrorColor =
    this.propertyProvider.themeUploadFileDropAreaErrorColor;

  @Input()
  themeUploadFileDropAreaAccentColor =
    this.propertyProvider.themeUploadFileDropAreaAccentColor;

  @Input()
  themeUploadFileDropAreaSuccessColor =
    this.propertyProvider.themeUploadFileDropAreaSuccessColor;

  @Input()
  themeUploadFileDropAreaForegroundColor =
    this.propertyProvider.themeUploadFileDropAreaForegroundColor;

  @Input()
  themeUploadEmptyForegroundMessageColor =
    this.propertyProvider.themeUploadEmptyForegroundMessageColor;

  @Input()
  themeUploadFileDropAreaSupportsMultipleFiles =
    this.propertyProvider.themeUploadFileDropAreaSupportsMultipleFiles;
  //#endregion

  //
  //#region File ...
  @Input()
  maxAllowedSize = this.propertyProvider.maxAllowedSize;

  @Input()
  minAllowedFileSize = this.propertyProvider.minAllowedFileSize;

  @Input()
  maxAllowedFileSize = this.propertyProvider.maxAllowedFileSize;

  @Input()
  maxAllowedFiles = this.propertyProvider.maxAllowedFiles;
  //#endregion
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XThemeManagerContainer,
    public colorPickerDialogService: XColorPickerDialogService
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXThemeManagerContainer
  ): Observable<IXThemeManagerContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            restartAfterSetTheme: this.getValue(
              container
                ? container.restartAfterSetTheme
                : this.restartAfterSetTheme
            ),
            colorViewBorderWidth: this.getValue(
              container
                ? container.colorViewBorderWidth
                : this.colorViewBorderWidth
            ),
            colorViewBorderStyle: this.getValue(
              container
                ? container.colorViewBorderStyle
                : this.colorViewBorderStyle
            ),

            //
            propertyLabelColor: this.getValue(
              container ? container.propertyLabelColor : this.propertyLabelColor
            ),
            propertyValueColor: this.getValue(
              container ? container.propertyValueColor : this.propertyValueColor
            ),
            colorViewBorderColor: this.getValue(
              container
                ? container.colorViewBorderColor
                : this.colorViewBorderColor
            ),
            color: this.getValue(container ? container.color : this.color),

            //
            propertyActionMenuIconColor: this.getValue(
              container
                ? container.propertyActionMenuIconColor
                : this.propertyActionMenuIconColor
            ),
            propertyActionMenuIcon: this.getValue(
              container
                ? container.propertyActionMenuIcon
                : this.propertyActionMenuIcon
            ),
            propertyEditIconColor: this.getValue(
              container
                ? container.propertyEditIconColor
                : this.propertyEditIconColor
            ),
            propertyEditIcon: this.getValue(
              container ? container.propertyEditIcon : this.propertyEditIcon
            ),
            propertyClearIconColor: this.getValue(
              container
                ? container.propertyClearIconColor
                : this.propertyClearIconColor
            ),
            propertyClearIcon: this.getValue(
              container ? container.propertyClearIcon : this.propertyClearIcon
            ),
            emptyPropertyEditIconColor: this.getValue(
              container
                ? container.emptyPropertyEditIconColor
                : this.emptyPropertyEditIconColor
            ),

            //
            presenterHeaderColor: this.getValue(
              container
                ? container.presenterHeaderColor
                : this.presenterHeaderColor
            ),
            presenterContentColor: this.getValue(
              container
                ? container.presenterContentColor
                : this.presenterContentColor
            ),
            presenterHeaderHighlightColor: this.getValue(
              container
                ? container.presenterHeaderHighlightColor
                : this.presenterHeaderHighlightColor
            ),

            //
            saveColor: this.getValue(
              container ? container.saveColor : this.saveColor
            ),
            saveLabel: this.getValue(
              container ? container.saveLabel : this.saveLabel
            ),

            //
            resetColor: this.getValue(
              container ? container.resetColor : this.resetColor
            ),
            resetLabel: this.getValue(
              container ? container.resetLabel : this.resetLabel
            ),

            //
            showActionBar: this.getValue(
              container ? container.showActionBar : this.showActionBar
            ),
            actionBarColor: this.getValue(
              container ? container.actionBarColor : this.actionBarColor
            ),
            actionBarCssClass: this.getValue(
              container ? container.actionBarCssClass : this.actionBarCssClass
            ),
            actionBarForegroundColor: this.getValue(
              container
                ? container.actionBarForegroundColor
                : this.actionBarForegroundColor
            ),

            //
            listCssClass: this.getValue(
              container ? container.listCssClass : this.listCssClass
            ),
            listShowDivider: this.getValue(
              container ? container.listShowDivider : this.listShowDivider
            ),

            //
            listShowEmpty: this.getValue(
              container ? container.listShowEmpty : this.listShowEmpty
            ),
            listEmptyColor: this.getValue(
              container ? container.listEmptyColor : this.listEmptyColor
            ),
            listEmptyCssClass: this.getValue(
              container ? container.listEmptyCssClass : this.listEmptyCssClass
            ),
            listEmptyForegroundColor: this.getValue(
              container
                ? container.listEmptyForegroundColor
                : this.listEmptyForegroundColor
            ),
            listEmptyForegroundMessageColor: this.getValue(
              container
                ? container.listEmptyForegroundMessageColor
                : this.listEmptyForegroundMessageColor
            ),
            listEmptyForegroundRefreshButtonColor: this.getValue(
              container
                ? container.listEmptyForegroundRefreshButtonColor
                : this.listEmptyForegroundRefreshButtonColor
            ),
            listEmptyHasRefresh: this.getValue(
              container
                ? container.listEmptyHasRefresh
                : this.listEmptyHasRefresh
            ),
            listEmptyMessage: this.getValue(
              container ? container.listEmptyMessage : this.listEmptyMessage
            ),
            listEmptyMessageColor: this.getValue(
              container
                ? container.listEmptyMessageColor
                : this.listEmptyMessageColor
            ),
            listEmptyRefreshButtonColor: this.getValue(
              container
                ? container.listEmptyRefreshButtonColor
                : this.listEmptyRefreshButtonColor
            ),
            listEmptyRefreshIcon: this.getValue(
              container
                ? container.listEmptyRefreshIcon
                : this.listEmptyRefreshIcon
            ),

            //
            listSearchBarValue: this.getValue(
              container ? container.listSearchBarValue : this.listSearchBarValue
            ),
            listSearchBarColor: this.getValue(
              container ? container.listSearchBarColor : this.listSearchBarColor
            ),
            listSearchBarDebounce: this.getValue(
              container
                ? container.listSearchBarDebounce
                : this.listSearchBarDebounce
            ),
            listSearchBarSearchInputColor: this.getValue(
              container
                ? container.listSearchBarSearchInputColor
                : this.listSearchBarSearchInputColor
            ),
            listSearchBarForegroundSearchInputColor: this.getValue(
              container
                ? container.listSearchBarForegroundSearchInputColor
                : this.listSearchBarForegroundSearchInputColor
            ),
            listSearchBarCancelButtonIcon: this.getValue(
              container
                ? container.listSearchBarCancelButtonIcon
                : this.listSearchBarCancelButtonIcon
            ),
            listSearchBarCancelButtonTitle: this.getValue(
              container
                ? container.listSearchBarCancelButtonTitle
                : this.listSearchBarCancelButtonTitle
            ),
            listSearchBarClearButtonIcon: this.getValue(
              container
                ? container.listSearchBarClearButtonIcon
                : this.listSearchBarClearButtonIcon
            ),
            listSearchBarClearButtonIconColor: this.getValue(
              container
                ? container.listSearchBarClearButtonIconColor
                : this.listSearchBarClearButtonIconColor
            ),
            listSearchBarCssClass: this.getValue(
              container
                ? container.listSearchBarCssClass
                : this.listSearchBarCssClass
            ),
            listSearchBarEnterKeyHint: this.getValue(
              container
                ? container.listSearchBarEnterKeyHint
                : this.listSearchBarEnterKeyHint
            ),
            listSearchBarForegroundColor: this.getValue(
              container
                ? container.listSearchBarForegroundColor
                : this.listSearchBarForegroundColor
            ),
            listSearchBarKeyboardType: this.getValue(
              container
                ? container.listSearchBarKeyboardType
                : this.listSearchBarKeyboardType
            ),
            listSearchBarSearchButtonIcon: this.getValue(
              container
                ? container.listSearchBarSearchButtonIcon
                : this.listSearchBarSearchButtonIcon
            ),
            listSearchBarSearchButtonIconColor: this.getValue(
              container
                ? container.listSearchBarSearchButtonIconColor
                : this.listSearchBarSearchButtonIconColor
            ),
            listSearchBarShowCancelButton: this.getValue(
              container
                ? container.listSearchBarShowCancelButton
                : this.listSearchBarShowCancelButton
            ),
            listSearchBarSpellcheck: this.getValue(
              container
                ? container.listSearchBarSpellcheck
                : this.listSearchBarSpellcheck
            ),
            listSearchBarTitle: this.getValue(
              container ? container.listSearchBarTitle : this.listSearchBarTitle
            ),
            listShowSearchBar: this.getValue(
              container ? container.listShowSearchBar : this.listShowSearchBar
            ),

            //
            themeUploadColor: this.getValue(
              container ? container.themeUploadColor : this.themeUploadColor
            ),
            themeUploadCssClass: this.getValue(
              container
                ? container.themeUploadCssClass
                : this.themeUploadCssClass
            ),
            themeUploadEmptyMessage: this.getValue(
              container
                ? container.themeUploadEmptyMessage
                : this.themeUploadEmptyMessage
            ),
            themeUploadForegroundColor: this.getValue(
              container
                ? container.themeUploadForegroundColor
                : this.themeUploadForegroundColor
            ),
            themeUploadFileDropAreaIcon: this.getValue(
              container
                ? container.themeUploadFileDropAreaIcon
                : this.themeUploadFileDropAreaIcon
            ),
            themeUploadShowFileDropArea: this.getValue(
              container
                ? container.themeUploadShowFileDropArea
                : this.themeUploadShowFileDropArea
            ),
            themeUploadFileDropAreaColor: this.getValue(
              container
                ? container.themeUploadFileDropAreaColor
                : this.themeUploadFileDropAreaColor
            ),
            themeUploadFileDropAreaMessage: this.getValue(
              container
                ? container.themeUploadFileDropAreaMessage
                : this.themeUploadFileDropAreaMessage
            ),
            themeUploadFileDropAreaCssClass: this.getValue(
              container
                ? container.themeUploadFileDropAreaCssClass
                : this.themeUploadFileDropAreaCssClass
            ),
            themeUploadFileDropAreaErrorColor: this.getValue(
              container
                ? container.themeUploadFileDropAreaErrorColor
                : this.themeUploadFileDropAreaErrorColor
            ),
            themeUploadFileDropAreaAccentColor: this.getValue(
              container
                ? container.themeUploadFileDropAreaAccentColor
                : this.themeUploadFileDropAreaAccentColor
            ),
            themeUploadFileDropAreaSuccessColor: this.getValue(
              container
                ? container.themeUploadFileDropAreaSuccessColor
                : this.themeUploadFileDropAreaSuccessColor
            ),
            themeUploadFileDropAreaForegroundColor: this.getValue(
              container
                ? container.themeUploadFileDropAreaForegroundColor
                : this.themeUploadFileDropAreaForegroundColor
            ),
            themeUploadEmptyForegroundMessageColor: this.getValue(
              container
                ? container.themeUploadEmptyForegroundMessageColor
                : this.themeUploadEmptyForegroundMessageColor
            ),
            themeUploadFileDropAreaSupportsMultipleFiles: this.getValue(
              container
                ? container.themeUploadFileDropAreaSupportsMultipleFiles
                : this.themeUploadFileDropAreaSupportsMultipleFiles
            ),

            //
            maxAllowedSize: this.getValue(
              container ? container.maxAllowedSize : this.maxAllowedSize
            ),
            minAllowedFileSize: this.getValue(
              container ? container.minAllowedFileSize : this.minAllowedFileSize
            ),
            maxAllowedFileSize: this.getValue(
              container ? container.maxAllowedFileSize : this.maxAllowedFileSize
            ),
            maxAllowedFiles: this.getValue(
              container ? container.maxAllowedFiles : this.maxAllowedFiles
            ),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            themeChange: this.themeChange,
            tabChange: this.tabChange,

            //
            listEmptyActionProvider: container
              ? container.listEmptyActionProvider
              : this.listEmptyActionProvider,

            //
            listEmptyRefresh: this.listEmptyRefresh,

            //
            listActionBarActionFired: this.listActionBarActionFired,
            listSearchBarOnEvent: this.listSearchBarOnEvent,
            listSearchBarQueryChange: this.listSearchBarQueryChange,
          };
        })
      );
  }
  //#endregion
}

function XStandardType<T>() {
  throw new Error('Function not implemented.');
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-thumbnail\x-thumbnail.component.html

<div
  (click)="handleClicked()"
  class="x-thumbnail x-wrapper"
  *ngIf="preparedIdentifier"
  [class.mobile]="isMobileUi$ | async"
  [ngClass]="getValue(cssClass) | async"
>
  <ion-thumbnail
    *ngIf="preparedIdentifier"
    class="x-thumbnail push-auto"
    [class.circular]="getValue(circular) | async"
    [class.ion-activatable]="
      (getValue(selectable) | async) || (getValue(clickable) | async)
    "
  >
    <!-- Ripple Effect -->
    <ion-ripple-effect
      *ngIf="!(getValue(uiDisabled) | async)"
    ></ion-ripple-effect>

    <!-- General Type -->
    <ng-container *ngIf="isTypeGeneral() | async">
      <ng-container *ngTemplateOutlet="generalRef"></ng-container>
    </ng-container>

    <!-- Custom Type -->
    <ng-container *ngIf="isTypeCustom() | async">
      <ng-container *ngTemplateOutlet="customRef"></ng-container>
    </ng-container>
  </ion-thumbnail>
</div>

<!-- General Ref -->
<ng-template #generalRef>
  <x-icon
    class="x-item"
    [name]="getGThumbnail(preparedIdentifier) | async"
  ></x-icon>
</ng-template>

<!-- By Url Ref -->
<ng-template #byUrlRef>
  <img class="x-item" [src]="preparedIdentifier" />
</ng-template>

<!-- Image Type Ref -->
<ng-template #imageTypeRef>
  <img
    #thumbImage
    class="x-item"
    [xFileReader]="preparedIdentifier"
    (onFileReaderFinish)="thumbImage.src = $event"
  />
</ng-template>

<!-- Custom Ref -->
<ng-template #customRef>
  <ng-container
    *ngTemplateOutlet="
      (isUrlIdentifier() | async)
        ? byUrlRef
        : (isImageTypeIdentifier() | async)
        ? imageTypeRef
        : generalRef
    "
  >
  </ng-container>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-thumbnail\x-thumbnail.component.scss

:host {
  --thumbnail-size: 32px;
}

x-icon {
  --size: var(--thumbnail-size, 32px);
  color: inherit;
}

.x-item,
.x-wrapper,
ion-thumbnail {
  //
  width: var(--thumbnail-size, 32px);
  min-width: var(--thumbnail-size, 32px);

  //
  height: var(--thumbnail-size, 32px);
  min-height: var(--thumbnail-size, 32px);

  //
  color: inherit;
}

ion-thumbnail.circular {
  --border-radius: 50%;
}

.x-thumbnail {
  //
  display: inline-block;
  position: relative;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-thumbnail\x-thumbnail.component.ts

import {
  nameof,
  isString,
  XFileType,
  getFileType,
  XGeneralThumbnails,
  getGeneralThumbnail,
} from 'x-framework-core';
import {
  XThumbnailType,
  XThumbnailAction,
  XThumbnailInputType,
  XThumbnailActionModel,
  XThumbnailContainerComponent,
} from './x-thumbnail.typings';
import { Subject, Subscription } from 'rxjs';
import { XIconNames } from '../x-icon/x-icon.typings';
import { map, filter, concatMap } from 'rxjs/operators';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-thumbnail',
  templateUrl: './x-thumbnail.component.html',
  styleUrls: ['./x-thumbnail.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XThumbnailComponent extends XThumbnailContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
  private PREPARED_IDENTIFIER: XThumbnailInputType;

  get preparedIdentifier() {
    return this.PREPARED_IDENTIFIER;
  }
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XThumbnailComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Identifier Changed ...
    const isIdentifierChanged = changeKeys.includes(
      nameof<XThumbnailComponent>('identifier')
    );
    if (isIdentifierChanged) {
      await this.prepareIdentifier();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  //
  //#region Type Providers ...
  isTypeGeneral() {
    return this.getValue(this.type).pipe(
      map(
        (type) =>
          type === XThumbnailType.GENERAL || type !== XThumbnailType.CUSTOM
      )
    );
  }

  isTypeCustom() {
    return this.getValue(this.type).pipe(
      map((type) => type === XThumbnailType.CUSTOM)
    );
  }
  //#endregion

  isUrlIdentifier() {
    //
    // && isValidURL(identifier as string)
    return this.getIdentifier().pipe(map((identifier) => isString(identifier)));
  }

  isImageTypeIdentifier() {
    return this.isUrlIdentifier().pipe(
      filter((isUrl) => !isUrl),
      concatMap(() => this.getIdentifier()),
      map((identifier) => {
        //
        const type = getFileType(identifier);
        return [
          XFileType.CoverImage,
          XFileType.Image,
          XFileType.ProfileImage,
        ].includes(type);
      })
    );
  }

  getGThumbnail(identifier: XThumbnailInputType) {
    return this.getValue(getGeneralThumbnail(identifier)).pipe(
      map((type) => {
        switch (type) {
          //
          case XGeneralThumbnails.IMAGE:
            return XIconNames.image;

          //
          case XGeneralThumbnails.AUDIO:
            return XIconNames.audio;

          //
          case XGeneralThumbnails.VIDEO:
            return XIconNames.video;

          //
          case XGeneralThumbnails.DOCUMENT:
          default:
            return XIconNames.document;
        }
      })
    );
  }
  //#endregion

  //
  //#region UI Handlers ...
  async handleClicked() {
    //
    if (this.uiDisabled || this.loading) {
      return;
    }

    //
    const isClickable = await this.getValueAsync(this.clickable);
    if (isClickable) {
      this.clicked.emit();
    }
  }
  //#endregion

  //
  //#region Actions ...
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XThumbnailActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XThumbnailAction.Refresh:
            this.detectChanges();
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  private getIdentifier() {
    return this.getValue(this.identifier);
  }

  private async prepareIdentifier() {
    //
    const identifier = await this.getValueAsync(this.identifier);
    this.PREPARED_IDENTIFIER = identifier;

    //
    this.detectChanges();
  }
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-thumbnail\x-thumbnail.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XIconModule } from '../x-icon/x-icon.module';
import { XThumbnailComponent } from './x-thumbnail.component';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XThumbnailComponent],
  imports: [CommonModule, XIconModule, XBaseComponentModule],
  exports: [XIconModule, XBaseComponentModule, XThumbnailComponent],
})
export class XThumbnailModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-thumbnail\x-thumbnail.typings.ts

import {
  Input,
  Output,
  Inject,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import { map } from 'rxjs/operators';
import {
  XBaseContainer,
  IXBaseContainer,
  XBaseContainerComponent,
} from '../classes/i-x-base.container';
import { Subject, forkJoin, Observable } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { XFileBlob, XOneOrManyType, XStandardType } from 'x-framework-core';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export enum XThumbnailType {
  GENERAL = 'general',
  CUSTOM = 'custom',
}

export type XThumbnailTypeIdentifier = XThumbnailType | string;

export type XThumbnailInputType = File | Blob | XFileBlob | string;

export enum XThumbnailAction {
  Refresh = 'refresh',
}

export type XThumbnailActionIdentifier = XThumbnailAction | string;

export interface XThumbnailActionModel
  extends XBaseActionModel<XThumbnailActionIdentifier> {}

//
//#region Container ...
export interface IXThumbnailContainer extends IXBaseContainer {
  //
  actionProvider: Subject<XThumbnailActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  circular: XStandardType<boolean>;
  clickable: XStandardType<boolean>;
  selectable: XStandardType<boolean>;
  type: XStandardType<XThumbnailTypeIdentifier>;
  identifier: XStandardType<XThumbnailInputType>;

  //
  clicked: EventEmitter<void>;
}

@Injectable({
  providedIn: 'root',
})
export class XThumbnailContainer
  extends XBaseContainer
  implements Readonly<IXThumbnailContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XThumbnailActionModel> =
    new Subject<XThumbnailActionModel>();

  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly circular: XStandardType<boolean> = false;

  readonly clickable: XStandardType<boolean> = false;

  readonly selectable: XStandardType<boolean> = false;

  readonly type: XStandardType<XThumbnailTypeIdentifier> = XThumbnailType.GENERAL;

  readonly identifier: XStandardType<XThumbnailInputType>;

  //
  readonly clicked: EventEmitter<void> = new EventEmitter();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XThumbnailContainerComponent
  extends XBaseContainerComponent
  implements IXThumbnailContainer
{
  //
  //#region Props ...
  @Input()
  actionProvider: Subject<XThumbnailActionModel> =
    this.propertyProvider.actionProvider;

  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  @Input()
  circular: XStandardType<boolean> = this.propertyProvider.circular;

  @Input()
  clickable: XStandardType<boolean> = this.propertyProvider.clickable;

  @Input()
  selectable: XStandardType<boolean> = this.propertyProvider.selectable;

  @Input()
  type: XStandardType<XThumbnailTypeIdentifier> = this.propertyProvider.type;

  @Input()
  identifier: XStandardType<XThumbnailInputType> =
    this.propertyProvider.identifier;

  @Output()
  clicked: EventEmitter<void> = new EventEmitter();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XThumbnailContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXThumbnailContainer
  ): Observable<IXThumbnailContainer> {
    return forkJoin({
      cssClass: this.getArrayValue(
        container ? container.cssClass : this.cssClass
      ),
      circular: this.getValue(container ? container.circular : this.circular),
      clickable: this.getValue(
        container ? container.clickable : this.clickable
      ),
      selectable: this.getValue(
        container ? container.selectable : this.selectable
      ),
      type: this.getValue(container ? container.type : this.type),
      identifier: this.getValue(
        container ? container.identifier : this.identifier
      ),
    }).pipe(
      map((data) => {
        return {
          //
          ...data,

          //
          actionProvider: container
            ? container.actionProvider
            : this.actionProvider,

          //
          clicked: this.clicked,
        };
      })
    );
  }
  //#endregion
}
//#endregion

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tree\x-tree.component.html

<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentRef">
  <x-card
    class="x-card"
    [showActions]="true"
    [color]="getValue(cardColor) | async"
    [ngClass]="getValue(cssClass) | async"
    [isInPage]="getValue(isInPage) | async"
    [showHeader]="
      (notEmptyValue(cardTitle) | async) ||
      (notEmptyValue(cardSubTitle) | async)
    "
    [subTitle]="getResourceValue(cardSubTitle)"
    [title]="getResourceValue(cardTitle) | async"
    [showFooter]="getValue(cardShowFooter) | async"
    [showActions]="getValue(cardShowActions) | async"
    [foregroundColor]="getValue(cardForegroundColor) | async"
  >
    <!-- Content -->
    <ng-container *ngTemplateOutlet="componentRef"></ng-container>

    <!-- Footer -->
    <div footer *ngIf="getValue(cardShowFooter) | async">
      <ng-content select="[footer]"></ng-content>
    </div>
  </x-card>
</ng-container>

<!-- Component Template -->
<ng-template #componentRef>
  <ng-container
    *ngIf="
      (notNullOrUndefinedValue(treeControl) | async) &&
      (notNullOrUndefinedValue(treeFlattener) | async) &&
      (notNullOrUndefinedValue(dataSource) | async) &&
      (hasChildValue(nodes) | async) &&
      (hasChildValue(dataSource.data) | async)
    "
  >
    <!-- Tree -->
    <mat-tree
      class="x-tree"
      [dataSource]="dataSource"
      [treeControl]="treeControl"
    >
      <!-- This is the tree node template for leaf nodes -->
      <mat-tree-node
        matTreeNodePadding
        *matTreeNodeDef="let node"
        [ngClass]="getNodeClassNames(node) | async"
      >
        <!-- Toggler Button -->
        <button mat-icon-button disabled></button>

        <!-- Body -->
        <div class="x-tree-node-container">
          <ng-container
            *ngTemplateOutlet="
              nodeTemplate;
              context: { $implicit: node.data, nodeId: node.id }
            "
          ></ng-container>
        </div>
      </mat-tree-node>

      <!-- This is the tree node template for expandable nodes -->
      <mat-tree-node
        matTreeNodePadding
        [ngClass]="getNodeClassNames(node) | async"
        *matTreeNodeDef="let node; when: nodeHasChild"
      >
        <!-- Toggler Button -->
        <button
          mat-icon-button
          matTreeNodeToggle
          [disabled]="loading || uiDisabled"
          [matTooltipDisabled]="loading || uiDisabled"
          [matTooltip]="getResourceValue(node.tooltip) | async"
          (click)="handleTreeNodeToggle(node.id, treeControl.isExpanded(node))"
        >
          <!-- Icon Button -->
          <mat-icon
            class="mat-icon-rtl-mirror"
            [xColor]="getValue(toggleIconColor) | async"
            [isForeground]="getValue(isForegroundToggleColor) | async"
          >
            {{ treeControl.isExpanded(node) ? "expand_more" : "chevron_right" }}
          </mat-icon>
        </button>

        <!-- Body -->
        <div class="x-tree-node-container">
          <ng-container
            *ngTemplateOutlet="
              nodeTemplate;
              context: { $implicit: node.data, nodeId: node.id }
            "
          ></ng-container>
        </div>
      </mat-tree-node>
    </mat-tree>
  </ng-container>
</ng-template>

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tree\x-tree.component.scss

:host(::ng-deep.mat-tree) {
  background-color: unset !important;
}

:host(::ng-deep.mat-tree-node) {
  align-items: baseline !important;
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tree\x-tree.component.ts

import {
  XTreeNode,
  XTreeAction,
  XTreeFlatNode,
  XTreeActionModel,
  XTreeContainerComponent,
} from './x-tree.typings';
import {
  nameof,
  toArray,
  isNullOrUndefined,
  isNullOrEmptyString,
} from 'x-framework-core';
import {
  MatTreeFlattener,
  MatTreeFlatDataSource,
} from '@angular/material/tree';
import { Subject, Subscription } from 'rxjs';
import { FlatTreeControl } from '@angular/cdk/tree';
import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  // tslint:disable-next-line:component-selector
  selector: 'x-tree',
  templateUrl: './x-tree.component.html',
  styleUrls: ['./x-tree.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class XTreeComponent extends XTreeContainerComponent {
  //
  //#region Props ...
  //
  private ACTION_PROVIDER_SUBSCRIPTION: Subscription;

  //
  treeControl: FlatTreeControl<XTreeFlatNode, XTreeFlatNode> = null;

  //
  treeFlattener: MatTreeFlattener<
    XTreeNode<any>,
    XTreeFlatNode,
    XTreeFlatNode
  > = null;

  //
  dataSource: MatTreeFlatDataSource<
    XTreeNode<any>,
    XTreeFlatNode,
    XTreeFlatNode
  > = null;
  //#endregion

  //
  //#region LifeCycles ...
  async onChange(changeKeys: string[]) {
    super.onChange(changeKeys);

    //
    //#region ActionProvider Changed ...
    const isActionProviderChanged = changeKeys.includes(
      nameof<XTreeComponent>('actionProvider')
    );
    if (isActionProviderChanged) {
      this.registerActionProvider();
    }
    //#endregion

    //
    //#region Nodes Changes ...
    const isNodesChanged = changeKeys.includes(nameof<XTreeComponent>('nodes'));
    if (isNodesChanged) {
      await this.prepareDataSource();
    }
    //#endregion
  }

  onDestroy() {
    super.onDestroy();

    //
    this.unregisterActionProvider();
  }

  registerViewHandlers() {
    super.registerViewHandlers();

    //
    this.registerActionProvider();
  }
  //#endregion

  //
  //#region Register Handlers ...
  //#endregion

  //
  //#region UI Providers ...
  getNodeClassNames(node: XTreeFlatNode<any>) {
    //
    const result = ['x-tree-node', `x-tree-node-level-${node.level}`];

    //
    if (node.expandable) {
      result.push('x-tree-expandable-node');
    }

    //
    if (!isNullOrEmptyString(node.id)) {
      result.push(`x-tree-node-id-${node.id}`);
      result.push(`x-tree-node-id-${node.id}-level-${node.level}`);
    }

    //
    return this.getArrayValue(result);
  }

  nodeHasChild = (_: number, node: XTreeFlatNode) => node.expandable;
  //#endregion

  //
  //#region UI Handlers ...
  handleTreeNodeToggle(id: string, isExpanded: boolean) {
    //
    if (isNullOrEmptyString(id)) {
      return;
    }

    //
    this.onNodeToggle.emit({
      id,
      isExpanded,
    });
  }
  //#endregion

  //
  //#region Actions ...
  //#endregion

  //
  //#region Private ...
  //
  //#region ActionProvider ...
  private registerActionProvider() {
    //
    this.unregisterActionProvider();

    //
    if (!this.actionProvider) {
      this.actionProvider = new Subject<XTreeActionModel>();
    }

    //
    // Handle Action Provider ...
    this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
      .asObservable()
      .subscribe(async (model) => {
        //
        if (!model) {
          return;
        }

        //
        switch (model.action) {
          //
          case XTreeAction.Refresh:
            this.detectChanges();
            break;

          //
          case XTreeAction.Expand:
            //
            if (this.treeControl) {
              //
              // Get Tree Node ...
              if (model.payload) {
                //
                const id = model.payload.toString();
                if (!isNullOrEmptyString(id)) {
                  //
                  const node = this.treeControl.dataNodes.find(
                    (d) => d.id === id
                  );
                  if (!isNullOrUndefined(node)) {
                    this.treeControl.expand(node);
                  }
                }
              }
            }
            break;

          //
          case XTreeAction.Collapse:
            //
            if (this.treeControl) {
              //
              // Get Tree Node ...
              if (model.payload) {
                //
                const id = model.payload.toString();
                if (!isNullOrEmptyString(id)) {
                  //
                  const node = this.treeControl.dataNodes.find(
                    (d) => d.id === id
                  );
                  if (!isNullOrUndefined(node)) {
                    this.treeControl.collapse(node);
                  }
                }
              }
            }
            break;

          //
          case XTreeAction.ExpandAll:
            //
            if (this.treeControl) {
              this.treeControl.expandAll();
            }
            break;

          //
          case XTreeAction.CollapseAll:
            //
            if (this.treeControl) {
              this.treeControl.collapseAll();
            }
            break;
        }
      });
  }

  private unregisterActionProvider() {
    //
    if (!this.ACTION_PROVIDER_SUBSCRIPTION) {
      return;
    }

    //
    this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
    this.ACTION_PROVIDER_SUBSCRIPTION = null;
  }
  //#endregion

  //
  //#region Tree ...
  async prepareDataSource() {
    //
    this.treeControl = null;
    this.treeFlattener = null;
    this.dataSource = null;

    //
    const nodes = await this.getArrayValueAsync(this.nodes);
    if (!this.hasChild(nodes)) {
      this.detectChanges();
      return;
    }

    //
    this.treeControl = new FlatTreeControl<XTreeFlatNode>(
      (node) => node.level,
      (node) => node.expandable
    );

    //
    this.treeFlattener = new MatTreeFlattener(
      (node: XTreeNode<any>, level: number) => {
        return {
          id: node.id,
          level: level,
          data: node.data,
          tooltip: node.tooltip,
          expandable: this.hasChild(node.childs),
        } as XTreeFlatNode;
      },
      (node) => node.level,
      (node) => node.expandable,
      (node) => toArray(node.childs)
    );

    //
    this.dataSource = new MatTreeFlatDataSource(
      this.treeControl,
      this.treeFlattener
    );
    this.dataSource.data = nodes;

    //
    this.detectChanges();
  }
  //#endregion
  //#endregion
}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tree\x-tree.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XTreeComponent } from './x-tree.component';
import { XCardModule } from '../x-card/x-card.module';
import { XIconModule } from '../x-icon/x-icon.module';
import { XBaseComponentModule } from '../modules/x-base-component.module';

@NgModule({
  declarations: [XTreeComponent],
  imports: [CommonModule, XCardModule, XIconModule, XBaseComponentModule],
  exports: [XIconModule, XCardModule, XBaseComponentModule, XTreeComponent],
})
export class XTreeModule {}

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFrameworkComponentsHolder\projects\x-framework-components\src\lib\x-tree\x-tree.typings.ts

import {
  Input,
  Inject,
  Output,
  NgZone,
  Component,
  Renderer2,
  ElementRef,
  Injectable,
  TemplateRef,
  EventEmitter,
  ChangeDetectorRef,
} from '@angular/core';
import {
  XStandardType,
  XOneOrManyType,
  XColorIdentifier,
  XResourceIdentifier,
  XColorWithBrightness,
} from 'x-framework-core';
import {
  XComponentCardContainer,
  IXComponentCardContainer,
  XComponentCardContainerComponent,
} from '../x-card/x-card.typings';
import { concatMap, map } from 'rxjs/operators';
import { Observable, Subject, forkJoin } from 'rxjs';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { XBaseActionModel } from '../classes/x-base-action.model';
import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';

export interface XTreeNodeToggle {
  id: string;
  isExpanded: boolean;
}

export interface XTreeNode<T = any> {
  id: string;
  data: T;
  childs?: XOneOrManyType<XTreeNode<T>>;
  tooltip?: XStandardType<XResourceIdentifier>;
}

export interface XTreeFlatNode<T = any> {
  id: string;
  level: number;
  data: XTreeNode<T>;
  expandable: boolean;
  tooltip?: XStandardType<XResourceIdentifier>;
}

export enum XTreeAction {
  //
  Refresh = 'refresh',

  //
  Expand = 'expand',
  Collapse = 'collapse',

  //
  ExpandAll = 'expand-all',
  CollapseAll = 'collapse-all',
}

export type XTreeActionIdentifier = XTreeAction | string;

export interface XTreeActionModel
  extends XBaseActionModel<XTreeActionIdentifier> {}

//
//#region Component Container Typings ...
export interface IXTreeContainer extends IXComponentCardContainer {
  //
  //#region Props ...
  //
  actionProvider: Subject<XTreeActionModel>;
  cssClass: XStandardType<XOneOrManyType<string>>;

  //
  isForegroundToggleColor: XStandardType<boolean>;
  toggleIconColor: XStandardType<XColorIdentifier>;

  //
  nodes: XStandardType<XOneOrManyType<XTreeNode<any>>>;

  //
  nodeTemplate: TemplateRef<any>;

  //
  onNodeToggle: EventEmitter<XTreeNodeToggle>;
  //#endregion
}

@Injectable({
  providedIn: 'root',
})
export class XTreeContainer
  extends XComponentCardContainer
  implements Readonly<IXTreeContainer>
{
  //
  //#region Props ...
  //
  readonly actionProvider: Subject<XTreeActionModel>;
  readonly cssClass: XStandardType<XOneOrManyType<string>>;

  //
  readonly isForegroundToggleColor: XStandardType<boolean> = true;
  readonly toggleIconColor: XStandardType<XColorIdentifier> =
    XColorWithBrightness.Dark;

  //
  readonly nodes: XStandardType<XOneOrManyType<XTreeNode<any>>>;

  //
  readonly nodeTemplate: TemplateRef<any>;

  //
  readonly onNodeToggle = new EventEmitter<XTreeNodeToggle>();
  //#endregion
}

@Injectable()
@Component({
  template: ``,
})
export abstract class XTreeContainerComponent
  extends XComponentCardContainerComponent
  implements IXTreeContainer
{
  //
  //#region Props ...
  //
  @Input()
  actionProvider: Subject<XTreeActionModel> =
    this.propertyProvider.actionProvider;

  //
  @Input()
  cssClass: XStandardType<XOneOrManyType<string>> =
    this.propertyProvider.cssClass;

  //
  @Input()
  toggleIconColor: XStandardType<string> =
    this.propertyProvider.toggleIconColor;

  //
  @Input()
  isForegroundToggleColor: XStandardType<boolean> =
    this.propertyProvider.isForegroundToggleColor;

  //
  @Input()
  nodes: XStandardType<XOneOrManyType<XTreeNode<any>>> =
    this.propertyProvider.nodes;

  //
  @Input()
  nodeTemplate: TemplateRef<any> = this.propertyProvider.nodeTemplate;

  //
  @Output()
  onNodeToggle = new EventEmitter<XTreeNodeToggle>();
  //#endregion

  //
  //#region Constructor ...
  public constructor(
    @Inject(X_FRAMEWORK_COMPONENTS_CONFIG)
    public config: XFrameworkComponentsConfig,
    public managerService: XManagerService,
    public element: ElementRef,
    public renderer: Renderer2,
    public ruler: ViewportRuler,
    public zone: NgZone,
    public changeDetector: ChangeDetectorRef,
    protected propertyProvider: XTreeContainer
  ) {
    super(
      config,
      managerService,
      element,
      renderer,
      ruler,
      zone,
      changeDetector,
      propertyProvider
    );
  }
  //#endregion

  //
  //#region Abstract ...
  public extractProps(
    container?: IXTreeContainer
  ): Observable<IXTreeContainer> {
    return super
      .extractProps(container ? container : this)
      .pipe(
        concatMap((superData) => {
          return forkJoin({
            //
            cssClass: this.getArrayValue(
              container ? container.cssClass : this.cssClass
            ),

            //
            toggleIconColor: this.getValue(
              container ? container.toggleIconColor : this.toggleIconColor
            ),

            //
            isForegroundToggleColor: this.getValue(
              container
                ? container.isForegroundToggleColor
                : this.isForegroundToggleColor
            ),

            //
            nodes: this.getArrayValue(container ? container.nodes : this.nodes),
          }).pipe(
            map((data) => {
              return {
                superData,
                data,
              };
            })
          );
        })
      )
      .pipe(
        map((info) => {
          return {
            //
            ...info.superData,

            //
            ...info.data,

            //
            actionProvider: container
              ? container.actionProvider
              : this.actionProvider,

            //
            nodeTemplate: container
              ? container.nodeTemplate
              : this.nodeTemplate,

            //
            onNodeToggle: container.onNodeToggle
              ? container.onNodeToggle
              : this.onNodeToggle,
          };
        })
      );
  }
  //#endregion
}
//#endregion
