// #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 { on } from '@breadstone/mosaik-elements';
import { CssLength } from '@breadstone/mosaik-themes';
import { RendererServiceLocator } from '../../../../Dom/Renderer';
import { Slottable } from '../../../Behaviors/Slottable';
import { Themeable } from '../../../Behaviors/Themeable';
import { addFocusTrapController } from '../../../Controllers/FocusTrapController';
import { addInheritanceProviderController } from '../../../Controllers/InheritanceProviderController';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { BackdropElement } from '../../Primitives/Backdrop/BackdropElement';
import { OverlayElement } from '../Abstracts/OverlayElement';
import { SheetContentElement } from './SheetContentElement';
import { sheetElementTemplate } from './SheetElementTemplate';
import { SheetFooterElement } from './SheetFooterElement';
import { SheetHeaderElement } from './SheetHeaderElement';
import { SHEET_INHERITANCE_CONTEXT } from './SheetInheritanceContext';
import { sheetElementCosmopolitanStyle } from './Themes/SheetElement.Cosmopolitan';
import { sheetElementJoyStyle } from './Themes/SheetElement.Joy';
import { sheetElementMemphisStyle } from './Themes/SheetElement.Memphis';
/**
 * Sheet - A bottom-sheet overlay element that slides up from the bottom of the viewport.
 *
 * @description
 * A gesture-driven bottom sheet that supports drag to open, close, and partial states.
 * Use the `height` attribute to control the initial expanded height.
 *
 * @name Sheet
 * @element mosaik-sheet
 * @category Overlays
 *
 * @slot content - The content slot for `SheetContentElement`.
 * @slot footer - The footer slot for `SheetFooterElement`.
 * @slot header - The header slot for `SheetHeaderElement`.
 *
 * @csspart backdrop - The backdrop part.
 * @csspart heading - The heading part.
 * @csspart root - The root part.
 * @csspart thumb - The thumb part.
 *
 * @cssprop {String} --sheet-background-color - The background color CSS custom property.
 * @cssprop {String} --sheet-border-color - The border color CSS custom property.
 * @cssprop {String} --sheet-border-width - The border width CSS custom property.
 * @cssprop {String} --sheet-font-family - The font family CSS custom property.
 * @cssprop {String} --sheet-font-letter-spacing - The font letter spacing CSS custom property.
 * @cssprop {String} --sheet-font-line-height - The font line height CSS custom property.
 * @cssprop {String} --sheet-font-size - The font size CSS custom property.
 * @cssprop {String} --sheet-font-text-decoration - The font text decoration CSS custom property.
 * @cssprop {String} --sheet-font-text-transform - The font text transform CSS custom property.
 * @cssprop {String} --sheet-font-weight - The font weight CSS custom property.
 * @cssprop {String} --sheet-foreground-color - The foreground color CSS custom property.
 * @cssprop {String} --sheet-gap - The gap CSS custom property.
 * @cssprop {String} --sheet-padding-bottom - The padding bottom CSS custom property.
 * @cssprop {String} --sheet-padding-left - The padding left CSS custom property.
 * @cssprop {String} --sheet-padding-right - The padding right CSS custom property.
 * @cssprop {String} --sheet-padding-top - The padding top CSS custom property.
 * @cssprop {String} --sheet-shadow - The shadow CSS custom property.
 * @cssprop {String} --sheet-shadow-blur - The shadow blur CSS custom property.
 * @cssprop {String} --sheet-shadow-color - The shadow color CSS custom property.
 * @cssprop {String} --sheet-shadow-offset-x - The shadow offset x CSS custom property.
 * @cssprop {String} --sheet-shadow-offset-y - The shadow offset y CSS custom property.
 * @cssprop {String} --sheet-shadow-spread - The shadow spread CSS custom property.
 * @cssprop {String} --sheet-transition-duration - The transition duration CSS custom property.
 * @cssprop {String} --sheet-transition-mode - The transition mode CSS custom property.
 * @cssprop {String} --sheet-transition-property - The transition property CSS custom property.
 * @cssprop {String} --sheet-translate - The translate CSS custom property.
 * @cssprop {String} --sheet-translate-y - The translate y CSS custom property.
 *
 * @dependency mosaik-backdrop - The Backdrop element.
 *
 * @example
 * Basic bottom sheet:
 * ```html
 * <mosaik-sheet></mosaik-sheet>
 * ```
 *
 * @example
 * Bottom sheet with custom height:
 * ```html
 * <mosaik-sheet height="50vh"></mosaik-sheet>
 * ```
 *
 * @public
 */
let SheetElement = class SheetElement extends Themeable(Slottable(OverlayElement)) {
    // #region Fields
    _bounceOffset;
    _height;
    _dragOffset;
    _positionY;
    _isPartial;
    _mouseDownSubscription;
    _touchStartSubscription;
    _mouseUpSubscription;
    _touchEndSubscription;
    _mouseMoveSubscription;
    _touchMoveSubscription;
    _clickSubscription;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._height = '60vh';
        this._dragOffset = 0;
        this._bounceOffset = 35;
        this._positionY = 0;
        this._isPartial = false;
        this._mouseDownSubscription = null;
        this._touchStartSubscription = null;
        this._mouseUpSubscription = null;
        this._touchEndSubscription = null;
        this._mouseMoveSubscription = null;
        this._touchMoveSubscription = null;
        this._clickSubscription = null;
        addFocusTrapController(this);
        addInheritanceProviderController(this, {
            context: SHEET_INHERITANCE_CONTEXT,
            values: () => ({
                closeable: this.closeable
            })
        });
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `is` property.
     * The `is` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-sheet';
    }
    /**
     * Gets or sets the `height` property.
     *
     * @public
     * @attr
     */
    get height() {
        return this._height;
    }
    set height(value) {
        if (this._height !== value) {
            this._height = value;
            this.requestUpdate('height');
        }
    }
    // #endregion
    // #region Methods
    /**
     * Opens the `SheetElement`.
     *
     * @public
     * @override
     */
    async open() {
        return super.open().then(() => {
            const root = this.getTemplatePart('root');
            this.moveDragElement(window.innerHeight - root.clientHeight, 'open');
            RendererServiceLocator.current.setAttribute(this, 'state', 'open');
        });
    }
    /**
     * Closes the `SheetElement`.
     *
     * @public
     * @override
     */
    async close() {
        return super.close().then((x) => {
            if (x) {
                if (this.closeable) {
                    this.moveDragElement(window.innerHeight + 50, 'close');
                    RendererServiceLocator.current.setAttribute(this, 'state', 'close');
                }
            }
            return x;
        });
    }
    /**
     * Sets the sheet to a partial (peek) state, showing only the heading area.
     *
     * @public
     */
    partial() {
        const heading = this.getTemplatePart('heading');
        this.moveDragElement(window.innerHeight - heading.clientHeight - this._bounceOffset, 'partial');
        RendererServiceLocator.current.setAttribute(this, 'state', 'partial');
    }
    /**
     * Toggles between open and partial states.
     *
     * @public
     */
    async toggle() {
        if (this._isPartial) {
            return this.open();
        }
        this.partial();
        return Promise.resolve();
    }
    /**
     * @protected
     * @override
     */
    onApplyTemplate() {
        super.onApplyTemplate();
        const heading = this.getTemplatePart('heading');
        this._clickSubscription = on(heading, 'click', () => void this.toggle());
        this._mouseDownSubscription = on(heading, 'mousedown', (e) => this.dragMouseDown(e));
        this._touchStartSubscription = on(heading, 'touchstart', (e) => this.dragMouseDown(e), { passive: true });
    }
    /**
     * Handles the backdrop click event.
     * Only closes the sheet if `clickOutsideToClose` is enabled.
     *
     * @public
     * @template
     */
    onBackdrop() {
        if (this.clickOutsideToClose) {
            void this.close();
        }
    }
    /**
     * @private
     */
    dragMouseDown(e) {
        const target = e.target;
        this._mouseUpSubscription = on(window, 'mouseup', (x) => this.dragMouseUp(x));
        this._mouseMoveSubscription = on(window, 'mousemove', (x) => this.dragMouseMove(x));
        this._touchEndSubscription = on(window, 'touchend', (x) => this.dragMouseUp(x));
        this._touchMoveSubscription = on(window, 'touchmove', (x) => this.dragMouseMove(x));
        this._positionY = this.getYCoord(e);
        this._dragOffset = this._positionY - target.getBoundingClientRect().top;
        document.body.style.overscrollBehaviorY = 'none';
    }
    /**
     * @private
     */
    dragMouseUp(e) {
        const root = this.getTemplatePart('root');
        const heading = this.getTemplatePart('heading');
        this._positionY = this.getYCoord(e);
        if (this.closeable && this._positionY >= window.innerHeight - 25) {
            // snap to close position.
            this.moveDragElement(window.innerHeight + 50, 'close');
            RendererServiceLocator.current.setAttribute(this, 'state', 'close');
            this._isPartial = false;
            return;
        }
        if (window.innerHeight - this._positionY > root.clientHeight / 2.5) {
            // snap to open position.
            this.moveDragElement(window.innerHeight - root.clientHeight, 'open');
            RendererServiceLocator.current.setAttribute(this, 'state', 'open');
            this._isPartial = false;
        }
        else {
            // snap to initial position.
            // only when the header is visible.
            this.moveDragElement(window.innerHeight - heading.clientHeight - this._bounceOffset, 'partial');
            RendererServiceLocator.current.setAttribute(this, 'state', 'partial');
            this._isPartial = true;
        }
    }
    /**
     * @private
     */
    dragMouseMove(e) {
        const root = this.getTemplatePart('root');
        this._positionY = this.getYCoord(e);
        if (this._positionY <= (window.innerHeight - root.clientHeight + this._dragOffset)) {
            return;
        }
        root.style.setProperty('transition', 'none');
        root.style.setProperty('transform', `translateY(${this._positionY - this._dragOffset}px)`);
    }
    /**
     * @private
     */
    moveDragElement(toPoint, state) {
        const root = this.getTemplatePart('root');
        const heading = this.getTemplatePart('heading');
        this._mouseUpSubscription?.dispose();
        this._touchEndSubscription?.dispose();
        this._mouseMoveSubscription?.dispose();
        this._touchMoveSubscription?.dispose();
        if (!toPoint && this._positionY >= window.innerHeight - heading.clientHeight) {
            root.style.setProperty('transform', `translateY(calc(100vh - ${heading.clientHeight}px))`);
        }
        else {
            root.style.setProperty('transform', `translateY(${(toPoint || this._positionY) + this._bounceOffset}px)`);
        }
        root.style.setProperty('transition', 'transform 500ms cubic-bezier(0.4, 0, 0, 1) 0s');
        if (state === 'close') {
            this._mouseDownSubscription?.dispose();
            this._touchStartSubscription?.dispose();
            this._clickSubscription?.dispose();
            setTimeout(() => {
                this._mouseDownSubscription?.dispose();
                this._touchStartSubscription?.dispose();
            }, 250);
        }
    }
    /**
     * @private
     */
    getYCoord(e) {
        if ('changedTouches' in e && e.changedTouches.length > 0) {
            return e.changedTouches[0].clientY;
        }
        if ('touches' in e && e.touches.length > 0) {
            return e.touches[0].clientY;
        }
        return e.clientY;
    }
};
__decorate([
    Attribute({ type: CssLength }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], SheetElement.prototype, "height", null);
SheetElement = __decorate([
    Component({
        selector: 'mosaik-sheet',
        template: sheetElementTemplate,
        themes: {
            joy: sheetElementJoyStyle,
            memphis: sheetElementMemphisStyle,
            cosmopolitan: sheetElementCosmopolitanStyle
        },
        imports: [
            BackdropElement,
            SheetHeaderElement,
            SheetContentElement,
            SheetFooterElement
        ]
    }),
    __metadata("design:paramtypes", [])
], SheetElement);
export { SheetElement };
//# sourceMappingURL=SheetElement.js.map