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