import * as i0 from '@angular/core';
import { ElementRef, Component, Inject, Optional, ViewChild, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i2 from '@angular/forms';
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
import * as i3 from '@wm/components/base';
import { PROP_STRING, PROP_ANY, PROP_NUMBER, PROP_BOOLEAN, register, registerFormWidget, styler, getOrderedDataset, provideAs, provideAsWidgetRef, WmComponentsModule } from '@wm/components/base';
import { FormWidgetType, generateGUId, setCSS, noop } from '@wm/core';
import { DatasetAwareFormComponent } from '@wm/components/input';
import { slice, isEmpty, find, isUndefined, toString } from 'lodash-es';

const ratingProps = new Map([
    ['caption', PROP_STRING],
    ['class', PROP_STRING],
    ['datafield', PROP_STRING],
    ['dataset', PROP_ANY],
    ['datavaluesource', PROP_ANY],
    ['datavalue', PROP_STRING],
    ['displayexpression', PROP_STRING],
    ['displayfield', PROP_STRING],
    ['iconcolor', PROP_STRING],
    ['iconsize', PROP_STRING],
    ['maxvalue', { value: 5, ...PROP_NUMBER }],
    ['name', PROP_STRING],
    ['orderby', PROP_STRING],
    ['readonly', PROP_BOOLEAN],
    ['show', { value: true, ...PROP_BOOLEAN }],
    ['showcaptions', { value: true, ...PROP_BOOLEAN }],
    ['tabindex', { value: 0, ...PROP_NUMBER }],
    ['usekeys', PROP_BOOLEAN]
]);
const registerProps = () => {
    register('wm-rating', ratingProps);
    registerFormWidget(FormWidgetType.RATING, new Map(ratingProps));
};

const _c0 = ["ratingInput"];
const _c1 = ["wmRating", ""];
const _c2 = () => ({ standalone: true });
const _c3 = a0 => ({ "font-size": a0 });
const _c4 = (a0, a1) => ({ width: a0, color: a1 });
const _c5 = a0 => ({ active: a0 });
const _c6 = (a0, a1) => ({ "font-size": a0, color: a1 });
function RatingComponent_div_1_label_1_Template(rf, ctx) { if (rf & 1) {
    const _r1 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "label", 8);
    i0.ɵɵlistener("touchstart", function RatingComponent_div_1_label_1_Template_label_touchstart_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onTouchStart($event)); })("mouseleave", function RatingComponent_div_1_label_1_Template_label_mouseleave_0_listener($event) { const rate_r3 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onMouseleave($event, rate_r3)); })("mouseover", function RatingComponent_div_1_label_1_Template_label_mouseover_0_listener($event) { const rate_r3 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onMouseOver($event, rate_r3)); });
    i0.ɵɵelementStart(1, "input", 9, 0);
    i0.ɵɵlistener("click", function RatingComponent_div_1_label_1_Template_input_click_1_listener($event) { const rate_r3 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onRatingClick($event, rate_r3)); });
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const rate_r3 = ctx.$implicit;
    const ctx_r1 = i0.ɵɵnextContext(2);
    i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(8, _c5, rate_r3.index <= ctx_r1.selectedRatingValue))("ngStyle", i0.ɵɵpureFunction2(10, _c6, ctx_r1.iconsize, rate_r3.index <= ctx_r1.selectedRatingValue && ctx_r1.iconcolor))("title", rate_r3.label || rate_r3.index)("for", ctx_r1._id + "-" + rate_r3.index);
    i0.ɵɵadvance();
    i0.ɵɵproperty("id", ctx_r1._id + "-" + rate_r3.index)("value", rate_r3.key || rate_r3.index);
    i0.ɵɵattribute("aria-label", (rate_r3.label || rate_r3.index) + " out of " + ctx_r1.ratingItems.length + " ratings")("aria-checked", rate_r3.label === ctx_r1.datavalue ? "true" : "false");
} }
function RatingComponent_div_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 6);
    i0.ɵɵtemplate(1, RatingComponent_div_1_label_1_Template, 3, 13, "label", 7);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵadvance();
    i0.ɵɵproperty("ngForOf", ctx_r1.ratingItems);
} }
function RatingComponent_label_4_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "label", 10);
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵproperty("textContent", ctx_r1.caption);
} }
const DEFAULT_CLS = 'app-ratings';
const WIDGET_CONFIG = { widgetType: 'wm-rating', hostClass: DEFAULT_CLS };
const MAX_RATING = 10;
const DEFAULT_RATING = 5;
class RatingComponent extends DatasetAwareFormComponent {
    static { this.initializeProps = registerProps(); }
    get selectedRatingValue() {
        return this._selectedRatingValue;
    }
    set selectedRatingValue(val) {
        this._selectedRatingValue = val;
        this.calculateRatingsWidth();
    }
    constructor(inj, explicitContext) {
        super(inj, WIDGET_CONFIG, explicitContext);
        this._id = generateGUId();
        styler(this.nativeElement, this);
        // prepare the rating options on dataset ready.
        const datasetSubscription = this.dataset$.subscribe(() => {
            this.prepareRatingDataset();
        });
        this.registerDestroyListener(() => datasetSubscription.unsubscribe());
        // listen to changes in datavalue.
        const datavalueSubscription = this.datavalue$.subscribe(() => this.onDatavalueChange(this.datavalue));
        this.registerDestroyListener(() => datavalueSubscription.unsubscribe());
    }
    // Change event is registered from the template, Prevent the framework from registering one more event
    handleEvent(node, eventName, eventCallback, locals) {
        if (eventName !== 'change' && eventName !== 'blur') {
            super.handleEvent(this.ratingEl.nativeElement, eventName, eventCallback, locals);
        }
    }
    // This function returns the rating widget dataset containing the index, value and label
    prepareRatingDataset() {
        if (!this.datasetItems.length) {
            this.ratingItems = [];
            this.caption = '';
        }
        let ratingItems = [];
        let data = this.datasetItems;
        const maxvalue = parseInt(this.maxvalue || this.datasetItems.length, 10);
        const maxValue = (maxvalue > MAX_RATING ? MAX_RATING : maxvalue) || DEFAULT_RATING;
        /**
         * 1. If datasetItems.length is more than maxValue (i.e. 10 ratings) then just extract maxValue of items from datasetItems.
         * 2. If datasetItems are not available then prepare ratings value depending on maxvalue. eg: 1,2,3 .. upto maxvalue
         * 3. If maxvalue / i value is more than datasetItems length, prepare default rating items for i values more than datasetItems.length
         */
        if (data.length && data.length > maxValue) {
            data = slice(data, 0, maxValue);
        }
        for (let i = maxValue; i > 0; i--) {
            if (!data.length) {
                ratingItems.push({ key: i, value: i, index: i, label: i });
            }
            else {
                if (i > data.length) {
                    ratingItems.push({ key: i, value: i, index: i, label: i });
                }
                else {
                    data = getOrderedDataset(data, 'index:desc');
                    ratingItems = ratingItems.concat(data);
                    break;
                }
            }
        }
        this.ratingItems = ratingItems;
        if (!data.length) { // constructs default datasetItems when there is no dataset binding.
            this.datasetItems = ratingItems;
        }
        this.onDatavalueChange(this.datavalue);
    }
    onRatingClick($event, rate) {
        this.modelByKey = rate.key;
        this.selectedRatingValue = rate.index;
        // support if the caption is binded in the old projects for backward compatibility
        if (!this.showcaptions) {
            this.caption = rate.label;
        }
        this.invokeOnTouched();
        // invoke on datavalue change.
        this.invokeOnChange(this.datavalue, $event || {}, true);
    }
    // Update the selected flag on datasetItems and assign the ratingValue.
    /**
     * On datavalue change, update the caption, selectedRatingValue.
     * 1. if datasetItems contain the selected item (check the selected flag on item), find the index of selected item.
     * 2. if not, just check if the datavalue is provided as the index on the item.
     *
     * @param dataVal datavalue
     */
    onDatavalueChange(dataVal) {
        if (!isEmpty(this.datasetItems)) {
            let selectedItem = find(this.datasetItems, { 'selected': true });
            if (!selectedItem && !isUndefined(dataVal)) {
                selectedItem = find(this.datasetItems, function (item) {
                    return toString(item.index) === dataVal;
                });
                if (selectedItem) {
                    selectedItem.selected = true;
                }
            }
            if (!selectedItem) {
                // reset the  model if there is no item found.
                this.modelByKey = undefined;
                this.caption = '';
            }
            this.selectedRatingValue = selectedItem ? selectedItem.index : 0;
            if (selectedItem) {
                this.caption = selectedItem.label;
            }
        }
        else {
            this.selectedRatingValue = 0;
        }
        if (this.readonly) {
            // when dataset is not given but datavalue is provided which is integer
            if (!this.selectedRatingValue && !isNaN(dataVal)) {
                this.selectedRatingValue = (parseFloat(dataVal) <= this.maxvalue) ? dataVal : this.maxvalue;
            }
            this.ratingsWidth = this.calculateRatingsWidth(dataVal);
        }
    }
    calculateRatingsWidth(dataVal) {
        const selectedRating = parseFloat(this.selectedRatingValue), starWidth = 0.925, maxValue = parseInt(this.maxvalue || this.datasetItems.length, 10) || DEFAULT_RATING;
        setCSS(this.nativeElement.querySelector('.ratings-container'), 'width', (starWidth * maxValue) + 'em');
        dataVal = dataVal || this.datavalue;
        if (dataVal === undefined || dataVal === '' || dataVal === null) {
            this.caption = '';
            return 0;
        }
        if (selectedRating <= maxValue && selectedRating >= 0) {
            return selectedRating * starWidth + 'em';
        }
        if (selectedRating > maxValue) {
            return maxValue * starWidth + 'em';
        }
    }
    onPropertyChange(key, nv, ov) {
        if (key === 'readonly') {
            if (nv) {
                this.ratingsWidth = this.calculateRatingsWidth();
            }
        }
        else if (key === 'maxvalue') {
            /** Storing the datavalue in temp variable and assiging back to datavalue
             * after default datalist is prepared to trigger Dataset Aware class select cycle
             * as the datavalue property change is triggering first rather than maxvalue
             * in the rating widget is used inside a datatable
             */
            let tempDataValue = this.datavalue;
            this.prepareRatingDataset();
            // reset all the items.
            this.resetDatasetItems();
            if (!isUndefined(tempDataValue)) {
                this.datavalue = tempDataValue;
            }
        }
        else {
            super.onPropertyChange(key, nv, ov);
        }
    }
    /* Detect touch enabled devices & update hover styles*/
    onTouchStart($event) {
        this.touchEnabled = true;
        this.onTouchStart = noop;
    }
    onMouseleave() {
        this.caption = this.displayValue;
    }
    onMouseOver($event, rate) {
        this.caption = rate.label;
        !this.touchEnabled && !$event.target.classList.contains('rating-label-hover') && $event.target.classList.add('rating-label-hover');
    }
    static { this.ɵfac = function RatingComponent_Factory(t) { return new (t || RatingComponent)(i0.ɵɵdirectiveInject(i0.Injector), i0.ɵɵdirectiveInject('EXPLICIT_CONTEXT', 8)); }; }
    static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RatingComponent, selectors: [["", "wmRating", ""]], viewQuery: function RatingComponent_Query(rf, ctx) { if (rf & 1) {
            i0.ɵɵviewQuery(_c0, 5, ElementRef);
        } if (rf & 2) {
            let _t;
            i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.ratingEl = _t.first);
        } }, features: [i0.ɵɵProvidersFeature([
                provideAs(RatingComponent, NG_VALUE_ACCESSOR, true),
                provideAsWidgetRef(RatingComponent)
            ]), i0.ɵɵInheritDefinitionFeature], attrs: _c1, decls: 5, vars: 14, consts: [["ratingInput", ""], ["aria-multiselectable", "true", "ngDefaultControl", "", 1, "ratings-wrapper", 3, "focus", "ngModelChange", "ngModel", "ngModelOptions"], ["class", "rating-style", 4, "ngIf"], [1, "ratings-container", "disabled", 3, "ngStyle"], [1, "ratings", "active", 3, "ngStyle"], ["class", "caption", 3, "textContent", 4, "ngIf"], [1, "rating-style"], [3, "ngClass", "ngStyle", "title", "for", "touchstart", "mouseleave", "mouseover", 4, "ngFor", "ngForOf"], [3, "touchstart", "mouseleave", "mouseover", "ngClass", "ngStyle", "title", "for"], ["type", "radio", "role", "radio", "name", "ratings-id", 3, "click", "id", "value"], [1, "caption", 3, "textContent"]], template: function RatingComponent_Template(rf, ctx) { if (rf & 1) {
            i0.ɵɵelementStart(0, "div", 1);
            i0.ɵɵlistener("focus", function RatingComponent_Template_div_focus_0_listener($event) { return ctx.onFocus($event); });
            i0.ɵɵtwoWayListener("ngModelChange", function RatingComponent_Template_div_ngModelChange_0_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.modelByKey, $event) || (ctx.modelByKey = $event); return $event; });
            i0.ɵɵtemplate(1, RatingComponent_div_1_Template, 2, 1, "div", 2);
            i0.ɵɵelementStart(2, "div", 3);
            i0.ɵɵelement(3, "div", 4);
            i0.ɵɵelementEnd();
            i0.ɵɵtemplate(4, RatingComponent_label_4_Template, 1, 1, "label", 5);
            i0.ɵɵelementEnd();
        } if (rf & 2) {
            i0.ɵɵtwoWayProperty("ngModel", ctx.modelByKey);
            i0.ɵɵproperty("ngModelOptions", i0.ɵɵpureFunction0(8, _c2));
            i0.ɵɵadvance();
            i0.ɵɵproperty("ngIf", !ctx.readonly);
            i0.ɵɵadvance();
            i0.ɵɵclassProp("hidden", !ctx.readonly);
            i0.ɵɵproperty("ngStyle", i0.ɵɵpureFunction1(9, _c3, ctx.iconsize));
            i0.ɵɵadvance();
            i0.ɵɵproperty("ngStyle", i0.ɵɵpureFunction2(11, _c4, ctx.ratingsWidth, ctx.iconcolor));
            i0.ɵɵadvance();
            i0.ɵɵproperty("ngIf", ctx.showcaptions);
        } }, dependencies: [i1.NgClass, i1.NgForOf, i1.NgIf, i1.NgStyle, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, i3.TextContentDirective], encapsulation: 2 }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RatingComponent, [{
        type: Component,
        args: [{ selector: '[wmRating]', providers: [
                    provideAs(RatingComponent, NG_VALUE_ACCESSOR, true),
                    provideAsWidgetRef(RatingComponent)
                ], template: "<div (focus)=\"onFocus($event)\" [(ngModel)]=\"modelByKey\" [ngModelOptions]=\"{standalone: true}\" aria-multiselectable=\"true\"\n     class=\"ratings-wrapper\" ngDefaultControl>\n    <div *ngIf=\"!readonly\" class=\"rating-style\">\n        <label *ngFor=\"let rate of ratingItems;\"\n               [ngClass]=\"{active : rate.index <= selectedRatingValue}\"\n               [ngStyle]=\"{'font-size' :iconsize, color: rate.index <= selectedRatingValue && iconcolor}\"\n               [title]=\"rate.label || rate.index\"\n               (touchstart)=\"onTouchStart($event)\"\n               (mouseleave)=\"onMouseleave($event, rate)\"\n               (mouseover)=\"onMouseOver($event, rate)\"\n               [for]=\"_id + '-' + rate.index\">\n            <input #ratingInput type=\"radio\" role=\"radio\" [attr.aria-label]=\"(rate.label || rate.index) + ' out of ' + ratingItems.length + ' ratings'\" [attr.aria-checked]=\"rate.label === datavalue ? 'true' : 'false'\"\n                   [id]=\"_id + '-' + rate.index\" (click)=\"onRatingClick($event, rate)\" name=\"ratings-id\" [value]=\"rate.key || rate.index\"/>\n        </label>\n    </div>\n    <div [class.hidden]=\"!readonly\" [ngStyle]=\"{'font-size' :iconsize}\" class=\"ratings-container disabled\" >\n        <div class=\"ratings active\" [ngStyle]=\"{width: ratingsWidth, color: iconcolor}\"></div>\n    </div>\n    <label *ngIf=\"showcaptions\" class=\"caption\" [textContent]=\"caption\"></label>\n</div>\n" }]
    }], () => [{ type: i0.Injector }, { type: undefined, decorators: [{
                type: Inject,
                args: ['EXPLICIT_CONTEXT']
            }, {
                type: Optional
            }] }], { ratingEl: [{
            type: ViewChild,
            args: ['ratingInput', /* TODO: add static flag */ { read: ElementRef }]
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RatingComponent, { className: "RatingComponent", filePath: "rating.component.ts", lineNumber: 25 }); })();

const components = [
    RatingComponent
];
class RatingModule {
    static { this.ɵfac = function RatingModule_Factory(t) { return new (t || RatingModule)(); }; }
    static { this.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: RatingModule }); }
    static { this.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule,
            FormsModule,
            WmComponentsModule] }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RatingModule, [{
        type: NgModule,
        args: [{
                imports: [
                    CommonModule,
                    FormsModule,
                    WmComponentsModule
                ],
                declarations: [...components],
                exports: [...components]
            }]
    }], null, null); })();
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(RatingModule, { declarations: [RatingComponent], imports: [CommonModule,
        FormsModule,
        WmComponentsModule], exports: [RatingComponent] }); })();

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

export { RatingComponent, RatingModule };
//# sourceMappingURL=index.mjs.map
