ratti
Version:
React Star Rating - Zero dependency, a highly-configurable, accessible rating component for React applications with TypeScript support.
246 lines (245 loc) • 7.55 kB
JavaScript
import { jsxs as rt, jsx as c } from "react/jsx-runtime";
import { forwardRef as at, useState as C, useRef as W, useId as st, useImperativeHandle as ct, useEffect as lt } from "react";
const it = "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z", q = ({
fillPercentage: r = 1,
starId: w,
fillColor: i,
forcedColor: f,
svgPathD: u = it,
size: l
}) => {
const h = `star-clip-${w}`, d = r < 1 && !f, a = d ? `${Math.max(0, Math.min(100, r * 100))}%` : "100%", p = f || i || "currentColor", $ = l ? typeof l == "number" ? l : parseFloat(l) : 24;
return /* @__PURE__ */ rt(
"svg",
{
"aria-hidden": "true",
xmlns: "http://www.w3.org/2000/svg",
width: $,
height: $,
viewBox: "0 0 24 24",
fill: "none",
stroke: f || "currentColor",
strokeWidth: "1.5",
strokeLinecap: "round",
strokeLinejoin: "round",
className: "star-icon",
style: { color: p },
children: [
d && /* @__PURE__ */ c("defs", { children: /* @__PURE__ */ c("clipPath", { id: h, children: /* @__PURE__ */ c("rect", { x: "0", y: "0", width: a, height: "100%" }) }) }),
/* @__PURE__ */ c("path", { d: u }),
/* @__PURE__ */ c("g", { clipPath: d ? `url(#${h})` : void 0, children: /* @__PURE__ */ c("path", { d: u, fill: p, stroke: "none" }) })
]
}
);
}, ut = [
"#d32f2f",
"#ed6c02",
"#ff9800",
"#4caf50",
"#00a63d"
];
function R(r, w) {
const i = 1 / w;
return Math.round(r * i) / i;
}
function X(r, w, i, f) {
if (!i || r <= 0) return;
const u = f || ut, l = u.length;
if (l === 0) return;
const h = w / l, d = Math.min(
Math.floor((r - 1e-3) / h),
l - 1
), a = Math.max(0, d);
return u[a >= 0 ? a : 0];
}
const vt = at(
({
maxRating: r = 5,
defaultValue: w = 0,
value: i,
onChange: f,
onHoverChange: u,
onBlur: l,
disabled: h = !1,
readOnly: d = !1,
precision: a = 1,
activeColorsEnabled: p = !1,
customActiveColors: $,
variant: S = "default",
svgPathD: T,
className: J = "",
size: y
}, O) => {
const V = i !== void 0, [Q, Y] = C(w), [_, v] = C(0), [b, k] = C(
null
), [N, P] = C(!1), L = W(!1), e = !h && !d, Z = st(), B = W(null);
ct(O, () => B.current);
const m = V ? i : Q;
lt(() => {
const n = () => P(!0), t = () => P(!1);
return e && (document.addEventListener("keydown", n), document.addEventListener("mousedown", t)), () => {
document.removeEventListener("keydown", n), document.removeEventListener("mousedown", t);
};
}, [e]);
function A(n, t) {
if (!e) return m;
const { left: o, width: s } = n.currentTarget.getBoundingClientRect(), D = (n.clientX - o) / s, K = Math.ceil(D / a) * a, M = t + Math.min(K, 1);
return Math.min(Math.max(M, 0), r);
}
function E(n) {
V || Y(n), f?.(n);
}
function U(n, t) {
if (!e) return;
const o = R(A(n, t), a);
o === m ? (v(0), k(0), E(0), L.current = !0) : (E(o), v(0), k(o), L.current = !1);
}
function j(n, t) {
if (!e || L.current) return;
const o = A(n, t);
v(o), u?.(o);
}
function G(n, t) {
if (!e || L.current) return;
const o = A(n, t);
v(o), u?.(o);
}
function z() {
e && (v(0), u?.(0), L.current = !1);
}
function tt() {
e && N && b === null && k(m);
}
function et(n) {
e && (k(null), v(0), l?.(n));
}
function nt(n) {
if (!e) return;
let t;
const o = b ?? m;
switch (n.key) {
case "ArrowRight":
case "ArrowUp":
t = Math.min(r, o + a);
break;
case "ArrowLeft":
case "ArrowDown":
t = Math.max(0, o > 0 ? o - a : 0);
break;
case "Home":
t = 0;
break;
case "End":
t = r;
break;
case "Enter":
case "Space": {
n.preventDefault();
const s = R(o, a);
s !== m && E(s), k(s), v(0);
return;
}
default:
return;
}
if (t !== void 0) {
n.preventDefault();
const s = R(t, a);
k(s), v(s);
}
}
const F = _ > 0 ? _ : b !== null ? b : m, x = y ? typeof y == "number" ? `${y}px` : y : void 0;
return /* @__PURE__ */ c(
"div",
{
ref: B,
className: `star-rating variant-${S} ${h ? "disabled" : ""} ${d ? "readonly" : ""} ${J}`,
role: "slider",
"aria-orientation": "horizontal",
"aria-valuemin": 0,
"aria-valuemax": r,
"aria-valuenow": e ? m : void 0,
"aria-valuetext": `${m.toFixed(a === 1 ? 0 : 1)} out of ${r} stars`,
"aria-label": "StarRating",
"aria-disabled": h,
"aria-readonly": d,
onMouseLeave: e ? z : void 0,
onFocus: e ? tt : void 0,
onBlur: e ? et : void 0,
onKeyDown: e ? nt : void 0,
tabIndex: e ? 0 : -1,
style: {
...x ? { "--star-size": x } : {}
},
children: [...Array(r)].map((n, t) => {
const o = t + 1, s = `${Z}-${t}`, D = F >= o, K = F - t;
let M = 0;
D ? M = 1 : F > t && (M = R(K, a), M = Math.max(0, Math.min(1, M)));
const H = `star-item ${b !== null && t === Math.max(0, Math.ceil(b) - 1) && N ? "keyboard-focused" : ""}`, ot = p ? X(
F,
r,
p,
$
) : void 0;
if (S === "circle" || S === "square") {
const g = p ? X(
F,
r,
p,
$
) : "var(--star-bg-selected)";
return /* @__PURE__ */ c(
"div",
{
role: "presentation",
className: `${H} star-background star-background--${S}`,
style: {
"--star-fill-percentage": M,
"--star-active-bg-color": g
},
onClick: e ? (I) => U(I, t) : void 0,
onMouseEnter: e ? (I) => j(I, t) : void 0,
onMouseMove: e ? (I) => G(I, t) : void 0,
children: /* @__PURE__ */ c(
q,
{
starId: s,
size: y,
fillPercentage: 1,
forcedColor: "var(--star-icon-on-bg)",
svgPathD: T
}
)
},
s
);
}
return /* @__PURE__ */ c(
"div",
{
role: "presentation",
className: `${H} star-button`,
onClick: e ? (g) => U(g, t) : void 0,
onMouseEnter: e ? (g) => j(g, t) : void 0,
onMouseMove: e ? (g) => G(g, t) : void 0,
children: /* @__PURE__ */ c(
q,
{
fillPercentage: M,
starId: s,
size: y,
fillColor: ot,
svgPathD: T
}
)
},
s
);
})
}
);
}
);
export {
vt as RateStar
};