/* Copyright 2026 Marimo. All rights reserved. */ import { type JSX, useId } from "react"; import { z } from "zod"; import { NativeSelect } from "../../components/ui/native-select"; import { cn } from "../../utils/cn"; import type { IPlugin, IPluginProps } from "../types"; import { Labeled } from "./common/labeled"; import { SearchableSelect } from "./SearchableSelect"; interface Data { label: string | null; options: string[]; allowSelectNone: boolean; fullWidth: boolean; searchable: boolean; } export class DropdownPlugin implements IPlugin { tagName = "marimo-dropdown"; validator = z.object({ initialValue: z.array(z.string()), label: z.string().nullable(), options: z.array(z.string()), allowSelectNone: z.boolean(), fullWidth: z.boolean().default(false), searchable: z.boolean().default(false), }); render(props: IPluginProps): JSX.Element { if (props.data.searchable) { const value = props.value.length > 0 ? props.value[0] : null; const setValue = (newValue: string | null) => props.setValue(newValue ? [newValue] : []); return ( ); } return ( ); } } type T = string[]; /** * Arguments for a dropdown menu * * @param options - text labels for each dropdown option * @param allowSelectNone - whether to have a null option * @param label - an optional label for the dropdown * @param value - an array of options that is selected by default * @param setValue - selects a radio option */ interface DropdownProps extends Data { value: T; setValue: (value: T) => void; } const EMPTY_VALUE = "--"; const Dropdown = (props: DropdownProps): JSX.Element => { const { label, options, value, setValue, allowSelectNone, fullWidth } = props; const id = useId(); const defaultValue = allowSelectNone ? EMPTY_VALUE : options[0]; const singleValue = value.length === 0 ? defaultValue : value[0]; return ( { const newValue = e.target.value; if (newValue === EMPTY_VALUE) { setValue([]); } else { setValue([newValue]); } }} className={cn({ "w-full": fullWidth, })} value={singleValue} id={id} > {allowSelectNone ? ( ) : null} {options.map((option) => ( ))} ); };