{"version":3,"file":"rating.mjs","names":[],"sources":["../../../../package/common/rating/rating.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faStar } from '@fortawesome/free-solid-svg-icons';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { RatingProps } from './rating.types';\n\nimport styles from './rating.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\nconst DRAG_THRESHOLD = 4;\n\nfunction clampValue(value: number, max: number): number {\n  return Math.max(0, Math.min(max, value));\n}\n\nfunction Rating(props: RatingProps): ReactElement {\n  const {\n    value: controlledValue,\n    defaultValue = 0,\n    onChange = () => {},\n    max = 5,\n    allowHalf = false,\n    allowClear = true,\n    readOnly = false,\n    disabled = false,\n    icon = faStar,\n    'aria-label': ariaLabel = 'Rating',\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Rating', props);\n\n  const isControlled = controlledValue !== undefined;\n  const [internalValue, setInternalValue] = useState(defaultValue);\n  const value = clampValue(isControlled ? controlledValue! : internalValue, max);\n\n  const [hoverValue, setHoverValue] = useState<number | null>(null);\n  const isInteractive = !readOnly && !disabled;\n  const displayValue = hoverValue ?? value;\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const valueBeforeGestureRef = useRef(value);\n  const draggingRef = useRef(false);\n  const gestureStartXRef = useRef(0);\n  const hasDraggedRef = useRef(false);\n\n  function commit(nextValue: number) {\n    const clamped = clampValue(nextValue, max);\n    if (!isControlled) setInternalValue(clamped);\n    onChange(clamped);\n  }\n\n  function getValueFromClientX(clientX: number): number | null {\n    const rect = rootRef.current?.getBoundingClientRect();\n    if (!rect || rect.width === 0) return null;\n    const fraction = clampValue((clientX - rect.left) / rect.width, 1);\n    const raw = fraction * max;\n    return allowHalf ? Math.round(raw * 2) / 2 : Math.ceil(raw);\n  }\n\n  function handlePointerDown(event: React.PointerEvent<HTMLDivElement>) {\n    if (!isInteractive) return;\n    event.currentTarget.setPointerCapture?.(event.pointerId);\n    draggingRef.current = true;\n    hasDraggedRef.current = false;\n    gestureStartXRef.current = event.clientX;\n    valueBeforeGestureRef.current = value;\n    const next = getValueFromClientX(event.clientX);\n    if (next !== null) setHoverValue(next);\n  }\n\n  function handlePointerMove(event: React.PointerEvent<HTMLDivElement>) {\n    if (!isInteractive) return;\n    const next = getValueFromClientX(event.clientX);\n    if (next === null) return;\n    if (draggingRef.current) {\n      event.preventDefault();\n      if (Math.abs(event.clientX - gestureStartXRef.current) > DRAG_THRESHOLD) {\n        hasDraggedRef.current = true;\n      }\n    }\n    setHoverValue(next);\n  }\n\n  function handlePointerUp(event: React.PointerEvent<HTMLDivElement>) {\n    if (!isInteractive || !draggingRef.current) return;\n    event.currentTarget.releasePointerCapture?.(event.pointerId);\n    draggingRef.current = false;\n    const next = getValueFromClientX(event.clientX) ?? hoverValue;\n    if (next !== null) {\n      const isClick = !hasDraggedRef.current;\n      commit(isClick && allowClear && next === valueBeforeGestureRef.current ? 0 : next);\n    }\n    setHoverValue(null);\n  }\n\n  function handlePointerLeave() {\n    if (!isInteractive || draggingRef.current) return;\n    setHoverValue(null);\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    const step = allowHalf ? 0.5 : 1;\n    if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {\n      event.preventDefault();\n      commit(value + step);\n    } else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {\n      event.preventDefault();\n      commit(value - step);\n    } else if (event.key === 'Home') {\n      event.preventDefault();\n      commit(0);\n    } else if (event.key === 'End') {\n      event.preventDefault();\n      commit(max);\n    }\n  }\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      role='slider'\n      aria-label={ariaLabel}\n      aria-valuemin={0}\n      aria-valuemax={max}\n      aria-valuenow={value}\n      aria-valuetext={`${value} out of ${max}`}\n      aria-readonly={readOnly || undefined}\n      aria-disabled={disabled || undefined}\n      tabIndex={isInteractive ? 0 : -1}\n      className={cx(\n        styles.rating,\n        disabled && styles.ratingDisabled,\n        readOnly && styles.ratingReadOnly,\n        classNames?.root,\n        className,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n      onKeyDown={isInteractive ? handleKeyDown : undefined}\n      onPointerDown={isInteractive ? handlePointerDown : undefined}\n      onPointerMove={isInteractive ? handlePointerMove : undefined}\n      onPointerUp={isInteractive ? handlePointerUp : undefined}\n      onPointerCancel={isInteractive ? handlePointerUp : undefined}\n      onPointerLeave={isInteractive ? handlePointerLeave : undefined}\n    >\n      {Array.from({ length: max }, (_, index) => {\n        const fillPercent = Math.round(Math.max(0, Math.min(1, displayValue - index)) * 100);\n\n        return (\n          <div key={index} className={cx(styles.ratingItem, classNames?.item)} style={slotStyles?.item}>\n            {renderIcon(icon, {\n              className: cx(styles.ratingIconEmpty, classNames?.iconEmpty),\n              style: slotStyles?.iconEmpty,\n            })}\n            <div className={styles.ratingIconFilledClip} style={{ width: `${fillPercent}%` }}>\n              {renderIcon(icon, {\n                className: cx(styles.ratingIconFilled, classNames?.iconFilled),\n                style: slotStyles?.iconFilled,\n              })}\n            </div>\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\nexport default Rating;\n"],"mappings":"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"}