mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
feat: implement navigation icon style store and UI settings for emoji/icon toggle
This commit is contained in:
43
src/lib/client/stores/navIcons.ts
Normal file
43
src/lib/client/stores/navIcons.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Navigation icon style store (emoji vs lucide icons)
|
||||
*/
|
||||
|
||||
import { writable } from 'svelte/store';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
export type NavIconStyle = 'emoji' | 'lucide';
|
||||
|
||||
function createNavIconStore() {
|
||||
let initialStyle: NavIconStyle = 'lucide';
|
||||
if (browser) {
|
||||
const stored = localStorage.getItem('navIconStyle') as NavIconStyle | null;
|
||||
if (stored && (stored === 'emoji' || stored === 'lucide')) {
|
||||
initialStyle = stored;
|
||||
}
|
||||
}
|
||||
|
||||
const { subscribe, set } = writable<NavIconStyle>(initialStyle);
|
||||
|
||||
function setStyle(style: NavIconStyle) {
|
||||
set(style);
|
||||
if (browser) {
|
||||
localStorage.setItem('navIconStyle', style);
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (browser) {
|
||||
const current = localStorage.getItem('navIconStyle') as NavIconStyle | null;
|
||||
const newStyle = current === 'emoji' ? 'lucide' : 'emoji';
|
||||
setStyle(newStyle);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
setStyle,
|
||||
toggle
|
||||
};
|
||||
}
|
||||
|
||||
export const navIconStore = createNavIconStore();
|
||||
@@ -1,17 +1,19 @@
|
||||
<script lang="ts">
|
||||
import GroupHeader from './groupHeader.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { Snippet, Component } from 'svelte';
|
||||
import type { IconProps } from 'lucide-svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
href: string;
|
||||
icon?: Component<IconProps>;
|
||||
initialOpen?: boolean;
|
||||
hasItems?: boolean;
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
let { label, href, initialOpen = true, hasItems = false, children }: Props = $props();
|
||||
let { label, href, icon, initialOpen = true, hasItems = false, children }: Props = $props();
|
||||
let isOpen = $state(initialOpen);
|
||||
|
||||
function toggleOpen() {
|
||||
@@ -20,7 +22,7 @@
|
||||
</script>
|
||||
|
||||
<div class="mb-4">
|
||||
<GroupHeader {label} {href} {isOpen} {hasItems} onToggle={toggleOpen} />
|
||||
<GroupHeader {label} {href} {icon} {isOpen} {hasItems} onToggle={toggleOpen} />
|
||||
|
||||
{#if isOpen && hasItems && children}
|
||||
<div class="mt-2 grid grid-cols-[auto_1fr]" transition:slide={{ duration: 200 }}>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import type { Component } from 'svelte';
|
||||
import type { IconProps } from 'lucide-svelte';
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
href: string;
|
||||
icon?: Component<IconProps>;
|
||||
isOpen: boolean;
|
||||
hasItems: boolean;
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
let { label, href, isOpen, hasItems, onToggle }: Props = $props();
|
||||
let { label, href, icon, isOpen, hasItems, onToggle }: Props = $props();
|
||||
|
||||
const isActive = $derived($page.url.pathname === href || $page.url.pathname.startsWith(href + '/'));
|
||||
</script>
|
||||
@@ -24,6 +27,9 @@
|
||||
? 'bg-neutral-200 hover:bg-neutral-300 dark:bg-neutral-800 dark:hover:bg-neutral-700'
|
||||
: ''}"
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component this={icon} class="h-4 w-4" />
|
||||
{/if}
|
||||
{label}
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
import Group from './group.svelte';
|
||||
import GroupItem from './groupItem.svelte';
|
||||
import Version from './version.svelte';
|
||||
import { Home, Sliders, Palette, Microscope, Tag, Clock, Settings } from 'lucide-svelte';
|
||||
import { navIconStore } from '$stores/navIcons';
|
||||
|
||||
export let collapsed: boolean = false;
|
||||
|
||||
$: useEmoji = $navIconStore === 'emoji';
|
||||
</script>
|
||||
|
||||
<nav
|
||||
@@ -11,24 +15,24 @@
|
||||
class:-translate-x-[calc(100%-24px)]={collapsed}
|
||||
>
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<Group label="🏠 Home" href="/" hasItems={true}>
|
||||
<Group label={useEmoji ? '🏠 Home' : 'Home'} href="/" icon={useEmoji ? undefined : Home} hasItems={true}>
|
||||
<GroupItem label="Databases" href="/databases" />
|
||||
<GroupItem label="Arrs" href="/arr" />
|
||||
</Group>
|
||||
|
||||
<Group label="⚡ Quality Profiles" href="/quality-profiles" initialOpen={true} hasItems={true}>
|
||||
<Group label={useEmoji ? '⚡ Quality Profiles' : 'Quality Profiles'} href="/quality-profiles" icon={useEmoji ? undefined : Sliders} initialOpen={true} hasItems={true}>
|
||||
<GroupItem label="Entity Testing" href="/quality-profiles/entity-testing" />
|
||||
</Group>
|
||||
|
||||
<Group label="🎨 Custom Formats" href="/custom-formats" initialOpen={false} />
|
||||
<Group label={useEmoji ? '🎨 Custom Formats' : 'Custom Formats'} href="/custom-formats" icon={useEmoji ? undefined : Palette} initialOpen={false} />
|
||||
|
||||
<Group label="🔬 Regular Expressions" href="/regular-expressions" initialOpen={false} />
|
||||
<Group label={useEmoji ? '🔬 Regular Expressions' : 'Regular Expressions'} href="/regular-expressions" icon={useEmoji ? undefined : Microscope} initialOpen={false} />
|
||||
|
||||
<Group label="🏷️ Media Management" href="/media-management" initialOpen={false} />
|
||||
<Group label={useEmoji ? '🏷️ Media Management' : 'Media Management'} href="/media-management" icon={useEmoji ? undefined : Tag} initialOpen={false} />
|
||||
|
||||
<Group label="⏱️ Delay Profiles" href="/delay-profiles" initialOpen={false} />
|
||||
<Group label={useEmoji ? '⏱️ Delay Profiles' : 'Delay Profiles'} href="/delay-profiles" icon={useEmoji ? undefined : Clock} initialOpen={false} />
|
||||
|
||||
<Group label="⚙️ Settings" href="/settings" initialOpen={true} hasItems={true}>
|
||||
<Group label={useEmoji ? '⚙️ Settings' : 'Settings'} href="/settings" icon={useEmoji ? undefined : Settings} initialOpen={true} hasItems={true}>
|
||||
<GroupItem label="General" href="/settings/general" />
|
||||
<GroupItem label="Jobs" href="/settings/jobs" />
|
||||
<GroupItem label="Logs" href="/settings/logs" />
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import BackupSettings from './components/BackupSettings.svelte';
|
||||
import AISettings from './components/AISettings.svelte';
|
||||
import TMDBSettings from './components/TMDBSettings.svelte';
|
||||
import UISettings from './components/UISettings.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -17,6 +18,9 @@
|
||||
</div>
|
||||
|
||||
<div class="space-y-8">
|
||||
<!-- UI Preferences -->
|
||||
<UISettings />
|
||||
|
||||
<!-- Backup Configuration -->
|
||||
<BackupSettings settings={data.backupSettings} />
|
||||
|
||||
|
||||
53
src/routes/settings/general/components/UISettings.svelte
Normal file
53
src/routes/settings/general/components/UISettings.svelte
Normal file
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { navIconStore, type NavIconStyle } from '$stores/navIcons';
|
||||
import { Check } from 'lucide-svelte';
|
||||
import IconCheckbox from '$ui/form/IconCheckbox.svelte';
|
||||
|
||||
let useEmojis = $derived($navIconStore === 'emoji');
|
||||
|
||||
function toggle() {
|
||||
navIconStore.toggle();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="rounded-lg border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-900"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="border-b border-neutral-200 px-6 py-4 dark:border-neutral-800">
|
||||
<h2 class="text-xl font-semibold text-neutral-900 dark:text-neutral-50">
|
||||
Interface
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
Customize the look and feel of the application
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Settings -->
|
||||
<div class="p-6">
|
||||
<div class="space-y-3">
|
||||
<h3 class="text-sm font-semibold text-neutral-900 dark:text-neutral-50">Navigation</h3>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<IconCheckbox
|
||||
icon={Check}
|
||||
checked={useEmojis}
|
||||
on:click={toggle}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="flex-1 text-left"
|
||||
on:click={toggle}
|
||||
>
|
||||
<span class="text-sm font-medium text-neutral-900 dark:text-neutral-50">
|
||||
Use Emojis
|
||||
</span>
|
||||
<p class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
Show emojis instead of icons in the sidebar navigation
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user