{"version":3,"sources":["../src/use-rating.tsx"],"sourcesContent":["import type {\n  CSSUIProps,\n  HTMLUIProps,\n  PropGetter,\n  RequiredPropGetter,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type { MotionProps } from \"@yamada-ui/motion\"\nimport type { Merge } from \"@yamada-ui/utils\"\nimport type { MouseEvent, ReactNode, TouchEvent } from \"react\"\nimport type { RatingGroupProps } from \"./rating-group\"\nimport type { RatingItemProps } from \"./rating-item\"\nimport {\n  formControlProperties,\n  useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport {\n  clampNumber,\n  dataAttr,\n  handlerAll,\n  mergeRefs,\n  runIfFunc,\n  splitObject,\n} from \"@yamada-ui/utils\"\nimport { useCallback, useId, useRef, useState } from \"react\"\nimport { RatingGroup } from \"./rating-group\"\nimport { getRoundedValue } from \"./rating-utils\"\n\ntype OmittedGroupProps = Omit<RatingGroupProps, \"children\" | \"items\" | \"value\">\ntype OmittedItemProps = Omit<\n  RatingItemProps,\n  \"children\" | \"fractionValue\" | \"groupValue\" | \"value\"\n>\ntype OmittedInputProps = Omit<\n  HTMLUIProps<\"input\">,\n  \"checked\" | \"defaultValue\" | \"value\"\n>\n\nexport type GroupProps =\n  | ((value: number) => OmittedGroupProps)\n  | OmittedGroupProps\nexport type ItemProps = ((value: number) => OmittedItemProps) | OmittedItemProps\nexport type InputProps =\n  | ((value: number) => OmittedInputProps)\n  | OmittedInputProps\n\ninterface UseRatingOptions {\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   * The color of the filled icons.\n   */\n  color?: ((value: number) => CSSUIProps[\"color\"]) | CSSUIProps[\"color\"]\n  /**\n   * The initial value of the rating.\n   *\n   * @default 0\n   */\n  defaultValue?: number\n  /**\n   * The empty icon for the rating.\n   */\n  emptyIcon?: ((value: number) => ReactNode) | ReactNode\n  /**\n   * The filled icon for the rating.\n   */\n  filledIcon?: ((value: number) => ReactNode) | ReactNode\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   * Number of controls that should be rendered.\n   *\n   * @default 5\n   */\n  items?: number\n  /**\n   * The value of the rating.\n   */\n  value?: number\n  /**\n   * Props for the rating group.\n   */\n  groupProps?: GroupProps\n  /**\n   * Props for the input element.\n   */\n  inputProps?: InputProps\n  /**\n   * Props for the rating item.\n   */\n  itemProps?: ItemProps\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 type UseRatingProps = FormControlOptions &\n  Omit<HTMLUIProps, \"color\" | \"defaultValue\" | \"id\" | \"onChange\"> &\n  UseRatingOptions\n\nexport const useRating = ({\n  name,\n  color,\n  defaultValue = 0,\n  emptyIcon,\n  filledIcon,\n  fractions = 1,\n  highlightSelectedOnly = false,\n  items = 5,\n  value: valueProp,\n  groupProps,\n  inputProps,\n  itemProps,\n  onChange: onChangeProp,\n  onHover,\n  onMouseEnter: onMouseEnterProp,\n  onMouseLeave: onMouseLeaveProp,\n  onMouseMove: onMouseMoveProp,\n  onTouchEnd: onTouchEndProp,\n  onTouchStart: onTouchStartProp,\n  ...props\n}: UseRatingProps) => {\n  const uuid = useId()\n  const { id = uuid, ...rest } = useFormControlProps(props)\n  const containerRef = useRef<HTMLDivElement>(null)\n  const [value, setValue] = useControllableState({\n    defaultValue,\n    value: valueProp,\n    onChange: onChangeProp,\n  })\n  const [hoveredValue, setHoveredValue] = useState<number>(-1)\n  const [outside, setOutside] = useState(true)\n  const [formControlProps, containerProps] = splitObject(\n    rest,\n    formControlProperties,\n  )\n  const { disabled, readOnly, ...omittedFormControlProps } = formControlProps\n  const resolvedFractions = Math.floor(fractions)\n  const resolvedItems = Math.floor(items)\n  const decimal = 1 / resolvedFractions\n  const roundedValue = getRoundedValue(value, decimal)\n  const resolvedValue = hoveredValue !== -1 ? hoveredValue : roundedValue\n\n  name ??= `rating-${id}`\n\n  const getHoveredValue = useCallback(\n    (x: number) => {\n      const { left, width } = containerRef.current!.getBoundingClientRect()\n      const itemWidth = width / resolvedItems\n\n      const hoveredValue = (x - left) / itemWidth\n\n      const value = clampNumber(\n        getRoundedValue(hoveredValue + decimal / 2, decimal),\n        decimal,\n        resolvedItems,\n      )\n\n      return value\n    },\n    [decimal, resolvedItems],\n  )\n\n  const onMouseEnter = useCallback(() => {\n    if (!disabled && !readOnly) setOutside(false)\n  }, [disabled, readOnly])\n\n  const onMouseLeave = useCallback(() => {\n    if (disabled || readOnly) return\n\n    setHoveredValue(-1)\n    setOutside(true)\n\n    if (hoveredValue !== -1) onHover?.(-1)\n  }, [disabled, hoveredValue, onHover, readOnly, setHoveredValue])\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 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 getContainerProps: PropGetter = useCallback(\n    (props = {}, ref = null) => ({\n      ref: mergeRefs(ref, containerRef),\n      \"aria-label\": `${value} Stars`,\n      role: \"radiogroup\",\n      ...omittedFormControlProps,\n      ...containerProps,\n      ...props,\n      id,\n      onMouseEnter: handlerAll(\n        onMouseEnter,\n        props.onMouseEnter,\n        onMouseEnterProp,\n      ),\n      onMouseLeave: handlerAll(\n        onMouseLeave,\n        props.onMouseLeave,\n        onMouseLeaveProp,\n      ),\n      onMouseMove: handlerAll(onMouseMove, props.onMouseMove, onMouseMoveProp),\n      onTouchEnd: handlerAll(onTouchEnd, props.onTouchEnd, onTouchEndProp),\n      onTouchStart: handlerAll(\n        onTouchStart,\n        props.onTouchStart,\n        onTouchStartProp,\n      ),\n    }),\n    [\n      omittedFormControlProps,\n      containerProps,\n      id,\n      value,\n      onMouseEnter,\n      onMouseEnterProp,\n      onMouseLeave,\n      onMouseLeaveProp,\n      onMouseMove,\n      onMouseMoveProp,\n      onTouchEnd,\n      onTouchEndProp,\n      onTouchStart,\n      onTouchStartProp,\n    ],\n  )\n\n  const getGroupProps: RequiredPropGetter<\n    Merge<MotionProps, { value: number }>,\n    MotionProps\n  > = useCallback(\n    ({ value, ...props }, ref = null) => {\n      const isActive = !readOnly && Math.ceil(hoveredValue) === value\n\n      return {\n        ref,\n        whileTap: !disabled && !readOnly ? { y: -4 } : undefined,\n        ...props,\n        \"data-active\": dataAttr(isActive),\n        tabIndex: -1,\n      }\n    },\n    [disabled, hoveredValue, readOnly],\n  )\n\n  const children = Array(resolvedItems)\n    .fill(0)\n    .map((_, index) => {\n      const value = index + 1\n\n      return (\n        <RatingGroup\n          key={value}\n          color={runIfFunc(color, value)}\n          items={index === 0 ? resolvedFractions + 1 : resolvedFractions}\n          value={value}\n        />\n      )\n    })\n\n  return {\n    id,\n    name,\n    children,\n    decimal,\n    emptyIcon,\n    filledIcon,\n    highlightSelectedOnly,\n    hoveredValue,\n    outside,\n    resolvedValue,\n    roundedValue,\n    setHoveredValue,\n    setValue,\n    value,\n    formControlProps,\n    getContainerProps,\n    getGroupProps,\n    groupProps,\n    inputProps,\n    itemProps,\n  }\n}\n\nexport type UseRatingReturn = ReturnType<typeof useRating>\n"],"mappings":";;;;;;;;;AAYA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa,OAAO,QAAQ,gBAAgB;AAmR7C;AAjLD,IAAM,YAAY,CAAC;AAAA,EACxB;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,wBAAwB;AAAA,EACxB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,GAAG;AACL,MAAsB;AACpB,QAAM,OAAO,MAAM;AACnB,QAAM,EAAE,KAAK,MAAM,GAAG,KAAK,IAAI,oBAAoB,KAAK;AACxD,QAAM,eAAe,OAAuB,IAAI;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,qBAAqB;AAAA,IAC7C;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAiB,EAAE;AAC3D,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,kBAAkB,cAAc,IAAI;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AACA,QAAM,EAAE,UAAU,UAAU,GAAG,wBAAwB,IAAI;AAC3D,QAAM,oBAAoB,KAAK,MAAM,SAAS;AAC9C,QAAM,gBAAgB,KAAK,MAAM,KAAK;AACtC,QAAM,UAAU,IAAI;AACpB,QAAM,eAAe,gBAAgB,OAAO,OAAO;AACnD,QAAM,gBAAgB,iBAAiB,KAAK,eAAe;AAE3D,+BAAS,UAAU,EAAE;AAErB,QAAM,kBAAkB;AAAA,IACtB,CAAC,MAAc;AACb,YAAM,EAAE,MAAM,MAAM,IAAI,aAAa,QAAS,sBAAsB;AACpE,YAAM,YAAY,QAAQ;AAE1B,YAAMA,iBAAgB,IAAI,QAAQ;AAElC,YAAMC,SAAQ;AAAA,QACZ,gBAAgBD,gBAAe,UAAU,GAAG,OAAO;AAAA,QACnD;AAAA,QACA;AAAA,MACF;AAEA,aAAOC;AAAA,IACT;AAAA,IACA,CAAC,SAAS,aAAa;AAAA,EACzB;AAEA,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,CAAC,YAAY,CAAC,SAAU,YAAW,KAAK;AAAA,EAC9C,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,YAAY,SAAU;AAE1B,oBAAgB,EAAE;AAClB,eAAW,IAAI;AAEf,QAAI,iBAAiB,GAAI,oCAAU;AAAA,EACrC,GAAG,CAAC,UAAU,cAAc,SAAS,UAAU,eAAe,CAAC;AAE/D,QAAM,eAAe;AAAA,IACnB,CAAC,OAAmC;AAClC,SAAG,eAAe;AAElB,YAAM,KAAK,GAAG,QAAQ,CAAC;AAEvB,UAAI,CAAC,GAAI;AAET,YAAMA,SAAQ,gBAAgB,GAAG,OAAO;AAExC,eAASA,MAAK;AAAA,IAChB;AAAA,IACA,CAAC,iBAAiB,QAAQ;AAAA,EAC5B;AAEA,QAAM,aAAa,YAAY,CAAC,OAAmC;AACjE,OAAG,eAAe;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc;AAAA,IAClB,CAAC,OAAmC;AAClC,UAAI,YAAY,SAAU;AAE1B,YAAMC,gBAAe,gBAAgB,GAAG,OAAO;AAE/C,sBAAgBA,aAAY;AAE5B,UAAIA,kBAAiB,aAAc,oCAAUA;AAAA,IAC/C;AAAA,IACA,CAAC,UAAU,iBAAiB,cAAc,UAAU,OAAO;AAAA,EAC7D;AAEA,QAAM,oBAAgC;AAAA,IACpC,CAACC,SAAQ,CAAC,GAAG,MAAM,UAAU;AAAA,MAC3B,KAAK,UAAU,KAAK,YAAY;AAAA,MAChC,cAAc,GAAG,KAAK;AAAA,MACtB,MAAM;AAAA,MACN,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAGA;AAAA,MACH;AAAA,MACA,cAAc;AAAA,QACZ;AAAA,QACAA,OAAM;AAAA,QACN;AAAA,MACF;AAAA,MACA,cAAc;AAAA,QACZ;AAAA,QACAA,OAAM;AAAA,QACN;AAAA,MACF;AAAA,MACA,aAAa,WAAW,aAAaA,OAAM,aAAa,eAAe;AAAA,MACvE,YAAY,WAAW,YAAYA,OAAM,YAAY,cAAc;AAAA,MACnE,cAAc;AAAA,QACZ;AAAA,QACAA,OAAM;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gBAGF;AAAA,IACF,CAAC,EAAE,OAAAF,QAAO,GAAGE,OAAM,GAAG,MAAM,SAAS;AACnC,YAAM,WAAW,CAAC,YAAY,KAAK,KAAK,YAAY,MAAMF;AAE1D,aAAO;AAAA,QACL;AAAA,QACA,UAAU,CAAC,YAAY,CAAC,WAAW,EAAE,GAAG,GAAG,IAAI;AAAA,QAC/C,GAAGE;AAAA,QACH,eAAe,SAAS,QAAQ;AAAA,QAChC,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,CAAC,UAAU,cAAc,QAAQ;AAAA,EACnC;AAEA,QAAM,WAAW,MAAM,aAAa,EACjC,KAAK,CAAC,EACN,IAAI,CAAC,GAAG,UAAU;AACjB,UAAMF,SAAQ,QAAQ;AAEtB,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO,UAAU,OAAOA,MAAK;AAAA,QAC7B,OAAO,UAAU,IAAI,oBAAoB,IAAI;AAAA,QAC7C,OAAOA;AAAA;AAAA,MAHFA;AAAA,IAIP;AAAA,EAEJ,CAAC;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["hoveredValue","value","roundedValue","props"]}