Getting Started
Components
- Agent Status
- Agent Template Card
- API Key Card
- Audio Player
- Bar Visualizer
- Channel Status Card
- Chat Composer
- Code Editor
- Condition Builder
- Confirm Dialog
- Conversation
- Conversation Bar
- Dataset Card
- Document Status Badge
- Extraction Strategy Select
- File Icon
- File Upload
- Indexing Progress
- Live Waveform
- Matrix
- Message
- Mic Selector
- Model Diff View
- Model Selector
- Model Type Filter
- Node Catalog
- Option Editor
- Orb
- Output Variables View
- Package Card
- Prompt Editor
- Provider Status Card
- Radio Card
- Resource Card
- Resource Sidebar
- Response
- Run Node List
- Scrub Bar
- Segment Card
- Shimmering Text
- Speech Input
- Token Trend Chart
- Tool Call Card
- Transcript Viewer
- Usage Stat Card
- Variable Binding Editor
- Variable Selector
- Voice Button
- Voice Picker
- Waveform
- Workflow Node Card
- Workspace Selector
Selected: start.query
"use client"
import { useState } from "react"
import {
VariableSelector,
type VariableSelectorItem,
} from "@/components/ui/variable-selector"
const variables: VariableSelectorItem[] = [
{
id: "start.query",
group: "Start",
label: "User query",
path: "{{start.query}}",
type: "string",
description: "The original request submitted by the user.",
},
{
id: "retrieve.documents",
group: "Retrieve documents",
label: "Matched documents",
path: "{{retrieve.documents}}",
type: "array",
},
{
id: "score.confidence",
group: "Score",
label: "Confidence",
path: "{{score.confidence}}",
type: "number",
},
]
export function VariableSelectorDemo() {
const [value, setValue] = useState("start.query")
return (
<div className="w-full max-w-sm space-y-3">
<VariableSelector
variables={variables}
value={value}
onValueChange={setValue}
/>
<p className="text-muted-foreground text-sm">Selected: {value}</p>
</div>
)
}
Installation
pnpm dlx shadcn@latest add https://ui.zgi.ai/r/variable-selector.jsonpnpm dlx shadcn@latest add https://ui.zgi.ai/r/variable-selector.json
yarn dlx shadcn@latest add https://ui.zgi.ai/r/variable-selector.jsonComponent
"use client"
import * as React from "react"
import { CheckIcon, ChevronsUpDownIcon, SearchIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export interface VariableSelectorItem {
id: string
label: string
path?: string
type?: string
group?: string
description?: string
}
export interface VariableSelectorProps {
variables: VariableSelectorItem[]
value?: string
onValueChange?: (id: string, item: VariableSelectorItem) => void
placeholder?: string
searchPlaceholder?: string
emptyText?: string
disabled?: boolean
className?: string
contentClassName?: string
}
export function VariableSelector({
variables,
value,
onValueChange,
placeholder = "Select variable",
searchPlaceholder = "Search variables...",
emptyText = "No variables found.",
disabled = false,
className,
contentClassName,
}: VariableSelectorProps) {
const [open, setOpen] = React.useState(false)
const selected = variables.find((item) => item.id === value)
const groups = React.useMemo(() => {
const grouped = new Map<string, VariableSelectorItem[]>()
for (const variable of variables) {
const group = variable.group ?? "Variables"
grouped.set(group, [...(grouped.get(group) ?? []), variable])
}
return Array.from(grouped.entries())
}, [variables])
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
type="button"
variant="outline"
role="combobox"
aria-expanded={open}
disabled={disabled}
className={cn(
"h-10 w-full justify-between rounded-md px-3 font-normal",
!selected && "text-muted-foreground",
className
)}
>
<span className="min-w-0 truncate text-left">
{selected ? (selected.path ?? selected.label) : placeholder}
</span>
<ChevronsUpDownIcon className="text-muted-foreground size-4 shrink-0" />
</Button>
</PopoverTrigger>
<PopoverContent
align="start"
className={cn("w-[min(420px,calc(100vw-2rem))] p-0", contentClassName)}
>
<Command>
<div className="border-border flex items-center border-b px-3">
<SearchIcon className="text-muted-foreground mr-2 size-4 shrink-0" />
<CommandInput
placeholder={searchPlaceholder}
className="h-10 border-0 px-0 focus:ring-0"
/>
</div>
<CommandList className="max-h-80">
<CommandEmpty>{emptyText}</CommandEmpty>
{groups.map(([group, items]) => (
<CommandGroup key={group} heading={group}>
{items.map((item) => {
const isSelected = item.id === value
return (
<CommandItem
key={item.id}
value={`${item.label} ${item.path ?? ""} ${item.type ?? ""} ${
item.description ?? ""
}`}
onSelect={() => {
onValueChange?.(item.id, item)
setOpen(false)
}}
className="items-start gap-3 py-3"
>
<CheckIcon
className={cn(
"mt-0.5 size-4 shrink-0",
isSelected ? "opacity-100" : "opacity-0"
)}
/>
<span className="min-w-0 flex-1">
<span className="flex min-w-0 items-center gap-2">
<span className="truncate text-sm font-medium">
{item.label}
</span>
{item.type ? (
<Badge
variant="secondary"
className="rounded-sm px-1.5 py-0 text-[11px]"
>
{item.type}
</Badge>
) : null}
</span>
{item.path ? (
<code className="text-muted-foreground mt-1 block truncate font-mono text-xs">
{item.path}
</code>
) : null}
{item.description ? (
<span className="text-muted-foreground mt-1 block text-xs leading-5">
{item.description}
</span>
) : null}
</span>
</CommandItem>
)
})}
</CommandGroup>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
Usage
import { VariableSelector } from "@/components/ui/variable-selector"
export function Example() {
return (
<VariableSelector
variables={[{ id: "start.query", label: "User query", path: "{{start.query}}" }]}
onValueChange={console.log}
/>
)
}Props
| Prop | Type | Default |
|---|---|---|
| variables | VariableSelectorItem[] | required |
| value | string | - |
| onValueChange | (id, item) => void | - |
| placeholder | string | "Select variable" |
| disabled | boolean | false |
Deploy and Scale Agents with ZGI
ZGI delivers the infrastructure and developer experience you need to ship reliable audio & agent applications at scale.
Talk to an expert