import * as i0 from '@angular/core';
import { input, booleanAttribute, numberAttribute, output, inject, signal, computed, effect, afterNextRender, ElementRef, forwardRef, ViewChildren, ChangeDetectionStrategy, Component } from '@angular/core';
import { ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import * as i1 from '@eui/components/shared';
import { BaseStatesDirective } from '@eui/components/shared';
import * as i2 from '@eui/components/eui-icon';
import { EUI_ICON } from '@eui/components/eui-icon';
import * as i3 from '@eui/components/eui-button';
import { EUI_BUTTON } from '@eui/components/eui-button';

/**
 * @description
 * Interactive star rating component for collecting user feedback and displaying rating values.
 * Implements ControlValueAccessor for seamless integration with Angular reactive and template-driven forms.
 * Supports keyboard navigation with arrow keys, Home, and End for accessibility.
 * Provides customizable number of stars and visual feedback for current rating state.
 * Commonly used for product reviews, feedback forms, and quality assessments.
 *
 * @usageNotes
 * ### Basic usage
 * ```html
 * <eui-rating [(rating)]="userRating"></eui-rating>
 * ```
 *
 * ### With reactive forms
 * ```typescript
 * ratingControl = new FormControl(3);
 * ```
 * ```html
 * <eui-rating [formControl]="ratingControl" [numberOfStars]="5"></eui-rating>
 * ```
 *
 * ### Disabled state
 * ```html
 * <eui-rating [rating]="4" [euiDisabled]="true"></eui-rating>
 * ```
 *
 * ### Accessibility
 * - Full keyboard navigation: Arrow keys to change rating, Home/End for first/last
 * - ARIA radiogroup role with proper labeling
 * - Each star is focusable and announces its value to screen readers
 * - Visual focus indicators for keyboard users
 *
 * ### Notes
 * - Click same star again to clear rating (set to 0)
 * - Only one star is focusable at a time (roving tabindex pattern)
 * - Emits `ratingChange` event on user interaction
 */
class EuiRatingComponent {
    constructor() {
        /**
         * Whether the toggle is disabled
         * @default false
         */
        this.euiDisabled = input(false, { ...(ngDevMode ? { debugName: "euiDisabled" } : {}), transform: booleanAttribute });
        /**
         * Current numbers of stars rating to display
         * @default 5
         */
        this.numberOfStars = input(5, { ...(ngDevMode ? { debugName: "numberOfStars" } : {}), transform: numberAttribute });
        /**
         * Current rating number
         * @default 0
         */
        this.rating = input(0, { ...(ngDevMode ? { debugName: "rating" } : {}), transform: numberAttribute });
        /**
         * Binds the aria-label attribute to the component.
         * @default 'eUI Rating'
         */
        this.ariaLabel = input('eUI Rating', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
        /**
         * Event emitted when toggle state changes
         * @emits number - The new rating value
         */
        this.ratingChange = output();
        this.baseStatesDirective = inject(BaseStatesDirective);
        this.internalRating = signal(0, ...(ngDevMode ? [{ debugName: "internalRating" }] : []));
        this.internalDisabled = signal(false, ...(ngDevMode ? [{ debugName: "internalDisabled" }] : []));
        this.cssClasses = computed(() => this.baseStatesDirective.getCssClasses('eui-rating'), ...(ngDevMode ? [{ debugName: "cssClasses" }] : []));
        this.numbers = computed(() => {
            const stars = [];
            const currentRating = this.internalRating();
            for (let i = 1; i <= this.numberOfStars(); i++) {
                stars.push({
                    index: i,
                    value: i <= currentRating,
                });
            }
            return stars;
        }, ...(ngDevMode ? [{ debugName: "numbers" }] : []));
        // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.onChange = () => {
            /* empty */
        };
        // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.onTouch = () => {
            /* empty */
        };
        effect(() => {
            this.internalRating.set(this.rating());
        });
        effect(() => {
            this.internalDisabled.set(this.euiDisabled());
        });
        afterNextRender(() => {
            this.ratingButtons.toArray().forEach((buttonRef) => {
                buttonRef.nativeElement?.addEventListener('blur', () => this.onTouch());
            });
        });
    }
    /**
     * Implements ControlValueAccessor
     * Updates the toggle's checked state
     */
    writeValue(value) {
        this.internalRating.set(value ?? 0);
    }
    /**
     * Implements ControlValueAccessor
     * Updates the rating's disabled state
     */
    setDisabledState(isDisabled) {
        this.internalDisabled.set(isDisabled);
    }
    /**
     * Handles focus out event
     * Notifies form control of touch event
     */
    onFocusOut() {
        this.onTouch();
    }
    /**
     * Implements ControlValueAccessor
     * Registers change handler
     * @todo Improve type definition or make generic
     */
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    registerOnChange(fn) {
        this.onChange = fn;
    }
    /**
     * Implements ControlValueAccessor
     * Registers touch handler
     * @todo Improve type definition or make generic
     */
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    registerOnTouched(fn) {
        this.onTouch = fn;
    }
    /**
     * Actions applied upon a keydown event
     *
     * @param event The keyboard event
     * @param index The index of the current star button
     */
    onKeydown(event, index) {
        const buttons = this.ratingButtons.toArray();
        const currentRating = this.internalRating();
        const numStars = this.numberOfStars();
        switch (event.key) {
            case 'Home': {
                buttons[0]?.nativeElement?.focus();
                event.preventDefault();
                this.updateRating(0, false);
                break;
            }
            case 'End': {
                buttons[numStars - 1]?.nativeElement?.focus();
                event.preventDefault();
                this.updateRating(numStars, false);
                break;
            }
            case 'ArrowRight': {
                if (currentRating < numStars) {
                    buttons[index]?.nativeElement?.focus();
                    if (currentRating === 0) {
                        this.updateRating(2, false);
                    }
                    else {
                        this.updateRating(currentRating + 1, false);
                    }
                }
                break;
            }
            case 'ArrowLeft': {
                if (currentRating === 1) {
                    buttons[0]?.nativeElement?.focus();
                }
                else if (currentRating > 1) {
                    buttons[index - 2]?.nativeElement?.focus();
                    this.updateRating(currentRating - 1, false);
                }
                break;
            }
        }
    }
    /**
     * Toggles the current state if not disabled
     * Emits the new state and notifies form control
     */
    onUpdate(rating) {
        this.updateRating(rating, true);
    }
    /**
     * Updates the checked state of the star button based on the index
     * @param index The index of the star button
     * @returns boolean - true if the index matches the rating, false otherwise
     */
    updateCheckedState(index) {
        return index === this.internalRating();
    }
    /**
     * Returns the tab index value based on the current rating and index
     * @param index The index of the star button
     * @returns number - 0 if the star is focusable, -1 otherwise
     */
    getTabIndexValue(index) {
        const currentRating = this.internalRating();
        if (currentRating === 0) {
            return index === 1 ? 0 : -1;
        }
        else {
            return index === currentRating ? 0 : -1;
        }
    }
    updateRating(rating, clearifSame = false) {
        if (!this.internalDisabled()) {
            const currentRating = this.internalRating();
            const newRating = currentRating === rating && clearifSame ? 0 : rating;
            this.internalRating.set(newRating);
            this.onChange(newRating);
            this.ratingChange.emit(newRating);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: EuiRatingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: EuiRatingComponent, isStandalone: true, selector: "eui-rating", inputs: { euiDisabled: { classPropertyName: "euiDisabled", publicName: "euiDisabled", isSignal: true, isRequired: false, transformFunction: null }, numberOfStars: { classPropertyName: "numberOfStars", publicName: "numberOfStars", isSignal: true, isRequired: false, transformFunction: null }, rating: { classPropertyName: "rating", publicName: "rating", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { ratingChange: "ratingChange" }, host: { attributes: { "role": "radiogroup" }, properties: { "class": "cssClasses()", "attr.aria-label": "ariaLabel()" } }, providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => EuiRatingComponent),
                multi: true,
            },
        ], viewQueries: [{ propertyName: "ratingButtons", predicate: ["euiRatingButtonRef"], descendants: true, read: ElementRef }], hostDirectives: [{ directive: i1.BaseStatesDirective }], ngImport: i0, template: "@for (n of numbers(); track n.index) {\n<button\n    #euiRatingButtonRef\n    euiButton\n    euiPrimary\n    euiRounded\n    euiBasicButton\n    euiIconButton\n    euiSizeS\n    hasNoFocusRing\n    isCompact\n    [euiDisabled]=\"internalDisabled()\"\n    (click)=\"onUpdate(n.index)\"\n    (keydown)=\"onKeydown($event, n.index)\"\n    [attr.tabindex]=\"getTabIndexValue(n.index)\"\n    [attr.aria-label]=\"'Rating star ' + n.index + ' - ' + (n.value ? 'Selected' : 'Not selected')\"\n    role=\"radio\"\n    [attr.aria-checked]=\"updateCheckedState(n.index)\">\n    @if (n.value) {\n        <eui-icon-svg icon=\"eui-star-fill\" fillColor=\"primary\" />\n    } @else {\n        <eui-icon-svg icon=\"eui-star\" fillColor=\"primary\" />\n    }\n</button>\n}\n", styles: [".eui-21 :host.eui-rating{display:inline-flex}.eui-21 :host.eui-rating:focus-visible:not([readonly]){outline:2px solid var(--eui-c-focus-visible)!important;outline-offset:-2px!important;transition:none}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: i2.EuiIconSvgComponent, selector: "eui-icon-svg, span[euiIconSvg], i[euiIconSvg]", inputs: ["icon", "fillColor", "set", "size", "style", "iconUrl", "transform", "ariaLabel", "ariaHidden", "focusable", "isLoading", "isInputIcon", "euiStart", "euiEnd"] }, { kind: "component", type: i3.EuiButtonComponent, selector: "button[euiButton], a[euiButton]", inputs: ["e2eAttr", "id", "euiBasicButton", "euiCTAButton", "euiBlockButton", "euiIconButton", "euiAvatarButton", "euiLineWrap", "isCompact", "hasNoFocusRing", "isChecked", "euiDisabled"], outputs: ["buttonClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: EuiRatingComponent, decorators: [{
            type: Component,
            args: [{ selector: 'eui-rating', changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class]': 'cssClasses()',
                        '[attr.aria-label]': 'ariaLabel()',
                        role: 'radiogroup',
                    }, hostDirectives: [
                        {
                            directive: BaseStatesDirective,
                        },
                    ], imports: [ReactiveFormsModule, ...EUI_ICON, ...EUI_BUTTON], providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: forwardRef(() => EuiRatingComponent),
                            multi: true,
                        },
                    ], template: "@for (n of numbers(); track n.index) {\n<button\n    #euiRatingButtonRef\n    euiButton\n    euiPrimary\n    euiRounded\n    euiBasicButton\n    euiIconButton\n    euiSizeS\n    hasNoFocusRing\n    isCompact\n    [euiDisabled]=\"internalDisabled()\"\n    (click)=\"onUpdate(n.index)\"\n    (keydown)=\"onKeydown($event, n.index)\"\n    [attr.tabindex]=\"getTabIndexValue(n.index)\"\n    [attr.aria-label]=\"'Rating star ' + n.index + ' - ' + (n.value ? 'Selected' : 'Not selected')\"\n    role=\"radio\"\n    [attr.aria-checked]=\"updateCheckedState(n.index)\">\n    @if (n.value) {\n        <eui-icon-svg icon=\"eui-star-fill\" fillColor=\"primary\" />\n    } @else {\n        <eui-icon-svg icon=\"eui-star\" fillColor=\"primary\" />\n    }\n</button>\n}\n", styles: [".eui-21 :host.eui-rating{display:inline-flex}.eui-21 :host.eui-rating:focus-visible:not([readonly]){outline:2px solid var(--eui-c-focus-visible)!important;outline-offset:-2px!important;transition:none}\n"] }]
        }], ctorParameters: () => [], propDecorators: { euiDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "euiDisabled", required: false }] }], numberOfStars: [{ type: i0.Input, args: [{ isSignal: true, alias: "numberOfStars", required: false }] }], rating: [{ type: i0.Input, args: [{ isSignal: true, alias: "rating", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ratingChange: [{ type: i0.Output, args: ["ratingChange"] }], ratingButtons: [{
                type: ViewChildren,
                args: ['euiRatingButtonRef', { read: ElementRef }]
            }] } });

const EUI_RATING = [
    EuiRatingComponent,
];
// export { EuiRatingComponent as EuiRating } from './eui-rating.component';

/**
 * Generated bundle index. Do not edit.
 */

export { EUI_RATING, EuiRatingComponent };
//# sourceMappingURL=eui-components-eui-rating.mjs.map
