@spark-ui/components
Version:
Spark (Leboncoin design system) components.
247 lines (241 loc) • 6.89 kB
JavaScript
import {
Icon
} from "../chunk-AESXFMCC.mjs";
import "../chunk-NBZKMCHF.mjs";
import "../chunk-4F5DOL57.mjs";
// src/rating/Rating.tsx
import { useCombinedState } from "@spark-ui/hooks/use-combined-state";
import { cx as cx3 } from "class-variance-authority";
import {
useCallback,
useRef
} from "react";
// src/rating/RatingStar.tsx
import { StarFill } from "@spark-ui/icons/StarFill";
import { StarOutline } from "@spark-ui/icons/StarOutline";
import { cx as cx2 } from "class-variance-authority";
// src/rating/RatingStar.styles.ts
import { cva, cx } from "class-variance-authority";
var emptyRemainingStarsOnHoverClass = cx("[&_>_div]:peer-hover:w-0!");
var ratingStarStyles = cva(
["peer", "after:inset-0", "group", "relative", "after:block after:absolute"],
{
variants: {
disabled: {
true: "opacity-dim-3",
false: ""
},
readOnly: {
true: "",
false: ""
},
gap: {
sm: ["after:w-[calc(100%+(var(--spacing-sm)))]", "last-of-type:after:content-none"],
md: ["after:w-[calc(100%+(var(--spacing-md)))]", "last-of-type:after:content-none"]
}
},
compoundVariants: [
{
readOnly: false,
disabled: false,
className: cx(emptyRemainingStarsOnHoverClass, "cursor-pointer")
}
],
defaultVariants: {
disabled: false,
readOnly: false,
gap: "sm"
}
}
);
var ratingStarIconStyles = cva("", {
variants: {
size: {
sm: "text-caption-link",
md: "text-body-1",
lg: "text-display-1"
},
design: {
filled: [
"text-main-variant",
"group-[[data-part=star][data-hovered]]:text-main-variant-hovered"
],
outlined: ["text-on-surface/dim-3"]
}
}
});
// src/rating/RatingStar.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var RatingStar = ({
value,
size,
disabled,
readOnly,
onClick,
onMouseEnter,
ref: forwardedRef
}) => {
return /* @__PURE__ */ jsxs(
"div",
{
ref: forwardedRef,
onMouseEnter,
className: ratingStarStyles({
gap: size === "lg" ? "md" : "sm",
disabled,
readOnly
}),
"data-part": "star",
onClick,
children: [
/* @__PURE__ */ jsx(
"div",
{
className: cx2(
"z-raised absolute overflow-hidden",
"group-[[data-part=star][data-hovered]]:overflow-visible"
),
style: { width: value * 100 + "%" },
children: /* @__PURE__ */ jsx(
Icon,
{
className: ratingStarIconStyles({
size,
design: "filled"
}),
children: /* @__PURE__ */ jsx(StarFill, {})
}
)
}
),
/* @__PURE__ */ jsx(Icon, { className: ratingStarIconStyles({ size, design: "outlined" }), children: /* @__PURE__ */ jsx(StarOutline, {}) })
]
}
);
};
// src/rating/utils.ts
function getNearestHalfDecimal(num) {
return Math.round(num / 0.5) * 0.5;
}
function getStarValue({ value, index }) {
if (value === void 0) return 0;
const starPosition = index + 1;
const formattedValue = getNearestHalfDecimal(value);
if (Math.ceil(formattedValue) < starPosition) return 0;
return formattedValue >= starPosition ? 1 : 0.5;
}
function splitAt(arr, index) {
const prev = arr.slice(0, index);
const next = arr.slice(index);
return [prev, next];
}
// src/rating/Rating.tsx
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
var Rating = ({
defaultValue,
value: propValue,
onValueChange,
size = "md",
disabled,
readOnly,
name,
id,
"aria-label": ariaLabel,
ref,
...rest
}) => {
const inputRef = useRef(null);
const starRefList = useRef([]);
const [value, setRatingValue] = useCombinedState(propValue, defaultValue, onValueChange);
const valueRef = useRef(value);
const isInteractive = !(disabled || readOnly);
function onStarClick(index) {
if (!inputRef.current) return;
setRatingValue(index + 1);
valueRef.current = index + 1;
inputRef.current.focus();
inputRef.current.setAttribute("data-clicked", "");
}
function onInputChange(event) {
if (valueRef.current === Number(event.target.value) || Number(event.target.value) === 0) {
return;
}
valueRef.current = Number(event.target.value);
setRatingValue(Number(event.target.value));
}
function onStarMouseEnter({ currentTarget }) {
const currentStarIndex = starRefList.current.findIndex((star) => star === currentTarget);
const [previousStars, followingStars] = splitAt(starRefList.current, currentStarIndex + 1);
previousStars.forEach((star) => star.setAttribute("data-hovered", ""));
followingStars.forEach((star) => star.removeAttribute("data-hovered"));
}
const handleStarRef = useCallback((elm) => {
if (!elm) return;
starRefList.current.push(elm);
}, []);
function resetDataPartInputAttr() {
inputRef.current?.removeAttribute("data-clicked");
}
function resetDataPartStarAttr() {
starRefList.current.forEach((star) => star.removeAttribute("data-hovered"));
}
return /* @__PURE__ */ jsxs2(
"div",
{
className: "relative inline-flex",
ref,
"data-spark-component": "rating",
...rest,
onMouseLeave: resetDataPartStarAttr,
children: [
/* @__PURE__ */ jsx2(
"input",
{
name,
id,
"aria-label": ariaLabel,
ref: inputRef,
"data-part": "input",
className: "peer absolute inset-0 opacity-0",
type: "range",
min: "0",
max: "5",
step: readOnly ? 0.5 : 1,
disabled,
readOnly,
value: getNearestHalfDecimal(value ?? 0),
onChange: (event) => isInteractive && onInputChange(event),
onBlur: resetDataPartInputAttr
}
),
/* @__PURE__ */ jsx2(
"div",
{
className: cx3(
size === "lg" ? "gap-x-md" : "gap-x-sm",
"flex",
"peer-focus-visible:u-outline peer-[[data-part=input][data-clicked]]:shadow-none"
),
children: Array.from({ length: 5 }).map((_, index) => /* @__PURE__ */ jsx2(
RatingStar,
{
disabled,
readOnly,
size,
onClick: () => isInteractive && onStarClick(index),
onMouseEnter: (event) => isInteractive && onStarMouseEnter(event),
ref: handleStarRef,
value: getStarValue({ index, value })
},
index
))
}
)
]
}
);
};
export {
Rating
};
//# sourceMappingURL=index.mjs.map