129 lines
2.9 KiB
JavaScript
129 lines
2.9 KiB
JavaScript
/**
|
|
* Book Review Item Component
|
|
* Individual review with rating, author, date, and text
|
|
*/
|
|
class BookReviewItem extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({ mode: "open" });
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
}
|
|
|
|
get rating() {
|
|
return parseInt(this.getAttribute("rating") || "5", 10);
|
|
}
|
|
|
|
get author() {
|
|
return this.getAttribute("author") || "Anoniem";
|
|
}
|
|
|
|
get date() {
|
|
return this.getAttribute("date") || "";
|
|
}
|
|
|
|
renderStars() {
|
|
const rating = Math.min(5, Math.max(0, this.rating));
|
|
const fullStars = rating;
|
|
const emptyStars = 5 - rating;
|
|
|
|
let stars = "";
|
|
for (let i = 0; i < fullStars; i++) {
|
|
stars += `<span class="star filled">★</span>`;
|
|
}
|
|
for (let i = 0; i < emptyStars; i++) {
|
|
stars += `<span class="star empty">☆</span>`;
|
|
}
|
|
return stars;
|
|
}
|
|
|
|
render() {
|
|
this.shadowRoot.innerHTML = `
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
}
|
|
|
|
.review {
|
|
padding-bottom: var(--spacing-lg, 1.5rem);
|
|
border-bottom: 1px solid var(--color-border, #e2e8f0);
|
|
}
|
|
|
|
:host(:last-child) .review {
|
|
border-bottom: none;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.review-header {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-xs, 0.25rem);
|
|
margin-bottom: var(--spacing-sm, 0.5rem);
|
|
}
|
|
|
|
.stars {
|
|
display: flex;
|
|
gap: 2px;
|
|
}
|
|
|
|
.star {
|
|
font-size: 16px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.star.filled {
|
|
color: var(--color-purple, #951d51);
|
|
}
|
|
|
|
.star.empty {
|
|
color: var(--color-border, #e2e8f0);
|
|
}
|
|
|
|
.review-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm, 0.5rem);
|
|
font-family: var(--font-family-outfit, "Outfit", sans-serif);
|
|
font-size: 14px;
|
|
line-height: 20px;
|
|
}
|
|
|
|
.author {
|
|
font-weight: 500;
|
|
color: var(--color-text, #1e293b);
|
|
}
|
|
|
|
.date {
|
|
color: var(--color-text-light, #64748b);
|
|
font-weight: 300;
|
|
}
|
|
|
|
.review-text {
|
|
font-family: var(--font-family-outfit, "Outfit", sans-serif);
|
|
font-size: 16px;
|
|
font-weight: 400;
|
|
line-height: 26px;
|
|
color: var(--color-text, #1e293b);
|
|
margin: 0;
|
|
}
|
|
</style>
|
|
<article class="review">
|
|
<div class="review-header">
|
|
<div class="stars">${this.renderStars()}</div>
|
|
<div class="review-meta">
|
|
<span class="author">${this.author}</span>
|
|
${this.date ? `<span class="date">• ${this.date}</span>` : ""}
|
|
</div>
|
|
</div>
|
|
<p class="review-text">
|
|
<slot></slot>
|
|
</p>
|
|
</article>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define("book-review-item", BookReviewItem);
|