Skip to content

Commit

Permalink
feat: navigation menu organization
Browse files Browse the repository at this point in the history
  • Loading branch information
johanohly committed Oct 9, 2024
1 parent 3d73903 commit 8556ef7
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 129 deletions.
80 changes: 80 additions & 0 deletions src/lib/components/NavigationDock.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script lang="ts">
import { Separator } from '$lib/components/ui/separator';
import {
ChartColumn,
GitBranchPlus,
Grip,
LayoutList,
Map,
Settings,
} from '@o7/icon/lucide';
import {
Dock,
DockDropdownItem,
DockFloatingItem,
DockTooltipItem,
} from '$lib/components/dock';
import { page } from '$app/stores';
import { flyAndScale } from '$lib/utils';
import { openModalsState } from '$lib/stores.svelte';
const addFlightItem = {
label: 'Add flight',
icon: GitBranchPlus,
onClick: () => {
openModalsState.addFlight = true;
},
};
const listFlightsItem = {
label: 'List flights',
icon: LayoutList,
onClick: () => {
openModalsState.listFlights = true;
},
};
const flightsStatisticsItem = {
label: 'Statistics',
icon: ChartColumn,
onClick: () => {
openModalsState.statistics = true;
},
};
const settingsItem = {
label: 'Settings',
icon: Settings,
onClick: () => {
openModalsState.settings = true;
},
};
const OTHER = [
{
label: 'Visited countries',
href: '/visited-countries',
},
];
</script>

<div class="absolute bottom-6 left-1/2 translate-x-[-50%]">
<div class="flex gap-4">
{#if $page.url.pathname !== '/'}
<div transition:flyAndScale>
<DockFloatingItem href="/" label="Home">
<Map />
</DockFloatingItem>
</div>
{/if}
<Dock>
<DockTooltipItem item={addFlightItem} />
{#if $page.url.pathname === '/'}
<DockTooltipItem item={listFlightsItem} />
<DockTooltipItem item={flightsStatisticsItem} />
{/if}
<DockDropdownItem items={OTHER} label="More">
<Grip />
</DockDropdownItem>
<Separator orientation="vertical" class="h-full w-[1px]" />
<DockTooltipItem item={settingsItem} />
</Dock>
</div>
</div>
61 changes: 26 additions & 35 deletions src/lib/components/dock/Dock.svelte
Original file line number Diff line number Diff line change
@@ -1,55 +1,46 @@
<script lang="ts">
import { Motion } from 'svelte-motion';
import { tv, type VariantProps } from 'tailwind-variants';
import { cn } from '$lib/utils';
interface DockProps extends VariantProps<typeof dockVariants> {
className?: string;
magnification?: number;
distance?: number;
import { dockContext } from './context.svelte';
import type { Snippet } from 'svelte';
let {
class: className = undefined,
direction = 'middle',
children,
}: {
class?: string;
direction?: 'top' | 'middle' | 'bottom';
}
let className: DockProps['className'] = undefined;
export { className as class };
export let magnification: DockProps['magnification'] = 60;
export let distance: DockProps['distance'] = 140;
export let direction: DockProps['direction'] = 'middle';
const dockVariants = tv({
base: 'h-[58px] p-2 flex gap-2 rounded-2xl border bg-background/70 backdrop-blur-md',
});
let dockElement: HTMLDivElement;
let mouseX = Infinity;
children: Snippet;
} = $props();
function handleMouseMove(e: MouseEvent) {
mouseX = e.pageX;
dockContext.mouseX = e.pageX;
}
function handleMouseLeave() {
mouseX = Infinity;
dockContext.mouseX = Infinity;
}
let dockClass = cn(dockVariants({ className }), {
'items-start': direction === 'top',
'items-center': direction === 'middle',
'items-end': direction === 'bottom',
});
let dockClass = cn(
'h-[58px] p-2 flex gap-2 rounded-2xl border bg-background/70 backdrop-blur-md',
className,
{
'items-start': direction === 'top',
'items-center': direction === 'middle',
'items-end': direction === 'bottom',
},
);
</script>

<Motion let:motion>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
use:motion
bind:this={dockElement}
on:mousemove={(e) => handleMouseMove(e)}
on:mouseleave={handleMouseLeave}
onmousemove={(e) => handleMouseMove(e)}
onmouseleave={handleMouseLeave}
class={dockClass}
>
<slot {mouseX} {magnification} {distance}>
<!-- Your Content -->
Default
</slot>
{@render children()}
</div>
</Motion>
21 changes: 11 additions & 10 deletions src/lib/components/dock/DockItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
useSpring,
useTransform,
} from 'svelte-motion';
import { dockContext } from './context.svelte';
import type { Snippet } from 'svelte';
export let magnification = 60;
export let distance = 160;
export let mouseX = 0;
let mint = useMotionValue(mouseX);
$: mint.set(mouseX);
let {
class: className = undefined,
children,
}: { class: string | undefined; children: Snippet } = $props();
let className: string | undefined = '';
export { className as class };
let mint = useMotionValue(dockContext.mouseX);
$effect(() => mint.set(dockContext.mouseX));
let iconElement: HTMLDivElement;
Expand All @@ -25,8 +26,8 @@
let widthSync = useTransform(
distanceCalc,
[-distance, 0, distance],
[38, magnification, 38],
[-dockContext.distance, 0, dockContext.distance],
[38, dockContext.magnification, 38],
);
let width = useSpring(widthSync, {
Expand All @@ -43,6 +44,6 @@

<Motion style={{ width: width }} let:motion>
<div use:motion bind:this={iconElement} class={iconClass}>
<slot></slot>
{@render children()}
</div>
</Motion>
6 changes: 1 addition & 5 deletions src/lib/components/dock/DockTooltipItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@
const onClick = () => {
if (item.onClick) item.onClick();
};
export let mouseX: number;
export let distance: number | undefined;
export let magnification: number | undefined;
</script>

<DockItem {mouseX} {distance} {magnification}>
<DockItem>
{#if item.href}
<a
href={item.href}
Expand Down
9 changes: 9 additions & 0 deletions src/lib/components/dock/context.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const dockContext: {
mouseX: number;
magnification: number;
distance: number;
} = $state({
mouseX: Infinity,
magnification: 60,
distance: 140,
});
1 change: 1 addition & 0 deletions src/lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as NavigationDock } from './NavigationDock.svelte';
82 changes: 3 additions & 79 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,14 @@
import { ModeWatcher } from 'mode-watcher';
import { ScreenSize } from '$lib/components/helpers';
import { appConfig } from '$lib/utils/stores';
import { Separator } from '$lib/components/ui/separator';
import {
Dock,
DockDropdownItem,
DockFloatingItem,
DockTooltipItem,
} from '$lib/components/dock';
import {
ChartColumn,
GitBranchPlus,
LayoutList,
Settings,
Map,
Grip,
} from '@o7/icon/lucide';
import { openModalsState } from '$lib/stores.svelte';
import { page } from '$app/stores';
import { flyAndScale } from '$lib/utils/other';
import {
AddFlightModal,
NewVersionAnnouncement,
SettingsModal,
} from '$lib/components/modals';
import { NavigationDock } from '$lib/components';
const { data, children } = $props();
Expand All @@ -37,45 +22,6 @@
});
const queryClient = trpc.hydrateFromServer(data.trpc);
const PRIMARY = [
{
label: 'Add flight',
icon: GitBranchPlus,
onClick: () => {
openModalsState.addFlight = true;
},
},
{
label: 'List flights',
icon: LayoutList,
onClick: () => {
openModalsState.listFlights = true;
},
},
{
label: 'Statistics',
icon: ChartColumn,
onClick: () => {
openModalsState.statistics = true;
},
},
];
const SECONDARY = [
{
label: 'Settings',
icon: Settings,
onClick: () => {
openModalsState.settings = true;
},
},
];
const OTHER = [
{
label: 'Visited countries',
href: '/visited-countries',
},
];
</script>

<ModeWatcher />
Expand All @@ -92,29 +38,7 @@

{@render children()}

{#if !['/login', '/setup'].includes($page.url.pathname)}
<div class="absolute bottom-6 left-1/2 translate-x-[-50%]">
<div class="flex gap-4">
{#if $page.url.pathname !== '/'}
<div transition:flyAndScale>
<DockFloatingItem href="/" label="Home">
<Map />
</DockFloatingItem>
</div>
{/if}
<Dock let:mouseX let:distance let:magnification>
{#each PRIMARY as item}
<DockTooltipItem {item} {mouseX} {distance} {magnification} />
{/each}
<DockDropdownItem items={OTHER} label="More">
<Grip />
</DockDropdownItem>
<Separator orientation="vertical" class="h-full w-[1px]" />
{#each SECONDARY as item}
<DockTooltipItem {item} {mouseX} {distance} {magnification} />
{/each}
</Dock>
</div>
</div>
{#if !$page.error && !['/login', '/setup'].includes($page.url.pathname)}
<NavigationDock />
{/if}
</QueryClientProvider>

0 comments on commit 8556ef7

Please sign in to comment.