feat: sliding navbar

This commit is contained in:
not-nullptr 2024-11-16 11:51:41 +00:00
parent c88112d226
commit ef20f76a85
2 changed files with 88 additions and 22 deletions

View File

@ -7,6 +7,8 @@
import { theme } from "$lib/store/index.svelte";
import { blur, duration } from "$lib/animation";
import { quintOut } from "svelte/easing";
import { browser } from "$app/environment";
import type { SvelteComponent } from "svelte";
type Props = {
items: {
@ -19,17 +21,32 @@
};
let { items }: Props = $props();
let links = $state<HTMLAnchorElement[]>([]);
let container = $state<HTMLDivElement>();
let containerRect = $derived(container?.getBoundingClientRect());
$effect(() => {
$inspect(containerRect);
});
const linkRects = $derived(links.map((l) => l.getBoundingClientRect()));
const selectedIndex = $derived(
items.findIndex((i) => i.activeMatch($page.url.pathname)),
);
</script>
{#snippet link(item: (typeof items)[0])}
{#snippet link(item: (typeof items)[0], index: number)}
{@const Icon = item.icon}
<a
bind:this={links[index]}
href={item.url}
aria-label={item.name}
class={clsx(
"w-32 h-full rounded-xl flex items-center justify-center gap-3 overflow-hidden",
"w-32 h-full relative z-10 rounded-xl flex items-center justify-center gap-3 overflow-hidden",
{
"bg-panel-accented": item.activeMatch($page.url.pathname),
"bg-panel-accented":
item.activeMatch($page.url.pathname) && !browser,
},
)}
>
@ -117,23 +134,36 @@
</a>
{/snippet}
<Panel class="w-fit h-20 flex items-center gap-3">
<div
class="w-32 h-full bg-accent rounded-xl flex items-center justify-center"
>
<div class="h-5 w-full">
<Logo />
<div bind:this={container}>
<Panel class="w-fit h-20 flex items-center gap-3 relative">
{#if linkRects[selectedIndex]}
<div
class="absolute bg-panel-accented rounded-xl"
style="width: {linkRects[selectedIndex]
.width}px; height: {linkRects[selectedIndex]
.height}px; top: {linkRects[selectedIndex].top -
32}px; left: {linkRects[selectedIndex].left -
(containerRect?.left ||
0)}px; transition: left var(--transition) {duration}ms, top var(--transition) {duration}ms;"
></div>
{/if}
<div
class="w-32 h-full bg-accent rounded-xl flex items-center justify-center"
>
<div class="h-5 w-full">
<Logo />
</div>
</div>
</div>
{#each items as item (item.url)}
{@render link(item)}
{/each}
<div class="w-0.5 bg-separator h-full"></div>
<button
onclick={theme.toggle}
class="w-14 h-full flex items-center justify-center"
>
<SunIcon class="dynadark:hidden block" />
<MoonIcon class="dynadark:block hidden" />
</button>
</Panel>
{#each items as item, i (item.url)}
{@render link(item, i)}
{/each}
<div class="w-0.5 bg-separator h-full"></div>
<button
onclick={theme.toggle}
class="w-14 h-full flex items-center justify-center"
>
<SunIcon class="dynadark:hidden block" />
<MoonIcon class="dynadark:block hidden" />
</button>
</Panel>
</div>

View File

@ -1,6 +1,8 @@
<script lang="ts">
import { blur, duration } from "$lib/animation";
import Dropdown from "$lib/components/functional/Dropdown.svelte";
import Uploader from "$lib/components/functional/Uploader.svelte";
import ProgressiveBlur from "$lib/components/visual/effects/ProgressiveBlur.svelte";
import Panel from "$lib/components/visual/Panel.svelte";
import ProgressBar from "$lib/components/visual/ProgressBar.svelte";
import { files } from "$lib/store/index.svelte";
@ -12,6 +14,7 @@
RotateCwIcon,
XIcon,
} from "lucide-svelte";
import { quintOut } from "svelte/easing";
</script>
{#snippet fileItem(file: VertFile, index: number)}
@ -107,3 +110,36 @@
{/if}
</div>
</div>
<div
class="fixed w-screen h-screen opacity-75 overflow-hidden top-0 left-0 -z-50 pointer-events-none grid grid-cols-1 grid-rows-1"
>
{#if files.files.length === 1}
<div
class="w-full relative"
transition:blur={{
blurMultiplier: 24,
duration,
easing: quintOut,
scale: {
start: 1.02,
end: 1,
},
}}
>
<img
class="object-cover w-full h-full blur-xs"
src={files.files[0].blobUrl}
alt={files.files[0].name}
/>
<div class="absolute bottom-0 left-0 w-full h-full">
<ProgressiveBlur
direction="bottom"
endIntensity={256}
iterations={8}
fadeTo="var(--bg)"
/>
</div>
</div>
{/if}
</div>