This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / shell / Topbar.svelte
5.6 kB 163 lines
1<script lang="ts"> 2 import { resolve } from "$app/paths"; 3 import Button from "../ui/Button.svelte"; 4 import Dropdown from "../ui/Dropdown.svelte"; 5 import DropdownItem from "../ui/DropdownItem.svelte"; 6 import ArrowRight from "$icon/arrow-right"; 7 import Bell from "$icon/bell"; 8 import BookMarked from "$icon/book-marked"; 9 import BookPlus from "$icon/book-plus"; 10 import Cog from "$icon/cog"; 11 import LineSquiggle from "$icon/line-squiggle"; 12 import LogOut from "$icon/log-out"; 13 import Plus from "$icon/plus"; 14 import Search from "$icon/search"; 15 import User from "$icon/user"; 16 import UserRound from "$icon/user-round"; 17 import Spinner from "../ui/Spinner.svelte"; 18 import LogotypeSmall from "../ui/LogotypeSmall.svelte"; 19 import TopbarSearch from "./TopbarSearch.svelte"; 20 21 interface User { 22 handle: string; 23 did: string; 24 avatar?: string; 25 } 26 27 interface Props { 28 variant: "marketing" | "app"; 29 onSignOut?: () => void | Promise<void>; 30 user: User | null; 31 loading?: boolean; 32 } 33 34 let { variant, onSignOut, user, loading = false }: Props = $props(); 35 36 const appPath = (path: string) => resolve(path as "/"); 37</script> 38 39<nav 40 class="h-11 w-full border-b border-border-default bg-background-default px-5 shadow-[0_1px_1px_0_rgba(0,0,0,0.05)]" 41 aria-label="main" 42> 43 <div class="flex h-full items-center justify-between gap-4"> 44 <div class="flex shrink-0 items-center gap-2"> 45 <a 46 href={resolve("/")} 47 class="flex shrink-0 items-center gap-2 no-underline hover:no-underline" 48 > 49 <span class="hidden sm:inline-flex"> 50 <LogotypeSmall /> 51 </span> 52 <span class="inline-flex sm:hidden"> 53 <img src="/logos/dolly.svg" alt="tangled" class="size-8" /> 54 </span> 55 </a> 56 </div> 57 58 <div class={variant === "marketing" ? "flex items-center gap-3.5" : "flex items-center gap-4"}> 59 {#if variant === "marketing"} 60 <TopbarSearch /> 61 <div class="hidden h-9 w-px rounded-md bg-background-inset md:block"></div> 62 <a 63 href={appPath("/login")} 64 class="px-3 text-sm text-foreground-default transition-colors hover:text-foreground-muted" 65 > 66 Login 67 </a> 68 <span class="text-sm text-foreground-muted">or</span> 69 <Button variant="primary" size="sm" href="/signup" class="px-3"> 70 <span>Join now</span> 71 <ArrowRight class="size-3.5" aria-hidden="true" /> 72 </Button> 73 {:else} 74 {#if user || loading} 75 <div class="order-3 flex items-center md:order-1"> 76 <TopbarSearch /> 77 </div> 78 79 <div class="order-1 flex items-center md:order-3"> 80 <Dropdown id="new-menu" group="topbar" variant="button"> 81 {#snippet trigger()} 82 <Plus class="size-3.5" aria-hidden="true" /> 83 <span class="hidden md:inline">New</span> 84 {/snippet} 85 <DropdownItem href="/repo/new" icon={BookPlus}>New repository</DropdownItem> 86 <DropdownItem href="/strings/new" icon={LineSquiggle}>New string</DropdownItem> 87 </Dropdown> 88 </div> 89 90 <div class="order-2 flex items-center md:order-4"> 91 <a 92 href={appPath("/notifications")} 93 class="flex items-center text-foreground-default transition-colors hover:text-foreground-muted" 94 aria-label="Notifications" 95 > 96 <Bell class="size-4" aria-hidden="true" /> 97 </a> 98 </div> 99 100 <div class="order-3 md:hidden"> 101 <a href={appPath("/search")} class="text-foreground-subtle" aria-label="Search"> 102 <Search class="size-5" aria-hidden="true" /> 103 </a> 104 </div> 105 106 <div 107 class="hidden h-9 w-px self-center rounded-md bg-background-inset md:order-2 md:block" 108 ></div> 109 {/if} 110 111 <div class="order-4 flex items-center md:order-5"> 112 {#if loading} 113 <div class="flex items-center gap-1" role="status" aria-label="Signing in"> 114 <span 115 class="flex size-8 items-center justify-center rounded-full border border-border-default bg-background-inset text-foreground-placeholder md:size-[21px]" 116 > 117 <Spinner class="size-4" /> 118 </span> 119 <span class="hidden h-4 w-24 rounded bg-background-inset md:block" aria-hidden="true" 120 ></span> 121 </div> 122 {:else if user} 123 <Dropdown id="profile-menu" group="topbar"> 124 {#snippet trigger()} 125 {#if user.avatar} 126 <img 127 src={user.avatar} 128 alt={user.handle} 129 class="size-8 rounded-full border border-border-default object-cover md:size-[21px]" 130 /> 131 {:else} 132 <span 133 class="flex size-8 items-center justify-center rounded-full border border-border-default bg-background-inset md:size-[21px]" 134 > 135 <UserRound 136 class="size-5 text-foreground-placeholder md:size-4" 137 aria-hidden="true" 138 /> 139 </span> 140 {/if} 141 <span class="hidden max-w-48 truncate md:inline">{user.handle}</span> 142 {/snippet} 143 <DropdownItem href={`/${user.handle}`} icon={User}>Profile</DropdownItem> 144 <DropdownItem href={`/${user.handle}?tab=repos`} icon={BookMarked}> 145 Repositories 146 </DropdownItem> 147 <DropdownItem href={`/${user.handle}?tab=strings`} icon={LineSquiggle}> 148 Strings 149 </DropdownItem> 150 <DropdownItem href="/settings" icon={Cog}>Settings</DropdownItem> 151 <DropdownItem danger icon={LogOut} onclick={onSignOut}>Logout</DropdownItem> 152 </Dropdown> 153 {:else} 154 <div class="flex items-center gap-2" aria-hidden="true"> 155 <span class="size-8 rounded-full bg-background-inset md:size-[21px]"></span> 156 <span class="hidden h-4 w-24 rounded bg-background-inset md:block"></span> 157 </div> 158 {/if} 159 </div> 160 {/if} 161 </div> 162 </div> 163</nav>