<rating-feedback>
<form>
<rating-stars>
<fieldset>
<legend class="visually-hidden">Rate</legend>
<label data-key="1">
<input type="radio" class="visually-hidden" name="rating" value="1">
<span class="label">☆</span>
</label>
<label data-key="2">
<input type="radio" class="visually-hidden" name="rating" value="2">
<span class="label">☆</span>
</label>
<label data-key="3">
<input type="radio" class="visually-hidden" name="rating" value="3">
<span class="label">☆</span>
</label>
<label data-key="4">
<input type="radio" class="visually-hidden" name="rating" value="4">
<span class="label">☆</span>
</label>
<label data-key="5">
<input type="radio" class="visually-hidden" name="rating" value="5">
<span class="label">☆</span>
</label>
</fieldset>
</rating-stars>
<div class="feedback" hidden>
<header>
<button button="button" class="hide" aria-label="Hide">×</button>
<p data-key="1" hidden>We're sorry to hear that! Your feedback is important, and we'd love to improve. Let us know how we can do better.</p>
<p data-key="2" hidden>Thank you for your honesty. We appreciate your feedback and will work on making things better.</p>
<p data-key="3" hidden>Thanks for your rating! If there's anything we can improve, we'd love to hear your thoughts.</p>
<p data-key="4" hidden>We're glad you had a good experience! If there's anything that could make it even better, let us know.</p>
<p data-key="5" hidden>Thank you for your support! We're thrilled you had a great experience. Your feedback keeps us motivated!</p>
</header>
<fieldset>
<label for="rating-feedback">Describe your experience (optional)</label>
<textarea id="rating-feedback"></textarea>
<input-button disabled>
<button type="submit" class="primary" disabled>Submit</button>
</input-button>
</fieldset>
</div>
</form>
</rating-feedback>
rating-feedback {
position: relative;
display: inline-flex;
flex-direction: column;
gap: var(--space-m);
width: 80%;
text-align: left;
header {
display: flex;
flex-direction: row-reverse;
gap: var(--space-s);
margin-block: var(--space-l);
}
.hide {
position: relative;
display: block;
box-sizing: border-box;
top: 0;
right: 0;
border: 0;
border-radius: var(--space-xs);
color: var(--color-text);
background: transparent;
height: var(--input-height);
font-size: var(--font-size-l);
&:hover,
&:focus {
background: rgba(0, 0, 0, 0.05);
}
&:active {
background: rgba(0, 0, 0, 0.1);
}
}
p {
margin: 0;
}
form {
display: flex;
flex-direction: column;
}
fieldset {
border: 0;
padding: 0;
margin: 0;
}
.feedback label {
display: block;
margin-bottom: var(--space-xxs);
}
textarea {
resize: vertical;
width: 100%;
display: inline-block;
box-sizing: border-box;
background: var(--color-input);
color: var(--color-text);
border: 1px solid var(--color-border);
padding: var(--space-xs) var(--space-xxs);
font-size: var(--font-size-m);
line-height: var(--line-height-m);
margin-bottom: var(--space-xs);
}
}
import {
type Component,
type ComponentProps,
all,
component,
first,
on,
setProperty,
state,
} from "../../../";
import type { InputButtonProps } from "../input-button/input-button";
import type { RatingStarsProps } from "../rating-stars/rating-stars";
export default component("rating-feedback", {}, (el) => {
const rating = state(0);
const empty = state(true);
const submitted = state(false);
const stars = el.querySelector<Component<RatingStarsProps>>("rating-stars");
if (!stars) throw new Error("No rating-stars component found");
const hasDifferentKey = (element: HTMLElement): boolean =>
rating.get() !== parseInt(element.dataset["key"] || "0");
return [
// Event listeners for rating changes and form submission
on<CustomEvent<number>>("change-rating", (e) => {
rating.set(e.detail);
}),
on("submit", (e) => {
e.preventDefault();
submitted.set(true);
console.log("Feedback submitted");
}),
// Event listener for hide button
first(
".hide",
on("click", () => {
const feedback = el.querySelector<HTMLElement>(".feedback");
if (feedback) feedback.hidden = true;
}),
),
// Event listener for textarea
first(
"textarea",
on("input", (e: Event) => {
empty.set(
(e.target as HTMLTextAreaElement)?.value.trim() === "",
);
}),
),
// Effects on rating changes
first<ComponentProps, HTMLElement>(
".feedback",
setProperty("hidden", () => submitted.get() || !rating.get()),
),
all<ComponentProps, HTMLElement>(
".feedback p",
setProperty("hidden", hasDifferentKey),
),
// Effect on empty state
first<ComponentProps, Component<InputButtonProps>>(
"input-button",
setProperty("disabled", empty),
),
];
});