🧼 Simplifies the menu structure
This commit is contained in:
@@ -1,271 +0,0 @@
|
||||
<script lang="ts">
|
||||
import logo from '$lib/assets/logo512.png';
|
||||
import MdiGithub from '~icons/mdi/github';
|
||||
import MdiConnection from '~icons/mdi/connection';
|
||||
import Users from '~icons/mdi/users';
|
||||
import Settings from '~icons/mdi/settings';
|
||||
import MdiController from '~icons/mdi/controller';
|
||||
import Devices from '~icons/mdi/devices'
|
||||
import Camera from '~icons/mdi/camera-outline';
|
||||
import Rotate3d from '~icons/mdi/rotate-3d';
|
||||
import MotorOutline from '~icons/mdi/motor-outline';
|
||||
import Health from '~icons/mdi/stethoscope';
|
||||
import Folder from '~icons/mdi/folder-outline';
|
||||
import Update from '~icons/mdi/reload';
|
||||
import WiFi from '~icons/mdi/wifi';
|
||||
import Router from '~icons/mdi/router';
|
||||
import AP from '~icons/mdi/access-point';
|
||||
import Remote from '~icons/mdi/network';
|
||||
import Avatar from '~icons/mdi/user-circle';
|
||||
import Logout from '~icons/mdi/logout';
|
||||
import Copyright from '~icons/mdi/copyright';
|
||||
import NTP from '~icons/mdi/clock-check';
|
||||
import Metrics from '~icons/mdi/report-bar';
|
||||
import { page } from '$app/stores';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { useFeatureFlags } from '$lib/stores/featureFlags';
|
||||
|
||||
const features = useFeatureFlags();
|
||||
|
||||
const appName = $page.data.app_name;
|
||||
|
||||
const copyright = $page.data.copyright;
|
||||
|
||||
const github = { href: 'https://github.com/' + $page.data.github, active: true };
|
||||
|
||||
type menuItem = {
|
||||
title: string;
|
||||
icon: ConstructorOfATypedSvelteComponent;
|
||||
href?: string;
|
||||
feature: boolean;
|
||||
active?: boolean;
|
||||
submenu?: menuItem[];
|
||||
};
|
||||
|
||||
$: menuItems = [
|
||||
{
|
||||
title: 'Controller',
|
||||
icon: MdiController,
|
||||
href: '/controller',
|
||||
feature: true,
|
||||
},
|
||||
{
|
||||
title: 'Peripherals',
|
||||
icon: Devices,
|
||||
feature: true,
|
||||
submenu: [
|
||||
{
|
||||
title: 'I2C',
|
||||
icon: MdiConnection,
|
||||
href: '/peripherals/i2c',
|
||||
feature: true,
|
||||
},
|
||||
{
|
||||
title: 'Camera',
|
||||
icon: Camera,
|
||||
href: '/peripherals/camera',
|
||||
feature: $features.camera,
|
||||
},
|
||||
{
|
||||
title: 'Servo',
|
||||
icon: MotorOutline,
|
||||
href: '/peripherals/servo',
|
||||
feature: true,
|
||||
},
|
||||
{
|
||||
title: 'IMU',
|
||||
icon: Rotate3d,
|
||||
href: '/peripherals/imu',
|
||||
feature: $features.imu || $features.mag || $features.bmp,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Connections',
|
||||
icon: Remote,
|
||||
feature: $features.ntp,
|
||||
submenu: [
|
||||
{
|
||||
title: 'NTP',
|
||||
icon: NTP,
|
||||
href: '/connections/ntp',
|
||||
feature: $features.ntp,
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'WiFi',
|
||||
icon: WiFi,
|
||||
feature: true,
|
||||
submenu: [
|
||||
{
|
||||
title: 'WiFi Station',
|
||||
icon: Router,
|
||||
href: '/wifi/sta',
|
||||
feature: true,
|
||||
|
||||
},
|
||||
{
|
||||
title: 'Access Point',
|
||||
icon: AP,
|
||||
href: '/wifi/ap',
|
||||
feature: true,
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Users',
|
||||
icon: Users,
|
||||
href: '/user',
|
||||
feature: $features.security && $user.admin,
|
||||
|
||||
},
|
||||
{
|
||||
title: 'System',
|
||||
icon: Settings,
|
||||
feature: true,
|
||||
submenu: [
|
||||
{
|
||||
title: 'System Status',
|
||||
icon: Health,
|
||||
href: '/system/status',
|
||||
feature: true,
|
||||
|
||||
},
|
||||
{
|
||||
title: 'File System',
|
||||
icon: Folder,
|
||||
href: '/system/filesystem',
|
||||
feature: true,
|
||||
|
||||
},
|
||||
{
|
||||
title: 'System Metrics',
|
||||
icon: Metrics,
|
||||
href: '/system/metrics',
|
||||
feature: $features.analytics,
|
||||
|
||||
},
|
||||
{
|
||||
title: 'Firmware Update',
|
||||
icon: Update,
|
||||
href: '/system/update',
|
||||
feature:
|
||||
($features.ota ||
|
||||
$features.upload_firmware ||
|
||||
$features.download_firmware) &&
|
||||
(!$features.security || $user.admin),
|
||||
}
|
||||
]
|
||||
}
|
||||
] as menuItem[];
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function setActiveMenuItem(targetTitle: string) {
|
||||
menuItems.forEach(item => {
|
||||
item.active = item.title === targetTitle;
|
||||
item.submenu?.forEach(subItem => {
|
||||
subItem.active = subItem.title === targetTitle;
|
||||
});
|
||||
});
|
||||
menuItems = menuItems
|
||||
dispatch('menuClicked');
|
||||
}
|
||||
|
||||
$: setActiveMenuItem($page.data.title);
|
||||
</script>
|
||||
|
||||
<div class="bg-base-200 text-base-content flex h-full w-80 flex-col p-4">
|
||||
<!-- Sidebar content here -->
|
||||
<a
|
||||
href="/"
|
||||
class="rounded-box mb-4 flex items-center hover:scale-[1.02] active:scale-[0.98]"
|
||||
on:click={() => setActiveMenuItem('')}
|
||||
>
|
||||
<img src={logo} alt="Logo" class="h-12 w-12" />
|
||||
<h1 class="px-4 text-2xl font-bold">{appName}</h1>
|
||||
</a>
|
||||
<ul class="menu rounded-box menu-vertical flex-nowrap overflow-y-auto">
|
||||
{#each menuItems as menuItem, i (menuItem.title)}
|
||||
{#if menuItem.feature}
|
||||
<li>
|
||||
{#if menuItem.submenu}
|
||||
<details>
|
||||
<summary class="text-lg font-bold">
|
||||
<svelte:component this={menuItem.icon} class="h-6 w-6" />
|
||||
{menuItem.title}
|
||||
</summary>
|
||||
<ul>
|
||||
{#each menuItem.submenu as subMenuItem}
|
||||
{#if subMenuItem.feature}
|
||||
<li class="hover-bordered">
|
||||
<a
|
||||
href={subMenuItem.href}
|
||||
class:bg-base-100={subMenuItem.active}
|
||||
class="text-ml font-bold"
|
||||
on:click={() => {
|
||||
setActiveMenuItem(subMenuItem.title);
|
||||
menuItems = menuItems;
|
||||
}}
|
||||
><svelte:component
|
||||
this={subMenuItem.icon}
|
||||
class="h-5 w-5"
|
||||
/>{subMenuItem.title}</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
</details>
|
||||
{:else}
|
||||
<a
|
||||
href={menuItem.href}
|
||||
class:bg-base-100={menuItem.active}
|
||||
class="text-lg font-bold"
|
||||
on:click={() => {
|
||||
setActiveMenuItem(menuItem.title);
|
||||
menuItems = menuItems;
|
||||
}}><svelte:component this={menuItem.icon} class="h-6 w-6" />{menuItem.title}</a
|
||||
>
|
||||
{/if}
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<div class="flex-col" />
|
||||
<div class="flex-grow" />
|
||||
|
||||
{#if $features.security}
|
||||
<div class="flex items-center">
|
||||
<Avatar class="h-8 w-8" />
|
||||
<span class="flex-grow px-4 text-xl font-bold">{$user.username}</span>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="btn btn-ghost"
|
||||
on:click={() => {
|
||||
user.invalidate();
|
||||
}}
|
||||
>
|
||||
<Logout class="h-8 w-8 rotate-180" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="divider my-0" />
|
||||
<div class="flex items-center">
|
||||
{#if github.active}
|
||||
<a href={github.href} class="btn btn-ghost" target="_blank" rel="noopener noreferrer"
|
||||
><MdiGithub class="h-5 w-5" /></a
|
||||
>
|
||||
{/if}
|
||||
<div class="inline-flex flex-grow items-center justify-end text-sm">
|
||||
<Copyright class="h-4 w-4" /><span class="px-2">{copyright}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import MdiGithub from '~icons/mdi/github';
|
||||
export let github;
|
||||
</script>
|
||||
|
||||
{#if github.active}
|
||||
<a href={github.href} class="btn btn-ghost" target="_blank" rel="noopener noreferrer"
|
||||
><MdiGithub class="h-5 w-5" />
|
||||
</a>
|
||||
{/if}
|
||||
@@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import logo from '$lib/assets/logo512.png';
|
||||
|
||||
export let appName;
|
||||
</script>
|
||||
|
||||
<a
|
||||
href="/"
|
||||
class="rounded-box mb-4 flex items-center hover:scale-[1.02] active:scale-[0.98]"
|
||||
>
|
||||
<img src={logo} alt="Logo" class="h-12 w-12" />
|
||||
<h1 class="px-4 text-2xl font-bold">{appName}</h1>
|
||||
</a>
|
||||
@@ -0,0 +1,192 @@
|
||||
<script lang="ts">
|
||||
import MdiConnection from '~icons/mdi/connection';
|
||||
import Users from '~icons/mdi/users';
|
||||
import Settings from '~icons/mdi/settings';
|
||||
import MdiController from '~icons/mdi/controller';
|
||||
import Devices from '~icons/mdi/devices';
|
||||
import Camera from '~icons/mdi/camera-outline';
|
||||
import Rotate3d from '~icons/mdi/rotate-3d';
|
||||
import MotorOutline from '~icons/mdi/motor-outline';
|
||||
import Health from '~icons/mdi/stethoscope';
|
||||
import Folder from '~icons/mdi/folder-outline';
|
||||
import Update from '~icons/mdi/reload';
|
||||
import WiFi from '~icons/mdi/wifi';
|
||||
import Router from '~icons/mdi/router';
|
||||
import AP from '~icons/mdi/access-point';
|
||||
import Remote from '~icons/mdi/network';
|
||||
import Copyright from '~icons/mdi/copyright';
|
||||
import NTP from '~icons/mdi/clock-check';
|
||||
import Metrics from '~icons/mdi/report-bar';
|
||||
import { page } from '$app/stores';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { useFeatureFlags } from '$lib/stores/featureFlags';
|
||||
import UserButton from '../menu/UserButton.svelte';
|
||||
import GithubButton from '../menu/GithubButton.svelte';
|
||||
import LogoButton from '../menu/LogoButton.svelte';
|
||||
import MenuList from '../menu/MenuList.svelte';
|
||||
|
||||
const features = useFeatureFlags();
|
||||
|
||||
const appName = $page.data.app_name;
|
||||
|
||||
const copyright = $page.data.copyright;
|
||||
|
||||
const github = { href: 'https://github.com/' + $page.data.github, active: true };
|
||||
|
||||
type menuItem = {
|
||||
title: string;
|
||||
icon: ConstructorOfATypedSvelteComponent;
|
||||
href?: string;
|
||||
feature: boolean;
|
||||
active?: boolean;
|
||||
submenu?: menuItem[];
|
||||
};
|
||||
|
||||
$: menuItems = [
|
||||
{
|
||||
title: 'Controller',
|
||||
icon: MdiController,
|
||||
href: '/controller',
|
||||
feature: true
|
||||
},
|
||||
{
|
||||
title: 'Peripherals',
|
||||
icon: Devices,
|
||||
feature: true,
|
||||
submenu: [
|
||||
{
|
||||
title: 'I2C',
|
||||
icon: MdiConnection,
|
||||
href: '/peripherals/i2c',
|
||||
feature: true
|
||||
},
|
||||
{
|
||||
title: 'Camera',
|
||||
icon: Camera,
|
||||
href: '/peripherals/camera',
|
||||
feature: $features.camera
|
||||
},
|
||||
{
|
||||
title: 'Servo',
|
||||
icon: MotorOutline,
|
||||
href: '/peripherals/servo',
|
||||
feature: true
|
||||
},
|
||||
{
|
||||
title: 'IMU',
|
||||
icon: Rotate3d,
|
||||
href: '/peripherals/imu',
|
||||
feature: $features.imu || $features.mag || $features.bmp
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Connections',
|
||||
icon: Remote,
|
||||
feature: $features.ntp,
|
||||
submenu: [
|
||||
{
|
||||
title: 'NTP',
|
||||
icon: NTP,
|
||||
href: '/connections/ntp',
|
||||
feature: $features.ntp
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'WiFi',
|
||||
icon: WiFi,
|
||||
feature: true,
|
||||
submenu: [
|
||||
{
|
||||
title: 'WiFi Station',
|
||||
icon: Router,
|
||||
href: '/wifi/sta',
|
||||
feature: true
|
||||
},
|
||||
{
|
||||
title: 'Access Point',
|
||||
icon: AP,
|
||||
href: '/wifi/ap',
|
||||
feature: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Users',
|
||||
icon: Users,
|
||||
href: '/user',
|
||||
feature: $features.security && $user.admin
|
||||
},
|
||||
{
|
||||
title: 'System',
|
||||
icon: Settings,
|
||||
feature: true,
|
||||
submenu: [
|
||||
{
|
||||
title: 'System Status',
|
||||
icon: Health,
|
||||
href: '/system/status',
|
||||
feature: true
|
||||
},
|
||||
{
|
||||
title: 'File System',
|
||||
icon: Folder,
|
||||
href: '/system/filesystem',
|
||||
feature: true
|
||||
},
|
||||
{
|
||||
title: 'System Metrics',
|
||||
icon: Metrics,
|
||||
href: '/system/metrics',
|
||||
feature: $features.analytics
|
||||
},
|
||||
{
|
||||
title: 'Firmware Update',
|
||||
icon: Update,
|
||||
href: '/system/update',
|
||||
feature:
|
||||
($features.ota || $features.upload_firmware || $features.download_firmware) &&
|
||||
(!$features.security || $user.admin)
|
||||
}
|
||||
]
|
||||
}
|
||||
] as menuItem[];
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function setActiveMenuItem(targetTitle: string) {
|
||||
menuItems.forEach((item) => {
|
||||
item.active = item.title === targetTitle;
|
||||
item.submenu?.forEach((subItem) => {
|
||||
subItem.active = subItem.title === targetTitle;
|
||||
});
|
||||
});
|
||||
menuItems = menuItems;
|
||||
dispatch('menuClicked');
|
||||
}
|
||||
|
||||
$: setActiveMenuItem($page.data.title);
|
||||
|
||||
const updateMenu = (event:any) => {
|
||||
setActiveMenuItem(event.details)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="bg-base-200 text-base-content flex h-full w-80 flex-col p-4">
|
||||
<LogoButton {appName} />
|
||||
|
||||
<MenuList {menuItems} on:select{updateMenu} class="flex-grow flex-nowrap overflow-y-auto"/>
|
||||
|
||||
<UserButton />
|
||||
|
||||
<div class="divider my-0" />
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<GithubButton {github} />
|
||||
<div class="flex items-center justify-end text-sm gap-2">
|
||||
<Copyright class="h-4 w-4" />{copyright}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
type menuItem = {
|
||||
title: string;
|
||||
icon: ConstructorOfATypedSvelteComponent;
|
||||
href?: string;
|
||||
feature: boolean;
|
||||
active?: boolean;
|
||||
submenu?: menuItem[];
|
||||
};
|
||||
|
||||
export let menuItems: menuItem[];
|
||||
|
||||
export let level = 0;
|
||||
|
||||
const selectMenuItem = (title: string) => {
|
||||
dispatch('select', title);
|
||||
};
|
||||
</script>
|
||||
|
||||
<ul class={$$props.class + ' menu'}>
|
||||
{#each menuItems as menuItem, i (menuItem.title)}
|
||||
{#if menuItem.feature}
|
||||
<li>
|
||||
{#if menuItem.submenu}
|
||||
<details>
|
||||
<summary class="text-lg font-bold">
|
||||
<svelte:component this={menuItem.icon} class="h-6 w-6" />
|
||||
{menuItem.title}
|
||||
</summary>
|
||||
<div class="pl-4">
|
||||
<svelte:self menuItems={menuItem.submenu} level={level + 1} />
|
||||
</div>
|
||||
</details>
|
||||
{:else}
|
||||
<a
|
||||
href={menuItem.href}
|
||||
class="font-bold"
|
||||
class:bg-base-100={menuItem.active}
|
||||
class:text-lg={level === 0}
|
||||
class:text-md={level === 1}
|
||||
on:click={() => selectMenuItem(menuItem.title)}
|
||||
>
|
||||
<svelte:component this={menuItem.icon} class="h-6 w-6" />
|
||||
{menuItem.title}
|
||||
</a>
|
||||
{/if}
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import Avatar from '~icons/mdi/user-circle';
|
||||
import Logout from '~icons/mdi/logout';
|
||||
import { user } from '$lib/stores';
|
||||
import { useFeatureFlags } from "$lib/stores";
|
||||
|
||||
const features = useFeatureFlags();
|
||||
</script>
|
||||
|
||||
{#if $features.security}
|
||||
<div class="flex items-center">
|
||||
<Avatar class="h-8 w-8" />
|
||||
<span class="flex-grow px-4 text-xl font-bold">{$user.username}</span>
|
||||
<button class="btn btn-ghost" on:click={user.invalidate}>
|
||||
<Logout class="h-8 w-8 rotate-180" />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user