Collapsiblestandard
하나의 영역을 펼치고 접는 접이식(collapsible)입니다. shadcn base-nova 의 원본을 그대로 담은 아이템이며 Base UI `Collapsible` 을 `data-slot` 만 붙여 감쌉니다. 스타일은 입히지 않습니다. 여러 항목 중 고르는 목록은 `accordion` 을 씁니다.
설치
pnpm
pnpm dlx shadcn@4.23.0 add @uikit/collapsibleDefault
collapsible-example.tsx
"use client"
import * as React from "react"
import { Example, ExampleWrapper,} from "@/components/example"import { Button } from "@/components/ui/button"import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card"import { Collapsible, CollapsibleContent, CollapsibleTrigger,} from "@/components/ui/collapsible"import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"import { Input } from "@/components/ui/input"import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"import { ChevronRightIcon, FileIcon, FolderIcon, MaximizeIcon, MinimizeIcon } from "lucide-react"
export default function CollapsibleExample() { return ( <ExampleWrapper> <CollapsibleFileTree /> <CollapsibleSettings /> </ExampleWrapper> )}
type FileTreeItem = { name: string } | { name: string; items: FileTreeItem[] }
function CollapsibleFileTree() { const fileTree: FileTreeItem[] = [ { name: "components", items: [ { name: "ui", items: [ { name: "button.tsx" }, { name: "card.tsx" }, { name: "dialog.tsx" }, { name: "input.tsx" }, { name: "select.tsx" }, { name: "table.tsx" }, ], }, { name: "login-form.tsx" }, { name: "register-form.tsx" }, ], }, { name: "lib", items: [{ name: "utils.ts" }, { name: "cn.ts" }, { name: "api.ts" }], }, { name: "hooks", items: [ { name: "use-media-query.ts" }, { name: "use-debounce.ts" }, { name: "use-local-storage.ts" }, ], }, { name: "types", items: [{ name: "index.d.ts" }, { name: "api.d.ts" }], }, { name: "public", items: [ { name: "favicon.ico" }, { name: "logo.svg" }, { name: "images" }, ], }, { name: "app.tsx" }, { name: "layout.tsx" }, { name: "globals.css" }, { name: "package.json" }, { name: "tsconfig.json" }, { name: "README.md" }, { name: ".gitignore" }, ]
const renderItem = (fileItem: FileTreeItem) => { if ("items" in fileItem) { return ( <Collapsible key={fileItem.name}> <CollapsibleTrigger render={ <Button variant="ghost" size="sm" className="group w-full justify-start transition-none hover:bg-accent hover:text-accent-foreground" /> } > <ChevronRightIcon className="transition-transform group-data-[state=open]:rotate-90" /> <FolderIcon /> {fileItem.name} </CollapsibleTrigger> <CollapsibleContent className="mt-1 ml-5 style-lyra:ml-4"> <div className="flex flex-col gap-1"> {fileItem.items.map((child) => renderItem(child))} </div> </CollapsibleContent> </Collapsible> ) } return ( <Button key={fileItem.name} variant="link" size="sm" className="w-full justify-start gap-2 text-foreground" > <FileIcon /> <span>{fileItem.name}</span> </Button> ) }
return ( <Example title="File Tree" className="items-center"> <Card className="mx-auto w-full max-w-[16rem] gap-2" size="sm"> <CardHeader> <Tabs defaultValue="explorer"> <TabsList className="w-full"> <TabsTrigger value="explorer">Explorer</TabsTrigger> <TabsTrigger value="settings">Outline</TabsTrigger> </TabsList> </Tabs> </CardHeader> <CardContent> <div className="flex flex-col gap-1"> {fileTree.map((item) => renderItem(item))} </div> </CardContent> </Card> </Example> )}
function CollapsibleSettings() { const [isOpen, setIsOpen] = React.useState(false)
return ( <Example title="Settings" className="items-center"> <Card className="mx-auto w-full max-w-xs" size="sm"> <CardHeader> <CardTitle>Radius</CardTitle> <CardDescription> Set the corner radius of the element. </CardDescription> </CardHeader> <CardContent> <Collapsible open={isOpen} onOpenChange={setIsOpen} className="flex items-start gap-2" > <FieldGroup className="grid w-full grid-cols-2 gap-2"> <Field> <FieldLabel htmlFor="radius-x" className="sr-only"> Radius X </FieldLabel> <Input id="radius" placeholder="0" defaultValue={0} /> </Field> <Field> <FieldLabel htmlFor="radius-y" className="sr-only"> Radius Y </FieldLabel> <Input id="radius" placeholder="0" defaultValue={0} /> </Field> <CollapsibleContent className="col-span-full grid grid-cols-subgrid gap-2"> <Field> <FieldLabel htmlFor="radius-x" className="sr-only"> Radius X </FieldLabel> <Input id="radius" placeholder="0" defaultValue={0} /> </Field> <Field> <FieldLabel htmlFor="radius-y" className="sr-only"> Radius Y </FieldLabel> <Input id="radius" placeholder="0" defaultValue={0} /> </Field> </CollapsibleContent> </FieldGroup> <CollapsibleTrigger aria-label="Toggle details" render={<Button variant="outline" size="icon" />} > {isOpen ? ( <MinimizeIcon /> ) : ( <MaximizeIcon /> )} </CollapsibleTrigger> </Collapsible> </CardContent> </Card> </Example> )}