UNPKG

angular-star

Version:

A lightweight and customizable **star rating** component for Angular 20+. Supports decimal ratings, hover interactions, read-only mode, and full accessibility.

115 lines (109 loc) 7.06 kB
import * as i0 from '@angular/core'; import { input, output, computed, signal, linkedSignal, Component } from '@angular/core'; class DefaultOptions { length = 5; value = 0; color = ''; badColor = '#f20808'; avgColor = '#f39c12'; goodColor = '#3df400'; spaceBetween = 0; // Optional, for spacing between stars icon = '&#9733;'; // Optional, for custom star icon constructor(data) { if (!data) return; Object.keys(data).forEach((key) => { typeof (data[key] !== undefined) && typeof (data[key] !== null) && (this[key] = data[key]); }); } } class AngularStar { config = input.required(); getValue = output(); defaultConfig = computed(() => { return { ...new DefaultOptions(), ...this.config() }; }); fractions = signal(false); rating = linkedSignal(() => { const _config = this.defaultConfig(); return _config.value || 0; }); hoverFraction = signal(0); setValue = linkedSignal(() => { const _config = this.defaultConfig(); return _config.value || 0; }); constructor() { } onMouseMove(event, index) { const _fraction = this.fractions(); if (_fraction) { const target = event.target; const rect = target.getBoundingClientRect(); const offsetX = event.clientX - rect.left; const fraction = offsetX / rect.width; this.hoverFraction.set(Math.round(fraction * 10) / 10); this.rating.set(index + this.hoverFraction()); } else { this.rating.set(index + 1); // Set to the next whole number } } resetHover() { const _value = this.setValue(); this.rating.set(_value); this.hoverFraction.set(0); } rate(value) { const _fraction = this.fractions(); const _value = _fraction ? Math.round(value * 10) / 10 : value + 1; this.setValue.set(_value); this.getValue.emit(_value); this.hoverFraction.set(0); } getFillPercentage(index) { const _fraction = this.fractions(); if (_fraction) { const diff = this.rating() - index; if (diff >= 1) return '100%'; if (diff > 0) return `${diff * 100}%`; return '0%'; } else { return index < Math.ceil(this.rating()) ? '100%' : '0%'; } } get classname() { const _rating = this.rating(); const _config = this.defaultConfig(); const _fraction = this.fractions(); const _value = _fraction ? _config.length / 3 : _config.length / 3 % 1 >= 0.5 ? Math.ceil(_config.length / 3) : Math.floor(_config.length / 3); if (_rating <= _value) { return 'bad-color'; } else if (_rating <= _config.length - _value) { return 'avg-color'; } else { return 'good-color'; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AngularStar, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.3", type: AngularStar, isStandalone: true, selector: "angular-star", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { getValue: "getValue" }, ngImport: i0, template: "@let _config = defaultConfig();\n@let _hoverFraction = hoverFraction();\n\n<div class=\"angular-stars\" (mouseleave)=\"resetHover()\" [style.--bad-color]=\"_config.color || _config.badColor\"\n [style.--avg-color]=\"_config.color || _config.avgColor\" [style.--good-color]=\"_config.color || _config.goodColor\"\n [style.--space-bw]=\"_config.spaceBetween+'px'\">\n @for (star of [].constructor(_config.length); track $index) {\n <div class=\"star-wrapper\" (mousemove)=\"onMouseMove($event, $index)\" (click)=\"rate($index + _hoverFraction)\"\n [style.--fill]=\"getFillPercentage($index)\">\n <div class=\"star full {{classname}}\" [innerHTML]=\"_config.icon\"></div>\n <div class=\"star empty\" [innerHTML]=\"_config.icon\"></div>\n </div>\n }\n</div>", styles: [".angular-stars{--bad-color: #f20808;--avg-color: #f39c12;--good-color: #3df400;--space-bw: 0;--empty-color: #ccc;display:flex;font-size:32px;justify-content:center;gap:var(--space-bw)}.angular-stars .star-wrapper{position:relative;width:32px;height:32px;cursor:pointer}.angular-stars .star-wrapper .star{position:absolute;top:0;left:0;color:var(--empty-color);width:100%;height:100%}.angular-stars .star-wrapper .full{overflow:hidden;width:var(--fill, 0%);z-index:2}.angular-stars .star-wrapper .full.bad-color{color:var(--bad-color)}.angular-stars .star-wrapper .full.avg-color{color:var(--avg-color)}.angular-stars .star-wrapper .full.good-color{color:var(--good-color)}.angular-stars .star-wrapper .empty{z-index:1}\n"] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.3", ngImport: i0, type: AngularStar, decorators: [{ type: Component, args: [{ selector: 'angular-star', imports: [], template: "@let _config = defaultConfig();\n@let _hoverFraction = hoverFraction();\n\n<div class=\"angular-stars\" (mouseleave)=\"resetHover()\" [style.--bad-color]=\"_config.color || _config.badColor\"\n [style.--avg-color]=\"_config.color || _config.avgColor\" [style.--good-color]=\"_config.color || _config.goodColor\"\n [style.--space-bw]=\"_config.spaceBetween+'px'\">\n @for (star of [].constructor(_config.length); track $index) {\n <div class=\"star-wrapper\" (mousemove)=\"onMouseMove($event, $index)\" (click)=\"rate($index + _hoverFraction)\"\n [style.--fill]=\"getFillPercentage($index)\">\n <div class=\"star full {{classname}}\" [innerHTML]=\"_config.icon\"></div>\n <div class=\"star empty\" [innerHTML]=\"_config.icon\"></div>\n </div>\n }\n</div>", styles: [".angular-stars{--bad-color: #f20808;--avg-color: #f39c12;--good-color: #3df400;--space-bw: 0;--empty-color: #ccc;display:flex;font-size:32px;justify-content:center;gap:var(--space-bw)}.angular-stars .star-wrapper{position:relative;width:32px;height:32px;cursor:pointer}.angular-stars .star-wrapper .star{position:absolute;top:0;left:0;color:var(--empty-color);width:100%;height:100%}.angular-stars .star-wrapper .full{overflow:hidden;width:var(--fill, 0%);z-index:2}.angular-stars .star-wrapper .full.bad-color{color:var(--bad-color)}.angular-stars .star-wrapper .full.avg-color{color:var(--avg-color)}.angular-stars .star-wrapper .full.good-color{color:var(--good-color)}.angular-stars .star-wrapper .empty{z-index:1}\n"] }] }], ctorParameters: () => [] }); /* * Public API Surface of angular-star */ /** * Generated bundle index. Do not edit. */ export { AngularStar }; //# sourceMappingURL=angular-star.mjs.map