@lolyjs/core 0.3.0-alpha.6 → 0.4.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +88 -0
  2. package/dist/{bootstrap-BfGTMUkj.d.mts → bootstrap-B6W6XoI5.d.mts} +6 -0
  3. package/dist/{bootstrap-BfGTMUkj.d.ts → bootstrap-B6W6XoI5.d.ts} +6 -0
  4. package/dist/cli.cjs +2969 -977
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.mjs +2936 -949
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/index.cjs +3159 -937
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.mts +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.mjs +3136 -920
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/react/cache.cjs.map +1 -1
  15. package/dist/react/cache.mjs.map +1 -1
  16. package/dist/react/components.cjs +16 -0
  17. package/dist/react/components.cjs.map +1 -1
  18. package/dist/react/components.d.mts +40 -2
  19. package/dist/react/components.d.ts +40 -2
  20. package/dist/react/components.mjs +15 -0
  21. package/dist/react/components.mjs.map +1 -1
  22. package/dist/react/hooks.cjs +24 -0
  23. package/dist/react/hooks.cjs.map +1 -1
  24. package/dist/react/hooks.d.mts +49 -1
  25. package/dist/react/hooks.d.ts +49 -1
  26. package/dist/react/hooks.mjs +22 -0
  27. package/dist/react/hooks.mjs.map +1 -1
  28. package/dist/react/themes.cjs +0 -169
  29. package/dist/react/themes.cjs.map +1 -1
  30. package/dist/react/themes.d.mts +18 -11
  31. package/dist/react/themes.d.ts +18 -11
  32. package/dist/react/themes.mjs +0 -159
  33. package/dist/react/themes.mjs.map +1 -1
  34. package/dist/runtime.cjs +268 -24
  35. package/dist/runtime.cjs.map +1 -1
  36. package/dist/runtime.d.mts +2 -2
  37. package/dist/runtime.d.ts +2 -2
  38. package/dist/runtime.mjs +255 -15
  39. package/dist/runtime.mjs.map +1 -1
  40. package/package.json +2 -2
@@ -3,10 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
6
  var __copyProps = (to, from, except, desc) => {
11
7
  if (from && typeof from === "object" || typeof from === "function") {
12
8
  for (let key of __getOwnPropNames(from))
@@ -19,170 +15,5 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
15
 
20
16
  // modules/react/themes/index.ts
21
17
  var themes_exports = {};
22
- __export(themes_exports, {
23
- ThemeProvider: () => ThemeProvider,
24
- useTheme: () => useTheme
25
- });
26
18
  module.exports = __toCommonJS(themes_exports);
27
-
28
- // modules/react/themes/theme-provider/index.tsx
29
- var import_react2 = require("react");
30
-
31
- // modules/react/hooks/useBroadcastChannel/index.tsx
32
- var import_react = require("react");
33
- var useBroadcastChannel = (channelName) => {
34
- const [message, setMessage] = (0, import_react.useState)(null);
35
- const channelRef = (0, import_react.useRef)(null);
36
- (0, import_react.useEffect)(() => {
37
- if (!channelRef.current && typeof window !== "undefined") {
38
- channelRef.current = new BroadcastChannel(channelName);
39
- }
40
- const channel = channelRef.current;
41
- if (!channel) return;
42
- const handleMessage = (event) => {
43
- setMessage(event.data);
44
- };
45
- channel.onmessage = handleMessage;
46
- return () => {
47
- if (channelRef.current) {
48
- channelRef.current.close();
49
- channelRef.current = null;
50
- }
51
- };
52
- }, [channelName]);
53
- const sendMessage = (0, import_react.useCallback)((msg) => {
54
- if (channelRef.current) {
55
- channelRef.current.postMessage(msg);
56
- }
57
- }, []);
58
- return { message, sendMessage };
59
- };
60
-
61
- // modules/react/themes/theme-provider/index.tsx
62
- var import_jsx_runtime = require("react/jsx-runtime");
63
- var ThemeContext = (0, import_react2.createContext)({ theme: "light", handleThemeChange: () => {
64
- } });
65
- function getCookie(name) {
66
- if (typeof document === "undefined") return null;
67
- const value = `; ${document.cookie}`;
68
- const parts = value.split(`; ${name}=`);
69
- if (parts.length === 2) {
70
- return parts.pop()?.split(";").shift() || null;
71
- }
72
- return null;
73
- }
74
- var ThemeProvider = ({
75
- children,
76
- initialTheme
77
- }) => {
78
- const { message: themeMessage, sendMessage } = useBroadcastChannel("theme_channel");
79
- const lastSentRef = (0, import_react2.useRef)(null);
80
- const [theme, setTheme] = (0, import_react2.useState)(() => {
81
- if (initialTheme) return initialTheme;
82
- if (typeof window !== "undefined") {
83
- const windowData = window.__FW_DATA__;
84
- if (windowData?.theme) return windowData.theme;
85
- }
86
- if (typeof window !== "undefined") {
87
- const cookieTheme = getCookie("theme");
88
- if (cookieTheme) return cookieTheme;
89
- }
90
- return "light";
91
- });
92
- (0, import_react2.useEffect)(() => {
93
- if (!themeMessage) return;
94
- if (themeMessage === lastSentRef.current) {
95
- lastSentRef.current = null;
96
- return;
97
- }
98
- setTheme((currentTheme) => {
99
- if (themeMessage !== currentTheme) {
100
- if (typeof document !== "undefined") {
101
- document.cookie = `theme=${themeMessage}; path=/; max-age=31536000`;
102
- }
103
- if (typeof window !== "undefined") {
104
- if (!window.__FW_DATA__) {
105
- window.__FW_DATA__ = {};
106
- }
107
- window.__FW_DATA__ = {
108
- ...window.__FW_DATA__,
109
- theme: themeMessage
110
- };
111
- }
112
- return themeMessage;
113
- }
114
- return currentTheme;
115
- });
116
- }, [themeMessage]);
117
- (0, import_react2.useEffect)(() => {
118
- const handleDataRefresh = () => {
119
- if (typeof window !== "undefined") {
120
- const windowData = window.__FW_DATA__;
121
- if (windowData?.theme) {
122
- setTheme((currentTheme) => {
123
- if (windowData.theme !== currentTheme) {
124
- return windowData.theme;
125
- }
126
- return currentTheme;
127
- });
128
- }
129
- }
130
- };
131
- if (typeof window !== "undefined") {
132
- window.addEventListener("fw-data-refresh", handleDataRefresh);
133
- return () => {
134
- window.removeEventListener("fw-data-refresh", handleDataRefresh);
135
- };
136
- }
137
- }, []);
138
- (0, import_react2.useEffect)(() => {
139
- if (initialTheme && initialTheme !== theme) {
140
- setTheme(initialTheme);
141
- }
142
- }, [initialTheme]);
143
- (0, import_react2.useEffect)(() => {
144
- if (typeof document === "undefined") return;
145
- const body = document.body;
146
- const currentClasses = body.className.split(" ").filter(Boolean);
147
- const themeClasses = ["light", "dark"];
148
- const filteredClasses = currentClasses.filter(
149
- (cls) => !themeClasses.includes(cls)
150
- );
151
- const newClassName = [...filteredClasses, theme].filter(Boolean).join(" ");
152
- if (body.className !== newClassName) {
153
- body.className = newClassName;
154
- }
155
- }, [theme]);
156
- const handleThemeChange = (newTheme) => {
157
- setTheme(newTheme);
158
- if (typeof document !== "undefined") {
159
- document.cookie = `theme=${newTheme}; path=/; max-age=31536000`;
160
- }
161
- if (typeof window !== "undefined") {
162
- if (!window.__FW_DATA__) {
163
- window.__FW_DATA__ = {};
164
- }
165
- window.__FW_DATA__ = {
166
- ...window.__FW_DATA__,
167
- theme: newTheme
168
- };
169
- }
170
- lastSentRef.current = newTheme;
171
- sendMessage(newTheme);
172
- setTimeout(() => {
173
- if (lastSentRef.current === newTheme) {
174
- lastSentRef.current = null;
175
- }
176
- }, 500);
177
- };
178
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: { theme, handleThemeChange }, children });
179
- };
180
- var useTheme = () => {
181
- return (0, import_react2.useContext)(ThemeContext);
182
- };
183
- // Annotate the CommonJS export names for ESM import in node:
184
- 0 && (module.exports = {
185
- ThemeProvider,
186
- useTheme
187
- });
188
19
  //# sourceMappingURL=themes.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../modules/react/themes/index.ts","../../modules/react/themes/theme-provider/index.tsx","../../modules/react/hooks/useBroadcastChannel/index.tsx"],"sourcesContent":["export {\r\n ThemeProvider,\r\n useTheme\r\n} from './theme-provider';","import React, { createContext, useContext, useState, useEffect, useRef } from \"react\";\r\nimport { useBroadcastChannel } from \"../../hooks/useBroadcastChannel\";\r\n\r\nconst ThemeContext = createContext<{\r\n theme: string;\r\n handleThemeChange: (theme: string) => void;\r\n}>({ theme: \"light\", handleThemeChange: () => {} });\r\n\r\n// Helper function to get cookie value\r\nfunction getCookie(name: string): string | null {\r\n if (typeof document === \"undefined\") return null;\r\n const value = `; ${document.cookie}`;\r\n const parts = value.split(`; ${name}=`);\r\n if (parts.length === 2) {\r\n return parts.pop()?.split(\";\").shift() || null;\r\n }\r\n return null;\r\n}\r\n\r\nexport const ThemeProvider = ({ \r\n children,\r\n initialTheme \r\n}: { \r\n children: React.ReactNode;\r\n initialTheme?: string;\r\n}) => {\r\n const { message: themeMessage, sendMessage } = useBroadcastChannel('theme_channel');\r\n \r\n // Track what we last sent to avoid loops\r\n const lastSentRef = useRef<string | null>(null);\r\n\r\n // Initialize theme consistently between server and client\r\n const [theme, setTheme] = useState<string>(() => {\r\n if (initialTheme) return initialTheme;\r\n \r\n if (typeof window !== \"undefined\") {\r\n const windowData = (window as any).__FW_DATA__;\r\n if (windowData?.theme) return windowData.theme;\r\n }\r\n \r\n if (typeof window !== \"undefined\") {\r\n const cookieTheme = getCookie(\"theme\");\r\n if (cookieTheme) return cookieTheme;\r\n }\r\n \r\n return \"light\";\r\n });\r\n\r\n // Handle messages from broadcast channel (other tabs)\r\n // This effect ONLY responds to themeMessage changes, not theme changes\r\n useEffect(() => {\r\n if (!themeMessage) return;\r\n \r\n // Ignore if this is a message we just sent\r\n if (themeMessage === lastSentRef.current) {\r\n lastSentRef.current = null;\r\n return;\r\n }\r\n \r\n // Only update if different from current theme\r\n setTheme((currentTheme) => {\r\n if (themeMessage !== currentTheme) {\r\n // Update cookie\r\n if (typeof document !== \"undefined\") {\r\n document.cookie = `theme=${themeMessage}; path=/; max-age=31536000`;\r\n }\r\n \r\n // Update window data\r\n if (typeof window !== \"undefined\") {\r\n if (!(window as any).__FW_DATA__) {\r\n (window as any).__FW_DATA__ = {};\r\n }\r\n (window as any).__FW_DATA__ = {\r\n ...(window as any).__FW_DATA__,\r\n theme: themeMessage,\r\n };\r\n }\r\n \r\n return themeMessage;\r\n }\r\n return currentTheme;\r\n });\r\n }, [themeMessage]); // Only depend on themeMessage, NOT theme!\r\n\r\n // Handle window.__FW_DATA__ changes during SPA navigation\r\n useEffect(() => {\r\n const handleDataRefresh = () => {\r\n if (typeof window !== \"undefined\") {\r\n const windowData = (window as any).__FW_DATA__;\r\n if (windowData?.theme) {\r\n setTheme((currentTheme) => {\r\n if (windowData.theme !== currentTheme) {\r\n return windowData.theme;\r\n }\r\n return currentTheme;\r\n });\r\n }\r\n }\r\n };\r\n\r\n if (typeof window !== \"undefined\") {\r\n window.addEventListener(\"fw-data-refresh\", handleDataRefresh);\r\n return () => {\r\n window.removeEventListener(\"fw-data-refresh\", handleDataRefresh);\r\n };\r\n }\r\n }, []); // No dependencies - event listener doesn't need theme\r\n\r\n // Handle initialTheme prop changes\r\n useEffect(() => {\r\n if (initialTheme && initialTheme !== theme) {\r\n setTheme(initialTheme);\r\n }\r\n }, [initialTheme]); // Only depend on initialTheme, not theme\r\n\r\n // Update body class when theme changes\r\n useEffect(() => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const body = document.body;\r\n const currentClasses = body.className.split(\" \").filter(Boolean);\r\n const themeClasses = [\"light\", \"dark\"];\r\n const filteredClasses = currentClasses.filter(\r\n (cls) => !themeClasses.includes(cls)\r\n );\r\n const newClassName = [...filteredClasses, theme].filter(Boolean).join(\" \");\r\n \r\n if (body.className !== newClassName) {\r\n body.className = newClassName;\r\n }\r\n }, [theme]);\r\n\r\n const handleThemeChange = (newTheme: string) => {\r\n // Update state immediately\r\n setTheme(newTheme);\r\n\r\n // Update cookie\r\n if (typeof document !== \"undefined\") {\r\n document.cookie = `theme=${newTheme}; path=/; max-age=31536000`;\r\n }\r\n\r\n // Update window data\r\n if (typeof window !== \"undefined\") {\r\n if (!(window as any).__FW_DATA__) {\r\n (window as any).__FW_DATA__ = {};\r\n }\r\n (window as any).__FW_DATA__ = {\r\n ...(window as any).__FW_DATA__,\r\n theme: newTheme,\r\n };\r\n }\r\n \r\n // Mark this as the last value we sent\r\n lastSentRef.current = newTheme;\r\n \r\n // Broadcast to other tabs\r\n sendMessage(newTheme);\r\n \r\n // Clear the ref after a delay\r\n setTimeout(() => {\r\n if (lastSentRef.current === newTheme) {\r\n lastSentRef.current = null;\r\n }\r\n }, 500);\r\n };\r\n\r\n return (\r\n <ThemeContext.Provider value={{ theme, handleThemeChange }}>\r\n {children}\r\n </ThemeContext.Provider>\r\n );\r\n};\r\n\r\nexport const useTheme = () => {\r\n return useContext(ThemeContext);\r\n};\r\n","import React, { useEffect, useState, useRef, useCallback } from \"react\";\r\n\r\nexport const useBroadcastChannel = (channelName: string) => {\r\n const [message, setMessage] = useState(null);\r\n const channelRef = useRef<BroadcastChannel | null>(null);\r\n\r\n useEffect(() => {\r\n // Create channel only once, inside useEffect\r\n if (!channelRef.current && typeof window !== \"undefined\") {\r\n channelRef.current = new BroadcastChannel(channelName);\r\n }\r\n\r\n const channel = channelRef.current;\r\n if (!channel) return;\r\n\r\n const handleMessage = (event: MessageEvent) => {\r\n setMessage(event.data);\r\n };\r\n\r\n channel.onmessage = handleMessage;\r\n\r\n // Clean up the channel when the component unmounts\r\n return () => {\r\n if (channelRef.current) {\r\n channelRef.current.close();\r\n channelRef.current = null;\r\n }\r\n };\r\n }, [channelName]);\r\n\r\n const sendMessage = useCallback((msg: unknown) => {\r\n if (channelRef.current) {\r\n channelRef.current.postMessage(msg);\r\n }\r\n }, []);\r\n\r\n return { message, sendMessage };\r\n};\r\n"],"mappings":"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","names":["import_react"]}
1
+ {"version":3,"sources":["../../modules/react/themes/index.ts"],"sourcesContent":["export type { LolyThemeAPI, ThemeScriptOptions } from \"./theme-global\";"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -1,13 +1,20 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
3
-
4
- declare const ThemeProvider: ({ children, initialTheme }: {
5
- children: React.ReactNode;
6
- initialTheme?: string;
7
- }) => react_jsx_runtime.JSX.Element;
8
- declare const useTheme: () => {
9
- theme: string;
10
- handleThemeChange: (theme: string) => void;
1
+ type LolyThemeAPI = {
2
+ set: (theme: string) => void;
3
+ get: () => string;
4
+ };
5
+ type ThemeScriptOptions = {
6
+ initialTheme?: string | null;
7
+ channelName?: string;
8
+ cookieName?: string;
9
+ themeClasses?: string[];
11
10
  };
11
+ declare global {
12
+ interface Window {
13
+ loly?: {
14
+ theme?: LolyThemeAPI;
15
+ [key: string]: any;
16
+ };
17
+ }
18
+ }
12
19
 
13
- export { ThemeProvider, useTheme };
20
+ export type { LolyThemeAPI, ThemeScriptOptions };
@@ -1,13 +1,20 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
3
-
4
- declare const ThemeProvider: ({ children, initialTheme }: {
5
- children: React.ReactNode;
6
- initialTheme?: string;
7
- }) => react_jsx_runtime.JSX.Element;
8
- declare const useTheme: () => {
9
- theme: string;
10
- handleThemeChange: (theme: string) => void;
1
+ type LolyThemeAPI = {
2
+ set: (theme: string) => void;
3
+ get: () => string;
4
+ };
5
+ type ThemeScriptOptions = {
6
+ initialTheme?: string | null;
7
+ channelName?: string;
8
+ cookieName?: string;
9
+ themeClasses?: string[];
11
10
  };
11
+ declare global {
12
+ interface Window {
13
+ loly?: {
14
+ theme?: LolyThemeAPI;
15
+ [key: string]: any;
16
+ };
17
+ }
18
+ }
12
19
 
13
- export { ThemeProvider, useTheme };
20
+ export type { LolyThemeAPI, ThemeScriptOptions };
@@ -1,160 +1 @@
1
- // modules/react/themes/theme-provider/index.tsx
2
- import { createContext, useContext, useState as useState2, useEffect as useEffect2, useRef as useRef2 } from "react";
3
-
4
- // modules/react/hooks/useBroadcastChannel/index.tsx
5
- import { useEffect, useState, useRef, useCallback } from "react";
6
- var useBroadcastChannel = (channelName) => {
7
- const [message, setMessage] = useState(null);
8
- const channelRef = useRef(null);
9
- useEffect(() => {
10
- if (!channelRef.current && typeof window !== "undefined") {
11
- channelRef.current = new BroadcastChannel(channelName);
12
- }
13
- const channel = channelRef.current;
14
- if (!channel) return;
15
- const handleMessage = (event) => {
16
- setMessage(event.data);
17
- };
18
- channel.onmessage = handleMessage;
19
- return () => {
20
- if (channelRef.current) {
21
- channelRef.current.close();
22
- channelRef.current = null;
23
- }
24
- };
25
- }, [channelName]);
26
- const sendMessage = useCallback((msg) => {
27
- if (channelRef.current) {
28
- channelRef.current.postMessage(msg);
29
- }
30
- }, []);
31
- return { message, sendMessage };
32
- };
33
-
34
- // modules/react/themes/theme-provider/index.tsx
35
- import { jsx } from "react/jsx-runtime";
36
- var ThemeContext = createContext({ theme: "light", handleThemeChange: () => {
37
- } });
38
- function getCookie(name) {
39
- if (typeof document === "undefined") return null;
40
- const value = `; ${document.cookie}`;
41
- const parts = value.split(`; ${name}=`);
42
- if (parts.length === 2) {
43
- return parts.pop()?.split(";").shift() || null;
44
- }
45
- return null;
46
- }
47
- var ThemeProvider = ({
48
- children,
49
- initialTheme
50
- }) => {
51
- const { message: themeMessage, sendMessage } = useBroadcastChannel("theme_channel");
52
- const lastSentRef = useRef2(null);
53
- const [theme, setTheme] = useState2(() => {
54
- if (initialTheme) return initialTheme;
55
- if (typeof window !== "undefined") {
56
- const windowData = window.__FW_DATA__;
57
- if (windowData?.theme) return windowData.theme;
58
- }
59
- if (typeof window !== "undefined") {
60
- const cookieTheme = getCookie("theme");
61
- if (cookieTheme) return cookieTheme;
62
- }
63
- return "light";
64
- });
65
- useEffect2(() => {
66
- if (!themeMessage) return;
67
- if (themeMessage === lastSentRef.current) {
68
- lastSentRef.current = null;
69
- return;
70
- }
71
- setTheme((currentTheme) => {
72
- if (themeMessage !== currentTheme) {
73
- if (typeof document !== "undefined") {
74
- document.cookie = `theme=${themeMessage}; path=/; max-age=31536000`;
75
- }
76
- if (typeof window !== "undefined") {
77
- if (!window.__FW_DATA__) {
78
- window.__FW_DATA__ = {};
79
- }
80
- window.__FW_DATA__ = {
81
- ...window.__FW_DATA__,
82
- theme: themeMessage
83
- };
84
- }
85
- return themeMessage;
86
- }
87
- return currentTheme;
88
- });
89
- }, [themeMessage]);
90
- useEffect2(() => {
91
- const handleDataRefresh = () => {
92
- if (typeof window !== "undefined") {
93
- const windowData = window.__FW_DATA__;
94
- if (windowData?.theme) {
95
- setTheme((currentTheme) => {
96
- if (windowData.theme !== currentTheme) {
97
- return windowData.theme;
98
- }
99
- return currentTheme;
100
- });
101
- }
102
- }
103
- };
104
- if (typeof window !== "undefined") {
105
- window.addEventListener("fw-data-refresh", handleDataRefresh);
106
- return () => {
107
- window.removeEventListener("fw-data-refresh", handleDataRefresh);
108
- };
109
- }
110
- }, []);
111
- useEffect2(() => {
112
- if (initialTheme && initialTheme !== theme) {
113
- setTheme(initialTheme);
114
- }
115
- }, [initialTheme]);
116
- useEffect2(() => {
117
- if (typeof document === "undefined") return;
118
- const body = document.body;
119
- const currentClasses = body.className.split(" ").filter(Boolean);
120
- const themeClasses = ["light", "dark"];
121
- const filteredClasses = currentClasses.filter(
122
- (cls) => !themeClasses.includes(cls)
123
- );
124
- const newClassName = [...filteredClasses, theme].filter(Boolean).join(" ");
125
- if (body.className !== newClassName) {
126
- body.className = newClassName;
127
- }
128
- }, [theme]);
129
- const handleThemeChange = (newTheme) => {
130
- setTheme(newTheme);
131
- if (typeof document !== "undefined") {
132
- document.cookie = `theme=${newTheme}; path=/; max-age=31536000`;
133
- }
134
- if (typeof window !== "undefined") {
135
- if (!window.__FW_DATA__) {
136
- window.__FW_DATA__ = {};
137
- }
138
- window.__FW_DATA__ = {
139
- ...window.__FW_DATA__,
140
- theme: newTheme
141
- };
142
- }
143
- lastSentRef.current = newTheme;
144
- sendMessage(newTheme);
145
- setTimeout(() => {
146
- if (lastSentRef.current === newTheme) {
147
- lastSentRef.current = null;
148
- }
149
- }, 500);
150
- };
151
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, handleThemeChange }, children });
152
- };
153
- var useTheme = () => {
154
- return useContext(ThemeContext);
155
- };
156
- export {
157
- ThemeProvider,
158
- useTheme
159
- };
160
1
  //# sourceMappingURL=themes.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../modules/react/themes/theme-provider/index.tsx","../../modules/react/hooks/useBroadcastChannel/index.tsx"],"sourcesContent":["import React, { createContext, useContext, useState, useEffect, useRef } from \"react\";\r\nimport { useBroadcastChannel } from \"../../hooks/useBroadcastChannel\";\r\n\r\nconst ThemeContext = createContext<{\r\n theme: string;\r\n handleThemeChange: (theme: string) => void;\r\n}>({ theme: \"light\", handleThemeChange: () => {} });\r\n\r\n// Helper function to get cookie value\r\nfunction getCookie(name: string): string | null {\r\n if (typeof document === \"undefined\") return null;\r\n const value = `; ${document.cookie}`;\r\n const parts = value.split(`; ${name}=`);\r\n if (parts.length === 2) {\r\n return parts.pop()?.split(\";\").shift() || null;\r\n }\r\n return null;\r\n}\r\n\r\nexport const ThemeProvider = ({ \r\n children,\r\n initialTheme \r\n}: { \r\n children: React.ReactNode;\r\n initialTheme?: string;\r\n}) => {\r\n const { message: themeMessage, sendMessage } = useBroadcastChannel('theme_channel');\r\n \r\n // Track what we last sent to avoid loops\r\n const lastSentRef = useRef<string | null>(null);\r\n\r\n // Initialize theme consistently between server and client\r\n const [theme, setTheme] = useState<string>(() => {\r\n if (initialTheme) return initialTheme;\r\n \r\n if (typeof window !== \"undefined\") {\r\n const windowData = (window as any).__FW_DATA__;\r\n if (windowData?.theme) return windowData.theme;\r\n }\r\n \r\n if (typeof window !== \"undefined\") {\r\n const cookieTheme = getCookie(\"theme\");\r\n if (cookieTheme) return cookieTheme;\r\n }\r\n \r\n return \"light\";\r\n });\r\n\r\n // Handle messages from broadcast channel (other tabs)\r\n // This effect ONLY responds to themeMessage changes, not theme changes\r\n useEffect(() => {\r\n if (!themeMessage) return;\r\n \r\n // Ignore if this is a message we just sent\r\n if (themeMessage === lastSentRef.current) {\r\n lastSentRef.current = null;\r\n return;\r\n }\r\n \r\n // Only update if different from current theme\r\n setTheme((currentTheme) => {\r\n if (themeMessage !== currentTheme) {\r\n // Update cookie\r\n if (typeof document !== \"undefined\") {\r\n document.cookie = `theme=${themeMessage}; path=/; max-age=31536000`;\r\n }\r\n \r\n // Update window data\r\n if (typeof window !== \"undefined\") {\r\n if (!(window as any).__FW_DATA__) {\r\n (window as any).__FW_DATA__ = {};\r\n }\r\n (window as any).__FW_DATA__ = {\r\n ...(window as any).__FW_DATA__,\r\n theme: themeMessage,\r\n };\r\n }\r\n \r\n return themeMessage;\r\n }\r\n return currentTheme;\r\n });\r\n }, [themeMessage]); // Only depend on themeMessage, NOT theme!\r\n\r\n // Handle window.__FW_DATA__ changes during SPA navigation\r\n useEffect(() => {\r\n const handleDataRefresh = () => {\r\n if (typeof window !== \"undefined\") {\r\n const windowData = (window as any).__FW_DATA__;\r\n if (windowData?.theme) {\r\n setTheme((currentTheme) => {\r\n if (windowData.theme !== currentTheme) {\r\n return windowData.theme;\r\n }\r\n return currentTheme;\r\n });\r\n }\r\n }\r\n };\r\n\r\n if (typeof window !== \"undefined\") {\r\n window.addEventListener(\"fw-data-refresh\", handleDataRefresh);\r\n return () => {\r\n window.removeEventListener(\"fw-data-refresh\", handleDataRefresh);\r\n };\r\n }\r\n }, []); // No dependencies - event listener doesn't need theme\r\n\r\n // Handle initialTheme prop changes\r\n useEffect(() => {\r\n if (initialTheme && initialTheme !== theme) {\r\n setTheme(initialTheme);\r\n }\r\n }, [initialTheme]); // Only depend on initialTheme, not theme\r\n\r\n // Update body class when theme changes\r\n useEffect(() => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const body = document.body;\r\n const currentClasses = body.className.split(\" \").filter(Boolean);\r\n const themeClasses = [\"light\", \"dark\"];\r\n const filteredClasses = currentClasses.filter(\r\n (cls) => !themeClasses.includes(cls)\r\n );\r\n const newClassName = [...filteredClasses, theme].filter(Boolean).join(\" \");\r\n \r\n if (body.className !== newClassName) {\r\n body.className = newClassName;\r\n }\r\n }, [theme]);\r\n\r\n const handleThemeChange = (newTheme: string) => {\r\n // Update state immediately\r\n setTheme(newTheme);\r\n\r\n // Update cookie\r\n if (typeof document !== \"undefined\") {\r\n document.cookie = `theme=${newTheme}; path=/; max-age=31536000`;\r\n }\r\n\r\n // Update window data\r\n if (typeof window !== \"undefined\") {\r\n if (!(window as any).__FW_DATA__) {\r\n (window as any).__FW_DATA__ = {};\r\n }\r\n (window as any).__FW_DATA__ = {\r\n ...(window as any).__FW_DATA__,\r\n theme: newTheme,\r\n };\r\n }\r\n \r\n // Mark this as the last value we sent\r\n lastSentRef.current = newTheme;\r\n \r\n // Broadcast to other tabs\r\n sendMessage(newTheme);\r\n \r\n // Clear the ref after a delay\r\n setTimeout(() => {\r\n if (lastSentRef.current === newTheme) {\r\n lastSentRef.current = null;\r\n }\r\n }, 500);\r\n };\r\n\r\n return (\r\n <ThemeContext.Provider value={{ theme, handleThemeChange }}>\r\n {children}\r\n </ThemeContext.Provider>\r\n );\r\n};\r\n\r\nexport const useTheme = () => {\r\n return useContext(ThemeContext);\r\n};\r\n","import React, { useEffect, useState, useRef, useCallback } from \"react\";\r\n\r\nexport const useBroadcastChannel = (channelName: string) => {\r\n const [message, setMessage] = useState(null);\r\n const channelRef = useRef<BroadcastChannel | null>(null);\r\n\r\n useEffect(() => {\r\n // Create channel only once, inside useEffect\r\n if (!channelRef.current && typeof window !== \"undefined\") {\r\n channelRef.current = new BroadcastChannel(channelName);\r\n }\r\n\r\n const channel = channelRef.current;\r\n if (!channel) return;\r\n\r\n const handleMessage = (event: MessageEvent) => {\r\n setMessage(event.data);\r\n };\r\n\r\n channel.onmessage = handleMessage;\r\n\r\n // Clean up the channel when the component unmounts\r\n return () => {\r\n if (channelRef.current) {\r\n channelRef.current.close();\r\n channelRef.current = null;\r\n }\r\n };\r\n }, [channelName]);\r\n\r\n const sendMessage = useCallback((msg: unknown) => {\r\n if (channelRef.current) {\r\n channelRef.current.postMessage(msg);\r\n }\r\n }, []);\r\n\r\n return { message, sendMessage };\r\n};\r\n"],"mappings":"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","names":["useState","useEffect","useRef","useRef","useState","useEffect"]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}