|
|
@@ -0,0 +1,99 @@
|
|
|
+import { useState } from "react"
|
|
|
+import type { Meta, StoryObj } from "@storybook/react"
|
|
|
+import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
|
|
|
+
|
|
|
+import { cn } from "@/lib/utils"
|
|
|
+import {
|
|
|
+ Button,
|
|
|
+ Command,
|
|
|
+ CommandEmpty,
|
|
|
+ CommandGroup,
|
|
|
+ CommandInput,
|
|
|
+ CommandItem,
|
|
|
+ CommandList,
|
|
|
+ Popover,
|
|
|
+ PopoverContent,
|
|
|
+ PopoverTrigger,
|
|
|
+} from "@/components/ui"
|
|
|
+
|
|
|
+const meta = {
|
|
|
+ title: "@shadcn/Combobox",
|
|
|
+ component: Combobox,
|
|
|
+ parameters: { layout: "centered" },
|
|
|
+ tags: ["autodocs"],
|
|
|
+} satisfies Meta<typeof Combobox>
|
|
|
+
|
|
|
+export default meta
|
|
|
+
|
|
|
+type Story = StoryObj<typeof meta>
|
|
|
+
|
|
|
+export const Default: Story = {
|
|
|
+ name: "Combobox",
|
|
|
+ render: () => <Combobox />,
|
|
|
+}
|
|
|
+
|
|
|
+const frameworks = [
|
|
|
+ {
|
|
|
+ value: "next.js",
|
|
|
+ label: "Next.js",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: "sveltekit",
|
|
|
+ label: "SvelteKit",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: "nuxt.js",
|
|
|
+ label: "Nuxt.js",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: "remix",
|
|
|
+ label: "Remix",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: "astro",
|
|
|
+ label: "Astro",
|
|
|
+ },
|
|
|
+]
|
|
|
+
|
|
|
+function Combobox() {
|
|
|
+ const [open, setOpen] = useState(false)
|
|
|
+ const [value, setValue] = useState("")
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Popover open={open} onOpenChange={setOpen}>
|
|
|
+ <PopoverTrigger asChild>
|
|
|
+ <Button variant="secondary" role="combobox" aria-expanded={open} className="w-[200px] justify-between">
|
|
|
+ {value ? frameworks.find((framework) => framework.value === value)?.label : "Select framework..."}
|
|
|
+ <CaretSortIcon className="opacity-50" />
|
|
|
+ </Button>
|
|
|
+ </PopoverTrigger>
|
|
|
+ <PopoverContent className="w-[200px] p-0">
|
|
|
+ <Command>
|
|
|
+ <CommandInput placeholder="Search framework..." className="h-9" />
|
|
|
+ <CommandList>
|
|
|
+ <CommandEmpty>No framework found.</CommandEmpty>
|
|
|
+ <CommandGroup>
|
|
|
+ {frameworks.map((framework) => (
|
|
|
+ <CommandItem
|
|
|
+ key={framework.value}
|
|
|
+ value={framework.value}
|
|
|
+ onSelect={(currentValue) => {
|
|
|
+ setValue(currentValue === value ? "" : currentValue)
|
|
|
+ setOpen(false)
|
|
|
+ }}>
|
|
|
+ {framework.label}
|
|
|
+ <CheckIcon
|
|
|
+ className={cn(
|
|
|
+ "ml-auto",
|
|
|
+ value === framework.value ? "opacity-100" : "opacity-0",
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ </CommandItem>
|
|
|
+ ))}
|
|
|
+ </CommandGroup>
|
|
|
+ </CommandList>
|
|
|
+ </Command>
|
|
|
+ </PopoverContent>
|
|
|
+ </Popover>
|
|
|
+ )
|
|
|
+}
|