mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 01:29:40 +02:00
♻️ refactor(shared-ui): simplify PillNavigation - remove sidebar mode
- Remove isSidebarMode and onModeChange props from PillNavigation - Remove desktopPosition prop (always bottom now) - Remove toolbarContent snippet support - Simplify PillTabGroup (remove sidebar mode) - Update navigation-simple.ts store factory - Remove navigation position settings from GlobalSettingsSection - Update all 12 app layouts to use simplified navigation - Add missing @sqlite.org/sqlite-wasm dependency to calendar BREAKING CHANGE: PillNavigation no longer supports sidebar mode. Navigation is now always horizontal at the bottom of the screen. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e5109da732
commit
497b12c561
27 changed files with 77 additions and 1104 deletions
|
|
@ -55,6 +55,7 @@
|
|||
"@manacore/shared-ui": "workspace:*",
|
||||
"@manacore/shared-utils": "workspace:*",
|
||||
"@manacore/shared-app-onboarding": "workspace:*",
|
||||
"@sqlite.org/sqlite-wasm": "^3.49.1-build1",
|
||||
"@neodrag/svelte": "^2.3.3",
|
||||
"d3-force": "^3.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({
|
||||
export const { isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({
|
||||
withToolbar: true,
|
||||
toolbarCollapsedDefault: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -520,7 +520,6 @@
|
|||
homeRoute="/"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
desktopPosition="bottom"
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -542,13 +541,7 @@
|
|||
profileHref="/profile"
|
||||
allAppsHref="/apps"
|
||||
onOpenInPanel={handleOpenInPanel}
|
||||
>
|
||||
{#snippet toolbarContent()}
|
||||
{#if showCalendarToolbar}
|
||||
<CalendarToolbarContent vertical={true} />
|
||||
{/if}
|
||||
{/snippet}
|
||||
</PillNavigation>
|
||||
/>
|
||||
|
||||
<!-- Date strip (only on main calendar page) -->
|
||||
{#if showCalendarToolbar}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores();
|
||||
export const { isNavCollapsed } = createSimpleNavigationStores();
|
||||
|
|
|
|||
|
|
@ -15,10 +15,7 @@
|
|||
} from '@manacore/shared-theme';
|
||||
import type { ThemeVariant } from '@manacore/shared-theme';
|
||||
import { filterHiddenNavItems } from '@manacore/shared-theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
|
|
@ -32,7 +29,6 @@
|
|||
let { children, data }: { children: any; data: LayoutData } = $props();
|
||||
|
||||
let isChecking = $state(true);
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Use theme store's isDark directly
|
||||
|
|
@ -125,14 +121,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('chat-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -166,13 +154,6 @@
|
|||
// Initialize theme
|
||||
theme.initialize();
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('chat-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('chat-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -213,7 +194,7 @@
|
|||
{:else}
|
||||
<!-- Navigation Layout -->
|
||||
<div class="layout-container">
|
||||
<!-- Floating/Sidebar Pill Navigation -->
|
||||
<!-- Floating Pill Navigation -->
|
||||
<PillNavigation
|
||||
items={navItems}
|
||||
currentPath={$page.url.pathname}
|
||||
|
|
@ -221,11 +202,8 @@
|
|||
homeRoute="/chat"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -247,13 +225,8 @@
|
|||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<!-- Main Content with dynamic padding based on nav mode -->
|
||||
<main
|
||||
class="main-content bg-background"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed}
|
||||
class:chat-page={isChatPage}
|
||||
>
|
||||
<!-- Main Content -->
|
||||
<main class="main-content bg-background" class:chat-page={isChatPage}>
|
||||
{#if isChatPage}
|
||||
<!-- Full-width layout for chat pages -->
|
||||
{@render children()}
|
||||
|
|
@ -275,17 +248,7 @@
|
|||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
/* Floating nav mode - add top padding for fixed nav */
|
||||
.main-content.floating-mode {
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
/* Sidebar mode - add left padding for sidebar nav */
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
/* Chat page - no content wrapper, but keep nav padding */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores({
|
||||
export const { isNavCollapsed } = createSimpleNavigationStores({
|
||||
storageKey: 'clock',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@
|
|||
} from '@manacore/shared-theme';
|
||||
import type { ThemeVariant } from '@manacore/shared-theme';
|
||||
import { filterHiddenNavItems } from '@manacore/shared-theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
|
||||
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||
|
|
@ -114,7 +111,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Use theme store's isDark directly
|
||||
|
|
@ -213,14 +209,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('clock-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -250,13 +238,6 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('clock-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('clock-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -291,11 +272,8 @@
|
|||
currentPath={$page.url.pathname}
|
||||
appName="Clock"
|
||||
homeRoute="/"
|
||||
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
showThemeToggle={true}
|
||||
|
|
@ -320,11 +298,7 @@
|
|||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<main
|
||||
class="main-content bg-background"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed}
|
||||
>
|
||||
<main class="main-content bg-background">
|
||||
<div class="content-wrapper">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
@ -351,17 +325,9 @@
|
|||
}
|
||||
|
||||
.main-content {
|
||||
transition: all 300ms ease;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.main-content.floating-mode {
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores();
|
||||
export const { isNavCollapsed } = createSimpleNavigationStores();
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@
|
|||
import { theme } from '$lib/stores/theme';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { userSettings } from '$lib/stores/user-settings.svelte';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
import { onboardingStore } from '$lib/stores/onboarding.svelte';
|
||||
import { OnboardingWizard } from '$lib/components/onboarding';
|
||||
|
|
@ -31,7 +28,6 @@
|
|||
const appItems = getPillAppItems('manacore');
|
||||
|
||||
let loading = $state(true);
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Get theme state
|
||||
|
|
@ -121,14 +117,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('manacore-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -177,13 +165,6 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('manacore-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('manacore-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -239,11 +220,8 @@
|
|||
onLogout={handleSignOut}
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -264,14 +242,8 @@
|
|||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<!-- Main content with dynamic padding -->
|
||||
<main
|
||||
class="transition-all duration-300 {isCollapsed
|
||||
? ''
|
||||
: isSidebarMode
|
||||
? 'pl-[180px]'
|
||||
: 'pt-20'}"
|
||||
>
|
||||
<!-- Main content -->
|
||||
<main class="pb-24">
|
||||
<div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores();
|
||||
export const { isNavCollapsed } = createSimpleNavigationStores();
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@
|
|||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { userSettings } from '$lib/stores/user-settings.svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
||||
import {
|
||||
|
|
@ -28,7 +25,6 @@
|
|||
|
||||
let { children } = $props();
|
||||
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Get theme state
|
||||
|
|
@ -120,14 +116,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('manadeck-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -166,13 +154,6 @@
|
|||
goto(userSettings.startPage, { replaceState: true });
|
||||
}
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('manadeck-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('manadeck-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -204,11 +185,8 @@
|
|||
onLogout={handleSignOut}
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -229,14 +207,8 @@
|
|||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<!-- Main content with dynamic padding -->
|
||||
<main
|
||||
class="transition-all duration-300 {isCollapsed
|
||||
? ''
|
||||
: isSidebarMode
|
||||
? 'pl-[180px]'
|
||||
: 'pt-20'}"
|
||||
>
|
||||
<!-- Main content -->
|
||||
<main class="pb-24">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,32 +3,9 @@
|
|||
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// Check if on mobile/tablet width
|
||||
function checkSidebarMode(): boolean {
|
||||
if (!browser) return false;
|
||||
return window.innerWidth < 1024;
|
||||
}
|
||||
|
||||
// Create reactive stores using Svelte 5 runes
|
||||
let _isSidebarMode = $state(checkSidebarMode());
|
||||
let _isNavCollapsed = $state(false);
|
||||
|
||||
// Listen for resize events
|
||||
if (browser) {
|
||||
window.addEventListener('resize', () => {
|
||||
_isSidebarMode = checkSidebarMode();
|
||||
});
|
||||
}
|
||||
|
||||
export const isSidebarMode = {
|
||||
get value() {
|
||||
return _isSidebarMode;
|
||||
},
|
||||
set(value: boolean) {
|
||||
_isSidebarMode = value;
|
||||
},
|
||||
};
|
||||
|
||||
export const isNavCollapsed = {
|
||||
get value() {
|
||||
return _isNavCollapsed;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@
|
|||
EXTENDED_THEME_VARIANTS,
|
||||
} from '@manacore/shared-theme';
|
||||
import type { ThemeVariant } from '@manacore/shared-theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation.svelte';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation.svelte';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
|
|
@ -86,7 +83,6 @@
|
|||
let initError = $state<string | null>(null);
|
||||
|
||||
// Navigation state
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Theme state
|
||||
|
|
@ -145,14 +141,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('matrix-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -176,13 +164,6 @@
|
|||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('matrix-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('matrix-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -333,11 +314,8 @@
|
|||
homeRoute="/chat"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition="bottom"
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -358,11 +336,7 @@
|
|||
/>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main
|
||||
class="main-content bg-background"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed}
|
||||
>
|
||||
<main class="main-content bg-background">
|
||||
{@render children()}
|
||||
</main>
|
||||
</div>
|
||||
|
|
@ -385,11 +359,6 @@
|
|||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
/* Sidebar mode - add left padding for sidebar nav */
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -29,24 +29,16 @@
|
|||
let { children } = $props();
|
||||
|
||||
// PillNav state
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Load persisted nav state
|
||||
$effect(() => {
|
||||
if (browser) {
|
||||
const savedSidebarMode = localStorage.getItem('picture-nav-sidebar');
|
||||
const savedCollapsed = localStorage.getItem('picture-nav-collapsed');
|
||||
if (savedSidebarMode !== null) isSidebarMode = savedSidebarMode === 'true';
|
||||
if (savedCollapsed !== null) isCollapsed = savedCollapsed === 'true';
|
||||
}
|
||||
});
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
if (browser) localStorage.setItem('picture-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
if (browser) localStorage.setItem('picture-nav-collapsed', String(collapsed));
|
||||
|
|
@ -263,11 +255,8 @@
|
|||
onLogout={handleLogout}
|
||||
onToggleTheme={handleToggleTheme}
|
||||
isDark={theme.isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -290,11 +279,7 @@
|
|||
{/if}
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main
|
||||
class="main-content transition-all duration-300"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed && $isUIVisible}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed && $isUIVisible}
|
||||
>
|
||||
<main class="main-content pb-24">
|
||||
<div class="min-h-screen">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
@ -306,24 +291,7 @@
|
|||
{/if}
|
||||
|
||||
<style>
|
||||
/* Floating nav mode - add top padding for fixed nav */
|
||||
.main-content.floating-mode {
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
/* Sidebar mode - add left padding for sidebar nav */
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.main-content.floating-mode {
|
||||
padding-top: 70px;
|
||||
}
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 0;
|
||||
padding-top: 70px;
|
||||
}
|
||||
.main-content {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores();
|
||||
export const { isNavCollapsed } = createSimpleNavigationStores();
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@
|
|||
import { userSettings } from '$lib/stores/user-settings.svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { THEME_DEFINITIONS } from '@manacore/shared-theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
|
||||
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||
|
|
@ -22,7 +19,6 @@
|
|||
|
||||
let { children } = $props();
|
||||
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Theme variant dropdown items
|
||||
|
|
@ -72,14 +68,6 @@
|
|||
return hideNavRoutes.some((route) => pathname.startsWith(route));
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('presi-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -132,13 +120,6 @@
|
|||
goto(userSettings.startPage, { replaceState: true });
|
||||
}
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('presi-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('presi-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -158,7 +139,7 @@
|
|||
{:else}
|
||||
<!-- Navigation Layout -->
|
||||
<div class="layout-container">
|
||||
<!-- Floating/Sidebar Pill Navigation -->
|
||||
<!-- Floating Pill Navigation -->
|
||||
<PillNavigation
|
||||
items={navItems}
|
||||
currentPath={$page.url.pathname}
|
||||
|
|
@ -166,11 +147,8 @@
|
|||
homeRoute="/"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
isDark={theme.isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition={userSettings.nav.desktopPosition}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -192,12 +170,8 @@
|
|||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<!-- Main Content with dynamic padding based on nav mode -->
|
||||
<main
|
||||
class="main-content"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed}
|
||||
>
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<div class="content-wrapper">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
@ -214,17 +188,7 @@
|
|||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
/* Floating nav mode - add top padding for fixed nav */
|
||||
.main-content.floating-mode {
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
/* Sidebar mode - add left padding for sidebar nav */
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
}
|
||||
|
||||
// Navigation mode state
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Theme state
|
||||
|
|
@ -61,11 +60,6 @@
|
|||
|
||||
// Restore nav mode from localStorage
|
||||
if (browser) {
|
||||
const savedSidebar = localStorage.getItem('questions-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
}
|
||||
|
||||
const savedCollapsed = localStorage.getItem('questions-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
isCollapsed = true;
|
||||
|
|
@ -83,13 +77,6 @@
|
|||
theme.toggle();
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('questions-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
|
|
@ -205,11 +192,8 @@
|
|||
homeRoute="/"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition="bottom"
|
||||
showThemeToggle={true}
|
||||
showLogout={true}
|
||||
onLogout={handleSignOut}
|
||||
|
|
@ -232,11 +216,11 @@
|
|||
onParseCreate={handleParseCreate}
|
||||
createText="Create"
|
||||
appIcon="help-circle"
|
||||
bottomOffset={isMobile ? '70px' : isSidebarMode ? '0px' : '70px'}
|
||||
bottomOffset={isMobile ? '70px' : '70px'}
|
||||
/>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content bg-background" class:sidebar-mode={isSidebarMode && !isCollapsed}>
|
||||
<main class="main-content bg-background">
|
||||
<div class="content-wrapper">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
@ -257,12 +241,6 @@
|
|||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding-bottom: calc(80px + env(safe-area-inset-bottom));
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores();
|
||||
export const { isNavCollapsed } = createSimpleNavigationStores();
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@
|
|||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { userSettings } from '$lib/stores/user-settings.svelte';
|
||||
import { THEME_DEFINITIONS } from '@manacore/shared-theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
|
||||
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||
|
|
@ -25,7 +22,6 @@
|
|||
let { children } = $props();
|
||||
|
||||
let loading = $state(true);
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Use theme store's isDark directly
|
||||
|
|
@ -111,14 +107,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('storage-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
|
|
@ -155,13 +143,6 @@
|
|||
// Load user settings
|
||||
await userSettings.load();
|
||||
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('storage-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('storage-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
|
|
@ -204,11 +185,8 @@
|
|||
homeRoute="/files"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition={userSettings.nav.desktopPosition}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
@ -231,11 +209,7 @@
|
|||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<main
|
||||
class="main-content bg-background"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed}
|
||||
>
|
||||
<main class="main-content bg-background">
|
||||
<div class="content-wrapper">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
@ -252,15 +226,7 @@
|
|||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
.main-content.floating-mode {
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createSimpleNavigationStores } from '@manacore/shared-stores';
|
||||
|
||||
export const { isSidebarMode, isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({
|
||||
export const { isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({
|
||||
withToolbar: true,
|
||||
toolbarCollapsedDefault: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -317,7 +317,6 @@
|
|||
homeRoute="/"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
|
|
|
|||
|
|
@ -238,7 +238,6 @@
|
|||
currentPath={$page.url.pathname}
|
||||
appName="Zitare"
|
||||
homeRoute="/"
|
||||
desktopPosition="bottom"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
showThemeToggle={true}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
import { writable, type Writable } from 'svelte/store';
|
||||
|
||||
export interface SimpleNavigationStores {
|
||||
/** Whether the app is in sidebar mode (desktop) */
|
||||
isSidebarMode: Writable<boolean>;
|
||||
/** Whether the nav is collapsed */
|
||||
isNavCollapsed: Writable<boolean>;
|
||||
/** Whether the toolbar is collapsed (optional) */
|
||||
|
|
@ -15,7 +13,7 @@ export interface SimpleNavigationStores {
|
|||
}
|
||||
|
||||
export interface SimpleNavigationOptions {
|
||||
/** App name for localStorage keys (e.g., 'clock' -> 'clock_sidebar_mode') */
|
||||
/** App name for localStorage keys (e.g., 'clock' -> 'clock_nav_collapsed') */
|
||||
storageKey?: string;
|
||||
/** Include isToolbarCollapsed store */
|
||||
withToolbar?: boolean;
|
||||
|
|
@ -28,17 +26,17 @@ export interface SimpleNavigationOptions {
|
|||
*
|
||||
* @example
|
||||
* // Basic usage (no persistence)
|
||||
* export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores();
|
||||
* export const { isNavCollapsed } = createSimpleNavigationStores();
|
||||
*
|
||||
* @example
|
||||
* // With persistence
|
||||
* export const { isSidebarMode, isNavCollapsed } = createSimpleNavigationStores({
|
||||
* export const { isNavCollapsed } = createSimpleNavigationStores({
|
||||
* storageKey: 'clock',
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // With toolbar
|
||||
* export const { isSidebarMode, isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({
|
||||
* export const { isNavCollapsed, isToolbarCollapsed } = createSimpleNavigationStores({
|
||||
* withToolbar: true,
|
||||
* toolbarCollapsedDefault: true,
|
||||
* });
|
||||
|
|
@ -71,16 +69,11 @@ export function createSimpleNavigationStores(
|
|||
}
|
||||
|
||||
// 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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -194,10 +194,6 @@
|
|||
onToggleTheme?: () => void;
|
||||
/** Whether dark mode is active */
|
||||
isDark?: boolean;
|
||||
/** Whether sidebar mode is enabled */
|
||||
isSidebarMode?: boolean;
|
||||
/** Called when sidebar mode changes */
|
||||
onModeChange?: (isSidebar: boolean) => void;
|
||||
/** Whether navigation is collapsed */
|
||||
isCollapsed?: boolean;
|
||||
/** Called when collapsed state changes */
|
||||
|
|
@ -257,12 +253,8 @@
|
|||
onA11yReduceMotionChange?: (reduce: boolean) => void;
|
||||
/** Show a11y quick toggles in theme dropdown */
|
||||
showA11yQuickToggles?: boolean;
|
||||
/** Desktop navigation position (mobile always at bottom) */
|
||||
desktopPosition?: 'top' | 'bottom';
|
||||
/** Called when an app should be opened in a split panel */
|
||||
onOpenInPanel?: (appId: string, url: string) => void;
|
||||
/** Toolbar content snippet (shown in sidebar mode) */
|
||||
toolbarContent?: Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -274,8 +266,6 @@
|
|||
onLogout,
|
||||
onToggleTheme,
|
||||
isDark = false,
|
||||
isSidebarMode: externalSidebarMode,
|
||||
onModeChange,
|
||||
isCollapsed: externalCollapsed,
|
||||
onCollapsedChange,
|
||||
languageItems = [],
|
||||
|
|
@ -305,9 +295,7 @@
|
|||
a11yReduceMotion = false,
|
||||
onA11yReduceMotionChange,
|
||||
showA11yQuickToggles = false,
|
||||
desktopPosition = 'top',
|
||||
onOpenInPanel,
|
||||
toolbarContent,
|
||||
}: Props = $props();
|
||||
|
||||
// Type guards for elements
|
||||
|
|
@ -338,48 +326,15 @@
|
|||
}
|
||||
|
||||
// Local state for uncontrolled mode
|
||||
let internalSidebarMode = $state(false);
|
||||
let internalCollapsed = $state(false);
|
||||
|
||||
// Use external or internal state
|
||||
const isSidebarMode = $derived(
|
||||
onModeChange !== undefined ? (externalSidebarMode ?? false) : internalSidebarMode
|
||||
);
|
||||
const isCollapsed = $derived(
|
||||
onCollapsedChange !== undefined ? (externalCollapsed ?? false) : internalCollapsed
|
||||
);
|
||||
|
||||
// Mobile detection for dropdown direction
|
||||
let isMobile = $state(false);
|
||||
$effect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const checkMobile = () => {
|
||||
isMobile = window.innerWidth <= 768;
|
||||
};
|
||||
checkMobile();
|
||||
window.addEventListener('resize', checkMobile);
|
||||
return () => window.removeEventListener('resize', checkMobile);
|
||||
}
|
||||
});
|
||||
|
||||
// Dropdown direction: up when nav is at bottom (mobile or desktop-bottom), down otherwise
|
||||
const dropdownDirection = $derived<'up' | 'down'>(
|
||||
// Mobile: always up (nav at bottom) unless in sidebar mode
|
||||
(isMobile && !isSidebarMode) ||
|
||||
// Desktop with bottom position: up unless in sidebar mode
|
||||
(!isMobile && desktopPosition === 'bottom' && !isSidebarMode)
|
||||
? 'up'
|
||||
: 'down'
|
||||
);
|
||||
|
||||
function toggleSidebarMode() {
|
||||
const newValue = !isSidebarMode;
|
||||
if (onModeChange) {
|
||||
onModeChange(newValue);
|
||||
} else {
|
||||
internalSidebarMode = newValue;
|
||||
}
|
||||
}
|
||||
// Dropdown direction: always up since nav is always at bottom
|
||||
const dropdownDirection = 'up' as const;
|
||||
|
||||
function collapseNav() {
|
||||
if (onCollapsedChange) {
|
||||
|
|
@ -463,12 +418,7 @@
|
|||
chevronLeft: 'M15 19l-7-7 7-7',
|
||||
chevronRight: 'M9 5l7 7-7 7',
|
||||
menu: 'M4 6h16M4 12h16M4 18h16',
|
||||
// Layout icons
|
||||
sidebar: 'M3 3h7v18H3V3zm9 0h9v18h-9V3z', // Sidebar layout icon
|
||||
layoutBottom: 'M3 3h18v9H3V3zm0 12h18v6H3v-6z', // Bottom bar layout icon
|
||||
panelRight: 'M9 3h12v18H9V3zM3 3h3v18H3V3z', // Panel right icon
|
||||
minimize: 'M4 12h16', // Minimize (minus) icon
|
||||
maximize: 'M4 8h16M4 16h16', // Two lines for expand
|
||||
fire: 'M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7C14 5 16.09 5.777 17.656 7.343A7.975 7.975 0 0120 13a7.975 7.975 0 01-2.343 5.657z',
|
||||
grid: 'M4 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM14 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V5zM4 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1v-4zM14 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z',
|
||||
gridSmall:
|
||||
|
|
@ -493,13 +443,8 @@
|
|||
</script>
|
||||
|
||||
{#if !isCollapsed}
|
||||
<nav
|
||||
class="pill-nav"
|
||||
class:sidebar-mode={isSidebarMode}
|
||||
class:desktop-bottom={desktopPosition === 'bottom'}
|
||||
style={primaryColor ? `--pill-primary-color: ${primaryColor}` : ''}
|
||||
>
|
||||
<div class="pill-nav-container" class:sidebar-container={isSidebarMode}>
|
||||
<nav class="pill-nav" style={primaryColor ? `--pill-primary-color: ${primaryColor}` : ''}>
|
||||
<div class="pill-nav-container">
|
||||
<!-- Logo pill / App Switcher -->
|
||||
{#if showAppSwitcher && appItems.length > 0}
|
||||
<PillDropdown
|
||||
|
|
@ -507,7 +452,7 @@
|
|||
direction={dropdownDirection}
|
||||
label={appName}
|
||||
icon="grid"
|
||||
iconOnly={!isSidebarMode}
|
||||
iconOnly={false}
|
||||
/>
|
||||
{:else}
|
||||
<a href={homeRoute} class="pill glass-pill logo-pill">
|
||||
|
|
@ -528,11 +473,10 @@
|
|||
onChange={element.onChange}
|
||||
sectionLabel={element.sectionLabel}
|
||||
onContextMenu={element.onContextMenu}
|
||||
{isSidebarMode}
|
||||
{primaryColor}
|
||||
/>
|
||||
{:else if isDivider(element)}
|
||||
<div class="pill-divider" class:sidebar-divider={isSidebarMode}></div>
|
||||
<div class="pill-divider"></div>
|
||||
{:else if isTagSelector(element)}
|
||||
<PillTagSelector
|
||||
tags={element.tags}
|
||||
|
|
@ -634,11 +578,10 @@
|
|||
onChange={element.onChange}
|
||||
sectionLabel={element.sectionLabel}
|
||||
onContextMenu={element.onContextMenu}
|
||||
{isSidebarMode}
|
||||
{primaryColor}
|
||||
/>
|
||||
{:else if isDivider(element)}
|
||||
<div class="pill-divider" class:sidebar-divider={isSidebarMode}></div>
|
||||
<div class="pill-divider"></div>
|
||||
{:else if isTagSelector(element)}
|
||||
<PillTagSelector
|
||||
tags={element.tags}
|
||||
|
|
@ -916,83 +859,24 @@
|
|||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- Control Button (right position in horizontal mode, bottom in sidebar mode) -->
|
||||
{#if !isSidebarMode}
|
||||
<div class="pill glass-pill segmented-control">
|
||||
<button onclick={collapseNav} class="segment-btn" title="Navigation minimieren">
|
||||
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={getIconPath('chevronRight')}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="segment-divider"></div>
|
||||
<button onclick={toggleSidebarMode} class="segment-btn" title="Zur Sidebar wechseln">
|
||||
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={getIconPath('sidebar')}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Control Button (bottom position in sidebar mode) -->
|
||||
{#if isSidebarMode}
|
||||
<!-- Toolbar content (if provided) -->
|
||||
{#if toolbarContent}
|
||||
<div class="pill-divider sidebar-divider"></div>
|
||||
<div class="sidebar-toolbar-content">
|
||||
{@render toolbarContent()}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="sidebar-spacer"></div>
|
||||
<div class="pill glass-pill segmented-control sidebar-segmented">
|
||||
<button
|
||||
onclick={toggleSidebarMode}
|
||||
class="segment-btn"
|
||||
title="Zur Bottom-Navigation wechseln"
|
||||
>
|
||||
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={getIconPath('layoutBottom')}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="segment-divider"></div>
|
||||
<button onclick={collapseNav} class="segment-btn" title="Sidebar minimieren">
|
||||
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={getIconPath('chevronRight')}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Collapse Button -->
|
||||
<button onclick={collapseNav} class="pill glass-pill" title="Navigation minimieren">
|
||||
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d={getIconPath('chevronRight')}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
{/if}
|
||||
|
||||
<!-- FAB for collapsed state -->
|
||||
{#if isCollapsed}
|
||||
<button
|
||||
onclick={expandNav}
|
||||
class="nav-fab glass-pill"
|
||||
class:desktop-bottom={desktopPosition === 'bottom'}
|
||||
title="Expand navigation"
|
||||
>
|
||||
<button onclick={expandNav} class="nav-fab glass-pill" title="Expand navigation">
|
||||
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
|
|
@ -1007,35 +891,17 @@
|
|||
<style>
|
||||
.pill-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
padding: 0.75rem 0 1.5rem;
|
||||
padding: 1rem 0 calc(env(safe-area-inset-bottom, 0px) + 0.75rem);
|
||||
pointer-events: none;
|
||||
/* Container query context */
|
||||
container-type: inline-size;
|
||||
container-name: pillnav;
|
||||
}
|
||||
|
||||
/* Desktop bottom position */
|
||||
@media (min-width: 769px) {
|
||||
.pill-nav.desktop-bottom:not(.sidebar-mode) {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
padding: 1rem 0 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile: always position at bottom */
|
||||
@media (max-width: 768px) {
|
||||
.pill-nav:not(.sidebar-mode) {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
padding: 1rem 0 calc(env(safe-area-inset-bottom, 0px) + 0.75rem);
|
||||
}
|
||||
}
|
||||
|
||||
.pill-nav-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -1052,15 +918,7 @@
|
|||
|
||||
/* Center when container has enough space (> 600px) */
|
||||
@container pillnav (min-width: 600px) {
|
||||
.pill-nav-container:not(.sidebar-container) {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Larger screens: always centered */
|
||||
@container pillnav (min-width: 900px) {
|
||||
.pill-nav-container:not(.sidebar-container) {
|
||||
.pill-nav-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
|
@ -1153,12 +1011,6 @@
|
|||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.sidebar-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
/* Logout pill */
|
||||
.logout-pill {
|
||||
color: #dc2626;
|
||||
|
|
@ -1183,431 +1035,22 @@
|
|||
display: inline;
|
||||
}
|
||||
|
||||
/* Sidebar mode styles */
|
||||
.pill-nav.sidebar-mode {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: auto;
|
||||
width: 180px;
|
||||
padding: 0.75rem 0;
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:global(.dark) .pill-nav.sidebar-mode {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Mobile: Sidebar as bottom sheet */
|
||||
@media (max-width: 768px) {
|
||||
.pill-nav.sidebar-mode {
|
||||
top: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
padding: 1.5rem 0 calc(env(safe-area-inset-bottom, 0px) + 0.75rem);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Drag handle */
|
||||
.pill-nav.sidebar-mode::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0.625rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 2.5rem;
|
||||
height: 0.25rem;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
:global(.dark) .pill-nav.sidebar-mode {
|
||||
background: rgba(30, 30, 30, 0.95);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
:global(.dark) .pill-nav.sidebar-mode::before {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 1rem 1rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Toolbar content in sidebar mode */
|
||||
.sidebar-toolbar-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0;
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content {
|
||||
scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.toolbar-bar) {
|
||||
flex-direction: column;
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.toolbar-content) {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* All buttons in sidebar toolbar - full width, left aligned */
|
||||
.sidebar-toolbar-content :global(.pill-toolbar-btn),
|
||||
.sidebar-toolbar-content :global(.pill-dropdown .trigger-button),
|
||||
.sidebar-toolbar-content :global(button) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-toolbar-btn:hover),
|
||||
.sidebar-toolbar-content :global(.pill-dropdown .trigger-button:hover),
|
||||
.sidebar-toolbar-content :global(button:hover) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-toolbar-btn:hover),
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-dropdown .trigger-button:hover),
|
||||
:global(.dark) .sidebar-toolbar-content :global(button:hover) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Style for PillViewSwitcher in sidebar - vertical layout */
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher) {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Hide the sliding indicator in vertical mode */
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .sliding-indicator) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem 0.875rem;
|
||||
border-radius: 9999px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn:hover) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn:hover) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn.active) {
|
||||
background: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 15%, transparent 85%);
|
||||
border-color: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 25%, transparent 75%);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn.active) {
|
||||
background: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 25%, transparent 75%);
|
||||
border-color: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 35%, transparent 65%);
|
||||
}
|
||||
|
||||
/* PillTimeRangeSelector in sidebar */
|
||||
.sidebar-toolbar-content :global(.pill-time-range-selector),
|
||||
.sidebar-toolbar-content :global(.pill-dropdown) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* PillCalendarSelector in sidebar */
|
||||
.sidebar-toolbar-content :global(.calendar-selector) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Mobile: Sidebar container adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar-container {
|
||||
padding: 1rem 1.5rem 1rem;
|
||||
gap: 0.5rem;
|
||||
height: auto;
|
||||
max-height: calc(70vh - 2rem);
|
||||
}
|
||||
|
||||
/* Hide spacer on mobile - not needed in bottom sheet */
|
||||
.sidebar-container .sidebar-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-container .pill {
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-container :global(.pill-dropdown) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-container :global(.pill-dropdown .trigger-button) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.sidebar-container .segmented-control {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-container .segmented-control .segment-btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Transparent pills in sidebar mode (desktop) */
|
||||
.sidebar-container .glass-pill,
|
||||
.sidebar-container :global(.pill-dropdown .trigger-button) {
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.sidebar-container .glass-pill:hover,
|
||||
.sidebar-container :global(.pill-dropdown .trigger-button:hover) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-container .glass-pill:hover,
|
||||
:global(.dark) .sidebar-container :global(.pill-dropdown .trigger-button:hover) {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Mobile: Visible pills in sidebar/bottom-sheet mode */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar-container .glass-pill,
|
||||
.sidebar-container :global(.pill-dropdown .trigger-button) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.sidebar-container .glass-pill:hover,
|
||||
.sidebar-container :global(.pill-dropdown .trigger-button:hover) {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-container .glass-pill,
|
||||
:global(.dark) .sidebar-container :global(.pill-dropdown .trigger-button) {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-container .glass-pill:hover,
|
||||
:global(.dark) .sidebar-container :global(.pill-dropdown .trigger-button:hover) {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep active state visible */
|
||||
.sidebar-container .pill.active {
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--pill-primary-color, var(--color-primary-500, #f8d62b)) 20%,
|
||||
transparent 80%
|
||||
);
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--pill-primary-color, var(--color-primary-500, #f8d62b)) 30%,
|
||||
transparent 70%
|
||||
);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-container .pill.active {
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--pill-primary-color, var(--color-primary-500, #f8d62b)) 15%,
|
||||
transparent 85%
|
||||
);
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--pill-primary-color, var(--color-primary-500, #f8d62b)) 25%,
|
||||
transparent 75%
|
||||
);
|
||||
}
|
||||
|
||||
/* Logo pill in sidebar - same as other pills (transparent) */
|
||||
.sidebar-container .logo-pill {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.sidebar-container .logo-pill:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-container .logo-pill:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Spacer to push toggle button to bottom */
|
||||
.sidebar-spacer {
|
||||
flex: 1;
|
||||
min-height: 1rem;
|
||||
}
|
||||
|
||||
/* Note: .toggle-pill class may be applied dynamically */
|
||||
|
||||
/* Segmented control */
|
||||
.segmented-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.segment-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.5rem 0.625rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.segment-btn:first-child {
|
||||
border-radius: 9999px 0 0 9999px;
|
||||
}
|
||||
|
||||
.segment-btn:last-child {
|
||||
border-radius: 0 9999px 9999px 0;
|
||||
}
|
||||
|
||||
.segment-btn:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .segment-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.segment-divider {
|
||||
width: 1px;
|
||||
height: 1rem;
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
:global(.dark) .segment-divider {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.sidebar-segmented {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* FAB for collapsed state - positioned at right */
|
||||
/* FAB for collapsed state - positioned at bottom right */
|
||||
.nav-fab {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.875rem;
|
||||
border-radius: 0 0 0 1rem;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 0.875rem);
|
||||
border-radius: 1rem 0 0 0;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Desktop: FAB at bottom when desktop-bottom */
|
||||
@media (min-width: 769px) {
|
||||
.nav-fab.desktop-bottom {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
border-radius: 1rem 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile: FAB always at bottom right */
|
||||
@media (max-width: 768px) {
|
||||
.nav-fab {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 1rem 0 0 0;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 0.875rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Transitions */
|
||||
.pill-nav {
|
||||
transition: all 0.3s ease;
|
||||
|
|
|
|||
|
|
@ -10,23 +10,13 @@
|
|||
onChange: (id: string) => void;
|
||||
/** Optional section label */
|
||||
sectionLabel?: string;
|
||||
/** Whether in sidebar mode (affects layout) */
|
||||
isSidebarMode?: boolean;
|
||||
/** Primary color for active state */
|
||||
primaryColor?: string;
|
||||
/** Called on right-click (context menu) - receives click coordinates */
|
||||
onContextMenu?: (x: number, y: number) => void;
|
||||
}
|
||||
|
||||
let {
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
sectionLabel,
|
||||
isSidebarMode = false,
|
||||
primaryColor,
|
||||
onContextMenu,
|
||||
}: Props = $props();
|
||||
let { options, value, onChange, sectionLabel, primaryColor, onContextMenu }: Props = $props();
|
||||
|
||||
function handleContextMenu(event: MouseEvent) {
|
||||
if (onContextMenu) {
|
||||
|
|
@ -66,13 +56,9 @@
|
|||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="pill-tab-group" class:sidebar-mode={isSidebarMode} oncontextmenu={handleContextMenu}>
|
||||
{#if sectionLabel && isSidebarMode}
|
||||
<p class="section-label">{sectionLabel}</p>
|
||||
{/if}
|
||||
<div class="pill-tab-group" oncontextmenu={handleContextMenu}>
|
||||
<div
|
||||
class="tab-container glass-pill"
|
||||
class:sidebar-tabs={isSidebarMode}
|
||||
style={primaryColor ? `--pill-primary-color: ${primaryColor}` : ''}
|
||||
>
|
||||
{#each options as option, index}
|
||||
|
|
@ -116,20 +102,6 @@
|
|||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #6b7280;
|
||||
margin: 0;
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
:global(.dark) .section-label {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -154,20 +126,6 @@
|
|||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Sidebar mode - transparent */
|
||||
.sidebar-tabs {
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-tabs {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -253,14 +211,4 @@
|
|||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Sidebar mode adjustments */
|
||||
.sidebar-mode .tab-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-mode .tab-btn {
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem 0.625rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -153,10 +153,6 @@ export interface PillNavigationProps {
|
|||
onToggleTheme?: () => void;
|
||||
/** Whether dark mode is active */
|
||||
isDark?: boolean;
|
||||
/** Whether sidebar mode is enabled (controlled) */
|
||||
isSidebarMode?: boolean;
|
||||
/** Called when sidebar mode changes */
|
||||
onModeChange?: (isSidebar: boolean) => void;
|
||||
/** Whether navigation is collapsed (controlled) */
|
||||
isCollapsed?: boolean;
|
||||
/** Called when collapsed state changes */
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type {
|
||||
UserSettingsStore,
|
||||
NavPosition,
|
||||
ThemeMode,
|
||||
WeekStartDay,
|
||||
} from '@manacore/shared-theme';
|
||||
import type { UserSettingsStore, ThemeMode, WeekStartDay } from '@manacore/shared-theme';
|
||||
import { getAvailableRoutes, getDefaultRoute } from '@manacore/shared-theme';
|
||||
import SettingsSection from './SettingsSection.svelte';
|
||||
import SettingsCard from './SettingsCard.svelte';
|
||||
|
|
@ -25,8 +20,6 @@
|
|||
navItems?: NavItem[];
|
||||
/** Items that should always be visible (e.g., home route) */
|
||||
alwaysVisibleHrefs?: string[];
|
||||
/** Whether to show navigation settings */
|
||||
showNavigation?: boolean;
|
||||
/** Whether to show nav visibility settings */
|
||||
showNavVisibility?: boolean;
|
||||
/** Whether to show theme settings */
|
||||
|
|
@ -48,7 +41,6 @@
|
|||
appId,
|
||||
navItems = [],
|
||||
alwaysVisibleHrefs = [],
|
||||
showNavigation = true,
|
||||
showNavVisibility = true,
|
||||
showTheme = true,
|
||||
showLanguage = true,
|
||||
|
|
@ -65,20 +57,6 @@
|
|||
appId ? userSettings.general?.startPages?.[appId] || defaultRoute : '/'
|
||||
);
|
||||
|
||||
// Navigation position handler
|
||||
async function handleNavPositionChange(position: NavPosition) {
|
||||
await userSettings.updateGlobal({
|
||||
nav: { ...userSettings.globalSettings.nav, desktopPosition: position },
|
||||
});
|
||||
}
|
||||
|
||||
// Sidebar collapsed handler
|
||||
async function handleSidebarChange(collapsed: boolean) {
|
||||
await userSettings.updateGlobal({
|
||||
nav: { ...userSettings.globalSettings.nav, sidebarCollapsed: collapsed },
|
||||
});
|
||||
}
|
||||
|
||||
// Theme mode handler
|
||||
async function handleThemeModeChange(mode: ThemeMode) {
|
||||
await userSettings.updateGlobal({
|
||||
|
|
@ -155,78 +133,9 @@
|
|||
<p class="text-sm text-[hsl(var(--muted-foreground))] mb-6">{description}</p>
|
||||
|
||||
<div class="space-y-6">
|
||||
{#if showNavigation}
|
||||
<!-- Navigation Settings -->
|
||||
<div class="space-y-4">
|
||||
<h3
|
||||
class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider"
|
||||
>
|
||||
Navigation
|
||||
</h3>
|
||||
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Position (Desktop)</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
Position der Navigation auf großen Bildschirmen
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors {userSettings
|
||||
.globalSettings.nav.desktopPosition === 'top'
|
||||
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
|
||||
: 'bg-[hsl(var(--muted))] hover:bg-[hsl(var(--muted))]/80 text-[hsl(var(--foreground))]'}"
|
||||
onclick={() => handleNavPositionChange('top')}
|
||||
>
|
||||
Oben
|
||||
</button>
|
||||
<button
|
||||
class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors {userSettings
|
||||
.globalSettings.nav.desktopPosition === 'bottom'
|
||||
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
|
||||
: 'bg-[hsl(var(--muted))] hover:bg-[hsl(var(--muted))]/80 text-[hsl(var(--foreground))]'}"
|
||||
onclick={() => handleNavPositionChange('bottom')}
|
||||
>
|
||||
Unten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between py-2 border-t border-[hsl(var(--border))]"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Sidebar eingeklappt</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
Standard-Zustand der Sidebar
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors {userSettings
|
||||
.globalSettings.nav.sidebarCollapsed
|
||||
? 'bg-[hsl(var(--primary))]'
|
||||
: 'bg-gray-200 dark:bg-gray-700'}"
|
||||
onclick={() =>
|
||||
handleSidebarChange(!userSettings.globalSettings.nav.sidebarCollapsed)}
|
||||
aria-label="Toggle sidebar collapsed state"
|
||||
>
|
||||
<span
|
||||
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform {userSettings
|
||||
.globalSettings.nav.sidebarCollapsed
|
||||
? 'translate-x-6'
|
||||
: 'translate-x-1'}"
|
||||
></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showNavVisibility && appId && navItems.length > 0}
|
||||
<!-- Navigation Visibility Settings -->
|
||||
<div
|
||||
class="space-y-4 {showNavigation ? 'pt-4 border-t border-[hsl(var(--border))]' : ''}"
|
||||
>
|
||||
<div class="space-y-4">
|
||||
<NavVisibilitySettings {userSettings} {appId} {navItems} {alwaysVisibleHrefs} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -234,7 +143,7 @@
|
|||
{#if showTheme}
|
||||
<!-- Theme Settings -->
|
||||
<div
|
||||
class="space-y-4 {showNavigation || (showNavVisibility && appId)
|
||||
class="space-y-4 {showNavVisibility && appId
|
||||
? 'pt-4 border-t border-[hsl(var(--border))]'
|
||||
: ''}"
|
||||
>
|
||||
|
|
@ -294,7 +203,7 @@
|
|||
{#if showLanguage}
|
||||
<!-- Language Settings -->
|
||||
<div
|
||||
class="space-y-4 {showTheme || showNavigation || (showNavVisibility && appId)
|
||||
class="space-y-4 {showTheme || (showNavVisibility && appId)
|
||||
? 'pt-4 border-t border-[hsl(var(--border))]'
|
||||
: ''}"
|
||||
>
|
||||
|
|
@ -331,10 +240,7 @@
|
|||
{#if showGeneral}
|
||||
<!-- General Settings -->
|
||||
<div
|
||||
class="space-y-4 {showLanguage ||
|
||||
showTheme ||
|
||||
showNavigation ||
|
||||
(showNavVisibility && appId)
|
||||
class="space-y-4 {showLanguage || showTheme || (showNavVisibility && appId)
|
||||
? 'pt-4 border-t border-[hsl(var(--border))]'
|
||||
: ''}"
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue