// #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 { HTMLElementEventEmitter, on } from '@breadstone/mosaik-elements';
import { RendererServiceLocator } from '../../../../Dom/Renderer';
import { Emit } from '../../../../events/Decorators/EmitDecorator';
import { Attachable } from '../../../Behaviors/Attachable';
import { Themeable } from '../../../Behaviors/Themeable';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { CustomElement } from '../../Abstracts/CustomElement';
import { backTopElementTemplate } from './BackTopElementTemplate';
import { backTopElementCosmopolitanStyle } from './Themes/BackTopElement.Cosmopolitan';
import { backTopElementJoyStyle } from './Themes/BackTopElement.Joy';
import { backTopElementMemphisStyle } from './Themes/BackTopElement.Memphis';
/**
 * BackTop - A scrollable page navigation element that provides quick access to return to the top or bottom.
 *
 * @description
 * The BackTop component creates an interactive floating action button that appears when a user scrolls
 * beyond a specified offset threshold. It provides smooth scroll-to-position functionality for returning
 * to either the top or bottom of scrollable containers. The component monitors scroll position in real-time,
 * automatically showing or hiding itself based on the configured offset, and offers smooth animated scrolling
 * behavior when activated. It includes attachment positioning capabilities for flexible placement within
 * scrollable regions, making it ideal for long-form content pages, infinite scroll interfaces, or any
 * scenario requiring convenient navigation across extensive vertical content.
 *
 * @name BackTop
 * @element mosaik-back-top
 * @category Misc
 *
 * @slot - Default slot for custom content (overrides default floating action button)
 *
 * @csspart root - The root container element wrapping the component
 * @csspart button - The floating action button element that triggers scroll navigation
 *
 * @cssprop {String} --back-top-position - The positioning context for the back-to-top button
 * @cssprop {String} --back-top-bottom - The distance from the bottom edge of the viewport
 * @cssprop {String} --back-top-right - The distance from the right edge of the viewport
 * @cssprop {String} --back-top-z-index - The stacking order above other page content
 * @cssprop {String} --back-top-opacity - The base opacity when visible
 * @cssprop {String} --back-top-visibility - The visibility state control
 * @cssprop {String} --back-top-transition-property - The CSS properties to animate during state changes
 * @cssprop {String} --back-top-transition-duration - The duration of show/hide transitions
 * @cssprop {String} --back-top-transition-timing-function - The easing function for animations
 *
 * @dependency {FloatingActionButtonElement} - Floating action button for scroll trigger
 *
 * @fires restored {RestoredEvent} - Fired when scroll position has been restored to target (top or bottom)
 *
 * @example
 * Basic back-to-top button (default):
 * ```html
 * <mosaik-back-top></mosaik-back-top>
 * ```
 *
 * @example
 * Configured for scrolling to bottom with custom offset:
 * ```html
 * <mosaik-back-top offset="200" to="bottom"></mosaik-back-top>
 * ```
 *
 * @example
 * Attached to a specific scrollable container:
 * ```html
 * <div id="scrollContainer" style="height: 400px; overflow-y: auto;">
 *   <!-- Long content -->
 *   <mosaik-back-top for="scrollContainer"></mosaik-back-top>
 * </div>
 * ```
 *
 * @public
 */
let BackTopElement = class BackTopElement extends Themeable(Attachable(CustomElement)) {
    // #region Fields
    _restored;
    _offset;
    _visible;
    _position;
    _to;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._offset = 120;
        this._visible = false;
        this._position = 0;
        this._to = 'top';
        this._restored = new HTMLElementEventEmitter(this, 'restored');
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `is` property.
     * The `is` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-back-top';
    }
    /**
     * Gets or sets the `offset` property.
     * The scroll offset threshold (in pixels) after which the back-to-top button becomes visible.
     * The default value is 120 pixels.
     *
     * @public
     * @attr
     */
    get offset() {
        return this._offset;
    }
    set offset(value) {
        if (this._offset !== value) {
            this._offset = value;
            this.requestUpdate('offset');
        }
    }
    /**
     * Gets or sets the `to` property.
     * Specifies the scroll destination: 'top' scrolls to the beginning, 'bottom' scrolls to the end.
     * The default value is 'top'.
     *
     * @public
     * @attr
     */
    get to() {
        return this._to;
    }
    set to(value) {
        if (this._to !== value) {
            this._to = value;
            this.requestUpdate('to');
        }
    }
    /**
     * Returns the `position` property.
     * The current scroll position as a percentage (0-100) of the total scrollable height.
     *
     * @public
     * @readonly
     */
    get position() {
        return this._position;
    }
    /**
     * Called when the scroll position has been restored to the target location.
     * Provides reference to `IEventDetail` as event detail.
     *
     * @public
     * @readonly
     * @eventProperty
     */
    get restored() {
        return this._restored;
    }
    /**
     * Emits the `restored` event.
     *
     * @protected
     */
    onRestored(args) {
        this._restored.emit(args);
    }
    // #endregion
    // #region Methods
    /**
     * @public
     */
    onApplyTemplate() {
        super.onApplyTemplate();
        if (this.control) {
            on(this.control, 'scroll', () => {
                this._position = this.getPosition(this.control);
                const offset = this.measureScrollOffset(this.control, this._to) ?? 0;
                const isVisible = offset > this._offset;
                if (isVisible !== this._visible) {
                    this._visible = isVisible;
                    if (isVisible) {
                        RendererServiceLocator.current.addClass(this, 'active');
                    }
                    else {
                        RendererServiceLocator.current.removeClass(this, 'active');
                    }
                }
            });
        }
    }
    /**
     * @public
     */
    onClick() {
        if (this.control) {
            this.scrollToPostion(this.to);
            this.restored.emit({ restored: true });
        }
    }
    /**
     * @private
     */
    scrollToPostion(position) {
        if (this.control) {
            switch (position) {
                case 'top':
                    this.control.scrollTo({
                        top: 0,
                        behavior: 'smooth'
                    });
                    break;
                case 'bottom':
                    this.control.scrollTo({
                        top: this.control.scrollHeight,
                        behavior: 'smooth'
                    });
                    break;
                case 'left':
                    this.control.scrollTo({
                        left: 0,
                        behavior: 'smooth'
                    });
                    break;
                case 'right':
                    this.control.scrollTo({
                        left: this.control.scrollWidth,
                        behavior: 'smooth'
                    });
                    break;
                default:
                    break;
            }
        }
    }
    /**
     * @private
     */
    getPosition(el) {
        return el ? Math.round((100 / (el.scrollHeight - el.offsetHeight)) * el.scrollTop) ?? 0 : 0;
    }
    /**
     * Measures the scroll offset relative to the specified edge of the viewport. This method can be
     * used instead of directly checking scrollLeft or scrollTop, since browsers are not consistent
     * about what scrollLeft means in RTL. The values returned by this method are normalized such that
     * left and right always refer to the left and right side of the scrolling container irrespective
     * of the layout direction. start and end refer to left and right in an LTR context and vice-versa
     * in an RTL context.
     *
     * @private
     * @param from The edge to measure from.
     */
    measureScrollOffset(el, from) {
        const LEFT = 'left';
        const RIGHT = 'right';
        if (from === 'top') {
            return el.scrollTop;
        }
        if (from === 'bottom') {
            return el.scrollHeight - el.clientHeight - el.scrollTop;
        }
        // Rewrite start & end as left or right offsets.
        const isRtl = this.dir && this.dir === 'rtl';
        if (from === 'start') {
            from = isRtl ? RIGHT : LEFT;
        }
        else if (from === 'end') {
            from = isRtl ? LEFT : RIGHT;
        }
        return from === LEFT
            ? el.scrollLeft
            : el.scrollWidth - el.clientWidth - el.scrollLeft;
    }
};
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], BackTopElement.prototype, "offset", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], BackTopElement.prototype, "to", null);
__decorate([
    Emit({ eventName: 'restored' }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], BackTopElement.prototype, "restored", null);
BackTopElement = __decorate([
    Component({
        selector: 'mosaik-back-top',
        template: backTopElementTemplate,
        themes: {
            joy: backTopElementJoyStyle,
            memphis: backTopElementMemphisStyle,
            cosmopolitan: backTopElementCosmopolitanStyle
        },
        imports: []
    }),
    __metadata("design:paramtypes", [])
], BackTopElement);
export { BackTopElement };
//# sourceMappingURL=BackTopElement.js.map