a new start
This commit is contained in:
191
components/theme/theme-switcher.tsx
Normal file
191
components/theme/theme-switcher.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { Moon, Sun, Palette, Check } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { colorPalettes, type ColorPalette } from '@/lib/themes'
|
||||
|
||||
export function ThemeSwitcher() {
|
||||
const { theme, setTheme } = useTheme()
|
||||
const [selectedPalette, setSelectedPalette] = React.useState('awesome')
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setMounted(true)
|
||||
// Load saved palette from localStorage
|
||||
const savedPalette = localStorage.getItem('color-palette')
|
||||
if (savedPalette) {
|
||||
setSelectedPalette(savedPalette)
|
||||
applyPalette(colorPalettes.find(p => p.id === savedPalette) || colorPalettes[0])
|
||||
}
|
||||
}, [])
|
||||
|
||||
const applyPalette = (palette: ColorPalette) => {
|
||||
const root = document.documentElement
|
||||
|
||||
// Apply CSS custom properties
|
||||
root.style.setProperty('--color-primary', palette.colors.primary)
|
||||
root.style.setProperty('--color-primary-light', palette.colors.primaryLight)
|
||||
root.style.setProperty('--color-primary-dark', palette.colors.primaryDark)
|
||||
|
||||
root.style.setProperty('--color-secondary', palette.colors.secondary)
|
||||
root.style.setProperty('--color-secondary-light', palette.colors.secondaryLight)
|
||||
root.style.setProperty('--color-secondary-dark', palette.colors.secondaryDark)
|
||||
|
||||
root.style.setProperty('--color-accent', palette.colors.accent)
|
||||
root.style.setProperty('--color-accent-light', palette.colors.accentLight)
|
||||
root.style.setProperty('--color-accent-dark', palette.colors.accentDark)
|
||||
|
||||
root.style.setProperty('--gradient-awesome', palette.gradient)
|
||||
|
||||
// Update awesome-specific colors
|
||||
root.style.setProperty('--awesome-purple', palette.colors.primary)
|
||||
root.style.setProperty('--awesome-pink', palette.colors.secondary)
|
||||
root.style.setProperty('--awesome-gold', palette.colors.accent)
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('color-palette', palette.id)
|
||||
setSelectedPalette(palette.id)
|
||||
}
|
||||
|
||||
const handlePaletteChange = (palette: ColorPalette) => {
|
||||
applyPalette(palette)
|
||||
}
|
||||
|
||||
if (!mounted) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isDark = theme === 'dark'
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="relative h-10 w-10 rounded-full border-2 border-primary/20 bg-background/80 backdrop-blur-sm transition-all hover:border-primary/40 hover:bg-primary/5"
|
||||
>
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-awesome opacity-10" />
|
||||
{isDark ? (
|
||||
<Moon className="h-5 w-5 text-primary" />
|
||||
) : (
|
||||
<Sun className="h-5 w-5 text-primary" />
|
||||
)}
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-[320px] border-2 border-primary/20 bg-background/95 backdrop-blur-xl"
|
||||
>
|
||||
<DropdownMenuLabel className="flex items-center gap-2 text-base">
|
||||
<Palette className="h-5 w-5 text-primary" />
|
||||
<span className="gradient-text font-bold">Theme Settings</span>
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuSeparator className="bg-primary/20" />
|
||||
|
||||
{/* Mode Selection */}
|
||||
<div className="px-2 py-3">
|
||||
<div className="mb-2 text-xs font-semibold text-muted-foreground">
|
||||
MODE
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button
|
||||
variant={!isDark ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setTheme('light')}
|
||||
className={!isDark ? 'btn-awesome' : ''}
|
||||
>
|
||||
<Sun className="mr-2 h-4 w-4" />
|
||||
Light
|
||||
</Button>
|
||||
<Button
|
||||
variant={isDark ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setTheme('dark')}
|
||||
className={isDark ? 'btn-awesome' : ''}
|
||||
>
|
||||
<Moon className="mr-2 h-4 w-4" />
|
||||
Dark
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DropdownMenuSeparator className="bg-primary/20" />
|
||||
|
||||
{/* Palette Selection */}
|
||||
<div className="px-2 py-3">
|
||||
<div className="mb-3 text-xs font-semibold text-muted-foreground">
|
||||
COLOR PALETTE
|
||||
</div>
|
||||
<div className="max-h-[300px] space-y-2 overflow-y-auto pr-1">
|
||||
{colorPalettes.map((palette) => (
|
||||
<button
|
||||
key={palette.id}
|
||||
onClick={() => handlePaletteChange(palette)}
|
||||
className={`group relative w-full rounded-lg border-2 p-3 text-left transition-all hover:border-primary/40 hover:bg-primary/5 ${
|
||||
selectedPalette === palette.id
|
||||
? 'border-primary/60 bg-primary/10'
|
||||
: 'border-border/40'
|
||||
}`}
|
||||
>
|
||||
{/* Color Preview */}
|
||||
<div className="mb-2 flex gap-1.5">
|
||||
<div
|
||||
className="h-6 w-6 rounded-md ring-1 ring-black/10"
|
||||
style={{ background: palette.colors.primary }}
|
||||
/>
|
||||
<div
|
||||
className="h-6 w-6 rounded-md ring-1 ring-black/10"
|
||||
style={{ background: palette.colors.secondary }}
|
||||
/>
|
||||
<div
|
||||
className="h-6 w-6 rounded-md ring-1 ring-black/10"
|
||||
style={{ background: palette.colors.accent }}
|
||||
/>
|
||||
{selectedPalette === palette.id && (
|
||||
<div className="ml-auto flex h-6 w-6 items-center justify-center rounded-md bg-primary">
|
||||
<Check className="h-4 w-4 text-white" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Palette Info */}
|
||||
<div className="font-semibold text-foreground">
|
||||
{palette.name}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{palette.description}
|
||||
</div>
|
||||
|
||||
{/* Gradient Preview */}
|
||||
<div
|
||||
className="mt-2 h-1.5 rounded-full"
|
||||
style={{ background: palette.gradient }}
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DropdownMenuSeparator className="bg-primary/20" />
|
||||
|
||||
{/* Footer */}
|
||||
<div className="px-4 py-2 text-center text-xs text-muted-foreground">
|
||||
Choose your awesome style! 💜💗💛
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user