repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
Projectflow
stars
latest
clone command
git clone gitlawb://did:key:z6Mkfh4Y...QBEi/projectflowgit clone gitlawb://did:key:z6Mkfh4Y.../projectflowb3cded1async from playground1d ago| #1 | import { motion } from "framer-motion"; |
| #2 | import { Monitor, Sun, Moon, Check } from "lucide-react"; |
| #3 | import clsx from "clsx"; |
| #4 | import { useStore } from "../../application/stores"; |
| #5 | import type { ThemePreference } from "../../application/stores/slices/ui.slice"; |
| #6 | |
| #7 | const themeOptions: { |
| #8 | value: ThemePreference; |
| #9 | label: string; |
| #10 | description: string; |
| #11 | icon: typeof Monitor; |
| #12 | }[] = [ |
| #13 | { |
| #14 | value: "system", |
| #15 | label: "System", |
| #16 | description: "Match your device settings", |
| #17 | icon: Monitor, |
| #18 | }, |
| #19 | { |
| #20 | value: "light", |
| #21 | label: "Light", |
| #22 | description: "Light theme for daytime use", |
| #23 | icon: Sun, |
| #24 | }, |
| #25 | { |
| #26 | value: "dark", |
| #27 | label: "Dark", |
| #28 | description: "Dark theme for low light", |
| #29 | icon: Moon, |
| #30 | }, |
| #31 | ]; |
| #32 | |
| #33 | export function SettingsView() { |
| #34 | const { theme, setTheme } = useStore(); |
| #35 | |
| #36 | const container = { |
| #37 | hidden: { opacity: 0 }, |
| #38 | show: { opacity: 1, transition: { staggerChildren: 0.05 } }, |
| #39 | }; |
| #40 | const item = { |
| #41 | hidden: { opacity: 0, y: 10 }, |
| #42 | show: { opacity: 1, y: 0 }, |
| #43 | }; |
| #44 | |
| #45 | return ( |
| #46 | <motion.div |
| #47 | variants={container} |
| #48 | initial="hidden" |
| #49 | animate="show" |
| #50 | className="p-6 max-w-3xl mx-auto space-y-8" |
| #51 | > |
| #52 | <motion.div variants={item}> |
| #53 | <h1 className="page-title">Settings</h1> |
| #54 | <p className="text-sm text-zinc-500 mt-1"> |
| #55 | Manage your preferences |
| #56 | </p> |
| #57 | </motion.div> |
| #58 | |
| #59 | <motion.div variants={item} className="card space-y-4"> |
| #60 | <div> |
| #61 | <h2 className="text-sm font-semibold text-zinc-200">Appearance</h2> |
| #62 | <p className="text-sm text-zinc-500 mt-0.5"> |
| #63 | Choose how ProjectFlow looks on this device |
| #64 | </p> |
| #65 | </div> |
| #66 | |
| #67 | <div className="grid grid-cols-3 gap-3"> |
| #68 | {themeOptions.map((option) => { |
| #69 | const isActive = theme === option.value; |
| #70 | const Icon = option.icon; |
| #71 | |
| #72 | return ( |
| #73 | <button |
| #74 | key={option.value} |
| #75 | onClick={() => setTheme(option.value)} |
| #76 | className={clsx( |
| #77 | "relative flex flex-col items-center gap-3 p-4 rounded-xl border-2 transition-all", |
| #78 | isActive |
| #79 | ? "border-accent bg-accent/5" |
| #80 | : "border-border hover:border-border-strong bg-surface-2" |
| #81 | )} |
| #82 | > |
| #83 | {isActive && ( |
| #84 | <div className="absolute top-2 right-2"> |
| #85 | <div className="w-5 h-5 rounded-full bg-accent flex items-center justify-center"> |
| #86 | <Check size={12} className="text-white" /> |
| #87 | </div> |
| #88 | </div> |
| #89 | )} |
| #90 | |
| #91 | <div |
| #92 | className={clsx( |
| #93 | "w-10 h-10 rounded-lg flex items-center justify-center transition-colors", |
| #94 | isActive |
| #95 | ? "bg-accent/15 text-accent" |
| #96 | : "bg-surface-3 text-zinc-400" |
| #97 | )} |
| #98 | > |
| #99 | <Icon size={20} /> |
| #100 | </div> |
| #101 | |
| #102 | <div className="text-center"> |
| #103 | <p |
| #104 | className={clsx( |
| #105 | "text-sm font-medium", |
| #106 | isActive ? "text-zinc-100" : "text-zinc-300" |
| #107 | )} |
| #108 | > |
| #109 | {option.label} |
| #110 | </p> |
| #111 | <p className="text-2xs text-zinc-500 mt-0.5"> |
| #112 | {option.description} |
| #113 | </p> |
| #114 | </div> |
| #115 | |
| #116 | {/* Theme preview */} |
| #117 | <div |
| #118 | className={clsx( |
| #119 | "w-full h-12 rounded-lg border overflow-hidden relative", |
| #120 | option.value === "dark" || (option.value === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches) |
| #121 | ? "bg-[#09090b] border-[#27272a]" |
| #122 | : "bg-white border-gray-200" |
| #123 | )} |
| #124 | > |
| #125 | <div className="absolute inset-0 flex"> |
| #126 | {/* Sidebar preview */} |
| #127 | <div |
| #128 | className={clsx( |
| #129 | "w-1/4 h-full", |
| #130 | option.value === "dark" || (option.value === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches) |
| #131 | ? "bg-[#111113] border-r border-[#27272a]" |
| #132 | : "bg-gray-50 border-r border-gray-200" |
| #133 | )} |
| #134 | /> |
| #135 | {/* Content preview */} |
| #136 | <div className="flex-1 p-1.5 space-y-1"> |
| #137 | <div |
| #138 | className={clsx( |
| #139 | "h-1.5 w-2/3 rounded-full", |
| #140 | option.value === "dark" || (option.value === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches) |
| #141 | ? "bg-[#27272a]" |
| #142 | : "bg-gray-200" |
| #143 | )} |
| #144 | /> |
| #145 | <div |
| #146 | className={clsx( |
| #147 | "h-1 w-1/2 rounded-full", |
| #148 | option.value === "dark" || (option.value === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches) |
| #149 | ? "bg-[#1f1f23]" |
| #150 | : "bg-gray-100" |
| #151 | )} |
| #152 | /> |
| #153 | </div> |
| #154 | </div> |
| #155 | </div> |
| #156 | </button> |
| #157 | ); |
| #158 | })} |
| #159 | </div> |
| #160 | </motion.div> |
| #161 | </motion.div> |
| #162 | ); |
| #163 | } |
| #164 |