import clsx from "clsx"; import { ChangeEvent, ComponentPropsWithRef, RefObject, useRef, useState } from "react"; import { useMergeRefs } from "../../../hooks"; import { Button } from "../../button"; export type MappedStarOption = { value: string; label: string; }; export interface MappedStarsProps extends Omit< ComponentPropsWithRef<"input">, "defaultValue" | "onChange" | "value" > { onChange?: (value: string | null) => void; value?: string | null; defaultValue?: string | null; options: MappedStarOption[]; showLabel?: boolean; labelClassName?: string; ref?: RefObject; } export function MappedStars({ onChange, value: controlledValue, defaultValue, options, showLabel = false, labelClassName, required = false, ref, ...rest }: MappedStarsProps) { const isControlled = typeof controlledValue !== "undefined"; const [unControlledValue, setUnControlledValue] = useState(defaultValue); const [hoveredRangeValue, setHoveredRangeValue] = useState(-1); const value = isControlled ? controlledValue : unControlledValue; if (typeof value === "undefined") { throw Error("you have to define either value or defaultValue"); } const inputTextRef = useRef(null!); const combinedRef = useMergeRefs([inputTextRef, ref]); const inputRangeRef = useRef(null!); const rangeValue = options.findIndex((o) => o.value === value); function handleClickStar(nextRangeValue: number) { const nextValue = options[nextRangeValue].value; inputTextRef.current.value = nextValue.toString(); if (!isControlled) { inputRangeRef.current.value = nextValue.toString(); setUnControlledValue(nextValue); } if (onChange) { onChange(nextValue); } } /** * for a11y */ function handleInputChange(event: ChangeEvent) { const nextValue = options[event.target.valueAsNumber].value; inputTextRef.current.value = nextValue.toString(); if (!isControlled) { setUnControlledValue(nextValue); } if (onChange) { onChange(nextValue); } } function handleReset() { if (!isControlled) { setUnControlledValue(null); } onChange?.(null); } const currentValue = hoveredRangeValue !== -1 ? hoveredRangeValue : rangeValue; return (
{Array.from({ length: options.length }).map((_, i) => { const hasChanged = hoveredRangeValue !== rangeValue; return ( setHoveredRangeValue(i)} onMouseOut={() => setHoveredRangeValue(-1)} onClick={() => handleClickStar(i)} className={clsx( "cursor-pointer", i <= currentValue ? hasChanged && hoveredRangeValue !== -1 ? "fe-star text-yellow-3" : "fe-star text-yellow-4" : "fe-star-empty text-gray-2", )} /> ); })} {!required && rangeValue !== -1 && ( )} {showLabel && currentValue !== -1 && ( {options[currentValue].label} )} {/* {rangeValue === null ? "null" : (rangeValue ?? "undefined")}| {value === null ? "null" : (value ?? "undefined")} */}
); }