@drivy/cobalt
Version:
Opinionated design system for Drivy's projects.
92 lines (89 loc) • 3.87 kB
JavaScript
import React, { useRef, useState, useCallback } from 'react';
import { nanoid } from 'nanoid';
import cx from 'classnames';
import '../Icon/index.js';
import StarIcon from '../Icon/__generated__/StarIcon.js';
const defaultRatingIcon = React.createElement(StarIcon, { color: "primary" });
const itemEmptyColor = "surfaceContainerVariant";
function roundHalf(num) {
return Math.floor(num * 2) / 2;
}
function computeItemStatus(ratingValue, itemValue, icon) {
let itemStatus = "empty";
if (!Array.isArray(icon)) {
// gauge
const isHalfItem = itemValue > ratingValue && ratingValue > itemValue - 1;
if (isHalfItem) {
itemStatus = "half";
}
else if (itemValue <= ratingValue) {
itemStatus = "full";
}
}
else if (itemValue === ratingValue) {
// radio
itemStatus = "full";
}
return itemStatus;
}
const RatingIcon = ({ icon, status = "full", size = 24, }) => {
if (status === "half") {
return (React.createElement(React.Fragment, null,
React.cloneElement(icon, {
color: itemEmptyColor,
size,
}),
React.createElement("div", { className: "cobalt-rating-icons__icon-half-container" }, React.cloneElement(icon, { size }))));
}
else {
const iconColor = status === "empty" ? itemEmptyColor : icon.props.color;
return 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); // used to enforce selection effect (icon scale reset)
const resetSelection = useCallback(() => {
setSelectedIndex(-1);
}, []);
const itemSize = size === 48 ? 16 : size;
// input element is used for a11y purpose ( focus feedback and keyboard selection )
return (React.createElement("div", { className: cx("cobalt-rating-icons", className, {
"cobalt-rating-icons--icon-size-48": size === 48,
}), onPointerLeave: () => {
if (editable) {
document.activeElement.blur();
}
} }, items.current.map((_value, idx) => {
const itemValue = idx + 1;
const itemStatus = computeItemStatus(ratingValue, itemValue, icon);
return (React.createElement("label", { className: cx("cobalt-rating-icons__icon-wrapper", {
"cobalt-rating-icons__icon-wrapper--selected": selectedIndex === idx,
}), key: idx, onClick: () => {
if (editable) {
setRatingValue(itemValue);
setSelectedIndex(idx);
}
}, onMouseEnter: (e) => {
if (editable) {
e.target.focus();
resetSelection();
}
} },
React.createElement("input", { tabIndex: editable &&
(ratingValue === itemValue ||
(ratingValue === 0 && itemValue === 1))
? 0
: -1, name: name.current, type: "radio", value: itemValue, onChange: () => {
onChange && onChange(itemValue);
}, disabled: !editable }),
React.createElement(RatingIcon, { icon: Array.isArray(icon) ? icon[idx] : icon, status: itemStatus, size: itemSize })));
})));
};
export { RatingIcon, RatingIcons, computeItemStatus, defaultRatingIcon, roundHalf };
//# sourceMappingURL=RatingIcons.js.map