File size: 4,332 Bytes
fc9bd9f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | import React, { useState } from "react";
import { Plus, Check, ChevronsUpDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { cn } from "@/lib/utils";
interface RobotSelectorProps {
selectedName: string | null;
availableNames: string[];
onSelect: (name: string) => void;
onCreateNew: (name: string) => Promise<boolean>;
isLoading: boolean;
}
const RobotSelector: React.FC<RobotSelectorProps> = ({
selectedName,
availableNames,
onSelect,
onCreateNew,
isLoading,
}) => {
const [open, setOpen] = useState(false);
const [query, setQuery] = useState("");
const trimmed = query.trim();
const matchesExisting = availableNames.some(
(n) => n.toLowerCase() === trimmed.toLowerCase()
);
const canCreate = trimmed.length > 0 && !matchesExisting;
const createDisabled = !canCreate;
const createLabel = matchesExisting
? "Already exists"
: trimmed === ""
? "Create new robot…"
: `Create "${trimmed}"`;
const reset = () => {
setQuery("");
setOpen(false);
};
const handlePickExisting = (name: string) => {
onSelect(name);
reset();
};
const handleCreate = async () => {
if (!canCreate) return;
const ok = await onCreateNew(trimmed);
if (ok) reset();
};
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
disabled={isLoading}
className="w-full justify-between bg-gray-900 border-gray-700 text-white hover:bg-gray-700 hover:text-white font-normal"
>
<span className={cn("truncate", selectedName ? "" : "text-gray-400")}>
{isLoading
? "Loading..."
: selectedName ?? "Select a robot or type a new name"}
</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0 bg-gray-800 border-gray-700 text-white"
style={{ width: "var(--radix-popover-trigger-width)" }}
align="start"
>
<Command className="bg-gray-800">
<CommandInput
placeholder="Search or type new name..."
value={query}
onValueChange={setQuery}
onKeyDown={(e) => {
if (e.key === "Enter" && canCreate) {
e.preventDefault();
handleCreate();
}
}}
className="text-white"
/>
<CommandList>
{availableNames.length === 0 && (
<CommandEmpty className="py-4 text-sm text-gray-400 text-center">
No robots yet. Type a name to create one.
</CommandEmpty>
)}
{availableNames.length > 0 && (
<CommandGroup heading="Existing">
{availableNames.map((name) => (
<CommandItem
key={name}
value={name}
onSelect={() => handlePickExisting(name)}
className="text-white aria-selected:bg-gray-700"
>
<Check
className={cn(
"mr-2 h-4 w-4",
selectedName === name ? "opacity-100" : "opacity-0"
)}
/>
{name}
</CommandItem>
))}
</CommandGroup>
)}
</CommandList>
<button
type="button"
onClick={handleCreate}
disabled={createDisabled}
className="flex w-full items-center gap-2 border-t border-gray-700 px-3 py-2 text-sm text-white hover:bg-gray-700 disabled:cursor-not-allowed disabled:text-gray-500 disabled:hover:bg-transparent"
>
<Plus className="h-4 w-4" />
{createLabel}
</button>
</Command>
</PopoverContent>
</Popover>
);
};
export default RobotSelector;
|