// #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 { Appearanceable } from '../../../Behaviors/Appearanceable';
import { Disableable } from '../../../Behaviors/Disableable';
import { Focusable } from '../../../Behaviors/Focusable';
import { Orientable } from '../../../Behaviors/Orientable';
import { Variantable } from '../../../Behaviors/Variantable';
import { Themeable } from '../../../Behaviors/Themeable';
import { Invalidable } from '../../../Behaviors/Invalidable';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { Property } from '../../../Decorators/PropertyDecorator';
import { AutoToolTipPlacement } from '../../../Types/AutoToolTipPlacement';
import { TickPlacement } from '../../../Types/TickPlacement';
import { TextElement } from '../../Primitives/Text/TextElement';
import { TickBarElement } from '../../Primitives/TickBar/TickBarElement';
import { RangeBaseElement } from '../Abstracts/RangeBaseElement';
import { scrubSliderElementTemplate } from './ScrubSliderElementTemplate';
import { scrubSliderElementJoyStyle } from './Themes/ScrubSliderElement.Joy';
import { scrubSliderElementCosmopolitanStyle } from './Themes/ScrubSliderElement.Cosmopolitan';
import { scrubSliderElementMemphisStyle } from './Themes/ScrubSliderElement.Memphis';
// #endregion
/**
 * Scrub Slider – A customizable slider component for precise value selection.
 *
 * @element mosaik-scrub-slider
 *
 * @description
 * The Scrub Slider component allows users to select a value from a range with precision.
 * It supports features like ticks, tooltips, and customizable step sizes.
 *
 * @category Ranges
 *
 * @slot hint - The hint slot.
 *
 * @public
 */
let ScrubSliderElement = class ScrubSliderElement extends Themeable(Orientable(Disableable(Variantable(Appearanceable(Focusable(Invalidable(RangeBaseElement))))))) {
    // #region Fields
    _inputElement;
    _step;
    _ticks;
    _tickPlacement;
    _tooltipPlacement;
    _tooltipAccessor;
    _track;
    _showTickLabels;
    _tickLabelAccessor;
    _pxPerStep;
    _accelerate;
    _dragging;
    _pointerId;
    _lastX;
    _pxRemainder;
    _bgOffsetPx;
    // #endregion
    // #region Ctor
    constructor() {
        super();
        this._step = 1;
        this._ticks = 0;
        this._tickPlacement = TickPlacement.Both;
        this._tooltipPlacement = AutoToolTipPlacement.None;
        this._tooltipAccessor = (v) => {
            if (v !== undefined) {
                return v.toString();
            }
            return '';
        };
        this._track = true;
        this._showTickLabels = true;
        this._tickLabelAccessor = (v) => {
            if (v !== undefined) {
                return v.toString();
            }
            return '';
        };
        this._pxPerStep = 8;
        this._accelerate = true;
        this._dragging = false;
        this._pointerId = undefined;
        this._lastX = 0;
        this._pxRemainder = 0;
        this._bgOffsetPx = 0;
    }
    // #endregion
    // #region Properties
    static get is() {
        return 'mosaik-scrub-slider';
    }
    get step() { return this._step; }
    set step(value) {
        if (this._step !== value && value > 0) {
            this._step = value;
            this.requestUpdate('step');
        }
    }
    get ticks() { return this._ticks; }
    set ticks(value) {
        if (this._ticks !== value) {
            this._ticks = value;
            this.requestUpdate('ticks');
        }
    }
    get tickPlacement() { return this._tickPlacement; }
    set tickPlacement(value) {
        if (this._tickPlacement !== value) {
            this._tickPlacement = value;
            this.requestUpdate('tickPlacement');
        }
    }
    get tooltipPlacement() { return this._tooltipPlacement; }
    set tooltipPlacement(value) {
        if (this._tooltipPlacement !== value) {
            this._tooltipPlacement = value;
            this.requestUpdate('tooltipPlacement');
        }
    }
    get tooltipAccessor() { return this._tooltipAccessor; }
    set tooltipAccessor(value) {
        if (this._tooltipAccessor !== value) {
            this._tooltipAccessor = value;
            this.requestUpdate('tooltipAccessor');
        }
    }
    get track() { return this._track; }
    set track(value) {
        if (this._track !== value) {
            this._track = value;
            this.requestUpdate('track');
        }
    }
    get showTickLabels() { return this._showTickLabels; }
    set showTickLabels(value) {
        if (this._showTickLabels !== value) {
            this._showTickLabels = value;
            this.requestUpdate('showTickLabels');
        }
    }
    get tickLabelAccessor() { return this._tickLabelAccessor; }
    set tickLabelAccessor(value) {
        if (this._tickLabelAccessor !== value) {
            this._tickLabelAccessor = value;
            this.requestUpdate('tickLabelAccessor');
        }
    }
    get pxPerStep() { return this._pxPerStep; }
    set pxPerStep(v) {
        if (this._pxPerStep !== v && v > 0) {
            this._pxPerStep = v;
            this.requestUpdate('pxPerStep');
        }
    }
    /**
     * Accelerate the slider when dragging.
     *
     * @public
     * @default true
     */
    get accelerate() { return this._accelerate; }
    set accelerate(v) {
        if (this._accelerate !== v) {
            this._accelerate = v;
            this.requestUpdate('accelerate');
        }
    }
    // #endregion
    // #region Methods
    /**
     * Checks the validity of the element and returns `true` if it is valid; otherwise, `false`.
     *
     * @public
     * @override
     */
    checkValidity() {
        return this.shadowRoot
            ? this._inputElement.checkValidity()
            : true;
    }
    onApplyTemplate() {
        super.onApplyTemplate();
        this._inputElement = this.getTemplatePart('input');
        const surface = this.getTemplatePart('surface');
        on(this._inputElement, 'input', (e) => {
            this.value = Number.parseInt(e.target.value);
        });
        surface.style.touchAction = 'none';
        surface.tabIndex = surface.tabIndex || 0;
        on(surface, 'pointerdown', (ev) => {
            if (this.disabled) {
                return;
            }
            this._dragging = true;
            this._pointerId = ev.pointerId;
            surface.setPointerCapture(ev.pointerId);
            this._lastX = ev.clientX;
            this._pxRemainder = 0;
            ev.preventDefault();
            ev.stopPropagation();
        });
        on(surface, 'pointermove', (ev) => {
            if (!this._dragging || ev.pointerId !== this._pointerId)
                return;
            const dx = ev.clientX - this._lastX;
            this._lastX = ev.clientX;
            const accel = this._accelerate ? Math.max(1, Math.min(4, Math.abs(dx) / 12)) : 1;
            const px = dx * accel + this._pxRemainder;
            const perStep = Math.max(1, this._pxPerStep || 1);
            const stepsDelta = Math.trunc(px / perStep);
            this._pxRemainder = px - stepsDelta * perStep;
            if (stepsDelta !== 0) {
                const step = this.step || 1;
                const next = this.clamp(this.value + stepsDelta * step);
                if (next !== this.value) {
                    this.value = next;
                    this.dispatchEvent(new InputEvent('input', {
                        bubbles: true,
                        composed: true
                    }));
                }
            }
            this._bgOffsetPx = (this._bgOffsetPx - dx) % (this._pxPerStep || 1);
            this.applyTickOffset();
        });
        const end = () => {
            if (!this._dragging)
                return;
            this._dragging = false;
            this._pointerId = undefined;
            this._pxRemainder = 0;
            this.dispatchEvent(new Event('change', {
                bubbles: true,
                composed: true
            }));
        };
        on(surface, 'pointerup', end);
        on(surface, 'pointercancel', end);
        on(this, 'blur', end);
        on(surface, 'keydown', (ev) => {
            if (this.disabled)
                return;
            let delta = 0;
            switch (ev.key) {
                case 'ArrowRight':
                case 'ArrowUp':
                    delta = delta + 1;
                    break;
                case 'ArrowLeft':
                case 'ArrowDown':
                    delta = delta - 1;
                    break;
                case 'PageUp':
                    delta = delta + 5;
                    break;
                case 'PageDown':
                    delta = delta - 5;
                    break;
                case 'Home':
                    this.value = this.min;
                    break;
                case 'End':
                    this.value = this.max;
                    break;
                default: return;
            }
            const step = this.step || 1;
            this.value = this.clamp(this.value + delta * step);
            this.dispatchEvent(new InputEvent('input', {
                bubbles: true,
                composed: true
            }));
            this.dispatchEvent(new Event('change', {
                bubbles: true,
                composed: true
            }));
        });
        this.applyTickOffset();
    }
    clamp(v) {
        return Math.max(this.min, Math.min(this.max, v));
    }
    applyTickOffset() {
        this.style.setProperty('--tick-offset', `${this._bgOffsetPx}px`);
    }
};
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], ScrubSliderElement.prototype, "step", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], ScrubSliderElement.prototype, "ticks", null);
__decorate([
    Attribute({ type: TickPlacement }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], ScrubSliderElement.prototype, "tickPlacement", null);
__decorate([
    Attribute({ type: AutoToolTipPlacement }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], ScrubSliderElement.prototype, "tooltipPlacement", null);
__decorate([
    Property({ type: Function }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function])
], ScrubSliderElement.prototype, "tooltipAccessor", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], ScrubSliderElement.prototype, "track", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], ScrubSliderElement.prototype, "showTickLabels", null);
__decorate([
    Property({ type: Function }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function])
], ScrubSliderElement.prototype, "tickLabelAccessor", null);
__decorate([
    Attribute({
        type: Number,
        useDefault: true,
        attribute: 'px-per-step'
    }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], ScrubSliderElement.prototype, "pxPerStep", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], ScrubSliderElement.prototype, "accelerate", null);
ScrubSliderElement = __decorate([
    Component({
        selector: 'mosaik-scrub-slider',
        template: scrubSliderElementTemplate,
        themes: {
            joy: scrubSliderElementJoyStyle,
            cosmopolitan: scrubSliderElementCosmopolitanStyle,
            memphis: scrubSliderElementMemphisStyle
        },
        imports: [
            TextElement,
            TickBarElement
        ]
    }),
    __metadata("design:paramtypes", [])
], ScrubSliderElement);
export { ScrubSliderElement };
//# sourceMappingURL=ScrubSliderElement.js.map