feat(frontend): integrate ThemeSwitcher, restore custom palette on page load
This commit is contained in:
parent
ce29eb3062
commit
7736869fc4
@ -10,9 +10,24 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import {BookOpen, Box, ChevronDown, Compass, Home, LayoutGrid, Monitor, Moon, Plus, Sun, Users} from 'lucide-react';
|
import {
|
||||||
|
BookOpen,
|
||||||
|
Box,
|
||||||
|
ChevronDown,
|
||||||
|
Compass,
|
||||||
|
Home,
|
||||||
|
LayoutGrid,
|
||||||
|
Monitor,
|
||||||
|
Moon,
|
||||||
|
Plus,
|
||||||
|
Sliders,
|
||||||
|
Sun,
|
||||||
|
Users
|
||||||
|
} from 'lucide-react';
|
||||||
import {useNavigate} from 'react-router-dom';
|
import {useNavigate} from 'react-router-dom';
|
||||||
import {Avatar, AvatarFallback, AvatarImage} from '@/components/ui/avatar';
|
import {Avatar, AvatarFallback, AvatarImage} from '@/components/ui/avatar';
|
||||||
|
import {useState} from 'react';
|
||||||
|
import {ThemeSwitcher} from '@/components/room/ThemeSwitcher';
|
||||||
|
|
||||||
const btnClass = 'flex w-full h-9 justify-start items-center rounded-md font-medium hover:bg-muted cursor-pointer bg-transparent border-0 text-left text-sm';
|
const btnClass = 'flex w-full h-9 justify-start items-center rounded-md font-medium hover:bg-muted cursor-pointer bg-transparent border-0 text-left text-sm';
|
||||||
|
|
||||||
@ -22,10 +37,10 @@ export function SidebarSystem({collapsed}: {collapsed: boolean}) {
|
|||||||
const workspaceCtx = tryUseWorkspace();
|
const workspaceCtx = tryUseWorkspace();
|
||||||
const workspaces = workspaceCtx?.workspaces;
|
const workspaces = workspaceCtx?.workspaces;
|
||||||
const currentWorkspace = workspaceCtx?.currentWorkspace;
|
const currentWorkspace = workspaceCtx?.currentWorkspace;
|
||||||
|
const [themeSheetOpen, setThemeSheetOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{/* Workspace switcher — only shown when inside WorkspaceProvider */}
|
|
||||||
{workspaceCtx && (
|
{workspaceCtx && (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
@ -88,29 +103,33 @@ export function SidebarSystem({collapsed}: {collapsed: boolean}) {
|
|||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => navigate('/')}>
|
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')}
|
||||||
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
|
onClick={() => navigate('/')}>
|
||||||
|
<span className={cn('flex h-6 w-6 items-center justify-center shrink-0')}>
|
||||||
<Home className="h-4 w-4"/>
|
<Home className="h-4 w-4"/>
|
||||||
</span>
|
</span>
|
||||||
{!collapsed && <span className="text-sm leading-none">Home</span>}
|
{!collapsed && <span className="text-sm leading-none">Home</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => navigate('/explore')}>
|
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')}
|
||||||
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
|
onClick={() => navigate('/explore')}>
|
||||||
|
<span className={cn('flex h-6 w-6 items-center justify-center shrink-0')}>
|
||||||
<Compass className="h-4 w-4"/>
|
<Compass className="h-4 w-4"/>
|
||||||
</span>
|
</span>
|
||||||
{!collapsed && <span className="text-sm leading-none">Explore</span>}
|
{!collapsed && <span className="text-sm leading-none">Explore</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => navigate('/market')}>
|
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')}
|
||||||
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
|
onClick={() => navigate('/market')}>
|
||||||
|
<span className={cn('flex h-6 w-6 items-center justify-center shrink-0')}>
|
||||||
<Box className="h-4 w-4"/>
|
<Box className="h-4 w-4"/>
|
||||||
</span>
|
</span>
|
||||||
{!collapsed && <span className="text-sm leading-none">Marketplace</span>}
|
{!collapsed && <span className="text-sm leading-none">Marketplace</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => window.open('/docs', '_blank')}>
|
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')}
|
||||||
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
|
onClick={() => window.open('/docs', '_blank')}>
|
||||||
|
<span className={cn('flex h-6 w-6 items-center justify-center shrink-0')}>
|
||||||
<BookOpen className="h-4 w-4"/>
|
<BookOpen className="h-4 w-4"/>
|
||||||
</span>
|
</span>
|
||||||
{!collapsed && <span className="text-sm leading-none">Docs</span>}
|
{!collapsed && <span className="text-sm leading-none">Docs</span>}
|
||||||
@ -125,7 +144,7 @@ export function SidebarSystem({collapsed}: {collapsed: boolean}) {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
|
<span className={cn('flex h-6 w-6 items-center justify-center shrink-0')}>
|
||||||
{theme === 'dark' ? (
|
{theme === 'dark' ? (
|
||||||
<Moon className="h-4 w-4"/>
|
<Moon className="h-4 w-4"/>
|
||||||
) : theme === 'light' ? (
|
) : theme === 'light' ? (
|
||||||
@ -139,7 +158,12 @@ export function SidebarSystem({collapsed}: {collapsed: boolean}) {
|
|||||||
<DropdownMenuContent side="right" align="start">
|
<DropdownMenuContent side="right" align="start">
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuLabel>Theme Settings</DropdownMenuLabel>
|
<DropdownMenuLabel>Theme</DropdownMenuLabel>
|
||||||
|
<DropdownMenuItem onClick={() => setThemeSheetOpen(true)}>
|
||||||
|
<Sliders className="mr-2 h-4 w-4"/>
|
||||||
|
<span>Design System</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator/>
|
||||||
<DropdownMenuItem onClick={() => setTheme('light')}>
|
<DropdownMenuItem onClick={() => setTheme('light')}>
|
||||||
<Sun className="mr-2 h-4 w-4"/>
|
<Sun className="mr-2 h-4 w-4"/>
|
||||||
<span>Light</span>
|
<span>Light</span>
|
||||||
@ -155,20 +179,13 @@ export function SidebarSystem({collapsed}: {collapsed: boolean}) {
|
|||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
)}
|
)}
|
||||||
{collapsed && (
|
{collapsed && (
|
||||||
<>
|
<DropdownMenuItem onClick={() => setThemeSheetOpen(true)}>
|
||||||
<DropdownMenuItem onClick={() => setTheme('light')}>
|
<Sliders className="mr-2 h-4 w-4"/>
|
||||||
<Sun className="mr-2 h-4 w-4" />
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme('dark')}>
|
|
||||||
<Moon className="mr-2 h-4 w-4" />
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => setTheme('system')}>
|
|
||||||
<Monitor className="mr-2 h-4 w-4" />
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
<ThemeSwitcher open={themeSheetOpen} onOpenChange={setThemeSheetOpen}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
14
src/main.tsx
14
src/main.tsx
@ -6,6 +6,20 @@ import {UserProvider} from '@/contexts';
|
|||||||
import {ThemeProvider} from '@/contexts/theme-context';
|
import {ThemeProvider} from '@/contexts/theme-context';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
import {applyPaletteToDOM, loadActivePresetId} from '@/components/room/design-system';
|
||||||
|
|
||||||
|
// Restore custom palette on page load (before first render)
|
||||||
|
const activePreset = loadActivePresetId();
|
||||||
|
if (activePreset === 'custom') {
|
||||||
|
const customPalette = localStorage.getItem('theme-custom-palette');
|
||||||
|
if (customPalette) {
|
||||||
|
try {
|
||||||
|
applyPaletteToDOM(JSON.parse(customPalette));
|
||||||
|
} catch {
|
||||||
|
// ignore malformed stored palette
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user