UNPKG

@yamada-ui/react

Version:

React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion

1 lines • 14.3 kB
{"version":3,"file":"use-rating.cjs","names":["createContext","useFieldProps","useControllableState","roundedValue","props","mergeRefs","value","getLabelProps: PropGetter<\"label\">","visuallyHiddenAttributes"],"sources":["../../../../src/components/rating/use-rating.tsx"],"sourcesContent":["\"use client\"\n\nimport type { ChangeEvent, KeyboardEvent, MouseEvent, TouchEvent } from \"react\"\nimport type { HTMLProps, PropGetter } from \"../../core\"\nimport type { FieldProps } from \"../field\"\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\"\nimport { useControllableState } from \"../../hooks/use-controllable-state\"\nimport {\n ariaAttr,\n clampNumber,\n createContext,\n dataAttr,\n handlerAll,\n mergeRefs,\n runKeyAction,\n visuallyHiddenAttributes,\n} from \"../../utils\"\nimport { useFieldProps } from \"../field\"\n\nconst getRoundedValue = (value: number, to: number) => {\n const rounded = Math.round(value / to) * to\n const precision = `${to}`.split(\".\")[1]?.length || 0\n\n return Number(rounded.toFixed(precision))\n}\n\nexport interface RatingContext extends Omit<UseRatingReturn, \"getRootProps\"> {}\n\nconst [RatingContext, useRatingContext] = createContext<RatingContext>({\n name: \"RatingContext\",\n})\n\nexport { RatingContext, useRatingContext }\n\nexport interface UseRatingProps\n extends FieldProps,\n Omit<HTMLProps, \"color\" | \"defaultValue\" | \"onChange\"> {\n /**\n * The top-level id string that will be applied to the rating.\n * The index of the rating item will be appended to this top-level id.\n */\n id?: string\n /**\n * The name of the input element.\n */\n name?: string\n /**\n * Number of controls that should be rendered.\n *\n * @default 5\n */\n count?: number\n /**\n * The initial value of the rating.\n *\n * @default 0\n */\n defaultValue?: number\n /**\n * Number of fractions each item can be divided into,\n *\n * @default 1\n */\n fractions?: number\n /**\n * If `true`, only the selected icons will be filled.\n *\n * @default false\n */\n highlightSelectedOnly?: boolean\n /**\n * The value of the rating.\n */\n value?: number\n /**\n * The callback invoked when value state changes.\n */\n onChange?: (value: number) => void\n /**\n * The callback invoked when hovering over the rating.\n */\n onHover?: (value: number) => void\n}\n\nexport const useRating = (props: UseRatingProps = {}) => {\n const uuid = useId()\n const {\n props: {\n id = uuid,\n name = uuid,\n count: countProp = 5,\n defaultValue = 0,\n disabled,\n fractions: fractionsProp = 1,\n highlightSelectedOnly = false,\n readOnly,\n required,\n value: valueProp,\n onChange: onChangeProp,\n onHover,\n ...rest\n },\n ariaProps,\n dataProps,\n eventProps,\n } = useFieldProps({ ...props, notSupportReadOnly: true })\n const rootRef = useRef<HTMLDivElement>(null)\n const [value, setValue] = useControllableState({\n defaultValue,\n value: valueProp,\n onChange: onChangeProp,\n })\n const [hoveredValue, setHoveredValue] = useState(-1)\n const outsideRef = useRef(true)\n const fractions = Math.floor(fractionsProp)\n const count = Math.floor(countProp)\n const decimal = 1 / fractions\n const roundedValue = useMemo(\n () => getRoundedValue(value, decimal),\n [decimal, value],\n )\n const interactive = !(readOnly || disabled)\n const displayValue = hoveredValue !== -1 ? hoveredValue : roundedValue\n\n const getHoveredValue = useCallback(\n (x: number) => {\n if (!rootRef.current) return -1\n\n const { left, width } = rootRef.current.getBoundingClientRect()\n const itemWidth = width / count\n\n const hoveredValue = (x - left) / itemWidth\n\n const value = clampNumber(\n getRoundedValue(hoveredValue + decimal / 2, decimal),\n decimal,\n count,\n )\n\n return value\n },\n [count, decimal],\n )\n\n const onMouseEnter = useCallback(() => {\n if (interactive) outsideRef.current = false\n }, [interactive])\n\n const onMouseLeave = useCallback(() => {\n if (!interactive) return\n\n setHoveredValue(-1)\n outsideRef.current = true\n\n if (hoveredValue !== -1) onHover?.(-1)\n }, [hoveredValue, onHover, interactive, setHoveredValue])\n\n const onMouseMove = useCallback(\n (ev: MouseEvent<HTMLDivElement>) => {\n if (disabled || readOnly) return\n\n const roundedValue = getHoveredValue(ev.clientX)\n\n setHoveredValue(roundedValue)\n\n if (roundedValue !== hoveredValue) onHover?.(roundedValue)\n },\n [disabled, getHoveredValue, hoveredValue, readOnly, onHover],\n )\n\n const onTouchStart = useCallback(\n (ev: TouchEvent<HTMLDivElement>) => {\n ev.preventDefault()\n\n const el = ev.touches[0]\n\n if (!el) return\n\n const value = getHoveredValue(el.clientX)\n\n setValue(value)\n },\n [getHoveredValue, setValue],\n )\n\n const onTouchEnd = useCallback((ev: TouchEvent<HTMLDivElement>) => {\n ev.preventDefault()\n }, [])\n\n const getRootProps: PropGetter = useCallback(\n ({ ref, ...props } = {}) => ({\n ...dataProps,\n ...eventProps,\n ...ariaProps,\n id,\n \"aria-label\": `${value} Stars`,\n \"aria-readonly\": ariaAttr(readOnly),\n role: \"radiogroup\",\n ...rest,\n ...props,\n ref: mergeRefs(ref, rest.ref, rootRef),\n onMouseEnter: handlerAll(\n props.onMouseEnter,\n rest.onMouseEnter,\n onMouseEnter,\n ),\n onMouseLeave: handlerAll(\n props.onMouseLeave,\n rest.onMouseLeave,\n onMouseLeave,\n ),\n onMouseMove: handlerAll(props.onMouseMove, rest.onMouseMove, onMouseMove),\n onTouchEnd: handlerAll(props.onTouchEnd, rest.onTouchEnd, onTouchEnd),\n onTouchStart: handlerAll(\n props.onTouchStart,\n rest.onTouchStart,\n onTouchStart,\n ),\n }),\n [\n ariaProps,\n dataProps,\n eventProps,\n id,\n onMouseEnter,\n onMouseLeave,\n onMouseMove,\n onTouchEnd,\n onTouchStart,\n readOnly,\n rest,\n value,\n ],\n )\n\n return {\n id,\n name,\n count,\n decimal,\n disabled,\n displayValue,\n fractions,\n highlightSelectedOnly,\n hoveredValue,\n interactive,\n outsideRef,\n readOnly,\n required,\n roundedValue,\n setHoveredValue,\n setValue,\n value,\n ariaProps,\n dataProps,\n eventProps,\n getRootProps,\n }\n}\n\nexport type UseRatingReturn = ReturnType<typeof useRating>\n\nexport interface UseRatingItemProps extends HTMLProps<\"label\"> {\n groupValue: number\n index: number\n}\n\nexport const useRatingItem = ({\n groupValue,\n index,\n ...rest\n}: UseRatingItemProps) => {\n const {\n id: rootId,\n name,\n decimal,\n disabled,\n displayValue,\n highlightSelectedOnly,\n interactive,\n outsideRef,\n readOnly,\n required,\n roundedValue,\n setHoveredValue,\n setValue,\n ariaProps,\n dataProps,\n eventProps,\n } = useRatingContext()\n const fractionValue = decimal * (groupValue === 1 ? index : index + 1)\n const value = useMemo(\n () => getRoundedValue(groupValue - 1 + fractionValue, decimal),\n [decimal, fractionValue, groupValue],\n )\n const active = value === displayValue\n const checked = value === roundedValue\n const filled = highlightSelectedOnly\n ? value === displayValue\n : value <= displayValue\n const id = `${rootId}-${groupValue}-${value}`\n\n const onBlur = useCallback(() => {\n if (outsideRef.current) setHoveredValue(-1)\n }, [outsideRef, setHoveredValue])\n\n const onInputChange = useCallback(\n (ev: ChangeEvent<HTMLInputElement>) => {\n if (!interactive) return\n\n const value = parseFloat(ev.target.value)\n\n setHoveredValue(value)\n },\n [interactive, setHoveredValue],\n )\n\n const onChange = useCallback(\n (value: number) => {\n if (!interactive) return\n\n setValue(value)\n },\n [interactive, setValue],\n )\n\n const onMouseDown = useCallback(() => {\n onChange(value)\n }, [onChange, value])\n\n const onTouchStart = useCallback(() => {\n onChange(value)\n }, [onChange, value])\n\n const onKeyDown = useCallback(\n (ev: KeyboardEvent<HTMLInputElement>) => {\n runKeyAction(ev, {\n Space: () => onChange(value),\n })\n },\n [onChange, value],\n )\n\n const getLabelProps: PropGetter<\"label\"> = useCallback(\n ({ style, ...props } = {}) => ({\n ...dataProps,\n ...ariaProps,\n htmlFor: id,\n \"data-active\": dataAttr(active),\n \"data-filled\": dataAttr(filled),\n ...rest,\n ...props,\n style: {\n ...style,\n zIndex: fractionValue !== 1 ? (active ? 1 : -1) : undefined,\n },\n onMouseDown: handlerAll(props.onMouseDown, onMouseDown),\n onTouchStart: handlerAll(props.onTouchStart, onTouchStart),\n }),\n [\n active,\n ariaProps,\n dataProps,\n filled,\n fractionValue,\n id,\n onMouseDown,\n onTouchStart,\n rest,\n ],\n )\n\n const getInputProps: PropGetter<\"input\"> = useCallback(\n (props = {}) => ({\n ...dataProps,\n ...eventProps,\n ...ariaProps,\n id,\n type: \"radio\",\n name,\n style: visuallyHiddenAttributes.style,\n \"aria-label\": value.toString(),\n \"data-active\": dataAttr(active),\n \"data-checked\": dataAttr(checked),\n checked,\n disabled,\n readOnly,\n required,\n value,\n ...props,\n onBlur: handlerAll(onBlur, props.onBlur),\n onChange: handlerAll(props.onChange, onInputChange),\n onKeyDown: handlerAll(props.onKeyDown, onKeyDown),\n }),\n [\n id,\n name,\n value,\n active,\n checked,\n dataProps,\n eventProps,\n ariaProps,\n disabled,\n readOnly,\n required,\n onBlur,\n onInputChange,\n onKeyDown,\n ],\n )\n\n return {\n active,\n checked,\n filled,\n fractionValue,\n groupValue,\n value,\n getInputProps,\n getLabelProps,\n }\n}\n\nexport type UseRatingItemReturn = ReturnType<typeof useRatingItem>\n"],"mappings":"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"}