208 lines
7.1 KiB
Svelte
208 lines
7.1 KiB
Svelte
<script lang="ts">
|
|
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
|
|
import SettingsIcon from '@lucide/svelte/icons/settings';
|
|
import UserIcon from '@lucide/svelte/icons/user';
|
|
import HeartIcon from '@lucide/svelte/icons/heart';
|
|
import FolderIcon from '@lucide/svelte/icons/folder';
|
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
|
import { useSidebar } from '$lib/components/ui/sidebar/context.svelte.js';
|
|
import {
|
|
appStateStore,
|
|
setVideoSourceFilter,
|
|
clearAll,
|
|
ToQuery,
|
|
resetCurrentPage
|
|
} from '$lib/stores/filter';
|
|
|
|
import { type VideoSourcesResponse } from '$lib/types';
|
|
import { VIDEO_SOURCES } from '$lib/consts';
|
|
import * as Collapsible from '$lib/components/ui/collapsible/index.js';
|
|
import { goto } from '$app/navigation';
|
|
import { videoSourceStore } from '$lib/stores/video-source';
|
|
const sidebar = useSidebar();
|
|
|
|
const items = Object.values(VIDEO_SOURCES);
|
|
|
|
function handleSourceClick(sourceType: string, sourceId: number) {
|
|
setVideoSourceFilter({
|
|
type: sourceType,
|
|
id: sourceId.toString()
|
|
});
|
|
resetCurrentPage();
|
|
goto(`/${ToQuery($appStateStore)}`);
|
|
if (sidebar.isMobile) {
|
|
sidebar.setOpenMobile(false);
|
|
}
|
|
}
|
|
|
|
function handleLogoClick() {
|
|
clearAll();
|
|
goto('/');
|
|
|
|
if (sidebar.isMobile) {
|
|
sidebar.setOpenMobile(false);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<Sidebar.Root class="border-border bg-background border-r">
|
|
<Sidebar.Header class="border-border flex h-[73px] items-center border-b">
|
|
<a
|
|
href="/"
|
|
class="flex w-full items-center gap-3 px-4 py-3 hover:cursor-pointer"
|
|
onclick={handleLogoClick}
|
|
>
|
|
<div class="flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg">
|
|
<img src="/favicon.png" alt="Bili Sync" class="h-6 w-6" />
|
|
</div>
|
|
<div class="grid flex-1 text-left text-sm leading-tight">
|
|
<span class="truncate font-semibold">Bili Sync</span>
|
|
<span class="text-muted-foreground truncate text-xs">视频管理系统</span>
|
|
</div>
|
|
</a>
|
|
</Sidebar.Header>
|
|
<Sidebar.Content class="flex flex-col px-2 py-3">
|
|
<div class="flex-1">
|
|
<Sidebar.Group>
|
|
<Sidebar.GroupLabel
|
|
class="text-muted-foreground mb-2 px-2 text-xs font-medium tracking-wider uppercase"
|
|
>
|
|
视频筛选
|
|
</Sidebar.GroupLabel>
|
|
<Sidebar.GroupContent>
|
|
<Sidebar.Menu class="space-y-1">
|
|
{#each items as item (item.type)}
|
|
<Collapsible.Root class="group/collapsible">
|
|
<Sidebar.MenuItem>
|
|
<Collapsible.Trigger class="w-full">
|
|
{#snippet child({ props })}
|
|
<Sidebar.MenuButton
|
|
{...props}
|
|
class="hover:bg-accent/50 text-foreground flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2.5 font-medium transition-all duration-200"
|
|
>
|
|
<div class="flex flex-1 items-center gap-3">
|
|
<item.icon class="text-muted-foreground h-4 w-4" />
|
|
<span class="text-sm">{item.title}</span>
|
|
</div>
|
|
<ChevronRightIcon
|
|
class="text-muted-foreground h-3 w-3 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
|
|
/>
|
|
</Sidebar.MenuButton>
|
|
{/snippet}
|
|
</Collapsible.Trigger>
|
|
<Collapsible.Content class="mt-1">
|
|
<div class="border-border ml-5 space-y-0.5 border-l pl-2">
|
|
{#if $videoSourceStore}
|
|
{#if $videoSourceStore[item.type as keyof VideoSourcesResponse]?.length > 0}
|
|
{#each $videoSourceStore[item.type as keyof VideoSourcesResponse] as source (source.id)}
|
|
<Sidebar.MenuItem>
|
|
<button
|
|
class="text-foreground hover:bg-accent/50 w-full cursor-pointer rounded-md px-3 py-2 text-left text-sm transition-all duration-200"
|
|
onclick={() => handleSourceClick(item.type, source.id)}
|
|
>
|
|
<span class="block truncate">{source.name}</span>
|
|
</button>
|
|
</Sidebar.MenuItem>
|
|
{/each}
|
|
{:else}
|
|
<div class="text-muted-foreground px-3 py-2 text-sm">无数据</div>
|
|
{/if}
|
|
{:else}
|
|
<div class="text-muted-foreground px-3 py-2 text-sm">加载中...</div>
|
|
{/if}
|
|
</div>
|
|
</Collapsible.Content>
|
|
</Sidebar.MenuItem>
|
|
</Collapsible.Root>
|
|
{/each}
|
|
</Sidebar.Menu>
|
|
</Sidebar.GroupContent>
|
|
</Sidebar.Group>
|
|
|
|
<Sidebar.Group>
|
|
<Sidebar.GroupLabel
|
|
class="text-muted-foreground mb-2 px-2 text-xs font-medium tracking-wider uppercase"
|
|
>
|
|
快捷订阅
|
|
</Sidebar.GroupLabel>
|
|
<Sidebar.GroupContent>
|
|
<Sidebar.Menu class="space-y-1">
|
|
<Sidebar.MenuItem>
|
|
<Sidebar.MenuButton>
|
|
<a
|
|
href="/me/favorites"
|
|
class="hover:bg-accent/50 text-foreground flex w-full cursor-pointer items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200"
|
|
onclick={() => {
|
|
if (sidebar.isMobile) {
|
|
sidebar.setOpenMobile(false);
|
|
}
|
|
}}
|
|
>
|
|
<HeartIcon class="text-muted-foreground h-4 w-4" />
|
|
<span>创建的收藏夹</span>
|
|
</a>
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.MenuItem>
|
|
<Sidebar.MenuItem>
|
|
<Sidebar.MenuButton>
|
|
<a
|
|
href="/me/collections"
|
|
class="hover:bg-accent/50 text-foreground flex w-full cursor-pointer items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200"
|
|
onclick={() => {
|
|
if (sidebar.isMobile) {
|
|
sidebar.setOpenMobile(false);
|
|
}
|
|
}}
|
|
>
|
|
<FolderIcon class="text-muted-foreground h-4 w-4" />
|
|
<span>关注的合集</span>
|
|
</a>
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.MenuItem>
|
|
<Sidebar.MenuItem>
|
|
<Sidebar.MenuButton>
|
|
<a
|
|
href="/me/uppers"
|
|
class="hover:bg-accent/50 text-foreground flex w-full cursor-pointer items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200"
|
|
onclick={() => {
|
|
if (sidebar.isMobile) {
|
|
sidebar.setOpenMobile(false);
|
|
}
|
|
}}
|
|
>
|
|
<UserIcon class="text-muted-foreground h-4 w-4" />
|
|
<span>关注的 UP 主</span>
|
|
</a>
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.MenuItem>
|
|
</Sidebar.Menu>
|
|
</Sidebar.GroupContent>
|
|
</Sidebar.Group>
|
|
</div>
|
|
|
|
<!-- 固定在底部的菜单选项 -->
|
|
<div class="border-border mt-auto border-t pt-4">
|
|
<Sidebar.Menu class="space-y-1">
|
|
<Sidebar.MenuItem>
|
|
<Sidebar.MenuButton>
|
|
<a
|
|
href="/settings"
|
|
class="hover:bg-accent/50 text-foreground flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2.5 font-medium transition-all duration-200"
|
|
onclick={() => {
|
|
if (sidebar.isMobile) {
|
|
sidebar.setOpenMobile(false);
|
|
}
|
|
}}
|
|
>
|
|
<div class="flex flex-1 items-center gap-3">
|
|
<SettingsIcon class="text-muted-foreground h-4 w-4" />
|
|
<span class="text-sm">设置</span>
|
|
</div>
|
|
</a>
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.MenuItem>
|
|
</Sidebar.Menu>
|
|
</div>
|
|
</Sidebar.Content>
|
|
</Sidebar.Root>
|