UNPKG

@drivy/cobalt

Version:

Opinionated design system for Drivy's projects.

92 lines (89 loc) 3.87 kB
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