Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: RatingGroup component #516

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
62 changes: 62 additions & 0 deletions apps/docs/src/examples/rating-group.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.rating-group {
display: flex;
flex-direction: column;
gap: 8px;
}

.rating-group__label {
color: hsl(240 6% 10%);
font-size: 14px;
font-weight: 500;
user-select: none;
}

.rating-group__description {
color: hsl(240 5% 26%);
font-size: 12px;
user-select: none;
}

.rating-group__error-message {
color: hsl(0 72% 51%);
font-size: 12px;
user-select: none;
}

.rating-group__control {
display: flex;
gap: 4px;
}

.rating-group-item {
cursor: pointer;
fill: hsl(240 6% 90%);
}

.rating-group-item:focus {
outline: none;
}

[data-kb-theme="dark"] .rating-group-item {
fill: hsl(240 5% 26%);
}

.rating-group-item[data-highlighted] {
fill: hsl(200 98% 39%);
}

.half-star-icon > path + path {
fill: hsl(240 6% 90%);
}

[data-kb-theme="dark"] .half-star-icon > path + path {
fill: hsl(240 5% 26%);
}

[data-kb-theme="dark"] .rating-group__label {
color: hsl(240 5% 84%);
}

[data-kb-theme="dark"] .rating-group__description {
color: hsl(240 5% 65%);
}
235 changes: 235 additions & 0 deletions apps/docs/src/examples/rating-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { Index, createSignal } from "solid-js";
HBS999 marked this conversation as resolved.
Show resolved Hide resolved
import { RatingGroup } from "../../../../packages/core/src/rating-group";

import style from "./rating-group.module.css";

export function BasicExample() {
return (
<RatingGroup class={style["rating-group"]}>
<RatingGroup.Label class={style["rating-group__label"]}>
Rate Us:
</RatingGroup.Label>
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
<StarIcon />
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
</RatingGroup>
);
}

export function DefaultValueExample() {
return (
<RatingGroup class={style["rating-group"]} defaultValue={3}>
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
<StarIcon />
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
</RatingGroup>
);
}

export function ControlledExample() {
const [value, setValue] = createSignal(0);

return (
<>
<RatingGroup
class={style["rating-group"]}
value={value()}
onChange={setValue}
>
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
<StarIcon />
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
</RatingGroup>
<p class="not-prose text-sm mt-4">Your rating is: {value()}/5</p>
</>
);
}

export function HalfRatingsExample() {
return (
<RatingGroup class={style["rating-group"]} allowHalf>
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
{(state) => (state.half() ? <StarHalfIcon /> : <StarIcon />)}
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
</RatingGroup>
);
}

export function DescriptionExample() {
return (
<RatingGroup class={style["rating-group"]}>
<RatingGroup.Label class={style["rating-group__label"]}>
Rate Us:
</RatingGroup.Label>
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
<StarIcon />
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
<RatingGroup.Description class={style["rating-group__description"]}>
Rate your experience with us.
</RatingGroup.Description>
</RatingGroup>
);
}

export function ErrorMessageExample() {
const [value, setValue] = createSignal(0);

return (
<RatingGroup
class={style["rating-group"]}
value={value()}
onChange={setValue}
validationState={value() === 0 ? "invalid" : "valid"}
>
<RatingGroup.Label class={style["rating-group__label"]}>
Rate Us:
</RatingGroup.Label>
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
<StarIcon />
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
<RatingGroup.ErrorMessage class={style["rating-group__error-message"]}>
Please select a rating between 1 and 5.
</RatingGroup.ErrorMessage>
</RatingGroup>
);
}

export function HTMLFormExample() {
let formRef: HTMLFormElement | undefined;

const onSubmit = (e: SubmitEvent) => {
e.preventDefault();
e.stopPropagation();

const formData = new FormData(formRef);

alert(JSON.stringify(Object.fromEntries(formData), null, 2));
};

return (
<form
ref={formRef}
onSubmit={onSubmit}
class="flex flex-col items-center space-y-6"
>
<RatingGroup class={style["rating-group"]} name="rate">
<RatingGroup.Control class={style["rating-group__control"]}>
<Index each={Array(5)}>
{(_) => (
<RatingGroup.Item class={style["rating-group-item"]}>
<RatingGroup.ItemControl>
<StarIcon />
</RatingGroup.ItemControl>
</RatingGroup.Item>
)}
</Index>
</RatingGroup.Control>
<RatingGroup.HiddenInput />
</RatingGroup>
<div class="flex space-x-2">
<button type="reset" class="kb-button">
Reset
</button>
<button type="submit" class="kb-button-primary">
Submit
</button>
</div>
</form>
);
}

function StarIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 273 260"
fill="inherit"
stroke="inherit"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<title>Star Icon</title>
<path d="M136.5 0L177.83 86.614L272.977 99.1561L203.374 165.229L220.847 259.594L136.5 213.815L52.1528 259.594L69.6265 165.229L0.0233917 99.1561L95.1699 86.614L136.5 0Z" />
</svg>
);
}

function StarHalfIcon() {
return (
<svg
class={style["half-star-icon"]}
viewBox="0 0 273 260"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
stroke="inherit"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<title>Half Star Icon</title>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M135.977 214.086L52.1294 259.594L69.6031 165.229L0 99.1561L95.1465 86.614L135.977 1.04785V214.086Z"
fill="inherit"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M135.977 213.039L219.826 258.546L202.352 164.181L271.957 98.1082L176.808 85.5661L135.977 0V213.039Z"
/>
</svg>
);
}
5 changes: 5 additions & 0 deletions apps/docs/src/routes/docs/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ const CORE_NAV_SECTIONS: NavSection[] = [
title: "Radio Group",
href: "/docs/core/components/radio-group",
},
{
title: "Rating Group",
href: "/docs/core/components/rating-group",
status: "new",
},
{
title: "Search",
href: "/docs/core/components/search",
Expand Down
Loading