ThemeSettings.tsx129 lines · main
| 1 | import { LOCAL_STORAGE_KEYS } from 'common' |
| 2 | import { useTheme } from 'next-themes' |
| 3 | import { useEffect, useState } from 'react' |
| 4 | import SVG from 'react-inlinesvg' |
| 5 | import { |
| 6 | Card, |
| 7 | CardContent, |
| 8 | Label, |
| 9 | RadioGroup, |
| 10 | RadioGroupLargeItem, |
| 11 | Select, |
| 12 | SelectContent, |
| 13 | SelectItem, |
| 14 | SelectTrigger, |
| 15 | SelectValue, |
| 16 | Separator, |
| 17 | singleThemes, |
| 18 | Theme, |
| 19 | } from 'ui' |
| 20 | import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' |
| 21 | import { |
| 22 | PageSection, |
| 23 | PageSectionContent, |
| 24 | PageSectionDescription, |
| 25 | PageSectionMeta, |
| 26 | PageSectionSummary, |
| 27 | PageSectionTitle, |
| 28 | } from 'ui-patterns/PageSection' |
| 29 | |
| 30 | import { DEFAULT_SIDEBAR_BEHAVIOR } from '@/components/interfaces/Sidebar' |
| 31 | import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage' |
| 32 | import { BASE_PATH } from '@/lib/constants' |
| 33 | |
| 34 | export const ThemeSettings = () => { |
| 35 | const [mounted, setMounted] = useState(false) |
| 36 | const { theme, setTheme } = useTheme() |
| 37 | |
| 38 | const [sidebarBehaviour, setSidebarBehaviour] = useLocalStorageQuery( |
| 39 | LOCAL_STORAGE_KEYS.SIDEBAR_BEHAVIOR, |
| 40 | DEFAULT_SIDEBAR_BEHAVIOR |
| 41 | ) |
| 42 | /** |
| 43 | * Avoid Hydration Mismatch |
| 44 | * https://github.com/pacocoursey/next-themes?tab=readme-ov-file#avoid-hydration-mismatch |
| 45 | */ |
| 46 | // useEffect only runs on the client, so now we can safely show the UI |
| 47 | useEffect(() => setMounted(true), []) |
| 48 | |
| 49 | if (!mounted) return null |
| 50 | |
| 51 | function SingleThemeSelection() { |
| 52 | return ( |
| 53 | <RadioGroup |
| 54 | name="theme" |
| 55 | onValueChange={setTheme} |
| 56 | aria-label="Choose a theme" |
| 57 | defaultValue={theme} |
| 58 | value={theme} |
| 59 | className="grid grid-cols-2 gap-4" |
| 60 | > |
| 61 | {singleThemes.map((theme: Theme) => ( |
| 62 | <RadioGroupLargeItem |
| 63 | className="p-3 w-full" |
| 64 | key={theme.value} |
| 65 | value={theme.value} |
| 66 | label={theme.name} |
| 67 | > |
| 68 | <SVG src={`${BASE_PATH}/img/themes/${theme.value}.svg?v=2`} /> |
| 69 | </RadioGroupLargeItem> |
| 70 | ))} |
| 71 | </RadioGroup> |
| 72 | ) |
| 73 | } |
| 74 | |
| 75 | return ( |
| 76 | <PageSection> |
| 77 | <PageSectionMeta> |
| 78 | <PageSectionSummary> |
| 79 | <PageSectionTitle>Appearance</PageSectionTitle> |
| 80 | <PageSectionDescription> |
| 81 | Choose how Briven looks and behaves in the dashboard. |
| 82 | </PageSectionDescription> |
| 83 | </PageSectionSummary> |
| 84 | </PageSectionMeta> |
| 85 | <PageSectionContent> |
| 86 | <Card> |
| 87 | <CardContent className="grid grid-cols-12 gap-6"> |
| 88 | <div className="col-span-full md:col-span-4 flex flex-col gap-2"> |
| 89 | <Label htmlFor="theme" className="text-foreground"> |
| 90 | Theme mode |
| 91 | </Label> |
| 92 | <p className="text-sm text-foreground-light"> |
| 93 | Choose how Briven looks to you. Select a single theme, or sync with your system. |
| 94 | </p> |
| 95 | </div> |
| 96 | |
| 97 | <div className="col-span-full md:col-span-8 flex flex-col gap-4"> |
| 98 | <SingleThemeSelection /> |
| 99 | </div> |
| 100 | </CardContent> |
| 101 | <Separator /> |
| 102 | <CardContent> |
| 103 | <FormItemLayout |
| 104 | isReactForm={false} |
| 105 | label="Sidebar behavior" |
| 106 | layout="flex-row-reverse" |
| 107 | description="Choose your preferred sidebar behavior: open, closed, or expand on hover." |
| 108 | > |
| 109 | <Select |
| 110 | value={sidebarBehaviour} |
| 111 | onValueChange={setSidebarBehaviour} |
| 112 | aria-label="Select an option" |
| 113 | > |
| 114 | <SelectTrigger> |
| 115 | <SelectValue placeholder="Choose an option" /> |
| 116 | </SelectTrigger> |
| 117 | <SelectContent> |
| 118 | <SelectItem value="open">Expanded</SelectItem> |
| 119 | <SelectItem value="closed">Collapsed</SelectItem> |
| 120 | <SelectItem value="expandable">Expand on hover</SelectItem> |
| 121 | </SelectContent> |
| 122 | </Select> |
| 123 | </FormItemLayout> |
| 124 | </CardContent> |
| 125 | </Card> |
| 126 | </PageSectionContent> |
| 127 | </PageSection> |
| 128 | ) |
| 129 | } |