Collapsible
An interactive component which expands/collapses a panel.
Order #4189
StatusShipped
use leptos::prelude::*; use singlestage::*; #[component] pub fn CollapsibleExample() -> impl IntoView { let open = RwSignal::new(false); view! { <Collapsible open class="flex w-[350px] flex-col gap-2"> <div class="flex items-center justify-between gap-4 px-4"> <h4 class="text-sm font-semibold">"Order #4189"</h4> <CollapsibleTrigger> <Button variant="ghost" size="icon" class="size-8"> {icon!(icondata::LuChevronsUpDown)} <span class="sr-only">"Toggle details"</span> </Button> </CollapsibleTrigger> </div> <div class="flex items-center justify-between rounded-md border px-4 py-2 text-sm"> <span class="text-(--muted-foreground)">"Status"</span> <span class="font-medium">"Shipped"</span> </div> <CollapsibleContent class="flex flex-col gap-2"> <div class="rounded-md border px-4 py-2 text-sm"> <p class="font-medium">"Shipping address"</p> <p class="text-(--muted-foreground)">"100 Market St, San Francisco"</p> </div> <div class="rounded-md border px-4 py-2 text-sm"> <p class="font-medium">"Items"</p> <p class="text-(--muted-foreground)">"2x Studio Headphones"</p> </div> </CollapsibleContent> </Collapsible> } }
Basic
use leptos::prelude::*; use singlestage::*; #[component] pub fn CollapsibleBasicExample() -> impl IntoView { let open = RwSignal::new(false); view! { <Card class="mx-auto w-full max-w-sm"> <CardContent> <Collapsible open attr:class=move || { format!( "rounded-md{}", match open.get() { true => " bg-(--muted)", false => "", }, ) } > <CollapsibleTrigger> <Button variant="ghost" class="group w-full"> "Product details" <span class=move || { format!( "ml-auto{}", match open.get() { true => " rotate-180", false => "", }, ) }>{icon!(icondata::LuChevronDown)}</span> </Button> </CollapsibleTrigger> <CollapsibleContent class="flex flex-col items-start gap-2 p-2.5 pt-0 text-sm"> <div> "This panel can be expanded or collapsed to reveal additional content." </div> <Button size="xs">"Learn More"</Button> </CollapsibleContent> </Collapsible> </CardContent> </Card> } }
Settings Panel
Use a trigger button to reveal additional settings.
Radius
Set the corner radius of the element.
use leptos::prelude::*; use singlestage::*; #[component] pub fn CollapsibleSettingsPanelExample() -> impl IntoView { let open = RwSignal::new(false); view! { <Card class="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 class="flex items-start gap-2"> <FieldGroup class="grid w-full grid-cols-2 gap-2"> <Field> <FieldLabel class="sr-only">"Radius X"</FieldLabel> <Input placeholder="0" /> </Field> <Field> <FieldLabel class="sr-only">"Radius Y"</FieldLabel> <Input placeholder="0" /> </Field> <CollapsibleContent class="col-span-full grid grid-cols-subgrid gap-2"> <Field> <FieldLabel class="sr-only">"Radius X"</FieldLabel> <Input placeholder="0" /> </Field> <Field> <FieldLabel class="sr-only">"Radius Y"</FieldLabel> <Input placeholder="0" /> </Field> </CollapsibleContent> </FieldGroup> <CollapsibleTrigger> <Button variant="outline" size="icon"> <Show when=move || open.get() fallback=move || { icon!(icondata::LuMaximize) } > {icon!(icondata::LuMinimize)} </Show> </Button> </CollapsibleTrigger> </Collapsible> </CardContent> </Card> } }
File Tree
Use nested collapsibles to build a file tree.
use leptos::prelude::*; use singlestage::*; #[derive(Clone, Default)] pub struct FileTreeItem { name: String, items: Vec<FileTreeItem>, } #[component] pub fn RenderItem(file_item: FileTreeItem) -> impl IntoView { if !file_item.items.is_empty() { let open = RwSignal::new(false); view! { <Collapsible open> <CollapsibleTrigger> <Button variant="ghost" size="sm" class="group w-full justify-start transition-none hover:bg-accent hover:text-accent-foreground" > <span class=move || { format!( "p-0 transition-transform{}", match open.get() { true => " rotate-90", false => "", }, ) }>{icon!(icondata::LuChevronRight)}</span> {icon!(icondata::LuFolder)} {file_item.name} </Button> </CollapsibleTrigger> <CollapsibleContent class="mt-1 ml-6"> <div class="flex flex-col gap-1"> <For each=move || file_item.items.to_owned() key=|file_item| file_item.name.clone() let(file_item) > <RenderItem file_item /> </For> </div> </CollapsibleContent> </Collapsible> }.into_any() } else { view! { <Button variant="link" size="sm" class="w-full justify-start gap-2 text-foreground"> {icon!(icondata::LuFile)} <span>{file_item.name}</span> </Button> } .into_any() } } #[component] pub fn CollapsibleFileTreeExample() -> impl IntoView { let file_tree: Vec<FileTreeItem> = vec![ FileTreeItem { name: "components".to_string(), items: vec![ FileTreeItem { name: "ui".to_string(), items: vec![ FileTreeItem { name: "button.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "card.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "dialog.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "input.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "select.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "table.tsx".to_string(), ..Default::default() }, ], }, FileTreeItem { name: "login-form.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "register-form.tsx".to_string(), ..Default::default() }, ], }, FileTreeItem { name: "lib".to_string(), items: vec![ FileTreeItem { name: "utils.ts".to_string(), ..Default::default() }, FileTreeItem { name: "cn.ts".to_string(), ..Default::default() }, FileTreeItem { name: "api.ts".to_string(), ..Default::default() }, ], }, FileTreeItem { name: "hooks".to_string(), items: vec![ FileTreeItem { name: "use-media-query.ts".to_string(), ..Default::default() }, FileTreeItem { name: "use-debounce.ts".to_string(), ..Default::default() }, FileTreeItem { name: "use-local-storage.ts".to_string(), ..Default::default() }, ], }, FileTreeItem { name: "types".to_string(), items: vec![ FileTreeItem { name: "index.d.ts".to_string(), ..Default::default() }, FileTreeItem { name: "api.d.ts".to_string(), ..Default::default() }, ], }, FileTreeItem { name: "public".to_string(), items: vec![ FileTreeItem { name: "favicon.ico".to_string(), ..Default::default() }, FileTreeItem { name: "logo.svg".to_string(), ..Default::default() }, FileTreeItem { name: "images".to_string(), ..Default::default() }, ], }, FileTreeItem { name: "app.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "layout.tsx".to_string(), ..Default::default() }, FileTreeItem { name: "globals.css".to_string(), ..Default::default() }, FileTreeItem { name: "package.json".to_string(), ..Default::default() }, FileTreeItem { name: "tsconfig.json".to_string(), ..Default::default() }, FileTreeItem { name: "README.md".to_string(), ..Default::default() }, FileTreeItem { name: ".gitignore".to_string(), ..Default::default() }, ]; view! { <Card class="mx-auto w-full max-w-[16rem] gap-2" size="sm"> <CardHeader> <Tabs value="explorer"> <TabsList class="w-full"> <TabsTrigger value="explorer">"Explorer"</TabsTrigger> <TabsTrigger value="settings">"Outline"</TabsTrigger> </TabsList> </Tabs> </CardHeader> <CardContent> <div class="flex flex-col gap-1"> <For each=move || file_tree.to_owned() key=|file_item| file_item.name.clone() let(file_item) > <RenderItem file_item /> </For> </div> </CardContent> </Card> } }
Anatomy
Import all parts and piece them together.
use leptos::prelude::*; use singlestage::*; #[component] pub fn CollapsibleAnatomy() -> impl IntoView { view! { <Collapsible> <CollapsibleTrigger> <Button /> </CollapsibleTrigger> <CollapsibleContent /> </CollapsibleTrigger> } }
API Reference
Collapsible
Contains the collapsible trigger and content.
| Name | Type | Default | Description |
|---|---|---|---|
| open | bool | false | A reactive signal that is directly coupled to the open state of the collapsible. |
Implements global attributes.
CollapsibleTrigger
A context provider that wraps a button that opens the collapsible menu.