// #region Imports
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { DomServiceLocator } from '../../../../Dom/Dom';
import { Emit } from '../../../../Events/Decorators/EmitDecorator';
import { HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { Fitable } from '../../../Behaviors/Fitable';
import { ResizeController } from '../../../Controllers/ResizeController';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { DrawerMode } from '../../../Types/DrawerMode';
import { DrawerPosition } from '../../../Types/DrawerPosition';
import { CustomElement } from '../../Abstracts/CustomElement';
import { drawerContainerElementTemplate } from './DrawerContainerElementTemplate';
import { DrawerElement } from './DrawerElement';
import { drawerContainerElementJoyStyle } from './Themes/DrawerContainerElement.Joy';
import { drawerContainerElementRetroStyle } from './Themes/DrawerContainerElement.Retro';
import { drawerContainerElementCosmopolitanStyle } from './Themes/DrawerContainerElement.Cosmopolitan';
import { DrawerContentElement } from './DrawerContentElement';
import { Themeable } from '../../../Behaviors/Themeable';
// #endregion
/**
 * Drawer Container - A container element often used to organize content in a drawer view.
 *
 * @element mosaik-drawer-container
 *
 * @slot drawer - The drawer element.
 * @slot content - The content element.
 *
 * @fires contentMarginChanged - Called when the content margin changed.
 *
 * @public
 */
let DrawerContainerElement = class DrawerContainerElement extends Themeable(Fitable(CustomElement)) {
    // #region Fields
    _contentMarginChanged;
    _resizeController;
    _drawer;
    _content;
    _autoSize;
    _hasBackdrop;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._drawer = null;
        this._content = null;
        this._autoSize = false;
        this._hasBackdrop = false;
        this._contentMarginChanged = new HTMLElementEventEmitter(this, 'contentMarginChanged', { bubbles: false });
        this._resizeController = new ResizeController(this, {
            callback: () => this.updateContentMargins()
        });
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `{@link is}` property.
     * The `{@link is}` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-drawer-container';
    }
    /**
     * Gets or sets the `autoSize` property.
     *
     * @public
     * @attr
     */
    get autoSize() {
        return this._autoSize;
    }
    set autoSize(value) {
        if (this._autoSize !== value) {
            this._autoSize = value;
            this.requestUpdate('autoSize');
        }
    }
    /**
     * Gets or sets the `hasBackdrop` property.
     *
     * @public
     * @attr
     */
    get hasBackdrop() {
        return this._hasBackdrop;
    }
    set hasBackdrop(value) {
        if (this._hasBackdrop !== value) {
            this._hasBackdrop = value;
            this.requestUpdate('hasBackdrop');
        }
    }
    /**
     * Called when the content margin changed.
     * Provides reference to `IDrawerContainerContentMarginChangedEventDetails` as event argument.
     *
     * @public
     * @readonly
     * @eventProperty
     * @type IEventEmitter<IDrawerContainerContentMarginChangedEventDetail>
     */
    get contentMarginChanged() {
        return this._contentMarginChanged;
    }
    /**
     * Returns the `content` property.
     *
     * @public
     * @readonly
     */
    get content() {
        return this._content;
    }
    /**
     * Returns the `drawer` property.
     *
     * @public
     * @readonly
     */
    get drawer() {
        return this._drawer;
    }
    // #endregion
    // #region Methods
    /**
     * @public
     */
    open() {
        void this._drawer?.open();
    }
    /**
     * @public
     */
    close() {
        void this._drawer?.close();
    }
    /**
     * @protected
     * @override
     */
    onApplyTemplate() {
        super.onApplyTemplate();
        this._drawer = DomServiceLocator.current.findDescendant(this, DrawerElement);
        this._content = DomServiceLocator.current.findDescendant(this, DrawerContentElement);
        // If the drawer is not a direct child of the container, then we can't do anything.
        if (this._drawer?.parentElement !== this) {
            return;
        }
        this._drawer.on('opened', () => this.updateContentMargins());
        this._drawer.on('closed', () => this.updateContentMargins());
        this.updateContentMargins();
    }
    /**
     * @private
     *
     * Recalculates and updates the inline styles for the content. Note that this should be used
     * sparingly, because it causes a reflow.
     */
    updateContentMargins() {
        void this.updateComplete.then(() => {
            // 1. For drawers in `over` mode, they don't affect the content.
            // 2. For drawers in `side` mode they should shrink the content. We do this by adding to the
            //    left margin (for left drawer) or right margin (for right the drawer).
            // 3. For drawers in `push` mode the should shift the content without resizing it. We do this by
            //    adding to the left or right margin and simultaneously subtracting the same amount of
            //    margin from the other side.
            let left = 0;
            let right = 0;
            if (this._drawer?.position === DrawerPosition.Left) {
                if (this._drawer.isOpen) {
                    if (this._drawer.mode === DrawerMode.Side) {
                        left += this._drawer.innerOffsetWidth();
                    }
                    else if (this._drawer.mode === DrawerMode.Push) {
                        const width = this._drawer.innerOffsetWidth();
                        left += width;
                        right -= width;
                    }
                }
            }
            if (this._drawer?.position === DrawerPosition.Right) {
                if (this._drawer.isOpen) {
                    if (this._drawer.mode === DrawerMode.Side) {
                        right += this._drawer.innerOffsetWidth();
                    }
                    else if (this._drawer.mode === DrawerMode.Push) {
                        const width = this._drawer.innerOffsetWidth();
                        right += width;
                        left -= width;
                    }
                }
            }
            // If either `right` or `left` is zero, don't set a style to the element. This
            // allows users to specify a custom size via CSS class in SSR scenarios where the
            // measured widths will always be zero. Note that we reset to `null` here, rather
            // than below, in order to ensure that the types in the `if` below are consistent.
            left ??= null;
            right ??= null;
            // Pull back into the NgZone since in some cases we could be outside. We need to be careful
            // to do it only when something changed, otherwise we can end up hitting the zone too often.
            this._contentMarginChanged.emit({
                left: left,
                right: right
            });
        });
    }
};
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], DrawerContainerElement.prototype, "autoSize", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], DrawerContainerElement.prototype, "hasBackdrop", null);
__decorate([
    Emit({ eventName: 'contentMarginChanged' }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], DrawerContainerElement.prototype, "contentMarginChanged", null);
DrawerContainerElement = __decorate([
    Component({
        selector: 'mosaik-drawer-container',
        template: drawerContainerElementTemplate,
        themes: {
            joy: drawerContainerElementJoyStyle,
            retro: drawerContainerElementRetroStyle,
            cosmopolitan: drawerContainerElementCosmopolitanStyle
        },
        options: {
            mode: 'open',
            delegatesFocus: true
        },
        imports: []
    }),
    __metadata("design:paramtypes", [])
], DrawerContainerElement);
export { DrawerContainerElement };
//# sourceMappingURL=DrawerContainerElement.js.map