JavaScript 56.7%
TypeScript 42.6%
1"use client";23import { Sun, Moon, Monitor, Check, Rows3, AlignJustify, Zap, ZapOff } from "lucide-react";4import Modal from "../ui/Modal";5import { useTheme } from "./ThemeProvider";6import { useShell } from "../layout/ShellContext";7import { ACCENTS, type ThemePref } from "@/lib/prefs";89const themeOptions: { id: ThemePref; label: string; icon: typeof Sun }[] = [10 { id: "light", label: "Clair", icon: Sun },11 { id: "dark", label: "Sombre", icon: Moon },12 { id: "system", label: "Système", icon: Monitor },13];1415export default function PreferencesModal() {16 const { prefsOpen, setPrefsOpen } = useShell();17 const { themePref, setThemePref, accent, setAccent, density, setDensity, reduceMotion, setReduceMotion } = useTheme();1819 return (20 <Modal isOpen={prefsOpen} onClose={() => setPrefsOpen(false)} title="Apparence" description="Vos préférences sont enregistrées sur cet appareil." maxWidth="max-w-lg">21 <div className="space-y-6">22 {/* Thème */}23 <section>24 <h3 className="text-[12px] font-semibold uppercase tracking-[0.12em] text-gray-400 mb-2.5">Thème</h3>25 <div className="grid grid-cols-3 gap-2">26 {themeOptions.map((o) => {27 const on = themePref === o.id;28 return (29 <button30 key={o.id}31 onClick={() => setThemePref(o.id)}32 className={`relative flex flex-col items-center gap-2 p-3 rounded-2xl border transition-all ${on ? "border-blue-500 bg-blue-500/10 shadow-glow" : "border-line dark:border-white/10 hover:border-gray-300 dark:hover:border-white/20"}`}33 >34 <span className={`w-full h-12 rounded-xl overflow-hidden border border-black/5 dark:border-white/10 ${o.id === "light" ? "bg-[#f5f6f8]" : o.id === "dark" ? "bg-[#0a0b10]" : "bg-gradient-to-r from-[#f5f6f8] to-[#0a0b10]"}`}>35 <span className={`block mt-2 ml-2 w-1/2 h-2 rounded-full ${o.id === "dark" ? "bg-white/20" : "bg-black/10"}`} />36 <span className={`block mt-1.5 ml-2 w-2/3 h-2 rounded-full ${o.id === "dark" ? "bg-white/10" : "bg-black/5"}`} />37 </span>38 <span className="flex items-center gap-1.5 text-[13px] font-medium text-gray-800 dark:text-gray-100"><o.icon className="w-3.5 h-3.5" />{o.label}</span>39 {on && <Check className="absolute top-2 right-2 w-4 h-4 text-blue-600 dark:text-blue-300" />}40 </button>41 );42 })}43 </div>44 </section>4546 {/* Accent */}47 <section>48 <h3 className="text-[12px] font-semibold uppercase tracking-[0.12em] text-gray-400 mb-2.5">Couleur d'accent</h3>49 <div className="flex flex-wrap gap-2.5">50 {ACCENTS.map((a) => {51 const on = accent === a.id;52 return (53 <button54 key={a.id}55 onClick={() => setAccent(a.id)}56 title={`${a.label} — ${a.hint}`}57 className={`group flex items-center gap-2 pl-1.5 pr-3 h-10 rounded-full border transition-all ${on ? "border-gray-900 dark:border-white bg-gray-900/5 dark:bg-white/10" : "border-line dark:border-white/10 hover:border-gray-300 dark:hover:border-white/25"}`}58 >59 <span className="w-7 h-7 rounded-full flex items-center justify-center shadow-inner" style={{ background: `linear-gradient(135deg, ${a.hex}, ${a.hex}cc)` }}>60 {on && <Check className="w-4 h-4 text-white drop-shadow" />}61 </span>62 <span className="text-[13px] font-medium text-gray-800 dark:text-gray-100">{a.label}</span>63 </button>64 );65 })}66 </div>67 </section>6869 {/* Densité */}70 <section>71 <h3 className="text-[12px] font-semibold uppercase tracking-[0.12em] text-gray-400 mb-2.5">Densité des listes</h3>72 <div className="grid grid-cols-2 gap-2">73 {[74 { id: "comfortable" as const, label: "Confortable", icon: Rows3, hint: "Lignes aérées" },75 { id: "compact" as const, label: "Compacte", icon: AlignJustify, hint: "Plus de fichiers à l'écran" },76 ].map((d) => {77 const on = density === d.id;78 return (79 <button key={d.id} onClick={() => setDensity(d.id)} className={`flex items-center gap-3 p-3 rounded-2xl border text-left transition-all ${on ? "border-blue-500 bg-blue-500/10" : "border-line dark:border-white/10 hover:border-gray-300 dark:hover:border-white/20"}`}>80 <d.icon className={`w-5 h-5 ${on ? "text-blue-600 dark:text-blue-300" : "text-gray-400"}`} />81 <span>82 <span className="block text-[13px] font-medium text-gray-800 dark:text-gray-100">{d.label}</span>83 <span className="block text-[11.5px] text-gray-400">{d.hint}</span>84 </span>85 </button>86 );87 })}88 </div>89 </section>9091 {/* Mouvement */}92 <section>93 <button94 onClick={() => setReduceMotion(!reduceMotion)}95 className="w-full flex items-center gap-3 p-3 rounded-2xl border border-line dark:border-white/10 hover:border-gray-300 dark:hover:border-white/20 transition-colors text-left"96 role="switch"97 aria-checked={reduceMotion}98 >99 {reduceMotion ? <ZapOff className="w-5 h-5 text-gray-400" /> : <Zap className="w-5 h-5 text-blue-500" />}100 <span className="flex-1">101 <span className="block text-[13px] font-medium text-gray-800 dark:text-gray-100">Réduire les animations</span>102 <span className="block text-[11.5px] text-gray-400">Désactive les transitions et effets de mouvement</span>103 </span>104 <span className={`relative w-11 h-6 rounded-full transition-colors ${reduceMotion ? "bg-blue-500" : "bg-gray-300 dark:bg-white/15"}`}>105 <span className={`absolute top-0.5 w-5 h-5 rounded-full bg-white shadow transition-transform ${reduceMotion ? "translate-x-[22px]" : "translate-x-0.5"}`} />106 </span>107 </button>108 </section>109110 <p className="text-[11.5px] text-gray-400 text-center">111 Raccourcis : <kbd>⌘K</kbd> palette · <kbd>⌘B</kbd> barre latérale · <kbd>⌘,</kbd> préférences · <kbd>/</kbd> recherche112 </p>113 </div>114 </Modal>115 );116}117