import { XMarkMini } from "@medusajs/icons" import { Badge, clx } from "@medusajs/ui" import { AnimatePresence, motion } from "motion/react" import { FocusEvent, KeyboardEvent, forwardRef, useImperativeHandle, useRef, useState, } from "react" type ChipInputProps = { value?: string[] onChange?: (value: string[]) => void onBlur?: () => void name?: string disabled?: boolean allowDuplicates?: boolean showRemove?: boolean variant?: "base" | "contrast" placeholder?: string className?: string } export const ChipInput = forwardRef( ( { value, onChange, onBlur, disabled, name, showRemove = true, variant = "base", allowDuplicates = false, placeholder, className, }, ref ) => { const innerRef = useRef(null) const isControlledRef = useRef(typeof value !== "undefined") const isControlled = isControlledRef.current const [uncontrolledValue, setUncontrolledValue] = useState([]) useImperativeHandle( ref, () => innerRef.current ) const [duplicateIndex, setDuplicateIndex] = useState(null) const chips = isControlled ? (value as string[]) : uncontrolledValue const handleAddChip = (chip: string) => { const cleanValue = chip.trim() if (!cleanValue) { return } if (!allowDuplicates && chips.includes(cleanValue)) { setDuplicateIndex(chips.indexOf(cleanValue)) setTimeout(() => { setDuplicateIndex(null) }, 300) return } onChange?.([...chips, cleanValue]) if (!isControlled) { setUncontrolledValue([...chips, cleanValue]) } } const handleRemoveChip = (chip: string) => { onChange?.(chips.filter((v) => v !== chip)) if (!isControlled) { setUncontrolledValue(chips.filter((v) => v !== chip)) } } const handleBlur = (e: FocusEvent) => { onBlur?.() if (e.target.value) { handleAddChip(e.target.value) e.target.value = "" } } const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Enter" || e.key === ",") { e.preventDefault() if (!innerRef.current?.value) { return } handleAddChip(innerRef.current?.value ?? "") innerRef.current.value = "" innerRef.current?.focus() } if (e.key === "Backspace" && innerRef.current?.value === "") { handleRemoveChip(chips[chips.length - 1]) } } // create a shake animation using framer motion const shake = { x: [0, -2, 2, -2, 2, 0], transition: { duration: 0.3 }, } return (
innerRef.current?.focus()} > {chips.map((v, index) => { return ( {v} {showRemove && ( )} ) })}
) } ) ChipInput.displayName = "ChipInput"