<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>
<basic-button disabled>
<button type="submit" class="primary" disabled>Submit</button>
</basic-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,
component,
on,
setProperty,
show,
state,
} from '../../../'
import type { BasicButtonProps } from '../basic-button/basic-button'
import type { RatingStarsProps } from '../rating-stars/rating-stars'
export default component('rating-feedback', {}, (el, { all, first }) => {
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')
return [
// Event listeners for rating changes and form submission
on('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 => {
empty.set(
(e.target as HTMLTextAreaElement)?.value.trim() === '',
)
}),
),
// Effects on rating changes
first(
'.feedback',
show(() => !submitted.get() && !!rating.get()),
),
all(
'.feedback p',
show(
target =>
rating.get() === parseInt(target.dataset['key'] || '0'),
),
),
// Effect on empty state
first<Component<BasicButtonProps>>(
'basic-button',
setProperty('disabled', empty),
),
]
})