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
JavaScript
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 = '★'; // 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