@@ -2,59 +2,173 @@ import type React from "react";
22import type { ThemeMode } from "../hooks/use-theme-mode" ;
33import { getPrefix } from "../store" ;
44
5+ const defaultOptions = {
6+ mode : "auto" as ThemeMode ,
7+ defaultMode : "auto" as ThemeMode ,
8+ localStorageKey : "flowbite-theme-mode" ,
9+ prefix : "" ,
10+ } ;
11+
512export interface ThemeModeScriptProps extends React . ComponentPropsWithoutRef < "script" > {
13+ /**
14+ * The current theme mode to use
15+ *
16+ * @type {"light" | "dark" | "auto" }
17+ */
618 mode ?: ThemeMode ;
719 /**
8- * "light" | "dark" | "auto"
20+ * The default theme mode if none is set
21+ *
22+ * @type {"light" | "dark" | "auto" }
23+ * @default "auto"
924 */
1025 defaultMode ?: ThemeMode ;
1126 /**
27+ * Key used to store theme mode in localStorage
28+ *
29+ * @type {string }
1230 * @default "flowbite-theme-mode"
1331 */
1432 localStorageKey ?: string ;
1533}
1634
35+ /**
36+ * A script component that handles theme mode initialization
37+ *
38+ * @param {Object } props - The component props
39+ * @param {ThemeMode } [props.mode] - The current theme mode to use
40+ * @param {ThemeMode } [props.defaultMode="auto"] - The default theme mode if none is set
41+ * @param {string } [props.localStorageKey="flowbite-theme-mode"] - Key used to store theme mode in localStorage
42+ * @param {React.ComponentPropsWithoutRef<"script"> } props.others - Additional script element props
43+ * @returns {JSX.Element } Script element that initializes theme mode
44+ */
1745export function ThemeModeScript ( {
1846 mode,
19- defaultMode = "light" ,
20- localStorageKey = "flowbite-theme-mode" ,
47+ defaultMode = defaultOptions . defaultMode ,
48+ localStorageKey = defaultOptions . localStorageKey ,
2149 ...others
22- } : ThemeModeScriptProps ) {
50+ } : ThemeModeScriptProps ) : JSX . Element {
2351 return (
2452 < script
2553 { ...others }
2654 data-flowbite-theme-mode-script
2755 dangerouslySetInnerHTML = { {
28- __html : getScript ( { mode, defaultMode, localStorageKey, prefix : getPrefix ( ) ?? "" } ) ,
56+ __html : getThemeModeScript ( { mode, defaultMode, localStorageKey, prefix : getPrefix ( ) ?? "" } ) ,
2957 } }
3058 />
3159 ) ;
3260}
3361
3462ThemeModeScript . displayName = "ThemeModeScript" ;
3563
36- function getScript ( {
37- mode,
38- defaultMode,
39- localStorageKey,
40- prefix,
41- } : {
42- mode ?: ThemeMode ;
43- defaultMode : ThemeMode ;
44- localStorageKey : string ;
45- prefix : string ;
46- } ) {
64+ /**
65+ * Generates a script string that handles theme mode initialization
66+ *
67+ * @param {Object } options - The options object
68+ * @param {ThemeMode } [options.mode] - The current theme mode to use
69+ * @param {ThemeMode } [options.defaultMode="auto"] - The default theme mode if none is set
70+ * @param {string } [options.localStorageKey="flowbite-theme-mode"] - Key used to store theme mode in localStorage
71+ * @param {string } [options.prefix=""] - The prefix to use for the theme mode class
72+ * @returns {string } Script string that initializes theme mode
73+ */
74+ export function getThemeModeScript (
75+ props : {
76+ mode ?: ThemeMode ;
77+ defaultMode ?: ThemeMode ;
78+ localStorageKey ?: string ;
79+ prefix ?: string ;
80+ } = { } ,
81+ ) : string {
82+ const {
83+ mode,
84+ defaultMode = defaultOptions . defaultMode ,
85+ localStorageKey = defaultOptions . localStorageKey ,
86+ prefix = defaultOptions . prefix ,
87+ } = props ;
88+
4789 return `
4890 try {
49- const mode = window.localStorage.getItem("${ localStorageKey } ") ?? "${ mode } " ?? "${ defaultMode } ";
91+ const resolvedMode = window.localStorage.getItem("${ localStorageKey } ") ?? "${ mode } " ?? "${ defaultMode } ";
5092 const computedMode =
51- mode === "auto" ? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light") : mode ;
93+ resolvedMode === "auto" ? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light") : resolvedMode ;
5294
5395 if (computedMode === "dark") {
5496 document.documentElement.classList.add("${ prefix } dark");
5597 } else {
5698 document.documentElement.classList.remove("${ prefix } dark");
5799 }
100+ localStorage.setItem("${ localStorageKey } ", resolvedMode);
101+
102+ // Add listener for system theme changes
103+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
104+ mediaQuery.addEventListener("change", (e) => {
105+ const currentMode = window.localStorage.getItem("${ localStorageKey } ");
106+ if (currentMode === "auto") {
107+ if (e.matches) {
108+ document.documentElement.classList.add("${ prefix } dark");
109+ } else {
110+ document.documentElement.classList.remove("${ prefix } dark");
111+ }
112+ }
113+ });
58114 } catch (e) {}
59115 ` ;
60116}
117+
118+ /**
119+ * Initializes the theme mode by checking localStorage, provided mode, or default mode
120+ * and applies the appropriate class to the document element
121+ *
122+ * @param {Object } options - The options object
123+ * @param {ThemeMode } [options.mode] - The current theme mode to use
124+ * @param {ThemeMode } [options.defaultMode="auto"] - The default theme mode if none is set
125+ * @param {string } [options.localStorageKey="flowbite-theme-mode"] - Key used to store theme mode in localStorage
126+ * @param {string } [options.prefix=""] - The prefix to use for the theme mode class
127+ * @returns {void }
128+ */
129+ export function initThemeMode (
130+ props : {
131+ mode ?: ThemeMode ;
132+ defaultMode ?: ThemeMode ;
133+ localStorageKey ?: string ;
134+ prefix ?: string ;
135+ } = { } ,
136+ ) : void {
137+ const {
138+ mode,
139+ defaultMode = defaultOptions . defaultMode ,
140+ localStorageKey = defaultOptions . localStorageKey ,
141+ prefix = defaultOptions . prefix ,
142+ } = props ;
143+
144+ try {
145+ const resolvedMode = window . localStorage . getItem ( localStorageKey ) ?? mode ?? defaultMode ;
146+ const computedMode =
147+ resolvedMode === "auto"
148+ ? window . matchMedia ( "(prefers-color-scheme: dark)" ) . matches
149+ ? "dark"
150+ : "light"
151+ : resolvedMode ;
152+
153+ if ( computedMode === "dark" ) {
154+ document . documentElement . classList . add ( `${ prefix } dark` ) ;
155+ } else {
156+ document . documentElement . classList . remove ( `${ prefix } dark` ) ;
157+ }
158+ localStorage . setItem ( localStorageKey , resolvedMode ) ;
159+
160+ // Add listener for system theme changes
161+ const mediaQuery = window . matchMedia ( "(prefers-color-scheme: dark)" ) ;
162+ mediaQuery . addEventListener ( "change" , ( e ) => {
163+ const currentMode = window . localStorage . getItem ( localStorageKey ) ;
164+ if ( currentMode === "auto" ) {
165+ if ( e . matches ) {
166+ document . documentElement . classList . add ( `${ prefix } dark` ) ;
167+ } else {
168+ document . documentElement . classList . remove ( `${ prefix } dark` ) ;
169+ }
170+ }
171+ } ) ;
172+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-empty
173+ } catch ( e ) { }
174+ }
0 commit comments