# VelvetKit

## Sidebar (`UI::Sidebar`)

A collapsible application sidebar with groups and menus.

Add it: `bin/rails generate velvetkit:add sidebar`

Also adds: `icon`, `spinner`, `button`

Stimulus: `ui--sidebar`

### Arguments

```ruby
UI::Sidebar.new(collapsible: :none, variant: :sidebar, side: :left, state: :expanded, **system_args)
```

Slots: `with_sidebar_header`, `with_sidebar_content`, `with_sidebar_footer`, `with_sidebar_group`, `with_label`, `with_menu`, `with_menu_item`, `with_menu_button`

### Example: Default

```erb
<% brand = render("ui/sidebar_preview/brand") %>

<% main = capture do %>
  <p class="text-muted-foreground">Main content area next to the sidebar.</p>
<% end %>

<%= render "ui/sidebar_preview/shell",
      sidebar_id: "preview-sidebar-default",
      brand: brand,
      main: main,
      groups: [
        { label: "Applications", items: [
          { icon: "layout-dashboard", text: "Dashboard", active: true, icon_class: "stroke-orange-500" },
          { icon: "users",           text: "Team",                icon_class: "stroke-purple-500" },
          { icon: "file-text",       text: "Reports" }
        ] }
      ] %>
```

### Example: With Groups

```erb
<% brand = render("ui/sidebar_preview/brand") %>

<% main = capture do %>
  <p class="text-muted-foreground">Two labelled groups sharing one sidebar.</p>
<% end %>

<%= render "ui/sidebar_preview/shell",
      sidebar_id: "preview-sidebar-groups",
      brand: brand,
      main: main,
      groups: [
        { label: "Navigation", items: [
          { icon: "home",             text: "Home", active: true },
          { icon: "layout-dashboard", text: "Dashboard" },
          { icon: "mail",             text: "Messages" }
        ] },
        { label: "Settings", items: [
          { icon: "settings", text: "General" },
          { icon: "shield",   text: "Security" },
          { icon: "bell",     text: "Notifications" }
        ] }
      ] %>
```

### Example: With Dropdown Header

```erb
<% brand = capture do %>
  <%= render(UI::Menu.new) do |menu| %>
    <% menu.with_trigger(variant: :ghost, class: "w-full justify-start px-2 h-auto py-1.5 group-data-[collapsible=icon]:size-8 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0") do %>
      <img src="/velvetkit/mark-light.svg" alt="" class="size-8 max-w-none shrink-0 object-contain dark:hidden"><img src="/velvetkit/mark-dark.svg" alt="" class="hidden size-8 max-w-none shrink-0 object-contain dark:block">
      <div class="grid flex-1 text-left text-sm leading-tight group-data-[collapsible=icon]:hidden">
        <span class="truncate font-medium">VelvetKit</span>
        <span class="truncate text-xs text-muted-foreground">Workspace</span>
      </div>
      <%= render(UI::Icon.new(name: "chevrons-up-down", size: :sm, class: "ml-auto group-data-[collapsible=icon]:hidden")) %>
    <% end %>
    <% menu.with_content(side: :right, align: :start, class: "w-56") do |content| %>
      <% content.with_label("Workspaces") %>
      <% content.with_item("VelvetKit") %>
      <% content.with_item("Marketing site") %>
      <% content.with_item("Internal tools") %>
    <% end %>
  <% end %>
<% end %>

<% main = capture do %>
  <p class="text-muted-foreground">Sidebar with a dropdown menu inside the header for context switching.</p>
<% end %>

<%= render "ui/sidebar_preview/shell",
      sidebar_id: "preview-sidebar-dropdown",
      brand: brand,
      main: main,
      groups: [
        { label: "Applications", items: [
          { icon: "layout-dashboard", text: "Dashboard", active: true, icon_class: "stroke-orange-500" },
          { icon: "users",            text: "Team",                icon_class: "stroke-purple-500" }
        ] }
      ] %>
```

### Example: Collapsible

```erb
<% brand = render("ui/sidebar_preview/brand") %>

<% main = capture do %>
  <div class="space-y-6">
    <div>
      <h2 class="text-lg font-semibold mb-2">Collapsible Sidebar</h2>
      <p class="text-sm text-muted-foreground">Click the toggle button to collapse the sidebar to icon-only mode. State is persisted in localStorage.</p>
    </div>
    <%= render(UI::Tabs.new(default_value: "overview")) do |tabs| %>
      <% tabs.with_list do |list| %>
        <% list.with_trigger(value: "overview") { "Overview" } %>
        <% list.with_trigger(value: "analytics") { "Analytics" } %>
        <% list.with_trigger(value: "reports") { "Reports" } %>
      <% end %>
      <% tabs.with_panel(value: "overview") do %>
        <div class="space-y-3">
          <p class="text-sm text-muted-foreground">Project overview with key metrics and recent activity.</p>
          <%= render(UI::Menu.new) do |menu| %>
            <% menu.with_trigger(variant: :outline, size: :sm) { "Actions" } %>
            <% menu.with_content do |content| %>
              <% content.with_label("Options") %>
              <% content.with_item("Edit project") %>
              <% content.with_item("Share") %>
              <% content.with_divider %>
              <% content.with_item("Delete", variant: :destructive) %>
            <% end %>
          <% end %>
        </div>
      <% end %>
      <% tabs.with_panel(value: "analytics") do %>
        <p class="text-sm text-muted-foreground">Traffic and conversion analytics over the last 30 days.</p>
      <% end %>
      <% tabs.with_panel(value: "reports") do %>
        <p class="text-sm text-muted-foreground">Generated reports and scheduled exports.</p>
      <% end %>
    <% end %>
  </div>
<% end %>

<%= render "ui/sidebar_preview/shell",
      sidebar_id: "preview-sidebar-collapsible",
      collapsible: :icon,
      brand: brand,
      main: main,
      groups: [
        { label: "Platform", items: [
          { icon: "layout-dashboard", text: "Dashboard", active: true },
          { icon: "bar-chart-3",      text: "Analytics" },
          { icon: "folder-kanban",    text: "Projects" }
        ] },
        { label: "Account", items: [
          { icon: "user",        text: "Profile" },
          { icon: "credit-card", text: "Billing" }
        ] }
      ] %>
```
