diff --git a/apps/calendar/apps/web/src/lib/stores/navigation.ts b/apps/calendar/apps/web/src/lib/stores/navigation.ts index fb8b2b973..59cfe67d9 100644 --- a/apps/calendar/apps/web/src/lib/stores/navigation.ts +++ b/apps/calendar/apps/web/src/lib/stores/navigation.ts @@ -1,5 +1,6 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); -export const isToolbarCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({ + withToolbar: true, + toolbarCollapsedDefault: false, +}); diff --git a/apps/chat/apps/web/src/lib/stores/navigation.ts b/apps/chat/apps/web/src/lib/stores/navigation.ts index f4072e926..5527a3e14 100644 --- a/apps/chat/apps/web/src/lib/stores/navigation.ts +++ b/apps/chat/apps/web/src/lib/stores/navigation.ts @@ -1,4 +1,3 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/clock/apps/web/src/lib/stores/navigation.ts b/apps/clock/apps/web/src/lib/stores/navigation.ts index 501357be9..bc4ec2a00 100644 --- a/apps/clock/apps/web/src/lib/stores/navigation.ts +++ b/apps/clock/apps/web/src/lib/stores/navigation.ts @@ -1,35 +1,5 @@ -/** - * Navigation Store - Manages navigation state - */ +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -import { writable } from 'svelte/store'; -import { browser } from '$app/environment'; - -const SIDEBAR_MODE_KEY = 'clock_sidebar_mode'; -const NAV_COLLAPSED_KEY = 'clock_nav_collapsed'; - -// Check localStorage for initial values -function getInitialSidebarMode(): boolean { - if (!browser) return false; - return localStorage.getItem(SIDEBAR_MODE_KEY) === 'true'; -} - -function getInitialCollapsed(): boolean { - if (!browser) return false; - return localStorage.getItem(NAV_COLLAPSED_KEY) === 'true'; -} - -// Create stores -export const isSidebarMode = writable(getInitialSidebarMode()); -export const isNavCollapsed = writable(getInitialCollapsed()); - -// Subscribe to persist changes -if (browser) { - isSidebarMode.subscribe((value) => { - localStorage.setItem(SIDEBAR_MODE_KEY, String(value)); - }); - - isNavCollapsed.subscribe((value) => { - localStorage.setItem(NAV_COLLAPSED_KEY, String(value)); - }); -} +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores({ + storageKey: 'clock', +}); diff --git a/apps/contacts/apps/web/src/lib/stores/navigation.ts b/apps/contacts/apps/web/src/lib/stores/navigation.ts index f4072e926..5527a3e14 100644 --- a/apps/contacts/apps/web/src/lib/stores/navigation.ts +++ b/apps/contacts/apps/web/src/lib/stores/navigation.ts @@ -1,4 +1,3 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/manacore/apps/web/src/lib/stores/navigation.ts b/apps/manacore/apps/web/src/lib/stores/navigation.ts index f4072e926..5527a3e14 100644 --- a/apps/manacore/apps/web/src/lib/stores/navigation.ts +++ b/apps/manacore/apps/web/src/lib/stores/navigation.ts @@ -1,4 +1,3 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/manadeck/apps/web/src/lib/stores/navigation.ts b/apps/manadeck/apps/web/src/lib/stores/navigation.ts index f4072e926..5527a3e14 100644 --- a/apps/manadeck/apps/web/src/lib/stores/navigation.ts +++ b/apps/manadeck/apps/web/src/lib/stores/navigation.ts @@ -1,4 +1,3 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/matrix/apps/web/src/lib/stores/navigation.ts b/apps/matrix/apps/web/src/lib/stores/navigation.ts index f4072e926..5527a3e14 100644 --- a/apps/matrix/apps/web/src/lib/stores/navigation.ts +++ b/apps/matrix/apps/web/src/lib/stores/navigation.ts @@ -1,4 +1,3 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/presi/apps/web/src/lib/stores/navigation.ts b/apps/presi/apps/web/src/lib/stores/navigation.ts index f4072e926..5527a3e14 100644 --- a/apps/presi/apps/web/src/lib/stores/navigation.ts +++ b/apps/presi/apps/web/src/lib/stores/navigation.ts @@ -1,4 +1,3 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/storage/apps/web/src/lib/stores/navigation.ts b/apps/storage/apps/web/src/lib/stores/navigation.ts index ed3ac1cb1..5527a3e14 100644 --- a/apps/storage/apps/web/src/lib/stores/navigation.ts +++ b/apps/storage/apps/web/src/lib/stores/navigation.ts @@ -1,8 +1,3 @@ -/** - * Navigation Store - Manages sidebar and navigation state - */ +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -import { writable } from 'svelte/store'; - -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); diff --git a/apps/todo/apps/web/src/lib/stores/navigation.ts b/apps/todo/apps/web/src/lib/stores/navigation.ts index d61bbd81e..c033f7e13 100644 --- a/apps/todo/apps/web/src/lib/stores/navigation.ts +++ b/apps/todo/apps/web/src/lib/stores/navigation.ts @@ -1,5 +1,6 @@ -import { writable } from 'svelte/store'; +import { createSimpleNavigationStores } from '@manacore/shared-stores'; -export const isSidebarMode = writable(false); -export const isNavCollapsed = writable(false); -export const isToolbarCollapsed = writable(true); +export const { isSidebarMode, isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({ + withToolbar: true, + toolbarCollapsedDefault: true, +}); diff --git a/docs/CONSOLIDATION_OPPORTUNITIES.md b/docs/CONSOLIDATION_OPPORTUNITIES.md index d0cbc21e5..0f61c05f0 100644 --- a/docs/CONSOLIDATION_OPPORTUNITIES.md +++ b/docs/CONSOLIDATION_OPPORTUNITIES.md @@ -14,7 +14,7 @@ | ~~**MITTEL**~~ | ~~TypeScript Configs~~ | ~~400 LOC~~ ✅ **~280 LOC entfernt** | ~~Niedrig~~ | | **MITTEL** | UI Component Cleanup | 400 LOC | Niedrig | | ~~**MITTEL**~~ | ~~Vite Configs~~ | ~~300 LOC~~ ✅ **~350 LOC entfernt** | ~~Niedrig~~ | -| **MITTEL** | Navigation Stores | 50 LOC | Niedrig | +| ~~**MITTEL**~~ | ~~Navigation Stores~~ | ~~50 LOC~~ ✅ **~50 LOC entfernt** | ~~Niedrig~~ | | ~~**NIEDRIG**~~ | ~~Drizzle Configs~~ | ~~200 LOC~~ ✅ **~160 LOC entfernt** | ~~Niedrig~~ | | **NIEDRIG** | Logger Utilities | 130 LOC | Niedrig | @@ -174,30 +174,38 @@ export const todoSettings = { --- -### 2.2 MITTEL: Navigation Stores (50 LOC) +### ~~2.2 MITTEL: Navigation Stores~~ ✅ ERLEDIGT (~50 LOC gespart) -**Problem:** 9 Apps haben fast identische Navigation-Stores. +**Status:** `createSimpleNavigationStores()` Factory erstellt und 10 Apps migriert (29.01.2026) -**Pattern (5-6 LOC pro App):** +**Erstellte Factory:** `packages/shared-stores/src/navigation-simple.ts` +- Erstellt `isSidebarMode`, `isNavCollapsed` writable Stores +- Optional: `isToolbarCollapsed` mit `withToolbar: true` +- Optional: localStorage Persistenz mit `storageKey` + +**Migrierte Apps (10 von 10):** +- ✅ Einfach: chat, contacts, manacore, manadeck, matrix, presi, storage +- ✅ Mit Toolbar: calendar, todo +- ✅ Mit Persistenz: clock + +**Vorher (5-36 LOC):** ```typescript import { writable } from 'svelte/store'; export const isSidebarMode = writable(false); export const isNavCollapsed = writable(false); +// + optional localStorage handling (30+ LOC) ``` -**Ausnahme:** Clock (36 LOC) mit localStorage Persistenz + Media Query Listeners - -**Empfehlung:** Factory in `@manacore/shared-stores` - +**Nachher (3-5 LOC):** ```typescript -export function createNavigationStore(options?: { - persist?: boolean; - mediaQueryCollapse?: string; -}) { - // ... -} +import { createSimpleNavigationStores } from '@manacore/shared-stores'; +export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores({ + storageKey: 'clock', // optional +}); ``` +**Einsparung:** ~50 LOC (besonders Clock: 36 → 4 LOC) + --- ### 2.3 NIEDRIG: Theme Stores Migration @@ -427,7 +435,7 @@ export default createDrizzleConfig({ dbName: 'chat' }); | ~~`createAppSettingsStore()` Factory erstellen~~ | ~~600~~ → **323** | ~~Mittel~~ | ✅ Erledigt | | ~~`@manacore/shared-tsconfig` Package erstellen~~ | ~~400~~ → **280** | ~~Niedrig~~ | ✅ Erledigt | | ~~`@manacore/shared-vite-config` erweitern (15 Apps)~~ | ~~300~~ → **350** | ~~Niedrig~~ | ✅ Erledigt | -| Navigation Store Factory erstellen | 50 | Niedrig | Offen | +| ~~Navigation Store Factory erstellen~~ | ~~50~~ → **50** | ~~Niedrig~~ | ✅ Erledigt | ### Phase 3: Backend Setup (5-7 Tage, ~2.000 LOC) diff --git a/packages/shared-stores/src/index.ts b/packages/shared-stores/src/index.ts index 557dc07cf..6e69c4aff 100644 --- a/packages/shared-stores/src/index.ts +++ b/packages/shared-stores/src/index.ts @@ -15,3 +15,8 @@ export { type AppSettingsStore, type AppSettingsStoreOptions, } from './settings.svelte'; +export { + createSimpleNavigationStores, + type SimpleNavigationStores, + type SimpleNavigationOptions, +} from './navigation-simple'; diff --git a/packages/shared-stores/src/navigation-simple.ts b/packages/shared-stores/src/navigation-simple.ts new file mode 100644 index 000000000..e4b73b2a3 --- /dev/null +++ b/packages/shared-stores/src/navigation-simple.ts @@ -0,0 +1,94 @@ +/** + * Simple Navigation Stores Factory + * Creates writable stores for navigation state with optional persistence. + */ + +import { writable, type Writable } from 'svelte/store'; + +export interface SimpleNavigationStores { + /** Whether the app is in sidebar mode (desktop) */ + isSidebarMode: Writable; + /** Whether the nav is collapsed */ + isNavCollapsed: Writable; + /** Whether the toolbar is collapsed (optional) */ + isToolbarCollapsed?: Writable; +} + +export interface SimpleNavigationOptions { + /** App name for localStorage keys (e.g., 'clock' -> 'clock_sidebar_mode') */ + storageKey?: string; + /** Include isToolbarCollapsed store */ + withToolbar?: boolean; + /** Default value for toolbar collapsed */ + toolbarCollapsedDefault?: boolean; +} + +/** + * Creates simple navigation stores compatible with Svelte's writable API. + * + * @example + * // Basic usage (no persistence) + * export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores(); + * + * @example + * // With persistence + * export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores({ + * storageKey: 'clock', + * }); + * + * @example + * // With toolbar + * export const { isSidebarMode, isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({ + * withToolbar: true, + * toolbarCollapsedDefault: true, + * }); + */ +export function createSimpleNavigationStores( + options: SimpleNavigationOptions = {} +): SimpleNavigationStores { + const { storageKey, withToolbar = false, toolbarCollapsedDefault = false } = options; + + const isBrowser = typeof localStorage !== 'undefined'; + + // Helper to get initial value from localStorage + function getInitialValue(key: string, defaultValue: boolean): boolean { + if (!isBrowser || !storageKey) return defaultValue; + const stored = localStorage.getItem(`${storageKey}_${key}`); + return stored !== null ? stored === 'true' : defaultValue; + } + + // Helper to create a persisted writable + function createPersistedWritable(key: string, defaultValue: boolean): Writable { + const store = writable(getInitialValue(key, defaultValue)); + + if (isBrowser && storageKey) { + store.subscribe((value) => { + localStorage.setItem(`${storageKey}_${key}`, String(value)); + }); + } + + return store; + } + + // Create stores (persisted if storageKey provided, otherwise simple) + const isSidebarMode = storageKey + ? createPersistedWritable('sidebar_mode', false) + : writable(false); + + const isNavCollapsed = storageKey + ? createPersistedWritable('nav_collapsed', false) + : writable(false); + + const result: SimpleNavigationStores = { + isSidebarMode, + isNavCollapsed, + }; + + if (withToolbar) { + result.isToolbarCollapsed = storageKey + ? createPersistedWritable('toolbar_collapsed', toolbarCollapsedDefault) + : writable(toolbarCollapsedDefault); + } + + return result; +}