react-custom-rating-component
Version:
A fully customizable rating component for react and next.js
123 lines (122 loc) • 7.23 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importStar(require("react"));
var svgIconPath = 'm25,1 6,17h18l-14,11 5,17-15-10-15,10 5-17-14-11h18z';
var svgIconViewBox = '0 0 51 48';
var FullStar = function (_a) {
var iconStyles = _a.iconStyles;
return (react_1.default.createElement("svg", { viewBox: svgIconViewBox, style: iconStyles },
react_1.default.createElement("path", { d: svgIconPath, fill: 'currentColor' })));
};
var EmptyStar = function (_a) {
var iconStyles = _a.iconStyles, _b = _a.defaultColor, defaultColor = _b === void 0 ? 'rgb(203, 211, 227)' : _b;
var pathStyle = {
fill: defaultColor,
transition: 'fill .2s ease-in-out',
};
return (react_1.default.createElement("svg", { viewBox: svgIconViewBox, style: iconStyles },
react_1.default.createElement("path", { style: pathStyle, d: svgIconPath })));
};
var HeartEmptyIcon = function (_a) {
var iconStyles = _a.iconStyles, defaultColor = _a.defaultColor;
return (react_1.default.createElement("svg", { viewBox: '0 0 24 24', style: iconStyles, fill: defaultColor },
react_1.default.createElement("path", { d: 'M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z' })));
};
var HeartFullIcon = function (_a) {
var iconStyles = _a.iconStyles;
return (react_1.default.createElement("svg", { viewBox: '0 0 24 24', style: iconStyles, fill: 'currentColor' },
react_1.default.createElement("path", { d: 'm12 21.35-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z' })));
};
var Rating = function (_a) {
var _b = _a.precision, precision = _b === void 0 ? 1 : _b, _c = _a.count, count = _c === void 0 ? 5 : _c, _d = _a.shape, shape = _d === void 0 ? 'star' : _d, _e = _a.defaultValue, defaultValue = _e === void 0 ? 0 : _e, onChange = _a.onChange, onHover = _a.onHover, _f = _a.readOnly, readOnly = _f === void 0 ? false : _f, _g = _a.size, size = _g === void 0 ? '24px' : _g, _h = _a.spacing, spacing = _h === void 0 ? '5px' : _h, _j = _a.activeColor, activeColor = _j === void 0 ? 'orange' : _j, _k = _a.defaultColor, defaultColor = _k === void 0 ? 'gray' : _k, _l = _a.titleArray, titleArray = _l === void 0 ? ['Poor', 'Good', 'Very Good', 'Best', 'Excellent'] : _l, _m = _a.showTitle, showTitle = _m === void 0 ? false : _m, emptyIcon = _a.emptyIcon, fillIcon = _a.fillIcon;
if (showTitle && titleArray.length < count) {
throw new Error('titleArray length must be greater than or equal to count.');
}
var _o = (0, react_1.useState)(defaultValue), activeIcon = _o[0], setActiveIcon = _o[1];
var _p = (0, react_1.useState)(-1), hoverActiveIcon = _p[0], setHoverActiveIcon = _p[1];
var _q = (0, react_1.useState)(false), isHovered = _q[0], setIsHovered = _q[1];
var ratingContainerRef = (0, react_1.useRef)(null);
var calculateRating = function (e) {
var _a;
var _b = ratingContainerRef.current.getBoundingClientRect(), width = _b.width, left = _b.left;
var percent = (e.clientX - left) / width;
var numberInStars = percent * count;
var nearestNumber = Math.round((numberInStars + precision / 2) / precision) * precision;
return Number(nearestNumber.toFixed(((_a = precision.toString().split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) || 0));
};
var handleClick = function (e) {
if (readOnly)
return;
setIsHovered(false);
setActiveIcon(calculateRating(e));
onChange && onChange(calculateRating(e));
};
var handleMouseMove = function (e) {
if (readOnly)
return;
setIsHovered(true);
setHoverActiveIcon(calculateRating(e));
onHover && onHover(calculateRating(e));
};
var handleMouseLeave = function () {
if (readOnly)
return;
setHoverActiveIcon(-1); // Reset to default state
setIsHovered(false);
};
var iconStyles = {
width: size,
height: size,
};
var IconProps = {
iconStyles: iconStyles,
activeColor: activeColor,
defaultColor: defaultColor,
};
var getShape = function (style) {
if (shape === 'custom' && fillIcon && emptyIcon) {
return style === 'full' ? fillIcon(IconProps) : emptyIcon(IconProps);
}
switch (shape) {
case 'heart':
if (style === 'full')
return react_1.default.createElement(HeartFullIcon, tslib_1.__assign({}, IconProps));
return react_1.default.createElement(HeartEmptyIcon, tslib_1.__assign({}, IconProps));
default:
if (style === 'full')
return react_1.default.createElement(FullStar, tslib_1.__assign({}, IconProps));
return react_1.default.createElement(EmptyStar, tslib_1.__assign({}, IconProps));
}
};
return (react_1.default.createElement("div", { style: { display: 'flex' }, className: 'rcr-component-wrapper' },
react_1.default.createElement("div", { onClick: handleClick, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, ref: ratingContainerRef, style: {
display: 'flex',
gap: spacing,
position: 'relative',
cursor: readOnly ? 'default' : 'pointer',
lineHeight: 0,
boxSizing: 'border-box',
padding: 0,
margin: 0,
} }, new Array(count).fill('').map(function (_, index) {
var activeState = isHovered ? hoverActiveIcon : activeIcon;
var showEmptyIcon = activeState === -1 || activeState < index + 1;
var isActiveRating = activeState !== 1;
var isRatingWithPrecision = activeState % 1 !== 0;
var isRatingEqualToIndex = Math.ceil(activeState) === index + 1;
var showRatingWithPrecision = isActiveRating && isRatingWithPrecision && isRatingEqualToIndex;
return (react_1.default.createElement("div", { style: {
position: 'relative',
cursor: readOnly ? 'default' : 'pointer',
}, key: index, title: showTitle ? titleArray[index] : '' },
react_1.default.createElement("div", { style: {
color: activeColor,
width: showRatingWithPrecision ? "".concat((activeState % 1) * 100, "%") : '0%',
overflow: 'hidden',
position: 'absolute',
} }, getShape('full')),
react_1.default.createElement("div", { style: { color: activeColor } }, showEmptyIcon ? getShape('empty') : getShape('full'))));
}))));
};
exports.default = Rating;