UNPKG

@yamada-ui/rating

Version:

Yamada UI rating component

537 lines (528 loc) • 17 kB
"use client" "use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/use-rating.tsx var use_rating_exports = {}; __export(use_rating_exports, { useRating: () => useRating }); module.exports = __toCommonJS(use_rating_exports); var import_form_control = require("@yamada-ui/form-control"); var import_use_controllable_state = require("@yamada-ui/use-controllable-state"); var import_utils5 = require("@yamada-ui/utils"); var import_react3 = require("react"); // src/rating-group.tsx var import_motion = require("@yamada-ui/motion"); var import_utils4 = require("@yamada-ui/utils"); // src/rating-context.ts var import_utils = require("@yamada-ui/utils"); var [RatingProvider, useRatingContext] = (0, import_utils.createContext)({ name: "RatingContext", errorMessage: `useRatingContext returned is 'undefined'. Seems you forgot to wrap the components in "<Rating />"` }); // src/rating-item.tsx var import_core = require("@yamada-ui/core"); var import_icon = require("@yamada-ui/icon"); var import_utils3 = require("@yamada-ui/utils"); var import_react2 = require("react"); // src/use-rating-item.ts var import_use_focus_visible = require("@yamada-ui/use-focus-visible"); var import_utils2 = require("@yamada-ui/utils"); var import_react = require("react"); var useRatingItem = ({ fractionValue, groupValue, value }) => { const { id, name, highlightSelectedOnly, outside, resolvedValue, roundedValue, setHoveredValue, setValue, formControlProps } = useRatingContext(); const { "aria-disabled": ariaDisabled, "aria-readonly": _ariaReadOnly, disabled, readOnly, ...omittedFormControlProps } = formControlProps; const [focused, setFocused] = (0, import_react.useState)(false); const [focusVisible, setFocusVisible] = (0, import_react.useState)(false); const active = value === resolvedValue; const checked = value === roundedValue; const filled = highlightSelectedOnly ? value === resolvedValue : value <= resolvedValue; const onBlur = (0, import_react.useCallback)(() => { setFocused(false); if (outside) setHoveredValue(-1); }, [outside, setHoveredValue]); const onInputChange = (0, import_react.useCallback)( (ev) => { if (readOnly || disabled) return; const value2 = parseFloat(ev.target.value); setHoveredValue(value2); }, [disabled, readOnly, setHoveredValue] ); const onChange = (0, import_react.useCallback)( (value2) => { if (readOnly || disabled) return; setValue(value2); }, [disabled, readOnly, setValue] ); const onMouseDown = (0, import_react.useCallback)(() => { if (readOnly || disabled) return; onChange(value); }, [disabled, onChange, readOnly, value]); const onTouchStart = (0, import_react.useCallback)(() => { if (readOnly || disabled) return; onChange(value); }, [disabled, onChange, readOnly, value]); const getItemProps = (0, import_react.useCallback)( (props = {}, ref = null) => { const zIndex = active ? 1 : -1; return { ref, htmlFor: `${id}-${groupValue}-${value}`, ...omittedFormControlProps, ...props, "data-active": (0, import_utils2.dataAttr)(active), "data-disabled": (0, import_utils2.dataAttr)(disabled), "data-filled": (0, import_utils2.dataAttr)(filled), "data-focus": (0, import_utils2.dataAttr)(focused), "data-focus-visible": (0, import_utils2.dataAttr)(focused && focusVisible), zIndex: fractionValue !== 1 ? zIndex : void 0, onMouseDown: (0, import_utils2.handlerAll)(onMouseDown, props.onMouseDown), onTouchStart: (0, import_utils2.handlerAll)(onTouchStart, props.onTouchStart) }; }, [ disabled, omittedFormControlProps, fractionValue, groupValue, id, active, filled, focusVisible, focused, onMouseDown, onTouchStart, value ] ); const getInputProps = (0, import_react.useCallback)( (props = {}, ref = null) => { return { ref, "aria-disabled": ariaDisabled, "aria-label": `${value}`, disabled, readOnly, ...omittedFormControlProps, ...props, id: `${id}-${groupValue}-${value}`, type: "radio", name, style: { border: "0px", clip: "rect(0px, 0px, 0px, 0px)", height: "1px", margin: "-1px", overflow: "hidden", padding: "0px", position: "absolute", whiteSpace: "nowrap", width: "1px" }, "data-active": (0, import_utils2.dataAttr)(active), "data-checked": (0, import_utils2.dataAttr)(checked), checked, value, onBlur: (0, import_utils2.handlerAll)(onBlur, props.onBlur), onChange: (0, import_utils2.handlerAll)(onInputChange, props.onChange), onFocus: (0, import_utils2.handlerAll)(() => setFocused(true), props.onFocus), onKeyDown: (0, import_utils2.handlerAll)( (ev) => ev.key === " " ? onChange(value) : void 0, props.onKeyDown ) }; }, [ ariaDisabled, disabled, readOnly, value, omittedFormControlProps, id, groupValue, name, checked, onInputChange, onBlur, active, onChange ] ); (0, import_react.useEffect)(() => { return (0, import_use_focus_visible.trackFocusVisible)(setFocusVisible); }, []); return { active, checked, filled, getInputProps, getItemProps }; }; // src/rating-item.tsx var import_jsx_runtime = require("react/jsx-runtime"); var RatingItem = (0, import_core.forwardRef)( ({ className, color, fractionValue, groupValue, value, ...rest }, ref) => { const { emptyIcon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingStarIcon, {}), filledIcon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingStarIcon, {}), styles, inputProps, itemProps } = useRatingContext(); const { active, filled, getInputProps, getItemProps } = useRatingItem({ fractionValue, groupValue, value }); const computedItemProps = (0, import_utils3.runIfFunc)(itemProps, value); const computedInputProps = (0, import_utils3.runIfFunc)(inputProps, value); const customColor = color ? { _filled: { color: (0, import_utils3.isString)(color) ? [color, color] : color } } : {}; const css = { display: "block", lineHeight: "0", ...styles.item, ...customColor }; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.ui.input, { ...getInputProps(computedInputProps, ref) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_core.ui.label, { className: (0, import_utils3.cx)("ui-rating__item", className), __css: css, ...getItemProps({ ...computedItemProps, ...rest }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( RatingIcon, { clipPath: fractionValue !== 1 ? `inset(0 ${active ? 100 - fractionValue * 100 : 100}% 0 0)` : void 0, children: filled ? (0, import_utils3.runIfFunc)(filledIcon, groupValue) : (0, import_utils3.runIfFunc)(emptyIcon, groupValue) } ) } ) ] }); } ); RatingItem.displayName = "RatingItem"; RatingItem.__ui__ = "RatingItem"; var RatingIcon = ({ className, children, ...rest }) => { const { styles } = useRatingContext(); const validChildren = (0, import_utils3.getValidChildren)(children); const cloneChildren = validChildren.map( (child) => (0, import_react2.cloneElement)(child, { style: { maxHeight: "1em", maxWidth: "1em" }, "aria-hidden": true, focusable: false }) ); const css = { alignItems: "center", display: "inline-flex", justifyContent: "center", ...styles.icon }; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_core.ui.div, { className: (0, import_utils3.cx)("ui-rating__item__icon", className), __css: css, ...rest, children: cloneChildren } ); }; RatingIcon.displayName = "RatingIcon"; RatingIcon.__ui__ = "RatingIcon"; var RatingStarIcon = ({ ...rest }) => { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_icon.Icon, { strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", ...rest, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 17.75l-6.172 3.245l1.179 -6.873l-5 -4.867l6.9 -1l3.086 -6.253l3.086 6.253l6.9 1l-5 4.867l1.179 6.873z" }) } ); }; RatingStarIcon.displayName = "RatingStarIcon"; RatingStarIcon.__ui__ = "RatingStarIcon"; // src/rating-utils.ts var getRoundedValue = (value, to) => { var _a; const rounded = Math.round(value / to) * to; const precision = ((_a = `${to}`.split(".")[1]) == null ? void 0 : _a.length) || 0; return Number(rounded.toFixed(precision)); }; // src/rating-group.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var RatingGroup = (0, import_motion.motionForwardRef)( ({ className, color, items, value: groupValue, ...rest }, ref) => { const { decimal, styles, getGroupProps, groupProps } = useRatingContext(); const computedGroupProps = (0, import_utils4.runIfFunc)(groupProps, groupValue); const css = { ...styles.group }; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_motion.motion.div, { className: (0, import_utils4.cx)("ui-rating__group", className), __css: css, ...getGroupProps( { value: groupValue, ...computedGroupProps, ...rest }, ref ), children: Array(items).fill(0).map((_, index) => { const fractionValue = decimal * (groupValue === 1 ? index : index + 1); const value = getRoundedValue( groupValue - 1 + fractionValue, decimal ); return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( RatingItem, { color, fractionValue, groupValue, value }, `${groupValue}-${fractionValue}` ); }) } ); } ); RatingGroup.displayName = "RatingGroup"; RatingGroup.__ui__ = "RatingGroup"; // src/use-rating.tsx var import_jsx_runtime3 = require("react/jsx-runtime"); var useRating = ({ name, color, defaultValue = 0, emptyIcon, filledIcon, fractions = 1, highlightSelectedOnly = false, items = 5, value: valueProp, groupProps, inputProps, itemProps, onChange: onChangeProp, onHover, onMouseEnter: onMouseEnterProp, onMouseLeave: onMouseLeaveProp, onMouseMove: onMouseMoveProp, onTouchEnd: onTouchEndProp, onTouchStart: onTouchStartProp, ...props }) => { const uuid = (0, import_react3.useId)(); const { id = uuid, ...rest } = (0, import_form_control.useFormControlProps)(props); const containerRef = (0, import_react3.useRef)(null); const [value, setValue] = (0, import_use_controllable_state.useControllableState)({ defaultValue, value: valueProp, onChange: onChangeProp }); const [hoveredValue, setHoveredValue] = (0, import_react3.useState)(-1); const [outside, setOutside] = (0, import_react3.useState)(true); const [formControlProps, containerProps] = (0, import_utils5.splitObject)( rest, import_form_control.formControlProperties ); const { disabled, readOnly, ...omittedFormControlProps } = formControlProps; const resolvedFractions = Math.floor(fractions); const resolvedItems = Math.floor(items); const decimal = 1 / resolvedFractions; const roundedValue = getRoundedValue(value, decimal); const resolvedValue = hoveredValue !== -1 ? hoveredValue : roundedValue; name != null ? name : name = `rating-${id}`; const getHoveredValue = (0, import_react3.useCallback)( (x) => { const { left, width } = containerRef.current.getBoundingClientRect(); const itemWidth = width / resolvedItems; const hoveredValue2 = (x - left) / itemWidth; const value2 = (0, import_utils5.clampNumber)( getRoundedValue(hoveredValue2 + decimal / 2, decimal), decimal, resolvedItems ); return value2; }, [decimal, resolvedItems] ); const onMouseEnter = (0, import_react3.useCallback)(() => { if (!disabled && !readOnly) setOutside(false); }, [disabled, readOnly]); const onMouseLeave = (0, import_react3.useCallback)(() => { if (disabled || readOnly) return; setHoveredValue(-1); setOutside(true); if (hoveredValue !== -1) onHover == null ? void 0 : onHover(-1); }, [disabled, hoveredValue, onHover, readOnly, setHoveredValue]); const onTouchStart = (0, import_react3.useCallback)( (ev) => { ev.preventDefault(); const el = ev.touches[0]; if (!el) return; const value2 = getHoveredValue(el.clientX); setValue(value2); }, [getHoveredValue, setValue] ); const onTouchEnd = (0, import_react3.useCallback)((ev) => { ev.preventDefault(); }, []); const onMouseMove = (0, import_react3.useCallback)( (ev) => { if (disabled || readOnly) return; const roundedValue2 = getHoveredValue(ev.clientX); setHoveredValue(roundedValue2); if (roundedValue2 !== hoveredValue) onHover == null ? void 0 : onHover(roundedValue2); }, [disabled, getHoveredValue, hoveredValue, readOnly, onHover] ); const getContainerProps = (0, import_react3.useCallback)( (props2 = {}, ref = null) => ({ ref: (0, import_utils5.mergeRefs)(ref, containerRef), "aria-label": `${value} Stars`, role: "radiogroup", ...omittedFormControlProps, ...containerProps, ...props2, id, onMouseEnter: (0, import_utils5.handlerAll)( onMouseEnter, props2.onMouseEnter, onMouseEnterProp ), onMouseLeave: (0, import_utils5.handlerAll)( onMouseLeave, props2.onMouseLeave, onMouseLeaveProp ), onMouseMove: (0, import_utils5.handlerAll)(onMouseMove, props2.onMouseMove, onMouseMoveProp), onTouchEnd: (0, import_utils5.handlerAll)(onTouchEnd, props2.onTouchEnd, onTouchEndProp), onTouchStart: (0, import_utils5.handlerAll)( onTouchStart, props2.onTouchStart, onTouchStartProp ) }), [ omittedFormControlProps, containerProps, id, value, onMouseEnter, onMouseEnterProp, onMouseLeave, onMouseLeaveProp, onMouseMove, onMouseMoveProp, onTouchEnd, onTouchEndProp, onTouchStart, onTouchStartProp ] ); const getGroupProps = (0, import_react3.useCallback)( ({ value: value2, ...props2 }, ref = null) => { const isActive = !readOnly && Math.ceil(hoveredValue) === value2; return { ref, whileTap: !disabled && !readOnly ? { y: -4 } : void 0, ...props2, "data-active": (0, import_utils5.dataAttr)(isActive), tabIndex: -1 }; }, [disabled, hoveredValue, readOnly] ); const children = Array(resolvedItems).fill(0).map((_, index) => { const value2 = index + 1; return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( RatingGroup, { color: (0, import_utils5.runIfFunc)(color, value2), items: index === 0 ? resolvedFractions + 1 : resolvedFractions, value: value2 }, value2 ); }); return { id, name, children, decimal, emptyIcon, filledIcon, highlightSelectedOnly, hoveredValue, outside, resolvedValue, roundedValue, setHoveredValue, setValue, value, formControlProps, getContainerProps, getGroupProps, groupProps, inputProps, itemProps }; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useRating }); //# sourceMappingURL=use-rating.js.map