react-awesome-stars
Version:
A flexible and customizable star rating component for React applications
96 lines (85 loc) • 6.96 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = "/* Root container */\n.star-rating-root {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n/* Stars container */\n.star-rating-stars {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n/* Individual star */\n.star-rating-star {\n position: relative;\n display: inline-block;\n cursor: pointer;\n transition: transform 0.15s;\n vertical-align: middle;\n}\n\n.star-rating-star-hovered {\n transform: scale(1.1);\n z-index: 1;\n}\n\n.star-rating-star-disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Half star hit areas */\n.star-rating-half {\n position: absolute;\n top: 0;\n width: 50%;\n height: 100%;\n z-index: 2;\n cursor: pointer;\n}\n.star-rating-half.left {\n left: 0;\n}\n.star-rating-half.right {\n right: 0;\n}\n\n/* SVGs */\n.star-rating-svg {\n display: block;\n pointer-events: none;\n}\n.star-rating-empty {\n position: absolute;\n top: 0;\n left: 0;\n}\n.star-rating-filled {\n position: relative;\n z-index: 1;\n}\n\n/* Filled star overlay */\n.star-rating-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n overflow: hidden;\n pointer-events: none;\n z-index: 2;\n}\n\n/* Value display */\n.star-rating-value {\n margin-left: 8px;\n color: #555;\n font-size: 0.95em;\n font-weight: 500;\n user-select: none;\n}\n\n/* Accessibility: focus ring */\n.star-rating-star:focus {\n outline: 2px solid #fbbf24;\n outline-offset: 2px;\n}\n";
styleInject(css_248z);
const StarRating = ({ rating = 0, maxRating = 5, interactive = true, onRatingChange, size = "md", fillColor = "#fbbf24", emptyColor = "#d1d5db", hoverColor = "#f59e0b", allowHalf = false, showRating = false, className = "", showTooltip = false, tooltipLabels = ["Poor", "Fair", "Good", "Great", "Excellent"], animationDuration = 150, disabled = false, }) => {
const [hoverRating, setHoverRating] = react.useState(null);
const [hoveredStar, setHoveredStar] = react.useState(null);
const starSize = react.useMemo(() => {
if (typeof size === "number")
return size;
const sizes = { xs: 16, sm: 20, md: 24, lg: 32, xl: 40 };
return sizes[size];
}, [size]);
const handleMouseEnter = react.useCallback((starIndex, isHalf = false) => {
if (!interactive || disabled)
return;
const newRating = isHalf ? starIndex - 0.5 : starIndex;
setHoverRating(newRating);
setHoveredStar(starIndex);
}, [interactive, disabled]);
const handleMouseLeave = react.useCallback(() => {
if (!interactive || disabled)
return;
setHoverRating(null);
setHoveredStar(null);
}, [interactive, disabled]);
const handleClick = react.useCallback((starIndex, isHalf = false) => {
if (!interactive || disabled)
return;
const newRating = isHalf ? starIndex - 0.5 : starIndex;
onRatingChange === null || onRatingChange === void 0 ? void 0 : onRatingChange(newRating);
}, [interactive, disabled, onRatingChange]);
const currentRating = hoverRating !== null ? hoverRating : rating;
const renderStar = (starIndex) => {
const isHovered = hoveredStar === starIndex;
const fillPercentage = Math.min(Math.max(currentRating - (starIndex - 1), 0), 1);
const starColor = isHovered
? hoverColor
: fillPercentage > 0
? fillColor
: emptyColor;
return (jsxRuntime.jsxs("div", { className: `star-rating-star${disabled ? " star-rating-star-disabled" : ""}${isHovered ? " star-rating-star-hovered" : ""}`, style: {
width: starSize,
height: starSize,
transition: `transform ${animationDuration}ms`,
}, onMouseEnter: () => handleMouseEnter(starIndex), onMouseLeave: handleMouseLeave, onClick: () => handleClick(starIndex), title: showTooltip ? tooltipLabels[starIndex - 1] : undefined, tabIndex: interactive && !disabled ? 0 : -1, role: interactive ? "button" : undefined, "aria-label": showTooltip ? tooltipLabels[starIndex - 1] : `Rate ${starIndex}`, children: [allowHalf && interactive && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "star-rating-half left", onMouseEnter: () => handleMouseEnter(starIndex, true), onClick: (e) => {
e.stopPropagation();
handleClick(starIndex, true);
} }), jsxRuntime.jsx("div", { className: "star-rating-half right", onMouseEnter: () => handleMouseEnter(starIndex, false), onClick: (e) => {
e.stopPropagation();
handleClick(starIndex, false);
} })] })), jsxRuntime.jsx("svg", { width: starSize, height: starSize, viewBox: "0 0 24 24", fill: emptyColor, className: "star-rating-svg star-rating-empty", children: jsxRuntime.jsx("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" }) }), jsxRuntime.jsx("div", { className: "star-rating-fill", style: {
width: `${fillPercentage * 100}%`,
transition: `width ${animationDuration}ms ease`,
}, children: jsxRuntime.jsx("svg", { width: starSize, height: starSize, viewBox: "0 0 24 24", fill: starColor, className: "star-rating-svg star-rating-filled", children: jsxRuntime.jsx("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" }) }) })] }, starIndex));
};
return (jsxRuntime.jsxs("div", { className: `star-rating-root${className ? ` ${className}` : ""}`, children: [jsxRuntime.jsx("div", { className: "star-rating-stars", children: Array.from({ length: maxRating }, (_, i) => renderStar(i + 1)) }), showRating && (jsxRuntime.jsxs("span", { className: "star-rating-value", style: { fontSize: Math.max(starSize * 0.5, 12) }, children: [currentRating.toFixed(allowHalf ? 1 : 0), " / ", maxRating] }))] }));
};
exports.StarRating = StarRating;
exports.default = StarRating;
//# sourceMappingURL=index.js.map