import type { ComponentPropsWithRef } from "react"; import { createContext, useContext, useId } from "react"; import { OTPInput, OTPInputContext } from "input-otp"; import { cx } from "@/utils/cx"; type PinInputContextType = { size: "sm" | "md" | "lg"; disabled: boolean; id: string; }; const PinInputContext = createContext({ size: "sm", id: "", disabled: false, }); export const usePinInputContext = () => { const context = useContext(PinInputContext); if (!context) { throw new Error("The 'usePinInputContext' hook must be used within a ''"); } return context; }; interface RootProps extends ComponentPropsWithRef<"div"> { size?: "sm" | "md" | "lg"; disabled?: boolean; } const Root = ({ className, size = "md", disabled = false, ...props }: RootProps) => { const id = useId(); return (
); }; Root.displayName = "Root"; type GroupProps = ComponentPropsWithRef & { width?: number; inputClassName?: string; }; const Group = ({ inputClassName, containerClassName, width, maxLength = 4, ...props }: GroupProps) => { const { id, size, disabled } = usePinInputContext(); const heights = { sm: "h-16.5", md: "h-20.5", lg: "h-24.5", }; return ( ); }; Group.displayName = "Group"; const sizes = { sm: "size-16 px-2 py-0.5 text-display-lg font-medium", md: "size-20 px-2 py-2.5 text-display-lg font-medium", lg: "size-24 px-2 py-3 text-display-xl font-medium", }; const Slot = ({ index, className, ...props }: ComponentPropsWithRef<"div"> & { index: number }) => { const { size, disabled } = usePinInputContext(); const { slots, isFocused } = useContext(OTPInputContext); const slot = slots[index]; return (
{slot?.char ? slot.char : slot?.hasFakeCaret ? : 0}
); }; Slot.displayName = "Slot"; const FakeCaret = ({ size = "md" }: { size?: "sm" | "md" | "lg" }) => { return (
); }; const Separator = (props: ComponentPropsWithRef<"p">) => { return (
); }; Separator.displayName = "Separator"; const Label = ({ className, ...props }: ComponentPropsWithRef<"label">) => { const { id } = usePinInputContext(); return