Forms
TypedOptionPicker
A searchable, grouped picker for long option lists (Popover + Command), in single and multi versions.
Guidelines#
Use it for choices with more than ~15 options or with groups: buyer roles, industries, sender accounts. Short lists belong in a Select.
What you provide: value (or values for TypedMultiOptionPicker), options as { value, label, group?, description? }, onChange, ariaLabel, id, and a placeholder (default "Choose…"). Ungrouped options fall under "Choices".
Anatomy: a full-width 44px trigger (min-h-11), the popover as wide as the trigger. The multi version lists up to two labels, then summarises as "N selected".
Facts#
| Group | Forms |
|---|---|
| Product | Max |
| Global | window.DigitalCrew.TypedOptionPicker |
| Source | max-agent: src/components/typed-option-picker.tsx |
| Import in the app | import { TypedOptionPicker } from "@/components/typed-option-picker"; |
API#
TypeScript declarations emitted from src/components/typed-option-picker.tsx (the module also exports TypedOptionPicker, TypedMultiOptionPicker).
export type TypedOption = {
value: string;
label: string;
group?: string;
description?: string;
};
export declare function TypedOptionPicker({ value, options, onChange, ariaLabel, id, placeholder, }: {
value: string;
options: TypedOption[];
onChange: (value: string) => void;
ariaLabel: string;
id?: string;
placeholder?: string;
}): JSX.Element;
export declare function TypedMultiOptionPicker({ values, options, onChange, ariaLabel, id, placeholder, }: {
values: string[];
options: TypedOption[];
onChange: (values: string[]) => void;
ariaLabel: string;
id?: string;
placeholder?: string;
}): JSX.Element;Example#
The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.
<div id="root" class="p-6"></div>
<script>
(function () {
var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
var OPTIONS = [
{ value: "vp_sales", label: "VP Sales", group: "Revenue" },
{ value: "head_growth", label: "Head of Growth", group: "Revenue" },
{ value: "cfo", label: "CFO", group: "Finance", description: "Signs off budgets over 50k" },
{ value: "cto", label: "CTO", group: "Technology" }];
function App() {
var one = React.useState("vp_sales"), many = React.useState(["cfo", "cto", "head_growth"]);
return h("div", { className: "grid grid-cols-2 gap-8" },
h("div", { className: "flex flex-col gap-2" }, h(DC.Label, null, "Buyer role"),
h(DC.TypedOptionPicker, { value: one[0], options: OPTIONS, onChange: one[1], ariaLabel: "Buyer role" })),
h("div", { className: "flex flex-col gap-2" }, h(DC.Label, null, "Also involve"),
h(DC.TypedMultiOptionPicker, { values: many[0], options: OPTIONS, onChange: many[1], ariaLabel: "Also involve" })));
}
ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>