/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { ifDefined, html, classMap } from "../vendor/lit.all.mjs"; import { MozLitElement } from "../lit-utils.mjs"; window.MozXULElement?.insertFTLIfNeeded("toolkit/global/mozFiveStar.ftl"); /** * @typedef {HTMLSpanElement} MozFiveStarRatingStarElement */ /** * @typedef {{ * rating: number * fill: 'full' | 'half' | 'empty' * }} MozFiveStarRenderedStarState */ /** * The visual representation is five stars, each of them either empty, * half-filled or full. The fill state is derived from the rating, * rounded to the nearest half. * * @tagname moz-five-star * @property {number} rating - The rating out of 5. * @property {string} title - The title text. */ export default class MozFiveStar extends MozLitElement { static properties = { rating: { type: Number, reflect: true }, title: { type: String }, selectable: { type: Boolean }, }; constructor() { super(); /** * The initial rating that is also dynamically updated to the selected * rating if {@link selectable} is set to true. * * @type {number} */ this.rating = 0; /** * Whether the stars in the component are selectable. * * @type {boolean} */ this.selectable = false; } static get queries() { return { starEls: { all: ".rating-star" }, starsWrapperEl: ".stars", }; } /** * @returns {Array} */ getStars() { /** * @type {Array} */ let stars = []; let roundedRating = Math.round(this.rating * 2) / 2; for (let i = 1; i <= 5; i++) { if (i <= roundedRating) { stars.push({ rating: i, fill: "full" }); } else if (i - roundedRating === 0.5) { stars.push({ rating: i, fill: "half" }); } else { stars.push({ rating: i, fill: "empty" }); } } return stars; } /** * @param {MozFiveStarRatingStarElement} ratingStarElement * @returns */ getStarElementRating(ratingStarElement) { const stringRating = ratingStarElement.getAttribute("rating") || ""; return parseInt(stringRating, 10); } /** * @param {MouseEvent} e */ onClick(e) { if (!this.selectable) { return; } /** * @type {MozFiveStarRatingStarElement} */ const ratingStarElement = /**@type {object} */ (e.target); this.rating = this.getStarElementRating(ratingStarElement); this.dispatchEvent( new CustomEvent("select", { detail: { rating: this.rating, }, }) ); } render() { const { rating, selectable, title } = this; const starsTitle = title || selectable; return html` `; } } customElements.define("moz-five-star", MozFiveStar);