import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { when } from 'lit/directives/when.js';
import { $ } from '@mdui/jq/$.js';
import '@mdui/jq/methods/css.js';
import '@mdui/jq/methods/innerWidth.js';
import { isFunction, isNull } from '@mdui/jq/shared/helper.js';
import { DefinedController } from '@mdui/shared/controllers/defined.js';
import { watch } from '@mdui/shared/decorators/watch.js';
import { animateTo, stopAnimations } from '@mdui/shared/helpers/animate.js';
import { breakpoint } from '@mdui/shared/helpers/breakpoint.js';
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
import { Modal } from '@mdui/shared/helpers/modal.js';
import { getDuration, getEasing } from '@mdui/shared/helpers/motion.js';
import { observeResize } from '@mdui/shared/helpers/observeResize.js';
import { lockScreen, unlockScreen } from '@mdui/shared/helpers/scroll.js';
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
import { LayoutItemBase } from '../layout/layout-item-base.js';
import { style } from './style.js';
/**
 * 在手机端，`modal` 始终为 `true`；大于手机端时，`modal` 属性才开始生效
 *
 * @summary 侧边抽屉栏组件
 *
 * ```html
 * <mdui-navigation-drawer>content</mdui-navigation-drawer>
 * ```
 *
 * @event open - 抽屉栏打开之前触发。可以通过调用 `event.preventDefault()` 阻止抽屉栏打开
 * @event opened - 抽屉栏打开动画完成之后触发
 * @event close - 抽屉栏关闭之前触发。可以通过调用 `event.preventDefault()` 阻止抽屉栏关闭
 * @event closed - 抽屉栏关闭动画完成之后触发
 * @event overlay-click - 点击遮罩层时触发
 *
 * @slot - 抽屉栏中的内容
 *
 * @csspart overlay - 遮罩层
 * @csspart panel - 抽屉栏容器
 *
 * @cssprop --shape-corner - 组件的圆角大小。可以指定一个具体的像素值；但更推荐引用[设计令牌](/docs/2/styles/design-tokens#shape-corner)
 * @cssprop --z-index - 组件的 CSS `z-index` 值
 */
let NavigationDrawer = class NavigationDrawer extends LayoutItemBase {
    constructor() {
        super(...arguments);
        /**
         * 是否打开抽屉栏
         */
        this.open = false;
        /**
         * 抽屉栏打开时，是否显示遮罩层
         *
         * 在窄屏设备上（屏幕宽度小于 [`--mdui-breakpoint-md`](/docs/2/styles/design-tokens#breakpoint)），会始终显示遮罩层，无视该参数
         */
        this.modal = false;
        /**
         * 在有遮罩层的情况下，按下 ESC 键是否关闭抽屉栏
         */
        this.closeOnEsc = false;
        /**
         * 点击遮罩层时，是否关闭抽屉栏
         */
        this.closeOnOverlayClick = false;
        /**
         * 抽屉栏的位置。可选值包括：
         *
         * * `left`：左侧
         * * `right`：右侧
         */
        this.placement = 'left';
        /**
         * 默认情况下，抽屉栏相对于 `body` 元素显示。当该参数设置为 `true` 时，抽屉栏将相对于其父元素显示。
         *
         * **Note**：设置该属性时，必须在父元素上手动设置样式 `position: relative; overflow: hidden;`。
         */
        this.contained = false;
        // 断点为 mobile 时为 `true` 时，强制使用遮罩层
        this.mobile = false;
        this.overlayRef = createRef();
        this.panelRef = createRef();
        this.definedController = new DefinedController(this, {
            needDomReady: true,
        });
    }
    get layoutPlacement() {
        return this.placement;
    }
    get lockTarget() {
        return this.contained || this.isParentLayout
            ? this.parentElement
            : document.documentElement;
    }
    get isModal() {
        return this.mobile || this.modal;
    }
    // contained 变更后，修改监听尺寸变化的元素。为 true 时，监听父元素；为 false 时，监听 body
    async onContainedChange() {
        await this.definedController.whenDefined();
        this.observeResize?.unobserve();
        this.setObserveResize();
    }
    onPlacementChange() {
        if (this.isParentLayout) {
            this.layoutManager.updateLayout(this);
        }
    }
    async onMobileChange() {
        if (!this.open || this.isParentLayout || this.contained) {
            return;
        }
        await this.definedController.whenDefined();
        if (this.isModal) {
            lockScreen(this, this.lockTarget);
            await this.getLockTargetAnimate(false, 0);
        }
        else {
            unlockScreen(this, this.lockTarget);
            await this.getLockTargetAnimate(true, 0);
        }
    }
    async onOpenChange() {
        let panel = this.panelRef.value;
        let overlay = this.overlayRef.value;
        const isRight = this.placement === 'right';
        const easingLinear = getEasing(this, 'linear');
        const easingEmphasized = getEasing(this, 'emphasized');
        // 在当前 drawer 位于 layout 中时，设置所有 layout-item 和 layout-main 元素的 transition 样式
        const setLayoutTransition = (duration, easing) => {
            $(this.layoutManager.getItemsAndMain()).css('transition', isNull(duration) ? null : `all ${duration}ms ${easing}`);
        };
        // 停止原有动画
        const stopOldAnimations = async () => {
            const elements = [];
            if (this.isModal) {
                elements.push(overlay, panel);
            }
            else if (!this.isParentLayout) {
                elements.push(this.lockTarget);
            }
            if (this.isParentLayout) {
                const layoutItems = this.layoutManager.getItemsAndMain();
                const layoutIndex = layoutItems.indexOf(this);
                elements.push(...layoutItems.slice(layoutIndex));
            }
            if (!this.isModal && !elements.includes(this)) {
                elements.push(this);
            }
            await Promise.all(elements.map((element) => stopAnimations(element)));
        };
        // 打开
        // 要区分是否首次渲染，首次渲染时不触发事件，不执行动画；非首次渲染，触发事件，执行动画
        if (this.open) {
            const hasUpdated = this.hasUpdated;
            if (!hasUpdated) {
                await this.updateComplete;
                panel = this.panelRef.value;
                overlay = this.overlayRef.value;
            }
            if (hasUpdated) {
                const eventProceeded = this.emit('open', { cancelable: true });
                if (!eventProceeded) {
                    return;
                }
            }
            await this.definedController.whenDefined();
            this.style.display = 'block';
            this.originalTrigger = document.activeElement;
            if (this.isModal) {
                this.modalHelper.activate();
                if (!this.contained) {
                    lockScreen(this, this.lockTarget);
                }
            }
            await stopOldAnimations();
            // 设置聚焦
            requestAnimationFrame(() => {
                const autoFocusTarget = this.querySelector('[autofocus]');
                if (autoFocusTarget) {
                    autoFocusTarget.focus({ preventScroll: true });
                }
                else {
                    panel.focus({ preventScroll: true });
                }
            });
            const duration = getDuration(this, 'long2');
            const animations = [];
            // 模态框 drawer，显示 overlay 动画
            if (this.isModal) {
                animations.push(animateTo(overlay, [{ opacity: 0 }, { opacity: 1, offset: 0.3 }, { opacity: 1 }], {
                    duration: hasUpdated ? duration : 0,
                    easing: easingLinear,
                }));
            }
            // 不位于 layout 中，父元素 padding 变化的动画
            else if (!this.isParentLayout) {
                animations.push(this.getLockTargetAnimate(true, hasUpdated ? duration : 0));
            }
            // 若位于 layout 中，则 layout-main 的 padding 变化需要有和 drawer 相同的动画
            // 但首次渲染不执行动画
            if (this.isParentLayout && hasUpdated) {
                setLayoutTransition(duration, easingEmphasized);
                this.layoutManager.updateLayout(this);
            }
            // drawer 显示动画
            animations.push(animateTo(this.isModal ? panel : this, [
                { transform: `translateX(${isRight ? '' : '-'}100%)` },
                { transform: 'translateX(0)' },
            ], {
                duration: hasUpdated ? duration : 0,
                easing: easingEmphasized,
            }));
            await Promise.all(animations);
            if (!this.open) {
                return;
            }
            // 若位于 layout 中，则 drawer 动画完成后，移除 layout-main 的动画
            if (this.isParentLayout && hasUpdated) {
                setLayoutTransition(null);
            }
            if (hasUpdated) {
                this.emit('opened');
            }
        }
        else if (this.hasUpdated) {
            // 关闭
            const eventProceeded = this.emit('close', { cancelable: true });
            if (!eventProceeded) {
                return;
            }
            await this.definedController.whenDefined();
            if (this.isModal) {
                this.modalHelper.deactivate();
            }
            await stopOldAnimations();
            const duration = getDuration(this, 'short4');
            const animations = [];
            // 模态框 drawer，显示 overlay 动画
            if (this.isModal) {
                animations.push(animateTo(overlay, [{ opacity: 1 }, { opacity: 0 }], {
                    duration,
                    easing: easingLinear,
                }));
            }
            // 不位于 layout 中，父元素 padding 变化的动画
            else if (!this.isParentLayout) {
                animations.push(this.getLockTargetAnimate(false, duration));
            }
            // 若位于 layout 中，则 layout-main 的 padding 变化需要有和 drawer 相同的动画
            if (this.isParentLayout) {
                setLayoutTransition(duration, easingEmphasized);
                // 关闭动画开始时，drawer 的宽度不变。等到关闭动画结束，drawer 的宽度才变为 0
                // 为了 layout-main 的动画能在关闭动画开始时就执行，强制调用 updateLayout 更新布局
                this.layoutManager.updateLayout(this, { width: 0 });
            }
            // drawer 显示动画
            animations.push(animateTo(this.isModal ? panel : this, [
                { transform: 'translateX(0)' },
                { transform: `translateX(${isRight ? '' : '-'}100%)` },
            ], { duration, easing: easingEmphasized }));
            await Promise.all(animations);
            if (this.open) {
                return;
            }
            // 若位于 layout 中，则 drawer 动画结束后，移除 layout-main 的动画
            if (this.isParentLayout) {
                setLayoutTransition(null);
            }
            this.style.display = 'none';
            if (this.isModal && !this.contained) {
                unlockScreen(this, this.lockTarget);
            }
            // 抽屉栏关闭后，恢复焦点到原有的元素上
            const trigger = this.originalTrigger;
            if (isFunction(trigger?.focus)) {
                setTimeout(() => trigger.focus());
            }
            this.emit('closed');
        }
    }
    connectedCallback() {
        super.connectedCallback();
        this.modalHelper = new Modal(this);
        this.definedController.whenDefined().then(() => {
            this.setObserveResize();
        });
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        unlockScreen(this, this.lockTarget);
        this.observeResize?.unobserve();
    }
    firstUpdated(_changedProperties) {
        super.firstUpdated(_changedProperties);
        this.addEventListener('keydown', (event) => {
            if (this.open &&
                this.closeOnEsc &&
                event.key === 'Escape' &&
                this.isModal) {
                event.stopPropagation();
                this.open = false;
            }
        });
    }
    render() {
        return html `${when(this.isModal, () => html `<div ${ref(this.overlayRef)} part="overlay" class="overlay" @click="${this.onOverlayClick}"></div>`)}<slot ${ref(this.panelRef)} part="panel" class="panel" tabindex="0"></slot>`;
    }
    setObserveResize() {
        this.observeResize = observeResize(this.contained ? this.parentElement : document.documentElement, () => {
            const target = this.contained ? this.parentElement : undefined;
            this.mobile = breakpoint(target).down('md');
            // 若位于 layout 中，且为模态化，则重新布局时，占据的宽度为 0
            if (this.isParentLayout) {
                this.layoutManager.updateLayout(this, {
                    width: this.isModal ? 0 : undefined,
                });
            }
        });
    }
    onOverlayClick() {
        this.emit('overlay-click');
        if (this.closeOnOverlayClick) {
            this.open = false;
        }
    }
    getLockTargetAnimate(open, duration) {
        const paddingName = this.placement === 'right' ? 'paddingRight' : 'paddingLeft';
        const panelWidth = $(this.panelRef.value).innerWidth() + 'px';
        return animateTo(this.lockTarget, [
            { [paddingName]: open ? 0 : panelWidth },
            { [paddingName]: open ? panelWidth : 0 },
        ], {
            duration,
            easing: getEasing(this, 'emphasized'),
            fill: 'forwards',
        });
    }
};
NavigationDrawer.styles = [componentStyle, style];
__decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter,
    })
], NavigationDrawer.prototype, "open", void 0);
__decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter,
    })
], NavigationDrawer.prototype, "modal", void 0);
__decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter,
        attribute: 'close-on-esc',
    })
], NavigationDrawer.prototype, "closeOnEsc", void 0);
__decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter,
        attribute: 'close-on-overlay-click',
    })
], NavigationDrawer.prototype, "closeOnOverlayClick", void 0);
__decorate([
    property({ reflect: true })
    // eslint-disable-next-line prettier/prettier
], NavigationDrawer.prototype, "placement", void 0);
__decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter,
    })
], NavigationDrawer.prototype, "contained", void 0);
__decorate([
    property({
        type: Boolean,
        reflect: true,
        converter: booleanConverter,
    })
], NavigationDrawer.prototype, "mobile", void 0);
__decorate([
    watch('contained', true)
], NavigationDrawer.prototype, "onContainedChange", null);
__decorate([
    watch('placement', true)
], NavigationDrawer.prototype, "onPlacementChange", null);
__decorate([
    watch('mobile', true),
    watch('modal', true)
], NavigationDrawer.prototype, "onMobileChange", null);
__decorate([
    watch('open')
], NavigationDrawer.prototype, "onOpenChange", null);
NavigationDrawer = __decorate([
    customElement('mdui-navigation-drawer')
], NavigationDrawer);
export { NavigationDrawer };
