feature/book-page (#4)
Co-authored-by: Tim Rijkse <trijkse@gmail.com> Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
128
js/components/book-review-item.js
Normal file
128
js/components/book-review-item.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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);
|
||||
Reference in New Issue
Block a user