Menu

Change language

GitHub repo

Documentation Sidebar

blocklayout

Docs-style sidebar with a version switcher at the top, a search input below, and collapsible navigation groups. All sample data inlined so consumers edit routes in place.

Installation

$npx shadcn@latest add https://uipkge.dev/r/lit/sidebar-03.json
Manifest:https://uipkge.dev/r/lit/sidebar-03.jsonInstalls to:src/uip/blocks/sidebar-03/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

NavChild
interface NavChild {
  title: string
  url: string
  isActive?: boolean
}
NavItem
interface NavItem {
  title: string
  url: string
  items: NavChild[]
}
HTMLElementTagNameMap
interface HTMLElementTagNameMap {
    'uip-sidebar-03': UipSidebar03
  }
}

Files installed (1)

  • src/uip/blocks/sidebar-03/sidebar-03.ts8.2 kB
    import { LitElement, css, html } from 'lit'
    import { Check, ChevronRight, ChevronsUpDown, Search } from 'lucide'
    import { tailwind } from '../../lib/styles'
    import { ThemeController } from '../../lib/theme'
    import { icon } from '../../lib/icon'
    // Side-effect imports: register the composed elements for standalone use
    // (consumers who install only this block still get working primitives).
    import '../../components/dropdown-menu/dropdown-menu'
    import '../../components/collapsible/collapsible'
    import '../../components/input/input'
    import type { UipInput } from '../../components/input/input'
    import '../../components/label/label'
    import '../../components/sidebar/sidebar'
    
    // Sample data (React's `data`, inlined so consumers edit routes in place).
    const versions = ['1.0.1', '1.1.0-alpha', '2.0.0-beta1']
    
    interface NavChild {
      title: string
      url: string
      isActive?: boolean
    }
    
    interface NavItem {
      title: string
      url: string
      items: NavChild[]
    }
    
    const navMain: NavItem[] = [
      {
        title: 'Getting Started',
        url: '#',
        items: [
          { title: 'Installation', url: '#' },
          { title: 'Project Structure', url: '#' },
        ],
      },
      {
        title: 'Building Your Application',
        url: '#',
        items: [
          { title: 'Routing', url: '#' },
          { title: 'Data Fetching', url: '#', isActive: true },
          { title: 'Rendering', url: '#' },
          { title: 'Caching', url: '#' },
          { title: 'Styling', url: '#' },
          { title: 'Optimizing', url: '#' },
          { title: 'Configuring', url: '#' },
          { title: 'Testing', url: '#' },
          { title: 'Authentication', url: '#' },
          { title: 'Deploying', url: '#' },
          { title: 'Upgrading', url: '#' },
          { title: 'Examples', url: '#' },
        ],
      },
      {
        title: 'API Reference',
        url: '#',
        items: [
          { title: 'Components', url: '#' },
          { title: 'File Conventions', url: '#' },
          { title: 'Functions', url: '#' },
          { title: 'next.config.js Options', url: '#' },
          { title: 'CLI', url: '#' },
          { title: 'Edge Runtime', url: '#' },
        ],
      },
      {
        title: 'Architecture',
        url: '#',
        items: [
          { title: 'Accessibility', url: '#' },
          { title: 'Fast Refresh', url: '#' },
          { title: 'Next.js Compiler', url: '#' },
          { title: 'Supported Browsers', url: '#' },
          { title: 'Turbopack', url: '#' },
        ],
      },
      {
        title: 'Community',
        url: '#',
        items: [{ title: 'Contribution Guide', url: '#' }],
      },
    ]
    
    /**
     * <uip-sidebar-03> — the registry Sidebar03 block as a web component
     * (VersionSwitcher + SearchForm inlined — one block, one install unit).
     *
     * Class strings are React's verbatim. The version switcher keeps its
     * selected version in { state: true }; the search field reads like any
     * input. No props, no events: edit the markup in place.
     */
    export class UipSidebar03 extends LitElement {
      static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }
      // :host display can't be a utility class (the host has no template of its own).
      static styles = [tailwind, css`:host { display: block; }`]
    
      static properties = {
        selectedVersion: { state: true },
        search: { state: true },
      }
    
      private selectedVersion = versions[0]!
      private search = ''
    
      constructor() {
        super()
        new ThemeController(this)
      }
    
      connectedCallback() {
        super.connectedCallback()
        this.setAttribute('data-uipkge', '')
        this.setAttribute('data-slot', 'sidebar-03')
      }
    
      render() {
        return html`
          <uip-sidebar>
            <uip-sidebar-header>
              <uip-sidebar-menu>
                <uip-sidebar-menu-item>
                  <uip-dropdown-menu class="[&::part(content)]:min-w-56">
                    <uip-sidebar-menu-button
                      slot="trigger"
                      size="lg"
                      class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
                    >
                      <div class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
                        <svg viewBox="0 0 24 24" class="size-4" fill="currentColor" aria-hidden="true">
                          <rect x="2" y="2" width="9" height="9" rx="1.5" />
                          <rect x="13" y="2" width="9" height="9" rx="1.5" opacity="0.55" />
                          <rect x="2" y="13" width="9" height="9" rx="1.5" opacity="0.55" />
                          <rect x="13" y="13" width="9" height="9" rx="1.5" />
                        </svg>
                      </div>
                      <div class="flex flex-col gap-0.5 leading-none">
                        <span class="font-medium">uipkge</span>
                        <span>v${this.selectedVersion}</span>
                      </div>
                      ${icon(ChevronsUpDown, 'chevrons-up-down', 'ml-auto')}
                    </uip-sidebar-menu-button>
                    <uip-dropdown-menu-content align="start">
                      ${versions.map(
                        (version) => html`
                          <uip-dropdown-menu-item
                            @select=${() => {
                              this.selectedVersion = version
                            }}
                          >
                            v${version} ${this.selectedVersion === version ? icon(Check, 'check', 'ml-auto') : ''}
                          </uip-dropdown-menu-item>
                        `,
                      )}
                    </uip-dropdown-menu-content>
                  </uip-dropdown-menu>
                </uip-sidebar-menu-item>
              </uip-sidebar-menu>
              <form @submit=${(e: Event) => e.preventDefault()}>
                <uip-sidebar-group class="[&::part(base)]:py-0">
                  <uip-sidebar-group-content>
                    <uip-label for="search" class="sr-only">Search</uip-label>
                    <uip-input
                      id="search"
                      type="text"
                      placeholder="Search the docs..."
                      allow-clear
                      class="[&::part(control)]:bg-background [&::part(control)]:h-8 [&::part(control)]:shadow-none"
                      .value=${this.search}
                      @input=${(e: Event) => {
                        this.search = (e.target as UipInput).value
                      }}
                    >
                      <span slot="prefix">${icon(Search, 'search', 'size-3.5')}</span>
                    </uip-input>
                  </uip-sidebar-group-content>
                </uip-sidebar-group>
              </form>
            </uip-sidebar-header>
    
            <uip-sidebar-content class="[&::part(base)]:gap-0">
              ${navMain.map(
                (item) => html`
                  <uip-collapsible class="contents group/collapsible" default-open>
                    <uip-sidebar-group>
                      <uip-collapsible-trigger as-child>
                        <uip-sidebar-group-label
                          class="group/label [&::part(base)]:text-sidebar-foreground [&::part(base)]:text-sm [&::part(base)]:hover:bg-sidebar-accent [&::part(base)]:hover:text-sidebar-accent-foreground"
                        >
                          ${item.title} ${icon(ChevronRight, 'chevron-right', 'ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90')}
                        </uip-sidebar-group-label>
                      </uip-collapsible-trigger>
                      <uip-collapsible-content>
                        <uip-sidebar-group-content>
                          <uip-sidebar-menu>
                            ${item.items.map(
                              (childItem) => html`
                                <uip-sidebar-menu-item>
                                  <uip-sidebar-menu-button href=${item.url} ?is-active=${!!childItem.isActive}>
                                    ${childItem.title}
                                  </uip-sidebar-menu-button>
                                </uip-sidebar-menu-item>
                              `,
                            )}
                          </uip-sidebar-menu>
                        </uip-sidebar-group-content>
                      </uip-collapsible-content>
                    </uip-sidebar-group>
                  </uip-collapsible>
                `,
              )}
            </uip-sidebar-content>
    
            <uip-sidebar-rail></uip-sidebar-rail>
          </uip-sidebar>
        `
      }
    }
    
    customElements.get('uip-sidebar-03') || customElements.define('uip-sidebar-03', UipSidebar03)
    
    declare global {
      interface HTMLElementTagNameMap {
        'uip-sidebar-03': UipSidebar03
      }
    }
    

Raw manifest:https://uipkge.dev/r/lit/sidebar-03.json