managarten/packages/shared-ui/src/navigation/SidebarSection.svelte
Wuesteon d36b321d9d style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write
  - TypeScript/JavaScript files
  - Svelte components
  - Astro pages
  - JSON configs
  - Markdown docs

  13 files still need manual review (Astro JSX comments)
2025-11-27 18:33:16 +01:00

115 lines
2.8 KiB
Svelte

<script lang="ts">
/**
* SidebarSection - Grouped navigation section within a sidebar
*
* Provides a labeled section for organizing related navigation items.
* Supports collapsible behavior and minimized sidebar mode.
*
* @example Basic usage
* ```svelte
* <SidebarSection title="Main" items={mainNavItems} {currentPath} />
* ```
*
* @example Collapsible section
* ```svelte
* <SidebarSection
* title="Settings"
* items={settingsItems}
* collapsible
* expanded={false}
* />
* ```
*/
import type { NavItem } from './types';
import NavLink from './NavLink.svelte';
import { Text } from '../atoms';
interface Props {
/** Section title (hidden when minimized) */
title?: string;
/** Navigation items in this section */
items: NavItem[];
/** Current path for active state */
currentPath?: string;
/** Whether sidebar is minimized */
minimized?: boolean;
/** Whether section can be collapsed */
collapsible?: boolean;
/** Whether section is expanded (when collapsible) */
expanded?: boolean;
/** Divider above section */
divider?: boolean;
/** Additional CSS classes */
class?: string;
}
let {
title,
items,
currentPath = '',
minimized = false,
collapsible = false,
expanded = $bindable(true),
divider = false,
class: className = '',
}: Props = $props();
function isActive(item: NavItem): boolean {
if (item.active !== undefined) return item.active;
return currentPath === item.href || currentPath.startsWith(item.href + '/');
}
function toggleExpanded() {
if (collapsible) {
expanded = !expanded;
}
}
</script>
<div class="sidebar-section {className}">
{#if divider && !minimized}
<div class="border-t border-theme my-2 mx-3"></div>
{/if}
{#if title && !minimized}
{#if collapsible}
<button
type="button"
class="w-full flex items-center justify-between px-3 py-1.5 text-left hover:bg-menu-hover rounded transition-colors"
onclick={toggleExpanded}
>
<Text variant="small" class="text-theme-tertiary uppercase tracking-wider">
{title}
</Text>
<svg
class="w-3 h-3 text-theme-tertiary transition-transform {expanded ? '' : '-rotate-90'}"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
{:else}
<div class="px-3 py-1.5">
<Text variant="small" class="text-theme-tertiary uppercase tracking-wider">
{title}
</Text>
</div>
{/if}
{/if}
{#if expanded || !collapsible}
<nav class="space-y-0.5 {title && !minimized ? 'mt-1' : ''}">
{#each items as item}
<NavLink {item} variant="sidebar" {minimized} active={isActive(item)} />
{/each}
</nav>
{/if}
</div>