@zolomedia/bifrost-client 1.7.74
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.
- package/L1_Foundation/L1_Foundation.js +13 -0
- package/L1_Foundation/bootstrap/bootstrap.js +11 -0
- package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
- package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
- package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
- package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
- package/L1_Foundation/bootstrap/module_registry.js +102 -0
- package/L1_Foundation/bootstrap/prism_loader.js +164 -0
- package/L1_Foundation/config/client_config.js +110 -0
- package/L1_Foundation/config/config.js +7 -0
- package/L1_Foundation/connection/connection.js +8 -0
- package/L1_Foundation/connection/websocket_connection.js +122 -0
- package/L1_Foundation/constants/bifrost_constants.js +284 -0
- package/L1_Foundation/constants/constants.js +7 -0
- package/L1_Foundation/logger/logger.js +10 -0
- package/L2_Handling/L2_Handling.js +15 -0
- package/L2_Handling/cache/cache.js +22 -0
- package/L2_Handling/cache/cache_constants.js +69 -0
- package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
- package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
- package/L2_Handling/cache/orchestration/orchestration.js +12 -0
- package/L2_Handling/cache/storage/session_manager.js +289 -0
- package/L2_Handling/cache/storage/storage.js +10 -0
- package/L2_Handling/cache/storage/storage_manager.js +590 -0
- package/L2_Handling/display/composite/composite.js +13 -0
- package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
- package/L2_Handling/display/composite/swiper_renderer.js +564 -0
- package/L2_Handling/display/composite/terminal_renderer.js +922 -0
- package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
- package/L2_Handling/display/display.js +30 -0
- package/L2_Handling/display/feedback/feedback.js +11 -0
- package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
- package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
- package/L2_Handling/display/inputs/button_renderer.js +634 -0
- package/L2_Handling/display/inputs/form_renderer.js +583 -0
- package/L2_Handling/display/inputs/input_renderer.js +658 -0
- package/L2_Handling/display/inputs/inputs.js +12 -0
- package/L2_Handling/display/navigation/menu_renderer.js +206 -0
- package/L2_Handling/display/navigation/navigation.js +11 -0
- package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
- package/L2_Handling/display/orchestration/orchestration.js +11 -0
- package/L2_Handling/display/orchestration/renderer.js +430 -0
- package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
- package/L2_Handling/display/outputs/alert_renderer.js +161 -0
- package/L2_Handling/display/outputs/audio_renderer.js +94 -0
- package/L2_Handling/display/outputs/card_renderer.js +229 -0
- package/L2_Handling/display/outputs/code_renderer.js +66 -0
- package/L2_Handling/display/outputs/dl_renderer.js +131 -0
- package/L2_Handling/display/outputs/header_renderer.js +162 -0
- package/L2_Handling/display/outputs/icon_renderer.js +107 -0
- package/L2_Handling/display/outputs/image_renderer.js +145 -0
- package/L2_Handling/display/outputs/list_renderer.js +190 -0
- package/L2_Handling/display/outputs/outputs.js +19 -0
- package/L2_Handling/display/outputs/table_renderer.js +765 -0
- package/L2_Handling/display/outputs/text_renderer.js +818 -0
- package/L2_Handling/display/outputs/typography_renderer.js +293 -0
- package/L2_Handling/display/outputs/video_renderer.js +116 -0
- package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
- package/L2_Handling/display/primitives/form_primitives.js +526 -0
- package/L2_Handling/display/primitives/generic_containers.js +109 -0
- package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
- package/L2_Handling/display/primitives/link_primitives.js +552 -0
- package/L2_Handling/display/primitives/lists_primitives.js +262 -0
- package/L2_Handling/display/primitives/media_primitives.js +383 -0
- package/L2_Handling/display/primitives/primitives.js +19 -0
- package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
- package/L2_Handling/display/primitives/table_primitives.js +528 -0
- package/L2_Handling/display/primitives/typography_primitives.js +175 -0
- package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
- package/L2_Handling/display/specialized/specialized.js +10 -0
- package/L2_Handling/hooks/hooks.js +9 -0
- package/L2_Handling/hooks/menu_integration.js +57 -0
- package/L2_Handling/hooks/widget_hook_manager.js +292 -0
- package/L2_Handling/message/message.js +8 -0
- package/L2_Handling/message/message_handler.js +701 -0
- package/L2_Handling/navigation/navigation.js +8 -0
- package/L2_Handling/navigation/navigation_manager.js +403 -0
- package/L2_Handling/zhooks/features/cache_live.js +287 -0
- package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
- package/L2_Handling/zhooks/zhooks_manager.js +65 -0
- package/L2_Handling/zvaf/zvaf.js +8 -0
- package/L2_Handling/zvaf/zvaf_manager.js +334 -0
- package/L3_Abstraction/L3_Abstraction.js +12 -0
- package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
- package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
- package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
- package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
- package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
- package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
- package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
- package/L3_Abstraction/renderer/renderer.js +1 -0
- package/L3_Abstraction/session/session.js +1 -0
- package/L4_Orchestration/L4_Orchestration.js +11 -0
- package/L4_Orchestration/client/client.js +1 -0
- package/L4_Orchestration/facade/facade.js +9 -0
- package/L4_Orchestration/facade/manager_registry.js +118 -0
- package/L4_Orchestration/facade/renderer_registry.js +274 -0
- package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
- package/L4_Orchestration/lifecycle/initializer.js +135 -0
- package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
- package/L4_Orchestration/rendering/facade.js +94 -0
- package/L4_Orchestration/rendering/rendering.js +7 -0
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bifrost_client.js +204 -0
- package/bifrost_core.js +1686 -0
- package/docs/ARCHITECTURE.md +111 -0
- package/docs/PROTOCOL.md +106 -0
- package/docs/RENDERERS.md +101 -0
- package/docs/SECURITY.md +92 -0
- package/package.json +24 -0
- package/syntax/prism-zconfig.js +41 -0
- package/syntax/prism-zenv.js +69 -0
- package/syntax/prism-zolo-theme.css +288 -0
- package/syntax/prism-zolo.js +380 -0
- package/syntax/prism-zschema.js +38 -0
- package/syntax/prism-zspark.js +25 -0
- package/syntax/prism-zui.js +68 -0
- package/zSys/accessibility/accessibility.js +10 -0
- package/zSys/accessibility/emoji_accessibility.js +173 -0
- package/zSys/dom/block_utils.js +122 -0
- package/zSys/dom/container_utils.js +370 -0
- package/zSys/dom/dom.js +13 -0
- package/zSys/dom/dom_utils.js +328 -0
- package/zSys/dom/encoding_utils.js +117 -0
- package/zSys/dom/style_utils.js +71 -0
- package/zSys/errors/error_display.js +299 -0
- package/zSys/errors/errors.js +10 -0
- package/zSys/theme/color_utils.js +274 -0
- package/zSys/theme/dark_mode_utils.js +272 -0
- package/zSys/theme/size_utils.js +256 -0
- package/zSys/theme/spacing_utils.js +405 -0
- package/zSys/theme/theme.js +14 -0
- package/zSys/theme/zbase.css +1735 -0
- package/zSys/theme/zbase_inject.js +161 -0
- package/zSys/theme/ztheme_utils.js +305 -0
- package/zSys/validation/error_boundary.js +201 -0
- package/zSys/validation/validation.js +11 -0
- package/zSys/validation/validation_utils.js +238 -0
- package/zSys/zSys.js +14 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Color Utilities - Visual Identity Primitives
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* Pure functions for generating and applying color classes.
|
|
7
|
+
* These are foundational primitives for consistent visual identity.
|
|
8
|
+
*
|
|
9
|
+
* @module rendering/color_utils
|
|
10
|
+
* @layer 0 (Foundation - below Layer 2 utilities)
|
|
11
|
+
* @pattern Pure Functions (no state, no side effects)
|
|
12
|
+
*
|
|
13
|
+
* Philosophy:
|
|
14
|
+
* - "Color first" - Visual identity and semantics for all content
|
|
15
|
+
* - Pure functions (input → output, no side effects)
|
|
16
|
+
* - Uses zTheme color system exclusively (semantic colors)
|
|
17
|
+
* - Generates class names programmatically (no hardcoded strings)
|
|
18
|
+
*
|
|
19
|
+
* zTheme Semantic Colors:
|
|
20
|
+
* - primary: Brand color (green in zCLI)
|
|
21
|
+
* - secondary: Supporting brand
|
|
22
|
+
* - success: Green - positive actions/states
|
|
23
|
+
* - danger/error: Red - errors/critical
|
|
24
|
+
* - warning: Yellow - caution
|
|
25
|
+
* - info: Blue - informational
|
|
26
|
+
* - light: Light gray
|
|
27
|
+
* - dark: Dark gray
|
|
28
|
+
* - white: Pure white
|
|
29
|
+
* - black: Pure black (text only)
|
|
30
|
+
* - body: Default text color
|
|
31
|
+
* - muted: Secondary/subdued text
|
|
32
|
+
* - transparent: Transparent background
|
|
33
|
+
*
|
|
34
|
+
* Dependencies:
|
|
35
|
+
* - None (pure utility, no imports needed)
|
|
36
|
+
*
|
|
37
|
+
* Exports:
|
|
38
|
+
* - getBackgroundClass()
|
|
39
|
+
* - getTextColorClass()
|
|
40
|
+
* - getBorderColorClass()
|
|
41
|
+
* - applyColorScheme()
|
|
42
|
+
*
|
|
43
|
+
* Example:
|
|
44
|
+
* ```javascript
|
|
45
|
+
* import { getBackgroundClass, getTextColorClass, applyColorScheme } from './color_utils.js';
|
|
46
|
+
*
|
|
47
|
+
* const bg = getBackgroundClass('primary'); // 'zBg-primary'
|
|
48
|
+
* const text = getTextColorClass('white'); // 'zText-white'
|
|
49
|
+
*
|
|
50
|
+
* applyColorScheme(element, { background: 'light', text: 'dark', border: 'primary' });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
//
|
|
55
|
+
// Color Constants (verified against zTheme CSS)
|
|
56
|
+
//
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Valid background colors in zTheme
|
|
60
|
+
*/
|
|
61
|
+
const VALID_BG_COLORS = [
|
|
62
|
+
'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'error',
|
|
63
|
+
'light', 'dark', 'white', 'body', 'transparent'
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Valid text colors in zTheme
|
|
68
|
+
*/
|
|
69
|
+
const VALID_TEXT_COLORS = [
|
|
70
|
+
'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'error',
|
|
71
|
+
'light', 'dark', 'body', 'muted', 'white', 'black',
|
|
72
|
+
'black-50', 'white-50' // Opacity variants
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Valid border colors in zTheme
|
|
77
|
+
*/
|
|
78
|
+
const VALID_BORDER_COLORS = [
|
|
79
|
+
'primary', 'secondary', 'success', 'info', 'warning', 'danger',
|
|
80
|
+
'light', 'dark', 'white'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
//
|
|
84
|
+
// Background Color Functions
|
|
85
|
+
//
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generate background color class (zTheme: zBg-)
|
|
89
|
+
*
|
|
90
|
+
* @param {string|null} color - Color name (semantic)
|
|
91
|
+
* @returns {string|null} Class name or null if invalid
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* getBackgroundClass('primary'); // 'zBg-primary' (brand color)
|
|
95
|
+
* getBackgroundClass('light'); // 'zBg-light' (light gray)
|
|
96
|
+
* getBackgroundClass('transparent'); // 'zBg-transparent'
|
|
97
|
+
*/
|
|
98
|
+
export function getBackgroundClass(color) {
|
|
99
|
+
if (!color) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const normalized = color.toLowerCase();
|
|
104
|
+
|
|
105
|
+
if (VALID_BG_COLORS.includes(normalized)) {
|
|
106
|
+
return `zBg-${normalized}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//
|
|
113
|
+
// Text Color Functions
|
|
114
|
+
//
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generate text color class (zTheme: zText-)
|
|
118
|
+
*
|
|
119
|
+
* @param {string|null} color - Color name (semantic)
|
|
120
|
+
* @returns {string|null} Class name or null if invalid
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* getTextColorClass('primary'); // 'zText-primary' (brand color)
|
|
124
|
+
* getTextColorClass('muted'); // 'zText-muted' (subdued text)
|
|
125
|
+
* getTextColorClass('white'); // 'zText-white'
|
|
126
|
+
* getTextColorClass('black-50'); // 'zText-black-50' (50% opacity)
|
|
127
|
+
*/
|
|
128
|
+
export function getTextColorClass(color) {
|
|
129
|
+
if (!color) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const normalized = color.toLowerCase();
|
|
134
|
+
|
|
135
|
+
if (VALID_TEXT_COLORS.includes(normalized)) {
|
|
136
|
+
return `zText-${normalized}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//
|
|
143
|
+
// Border Color Functions
|
|
144
|
+
//
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Generate border color class (zTheme: zBorder-)
|
|
148
|
+
*
|
|
149
|
+
* Note: This sets the border COLOR only, not the border itself.
|
|
150
|
+
* You still need to add the `zBorder` class to show the border.
|
|
151
|
+
*
|
|
152
|
+
* @param {string|null} color - Color name (semantic)
|
|
153
|
+
* @returns {string|null} Class name or null if invalid
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* // Must combine with zBorder to show border:
|
|
157
|
+
* element.className = 'zBorder ' + getBorderColorClass('primary');
|
|
158
|
+
* // Result: 'zBorder zBorder-primary'
|
|
159
|
+
*
|
|
160
|
+
* getBorderColorClass('primary'); // 'zBorder-primary'
|
|
161
|
+
* getBorderColorClass('danger'); // 'zBorder-danger'
|
|
162
|
+
*/
|
|
163
|
+
export function getBorderColorClass(color) {
|
|
164
|
+
if (!color) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const normalized = color.toLowerCase();
|
|
169
|
+
|
|
170
|
+
if (VALID_BORDER_COLORS.includes(normalized)) {
|
|
171
|
+
return `zBorder-${normalized}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
//
|
|
178
|
+
// Apply Color Scheme Function
|
|
179
|
+
//
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Apply color scheme to an element via config object
|
|
183
|
+
*
|
|
184
|
+
* This is a higher-level function for applying multiple color concerns at once.
|
|
185
|
+
*
|
|
186
|
+
* @param {HTMLElement} element - Element to apply colors to
|
|
187
|
+
* @param {Object} config - Color configuration
|
|
188
|
+
* @param {string} [config.background] - Background color
|
|
189
|
+
* @param {string} [config.text] - Text color
|
|
190
|
+
* @param {string} [config.border] - Border color (requires zBorder class to be visible)
|
|
191
|
+
* @returns {HTMLElement} Element with color classes applied
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* // Create a primary-colored card
|
|
195
|
+
* applyColorScheme(element, {
|
|
196
|
+
* background: 'primary',
|
|
197
|
+
* text: 'white',
|
|
198
|
+
* border: 'primary'
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* // Create a light info box
|
|
203
|
+
* applyColorScheme(element, {
|
|
204
|
+
* background: 'light',
|
|
205
|
+
* text: 'dark'
|
|
206
|
+
* });
|
|
207
|
+
*/
|
|
208
|
+
export function applyColorScheme(element, config = {}) {
|
|
209
|
+
const classes = [];
|
|
210
|
+
|
|
211
|
+
// Apply background color
|
|
212
|
+
if (config.background) {
|
|
213
|
+
const bgClass = getBackgroundClass(config.background);
|
|
214
|
+
if (bgClass) {
|
|
215
|
+
classes.push(bgClass);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Apply text color
|
|
220
|
+
if (config.text) {
|
|
221
|
+
const textClass = getTextColorClass(config.text);
|
|
222
|
+
if (textClass) {
|
|
223
|
+
classes.push(textClass);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Apply border color
|
|
228
|
+
if (config.border) {
|
|
229
|
+
const borderClass = getBorderColorClass(config.border);
|
|
230
|
+
if (borderClass) {
|
|
231
|
+
classes.push(borderClass);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Apply all classes at once
|
|
236
|
+
if (classes.length > 0) {
|
|
237
|
+
element.classList.add(...classes);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return element;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
//
|
|
244
|
+
// Helper: Get All Available Colors
|
|
245
|
+
//
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get lists of all available colors (useful for debugging/documentation)
|
|
249
|
+
*
|
|
250
|
+
* @returns {Object} Object with arrays of valid colors for each category
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* const colors = getAvailableColors();
|
|
254
|
+
* this.logger.log(colors.backgrounds); // ['primary', 'secondary', ...]
|
|
255
|
+
*/
|
|
256
|
+
export function getAvailableColors() {
|
|
257
|
+
return {
|
|
258
|
+
backgrounds: [...VALID_BG_COLORS],
|
|
259
|
+
text: [...VALID_TEXT_COLORS],
|
|
260
|
+
borders: [...VALID_BORDER_COLORS]
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
//
|
|
265
|
+
// Default Export (for convenience)
|
|
266
|
+
//
|
|
267
|
+
export default {
|
|
268
|
+
getBackgroundClass,
|
|
269
|
+
getTextColorClass,
|
|
270
|
+
getBorderColorClass,
|
|
271
|
+
applyColorScheme,
|
|
272
|
+
getAvailableColors
|
|
273
|
+
};
|
|
274
|
+
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ═══════════════════════════════════════════════════════════════
|
|
3
|
+
* Dark Mode Utilities - Theme Management (Layer 2)
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* Pure utility functions for managing dark/light theme modes.
|
|
7
|
+
* Extracted from duplicate implementations in hooks.js and bifrost_client.js.
|
|
8
|
+
*
|
|
9
|
+
* @module utils/dark_mode_utils
|
|
10
|
+
* @layer 2 (Utilities - Pure functions)
|
|
11
|
+
*
|
|
12
|
+
* Dependencies: None (Layer 0 - Browser APIs only)
|
|
13
|
+
*
|
|
14
|
+
* Exports:
|
|
15
|
+
* - applyDarkModeClasses(isDark, options): Apply/remove dark mode classes
|
|
16
|
+
* - toggleDarkMode(currentIsDark): Toggle between dark/light and persist
|
|
17
|
+
* - getDarkModeFromStorage(): Get saved theme preference
|
|
18
|
+
* - saveDarkModeToStorage(isDark): Persist theme preference
|
|
19
|
+
*
|
|
20
|
+
* Example:
|
|
21
|
+
* ```javascript
|
|
22
|
+
* import { applyDarkModeClasses, toggleDarkMode } from '../../zSys/theme/dark_mode_utils.js';
|
|
23
|
+
* applyDarkModeClasses(true); // Enable dark mode
|
|
24
|
+
* const newTheme = toggleDarkMode(false); // Returns 'dark'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────
|
|
29
|
+
// Constants
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────
|
|
31
|
+
const STORAGE_KEY = 'zTheme-mode';
|
|
32
|
+
const THEME_DARK = 'dark';
|
|
33
|
+
const THEME_LIGHT = 'light';
|
|
34
|
+
const COLOR_WHITE = '#ffffff';
|
|
35
|
+
|
|
36
|
+
const SELECTORS = {
|
|
37
|
+
NAVBAR: '.zNavbar',
|
|
38
|
+
NAVBAR_TOGGLER: '.zNavbar-toggler',
|
|
39
|
+
THEME_TOGGLE: '.zTheme-toggle',
|
|
40
|
+
BOOTSTRAP_ICON: 'i.bi',
|
|
41
|
+
CARD: '.zCard',
|
|
42
|
+
HEADERS: 'h1, h2, h3, h4, h5, h6',
|
|
43
|
+
PARAGRAPHS: 'p',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const CLASSES = {
|
|
47
|
+
BG_DARK: 'zBg-dark',
|
|
48
|
+
TEXT_LIGHT: 'zText-light',
|
|
49
|
+
NAVBAR_LIGHT: 'zNavbar-light',
|
|
50
|
+
NAVBAR_DARK: 'zNavbar-dark',
|
|
51
|
+
NAVBAR_TOGGLER_DARK: 'zNavbar-toggler-dark',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ─────────────────────────────────────────────────────────────────
|
|
55
|
+
// Storage Functions
|
|
56
|
+
// ─────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get dark mode preference from localStorage
|
|
60
|
+
* @returns {boolean} True if dark mode is enabled
|
|
61
|
+
*/
|
|
62
|
+
export function getDarkModeFromStorage() {
|
|
63
|
+
return localStorage.getItem(STORAGE_KEY) === THEME_DARK;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Save dark mode preference to localStorage
|
|
68
|
+
* @param {boolean} isDark - Whether dark mode is enabled
|
|
69
|
+
*/
|
|
70
|
+
export function saveDarkModeToStorage(isDark) {
|
|
71
|
+
localStorage.setItem(STORAGE_KEY, isDark ? THEME_DARK : THEME_LIGHT);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────
|
|
75
|
+
// Theme Application Functions
|
|
76
|
+
// ─────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Apply or remove dark mode classes to page elements
|
|
80
|
+
* @param {boolean} isDark - Whether to apply dark mode
|
|
81
|
+
* @param {Object} options - Configuration options
|
|
82
|
+
* @param {HTMLElement} [options.contentArea] - Content area element (defaults to body)
|
|
83
|
+
* @param {Function} [options.logger] - Optional logger for debugging
|
|
84
|
+
* @returns {void}
|
|
85
|
+
*/
|
|
86
|
+
export function applyDarkModeClasses(isDark, options = {}) {
|
|
87
|
+
const { contentArea = document.body, logger = null } = options;
|
|
88
|
+
|
|
89
|
+
const body = document.body;
|
|
90
|
+
const navbars = document.querySelectorAll(SELECTORS.NAVBAR);
|
|
91
|
+
const togglers = document.querySelectorAll(SELECTORS.NAVBAR_TOGGLER);
|
|
92
|
+
|
|
93
|
+
if (logger && logger.log) {
|
|
94
|
+
logger.log(`[DarkMode] Applying ${isDark ? 'DARK' : 'LIGHT'} mode`);
|
|
95
|
+
logger.log(`[DarkMode] Found ${navbars.length} navbar(s), ${togglers.length} toggler(s)`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (isDark) {
|
|
99
|
+
// Apply dark mode
|
|
100
|
+
_applyDarkMode(body, navbars, togglers, contentArea, logger);
|
|
101
|
+
} else {
|
|
102
|
+
// Remove dark mode
|
|
103
|
+
_removeDarkMode(body, navbars, togglers, contentArea, logger);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Apply dark mode classes and styles
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
function _applyDarkMode(body, navbars, togglers, contentArea, logger) {
|
|
112
|
+
// Apply dark background to body
|
|
113
|
+
body.classList.add(CLASSES.BG_DARK);
|
|
114
|
+
body.style.backgroundColor = 'var(--color-dark)';
|
|
115
|
+
|
|
116
|
+
// Apply white text to headers/paragraphs outside cards
|
|
117
|
+
_applyLightTextToElements(contentArea, logger);
|
|
118
|
+
|
|
119
|
+
// Update navbars
|
|
120
|
+
navbars.forEach(nav => {
|
|
121
|
+
nav.classList.remove(CLASSES.NAVBAR_LIGHT);
|
|
122
|
+
nav.classList.add(CLASSES.NAVBAR_DARK);
|
|
123
|
+
if (logger && logger.log) {
|
|
124
|
+
logger.log('[DarkMode] Navbar classes:', nav.className);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Update navbar togglers (hamburger icons)
|
|
129
|
+
togglers.forEach((toggler, idx) => {
|
|
130
|
+
toggler.classList.add(CLASSES.NAVBAR_TOGGLER_DARK);
|
|
131
|
+
const icon = toggler.querySelector(SELECTORS.BOOTSTRAP_ICON);
|
|
132
|
+
if (icon) {
|
|
133
|
+
icon.style.color = COLOR_WHITE;
|
|
134
|
+
}
|
|
135
|
+
if (logger && logger.log) {
|
|
136
|
+
const computedColor = icon ? window.getComputedStyle(icon).color : 'not found';
|
|
137
|
+
logger.log(`[DarkMode] Toggler ${idx} classes:`, toggler.className);
|
|
138
|
+
logger.log(`[DarkMode] Toggler ${idx} icon color:`, computedColor);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Update theme toggle button icon
|
|
143
|
+
const themeToggleBtn = document.querySelector(SELECTORS.THEME_TOGGLE);
|
|
144
|
+
if (themeToggleBtn) {
|
|
145
|
+
const icon = themeToggleBtn.querySelector(SELECTORS.BOOTSTRAP_ICON);
|
|
146
|
+
if (icon) {
|
|
147
|
+
icon.style.color = COLOR_WHITE;
|
|
148
|
+
if (logger && logger.log) {
|
|
149
|
+
logger.log('[DarkMode] Theme toggle icon color set to white');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} else if (logger && logger.log) {
|
|
153
|
+
logger.log('[DarkMode] Theme toggle button not found yet');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Remove dark mode classes and styles
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
function _removeDarkMode(body, navbars, togglers, contentArea, logger) {
|
|
162
|
+
// Remove dark background from body
|
|
163
|
+
body.classList.remove(CLASSES.BG_DARK);
|
|
164
|
+
body.style.backgroundColor = '';
|
|
165
|
+
body.style.color = '';
|
|
166
|
+
|
|
167
|
+
// Remove white text from elements
|
|
168
|
+
_removeLightTextFromElements(contentArea, logger);
|
|
169
|
+
|
|
170
|
+
// Update navbars
|
|
171
|
+
navbars.forEach(nav => {
|
|
172
|
+
nav.classList.remove(CLASSES.NAVBAR_DARK);
|
|
173
|
+
nav.classList.add(CLASSES.NAVBAR_LIGHT);
|
|
174
|
+
if (logger && logger.log) {
|
|
175
|
+
logger.log('[DarkMode] Navbar classes:', nav.className);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Update navbar togglers
|
|
180
|
+
togglers.forEach((toggler, idx) => {
|
|
181
|
+
toggler.classList.remove(CLASSES.NAVBAR_TOGGLER_DARK);
|
|
182
|
+
const icon = toggler.querySelector(SELECTORS.BOOTSTRAP_ICON);
|
|
183
|
+
if (icon) {
|
|
184
|
+
icon.style.color = '';
|
|
185
|
+
}
|
|
186
|
+
if (logger && logger.log) {
|
|
187
|
+
const computedColor = icon ? window.getComputedStyle(icon).color : 'not found';
|
|
188
|
+
logger.log(`[DarkMode] Toggler ${idx} classes:`, toggler.className);
|
|
189
|
+
logger.log(`[DarkMode] Toggler ${idx} icon color:`, computedColor);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Clear theme toggle button icon color
|
|
194
|
+
const themeToggleBtn = document.querySelector(SELECTORS.THEME_TOGGLE);
|
|
195
|
+
if (themeToggleBtn) {
|
|
196
|
+
const icon = themeToggleBtn.querySelector(SELECTORS.BOOTSTRAP_ICON);
|
|
197
|
+
if (icon) {
|
|
198
|
+
icon.style.color = '';
|
|
199
|
+
if (logger && logger.log) {
|
|
200
|
+
logger.log('[DarkMode] Theme toggle icon color cleared');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Apply light text to headers and paragraphs outside cards
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
function _applyLightTextToElements(contentArea, logger) {
|
|
211
|
+
if (!contentArea) return;
|
|
212
|
+
|
|
213
|
+
// Apply white text to headers outside cards
|
|
214
|
+
contentArea.querySelectorAll(SELECTORS.HEADERS).forEach(header => {
|
|
215
|
+
if (!header.closest(SELECTORS.CARD)) {
|
|
216
|
+
header.classList.add(CLASSES.TEXT_LIGHT);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Apply white text to paragraphs outside cards
|
|
221
|
+
contentArea.querySelectorAll(SELECTORS.PARAGRAPHS).forEach(p => {
|
|
222
|
+
if (!p.closest(SELECTORS.CARD)) {
|
|
223
|
+
p.classList.add(CLASSES.TEXT_LIGHT);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Remove light text from all elements
|
|
230
|
+
* @private
|
|
231
|
+
*/
|
|
232
|
+
function _removeLightTextFromElements(contentArea, logger) {
|
|
233
|
+
if (!contentArea) return;
|
|
234
|
+
|
|
235
|
+
contentArea.querySelectorAll(`.${CLASSES.TEXT_LIGHT}`).forEach(el => {
|
|
236
|
+
el.classList.remove(CLASSES.TEXT_LIGHT);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ─────────────────────────────────────────────────────────────────
|
|
241
|
+
// Toggle Function
|
|
242
|
+
// ─────────────────────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Toggle between dark and light mode
|
|
246
|
+
* @param {boolean} currentIsDark - Current dark mode state
|
|
247
|
+
* @param {Object} options - Configuration options (passed to applyDarkModeClasses)
|
|
248
|
+
* @returns {string} New theme ('dark' or 'light')
|
|
249
|
+
*/
|
|
250
|
+
export function toggleDarkMode(currentIsDark, options = {}) {
|
|
251
|
+
const newIsDark = !currentIsDark;
|
|
252
|
+
saveDarkModeToStorage(newIsDark);
|
|
253
|
+
applyDarkModeClasses(newIsDark, options);
|
|
254
|
+
return newIsDark ? THEME_DARK : THEME_LIGHT;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ─────────────────────────────────────────────────────────────────
|
|
258
|
+
// Initialization
|
|
259
|
+
// ─────────────────────────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Initialize dark mode from localStorage on page load
|
|
263
|
+
* @param {Object} options - Configuration options (passed to applyDarkModeClasses)
|
|
264
|
+
* @returns {boolean} Whether dark mode is active
|
|
265
|
+
*/
|
|
266
|
+
export function initializeDarkMode(options = {}) {
|
|
267
|
+
const isDark = getDarkModeFromStorage();
|
|
268
|
+
if (isDark) {
|
|
269
|
+
applyDarkModeClasses(true, options);
|
|
270
|
+
}
|
|
271
|
+
return isDark;
|
|
272
|
+
}
|