import { __awaiter, __decorate, __extends, __generator } from "tslib";
import Component from 'vue-class-component';
import { Emit, Prop, Watch } from 'vue-property-decorator';
import { Enums } from '../../utils/enums/enums';
import { MouseButtons } from '../../utils/mouse/mouse';
import uuid from '../../utils/uuid/uuid';
import { ModulVue } from '../../utils/vue/vue';
import { MediaQueries } from '../media-queries/media-queries';
import { MOpenTrigger, OpenTrigger } from '../open-trigger/open-trigger';
export var BackdropMode;
(function (BackdropMode) {
    BackdropMode[BackdropMode["None"] = 0] = "None";
    BackdropMode[BackdropMode["ScrollOnly"] = 1] = "ScrollOnly";
    BackdropMode[BackdropMode["BackdropFast"] = 2] = "BackdropFast";
    BackdropMode[BackdropMode["BackdropSlow"] = 3] = "BackdropSlow";
})(BackdropMode || (BackdropMode = {}));
export var PortalTransitionDuration;
(function (PortalTransitionDuration) {
    PortalTransitionDuration[PortalTransitionDuration["Fast"] = 200] = "Fast";
    PortalTransitionDuration[PortalTransitionDuration["Regular"] = 300] = "Regular";
    PortalTransitionDuration[PortalTransitionDuration["Slow"] = 450] = "Slow";
    PortalTransitionDuration[PortalTransitionDuration["XSlow"] = 600] = "XSlow";
})(PortalTransitionDuration || (PortalTransitionDuration = {}));
var Portal = /** @class */ (function (_super) {
    __extends(Portal, _super);
    function Portal() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.loaded = false;
        _this.propId = '';
        _this.internalTrigger = undefined;
        _this.internalOpen = false;
        _this.internalTransitionDuration = PortalTransitionDuration.Regular;
        _this.opening = false;
        _this.portalTargetCreated = false;
        _this.portalTargetMounted = false;
        return _this;
    }
    Portal.prototype.emitUpdateOpen = function (open) { };
    Portal.prototype.emitOpen = function () { };
    Portal.prototype.emitClose = function () { };
    Portal.prototype.emitPortalMounted = function () { };
    Portal.prototype.emitPortalAfterOpen = function () { };
    Portal.prototype.emitPortalAfterClose = function () { };
    Portal.prototype.emitAfterEnter = function (_el) { };
    Portal.prototype.emitClick = function (event) { };
    Portal.prototype.onOpenChange = function (open) {
        if (this.disabled || open === this.internalOpen) {
            return;
        }
        this.internalOpen = open;
        this.manageOpeningAndClosing(open);
    };
    Portal.prototype.setFocusToPortal = function () { };
    Portal.prototype.setFocusToTrigger = function () { };
    Portal.prototype.getPortalElement = function () {
        return this.portalTargetEl;
    };
    Portal.prototype.getReference = function () {
        return this.reference || this.internalTrigger;
    };
    Portal.prototype.getTrigger = function () {
        return this.internalTrigger;
    };
    Portal.prototype.tryClose = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!this.$toast) return [3 /*break*/, 2];
                        return [4 /*yield*/, this.$toast.clear()];
                    case 1:
                        _a.sent(); // @todo Portal should not know toast
                        _a.label = 2;
                    case 2:
                        if (this.$modul.peekElement() === this.stackId) {
                            if (this.$listeners && this.$listeners.beforeClose) {
                                this.$emit('portal-before-close', function (close) {
                                    _this.propOpen = !close;
                                });
                            }
                            else {
                                this.propOpen = false;
                            }
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    Portal.prototype.created = function () {
        if (!this.$modul) {
            throw new Error('Portal mixin -> this.$modul is undefined, you must install the Modul plugin.');
        }
    };
    Portal.prototype.mounted = function () {
        this.portalTargetEl = document.getElementById(this.propId);
        this.handleTrigger();
        if (!this.lazy) {
            this.ensurePortalTargetEl();
        }
    };
    Portal.prototype.beforeDestroy = function () {
        this.propOpen = false;
        if (this.internalTrigger) {
            if (this.clickEventListener) {
                this.internalTrigger.removeEventListener('click', this.clickEventListener);
            }
            if (this.mouseenterEventListener) {
                this.internalTrigger.removeEventListener('mouseenter', this.mouseenterEventListener);
            }
            if (this.mousedownEventListener) {
                this.internalTrigger.removeEventListener('mousedown', this.mousedownEventListener);
            }
        }
        if (this.portalTargetEl && this.portalTargetEl.parentNode) {
            this.portalTargetEl.parentNode.removeChild(this.portalTargetEl);
            this.portalTargetCreated = false;
            this.portalTargetMounted = false;
        }
    };
    Object.defineProperty(Portal.prototype, "propOpen", {
        get: function () {
            if (this.internalOpen) {
                this.loaded = true;
            }
            return this.internalOpen;
        },
        set: function (open) {
            if (open !== this.internalOpen && !this.disabled) {
                this.manageOpeningAndClosing(open);
                this.internalOpen = open;
                this.emitUpdateOpen(open);
            }
        },
        enumerable: false,
        configurable: true
    });
    Portal.prototype.manageOpeningAndClosing = function (open) {
        var _this = this;
        if (open) {
            this.ensurePortalTargetEl(function () {
                if (_this.portalTargetEl) {
                    _this.stackId = _this.$modul.pushElement(_this.portalTargetEl, _this.as().getBackdropMode(), _this.as().isMqMaxS);
                    if (!_this.as().doCustomPropOpen(open, _this.portalTargetEl)) {
                        _this.portalTargetEl.style.position = 'absolute';
                        _this.portalTargetEl.style.top = '0';
                        _this.portalTargetEl.style.left = '0';
                        // this.opening is important since it's fix a race condition where the portal
                        // could appear behind the content of the page if it was toggled too quickly.
                        _this.opening = true;
                        setTimeout(function () {
                            _this.emitPortalAfterOpen();
                            _this.emitAfterEnter(_this.portalTargetEl.children[0]);
                            _this.setFocusToPortal();
                            _this.opening = false;
                        }, _this.transitionDuration);
                    }
                    else {
                        _this.emitPortalAfterOpen();
                    }
                }
            });
            this.emitOpen();
        }
        else {
            if (this.portalTargetEl) {
                this.$modul.popElement(this.stackId);
                if (!this.as().doCustomPropOpen(open, this.portalTargetEl)) {
                    this.setFocusToTrigger();
                    setTimeout(function () {
                        // $emit update:open has been launched, animation already occurs
                        if (!_this.opening) {
                            _this.portalTargetEl.style.position = '';
                            _this.emitPortalAfterClose();
                        }
                    }, this.transitionDuration);
                }
                else {
                    this.emitPortalAfterClose();
                }
            }
            this.emitClose();
        }
    };
    Object.defineProperty(Portal.prototype, "transitionDuration", {
        get: function () {
            return this.internalTransitionDuration;
        },
        set: function (speed) {
            this.internalTransitionDuration = speed;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Portal.prototype, "portalTargetSelector", {
        get: function () {
            return this.propId ? "#" + this.propId : '';
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Portal.prototype, "portalCreated", {
        get: function () {
            return this.portalTargetCreated;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Portal.prototype, "portalMounted", {
        get: function () {
            return (this.propOpen || this.preload || this.loaded) && (this.portalTargetMounted || !this.lazy);
        },
        enumerable: false,
        configurable: true
    });
    Portal.prototype.onTriggerChange = function () {
        this.handleTrigger();
    };
    Portal.prototype.onTriggerHookChange = function () {
        this.handleTrigger();
    };
    Portal.prototype.handleTrigger = function () {
        if (this.trigger) {
            this.internalTrigger = this.trigger;
        }
        else if (this.$slots.trigger && this.$slots.trigger[0]) {
            this.internalTrigger = this.$slots.trigger[0].elm;
        }
        else if (this.as().triggerHook) {
            this.internalTrigger = this.as().triggerHook;
        }
        if (this.internalTrigger) {
            switch (this.openTrigger) {
                case MOpenTrigger.Click:
                    this.clickEventListener = this.toggle.bind(this);
                    this.internalTrigger.addEventListener('click', this.clickEventListener);
                    break;
                case MOpenTrigger.MouseDown:
                    this.mousedownEventListener = this.toggle.bind(this);
                    this.internalTrigger.addEventListener('mousedown', this.mousedownEventListener);
                    break;
                case MOpenTrigger.Hover:
                    this.mouseenterEventListener = this.handleMouseEnter.bind(this);
                    this.internalTrigger.addEventListener('mouseenter', this.mouseenterEventListener);
                    // Closing not supported for the moment, check source code history for how was handled mouse leave
                    break;
            }
        }
    };
    Portal.prototype.toggle = function (event) {
        if (!this.disabled && event.button !== undefined && event.button === MouseButtons.LEFT) {
            this.propOpen = !this.propOpen;
            this.emitClick(event);
        }
    };
    Portal.prototype.handleMouseEnter = function () {
        this.propOpen = true;
    };
    Portal.prototype.ensurePortalTargetEl = function (onPortalReady) {
        var _this = this;
        if (onPortalReady === void 0) { onPortalReady = function () { }; }
        if (!this.portalTargetEl) {
            this.propId = this.id === undefined ? 'mPortal-' + uuid.generate() : this.id;
            this.portalTargetEl = document.createElement('div');
            this.portalTargetEl.setAttribute('id', this.propId);
            this.portalTargetEl.classList.add('mu-app-root');
            document.body.appendChild(this.portalTargetEl);
            this.portalTargetCreated = true;
            // We wait for the portal creation / mounting.
            this.$nextTick(function () {
                _this.portalTargetMounted = true;
                _this.portalTargetEl = document.querySelector(_this.portalTargetSelector);
                _this.emitPortalMounted();
                onPortalReady();
            });
        }
        else {
            onPortalReady();
        }
    };
    __decorate([
        Prop({
            default: MOpenTrigger.Click,
            validator: function (value) { return Enums.toValueArray(MOpenTrigger).includes(value); }
        })
    ], Portal.prototype, "openTrigger", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "open", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "id", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "disabled", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "reference", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "trigger", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "className", void 0);
    __decorate([
        Prop()
    ], Portal.prototype, "preload", void 0);
    __decorate([
        Prop({ default: true })
    ], Portal.prototype, "lazy", void 0);
    __decorate([
        Emit('update:open')
    ], Portal.prototype, "emitUpdateOpen", null);
    __decorate([
        Emit('open')
    ], Portal.prototype, "emitOpen", null);
    __decorate([
        Emit('close')
    ], Portal.prototype, "emitClose", null);
    __decorate([
        Emit('portal-mounted')
    ], Portal.prototype, "emitPortalMounted", null);
    __decorate([
        Emit('portal-after-open')
    ], Portal.prototype, "emitPortalAfterOpen", null);
    __decorate([
        Emit('portal-after-close')
    ], Portal.prototype, "emitPortalAfterClose", null);
    __decorate([
        Emit('after-enter')
    ], Portal.prototype, "emitAfterEnter", null);
    __decorate([
        Emit('click')
    ], Portal.prototype, "emitClick", null);
    __decorate([
        Watch('open', { immediate: true })
    ], Portal.prototype, "onOpenChange", null);
    __decorate([
        Watch('trigger')
    ], Portal.prototype, "onTriggerChange", null);
    __decorate([
        Watch('internalTriggerHook')
    ], Portal.prototype, "onTriggerHookChange", null);
    Portal = __decorate([
        Component({
            mixins: [OpenTrigger, MediaQueries]
        })
    ], Portal);
    return Portal;
}(ModulVue));
export { Portal };
//# sourceMappingURL=portal.js.map