// #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 { RendererServiceLocator } from '../../../../Dom/Renderer';
import { Emit } from '../../../../Events/Decorators/EmitDecorator';
import { on, HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { Attachable } from '../../../Behaviors/Attachable';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { CustomElement } from '../../Abstracts/CustomElement';
import { backTopElementTemplate } from './BackTopElementTemplate';
import { backTopElementJoyStyle } from './Themes/BackTopElement.Joy';
import { backTopElementRetroStyle } from './Themes/BackTopElement.Retro';
import { backTopElementCosmopolitanStyle } from './Themes/BackTopElement.Cosmopolitan';
import { Themeable } from '../../../Behaviors/Themeable';
/**
 * The `{@link BackTopElement}` element.
 *
 * @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 `{@link is}` property.
     * The `{@link is}` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-back-top';
    }
    /**
     * Gets or sets the `offset` property.
     *
     * @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.
     *
     * @public
     * @attr
     */
    get to() {
        return this._to;
    }
    set to(value) {
        if (this._to !== value) {
            this._to = value;
            this.requestUpdate('to');
        }
    }
    /**
     * Returns the `position` property.
     *
     * @public
     * @readonly
     */
    get position() {
        return this._position;
    }
    /**
     * Called when back to top is restored.
     * Provides reference to `{@link IEventDetail}` as event detail.
     *
     * @public
     * @readonly
     * @eventProperty
     */
    get restored() {
        return this._restored;
    }
    /**
     * Emits the {@link 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,
            retro: backTopElementRetroStyle,
            cosmopolitan: backTopElementCosmopolitanStyle
        },
        imports: []
    }),
    __metadata("design:paramtypes", [])
], BackTopElement);
export { BackTopElement };
//# sourceMappingURL=BackTopElement.js.map