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