mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
refactor(web): sidebar menu links (#7891)
This commit is contained in:
parent
6bfa1fceec
commit
c24b6cf617
3 changed files with 70 additions and 104 deletions
|
@ -1,6 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import SideBarLink from '$lib/components/shared-components/side-bar/side-bar-link.svelte';
|
||||||
import SideBarButton from '$lib/components/shared-components/side-bar/side-bar-button.svelte';
|
|
||||||
import SideBarSection from '$lib/components/shared-components/side-bar/side-bar-section.svelte';
|
import SideBarSection from '$lib/components/shared-components/side-bar/side-bar-section.svelte';
|
||||||
import StatusBox from '$lib/components/shared-components/status-box.svelte';
|
import StatusBox from '$lib/components/shared-components/status-box.svelte';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
|
@ -8,32 +7,15 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SideBarSection>
|
<SideBarSection>
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_USER_MANAGEMENT} draggable="false">
|
<nav aria-label="Primary">
|
||||||
<SideBarButton
|
<SideBarLink title="Users" routeId={AppRoute.ADMIN_USER_MANAGEMENT} icon={mdiAccountMultipleOutline} />
|
||||||
title="Users"
|
<SideBarLink title="Jobs" routeId={AppRoute.ADMIN_JOBS} icon={mdiSync} />
|
||||||
icon={mdiAccountMultipleOutline}
|
<SideBarLink title="Settings" routeId={AppRoute.ADMIN_SETTINGS} icon={mdiCog} />
|
||||||
isSelected={$page.route.id === AppRoute.ADMIN_USER_MANAGEMENT}
|
<SideBarLink title="External Libraries" routeId={AppRoute.ADMIN_LIBRARY_MANAGEMENT} icon={mdiBookshelf} />
|
||||||
/>
|
<SideBarLink title="Server Stats" routeId={AppRoute.ADMIN_STATS} icon={mdiServer} />
|
||||||
</a>
|
<SideBarLink title="Repair" routeId={AppRoute.ADMIN_REPAIR} icon={mdiTools} preloadData={false} />
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_JOBS} draggable="false">
|
</nav>
|
||||||
<SideBarButton title="Jobs" icon={mdiSync} isSelected={$page.route.id === AppRoute.ADMIN_JOBS} />
|
|
||||||
</a>
|
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_SETTINGS} draggable="false">
|
|
||||||
<SideBarButton title="Settings" icon={mdiCog} isSelected={$page.route.id === AppRoute.ADMIN_SETTINGS} />
|
|
||||||
</a>
|
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_LIBRARY_MANAGEMENT} draggable="false">
|
|
||||||
<SideBarButton
|
|
||||||
title="External Libraries"
|
|
||||||
icon={mdiBookshelf}
|
|
||||||
isSelected={$page.route.id === AppRoute.ADMIN_LIBRARY_MANAGEMENT}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ADMIN_STATS} draggable="false">
|
|
||||||
<SideBarButton title="Server Stats" icon={mdiServer} isSelected={$page.route.id === AppRoute.ADMIN_STATS} />
|
|
||||||
</a>
|
|
||||||
<a data-sveltekit-preload-data="off" href={AppRoute.ADMIN_REPAIR} draggable="false">
|
|
||||||
<SideBarButton title="Repair" icon={mdiTools} isSelected={$page.route.id === AppRoute.ADMIN_REPAIR} />
|
|
||||||
</a>
|
|
||||||
<div class="mb-6 mt-auto">
|
<div class="mb-6 mt-auto">
|
||||||
<StatusBox />
|
<StatusBox />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { mdiInformationOutline } from '@mdi/js';
|
import { mdiInformationOutline } from '@mdi/js';
|
||||||
|
import { resolveRoute } from '$app/paths';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
export let title: string;
|
export let title: string;
|
||||||
|
export let routeId: string;
|
||||||
export let icon: string;
|
export let icon: string;
|
||||||
export let isSelected: boolean;
|
|
||||||
export let flippedLogo = false;
|
export let flippedLogo = false;
|
||||||
|
export let isSelected = false;
|
||||||
|
export let preloadData = true;
|
||||||
|
|
||||||
let showMoreInformation = false;
|
let showMoreInformation = false;
|
||||||
|
$: routePath = resolveRoute(routeId, {});
|
||||||
const dispatch = createEventDispatcher<{
|
$: isSelected = $page.route.id === routeId;
|
||||||
selected: void;
|
|
||||||
}>();
|
|
||||||
const onButtonClicked = () => dispatch('selected');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<a
|
||||||
<div
|
href={routePath}
|
||||||
on:click={onButtonClicked}
|
data-sveltekit-preload-data={preloadData ? 'hover' : 'off'}
|
||||||
on:keydown={onButtonClicked}
|
draggable="false"
|
||||||
|
aria-current={isSelected ? 'page' : undefined}
|
||||||
class="flex w-full place-items-center justify-between gap-4 rounded-r-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-immich-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary
|
class="flex w-full place-items-center justify-between gap-4 rounded-r-full py-3 transition-[padding] delay-100 duration-100 hover:cursor-pointer hover:bg-immich-gray hover:text-immich-primary dark:text-immich-dark-fg dark:hover:bg-immich-dark-gray dark:hover:text-immich-dark-primary
|
||||||
{isSelected
|
{isSelected
|
||||||
? 'bg-immich-primary/10 text-immich-primary hover:bg-immich-primary/25 dark:bg-immich-dark-primary/10 dark:text-immich-dark-primary'
|
? 'bg-immich-primary/10 text-immich-primary hover:bg-immich-primary/25 dark:bg-immich-dark-primary/10 dark:text-immich-dark-primary'
|
||||||
|
@ -29,14 +30,15 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="flex w-full place-items-center gap-4 overflow-hidden truncate">
|
<div class="flex w-full place-items-center gap-4 overflow-hidden truncate">
|
||||||
<Icon path={icon} size="1.5em" class="shrink-0" flipped={flippedLogo} />
|
<Icon path={icon} size="1.5em" class="shrink-0" flipped={flippedLogo} ariaHidden />
|
||||||
<p class="text-sm font-medium">{title}</p>
|
<span class="text-sm font-medium">{title}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="h-0 overflow-hidden transition-[height] delay-1000 duration-100 sm:group-hover:h-auto group-hover:sm:overflow-visible md:h-auto md:overflow-visible"
|
class="h-0 overflow-hidden transition-[height] delay-1000 duration-100 sm:group-hover:h-auto group-hover:sm:overflow-visible md:h-auto md:overflow-visible"
|
||||||
>
|
>
|
||||||
{#if $$slots.moreInformation}
|
{#if $$slots.moreInformation}
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
class="relative flex cursor-default select-none justify-center"
|
class="relative flex cursor-default select-none justify-center"
|
||||||
on:mouseenter={() => (showMoreInformation = true)}
|
on:mouseenter={() => (showMoreInformation = true)}
|
||||||
|
@ -60,4 +62,4 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
|
||||||
import { locale, sidebarSettings } from '$lib/stores/preferences.store';
|
import { locale, sidebarSettings } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/server-config.store';
|
import { featureFlags } from '$lib/stores/server-config.store';
|
||||||
import { getAlbumCount, getAssetStatistics } from '@immich/sdk';
|
import { getAlbumCount, getAssetStatistics } from '@immich/sdk';
|
||||||
|
@ -17,11 +16,10 @@
|
||||||
mdiMap,
|
mdiMap,
|
||||||
mdiTrashCanOutline,
|
mdiTrashCanOutline,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import { AppRoute } from '../../../constants';
|
|
||||||
import LoadingSpinner from '../loading-spinner.svelte';
|
import LoadingSpinner from '../loading-spinner.svelte';
|
||||||
import StatusBox from '../status-box.svelte';
|
import StatusBox from '../status-box.svelte';
|
||||||
import SideBarButton from './side-bar-button.svelte';
|
|
||||||
import SideBarSection from './side-bar-section.svelte';
|
import SideBarSection from './side-bar-section.svelte';
|
||||||
|
import SideBarLink from './side-bar-link.svelte';
|
||||||
|
|
||||||
const getStats = (dto: Parameters<typeof getAssetStatistics>[0]) => getAssetStatistics(dto);
|
const getStats = (dto: Parameters<typeof getAssetStatistics>[0]) => getAssetStatistics(dto);
|
||||||
|
|
||||||
|
@ -33,18 +31,18 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isFavoritesSelected = $page.route.id === '/(user)/favorites';
|
let isFavoritesSelected: boolean;
|
||||||
const isPhotosSelected = $page.route.id === '/(user)/photos';
|
let isPhotosSelected: boolean;
|
||||||
const isSharingSelected = $page.route.id === '/(user)/sharing';
|
let isSharingSelected: boolean;
|
||||||
const isTrashSelected = $page.route.id === '/(user)/trash';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SideBarSection>
|
<SideBarSection>
|
||||||
<a data-sveltekit-preload-data="hover" data-sveltekit-noscroll href={AppRoute.PHOTOS} draggable="false">
|
<nav aria-label="Primary">
|
||||||
<SideBarButton
|
<SideBarLink
|
||||||
title="Photos"
|
title="Photos"
|
||||||
|
routeId="/(user)/photos"
|
||||||
|
bind:isSelected={isPhotosSelected}
|
||||||
icon={isPhotosSelected ? mdiImageMultiple : mdiImageMultipleOutline}
|
icon={isPhotosSelected ? mdiImageMultiple : mdiImageMultipleOutline}
|
||||||
isSelected={isPhotosSelected}
|
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
{#await getStats({ isArchived: false })}
|
{#await getStats({ isArchived: false })}
|
||||||
|
@ -56,29 +54,24 @@
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarButton>
|
</SideBarLink>
|
||||||
</a>
|
{#if $featureFlags.search}
|
||||||
{#if $featureFlags.search}
|
<SideBarLink title="Explore" routeId="/(user)/explore" icon={mdiMagnify} />
|
||||||
<a data-sveltekit-preload-data="hover" data-sveltekit-noscroll href={AppRoute.EXPLORE} draggable="false">
|
{/if}
|
||||||
<SideBarButton title="Explore" icon={mdiMagnify} isSelected={$page.route.id === '/(user)/explore'} />
|
|
||||||
</a>
|
{#if $featureFlags.map}
|
||||||
{/if}
|
<SideBarLink title="Map" routeId="/(user)/map" icon={mdiMap} />
|
||||||
{#if $featureFlags.map}
|
{/if}
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.MAP} draggable="false">
|
|
||||||
<SideBarButton title="Map" icon={mdiMap} isSelected={$page.route.id === '/(user)/map'} />
|
{#if $sidebarSettings.people}
|
||||||
</a>
|
<SideBarLink title="People" routeId="/(user)/people" icon={mdiAccount} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if $sidebarSettings.people}
|
{#if $sidebarSettings.sharing}
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.PEOPLE} draggable="false">
|
<SideBarLink
|
||||||
<SideBarButton title="People" icon={mdiAccount} isSelected={$page.route.id === '/(user)/people'} />
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
{#if $sidebarSettings.sharing}
|
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.SHARING} draggable="false">
|
|
||||||
<SideBarButton
|
|
||||||
title="Sharing"
|
title="Sharing"
|
||||||
|
routeId="/(user)/sharing"
|
||||||
icon={isSharingSelected ? mdiAccountMultiple : mdiAccountMultipleOutline}
|
icon={isSharingSelected ? mdiAccountMultiple : mdiAccountMultipleOutline}
|
||||||
isSelected={isSharingSelected}
|
bind:isSelected={isSharingSelected}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
{#await handleAlbumCount()}
|
{#await handleAlbumCount()}
|
||||||
|
@ -89,19 +82,18 @@
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarButton>
|
</SideBarLink>
|
||||||
</a>
|
{/if}
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="text-xs transition-all duration-200 dark:text-immich-dark-fg">
|
<div class="text-xs transition-all duration-200 dark:text-immich-dark-fg">
|
||||||
<p class="hidden p-6 group-hover:sm:block md:block">LIBRARY</p>
|
<p class="hidden p-6 group-hover:sm:block md:block">LIBRARY</p>
|
||||||
<hr class="mx-4 mb-[31px] mt-8 block group-hover:sm:hidden md:hidden" />
|
<hr class="mx-4 mb-[31px] mt-8 block group-hover:sm:hidden md:hidden" />
|
||||||
</div>
|
</div>
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.FAVORITES} draggable="false">
|
<SideBarLink
|
||||||
<SideBarButton
|
|
||||||
title="Favorites"
|
title="Favorites"
|
||||||
|
routeId="/(user)/favorites"
|
||||||
icon={isFavoritesSelected ? mdiHeartMultiple : mdiHeartMultipleOutline}
|
icon={isFavoritesSelected ? mdiHeartMultiple : mdiHeartMultipleOutline}
|
||||||
isSelected={isFavoritesSelected}
|
bind:isSelected={isFavoritesSelected}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
{#await getStats({ isFavorite: true })}
|
{#await getStats({ isFavorite: true })}
|
||||||
|
@ -113,15 +105,8 @@
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarButton>
|
</SideBarLink>
|
||||||
</a>
|
<SideBarLink title="Albums" routeId="/(user)/albums" icon={mdiImageAlbum} flippedLogo>
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ALBUMS} draggable="false">
|
|
||||||
<SideBarButton
|
|
||||||
title="Albums"
|
|
||||||
icon={mdiImageAlbum}
|
|
||||||
flippedLogo={true}
|
|
||||||
isSelected={$page.route.id === '/(user)/albums'}
|
|
||||||
>
|
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
{#await handleAlbumCount()}
|
{#await handleAlbumCount()}
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
|
@ -131,10 +116,9 @@
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarButton>
|
</SideBarLink>
|
||||||
</a>
|
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.ARCHIVE} draggable="false">
|
<SideBarLink title="Archive" routeId="/(user)/archive" icon={mdiArchiveArrowDownOutline}>
|
||||||
<SideBarButton title="Archive" icon={mdiArchiveArrowDownOutline} isSelected={$page.route.id === '/(user)/archive'}>
|
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
{#await getStats({ isArchived: true })}
|
{#await getStats({ isArchived: true })}
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
|
@ -145,12 +129,10 @@
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarButton>
|
</SideBarLink>
|
||||||
</a>
|
|
||||||
|
|
||||||
{#if $featureFlags.trash}
|
{#if $featureFlags.trash}
|
||||||
<a data-sveltekit-preload-data="hover" href={AppRoute.TRASH} draggable="false">
|
<SideBarLink title="Trash" routeId="/(user)/trash" icon={mdiTrashCanOutline}>
|
||||||
<SideBarButton title="Trash" icon={mdiTrashCanOutline} isSelected={isTrashSelected}>
|
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
{#await getStats({ isTrashed: true })}
|
{#await getStats({ isTrashed: true })}
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
|
@ -161,9 +143,9 @@
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarButton>
|
</SideBarLink>
|
||||||
</a>
|
{/if}
|
||||||
{/if}
|
</nav>
|
||||||
|
|
||||||
<!-- Status Box -->
|
<!-- Status Box -->
|
||||||
<div class="mb-6 mt-auto">
|
<div class="mb-6 mt-auto">
|
||||||
|
|
Loading…
Reference in a new issue