GitHub

Select Model


Install

Install Dependencies

pnpm add @base-ui-components/react

Copy and paste the following code into your project.

import type React from "react";
import { motion } from "motion/react";
import { Select } from "@base-ui-components/react";
import { Check, ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
 
const exampleData = [
	"ChatGPT 4o",
	"ChatGPT 4o-mini",
	"ChatGPT 4-turbo",
	"ChatGPT o1",
	"ChatGPT o1-mini",
];
 
function generateLetters(value: string) {
	const letterCount = new Map<string, number>();
	return value.split("").map((letter) => {
		const count = letterCount.get(letter) || 0;
		const key = `${letter}-${count}`;
		letterCount.set(letter, count + 1);
		return { letter, key };
	});
}
 
function SelectModel() {
	return (
		<Select.Root defaultValue={exampleData[0]} alignItemToTrigger={false}>
			<Select.Trigger className="relative flex h-10 items-center justify-between gap-3 text-sm rounded-md pr-3 pl-3.5 select-none hover:bg-neutral-100 focus-visible:outline-none focus-visible:-outline-offset-1 active:bg-neutral-100 data-[popup-open]:bg-neutral-100">
				<Select.Value placeholder="Select Model">
					{(value) => (
						<>
							<span className="sr-only">{value}</span>
							{generateLetters(value).map(({ letter, key }) => (
								<motion.span
									aria-hidden
									key={key}
									layoutId={key}
									className="inline-block"
									transition={{ type: "spring", bounce: 0.35 }}
								>
									{letter.trim() || "\u00A0"}
								</motion.span>
							))}
						</>
					)}
				</Select.Value>
				<Select.Icon className="flex">
					<ChevronDown className="size-4" />
				</Select.Icon>
			</Select.Trigger>
			<Select.Portal>
				<Select.Positioner className="outline-none" sideOffset={8}>
					<Select.Popup className="group origin-[var(--transform-origin)] rounded-md bg-white py-1 shadow-lg outline outline-neutral-200 transition-[transform,scale,opacity] data-[ending-style]:scale-100 data-[ending-style]:opacity-100 data-[ending-style]:transition-none data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[side=none]:data-[starting-style]:scale-100 data-[side=none]:data-[starting-style]:opacity-100 data-[side=none]:data-[starting-style]:transition-none">
						{exampleData.map((item) => (
							<SelectItem value={item} key={item}>
								{item}
							</SelectItem>
						))}
					</Select.Popup>
				</Select.Positioner>
			</Select.Portal>
		</Select.Root>
	);
}
 
function SelectItem({
	className,
	children,
	...props
}: React.ComponentPropsWithRef<typeof Select.Item>) {
	return (
		<Select.Item
			className={cn(
				"grid min-w-[var(--anchor-width)] cursor-default grid-cols-[1fr_0.75rem] items-center gap-2 py-2 pr-4 pl-2.5 text-sm leading-4 outline-none select-none group-data-[side=none]:min-w-[calc(var(--anchor-width)+1rem)] group-data-[side=none]:pr-12 group-data-[side=none]:text-base group-data-[side=none]:leading-4 data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-gray-50 data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:rounded-sm data-[highlighted]:before:bg-gray-900",
				className,
			)}
			{...props}
		>
			<Select.ItemText>{children}</Select.ItemText>
			<Select.ItemIndicator>
				<Check className="size-3" />
			</Select.ItemIndicator>
		</Select.Item>
	);
}
 
export { SelectModel };