@drivy/cobalt
Version:
Opinionated design system for Drivy's projects.
106 lines (105 loc) • 4.23 kB
JavaScript
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import classnames from "classnames";
import { nanoid } from "nanoid";
import react, { useCallback, useRef, useState } from "react";
import { getA11yOnClick } from "../../helpers/index.js";
import { StarFilledIcon } from "../Icon/index.js";
const defaultRatingIcon = /*#__PURE__*/ jsx(StarFilledIcon, {
color: "primary"
});
const itemEmptyColor = "surfaceContainerVariant";
function roundHalf(num) {
return Math.floor(2 * num) / 2;
}
function computeItemStatus(ratingValue, itemValue, icon) {
let itemStatus = "empty";
if (Array.isArray(icon)) {
if (itemValue === ratingValue) itemStatus = "full";
} else {
const isHalfItem = itemValue > ratingValue && ratingValue > itemValue - 1;
if (isHalfItem) itemStatus = "half";
else if (itemValue <= ratingValue) itemStatus = "full";
}
return itemStatus;
}
const RatingIcon = ({ icon, status = "full", size = 24 })=>{
if ("half" === status) return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ react.cloneElement(icon, {
color: itemEmptyColor,
size
}),
/*#__PURE__*/ jsx("div", {
className: "cobalt-rating-icons__icon-half-container",
children: /*#__PURE__*/ react.cloneElement(icon, {
size
})
})
]
});
{
const iconColor = "empty" === status ? itemEmptyColor : icon.props.color;
return /*#__PURE__*/ react.cloneElement(icon, {
color: iconColor,
size
});
}
};
const RatingIcons = ({ className, max = 5, value, size = 24, editable = false, onChange, icon = defaultRatingIcon })=>{
const name = useRef(nanoid());
const items = useRef(Array.from(new Array(Array.isArray(icon) ? icon.length : max)));
const [ratingValue, setRatingValue] = useState(()=>editable ? Math.floor(value) : roundHalf(value));
const [selectedIndex, setSelectedIndex] = useState(-1);
const resetSelection = useCallback(()=>{
setSelectedIndex(-1);
}, []);
const itemSize = 48 === size ? 16 : size;
return /*#__PURE__*/ jsx("div", {
className: classnames("cobalt-rating-icons", className, {
"cobalt-rating-icons--icon-size-48": 48 === size
}),
onPointerLeave: ()=>{
if (editable) document.activeElement.blur();
},
children: items.current.map((_value, idx)=>{
const itemValue = idx + 1;
const itemStatus = computeItemStatus(ratingValue, itemValue, icon);
return /*#__PURE__*/ jsxs("label", {
className: classnames("cobalt-rating-icons__icon-wrapper", {
"cobalt-rating-icons__icon-wrapper--selected": selectedIndex === idx
}),
...getA11yOnClick(()=>{
if (editable) {
setRatingValue(itemValue);
setSelectedIndex(idx);
}
}),
onMouseEnter: (e)=>{
if (editable) {
e.target.focus();
resetSelection();
}
},
children: [
/*#__PURE__*/ jsx("input", {
tabIndex: editable && (ratingValue === itemValue || 0 === ratingValue && 1 === itemValue) ? 0 : -1,
name: name.current,
type: "radio",
value: itemValue,
onChange: ()=>{
onChange?.(itemValue);
},
disabled: !editable
}),
/*#__PURE__*/ jsx(RatingIcon, {
icon: Array.isArray(icon) ? icon[idx] : icon,
status: itemStatus,
size: itemSize
})
]
}, itemValue);
})
});
};
export { RatingIcon, RatingIcons, computeItemStatus, defaultRatingIcon, roundHalf };
//# sourceMappingURL=RatingIcons.js.map