UNPKG

@spark-ui/components

Version:

Spark (Leboncoin design system) components.

247 lines (241 loc) 6.89 kB
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