import { css, CSSResultArray, html, LitElement, TemplateResult } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; /** * @cssprop --filled-star-color - The color of the filled stars. * @cssprop --empty-star-color - The color of the empty stars. */ @customElement('z-reviews.stars') export class ReviewsStarsComponent extends LitElement { /// Private variables /// /// Protected variables /// @state() protected _filledStarCount = 0; /// Public variables /// @property({ type: Number }) public rating: number | undefined; @property({ type: Number, attribute: 'out-of' }) public outOf: number | undefined; @property({ type: Number }) public stars: number = 5; @property({ type: Boolean, attribute: 'allow-fractional' }) public allowFractional = false; /// constructor & lifecycle /// override connectedCallback(): void { super.connectedCallback(); this.validateProperties(); } override updated(changedProperties: Map): void { super.updated(changedProperties); this._filledStarCount = this.calculateFilledStarCount(); // this.requestUpdate(); } /// Public methods /// /// Protected methods /// protected override render(): TemplateResult { return html`
${this.renderStars()}
${this.renderStars()}
`; } protected renderStars(): TemplateResult { const stars: TemplateResult[] = []; for (let i = 0; i < this.stars; i++) { stars.push(this.renderStar()); } return html`${stars}`; } protected renderStar(): TemplateResult { return html` `; } /// Private methods /// /** * Validate the properties for the rules of availability. * * @throws {Error} If the properties are not valid. */ private validateProperties(): void { console.log(this.rating, this.stars, this.outOf, this.allowFractional); if (!this.rating) { throw new Error('Rating is required.'); } if (!this.outOf) { throw new Error('Out of is required when rating is used.'); } if (this.stars < 1) { throw new Error('Stars cannot be less than 1.'); } if (this.rating! > this.outOf) { throw new Error('Rating cannot be greater than out of.'); } } private calculateFilledStarCount(): number { if (this.allowFractional) { return (this.rating! / this.outOf!) * this.stars; } return Math.round((this.rating! / this.outOf!) * this.stars); } /// Statics /// static override styles: CSSResultArray = [ css` :host { display: flex; position: relative; } .icon { vertical-align: -0.125em; flex: 0 0 1em; height: 1em; width: 1em; fill: currentColor; } .stars { display: flex; color: var(--empty-star-color, #e4e5e9); } .stars--filled { position: absolute; top: 0; left: 0; overflow: hidden; opacity: 1; right: calc(100% - var(--_filled-star-width)); color: var(--filled-star-color, #f7b731); } `, ]; }