I am making a reusable selector component using headlessUI ListBox that gives a drop down of array of objects, and lets the use select a choice. Here it is being used in main.
//listbox data
const people = [
{ name: "Wade Cooper" },
{ name: "Arlene Mccoy" },
{ name: "Devon Webb" },
{ name: "Tom Cook" },
{ name: "Tanya Fox" },
{ name: "Hellen Schmidt" },
];
const [selected, setSelected] = useState<string | object>("");
const keyValue = 0;
<ListBox
placeHolder={"Data"}
apps={people}
keyValue={keyValue}
selected={selected}
setSelected={setSelected}
/>
<p>{selected[Object.keys(selected)[keyValue]]}</p>
here is the code for the component
import { ChevronUpDownIcon, XMarkIcon } from "@heroicons/react/20/solid";
import { Listbox, Transition } from "@headlessui/react";
import { Fragment } from "react";
type Props = {
placeHolder: string;
apps: Array<Object>;
keyValue: number;
setSelected: (value: any) => void;
selected: object | string;
};
const ListBox = ({
placeHolder,
apps,
keyValue,
selected,
setSelected,
}: Props) => {
return (
<Listbox value={selected} onChange={setSelected}>
<div className="relative py-2 mt-1 ">
<span
className={`text-lg tracking-wide absolute -translate-y-4 ${
selected != ""
? "text-blue-300 scale-1 transition duration-[350ms]"
: "text-gray-400 scale-0 transition duration-[350ms]"
}`}
>
{placeHolder}
</span>
<Listbox.Button
className={`relative py-2 pr-10 text-left cursor-default w-60 border-b-4 focus:outline-none ${
selected != "" ? "border-b-blue-300" : "border-b-gray-400"
}`}
>
<span
className={`block truncate tracking-wide text-gray-400 text-lg ${
selected ? "text-white" : "text-gray-400"
}`}
>
{selected ? selected[Object.keys(selected)[keyValue]] : placeHolder}
</span>
<span
className={`absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none ${
selected != "" ? "hidden" : ""
}`}
>
<ChevronUpDownIcon
className="w-5 h-5 text-gray-400"
aria-hidden="true"
/>
</span>
<span
className={`absolute inset-y-0 right-0 flex items-center pr-2 ${
selected != "" ? "" : "hidden"
}`}
onClick={() => {
setSelected("");
}}
>
<XMarkIcon className="w-5 h-5 text-gray-400" aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div style={{ position: "relative", zIndex: 20 }}>
<Listbox.Options className="absolute py-1 mt-1 overflow-auto text-base rounded-md shadow-lg bg-white/90 w-60 h-44 z-100 max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none scrollable">
{apps.map((app, appIdx) => (
<Listbox.Option
key={appIdx}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-2 pr-4 ${
active ? "bg-white text-gray-900" : "text-gray-900"
}`
}
value={app}
>
{({ selected }) => (
<>
<span
className={`block truncate ${
selected
? "text-md tracking-wide"
: "text-md tracking-wide"
}`}
>
{app[Object.keys(app)[keyValue]]}
</span>
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</div>
</Transition>
</div>
</Listbox>
);
};
export default ListBox;
I keep getting the 7053 Error with this line in both main and the component. The keyValue variable should let the use change which key they want to use in the objects in the event the objects have more than one key.
selected[Object.keys(selected)[keyValue]]
const selected: string | object Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'string | object'. No index signature with a parameter of type 'string' was found on type 'string | object'.ts(7053)