Spaces:
Running
Running
Ananya Sridhar
Add dark mode: shared theme system, toggle in sidebar, applied to Sidebar/TherapistInput/PracticeScreen/ResultScreen/CharacterSelect
1896040 | import { useState } from "react"; | |
| import { CHARACTERS } from "../assets/characters"; | |
| import { LANGUAGES } from "../utils/i18n"; | |
| import { getTheme } from "../utils/themes"; | |
| const THEMES = { | |
| BOLT: { bg: "#EEF4FB", accent: "#5B9BD5", card: "#DDEAF7", text: "#1A3A5C", sub: "#4A7AA5" }, | |
| ZARA: { bg: "#F5EEFB", accent: "#B57ED5", card: "#EDD8F7", text: "#3A1A5C", sub: "#7A4AA5" }, | |
| NOVA: { bg: "#EEF7EF", accent: "#6BBF7A", card: "#D5EDDA", text: "#1A3A1C", sub: "#3A7A4A" }, | |
| BEEP: { bg: "#FDF6E8", accent: "#E8B84B", card: "#FAE8B8", text: "#3A2A00", sub: "#7A5A10" }, | |
| ECHO: { bg: "#FBF0EC", accent: "#E87B5A", card: "#F5D5C8", text: "#3A1200", sub: "#8A3A20" }, | |
| MIRA: { bg: "#EAF7F7", accent: "#4ABFBF", card: "#C8EAEA", text: "#003A3A", sub: "#1A6A6A" }, | |
| }; | |
| const LANG_NATIVE = { english: "English", hindi: "हिन्दी", kannada: "ಕನ್ನಡ" }; | |
| export default function Sidebar({ character, language, currentScreen, onSwitchCharacter, onSwitchLanguage, onHome, onShowTutorial, darkMode, onToggleDarkMode }) { | |
| const [open, setOpen] = useState(false); | |
| const th = getTheme(character, darkMode); | |
| if (currentScreen === "language_select") return null; | |
| return ( | |
| <> | |
| {open && ( | |
| <div onClick={() => setOpen(false)} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.15)", zIndex: 98, backdropFilter: "blur(2px)" }} /> | |
| )} | |
| <div style={{ | |
| position: "fixed", top: 0, right: 0, bottom: 0, zIndex: 99, | |
| width: open ? "260px" : "0px", | |
| transition: "width 0.3s cubic-bezier(0.4,0,0.2,1)", | |
| overflow: "hidden", | |
| }}> | |
| <div style={{ | |
| width: "260px", height: "100%", | |
| background: darkMode ? "rgba(20,16,14,0.97)" : "rgba(255,255,255,0.97)", backdropFilter: "blur(20px)", | |
| boxShadow: "-4px 0 24px rgba(0,0,0,0.1)", | |
| display: "flex", flexDirection: "column", | |
| padding: "24px 16px", overflowY: "auto", | |
| }}> | |
| <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "24px" }}> | |
| <p style={{ fontFamily: "Nunito, sans-serif", fontWeight: 900, fontSize: "1rem", color: th.text, margin: 0 }}>Settings</p> | |
| <button onClick={() => setOpen(false)} style={{ background: th.card, border: "none", borderRadius: "50%", width: "32px", height: "32px", cursor: "pointer", fontSize: "1rem", color: th.sub }}>✕</button> | |
| </div> | |
| <button onClick={() => { onHome(); setOpen(false); }} style={{ | |
| display: "flex", alignItems: "center", gap: "12px", | |
| background: th.card, border: `1.5px solid ${th.accent}33`, | |
| borderRadius: "14px", padding: "12px 16px", cursor: "pointer", | |
| marginBottom: "10px", width: "100%", | |
| }}> | |
| <span style={{ fontSize: "1.3rem" }}>🏠</span> | |
| <span style={{ fontFamily: "Nunito, sans-serif", fontWeight: 800, fontSize: "0.9rem", color: th.text }}>Home</span> | |
| </button> | |
| <button onClick={() => { onShowTutorial(); setOpen(false); }} style={{ | |
| display: "flex", alignItems: "center", gap: "12px", | |
| background: "transparent", border: `1.5px solid ${th.accent}33`, | |
| borderRadius: "14px", padding: "12px 16px", cursor: "pointer", | |
| marginBottom: "20px", width: "100%", | |
| }}> | |
| <span style={{ fontSize: "1.3rem" }}>❓</span> | |
| <span style={{ fontFamily: "Nunito, sans-serif", fontWeight: 800, fontSize: "0.9rem", color: th.text }}>How it works</span> | |
| </button> | |
| <button onClick={onToggleDarkMode} style={{ | |
| display: "flex", alignItems: "center", justifyContent: "space-between", | |
| background: "transparent", border: `1.5px solid ${th.accent}33`, | |
| borderRadius: "14px", padding: "12px 16px", cursor: "pointer", | |
| marginBottom: "20px", width: "100%", | |
| }}> | |
| <span style={{ display: "flex", alignItems: "center", gap: "12px" }}> | |
| <span style={{ fontSize: "1.3rem" }}>{darkMode ? "🌙" : "☀️"}</span> | |
| <span style={{ fontFamily: "Nunito, sans-serif", fontWeight: 800, fontSize: "0.9rem", color: th.text }}> | |
| {darkMode ? "Dark mode" : "Light mode"} | |
| </span> | |
| </span> | |
| <span style={{ | |
| width: "38px", height: "22px", borderRadius: "11px", | |
| background: darkMode ? th.accent : "rgba(0,0,0,0.15)", | |
| position: "relative", transition: "background 0.25s", | |
| }}> | |
| <span style={{ | |
| position: "absolute", top: "2px", | |
| left: darkMode ? "18px" : "2px", | |
| width: "18px", height: "18px", borderRadius: "50%", | |
| background: "#fff", transition: "left 0.25s", | |
| boxShadow: "0 1px 3px rgba(0,0,0,0.3)", | |
| }} /> | |
| </span> | |
| </button> | |
| <p style={{ fontFamily: "Nunito, sans-serif", fontWeight: 800, fontSize: "0.7rem", color: th.sub, letterSpacing: "0.1em", textTransform: "uppercase", margin: "0 0 10px 0" }}>Character</p> | |
| <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "8px", marginBottom: "24px" }}> | |
| {Object.values(CHARACTERS).map(c => ( | |
| <button key={c.id} onClick={() => { onSwitchCharacter(c.id); setOpen(false); }} style={{ | |
| display: "flex", flexDirection: "column", alignItems: "center", gap: "4px", | |
| background: c.id === character ? getTheme(c.id, darkMode).card : "transparent", | |
| border: `1.5px solid ${c.id === character ? getTheme(c.id, darkMode).accent : "rgba(0,0,0,0.08)"}`, | |
| borderRadius: "12px", padding: "8px 4px", cursor: "pointer", transition: "all 0.2s", | |
| }}> | |
| <img src={c.image} alt={c.name} style={{ width: "36px", height: "36px", objectFit: "contain" }} /> | |
| <span style={{ fontSize: "0.6rem", fontWeight: 700, color: getTheme(c.id, darkMode).text, fontFamily: "Nunito, sans-serif" }}>{c.name}</span> | |
| </button> | |
| ))} | |
| </div> | |
| <p style={{ fontFamily: "Nunito, sans-serif", fontWeight: 800, fontSize: "0.7rem", color: th.sub, letterSpacing: "0.1em", textTransform: "uppercase", margin: "0 0 10px 0" }}>Language</p> | |
| <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}> | |
| {LANGUAGES.map(lang => ( | |
| <button key={lang.code} onClick={() => { onSwitchLanguage(lang.code); setOpen(false); }} style={{ | |
| display: "flex", alignItems: "center", gap: "12px", | |
| background: lang.code === language ? th.card : "transparent", | |
| border: `1.5px solid ${lang.code === language ? th.accent : "rgba(0,0,0,0.08)"}`, | |
| borderRadius: "12px", padding: "10px 12px", cursor: "pointer", transition: "all 0.2s", | |
| }}> | |
| <span style={{ fontSize: "1.2rem" }}>{lang.flag}</span> | |
| <span style={{ fontFamily: "Nunito, sans-serif", fontWeight: 700, fontSize: "0.85rem", color: th.text }}>{LANG_NATIVE[lang.code]}</span> | |
| {lang.code === language && ( | |
| <div style={{ marginLeft: "auto", width: "18px", height: "18px", borderRadius: "50%", background: th.accent, display: "flex", alignItems: "center", justifyContent: "center" }}> | |
| <svg width="9" height="9" viewBox="0 0 14 14" fill="none"> | |
| <path d="M2 7l4 4 6-6" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/> | |
| </svg> | |
| </div> | |
| )} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| <button onClick={() => setOpen(!open)} style={{ | |
| position: "fixed", right: open ? "260px" : "0px", top: "50%", | |
| transform: "translateY(-50%)", | |
| background: th.accent, border: "none", | |
| borderRadius: "12px 0 0 12px", | |
| width: "28px", height: "56px", | |
| cursor: "pointer", zIndex: 100, | |
| display: "flex", alignItems: "center", justifyContent: "center", | |
| boxShadow: "-2px 0 12px rgba(0,0,0,0.1)", | |
| transition: "right 0.3s cubic-bezier(0.4,0,0.2,1)", | |
| color: "#fff", fontSize: "1.1rem", | |
| }}> | |
| {open ? "›" : "‹"} | |
| </button> | |
| </> | |
| ); | |
| } | |