|
34 | 34 | import ChooseLanguage from "$lib/components/views/ChooseLanguage.svelte"; |
35 | 35 | import Avatar from "$lib/components/ui/Avatar.svelte"; |
36 | 36 | import { Trans } from "$lib/components/locale"; |
37 | | - import { getMetadataString, openIdLogo } from "$lib/utils/openID"; |
| 37 | + import { getMetadataString } from "$lib/utils/openID"; |
38 | 38 | import ProgressRing from "$lib/components/ui/ProgressRing.svelte"; |
39 | 39 | import { analytics } from "$lib/utils/analytics/analytics"; |
40 | 40 | import { onMount } from "svelte"; |
41 | | - import PasskeyIcon from "$lib/components/icons/PasskeyIcon.svelte"; |
42 | 41 |
|
43 | 42 | const { children, data }: LayoutProps = $props(); |
44 | 43 |
|
|
191 | 190 |
|
192 | 191 | <!-- Layout --> |
193 | 192 | <div class="bg-bg-primary_alt flex min-h-[100dvh] flex-row"> |
194 | | - <!-- Sidebar --> |
| 193 | + <!-- Sidebar and backdrop on mobile --> |
| 194 | + <div |
| 195 | + onclick={() => (isMobileSidebarOpen = false)} |
| 196 | + class={[ |
| 197 | + "bg-bg-overlay absolute inset-0 z-1 transition-opacity duration-200 ease-out sm:hidden", |
| 198 | + isMobileSidebarOpen ? "opacity-80" : "pointer-events-none opacity-0 ", |
| 199 | + ]} |
| 200 | + aria-hidden="true" |
| 201 | + ></div> |
195 | 202 | <aside |
196 | 203 | class={[ |
197 | 204 | "bg-bg-primary border-border-secondary flex w-74 flex-col sm:border-r sm:max-md:w-19", |
198 | | - "max-sm:invisible max-sm:absolute max-sm:inset-0 max-sm:z-1 max-sm:w-full max-sm:overflow-y-auto", |
199 | | - isMobileSidebarOpen && "max-sm:visible", |
| 205 | + "max-sm:absolute max-sm:start-0 max-sm:end-20 max-sm:top-0 max-sm:bottom-0 max-sm:z-1 max-sm:w-auto max-sm:overflow-y-auto max-sm:transition-transform max-sm:duration-200 max-sm:ease-out", |
| 206 | + isMobileSidebarOpen |
| 207 | + ? "max-sm:translate-x-0" |
| 208 | + : "max-sm:pointer-events-none max-sm:translate-x-[-100%]", |
200 | 209 | ]} |
201 | 210 | > |
202 | 211 | <div class="h-[env(safe-area-inset-top)]"></div> |
|
210 | 219 | </div> |
211 | 220 | <button |
212 | 221 | onclick={() => (isMobileSidebarOpen = false)} |
213 | | - class="btn btn-tertiary btn-icon ms-auto sm:hidden" |
| 222 | + class="btn btn-tertiary btn-icon ms-auto -me-1.5 sm:hidden" |
214 | 223 | aria-label={$t`Close menu`} |
215 | 224 | > |
216 | 225 | <XIcon class="size-5" /> |
|
228 | 237 | Internet Identity |
229 | 238 | </div> |
230 | 239 | </div> |
231 | | - <!-- Mobile identity button--> |
232 | | - {#if $lastUsedIdentitiesStore.selected !== undefined} |
233 | | - {@const logo = |
234 | | - "openid" in $lastUsedIdentitiesStore.selected.authMethod && |
235 | | - $lastUsedIdentitiesStore.selected.authMethod.openid.metadata !== |
236 | | - undefined |
237 | | - ? openIdLogo( |
238 | | - $lastUsedIdentitiesStore.selected.authMethod.openid.iss, |
239 | | - $lastUsedIdentitiesStore.selected.authMethod.openid.metadata, |
240 | | - ) |
241 | | - : undefined} |
242 | | - <button |
243 | | - onclick={() => (isIdentityPopoverOpen = true)} |
244 | | - class={[ |
245 | | - "mx-4 mb-6 flex flex-row items-center gap-3 p-3 text-start sm:hidden", |
246 | | - "border-border-secondary hover:bg-bg-primary_hover rounded-md border", |
247 | | - ]} |
248 | | - aria-label={$t`Switch identity`} |
249 | | - > |
250 | | - <div class="relative"> |
251 | | - <Avatar size="sm"> |
252 | | - <UserIcon class="size-5" /> |
253 | | - </Avatar> |
254 | | - <div |
255 | | - class="bg-bg-primary_alt border-border-secondary absolute -right-1.25 -bottom-1.25 flex size-5 items-center justify-center rounded-full border" |
256 | | - > |
257 | | - {#if logo !== undefined} |
258 | | - <div class="text-fg-tertiary size-3.25"> |
259 | | - {@html logo} |
260 | | - </div> |
261 | | - {:else} |
262 | | - <PasskeyIcon class="text-fg-tertiary !size-3" /> |
263 | | - {/if} |
264 | | - </div> |
265 | | - </div> |
266 | | - <div class="flex flex-col overflow-hidden"> |
267 | | - <div class="text-text-primary text-sm font-semibold"> |
268 | | - {$lastUsedIdentitiesStore.selected.name ?? |
269 | | - $lastUsedIdentitiesStore.selected.identityNumber} |
270 | | - </div> |
271 | | - <div |
272 | | - class="text-text-tertiary overflow-hidden text-sm overflow-ellipsis whitespace-nowrap" |
273 | | - > |
274 | | - {#if "openid" in $lastUsedIdentitiesStore.selected.authMethod && $lastUsedIdentitiesStore.selected.authMethod.openid.metadata !== undefined} |
275 | | - <span |
276 | | - >{getMetadataString( |
277 | | - $lastUsedIdentitiesStore.selected.authMethod.openid.metadata, |
278 | | - "email", |
279 | | - ) ?? $t`Hidden email`}</span |
280 | | - > |
281 | | - {:else} |
282 | | - <span> |
283 | | - {$t`Passkey`} |
284 | | - </span> |
285 | | - {/if} |
286 | | - </div> |
287 | | - </div> |
288 | | - <ChevronDownIcon class="text-text-primary ms-auto size-5 shrink-0" /> |
289 | | - </button> |
290 | | - {/if} |
291 | 240 | <!-- Navigation --> |
292 | 241 | <nav class="flex flex-col gap-0.5 px-4"> |
293 | 242 | <ul class="contents"> |
|
355 | 304 | <div class="h-[env(safe-area-inset-top)]"></div> |
356 | 305 | <!-- Header --> |
357 | 306 | <header class="flex h-16 flex-row items-center px-4 sm:px-8 md:px-12"> |
358 | | - <!-- Mobile logo --> |
359 | | - <Logo class="text-fg-primary h-4 sm:hidden" /> |
360 | | - <!-- Empty space between left and right content --> |
361 | | - <div class="flex-1"></div> |
362 | | - <!-- Identity button--> |
363 | | - <button |
364 | | - bind:this={identityButtonRef} |
365 | | - onclick={() => (isIdentityPopoverOpen = true)} |
366 | | - class="btn btn-tertiary gap-2.5 pr-3 max-sm:hidden sm:-mr-3" |
367 | | - aria-label={$t`Switch identity`} |
368 | | - > |
369 | | - <Avatar size="xs"> |
370 | | - <UserIcon class="size-4" /> |
371 | | - </Avatar> |
372 | | - <span> |
373 | | - {data.identityInfo.name[0] ?? data.identityNumber.toString()} |
374 | | - </span> |
375 | | - <ChevronDownIcon size="1rem" /> |
376 | | - </button> |
377 | 307 | <!-- Mobile menu button --> |
378 | | - <div class="relative ms-2 sm:hidden"> |
| 308 | + <div class="relative -ms-1.5 sm:hidden"> |
379 | 309 | <button |
380 | 310 | onclick={() => (isMobileSidebarOpen = true)} |
381 | 311 | class="btn btn-tertiary btn-icon" |
|
394 | 324 | ]} |
395 | 325 | ></div> |
396 | 326 | </div> |
| 327 | + <!-- Empty space between left and right content --> |
| 328 | + <div class="flex-1"></div> |
| 329 | + <!-- Identity button--> |
| 330 | + <button |
| 331 | + bind:this={identityButtonRef} |
| 332 | + onclick={() => (isIdentityPopoverOpen = true)} |
| 333 | + class="btn btn-tertiary gap-2.5 pe-3 max-sm:-me-2 sm:-me-3" |
| 334 | + aria-label={$t`Switch identity`} |
| 335 | + > |
| 336 | + <Avatar size="xs"> |
| 337 | + <UserIcon class="size-4" /> |
| 338 | + </Avatar> |
| 339 | + <span> |
| 340 | + {data.identityInfo.name[0] ?? data.identityNumber.toString()} |
| 341 | + </span> |
| 342 | + <ChevronDownIcon size="1rem" /> |
| 343 | + </button> |
397 | 344 | </header> |
398 | 345 | <!-- Page content --> |
399 | 346 | <main class="flex flex-col px-4 py-5 sm:px-8 sm:py-3 md:px-12"> |
|
0 commit comments