"use client"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@mdxui/primitives/command"; import { Popover, PopoverContent, PopoverTrigger, } from "@mdxui/primitives/popover"; import { ChevronsUpDown, X } from "lucide-react"; import type React from "react"; import { useState } from "react"; import { cx, focusInput } from "../lib/utils"; import { OnboardingButton } from "../primitives/onboarding-button"; import { OnboardingCheckbox } from "../primitives/onboarding-checkbox"; import { OnboardingLabel } from "../primitives/onboarding-label"; export interface MultiSelectOption { value: string; label: string; disabled?: boolean; } export interface MultiSelectField { /** Unique identifier for the field */ id: string; /** Label displayed above the multi-select */ label: string; /** Placeholder text when no values selected */ placeholder?: string; /** Search input placeholder */ searchPlaceholder?: string; /** Message shown when no options match search */ emptyMessage?: string; /** Optional description/helper text */ description?: string; /** Whether at least one value is required */ required?: boolean; /** Minimum number of selections required */ minSelections?: number; /** Maximum number of selections allowed */ maxSelections?: number; /** Available options */ options: MultiSelectOption[]; /** Default selected values */ defaultValue?: string[]; } export interface MultiSelectStepProps { /** Title displayed at the top of the step */ title?: string; /** Description text below the title */ description?: string; /** Array of multi-select fields */ fields: MultiSelectField[]; /** Called when any value changes */ onValuesChange?: (values: Record) => void; /** Called when the user submits the form */ onSubmit: (values: Record) => void | Promise; /** Text for the submit button */ submitText?: string; /** Text shown while submitting */ loadingText?: string; /** Optional back button config */ backButton?: { text: string; onClick: () => void; }; } function MultiSelectInput({ field, value, onChange, }: { field: MultiSelectField; value: string[]; onChange: (value: string[]) => void; }) { const [open, setOpen] = useState(false); const handleSelect = (optionValue: string) => { if (value.includes(optionValue)) { onChange(value.filter((v) => v !== optionValue)); } else { if (field.maxSelections && value.length >= field.maxSelections) { return; } onChange([...value, optionValue]); } }; const handleRemove = (optionValue: string, e: React.MouseEvent) => { e.stopPropagation(); onChange(value.filter((v) => v !== optionValue)); }; return ( {field.emptyMessage || "No items found."} {field.options.map((option) => { const isSelected = value.includes(option.value); const isDisabled = option.disabled || (!isSelected && !!field.maxSelections && value.length >= field.maxSelections); return ( handleSelect(option.value)} className="cursor-pointer" > {option.label} ); })} ); } export function MultiSelectStep({ title = "Select your options", description = "Choose the options that apply.", fields, onValuesChange, onSubmit, submitText = "Continue", loadingText = "Submitting...", backButton, }: MultiSelectStepProps) { const [values, setValues] = useState>(() => { const initial: Record = {}; fields.forEach((field) => { initial[field.id] = field.defaultValue ?? []; }); return initial; }); const [loading, setLoading] = useState(false); const handleChange = (fieldId: string, value: string[]) => { const newValues = { ...values, [fieldId]: value }; setValues(newValues); onValuesChange?.(newValues); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); try { await onSubmit(values); } finally { setLoading(false); } }; const isValid = fields.every((field) => { const fieldValue = values[field.id]; if (field.required && fieldValue.length === 0) return false; if (field.minSelections && fieldValue.length < field.minSelections) return false; return true; }); return (

{title}

{description}

{fields.map((field, index) => (
{field.label} {field.required && ( * )} {field.description && (

{field.description}

)} handleChange(field.id, value)} /> {field.minSelections && values[field.id].length < field.minSelections && (

Select at least {field.minSelections} option {field.minSelections > 1 ? "s" : ""}

)} {field.maxSelections && (

{values[field.id].length}/{field.maxSelections} selected

)}
))}
{backButton && ( {backButton.text} )} {loading ? loadingText : submitText}
); }