import { __decorate, __param } from 'tslib';
import { HostBinding, Component, ChangeDetectionStrategy, ViewEncapsulation, Injector, ApplicationRef, Optional, ComponentFactoryResolver, Injectable, NgZone, ChangeDetectorRef, Input, ViewChild, NgModule } from '@angular/core';
import { trigger, transition, query, group, style, stagger, animate } from '@angular/animations';
import { Localize, LocalizeModule } from '@bravobit/bb-foundation/localize';
import { Platform } from '@bravobit/bb-foundation';
import { BehaviorSubject } from 'rxjs';
import { share } from 'rxjs/operators';
import { CommonModule } from '@angular/common';

var NotificationType;
(function (NotificationType) {
    NotificationType["Success"] = "success";
    NotificationType["Error"] = "error";
    NotificationType["Warning"] = "warning";
    NotificationType["Info"] = "info";
    NotificationType["Custom"] = "custom";
})(NotificationType || (NotificationType = {}));
class NotificationsConfig {
}
var NotificationsPosition;
(function (NotificationsPosition) {
    NotificationsPosition["TopRight"] = "flex-start|flex-end";
    NotificationsPosition["BottomRight"] = "flex-end|flex-end";
    NotificationsPosition["TopLeft"] = "flex-start|flex-start";
    NotificationsPosition["BottomLeft"] = "flex-end|flex-start";
})(NotificationsPosition || (NotificationsPosition = {}));
class NotificationsData {
}

const s = '250ms cubic-bezier(0, 0, .2, 1)';
const l = '400ms cubic-bezier(0, 0, .2, 1)';
const notificationAnimation = trigger('notificationListAnimation', [
    transition('* => *', [
        query(':enter', group([
            // 1. Set the initial state.
            style({ height: 0, opacity: 0, transform: 'translateX({{ to }}%) scale(0.95)' }),
            // 2. Start the animation to show the item.
            stagger(0, [
                animate(s, style({ height: '*' })),
                animate(l, style({ opacity: 1, transform: 'translateX(0) scale(0.95)' })),
                animate(s, style({ transform: 'translateX(0) scale(1)' }))
            ])
        ]), { optional: true }),
        query(':leave', group([
            // 1. Set the initial state.
            style({ height: '*', opacity: 1, transform: 'translateX(0) scale(1)' }),
            // 2. Start the animation to hide the item.
            stagger(0, [
                animate(s, style({ transform: 'translateX(0) scale(0.95)' })),
                animate(l, style({ opacity: 0, transform: 'translateX({{ to }}%) scale(0.95)' })),
                animate(s, style({ height: 0 }))
            ])
        ]), { optional: true })
    ])
]);

let BbNotificationsList = class BbNotificationsList {
    constructor(data) {
        this.data = data;
        // Data.
        this.position = null;
        // Track by function.
        this.trackByFn = (_, item) => item.id;
    }
    get horizontalPosition() {
        return this.location.horizontal;
    }
    get verticalPosition() {
        return this.location.vertical;
    }
    get animation() {
        const to = this.horizontalPosition === 'flex-start'
            ? -50
            : 50;
        return { to };
    }
    get location() {
        const [y, x] = (this.data.position || '').split('|');
        return { vertical: y || null, horizontal: x || null };
    }
};
BbNotificationsList.ctorParameters = () => [
    { type: NotificationsData }
];
__decorate([
    HostBinding('style.justify-content')
], BbNotificationsList.prototype, "horizontalPosition", null);
__decorate([
    HostBinding('style.align-items')
], BbNotificationsList.prototype, "verticalPosition", null);
BbNotificationsList = __decorate([
    Component({
        selector: 'bb-notifications-list',
        template: "<!--\r\n    The notifications list.\r\n\r\n    The list provides the animation effect for the\r\n    incoming notifications.\r\n-->\r\n<div *ngIf=\"data?.data | async as notifications\"\r\n     [@notificationListAnimation]=\"{value: notifications?.length, params: animation}\"\r\n     class=\"notifications-list-wrapper\">\r\n    <!--\r\n        The loop for the notifications.\r\n\r\n        All notifications will be displayed here. They\r\n        are wrapped within a wrapper which separates them.\r\n    -->\r\n    <div *ngFor=\"let notification of notifications; trackBy: trackByFn\"\r\n         class=\"notifications-item-wrapper\">\r\n        <bb-notifications-item [notification]=\"notification\"\r\n                               [dismissButtonText]=\"data?.dismissText\">\r\n        </bb-notifications-item>\r\n    </div>\r\n</div>\r\n",
        animations: [notificationAnimation],
        changeDetection: ChangeDetectionStrategy.OnPush,
        encapsulation: ViewEncapsulation.None,
        preserveWhitespaces: false,
        host: {
            'class': 'bb-notifications-list',
            'role': 'list'
        },
        styles: [".bb-notifications-list{top:0;left:0;right:0;bottom:0;z-index:1100;padding:10px;display:-ms-flexbox;display:flex;position:fixed;pointer-events:none}.notifications-list-wrapper{width:100%;display:block;max-width:400px;position:relative}.notifications-item-wrapper{padding:0;margin:0 0 10px;pointer-events:all}"]
    })
], BbNotificationsList);

class NotificationsInjector {
    constructor(_parentInjector, _additionalTokens) {
        this._parentInjector = _parentInjector;
        this._additionalTokens = _additionalTokens;
    }
    get(token, notFoundValue) {
        // First check if the additional token
        // contains the token.
        const value = this._additionalTokens.get(token);
        // If that value exists return it.
        if (value) {
            return value;
        }
        // Else query the parent injector.
        return this._parentInjector.get(token, notFoundValue);
    }
}

let Notifications = class Notifications {
    constructor(_injector, _platform, _applicationRef, _localize, _config, _componentFactoryResolver) {
        this._injector = _injector;
        this._platform = _platform;
        this._applicationRef = _applicationRef;
        this._localize = _localize;
        this._config = _config;
        this._componentFactoryResolver = _componentFactoryResolver;
        // The data containing the notifications.
        this._notifications$ = new BehaviorSubject([]);
        this.getProperty = (property, defaultValue) => {
            return typeof property === 'undefined'
                ? defaultValue
                : property;
        };
        // Get the config.
        const config = this._config || {};
        // Set the config.
        this._defaultMode = this.getProperty(config.mode, 'prepend');
        this._defaultTimeout = this.getProperty(config.timeout, 8000);
        this._defaultLocalize = this.getProperty(config.localize, false);
        this._defaultDismiss = this.getProperty(config.dismiss, true);
        this._defaultDismissText = this.getProperty(config.dismissText, 'Dismiss');
        this._defaultPosition = this.getProperty(config.position, NotificationsPosition.TopRight);
        // Create the element that holds all notifications.
        this.createElement();
    }
    success(content, actions = [], timeout = this._defaultTimeout) {
        return this.create({ content, timeout, actions, type: NotificationType.Success });
    }
    error(content, actions = [], timeout = this._defaultTimeout) {
        return this.create({ content, timeout, actions, type: NotificationType.Error });
    }
    warn(content, actions = [], timeout = this._defaultTimeout) {
        return this.create({ content, timeout, actions, type: NotificationType.Warning });
    }
    info(content, actions = [], timeout = this._defaultTimeout) {
        return this.create({ content, timeout, actions, type: NotificationType.Info });
    }
    create(notification) {
        // Compose a new notification item.
        const item = this.compose(notification);
        // Add the destroy function to the notification.
        item.destroy = () => this.pull(item);
        // Add the notification to the list
        // and return the item.
        return this.push(item);
    }
    push(notification) {
        // Get the current list.
        const oldList = this._notifications$.getValue();
        // Check which mode is activated.
        let newList;
        switch (this._defaultMode) {
            case 'append':
                newList = [...oldList, notification];
                break;
            case 'prepend':
            default:
                newList = [notification, ...oldList];
        }
        // Push the new notifications.
        this._notifications$.next(newList);
        // Return the notification for further use.
        return notification;
    }
    pull(notification) {
        // Get the current list.
        const newList = this._notifications$
            .getValue()
            .filter(item => item.id !== notification.id);
        // Push a new list.
        this._notifications$.next(newList);
    }
    compose(notification) {
        // Attach a random id to the notification.
        notification.id = Math.random().toString(36).substr(2, 10);
        // Set all properties.
        notification.type = this.getProperty(notification.type, NotificationType.Custom);
        notification.content = this.getProperty(notification.content, null);
        notification.data = this.getProperty(notification.data, {});
        notification.timeout = this.getProperty(notification.timeout, this._defaultTimeout);
        notification.localize = this.getProperty(notification.localize, this._defaultLocalize);
        notification.dismiss = this.getProperty(notification.dismiss, this._defaultDismiss);
        // Dismiss text localization.
        const dismissText = this.getProperty(notification.dismissText, this._defaultDismissText);
        notification.dismissText = this._defaultLocalize && this._localize
            ? this._localize.translate(dismissText)
            : dismissText;
        // Return the composed notification.
        return notification;
    }
    createElement() {
        // Create a map with the config.
        const map = new WeakMap();
        map.set(NotificationsData, {
            data: this._notifications$.pipe(share()),
            dismissText: this._defaultDismissText,
            position: this._defaultPosition
        });
        // Create a custom injector.
        const notificationsInjector = new NotificationsInjector(this._injector, map);
        // Create the notification list and
        // get a reference to the component.
        this._ref = this._componentFactoryResolver
            .resolveComponentFactory(BbNotificationsList)
            .create(notificationsInjector);
        // Detect the changes.
        this._ref.changeDetectorRef.detectChanges();
        // Attach the component's view to the application
        // so that the change detection will run properly.
        this._applicationRef.attachView(this._ref.hostView);
        // If the platform is not a browser return.
        if (!this._platform.isBrowser) {
            return;
        }
        try {
            // Append the element to the DOM.
            document.body.appendChild(this._ref.location.nativeElement);
        }
        catch (_a) {
            // Don't do anything, because it must've failed.
        }
    }
};
Notifications.ctorParameters = () => [
    { type: Injector },
    { type: Platform },
    { type: ApplicationRef },
    { type: Localize, decorators: [{ type: Optional }] },
    { type: NotificationsConfig, decorators: [{ type: Optional }] },
    { type: ComponentFactoryResolver }
];
Notifications = __decorate([
    Injectable(),
    __param(3, Optional()),
    __param(4, Optional())
], Notifications);

let BbNotificationsItem = class BbNotificationsItem {
    constructor(_zone, _platform, _changeDetection) {
        this._zone = _zone;
        this._platform = _platform;
        this._changeDetection = _changeDetection;
        // Data.
        this._progress = 0;
        this._count = 0;
        // Data.
        this._radius = 19;
        this._circumference = this._radius * 2 * Math.PI;
        this.callActionAndDestroy = (notification, action) => {
            // Perform the callback (if it exists).
            action && action.callback && action.callback();
            // Call the destroy method (if it exists).
            notification && notification.destroy && notification.destroy();
        };
        this.instance = () => {
            // Calculate the difference.
            this._difference = (new Date().getTime() - this._startTime) - (this._count * this._speed);
            // Add up the count.
            if (this._count++ === this._steps) {
                this.notification.destroy();
            }
            // Add the steps to the progress.
            this._progress += 100 / this._steps;
            // Set a new timer.
            this._timer = this.setTimeout(this.instance, this._speed - this._difference);
            // Run a new change detection cycle.
            this._zone.run(() => this._changeDetection.detectChanges());
        };
        this.setTimeout = (method, timeout) => {
            // Check if the window and method exist.
            if (!window || !window.setTimeout) {
                return null;
            }
            return window.setTimeout(method, timeout);
        };
    }
    get getClass() {
        return `bb-notifications-item ${this.notification.type}`;
    }
    get progressStrokeArray() {
        const value = Math.floor(this._circumference);
        return `${value}, ${value}`;
    }
    get progressStrokeOffset() {
        return Math.floor(this._circumference - this._progress / 100 * this._circumference);
    }
    get isContentString() {
        return typeof this.notification.content === 'string';
    }
    ngOnInit() {
        // Check if the notification timeout is not 0 and the platform is a browser.
        if (this.notification.timeout <= 0 || !this._platform.isBrowser) {
            this.notification.dismiss = true;
            return;
        }
        // Start the timeout.
        this.startTimeout();
    }
    ngOnDestroy() {
        // Clear the timeout if it was set.
        this._timer && window && window.clearTimeout && window.clearTimeout(this._timer);
    }
    startTimeout() {
        // Calculate the steps of the timeout.
        this._steps = this.notification.timeout / 10;
        // Calculate the speed of the timeout.
        this._speed = this.notification.timeout / this._steps;
        // Get the start time.
        this._startTime = new Date().getTime();
        // Set a new timer outside of Angular.
        this._zone.runOutsideAngular(() => this._timer = this.setTimeout(this.instance, this._speed));
    }
};
BbNotificationsItem.ctorParameters = () => [
    { type: NgZone },
    { type: Platform },
    { type: ChangeDetectorRef }
];
__decorate([
    Input()
], BbNotificationsItem.prototype, "notification", void 0);
__decorate([
    Input()
], BbNotificationsItem.prototype, "dismissButtonText", void 0);
__decorate([
    ViewChild('progress')
], BbNotificationsItem.prototype, "progressElementRef", void 0);
__decorate([
    HostBinding('class')
], BbNotificationsItem.prototype, "getClass", null);
BbNotificationsItem = __decorate([
    Component({
        selector: 'bb-notifications-item',
        template: "<div class=\"notification-content-wrapper\">\r\n    <!--\r\n        The icon for the notification.\r\n\r\n        The icon is reflected by the type of the\r\n        notification. Same goes for the color and\r\n        background-color.\r\n    -->\r\n    <div [style.color]=\"notification?.color\"\r\n         [style.background-color]=\"notification?.backgroundColor\"\r\n         class=\"notification-icon-wrapper\">\r\n        <svg xmlns=\"http://www.w3.org/2000/svg\"\r\n             viewBox=\"0 0 16 19.5\"\r\n             class=\"notification-icon\">\r\n            <path\r\n                d=\"M8 19.5c1.1 0 2-.9 2-2H6c0 1.1.9 2 2 2zM14 13.5v-5c0-3.1-1.6-5.6-4.5-6.3v-.7C9.5.7 8.8 0 8 0S6.5.7 6.5 1.5v.7C3.6 2.9 2 5.4 2 8.5v5l-2 2v1h16v-1l-2-2z\">\r\n            </path>\r\n        </svg>\r\n\r\n        <!--\r\n            The circular progress for the notification.\r\n\r\n            The progress is only visible when a timeout is\r\n            set. The progress adapts to the time remaining\r\n            for the notification.\r\n        -->\r\n        <svg *ngIf=\"notification?.timeout > 0\"\r\n             class=\"notification-progress-ring\"\r\n             viewBox=\"0 0 40 40\"\r\n             role=\"progressbar\">\r\n            <circle #progress\r\n                    [attr.stroke-dasharray]=\"progressStrokeArray\"\r\n                    [attr.stroke-dashoffset]=\"progressStrokeOffset\"\r\n                    [style.stroke]=\"notification?.color\"\r\n                    class=\"notification-progress-circle\"\r\n                    stroke-width=\"2\"\r\n                    fill=\"transparent\"\r\n                    r=\"19\"\r\n                    cx=\"20\"\r\n                    cy=\"20\">\r\n            </circle>\r\n        </svg>\r\n    </div>\r\n\r\n    <!--\r\n        The content for the notification.\r\n\r\n        This displays a string or a template based\r\n        on the given parameter by the user.\r\n    -->\r\n    <div class=\"notification-content\">\r\n        <ng-container *ngIf=\"isContentString; else templateContent\"\r\n                      [ngSwitch]=\"notification?.localize\">\r\n            <ng-container *ngSwitchCase=\"true\">\r\n                {{ notification?.content | bbLocalize:{data: notification?.data} }}\r\n            </ng-container>\r\n            <ng-container *ngSwitchDefault>\r\n                {{ notification?.content }}\r\n            </ng-container>\r\n        </ng-container>\r\n    </div>\r\n</div>\r\n\r\n<!--\r\n    The actions of the notification.\r\n\r\n    All actions are displayed here and are\r\n    based on the actions array the user provided.\r\n-->\r\n<div *ngIf=\"notification?.actions?.length > 0 || notification?.dismiss\"\r\n     class=\"notification-actions\">\r\n    <button *ngFor=\"let action of notification?.actions\"\r\n            (click)=\"callActionAndDestroy(notification, action)\"\r\n            type=\"button\"\r\n            [class.destructive]=\"action?.type === 'cancel'\"\r\n            class=\"notification-actions-button\">\r\n        <span [ngSwitch]=\"notification?.localize\"\r\n              class=\"notification-actions-button-highlight\">\r\n            <ng-container *ngSwitchCase=\"true\">\r\n                {{ action?.title | bbLocalize:{data: notification?.data} }}\r\n            </ng-container>\r\n            <ng-container *ngSwitchDefault>\r\n                {{ action?.title }}\r\n            </ng-container>\r\n        </span>\r\n    </button>\r\n    <button *ngIf=\"notification?.dismiss\"\r\n            (click)=\"notification?.destroy()\"\r\n            class=\"notification-actions-button destructive\"\r\n            type=\"button\">\r\n        <span class=\"notification-actions-button-highlight\">\r\n            {{ notification?.dismissText || dismissButtonText }}\r\n        </span>\r\n    </button>\r\n</div>\r\n\r\n<!--\r\n    The outlet for the notification content template ref.\r\n-->\r\n<ng-template #templateContent>\r\n    <ng-container *ngTemplateOutlet=\"notification?.content\"></ng-container>\r\n</ng-template>\r\n",
        changeDetection: ChangeDetectionStrategy.OnPush,
        encapsulation: ViewEncapsulation.None,
        preserveWhitespaces: false,
        host: {
            'role': 'alert'
        },
        styles: [".bb-notifications-item{display:-ms-flexbox;display:flex;border-radius:4px;-ms-flex-direction:column;flex-direction:column;background-color:#fff;border:1px solid #d6dbe0}.bb-notifications-item.success .notification-icon-wrapper{color:#2dc05d;background-color:#ebfaf0}.bb-notifications-item.success .notification-progress-circle{stroke:#36d068}.bb-notifications-item.error .notification-icon-wrapper{color:#f43e3e;background-color:#feeeee}.bb-notifications-item.error .notification-progress-circle{stroke:#f55656}.bb-notifications-item.warning .notification-icon-wrapper{color:#f1ae00;background-color:#fff8e7}.bb-notifications-item.warning .notification-progress-circle{stroke:#ffbc0b}.bb-notifications-item.info .notification-icon-wrapper{color:#099bf6;background-color:#e9f6fe}.bb-notifications-item.info .notification-progress-circle{stroke:#22a5f7}.bb-notifications-item.custom .notification-icon-wrapper{color:#657385;background-color:#f3f5f6}.bb-notifications-item.custom .notification-progress-circle{stroke:#657385}.notification-content-wrapper{padding:20px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.notification-icon-wrapper{width:40px;height:40px;display:-ms-flexbox;display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;border-radius:50%;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;color:#657385;background-color:#f3f5f6}.notification-icon{width:100%;height:100%;padding:10px;fill:currentColor}.notification-content{-ms-flex:1;flex:1;font-size:14px;line-height:1.5;font-weight:500;margin-left:20px;color:rgba(0,0,0,.85)}.notification-content i{font-style:italic}.notification-actions-button,.notification-content{font-family:inherit}.notification-actions{display:-ms-flexbox;display:flex;margin:0 20px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-top:1px solid #f3f5f6}.notification-actions-button{-ms-flex:1;flex:1;border:none;color:#22a5f7;cursor:pointer;font-size:14px;font-weight:500;padding:12px 2px;text-align:center;background-color:transparent}.notification-actions-button:focus>.notification-actions-button-highlight,.notification-actions-button:hover>.notification-actions-button-highlight{background-color:rgba(34,165,247,.1)}.notification-actions-button:active>.notification-actions-button-highlight{background-color:rgba(34,165,247,.2)}.notification-actions-button.destructive{color:#f55656}.notification-actions-button.destructive:focus>.notification-actions-button-highlight,.notification-actions-button.destructive:hover>.notification-actions-button-highlight{background-color:rgba(245,86,86,.1)}.notification-actions-button.destructive:active>.notification-actions-button-highlight{background-color:rgba(245,86,86,.2)}.notification-actions-button-highlight{padding:4px 12px;border-radius:12px;display:-ms-inline-flexbox;display:inline-flex;transition:background-color 150ms cubic-bezier(0,0,.2,1)}.notification-progress-ring{top:0;left:0;right:0;bottom:0;width:100%;height:100%;position:absolute}.notification-progress-circle{-ms-transform:rotate(-90deg);transform:rotate(-90deg);-ms-transform-origin:50% 50%;transform-origin:50% 50%;stroke:#657385}"]
    })
], BbNotificationsItem);

var NotificationsModule_1;
let NotificationsModule = NotificationsModule_1 = class NotificationsModule {
    static forRoot(config) {
        return {
            ngModule: NotificationsModule_1,
            providers: [
                { provide: NotificationsConfig, useValue: config },
                Notifications
            ]
        };
    }
};
NotificationsModule = NotificationsModule_1 = __decorate([
    NgModule({
        imports: [CommonModule, LocalizeModule],
        declarations: [BbNotificationsList, BbNotificationsItem],
        entryComponents: [BbNotificationsList]
    })
], NotificationsModule);

/**
 * Generated bundle index. Do not edit.
 */

export { NotificationType, Notifications, NotificationsConfig, NotificationsData, NotificationsModule, NotificationsPosition, BbNotificationsList as ɵa, notificationAnimation as ɵb, BbNotificationsItem as ɵc };
//# sourceMappingURL=bravobit-bb-foundation-notifications.js.map
