@shohojdhara/atomix 0.3.5 → 0.3.7
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/README.md +101 -199
- package/atomix.config.ts +241 -0
- package/dist/atomix.css +260 -179
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +250 -179
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +69 -166
- package/dist/charts.js.map +1 -1
- package/dist/core.js +184 -263
- package/dist/core.js.map +1 -1
- package/dist/forms.js +55 -131
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +184 -263
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +1831 -1657
- package/dist/index.esm.js +4497 -4318
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +4510 -4328
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +1431 -1472
- package/dist/theme.js +4175 -4138
- package/dist/theme.js.map +1 -1
- package/package.json +6 -20
- package/src/components/Accordion/Accordion.stories.tsx +50 -17
- package/src/components/AtomixGlass/AtomixGlass.tsx +128 -322
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +12 -5
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
- package/src/components/Avatar/Avatar.stories.tsx +7 -0
- package/src/components/Badge/Badge.stories.tsx +91 -13
- package/src/components/Block/Block.stories.tsx +7 -23
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
- package/src/components/Button/Button.stories.tsx +141 -22
- package/src/components/Button/Button.tsx +85 -167
- package/src/components/Button/ButtonGroup.stories.tsx +315 -0
- package/src/components/Button/ButtonGroup.tsx +67 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Callout/Callout.stories.tsx +8 -6
- package/src/components/Card/Card.stories.tsx +82 -28
- package/src/components/Chart/AnimatedChart.tsx +0 -1
- package/src/components/Chart/AreaChart.tsx +0 -1
- package/src/components/Chart/BarChart.tsx +0 -1
- package/src/components/Chart/BubbleChart.tsx +0 -1
- package/src/components/Chart/CandlestickChart.tsx +0 -1
- package/src/components/Chart/Chart.stories.tsx +5 -7
- package/src/components/Chart/Chart.tsx +0 -16
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +0 -1
- package/src/components/Chart/FunnelChart.tsx +0 -1
- package/src/components/Chart/GaugeChart.tsx +0 -1
- package/src/components/Chart/HeatmapChart.tsx +0 -1
- package/src/components/Chart/LineChart.tsx +0 -1
- package/src/components/Chart/MultiAxisChart.tsx +0 -1
- package/src/components/Chart/PieChart.tsx +0 -1
- package/src/components/Chart/RadarChart.tsx +0 -1
- package/src/components/Chart/ScatterChart.tsx +0 -1
- package/src/components/Chart/WaterfallChart.tsx +0 -1
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
- package/src/components/DataTable/DataTable.stories.tsx +23 -16
- package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
- package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
- package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
- package/src/components/Footer/Footer.stories.tsx +8 -6
- package/src/components/Footer/FooterLink.tsx +9 -2
- package/src/components/Form/Checkbox.stories.tsx +7 -0
- package/src/components/Form/Form.stories.tsx +7 -0
- package/src/components/Form/FormGroup.stories.tsx +9 -1
- package/src/components/Form/Input.stories.tsx +69 -16
- package/src/components/Form/Radio.stories.tsx +9 -1
- package/src/components/Form/Select.stories.tsx +9 -1
- package/src/components/Form/Textarea.stories.tsx +10 -2
- package/src/components/Hero/Hero.stories.tsx +7 -0
- package/src/components/List/List.stories.tsx +7 -0
- package/src/components/Messages/Messages.stories.tsx +8 -7
- package/src/components/Modal/Modal.stories.tsx +17 -6
- package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
- package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +188 -111
- package/src/components/Pagination/Pagination.tsx +83 -3
- package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
- package/src/components/Popover/Popover.stories.tsx +191 -115
- package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
- package/src/components/Progress/Progress.stories.tsx +79 -49
- package/src/components/Rating/Rating.stories.tsx +109 -84
- package/src/components/River/River.stories.tsx +194 -114
- package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
- package/src/components/Slider/Slider.stories.tsx +7 -0
- package/src/components/Spinner/Spinner.stories.tsx +15 -11
- package/src/components/Steps/Steps.stories.tsx +132 -98
- package/src/components/Tabs/Tabs.stories.tsx +163 -112
- package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
- package/src/components/Todo/Todo.stories.tsx +38 -12
- package/src/components/Toggle/Toggle.stories.tsx +61 -28
- package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
- package/src/components/Upload/Upload.stories.tsx +122 -84
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
- package/src/components/index.ts +1 -0
- package/src/lib/composables/useAtomixGlass.ts +9 -10
- package/src/lib/composables/useNavbar.ts +0 -10
- package/src/lib/config/loader.ts +4 -4
- package/src/lib/constants/components.ts +17 -0
- package/src/lib/hooks/useComponentCustomization.ts +1 -1
- package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
- package/src/lib/hooks/useThemeTokens.ts +105 -0
- package/src/lib/theme/README.md +174 -0
- package/src/lib/theme/adapters/index.ts +31 -0
- package/src/lib/theme/adapters/themeAdapter.ts +287 -0
- package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
- package/src/lib/theme/config/configLoader.ts +95 -0
- package/src/lib/theme/config/loader.ts +37 -54
- package/src/lib/theme/config/types.ts +2 -2
- package/src/lib/theme/config/validator.ts +15 -91
- package/src/lib/theme/{constants.ts → constants/constants.ts} +1 -19
- package/src/lib/theme/constants/index.ts +8 -0
- package/src/lib/theme/core/ThemeRegistry.ts +75 -266
- package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
- package/src/lib/theme/core/composeTheme.ts +105 -0
- package/src/lib/theme/core/createTheme.ts +108 -0
- package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +12 -8
- package/src/lib/theme/core/index.ts +19 -19
- package/src/lib/theme/devtools/Comparator.tsx +346 -22
- package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
- package/src/lib/theme/devtools/Inspector.tsx +335 -51
- package/src/lib/theme/devtools/LiveEditor.tsx +478 -107
- package/src/lib/theme/devtools/Preview.tsx +471 -221
- package/src/lib/theme/{core → devtools}/ThemeValidator.ts +1 -1
- package/src/lib/theme/devtools/index.ts +14 -4
- package/src/lib/theme/devtools/useHistory.ts +130 -0
- package/src/lib/theme/{errors.ts → errors/errors.ts} +1 -1
- package/src/lib/theme/errors/index.ts +12 -0
- package/src/lib/theme/generators/cssFile.ts +79 -0
- package/src/lib/theme/generators/generateCSS.ts +89 -0
- package/src/lib/theme/generators/generateCSSNested.ts +130 -0
- package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +3 -13
- package/src/lib/theme/generators/index.ts +25 -0
- package/src/lib/theme/i18n/rtl.ts +5 -6
- package/src/lib/theme/index.ts +149 -19
- package/src/lib/theme/runtime/ThemeApplicator.ts +53 -112
- package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +5 -5
- package/src/lib/theme/runtime/ThemeProvider.tsx +266 -282
- package/src/lib/theme/runtime/index.ts +2 -2
- package/src/lib/theme/runtime/useTheme.ts +1 -2
- package/src/lib/theme/runtime/useThemeTokens.ts +131 -0
- package/src/lib/theme/test/testTheme.ts +385 -0
- package/src/lib/theme/tokens/index.ts +12 -0
- package/src/lib/theme/tokens/tokens.ts +721 -0
- package/src/lib/theme/types.ts +6 -42
- package/src/lib/theme/utils/componentTheming.ts +132 -0
- package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
- package/src/lib/theme/utils/index.ts +11 -0
- package/src/lib/theme/utils/injectCSS.ts +90 -0
- package/src/lib/theme/utils/naming.ts +100 -0
- package/src/lib/theme/utils/themeHelpers.ts +78 -0
- package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +7 -7
- package/src/lib/theme-tools.ts +7 -8
- package/src/lib/types/components.ts +40 -130
- package/src/lib/utils/componentUtils.ts +2 -2
- package/src/lib/utils/memoryMonitor.ts +3 -3
- package/src/lib/utils/themeNaming.ts +135 -0
- package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
- package/src/styles/02-tools/_tools.button.scss +66 -79
- package/src/styles/06-components/_components.atomix-glass.scss +13 -3
- package/src/styles/06-components/_components.pagination.scss +88 -0
- package/scripts/sync-theme-config.js +0 -309
- package/src/lib/theme/composeTheme.ts +0 -370
- package/src/lib/theme/core/ThemeCache.ts +0 -283
- package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
- package/src/lib/theme/core/ThemeEngine.ts +0 -665
- package/src/lib/theme/createThemeFromConfig.ts +0 -132
- package/src/lib/theme/devtools/CLI.ts +0 -364
- package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
- package/src/lib/theme/runtime/ThemeManager.ts +0 -446
- package/src/styles/03-generic/_generated-root.css +0 -26
- package/src/themes/README.md +0 -442
- package/src/themes/themes.config.js +0 -68
- /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
|
@@ -1,318 +1,302 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme Provider
|
|
3
3
|
*
|
|
4
|
-
* React context provider for theme management
|
|
5
|
-
* Updated to use the new
|
|
4
|
+
* React context provider for theme management with separated concerns
|
|
5
|
+
* Updated to use the new simplified theme system
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import { isJSTheme } from '../themeUtils';
|
|
9
|
+
import { ThemeContext } from './ThemeContext';
|
|
10
|
+
import type { ThemeProviderProps, Theme, ThemeLoadOptions } from '../types';
|
|
11
|
+
import { isJSTheme } from '../utils/themeUtils';
|
|
13
12
|
import { getLogger } from '../errors';
|
|
13
|
+
import { createTheme } from '../core';
|
|
14
|
+
import { injectCSS, removeCSS } from '../utils/injectCSS';
|
|
15
|
+
import {
|
|
16
|
+
isServer,
|
|
17
|
+
createLocalStorageAdapter,
|
|
18
|
+
applyThemeAttributes,
|
|
19
|
+
buildThemePath,
|
|
20
|
+
} from '../utils/domUtils';
|
|
21
|
+
import {
|
|
22
|
+
DEFAULT_STORAGE_KEY,
|
|
23
|
+
DEFAULT_DATA_ATTRIBUTE,
|
|
24
|
+
DEFAULT_BASE_PATH,
|
|
25
|
+
} from '../constants/constants';
|
|
14
26
|
|
|
15
27
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* function App() {
|
|
26
|
-
* return (
|
|
27
|
-
* <ThemeProvider>
|
|
28
|
-
* <YourApp />
|
|
29
|
-
* </ThemeProvider>
|
|
30
|
-
* );
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
28
|
+
* Theme Provider
|
|
29
|
+
*
|
|
30
|
+
* React context provider for theme management with separated concerns.
|
|
31
|
+
* Simplified version focusing on core functionality:
|
|
32
|
+
* - String-based themes (CSS files)
|
|
33
|
+
* - JS Theme objects
|
|
34
|
+
* - Persistence via localStorage
|
|
35
|
+
*
|
|
36
|
+
* Falls back to 'default' theme if no configuration is found.
|
|
33
37
|
*/
|
|
34
38
|
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
onThemeChange,
|
|
47
|
-
onError,
|
|
39
|
+
children,
|
|
40
|
+
defaultTheme,
|
|
41
|
+
themes = {},
|
|
42
|
+
basePath = DEFAULT_BASE_PATH,
|
|
43
|
+
cdnPath = null,
|
|
44
|
+
useMinified = false,
|
|
45
|
+
storageKey = DEFAULT_STORAGE_KEY,
|
|
46
|
+
dataAttribute = DEFAULT_DATA_ATTRIBUTE,
|
|
47
|
+
enablePersistence = true,
|
|
48
|
+
onThemeChange,
|
|
49
|
+
onError,
|
|
48
50
|
}) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
// Store callbacks in refs to avoid recreating when they change
|
|
52
|
+
const onThemeChangeRef = useRef(onThemeChange);
|
|
53
|
+
const onErrorRef = useRef(onError);
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
// Update ref when callback changes
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
onThemeChangeRef.current = onThemeChange;
|
|
58
|
+
onErrorRef.current = onError;
|
|
59
|
+
}, [onThemeChange, onError]);
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// Create stable wrapper functions that read from ref
|
|
62
|
+
const handleThemeChange = useCallback((theme: string | Theme) => {
|
|
63
|
+
onThemeChangeRef.current?.(theme);
|
|
64
|
+
}, []);
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
const handleError = useCallback((error: Error, themeName: string) => {
|
|
67
|
+
onErrorRef.current?.(error, themeName);
|
|
68
|
+
}, []);
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const themesStable = useMemo(() => {
|
|
71
|
-
// Only update if themes object actually changed (shallow comparison)
|
|
72
|
-
const currentKeys = Object.keys(themes);
|
|
73
|
-
const prevKeys = Object.keys(themesRef.current);
|
|
74
|
-
|
|
75
|
-
if (currentKeys.length !== prevKeys.length) {
|
|
76
|
-
themesRef.current = themes;
|
|
77
|
-
return themes;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const hasChanged = currentKeys.some(key => themes[key] !== themesRef.current[key]);
|
|
81
|
-
if (hasChanged) {
|
|
82
|
-
themesRef.current = themes;
|
|
83
|
-
return themes;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return themesRef.current;
|
|
87
|
-
}, [themes]);
|
|
70
|
+
// Initialize storage adapter
|
|
71
|
+
const storageAdapter = useMemo(() => createLocalStorageAdapter(), []);
|
|
88
72
|
|
|
89
|
-
|
|
73
|
+
// Get initial default theme
|
|
74
|
+
const initialDefaultTheme = useMemo(() => {
|
|
75
|
+
// Check storage first
|
|
76
|
+
if (enablePersistence && storageAdapter.isAvailable()) {
|
|
77
|
+
const stored = storageAdapter.getItem(storageKey);
|
|
78
|
+
if (stored) {
|
|
79
|
+
return stored;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
90
82
|
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
themes: themesStable,
|
|
96
|
-
defaultTheme,
|
|
97
|
-
basePath,
|
|
98
|
-
cdnPath,
|
|
99
|
-
preload,
|
|
100
|
-
lazy,
|
|
101
|
-
storageKey,
|
|
102
|
-
dataAttribute,
|
|
103
|
-
enablePersistence,
|
|
104
|
-
useMinified,
|
|
105
|
-
onThemeChange: handleThemeChange,
|
|
106
|
-
onError: handleError,
|
|
107
|
-
});
|
|
108
|
-
} catch (error) {
|
|
109
|
-
logger.error(
|
|
110
|
-
'Failed to create ThemeManager',
|
|
111
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
112
|
-
{ themes: Object.keys(themesStable), defaultTheme }
|
|
113
|
-
);
|
|
114
|
-
// Return a minimal manager that won't crash
|
|
115
|
-
return new ThemeManager({
|
|
116
|
-
themes: {},
|
|
117
|
-
defaultTheme,
|
|
118
|
-
basePath,
|
|
119
|
-
storageKey,
|
|
120
|
-
enablePersistence: false,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}, [
|
|
124
|
-
themesStable,
|
|
125
|
-
defaultTheme,
|
|
126
|
-
basePath,
|
|
127
|
-
cdnPath,
|
|
128
|
-
preload,
|
|
129
|
-
lazy,
|
|
130
|
-
storageKey,
|
|
131
|
-
dataAttribute,
|
|
132
|
-
enablePersistence,
|
|
133
|
-
useMinified,
|
|
134
|
-
handleThemeChange,
|
|
135
|
-
handleError,
|
|
136
|
-
logger,
|
|
137
|
-
]);
|
|
83
|
+
// If defaultTheme is provided, use it
|
|
84
|
+
if (defaultTheme !== undefined && defaultTheme !== null) {
|
|
85
|
+
return defaultTheme;
|
|
86
|
+
}
|
|
138
87
|
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return defaultTheme.name || 'js-theme';
|
|
146
|
-
}
|
|
147
|
-
return ''; // No default theme - use built-in styles
|
|
148
|
-
});
|
|
88
|
+
// Try to load from atomix.config.ts as fallback, but only in Node.js/SSR environments
|
|
89
|
+
if (typeof window === 'undefined') {
|
|
90
|
+
try {
|
|
91
|
+
// Dynamically import the config loader to avoid bundling issues in browser
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
93
|
+
const { loadThemeFromConfigSync } = require('../config/configLoader');
|
|
149
94
|
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
|
|
95
|
+
const configTokens = loadThemeFromConfigSync();
|
|
96
|
+
if (configTokens && Object.keys(configTokens).length > 0) {
|
|
97
|
+
// For simplicity, we'll treat config tokens as a special theme name
|
|
98
|
+
return 'config-theme';
|
|
153
99
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return themeManager.getAvailableThemes();
|
|
159
|
-
});
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.warn('Failed to load theme from config, using default');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
160
104
|
|
|
161
|
-
|
|
162
|
-
|
|
105
|
+
// Default fallback
|
|
106
|
+
return 'default';
|
|
107
|
+
}, [defaultTheme, enablePersistence, storageKey]);
|
|
163
108
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
109
|
+
// State for current theme
|
|
110
|
+
const [currentTheme, setCurrentTheme] = useState<string | Theme>(() => initialDefaultTheme);
|
|
111
|
+
const [activeTheme, setActiveTheme] = useState<Theme | null>(null);
|
|
112
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
113
|
+
const [error, setError] = useState<Error | null>(null);
|
|
168
114
|
|
|
169
|
-
|
|
170
|
-
|
|
115
|
+
// Track loaded themes
|
|
116
|
+
const loadedThemesRef = useRef<Set<string>>(new Set());
|
|
117
|
+
const themePromisesRef = useRef<Record<string, Promise<void>>>({});
|
|
171
118
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
119
|
+
// Apply initial theme attributes to document element
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!isServer()) {
|
|
122
|
+
applyThemeAttributes(String(currentTheme), dataAttribute);
|
|
123
|
+
}
|
|
124
|
+
}, [currentTheme, dataAttribute]);
|
|
175
125
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (current === prev) return prev;
|
|
183
|
-
return current;
|
|
184
|
-
});
|
|
185
|
-
setActiveTheme(prev => {
|
|
186
|
-
const current = themeManager.getActiveTheme();
|
|
187
|
-
// Prevent unnecessary updates by comparing references
|
|
188
|
-
if (current === prev) return prev;
|
|
189
|
-
if (!current && !prev) return prev;
|
|
190
|
-
return current;
|
|
191
|
-
});
|
|
192
|
-
setAvailableThemes(prev => {
|
|
193
|
-
const current = themeManager.getAvailableThemes();
|
|
194
|
-
// Only update if actually different
|
|
195
|
-
if (current.length !== prev.length) return current;
|
|
196
|
-
// Compare by name since ThemeMetadata doesn't have id
|
|
197
|
-
const hasChanged = current.some((t, i) => t.name !== prev[i]?.name || t.class !== prev[i]?.class);
|
|
198
|
-
return hasChanged ? current : prev;
|
|
199
|
-
});
|
|
200
|
-
};
|
|
126
|
+
// Handle theme persistence
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (enablePersistence && storageAdapter.isAvailable()) {
|
|
129
|
+
storageAdapter.setItem(storageKey, String(currentTheme));
|
|
130
|
+
}
|
|
131
|
+
}, [currentTheme, storageKey, enablePersistence]);
|
|
201
132
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
133
|
+
// Function to set theme with proper type handling
|
|
134
|
+
const setTheme = useCallback(async (
|
|
135
|
+
theme: string | Theme | import('../tokens').DesignTokens | Partial<import('../tokens').DesignTokens>,
|
|
136
|
+
options?: ThemeLoadOptions
|
|
137
|
+
) => {
|
|
138
|
+
setIsLoading(true);
|
|
139
|
+
setError(null);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
let themeName: string;
|
|
143
|
+
let themeObj: Theme | null = null;
|
|
144
|
+
|
|
145
|
+
if (typeof theme === 'string') {
|
|
146
|
+
themeName = theme;
|
|
147
|
+
} else {
|
|
148
|
+
// If it's a Theme object or DesignTokens, we need to process it
|
|
149
|
+
if (isJSTheme(theme)) {
|
|
150
|
+
themeObj = theme as Theme;
|
|
151
|
+
// For JS themes, we use a generic name
|
|
152
|
+
themeName = 'js-theme';
|
|
153
|
+
setActiveTheme(themeObj);
|
|
154
|
+
} else {
|
|
155
|
+
// For DesignTokens, we might create a theme from tokens
|
|
156
|
+
themeName = 'tokens-theme';
|
|
157
|
+
// Create theme from tokens if needed
|
|
158
|
+
}
|
|
159
|
+
}
|
|
216
160
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
161
|
+
// If it's a string theme name, load the associated CSS
|
|
162
|
+
if (typeof theme === 'string' && themes[theme]) {
|
|
163
|
+
// Check if theme is already loading
|
|
164
|
+
if (themePromisesRef.current[theme]) {
|
|
165
|
+
await themePromisesRef.current[theme];
|
|
166
|
+
setCurrentTheme(theme);
|
|
167
|
+
setActiveTheme(null);
|
|
168
|
+
handleThemeChange(theme);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
222
171
|
|
|
223
|
-
//
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
172
|
+
// Load CSS theme
|
|
173
|
+
const themeLoadPromise = new Promise<void>(async (resolve, reject) => {
|
|
174
|
+
try {
|
|
175
|
+
const themeMetadata = themes[theme];
|
|
176
|
+
|
|
177
|
+
if (themeMetadata) {
|
|
178
|
+
// Build CSS path using utility function
|
|
179
|
+
const cssPath = buildThemePath(
|
|
180
|
+
theme,
|
|
181
|
+
basePath,
|
|
182
|
+
useMinified,
|
|
183
|
+
cdnPath
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Remove any previously loaded theme CSS
|
|
187
|
+
removeCSS(`theme-${String(currentTheme)}`);
|
|
188
|
+
|
|
189
|
+
// Inject new theme CSS
|
|
190
|
+
await injectCSS(cssPath, `theme-${theme}`);
|
|
191
|
+
loadedThemesRef.current.add(theme);
|
|
192
|
+
|
|
193
|
+
setCurrentTheme(theme);
|
|
194
|
+
setActiveTheme(null);
|
|
195
|
+
handleThemeChange(theme);
|
|
196
|
+
resolve();
|
|
197
|
+
} else {
|
|
198
|
+
throw new Error(`Theme metadata not found for theme: ${theme}`);
|
|
199
|
+
}
|
|
200
|
+
} catch (err) {
|
|
201
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
202
|
+
setError(error);
|
|
203
|
+
handleError(error, String(theme));
|
|
204
|
+
reject(error);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
227
207
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
208
|
+
themePromisesRef.current[theme] = themeLoadPromise;
|
|
209
|
+
await themeLoadPromise;
|
|
210
|
+
} else if (themeObj) {
|
|
211
|
+
// For JS themes, set them directly
|
|
212
|
+
setCurrentTheme(themeName);
|
|
213
|
+
setActiveTheme(themeObj);
|
|
214
|
+
handleThemeChange(themeObj);
|
|
215
|
+
} else {
|
|
216
|
+
// For string theme that isn't in our themes record, just set the name
|
|
217
|
+
setCurrentTheme(themeName);
|
|
218
|
+
setActiveTheme(null);
|
|
219
|
+
handleThemeChange(themeName);
|
|
220
|
+
}
|
|
221
|
+
} catch (err) {
|
|
222
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
223
|
+
setError(error);
|
|
224
|
+
handleError(error, String(theme));
|
|
225
|
+
} finally {
|
|
226
|
+
setIsLoading(false);
|
|
227
|
+
}
|
|
228
|
+
}, [themes, currentTheme, handleThemeChange, handleError, basePath, useMinified, cdnPath]);
|
|
248
229
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
230
|
+
// Check if theme is loaded
|
|
231
|
+
const isThemeLoaded = useCallback((themeName: string) => {
|
|
232
|
+
return loadedThemesRef.current.has(themeName);
|
|
233
|
+
}, []);
|
|
253
234
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
};
|
|
260
|
-
}, [themeManager]);
|
|
235
|
+
// Preload theme function
|
|
236
|
+
const preloadTheme = useCallback(async (themeName: string) => {
|
|
237
|
+
if (!themes[themeName] || isThemeLoaded(themeName)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
261
240
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
241
|
+
setIsLoading(true);
|
|
242
|
+
try {
|
|
243
|
+
// Build CSS path using utility function
|
|
244
|
+
const cssPath = buildThemePath(
|
|
245
|
+
themeName,
|
|
246
|
+
basePath,
|
|
247
|
+
useMinified,
|
|
248
|
+
cdnPath
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Preload CSS by fetching it
|
|
252
|
+
await fetch(cssPath);
|
|
253
|
+
loadedThemesRef.current.add(themeName);
|
|
254
|
+
} catch (err) {
|
|
255
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
256
|
+
setError(error);
|
|
257
|
+
handleError(error, themeName);
|
|
258
|
+
} finally {
|
|
259
|
+
setIsLoading(false);
|
|
260
|
+
}
|
|
261
|
+
}, [themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath]);
|
|
268
262
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
await themeManager.setTheme(theme, options);
|
|
278
|
-
} catch (err) {
|
|
279
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
280
|
-
setError(error);
|
|
281
|
-
throw err;
|
|
282
|
-
} finally {
|
|
283
|
-
setIsLoading(false);
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
availableThemes,
|
|
287
|
-
isLoading,
|
|
288
|
-
error,
|
|
289
|
-
isThemeLoaded: (themeName: string) => themeManager.isThemeLoaded(themeName),
|
|
290
|
-
preloadTheme: async (themeName: string) => {
|
|
291
|
-
setIsLoading(true);
|
|
292
|
-
try {
|
|
293
|
-
await themeManager.preloadTheme(themeName);
|
|
294
|
-
} catch (err) {
|
|
295
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
296
|
-
setError(error);
|
|
297
|
-
} finally {
|
|
298
|
-
setIsLoading(false);
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
themeManager: themeManager,
|
|
302
|
-
}), [
|
|
303
|
-
currentTheme,
|
|
304
|
-
activeTheme,
|
|
305
|
-
availableThemes,
|
|
306
|
-
isLoading,
|
|
307
|
-
error,
|
|
308
|
-
themeManager,
|
|
309
|
-
]);
|
|
263
|
+
// Create a mock theme manager instance for the context
|
|
264
|
+
const themeManager = useMemo(() => {
|
|
265
|
+
// This would normally be a real ThemeManager instance
|
|
266
|
+
// For now, we'll create a mock implementation that satisfies the type
|
|
267
|
+
return {
|
|
268
|
+
// Mock implementation - in a real app this would be a full ThemeManager
|
|
269
|
+
} ;
|
|
270
|
+
}, []);
|
|
310
271
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
272
|
+
// Theme context value
|
|
273
|
+
const contextValue = useMemo(() => ({
|
|
274
|
+
theme: typeof currentTheme === 'string' ? currentTheme : 'js-theme',
|
|
275
|
+
activeTheme,
|
|
276
|
+
setTheme,
|
|
277
|
+
availableThemes: Object.entries(themes).map(([name, metadata]) => ({
|
|
278
|
+
...metadata
|
|
279
|
+
})),
|
|
280
|
+
isLoading,
|
|
281
|
+
error,
|
|
282
|
+
isThemeLoaded,
|
|
283
|
+
preloadTheme,
|
|
284
|
+
themeManager,
|
|
285
|
+
}), [
|
|
286
|
+
currentTheme,
|
|
287
|
+
activeTheme,
|
|
288
|
+
setTheme,
|
|
289
|
+
themes,
|
|
290
|
+
isLoading,
|
|
291
|
+
error,
|
|
292
|
+
isThemeLoaded,
|
|
293
|
+
preloadTheme,
|
|
294
|
+
themeManager
|
|
295
|
+
]);
|
|
317
296
|
|
|
318
|
-
|
|
297
|
+
return (
|
|
298
|
+
<ThemeContext.Provider value={contextValue}>
|
|
299
|
+
{children}
|
|
300
|
+
</ThemeContext.Provider>
|
|
301
|
+
);
|
|
302
|
+
};
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* Runtime components for theme management
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export { ThemeManager } from './ThemeManager';
|
|
8
7
|
export { ThemeProvider } from './ThemeProvider';
|
|
9
8
|
export { ThemeErrorBoundary } from './ThemeErrorBoundary';
|
|
10
9
|
export { useTheme } from './useTheme';
|
|
10
|
+
export { useThemeTokens } from './useThemeTokens';
|
|
11
|
+
export { ThemeApplicator, getThemeApplicator, applyTheme } from './ThemeApplicator';
|
|
11
12
|
export type { ThemeErrorBoundaryProps } from './ThemeErrorBoundary';
|
|
12
13
|
|
|
13
14
|
export type {
|
|
14
|
-
ThemeManagerConfig,
|
|
15
15
|
ThemeChangeEvent,
|
|
16
16
|
ThemeLoadOptions,
|
|
17
17
|
} from '../types';
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* useTheme Hook
|
|
3
3
|
*
|
|
4
4
|
* React hook for accessing theme context
|
|
5
|
-
* Updated to work with new ThemeEngine architecture
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import { useContext } from 'react';
|
|
9
|
-
import { ThemeContext } from '
|
|
8
|
+
import { ThemeContext } from './ThemeContext';
|
|
10
9
|
import type { UseThemeReturn } from '../types';
|
|
11
10
|
|
|
12
11
|
/**
|