// #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 { Listen } from '../../../../Events/Decorators/ListenDecorator';
import { clamp } from '../../../../Extensions/NumberExtensions';
import { Icons } from '../../../../Resources/Icons';
import { Disableable } from '../../../Behaviors/Disableable';
import { Labelable } from '../../../Behaviors/Labelable';
import { Orientable } from '../../../Behaviors/Orientable';
import { Sizeable } from '../../../Behaviors/Sizeable';
import { Valueable } from '../../../Behaviors/Valueable';
import { Variantable } from '../../../Behaviors/Variantable';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { FlowDirection } from '../../../Types/FlowDirection';
import { CustomElement } from '../../Abstracts/CustomElement';
import { IconElement } from '../../Media/Icon/IconElement';
import { ratingElementTemplate } from './RatingElementTemplate';
import { ratingElementJoyStyle } from './Themes/RatingElement.Joy';
import { ratingElementMemphisStyle } from './Themes/RatingElement.Memphis';
import { ratingElementCosmopolitanStyle } from './Themes/RatingElement.Cosmopolitan';
import { Themeable } from '../../../Behaviors/Themeable';
import { Property } from '../../../Decorators/PropertyDecorator';
/**
 * Rating - An interactive rating input control using customizable symbols for user feedback collection.
 *
 * @description
 * The Rating component provides an intuitive interface for users to provide feedback using visual symbols
 * (typically stars). It supports fractional ratings, hover interactions, keyboard navigation, and extensive
 * customization options. Features include configurable symbol sets (stars, hearts, thumbs up, etc.),
 * adjustable scale lengths, read-only and interactive modes, horizontal and vertical orientations,
 * accessibility support with ARIA attributes, and smooth hover animations. The component handles both
 * click and touch interactions, supports RTL layouts, and provides precise fractional rating capabilities.
 * Perfect for product reviews, feedback forms, content ratings, and user satisfaction surveys.
 *
 * @name Rating
 * @element mosaik-rating
 * @category Inputs
 *
 * @csspart root - The root container element.
 * @csspart symbols - The container for rating symbols.
 * @csspart symbol - Individual rating symbol elements.
 *
 * @cssprop {String} --rating-background-color - The background color.
 * @cssprop {String} --rating-border-color - The border color.
 * @cssprop {String} --rating-border-radius - The border radius.
 * @cssprop {String} --rating-border-style - The border style.
 * @cssprop {String} --rating-border-width - The border width.
 * @cssprop {String} --rating-foreground-color - The foreground color.
 * @cssprop {String} --rating-shadow - The shadow.
 *
 * @dependency mosaik-icon - Used for rating symbols.
 *
 * @example
 * ```html
 * <mosaik-rating
 *   length="5"
 *   value="4.5"
 *   label="Rate this product">
 * </mosaik-rating>
 * ```
 *
 * @example
 * ```html
 * <mosaik-rating
 *   length="10"
 *   orientation="vertical"
 *   readonly
 *   value="7">
 * </mosaik-rating>
 * ```
 *
 * @public
 */
let RatingElement = class RatingElement extends Themeable(Disableable(Valueable(Labelable(Orientable(Sizeable(Variantable(CustomElement))))))) {
    // #region Fields
    _ratingElement;
    _length;
    _precision;
    _readonly;
    _symbol;
    _symbolAccessor;
    _hoverValue;
    _isHovering;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._hoverValue = 0;
        this._isHovering = false;
        this._length = 5;
        this._precision = 1;
        this._readonly = false;
        this._symbol = Icons.star;
        this._symbolAccessor = (_value) => this._symbol;
    }
    // #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-rating';
    }
    /**
     * Gets or sets the `length` property.
     *
     * @public
     * @attr
     */
    get length() {
        return this._length;
    }
    set length(value) {
        if (this._length !== value) {
            this._length = value;
            this.requestUpdate('length');
        }
    }
    /**
     * Gets or sets the `precision` property.
     *
     * @public
     * @attr
     */
    get precision() {
        return this._precision;
    }
    set precision(value) {
        if (this._precision !== value) {
            this._precision = value;
            this.requestUpdate('precision');
        }
    }
    /**
     * Gets or sets the `readonly` property.
     *
     * @public
     * @attr
     */
    get readonly() {
        return this._readonly;
    }
    set readonly(value) {
        if (this._readonly !== value) {
            this._readonly = value;
            this.requestUpdate('readonly');
        }
    }
    /**
     * Gets or sets the `symbol` property.
     *
     * @public
     * @attr
     */
    get symbol() {
        return this._symbol;
    }
    set symbol(value) {
        if (this._symbol !== value) {
            this._symbol = value;
            this.requestUpdate('symbol');
        }
    }
    /**
     * Gets or sets the `symbolAccessor` property.
     *
     * @public
     * @attr
     */
    get symbolAccessor() {
        return this._symbolAccessor;
    }
    set symbolAccessor(value) {
        if (this._symbolAccessor !== value) {
            this._symbolAccessor = value;
            this.requestUpdate('symbolAccessor');
        }
    }
    /**
     * Gets or sets the `hoverValue` property.
     *
     * @public
     */
    get hoverValue() {
        return this._hoverValue;
    }
    set hoverValue(value) {
        if (this._hoverValue !== value) {
            this._hoverValue = value;
            this.requestUpdate('hoverValue');
        }
    }
    /**
     * Gets or sets the `isHovering` property.
     *
     * @public
     */
    get isHovering() {
        return this._isHovering;
    }
    set isHovering(value) {
        if (this._isHovering !== value) {
            this._isHovering = value;
            this.requestUpdate('isHovering');
        }
    }
    // #endregion
    // #region Methods
    /**
     * @public
     * @override
     */
    focus(options) {
        this._ratingElement.focus(options);
    }
    /**
     * @public
     * @override
     */
    blur() {
        this._ratingElement.blur();
    }
    /**
     * @protected
     * @override
     */
    onApplyTemplate() {
        super.onApplyTemplate();
        this._ratingElement = this.getTemplatePart('root');
    }
    /**
     * @protected
     */
    onClick(event) {
        if (this.disabled) {
            return;
        }
        this.setValue(this.getValueFromMousePosition(event));
        this.emit('change');
    }
    /**
     * @protected
     */
    onKeyDown(event) {
        const isLtr = this.dir === FlowDirection.LeftToRight;
        const isRtl = this.dir === FlowDirection.RightToLeft;
        const oldValue = this.value;
        if (this.disabled || this.readonly) {
            return;
        }
        if (event.key === 'ArrowDown' || (isLtr && event.key === 'ArrowLeft') || (isRtl && event.key === 'ArrowRight')) {
            const decrement = event.shiftKey ? 1 : this.precision;
            this.value = Math.max(0, Number(this.value) - decrement);
            event.preventDefault();
        }
        if (event.key === 'ArrowUp' || (isLtr && event.key === 'ArrowRight') || (isRtl && event.key === 'ArrowLeft')) {
            const increment = event.shiftKey ? 1 : this.precision;
            this.value = Math.min(this._length, Number(this.value) + increment);
            event.preventDefault();
        }
        if (event.key === 'Home') {
            this.value = 0;
            event.preventDefault();
        }
        if (event.key === 'End') {
            this.value = this._length;
            event.preventDefault();
        }
        if (this.value !== oldValue) {
            this.emit('change');
        }
    }
    /**
     * @protected
     */
    onMouseEnter(event) {
        this._isHovering = true;
        this._hoverValue = this.getValueFromMousePosition(event);
    }
    /**
     * @protected
     */
    onMouseMove(event) {
        this._hoverValue = this.getValueFromMousePosition(event);
    }
    /**
     * @protected
     */
    onMouseLeave() {
        this._isHovering = false;
    }
    /**
     * @protected
     */
    onTouchStart(event) {
        this._isHovering = true;
        this._hoverValue = this.getValueFromTouchPosition(event);
        // Prevent scrolling when touch is initiated
        event.preventDefault();
    }
    /**
     * @protected
     */
    onTouchMove(event) {
        this._hoverValue = this.getValueFromTouchPosition(event);
    }
    /**
     * @protected
     */
    onTouchEnd(event) {
        this._isHovering = false;
        this.setValue(this._hoverValue);
        this.emit('change');
        // Prevent click on mobile devices
        event.preventDefault();
    }
    /**
     * @private
     */
    getValueFromMousePosition(event) {
        return this.getValueFromXCoordinate(event.clientX);
    }
    /**
     * @private
     */
    getValueFromTouchPosition(event) {
        return this.getValueFromXCoordinate(event.touches[0].clientX);
    }
    /**
     * @private
     */
    getValueFromXCoordinate(coordinate) {
        const isRtl = this.dir === FlowDirection.RightToLeft;
        const rect = this._ratingElement.getBoundingClientRect();
        const value = isRtl
            ? this.roundToPrecision(((rect.right - coordinate) / rect.width) * this._length, this.precision)
            : this.roundToPrecision(((coordinate - rect.left) / rect.width) * this._length, this.precision);
        return clamp(value, 0, this._length);
    }
    /**
     * @private
     */
    setValue(newValue) {
        if (this.disabled || this.readonly) {
            return;
        }
        this.value = newValue === this.value ? 0 : newValue;
        this._isHovering = false;
    }
    /**
     * @private
     */
    roundToPrecision(numberToRound, precision = 0.5) {
        const multiplier = 1 / precision;
        return Math.ceil(numberToRound * multiplier) / multiplier;
    }
};
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], RatingElement.prototype, "length", null);
__decorate([
    Attribute({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], RatingElement.prototype, "precision", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], RatingElement.prototype, "readonly", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], RatingElement.prototype, "symbol", null);
__decorate([
    Attribute({ type: Function }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function])
], RatingElement.prototype, "symbolAccessor", null);
__decorate([
    Property({ type: Number }),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [Number])
], RatingElement.prototype, "hoverValue", null);
__decorate([
    Property({ type: Boolean }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], RatingElement.prototype, "isHovering", null);
__decorate([
    Listen('click'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [MouseEvent]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onClick", null);
__decorate([
    Listen('keydown'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [KeyboardEvent]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onKeyDown", null);
__decorate([
    Listen('mouseenter'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [MouseEvent]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onMouseEnter", null);
__decorate([
    Listen('mousemove'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [MouseEvent]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onMouseMove", null);
__decorate([
    Listen('mouseleave'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onMouseLeave", null);
__decorate([
    Listen('touchstart'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onTouchStart", null);
__decorate([
    Listen('touchmove', 'self', { passive: true }),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onTouchMove", null);
__decorate([
    Listen('touchend'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", void 0)
], RatingElement.prototype, "onTouchEnd", null);
RatingElement = __decorate([
    Component({
        selector: 'mosaik-rating',
        template: ratingElementTemplate,
        themes: {
            joy: ratingElementJoyStyle,
            memphis: ratingElementMemphisStyle,
            cosmopolitan: ratingElementCosmopolitanStyle
        },
        imports: [
            IconElement
        ]
    }),
    __metadata("design:paramtypes", [])
], RatingElement);
export { RatingElement };
//# sourceMappingURL=RatingElement.js.map