@shohojdhara/atomix 0.3.6 → 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 +3 -3
- package/dist/charts.js +50 -142
- package/dist/charts.js.map +1 -1
- package/dist/core.js +179 -274
- package/dist/core.js.map +1 -1
- package/dist/forms.js +50 -142
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +179 -274
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +669 -703
- package/dist/index.esm.js +966 -1649
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1211 -1890
- 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 +163 -334
- package/dist/theme.js +774 -1473
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
- package/src/components/Button/Button.tsx +85 -167
- package/src/lib/composables/useAtomixGlass.ts +7 -7
- package/src/lib/config/loader.ts +2 -3
- package/src/lib/constants/components.ts +7 -0
- package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
- package/src/lib/hooks/useThemeTokens.ts +105 -0
- package/src/lib/theme/config/configLoader.ts +60 -219
- package/src/lib/theme/config/loader.ts +15 -21
- package/src/lib/theme/constants/constants.ts +1 -1
- package/src/lib/theme/core/ThemeRegistry.ts +75 -279
- package/src/lib/theme/core/composeTheme.ts +14 -64
- package/src/lib/theme/core/createTheme.ts +54 -40
- package/src/lib/theme/core/createThemeObject.ts +2 -2
- package/src/lib/theme/core/index.ts +15 -1
- package/src/lib/theme/errors/errors.ts +1 -1
- package/src/lib/theme/generators/generateCSSNested.ts +130 -0
- package/src/lib/theme/generators/index.ts +6 -0
- package/src/lib/theme/index.ts +35 -10
- package/src/lib/theme/runtime/ThemeApplicator.ts +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +4 -4
- package/src/lib/theme/runtime/ThemeProvider.tsx +261 -554
- package/src/lib/theme/runtime/index.ts +1 -0
- package/src/lib/theme/runtime/useThemeTokens.ts +131 -0
- package/src/lib/theme/utils/componentTheming.ts +132 -0
- package/src/lib/theme/utils/naming.ts +100 -0
- package/src/lib/theme/utils/themeUtils.ts +6 -6
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/lib/utils/memoryMonitor.ts +3 -3
- package/src/lib/utils/themeNaming.ts +135 -0
package/dist/theme.js
CHANGED
|
@@ -849,13 +849,6 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
849
849
|
* @param coefficient - Amount to emphasize (0-1), default 0.15
|
|
850
850
|
* @returns Emphasized hex color
|
|
851
851
|
*/
|
|
852
|
-
/**
|
|
853
|
-
* Check if a theme is a JS theme (created with createTheme)
|
|
854
|
-
*/
|
|
855
|
-
function isJSTheme(theme) {
|
|
856
|
-
return theme && "object" == typeof theme && !0 === theme.__isJSTheme;
|
|
857
|
-
}
|
|
858
|
-
|
|
859
852
|
/**
|
|
860
853
|
* Theme Adapter
|
|
861
854
|
*
|
|
@@ -875,7 +868,8 @@ function isJSTheme(theme) {
|
|
|
875
868
|
* const tokens = themeToDesignTokens(theme);
|
|
876
869
|
* // Returns: { 'primary': '#7c3aed', ... }
|
|
877
870
|
* ```
|
|
878
|
-
*/
|
|
871
|
+
*/
|
|
872
|
+
function themeToDesignTokens(theme) {
|
|
879
873
|
const tokens = {};
|
|
880
874
|
// Convert palette colors
|
|
881
875
|
if (theme.palette) {
|
|
@@ -1015,158 +1009,10 @@ function isJSTheme(theme) {
|
|
|
1015
1009
|
};
|
|
1016
1010
|
}
|
|
1017
1011
|
|
|
1018
|
-
/**
|
|
1019
|
-
* Config Loader
|
|
1020
|
-
*
|
|
1021
|
-
* Load design tokens from atomix.config.ts and convert to flat token format.
|
|
1022
|
-
*/
|
|
1023
|
-
/**
|
|
1024
|
-
* Convert nested config tokens to flat DesignTokens format
|
|
1025
|
-
*
|
|
1026
|
-
* Handles conversion from atomix.config.ts structure to flat tokens.
|
|
1027
|
-
* Maps config structure to actual DesignTokens key names.
|
|
1028
|
-
*/ function flattenConfigTokens(tokens, prefix = "atomix") {
|
|
1029
|
-
const flat = {};
|
|
1030
|
-
// Colors
|
|
1031
|
-
return tokens.colors && Object.entries(tokens.colors).forEach((([key, value]) => {
|
|
1032
|
-
var _context;
|
|
1033
|
-
// Simple color: 'primary': '#7AFFD7'
|
|
1034
|
-
// Map directly to DesignTokens keys (e.g., 'primary', 'secondary', 'error')
|
|
1035
|
-
// Only map if it's a valid semantic color key
|
|
1036
|
-
if ("string" == typeof value) _includesInstanceProperty(_context = [ "primary", "secondary", "success", "info", "warning", "error", "light", "dark" ]).call(_context, key) && (flat[key] = value); else if (value && "object" == typeof value)
|
|
1037
|
-
// Color scale or palette
|
|
1038
|
-
if ("main" in value) {
|
|
1039
|
-
var _context2;
|
|
1040
|
-
// PaletteColorOptions: { main: '#7AFFD7', light?: '#...', dark?: '#...' }
|
|
1041
|
-
const palette = value, baseKey = key;
|
|
1042
|
-
// Map main to base color (e.g., primary, secondary, error)
|
|
1043
|
-
_includesInstanceProperty(_context2 = [ "primary", "secondary", "success", "info", "warning", "error", "light", "dark" ]).call(_context2, key) && (flat[baseKey] = palette.main,
|
|
1044
|
-
// Map light/dark to appropriate scale values
|
|
1045
|
-
// light typically maps to step 3, dark to step 9
|
|
1046
|
-
palette.light && (flat[`${key}-3`] = palette.light), palette.dark && (flat[`${key}-9`] = palette.dark));
|
|
1047
|
-
} else
|
|
1048
|
-
// Color scale: { 1: '#fff', 2: '#eee', ..., 6: '#main', ... }
|
|
1049
|
-
// Or full scale: { 1: '#...', 2: '#...', ..., 10: '#...' }
|
|
1050
|
-
Object.entries(value).forEach((([scaleKey, scaleValue]) => {
|
|
1051
|
-
if ("string" == typeof scaleValue)
|
|
1052
|
-
// Map scale keys to DesignTokens format
|
|
1053
|
-
if ("main" === scaleKey || "6" === scaleKey) {
|
|
1054
|
-
var _context3, _context4;
|
|
1055
|
-
// Main color maps to base key (e.g., 'primary')
|
|
1056
|
-
_includesInstanceProperty(_context3 = [ "primary", "secondary", "success", "info", "warning", "error", "light", "dark" ]).call(_context3, key) && (flat[key] = scaleValue),
|
|
1057
|
-
// Also map to step 6 if it's a scale color
|
|
1058
|
-
_includesInstanceProperty(_context4 = [ "primary", "red", "green", "blue", "yellow", "gray" ]).call(_context4, key) && (flat[`${key}-6`] = scaleValue);
|
|
1059
|
-
} else {
|
|
1060
|
-
// Map scale numbers (1-10) to DesignTokens format
|
|
1061
|
-
const scaleNum = parseInt(scaleKey, 10);
|
|
1062
|
-
!isNaN(scaleNum) && scaleNum >= 1 && scaleNum <= 10 && (flat[`${key}-${scaleKey}`] = scaleValue);
|
|
1063
|
-
}
|
|
1064
|
-
}));
|
|
1065
|
-
})),
|
|
1066
|
-
// Spacing
|
|
1067
|
-
tokens.spacing && Object.entries(tokens.spacing).forEach((([key, value]) => {
|
|
1068
|
-
flat[`spacing-${key}`] = String(value);
|
|
1069
|
-
})),
|
|
1070
|
-
// Border Radius
|
|
1071
|
-
tokens.borderRadius && Object.entries(tokens.borderRadius).forEach((([key, value]) => {
|
|
1072
|
-
// Map to DesignTokens format
|
|
1073
|
-
"sm" === key || "base" === key || "" === key ? flat["border-radius-sm"] = String(value) : "md" === key || "default" === key ? flat["border-radius"] = String(value) : "lg" === key ? flat["border-radius-lg"] = String(value) : flat[`border-radius-${key}`] = String(value);
|
|
1074
|
-
})),
|
|
1075
|
-
// Typography
|
|
1076
|
-
tokens.typography && (
|
|
1077
|
-
// Font Families
|
|
1078
|
-
tokens.typography.fontFamilies && Object.entries(tokens.typography.fontFamilies).forEach((([key, value]) => {
|
|
1079
|
-
// Map to DesignTokens format
|
|
1080
|
-
"sans" === key || "base" === key ? (flat["font-sans-serif"] = String(value), flat["body-font-family"] = String(value)) : "mono" === key ? flat["font-monospace"] = String(value) : flat[`font-family-${key}`] = String(value);
|
|
1081
|
-
})),
|
|
1082
|
-
// Font Sizes
|
|
1083
|
-
tokens.typography.fontSizes && Object.entries(tokens.typography.fontSizes).forEach((([key, value]) => {
|
|
1084
|
-
flat[`font-size-${key}`] = String(value);
|
|
1085
|
-
})),
|
|
1086
|
-
// Font Weights
|
|
1087
|
-
tokens.typography.fontWeights && Object.entries(tokens.typography.fontWeights).forEach((([key, value]) => {
|
|
1088
|
-
flat[`font-weight-${key}`] = String(value);
|
|
1089
|
-
})),
|
|
1090
|
-
// Line Heights
|
|
1091
|
-
tokens.typography.lineHeights && Object.entries(tokens.typography.lineHeights).forEach((([key, value]) => {
|
|
1092
|
-
"base" === key || "default" === key ? flat["line-height-base"] = String(value) : flat[`line-height-${key}`] = String(value);
|
|
1093
|
-
}))),
|
|
1094
|
-
// Shadows
|
|
1095
|
-
tokens.shadows && Object.entries(tokens.shadows).forEach((([key, value]) => {
|
|
1096
|
-
flat[`shadow-${key}`] = String(value);
|
|
1097
|
-
})),
|
|
1098
|
-
// Z-Index
|
|
1099
|
-
tokens.zIndex && Object.entries(tokens.zIndex).forEach((([key, value]) => {
|
|
1100
|
-
flat[`z-index-${key}`] = String(value);
|
|
1101
|
-
})),
|
|
1102
|
-
// Transitions
|
|
1103
|
-
tokens.transitions && tokens.transitions.durations && Object.entries(tokens.transitions.durations).forEach((([key, value]) => {
|
|
1104
|
-
flat[`transition-${key}`] = String(value);
|
|
1105
|
-
})), flat;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
/**
|
|
1109
|
-
* Load theme tokens from atomix.config.ts
|
|
1110
|
-
*
|
|
1111
|
-
* Loads atomix.config.ts and extracts theme tokens.
|
|
1112
|
-
* Config file is required - throws error if not found.
|
|
1113
|
-
*
|
|
1114
|
-
* @param configPath - Optional custom config path (default: 'atomix.config.ts')
|
|
1115
|
-
* @returns Partial DesignTokens from config
|
|
1116
|
-
* @throws Error if config file is not found or cannot be loaded
|
|
1117
|
-
*
|
|
1118
|
-
* @example
|
|
1119
|
-
* ```typescript
|
|
1120
|
-
* const tokens = await loadThemeFromConfig();
|
|
1121
|
-
* const css = createTheme(tokens);
|
|
1122
|
-
* injectTheme(css);
|
|
1123
|
-
* ```
|
|
1124
|
-
*/ async function loadThemeFromConfig(configPath = "atomix.config.ts") {
|
|
1125
|
-
// In browser environments, config loading is not supported
|
|
1126
|
-
if ("undefined" != typeof window) throw new Error("loadThemeFromConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.");
|
|
1127
|
-
// Load config using the existing loader (required)
|
|
1128
|
-
const {loadAtomixConfig: loadAtomixConfig} = await Promise.resolve().then((() => loader)), config = loadAtomixConfig({
|
|
1129
|
-
configPath: configPath,
|
|
1130
|
-
required: !0
|
|
1131
|
-
});
|
|
1132
|
-
if (!config || !config.theme) throw new Error(`Config file ${configPath} does not contain theme configuration.`);
|
|
1133
|
-
// Extract tokens from config
|
|
1134
|
-
const tokens = config.theme.tokens || config.theme.extend || {};
|
|
1135
|
-
if (0 === Object.keys(tokens).length) throw new Error(`Config file ${configPath} has empty theme configuration.`);
|
|
1136
|
-
// Convert nested structure to flat tokens
|
|
1137
|
-
return flattenConfigTokens(tokens, config.prefix || "atomix");
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
/**
|
|
1141
|
-
* Load theme tokens from atomix.config.ts (synchronous version)
|
|
1142
|
-
*
|
|
1143
|
-
* Synchronous version that uses require() instead of dynamic import.
|
|
1144
|
-
* Only works in Node.js environment.
|
|
1145
|
-
* Config file is required - throws error if not found.
|
|
1146
|
-
*
|
|
1147
|
-
* @param configPath - Optional custom config path
|
|
1148
|
-
* @returns Partial DesignTokens from config
|
|
1149
|
-
* @throws Error if config file is not found or cannot be loaded
|
|
1150
|
-
*/ function loadThemeFromConfigSync(configPath = "atomix.config.ts") {
|
|
1151
|
-
// In browser environments, config loading is not supported
|
|
1152
|
-
if ("undefined" != typeof window) throw new Error("loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.");
|
|
1153
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1154
|
-
const {loadAtomixConfig: loadAtomixConfig} = require("../../config/loader"), config = loadAtomixConfig({
|
|
1155
|
-
configPath: configPath,
|
|
1156
|
-
required: !0
|
|
1157
|
-
});
|
|
1158
|
-
if (!config || !config.theme) throw new Error(`Config file ${configPath} does not contain theme configuration.`);
|
|
1159
|
-
// Extract tokens from config
|
|
1160
|
-
const tokens = config.theme.tokens || config.theme.extend || {};
|
|
1161
|
-
if (0 === Object.keys(tokens).length) throw new Error(`Config file ${configPath} has empty theme configuration.`);
|
|
1162
|
-
// Convert nested structure to flat tokens
|
|
1163
|
-
return flattenConfigTokens(tokens, config.prefix || "atomix");
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
1012
|
/**
|
|
1167
1013
|
* Core Theme Functions
|
|
1168
1014
|
*
|
|
1169
|
-
*
|
|
1015
|
+
* Simplified theme system that handles both DesignTokens and Theme objects.
|
|
1170
1016
|
* Config-first approach: loads from atomix.config.ts when no input is provided.
|
|
1171
1017
|
* Config file is required for automatic loading.
|
|
1172
1018
|
*/
|
|
@@ -1200,29 +1046,53 @@ function isJSTheme(theme) {
|
|
|
1200
1046
|
* const css = createTheme(undefined, { prefix: 'myapp', selector: ':root' });
|
|
1201
1047
|
* ```
|
|
1202
1048
|
*/ function createTheme(input, options) {
|
|
1203
|
-
|
|
1204
|
-
|
|
1049
|
+
// Determine tokens based on input
|
|
1050
|
+
let tokens;
|
|
1205
1051
|
if (input)
|
|
1206
|
-
//
|
|
1207
|
-
tokens =
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1052
|
+
// Use DesignTokens directly
|
|
1053
|
+
tokens =
|
|
1054
|
+
// Helper functions to simplify main function
|
|
1055
|
+
function(input) {
|
|
1056
|
+
return !0 === input?.__isJSTheme || input?.palette && input?.typography;
|
|
1057
|
+
}(input) ? themeToDesignTokens(input) : input;
|
|
1058
|
+
// Merge with defaults and generate CSS
|
|
1059
|
+
else {
|
|
1060
|
+
// Check if we're in a browser environment
|
|
1061
|
+
if ("undefined" != typeof window) throw new Error("createTheme: No input provided and config loading is not available in browser environment. Please provide tokens explicitly or use Node.js/SSR environment.");
|
|
1062
|
+
// Load from config when no input provided
|
|
1063
|
+
// Using dynamic import in a way that's more compatible with bundlers
|
|
1064
|
+
let loadThemeFromConfigSync, loadAtomixConfig;
|
|
1065
|
+
try {
|
|
1066
|
+
// Use dynamic require but only in Node.js environments
|
|
1067
|
+
// This approach allows bundlers to properly handle external dependencies
|
|
1068
|
+
const configLoaderModule = require("../config/configLoader"), loaderModule = require("../../config/loader");
|
|
1069
|
+
// Get prefix from config if needed
|
|
1070
|
+
if (loadThemeFromConfigSync = configLoaderModule.loadThemeFromConfigSync, loadAtomixConfig = loaderModule.loadAtomixConfig,
|
|
1071
|
+
tokens = loadThemeFromConfigSync(), !options?.prefix) try {
|
|
1072
|
+
const config = loadAtomixConfig({
|
|
1073
|
+
configPath: "atomix.config.ts",
|
|
1074
|
+
required: !1
|
|
1075
|
+
});
|
|
1076
|
+
options = {
|
|
1077
|
+
...options,
|
|
1078
|
+
prefix: config?.prefix || "atomix"
|
|
1079
|
+
};
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
// If config loading fails, use default prefix
|
|
1082
|
+
options = {
|
|
1083
|
+
...options,
|
|
1084
|
+
prefix: "atomix"
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1217
1087
|
} catch (error) {
|
|
1218
|
-
|
|
1088
|
+
throw new Error("createTheme: No input provided and config loading is not available in this environment. Please provide tokens explicitly.");
|
|
1219
1089
|
}
|
|
1220
|
-
tokens = configTokens;
|
|
1221
1090
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1091
|
+
const allTokens = createTokens(tokens), prefix = options?.prefix ?? "atomix";
|
|
1092
|
+
// Get prefix from options or use default
|
|
1093
|
+
return generateCSSVariables$1(allTokens, {
|
|
1224
1094
|
...options,
|
|
1225
|
-
prefix:
|
|
1095
|
+
prefix: prefix
|
|
1226
1096
|
});
|
|
1227
1097
|
}
|
|
1228
1098
|
|
|
@@ -1283,7 +1153,7 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
1283
1153
|
/**
|
|
1284
1154
|
* Theme Composition Utilities
|
|
1285
1155
|
*
|
|
1286
|
-
*
|
|
1156
|
+
* Simplified utilities for composing, merging, and extending themes.
|
|
1287
1157
|
*/
|
|
1288
1158
|
// ============================================================================
|
|
1289
1159
|
// Deep Merge Utility
|
|
@@ -1349,60 +1219,14 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
1349
1219
|
* });
|
|
1350
1220
|
* ```
|
|
1351
1221
|
*/ function extendTheme(baseTheme, extension) {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
*/
|
|
1355
|
-
var theme;
|
|
1356
|
-
// ============================================================================
|
|
1357
|
-
// Default Theme Values
|
|
1358
|
-
// ============================================================================
|
|
1359
|
-
return createThemeObject(mergeTheme(baseTheme.__isJSTheme ? {
|
|
1360
|
-
name: (theme = baseTheme).name,
|
|
1361
|
-
class: theme.class,
|
|
1362
|
-
description: theme.description,
|
|
1363
|
-
author: theme.author,
|
|
1364
|
-
version: theme.version,
|
|
1365
|
-
tags: theme.tags,
|
|
1366
|
-
supportsDarkMode: theme.supportsDarkMode,
|
|
1367
|
-
status: theme.status,
|
|
1368
|
-
a11y: theme.a11y,
|
|
1369
|
-
color: theme.color,
|
|
1370
|
-
features: theme.features,
|
|
1371
|
-
dependencies: theme.dependencies,
|
|
1372
|
-
palette: {
|
|
1373
|
-
primary: theme.palette.primary,
|
|
1374
|
-
secondary: theme.palette.secondary,
|
|
1375
|
-
error: theme.palette.error,
|
|
1376
|
-
warning: theme.palette.warning,
|
|
1377
|
-
info: theme.palette.info,
|
|
1378
|
-
success: theme.palette.success,
|
|
1379
|
-
background: theme.palette.background,
|
|
1380
|
-
text: theme.palette.text
|
|
1381
|
-
},
|
|
1382
|
-
typography: {
|
|
1383
|
-
fontFamily: theme.typography.fontFamily,
|
|
1384
|
-
fontSize: theme.typography.fontSize,
|
|
1385
|
-
fontWeightLight: theme.typography.fontWeightLight,
|
|
1386
|
-
fontWeightRegular: theme.typography.fontWeightRegular,
|
|
1387
|
-
fontWeightMedium: theme.typography.fontWeightMedium,
|
|
1388
|
-
fontWeightSemiBold: theme.typography.fontWeightSemiBold,
|
|
1389
|
-
fontWeightBold: theme.typography.fontWeightBold,
|
|
1390
|
-
h1: theme.typography.h1,
|
|
1391
|
-
h2: theme.typography.h2,
|
|
1392
|
-
h3: theme.typography.h3,
|
|
1393
|
-
h4: theme.typography.h4,
|
|
1394
|
-
h5: theme.typography.h5,
|
|
1395
|
-
h6: theme.typography.h6,
|
|
1396
|
-
body1: theme.typography.body1,
|
|
1397
|
-
body2: theme.typography.body2
|
|
1398
|
-
},
|
|
1399
|
-
shadows: theme.shadows,
|
|
1400
|
-
transitions: theme.transitions,
|
|
1401
|
-
zIndex: theme.zIndex,
|
|
1402
|
-
custom: theme.custom
|
|
1222
|
+
return createThemeObject(mergeTheme(baseTheme.__isJSTheme ? {
|
|
1223
|
+
...baseTheme
|
|
1403
1224
|
} : baseTheme, extension));
|
|
1404
1225
|
}
|
|
1405
1226
|
|
|
1227
|
+
// ============================================================================
|
|
1228
|
+
// Default Theme Values
|
|
1229
|
+
// ============================================================================
|
|
1406
1230
|
const DEFAULT_PALETTE = {
|
|
1407
1231
|
primary: {
|
|
1408
1232
|
main: "#7c3aed",
|
|
@@ -1664,7 +1488,10 @@ function createThemeObject(...options) {
|
|
|
1664
1488
|
"number" == typeof spacingInput ? (...values) => 0 === values.length ? "0px" : values.map((value => value * spacingInput + "px")).join(" ") :
|
|
1665
1489
|
// If it's an array, use it as a scale
|
|
1666
1490
|
Array.isArray(spacingInput) ? (...values) => 0 === values.length ? "0px" : values.map((value => `${spacingInput[value] || value}px`)).join(" ") : (...values) => 0 === values.length ? "0px" : values.map((value => 4 * value + "px")).join(" ");
|
|
1667
|
-
}
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Check if a theme is a JS theme (created with createTheme)
|
|
1494
|
+
*/ (mergedOptions.spacing), breakpoints = function(breakpointsInput) {
|
|
1668
1495
|
const values = {
|
|
1669
1496
|
xs: 0,
|
|
1670
1497
|
sm: 576,
|
|
@@ -1724,736 +1551,113 @@ function createThemeObject(...options) {
|
|
|
1724
1551
|
}
|
|
1725
1552
|
|
|
1726
1553
|
/**
|
|
1727
|
-
*
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
*/
|
|
1731
|
-
/**
|
|
1732
|
-
* Validate theme configuration
|
|
1733
|
-
*
|
|
1734
|
-
* @param config - Configuration to validate
|
|
1735
|
-
* @returns Validation result with errors and warnings
|
|
1736
|
-
*/
|
|
1737
|
-
/**
|
|
1738
|
-
* Validate CSS theme
|
|
1739
|
-
*/
|
|
1740
|
-
function validateCSSTheme(themeId, theme) {
|
|
1741
|
-
const errors = [];
|
|
1742
|
-
// CSS themes don't require createTheme function
|
|
1743
|
-
// But can have optional cssPath
|
|
1744
|
-
return {
|
|
1745
|
-
valid: 0 === errors.length,
|
|
1746
|
-
errors: errors,
|
|
1747
|
-
warnings: []
|
|
1748
|
-
};
|
|
1554
|
+
* Create a new theme registry
|
|
1555
|
+
*/ function createThemeRegistry() {
|
|
1556
|
+
return {};
|
|
1749
1557
|
}
|
|
1750
1558
|
|
|
1751
1559
|
/**
|
|
1752
|
-
*
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
valid: 0 === errors.length,
|
|
1759
|
-
errors: errors,
|
|
1760
|
-
warnings: []
|
|
1761
|
-
};
|
|
1560
|
+
* Register a theme
|
|
1561
|
+
* @param registry - Theme registry object
|
|
1562
|
+
* @param id - Theme identifier
|
|
1563
|
+
* @param metadata - Theme metadata
|
|
1564
|
+
*/ function registerTheme(registry, id, metadata) {
|
|
1565
|
+
registry[id] = metadata;
|
|
1762
1566
|
}
|
|
1763
1567
|
|
|
1764
1568
|
/**
|
|
1765
|
-
*
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
* Provides custom error classes and logging utilities.
|
|
1772
|
-
*/
|
|
1773
|
-
/**
|
|
1774
|
-
* Theme error codes
|
|
1775
|
-
*/
|
|
1776
|
-
var ThemeErrorCode, LogLevel;
|
|
1777
|
-
|
|
1778
|
-
!function(ThemeErrorCode) {
|
|
1779
|
-
/** Theme not found in registry */
|
|
1780
|
-
ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
|
|
1781
|
-
/** Theme failed to load */
|
|
1782
|
-
ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
|
|
1783
|
-
/** Theme validation failed */
|
|
1784
|
-
ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
|
|
1785
|
-
/** Configuration loading failed */
|
|
1786
|
-
ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
|
|
1787
|
-
/** Configuration validation failed */
|
|
1788
|
-
ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
|
|
1789
|
-
/** Circular dependency detected */
|
|
1790
|
-
ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
|
|
1791
|
-
/** Missing dependency */
|
|
1792
|
-
ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
|
|
1793
|
-
/** Storage operation failed */
|
|
1794
|
-
ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
|
|
1795
|
-
/** Invalid theme name */
|
|
1796
|
-
ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
|
|
1797
|
-
/** CSS injection failed */
|
|
1798
|
-
ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
|
|
1799
|
-
/** Unknown error */
|
|
1800
|
-
ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
|
1801
|
-
}(ThemeErrorCode || (ThemeErrorCode = {}));
|
|
1802
|
-
|
|
1803
|
-
/**
|
|
1804
|
-
* Custom error class for theme-related errors
|
|
1805
|
-
*/
|
|
1806
|
-
class ThemeError extends Error {
|
|
1807
|
-
constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
|
|
1808
|
-
super(message), this.name = "ThemeError", this.code = code, this.context = context,
|
|
1809
|
-
this.timestamp = Date.now(),
|
|
1810
|
-
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
1811
|
-
Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
|
|
1812
|
-
}
|
|
1813
|
-
/**
|
|
1814
|
-
* Convert error to JSON for logging
|
|
1815
|
-
*/ toJSON() {
|
|
1816
|
-
return {
|
|
1817
|
-
name: this.name,
|
|
1818
|
-
message: this.message,
|
|
1819
|
-
code: this.code,
|
|
1820
|
-
context: this.context,
|
|
1821
|
-
timestamp: this.timestamp,
|
|
1822
|
-
stack: this.stack
|
|
1823
|
-
};
|
|
1824
|
-
}
|
|
1569
|
+
* Unregister a theme
|
|
1570
|
+
* @param registry - Theme registry object
|
|
1571
|
+
* @param id - Theme identifier
|
|
1572
|
+
*/ function unregisterTheme(registry, id) {
|
|
1573
|
+
const exists = id in registry;
|
|
1574
|
+
return delete registry[id], exists;
|
|
1825
1575
|
}
|
|
1826
1576
|
|
|
1827
1577
|
/**
|
|
1828
|
-
*
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
/**
|
|
1835
|
-
* Theme Logger
|
|
1836
|
-
*
|
|
1837
|
-
* Centralized logging for the theme system.
|
|
1838
|
-
* Replaces console statements with structured logging.
|
|
1839
|
-
*/
|
|
1840
|
-
class ThemeLogger {
|
|
1841
|
-
constructor(config = {}) {
|
|
1842
|
-
this.config = {
|
|
1843
|
-
level: config.level ?? ("production" === process.env.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
|
|
1844
|
-
enableConsole: config.enableConsole ?? !0,
|
|
1845
|
-
onError: config.onError,
|
|
1846
|
-
onWarn: config.onWarn,
|
|
1847
|
-
onInfo: config.onInfo,
|
|
1848
|
-
onDebug: config.onDebug
|
|
1849
|
-
};
|
|
1850
|
-
}
|
|
1851
|
-
/**
|
|
1852
|
-
* Log an error
|
|
1853
|
-
*/ error(message, error, context) {
|
|
1854
|
-
if (this.config.level < LogLevel.ERROR) return;
|
|
1855
|
-
const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
|
|
1856
|
-
this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
|
|
1857
|
-
error: errorObj,
|
|
1858
|
-
context: {
|
|
1859
|
-
...context,
|
|
1860
|
-
...themeError.context
|
|
1861
|
-
},
|
|
1862
|
-
code: themeError.code
|
|
1863
|
-
}), this.config.onError?.(themeError, context);
|
|
1864
|
-
}
|
|
1865
|
-
/**
|
|
1866
|
-
* Log a warning
|
|
1867
|
-
*/ warn(message, context) {
|
|
1868
|
-
this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
|
|
1869
|
-
this.config.onWarn?.(message, context));
|
|
1870
|
-
}
|
|
1871
|
-
/**
|
|
1872
|
-
* Log an info message
|
|
1873
|
-
*/ info(message, context) {
|
|
1874
|
-
this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
|
|
1875
|
-
this.config.onInfo?.(message, context));
|
|
1876
|
-
}
|
|
1877
|
-
/**
|
|
1878
|
-
* Log a debug message
|
|
1879
|
-
*/ debug(message, context) {
|
|
1880
|
-
this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
|
|
1881
|
-
}
|
|
1578
|
+
* Check if a theme is registered
|
|
1579
|
+
* @param registry - Theme registry object
|
|
1580
|
+
* @param id - Theme identifier
|
|
1581
|
+
*/ function hasTheme(registry, id) {
|
|
1582
|
+
return id in registry;
|
|
1882
1583
|
}
|
|
1883
1584
|
|
|
1884
1585
|
/**
|
|
1885
|
-
*
|
|
1886
|
-
|
|
1586
|
+
* Get theme metadata
|
|
1587
|
+
* @param registry - Theme registry object
|
|
1588
|
+
* @param id - Theme identifier
|
|
1589
|
+
*/ function getTheme(registry, id) {
|
|
1590
|
+
return registry[id];
|
|
1591
|
+
}
|
|
1887
1592
|
|
|
1888
1593
|
/**
|
|
1889
|
-
* Get
|
|
1890
|
-
|
|
1891
|
-
|
|
1594
|
+
* Get all registered theme metadata
|
|
1595
|
+
* @param registry - Theme registry object
|
|
1596
|
+
*/ function getAllThemes(registry) {
|
|
1597
|
+
return Object.values(registry);
|
|
1892
1598
|
}
|
|
1893
1599
|
|
|
1894
1600
|
/**
|
|
1895
|
-
*
|
|
1896
|
-
*
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
* Default storage key for theme persistence
|
|
1901
|
-
*/ const DEFAULT_ATOMIX_CONFIG_PATH = "atomix.config.ts";
|
|
1601
|
+
* Get all registered theme IDs
|
|
1602
|
+
* @param registry - Theme registry object
|
|
1603
|
+
*/ function getThemeIds(registry) {
|
|
1604
|
+
return Object.keys(registry);
|
|
1605
|
+
}
|
|
1902
1606
|
|
|
1903
1607
|
/**
|
|
1904
|
-
*
|
|
1905
|
-
|
|
1608
|
+
* Clear all registered themes
|
|
1609
|
+
* @param registry - Theme registry object
|
|
1610
|
+
*/ function clearThemes(registry) {
|
|
1611
|
+
Object.keys(registry).forEach((key => delete registry[key]));
|
|
1612
|
+
}
|
|
1906
1613
|
|
|
1907
1614
|
/**
|
|
1908
|
-
*
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
}, DEFAULT_INTEGRATION_CSS_VARIABLES = {
|
|
1914
|
-
colorMode: "--storybook-color-mode"
|
|
1915
|
-
}, DEFAULT_SASS_CONFIG = {
|
|
1916
|
-
style: "expanded",
|
|
1917
|
-
sourceMap: !0,
|
|
1918
|
-
loadPaths: [ "src" ]
|
|
1919
|
-
};
|
|
1615
|
+
* Get the number of registered themes
|
|
1616
|
+
* @param registry - Theme registry object
|
|
1617
|
+
*/ function getThemeCount(registry) {
|
|
1618
|
+
return Object.keys(registry).length;
|
|
1619
|
+
}
|
|
1920
1620
|
|
|
1921
1621
|
/**
|
|
1922
|
-
*
|
|
1923
|
-
*/
|
|
1924
|
-
/**
|
|
1925
|
-
* Theme Configuration Loader
|
|
1622
|
+
* CSS Injection Utilities
|
|
1926
1623
|
*
|
|
1927
|
-
*
|
|
1928
|
-
*/
|
|
1929
|
-
/**
|
|
1930
|
-
* Cache for loaded configuration
|
|
1624
|
+
* Inject CSS into HTML head via <style> element.
|
|
1931
1625
|
*/
|
|
1932
|
-
let cachedConfig = null;
|
|
1933
|
-
|
|
1934
1626
|
/**
|
|
1935
|
-
*
|
|
1936
|
-
*/
|
|
1627
|
+
* Check if running in browser environment
|
|
1628
|
+
*/ function isBrowser$1() {
|
|
1629
|
+
return "undefined" != typeof document;
|
|
1630
|
+
}
|
|
1937
1631
|
|
|
1938
1632
|
/**
|
|
1939
|
-
*
|
|
1633
|
+
* Inject CSS into HTML head via <style> element
|
|
1940
1634
|
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1635
|
+
* Creates or updates a style element in the document head.
|
|
1636
|
+
* If an element with the same ID exists, it will be updated.
|
|
1637
|
+
*
|
|
1638
|
+
* @param css - CSS string to inject
|
|
1639
|
+
* @param id - Style element ID (default: 'atomix-theme')
|
|
1943
1640
|
*
|
|
1944
1641
|
* @example
|
|
1945
1642
|
* ```typescript
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1643
|
+
* const css = ':root { --atomix-color-primary: #7AFFD7; }';
|
|
1644
|
+
* injectCSS(css);
|
|
1645
|
+
*
|
|
1646
|
+
* // With custom ID
|
|
1647
|
+
* injectCSS(css, 'my-custom-theme');
|
|
1948
1648
|
* ```
|
|
1949
|
-
*/
|
|
1649
|
+
*/ function injectCSS$1(css, id = "atomix-theme") {
|
|
1650
|
+
if (!isBrowser$1()) return void console.warn("injectCSS: Not in browser environment, CSS not injected");
|
|
1651
|
+
let styleElement = document.getElementById(id);
|
|
1652
|
+
styleElement || (styleElement = document.createElement("style"), styleElement.id = id,
|
|
1653
|
+
styleElement.setAttribute("data-atomix-theme", "true"), document.head.appendChild(styleElement)),
|
|
1654
|
+
styleElement.textContent = css;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1950
1657
|
/**
|
|
1951
|
-
*
|
|
1658
|
+
* Remove injected CSS from DOM
|
|
1952
1659
|
*
|
|
1953
|
-
*
|
|
1954
|
-
*/
|
|
1955
|
-
class ThemeRegistry {
|
|
1956
|
-
constructor() {
|
|
1957
|
-
this.entries = new Map, this.config = null, this.initialized = !1;
|
|
1958
|
-
}
|
|
1959
|
-
/**
|
|
1960
|
-
* Initialize registry from config
|
|
1961
|
-
*/ async initialize(config) {
|
|
1962
|
-
if (!this.initialized) {
|
|
1963
|
-
// Load config if not provided
|
|
1964
|
-
if (config) this.config = config; else try {
|
|
1965
|
-
this.config = function(options = {}) {
|
|
1966
|
-
const {configPath: configPath = DEFAULT_ATOMIX_CONFIG_PATH, validate: validate = !0, env: env = ("undefined" != typeof process && process.env && "production" === process.env.NODE_ENV ? "production" : "development")} = options;
|
|
1967
|
-
// Return cached config if available
|
|
1968
|
-
if (cachedConfig) return cachedConfig;
|
|
1969
|
-
// Try to load config dynamically
|
|
1970
|
-
let config;
|
|
1971
|
-
try {
|
|
1972
|
-
// In browser/Vite environment, we can't load config dynamically
|
|
1973
|
-
if ("undefined" != typeof window) throw new Error("Theme config loading not supported in browser environment");
|
|
1974
|
-
// In ESM environments, require might be undefined.
|
|
1975
|
-
let nodeRequire, configModule;
|
|
1976
|
-
try {
|
|
1977
|
-
nodeRequire = require;
|
|
1978
|
-
} catch {
|
|
1979
|
-
// require is not defined
|
|
1980
|
-
}
|
|
1981
|
-
if (!nodeRequire) throw new Error("Theme config loading not supported in this environment (require is undefined)");
|
|
1982
|
-
// Try require (Node.js/CommonJS)
|
|
1983
|
-
try {
|
|
1984
|
-
// Try relative path first
|
|
1985
|
-
try {
|
|
1986
|
-
configModule = nodeRequire("../../../../atomix.config");
|
|
1987
|
-
} catch {
|
|
1988
|
-
// If relative path fails, try to resolve from process.cwd()
|
|
1989
|
-
const path = nodeRequire("path"), fs = nodeRequire("fs");
|
|
1990
|
-
let configFilePath = path.resolve(process.cwd(), configPath);
|
|
1991
|
-
// If atomix.config.ts not found at the root, use the default path
|
|
1992
|
-
if (fs.existsSync(configFilePath) || configPath !== DEFAULT_ATOMIX_CONFIG_PATH || (configFilePath = path.resolve(process.cwd(), DEFAULT_ATOMIX_CONFIG_PATH)),
|
|
1993
|
-
!fs.existsSync(configFilePath)) throw new Error(`Config file not found: ${configFilePath}`);
|
|
1994
|
-
{
|
|
1995
|
-
const resolvedPath = nodeRequire.resolve(configFilePath);
|
|
1996
|
-
nodeRequire.cache && nodeRequire.cache[resolvedPath] && delete nodeRequire.cache[resolvedPath],
|
|
1997
|
-
configModule = nodeRequire(configFilePath);
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
} catch (requireError) {
|
|
2001
|
-
const errorMessage = requireError instanceof Error ? requireError.message : String(requireError);
|
|
2002
|
-
throw new ThemeError(`Cannot load config: ${errorMessage}`, ThemeErrorCode.CONFIG_LOAD_FAILED, {
|
|
2003
|
-
configPath: configPath,
|
|
2004
|
-
error: errorMessage
|
|
2005
|
-
});
|
|
2006
|
-
}
|
|
2007
|
-
const rawConfig = configModule.default || configModule, finalConfig =
|
|
2008
|
-
/**
|
|
2009
|
-
* Apply environment-specific overrides to configuration
|
|
2010
|
-
*
|
|
2011
|
-
* @param config - Base configuration
|
|
2012
|
-
* @param env - Environment name
|
|
2013
|
-
* @returns Configuration with environment overrides applied
|
|
2014
|
-
*/
|
|
2015
|
-
function(config, env) {
|
|
2016
|
-
const overridden = {
|
|
2017
|
-
...config
|
|
2018
|
-
};
|
|
2019
|
-
// Production overrides
|
|
2020
|
-
return "production" === env && overridden.runtime && (overridden.runtime = {
|
|
2021
|
-
...overridden.runtime,
|
|
2022
|
-
useMinified: !0,
|
|
2023
|
-
lazy: !0
|
|
2024
|
-
}),
|
|
2025
|
-
// Development overrides
|
|
2026
|
-
"development" === env && (overridden.runtime && (overridden.runtime = {
|
|
2027
|
-
...overridden.runtime,
|
|
2028
|
-
useMinified: !1,
|
|
2029
|
-
lazy: !1
|
|
2030
|
-
}), overridden.build && (overridden.build = {
|
|
2031
|
-
...overridden.build,
|
|
2032
|
-
sass: {
|
|
2033
|
-
...overridden.build.sass,
|
|
2034
|
-
sourceMap: !0
|
|
2035
|
-
}
|
|
2036
|
-
})),
|
|
2037
|
-
// Test overrides
|
|
2038
|
-
"test" === env && overridden.runtime && (overridden.runtime = {
|
|
2039
|
-
...overridden.runtime,
|
|
2040
|
-
enablePersistence: !1,
|
|
2041
|
-
preload: []
|
|
2042
|
-
}), overridden;
|
|
2043
|
-
}({
|
|
2044
|
-
themes: rawConfig.theme?.themes || {},
|
|
2045
|
-
build: rawConfig.build || {},
|
|
2046
|
-
runtime: rawConfig.runtime || {},
|
|
2047
|
-
integration: rawConfig.integration || {},
|
|
2048
|
-
dependencies: rawConfig.dependencies || {},
|
|
2049
|
-
validated: !1,
|
|
2050
|
-
// Will be set after validation
|
|
2051
|
-
// Store tokens for generator
|
|
2052
|
-
__tokens: rawConfig.theme?.tokens,
|
|
2053
|
-
__extend: rawConfig.theme?.extend
|
|
2054
|
-
}, env);
|
|
2055
|
-
// Process the AtomixConfig structure
|
|
2056
|
-
// Validate if requested
|
|
2057
|
-
let validationResult = null;
|
|
2058
|
-
validate && (validationResult = function(config) {
|
|
2059
|
-
const errors = [], warnings = [];
|
|
2060
|
-
// Validate top-level structure
|
|
2061
|
-
if (!config || "object" != typeof config) return errors.push("Configuration must be an object"),
|
|
2062
|
-
{
|
|
2063
|
-
valid: !1,
|
|
2064
|
-
errors: errors,
|
|
2065
|
-
warnings: warnings
|
|
2066
|
-
};
|
|
2067
|
-
// Validate themes
|
|
2068
|
-
if (config.themes && "object" == typeof config.themes) {
|
|
2069
|
-
const themeErrors =
|
|
2070
|
-
/**
|
|
2071
|
-
* Validate themes object
|
|
2072
|
-
*/
|
|
2073
|
-
function(themes) {
|
|
2074
|
-
const errors = [], warnings = [];
|
|
2075
|
-
0 === Object.keys(themes).length && warnings.push("No themes defined in configuration");
|
|
2076
|
-
for (const [themeId, theme] of Object.entries(themes))
|
|
2077
|
-
// Validate theme ID
|
|
2078
|
-
if (themeId && "string" == typeof themeId)
|
|
2079
|
-
// Validate theme object
|
|
2080
|
-
if (theme && "object" == typeof theme)
|
|
2081
|
-
// Validate theme type
|
|
2082
|
-
if (!theme.type || "css" !== theme.type && "js" !== theme.type) errors.push(`Theme "${themeId}" must have type "css" or "js"`); else {
|
|
2083
|
-
// Validate CSS theme
|
|
2084
|
-
if (
|
|
2085
|
-
// Validate required fields
|
|
2086
|
-
theme.name && "string" == typeof theme.name || errors.push(`Theme "${themeId}" must have a "name" string`),
|
|
2087
|
-
"css" === theme.type) {
|
|
2088
|
-
const cssErrors = validateCSSTheme();
|
|
2089
|
-
errors.push(...cssErrors.errors), warnings.push(...cssErrors.warnings);
|
|
2090
|
-
}
|
|
2091
|
-
// Validate JS theme
|
|
2092
|
-
if ("js" === theme.type) {
|
|
2093
|
-
const jsErrors = validateJSTheme(themeId, theme);
|
|
2094
|
-
errors.push(...jsErrors.errors), warnings.push(...jsErrors.warnings);
|
|
2095
|
-
}
|
|
2096
|
-
// Validate accessibility (only critical checks)
|
|
2097
|
-
theme.a11y?.contrastTarget && ("number" != typeof theme.a11y.contrastTarget || theme.a11y.contrastTarget < 1 || theme.a11y.contrastTarget > 21) && warnings.push(`Theme "${themeId}" has invalid contrast target: ${theme.a11y.contrastTarget}`);
|
|
2098
|
-
} else errors.push(`Theme "${themeId}" must be an object`); else errors.push(`Invalid theme ID: ${themeId}`);
|
|
2099
|
-
return {
|
|
2100
|
-
valid: 0 === errors.length,
|
|
2101
|
-
errors: errors,
|
|
2102
|
-
warnings: warnings
|
|
2103
|
-
};
|
|
2104
|
-
}(config.themes);
|
|
2105
|
-
errors.push(...themeErrors.errors), warnings.push(...themeErrors.warnings);
|
|
2106
|
-
}
|
|
2107
|
-
// Validate build config (only if provided)
|
|
2108
|
-
else errors.push('Configuration must have a "themes" object');
|
|
2109
|
-
if (config.build) {
|
|
2110
|
-
const buildErrors = function(build) {
|
|
2111
|
-
const errors = [];
|
|
2112
|
-
// Only validate structure if provided
|
|
2113
|
-
return build.output && "object" != typeof build.output && errors.push("Build output must be an object"),
|
|
2114
|
-
build.sass && "object" != typeof build.sass && errors.push("Build sass config must be an object"),
|
|
2115
|
-
{
|
|
2116
|
-
valid: 0 === errors.length,
|
|
2117
|
-
errors: errors,
|
|
2118
|
-
warnings: []
|
|
2119
|
-
};
|
|
2120
|
-
}
|
|
2121
|
-
/**
|
|
2122
|
-
* Validate runtime configuration
|
|
2123
|
-
*/ (config.build);
|
|
2124
|
-
errors.push(...buildErrors.errors), warnings.push(...buildErrors.warnings);
|
|
2125
|
-
}
|
|
2126
|
-
// Validate runtime config (only if provided)
|
|
2127
|
-
if (config.runtime) {
|
|
2128
|
-
const runtimeErrors = function(runtime) {
|
|
2129
|
-
const errors = [];
|
|
2130
|
-
return runtime.basePath && "string" != typeof runtime.basePath && errors.push("Runtime basePath must be a string"),
|
|
2131
|
-
runtime.defaultTheme && "string" != typeof runtime.defaultTheme && errors.push("Runtime defaultTheme must be a string"),
|
|
2132
|
-
runtime.preload && !Array.isArray(runtime.preload) && errors.push("Runtime preload must be an array"),
|
|
2133
|
-
runtime.storageKey && "string" != typeof runtime.storageKey && errors.push("Runtime storageKey must be a string"),
|
|
2134
|
-
{
|
|
2135
|
-
valid: 0 === errors.length,
|
|
2136
|
-
errors: errors,
|
|
2137
|
-
warnings: []
|
|
2138
|
-
};
|
|
2139
|
-
}
|
|
2140
|
-
/**
|
|
2141
|
-
* Validate integration configuration
|
|
2142
|
-
*/ (config.runtime);
|
|
2143
|
-
errors.push(...runtimeErrors.errors), warnings.push(...runtimeErrors.warnings);
|
|
2144
|
-
}
|
|
2145
|
-
// Validate integration config (only if provided)
|
|
2146
|
-
if (config.integration) {
|
|
2147
|
-
const integrationErrors = function(integration) {
|
|
2148
|
-
const errors = [];
|
|
2149
|
-
// Only validate structure if provided
|
|
2150
|
-
return integration.classNames && "object" != typeof integration.classNames && errors.push("Integration classNames must be an object"),
|
|
2151
|
-
{
|
|
2152
|
-
valid: 0 === errors.length,
|
|
2153
|
-
errors: errors,
|
|
2154
|
-
warnings: []
|
|
2155
|
-
};
|
|
2156
|
-
}
|
|
2157
|
-
/**
|
|
2158
|
-
* Validate dependencies
|
|
2159
|
-
*/ (config.integration);
|
|
2160
|
-
errors.push(...integrationErrors.errors), warnings.push(...integrationErrors.warnings);
|
|
2161
|
-
}
|
|
2162
|
-
// Validate dependencies
|
|
2163
|
-
if (config.dependencies) {
|
|
2164
|
-
const depErrors = function(dependencies, themes) {
|
|
2165
|
-
const errors = [], warnings = [];
|
|
2166
|
-
for (const [themeId, deps] of Object.entries(dependencies))
|
|
2167
|
-
// Check if theme exists
|
|
2168
|
-
if (themes[themeId])
|
|
2169
|
-
// Validate dependencies array
|
|
2170
|
-
if (Array.isArray(deps))
|
|
2171
|
-
// Check if all dependencies exist
|
|
2172
|
-
for (const dep of deps) themes[dep] || errors.push(`Theme "${themeId}" depends on non-existent theme: ${dep}`); else errors.push(`Dependencies for "${themeId}" must be an array`); else warnings.push(`Dependencies defined for non-existent theme: ${themeId}`);
|
|
2173
|
-
return {
|
|
2174
|
-
valid: 0 === errors.length,
|
|
2175
|
-
errors: errors,
|
|
2176
|
-
warnings: warnings
|
|
2177
|
-
};
|
|
2178
|
-
}(config.dependencies, config.themes || {});
|
|
2179
|
-
errors.push(...depErrors.errors), warnings.push(...depErrors.warnings);
|
|
2180
|
-
}
|
|
2181
|
-
return {
|
|
2182
|
-
valid: 0 === errors.length,
|
|
2183
|
-
errors: errors,
|
|
2184
|
-
warnings: warnings
|
|
2185
|
-
};
|
|
2186
|
-
}(finalConfig)), config = {
|
|
2187
|
-
...finalConfig,
|
|
2188
|
-
validated: validate,
|
|
2189
|
-
errors: validationResult?.errors,
|
|
2190
|
-
warnings: validationResult?.warnings
|
|
2191
|
-
};
|
|
2192
|
-
} catch (error) {
|
|
2193
|
-
// Fallback: return default config structure
|
|
2194
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2195
|
-
logger.warn(`Failed to load theme config from ${configPath}`, {
|
|
2196
|
-
configPath: configPath,
|
|
2197
|
-
error: errorMessage
|
|
2198
|
-
}), config = {
|
|
2199
|
-
themes: {},
|
|
2200
|
-
build: {
|
|
2201
|
-
output: {
|
|
2202
|
-
directory: "themes",
|
|
2203
|
-
formats: {
|
|
2204
|
-
expanded: ".css",
|
|
2205
|
-
compressed: ".min.css"
|
|
2206
|
-
}
|
|
2207
|
-
},
|
|
2208
|
-
sass: {
|
|
2209
|
-
...DEFAULT_SASS_CONFIG,
|
|
2210
|
-
loadPaths: [ ...DEFAULT_SASS_CONFIG.loadPaths ]
|
|
2211
|
-
}
|
|
2212
|
-
},
|
|
2213
|
-
runtime: {
|
|
2214
|
-
basePath: "/themes",
|
|
2215
|
-
cdnPath: null,
|
|
2216
|
-
preload: [],
|
|
2217
|
-
lazy: !0,
|
|
2218
|
-
defaultTheme: "",
|
|
2219
|
-
// No default - use built-in styles (empty string instead of undefined)
|
|
2220
|
-
storageKey: "atomix-theme",
|
|
2221
|
-
dataAttribute: "data-theme",
|
|
2222
|
-
enablePersistence: !0,
|
|
2223
|
-
useMinified: "production" === env
|
|
2224
|
-
},
|
|
2225
|
-
integration: {
|
|
2226
|
-
cssVariables: DEFAULT_INTEGRATION_CSS_VARIABLES,
|
|
2227
|
-
classNames: DEFAULT_INTEGRATION_CLASS_NAMES
|
|
2228
|
-
},
|
|
2229
|
-
dependencies: {},
|
|
2230
|
-
validated: !1,
|
|
2231
|
-
errors: [ `Failed to load config: ${error instanceof Error ? error.message : String(error)}` ],
|
|
2232
|
-
warnings: [],
|
|
2233
|
-
__tokens: {},
|
|
2234
|
-
__extend: {}
|
|
2235
|
-
};
|
|
2236
|
-
}
|
|
2237
|
-
// Cache the loaded config
|
|
2238
|
-
return cachedConfig = config, config;
|
|
2239
|
-
}();
|
|
2240
|
-
} catch (error) {
|
|
2241
|
-
// In browser environments, config loading will fail
|
|
2242
|
-
// Use empty config as fallback
|
|
2243
|
-
this.config = {
|
|
2244
|
-
themes: {},
|
|
2245
|
-
build: {
|
|
2246
|
-
output: {
|
|
2247
|
-
directory: "themes",
|
|
2248
|
-
formats: {
|
|
2249
|
-
expanded: ".css",
|
|
2250
|
-
compressed: ".min.css"
|
|
2251
|
-
}
|
|
2252
|
-
},
|
|
2253
|
-
sass: {
|
|
2254
|
-
style: "expanded",
|
|
2255
|
-
sourceMap: !0,
|
|
2256
|
-
loadPaths: [ "src" ]
|
|
2257
|
-
}
|
|
2258
|
-
},
|
|
2259
|
-
runtime: {
|
|
2260
|
-
basePath: "",
|
|
2261
|
-
defaultTheme: void 0
|
|
2262
|
-
},
|
|
2263
|
-
integration: {
|
|
2264
|
-
cssVariables: {
|
|
2265
|
-
colorMode: "--color-mode"
|
|
2266
|
-
},
|
|
2267
|
-
classNames: {
|
|
2268
|
-
theme: "data-theme",
|
|
2269
|
-
colorMode: "data-color-mode"
|
|
2270
|
-
}
|
|
2271
|
-
},
|
|
2272
|
-
dependencies: {},
|
|
2273
|
-
validated: !1,
|
|
2274
|
-
errors: [],
|
|
2275
|
-
warnings: [],
|
|
2276
|
-
__tokens: {},
|
|
2277
|
-
__extend: {}
|
|
2278
|
-
};
|
|
2279
|
-
}
|
|
2280
|
-
// Register all themes from config
|
|
2281
|
-
for (const [themeId, definition] of Object.entries(this.config.themes)) this.register(themeId, definition);
|
|
2282
|
-
// Resolve dependencies
|
|
2283
|
-
this.resolveDependencies(), this.initialized = !0;
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
/**
|
|
2287
|
-
* Register a theme
|
|
2288
|
-
*/ register(themeId, definition) {
|
|
2289
|
-
// Get dependencies from config or definition
|
|
2290
|
-
const entry = {
|
|
2291
|
-
id: themeId,
|
|
2292
|
-
definition: definition,
|
|
2293
|
-
loaded: !1,
|
|
2294
|
-
dependencies: [ ...this.config?.dependencies?.[themeId] || definition.dependencies || [] ],
|
|
2295
|
-
dependents: []
|
|
2296
|
-
};
|
|
2297
|
-
this.entries.set(themeId, entry);
|
|
2298
|
-
}
|
|
2299
|
-
/**
|
|
2300
|
-
* Get theme entry
|
|
2301
|
-
*/ get(themeId) {
|
|
2302
|
-
return this.entries.get(themeId);
|
|
2303
|
-
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Check if theme exists
|
|
2306
|
-
*/ has(themeId) {
|
|
2307
|
-
return this.entries.has(themeId);
|
|
2308
|
-
}
|
|
2309
|
-
/**
|
|
2310
|
-
* Get all theme IDs
|
|
2311
|
-
*/ getAllIds() {
|
|
2312
|
-
return Array.from(this.entries.keys());
|
|
2313
|
-
}
|
|
2314
|
-
/**
|
|
2315
|
-
* Get all theme metadata
|
|
2316
|
-
*/ getAllMetadata() {
|
|
2317
|
-
return Array.from(this.entries.values()).map((entry => ({
|
|
2318
|
-
id: entry.id,
|
|
2319
|
-
name: entry.definition.name,
|
|
2320
|
-
type: entry.definition.type,
|
|
2321
|
-
class: entry.definition.class,
|
|
2322
|
-
description: entry.definition.description,
|
|
2323
|
-
author: entry.definition.author,
|
|
2324
|
-
version: entry.definition.version,
|
|
2325
|
-
tags: entry.definition.tags,
|
|
2326
|
-
supportsDarkMode: entry.definition.supportsDarkMode,
|
|
2327
|
-
status: entry.definition.status,
|
|
2328
|
-
a11y: entry.definition.a11y,
|
|
2329
|
-
color: entry.definition.color,
|
|
2330
|
-
features: entry.definition.features,
|
|
2331
|
-
dependencies: entry.dependencies
|
|
2332
|
-
})));
|
|
2333
|
-
}
|
|
2334
|
-
/**
|
|
2335
|
-
* Get theme definition
|
|
2336
|
-
*/ getDefinition(themeId) {
|
|
2337
|
-
return this.entries.get(themeId)?.definition;
|
|
2338
|
-
}
|
|
2339
|
-
/**
|
|
2340
|
-
* Check if a theme is loaded
|
|
2341
|
-
*/ isThemeLoaded(themeId) {
|
|
2342
|
-
const entry = this.entries.get(themeId);
|
|
2343
|
-
return !!entry && entry.loaded;
|
|
2344
|
-
}
|
|
2345
|
-
/**
|
|
2346
|
-
* Mark a theme as loaded
|
|
2347
|
-
*/ markLoaded(themeId, theme) {
|
|
2348
|
-
const entry = this.entries.get(themeId);
|
|
2349
|
-
entry && (entry.loaded = !0, theme && (entry.theme = theme));
|
|
2350
|
-
}
|
|
2351
|
-
/**
|
|
2352
|
-
* Get theme object (for JS themes)
|
|
2353
|
-
*/ getTheme(themeId) {
|
|
2354
|
-
const entry = this.entries.get(themeId);
|
|
2355
|
-
return entry?.loaded ? entry.theme : void 0;
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Get dependencies for a theme
|
|
2359
|
-
*/ getDependencies(themeId) {
|
|
2360
|
-
return this.entries.get(themeId)?.dependencies || [];
|
|
2361
|
-
}
|
|
2362
|
-
/**
|
|
2363
|
-
* Get dependents for a theme (themes that depend on this one)
|
|
2364
|
-
*/ getDependents(themeId) {
|
|
2365
|
-
return this.entries.get(themeId)?.dependents || [];
|
|
2366
|
-
}
|
|
2367
|
-
/**
|
|
2368
|
-
* Resolve all dependencies in correct order
|
|
2369
|
-
*/ resolveDependencyOrder(themeId) {
|
|
2370
|
-
const resolved = [], visited = new Set, visiting = new Set, visit = id => {
|
|
2371
|
-
if (visiting.has(id)) throw new Error(`Circular dependency detected involving theme: ${id}`);
|
|
2372
|
-
if (visited.has(id)) return;
|
|
2373
|
-
visiting.add(id);
|
|
2374
|
-
const entry = this.entries.get(id);
|
|
2375
|
-
if (entry) for (const dep of entry.dependencies) {
|
|
2376
|
-
if (!this.has(dep)) throw new Error(`Theme "${id}" depends on non-existent theme: ${dep}`);
|
|
2377
|
-
visit(dep);
|
|
2378
|
-
}
|
|
2379
|
-
visiting.delete(id), visited.add(id), resolved.push(id);
|
|
2380
|
-
};
|
|
2381
|
-
return visit(themeId), resolved;
|
|
2382
|
-
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Resolve dependencies and build dependency graph
|
|
2385
|
-
*/ resolveDependencies() {
|
|
2386
|
-
// Build dependents map
|
|
2387
|
-
for (const entry of this.entries.values()) for (const dep of entry.dependencies) {
|
|
2388
|
-
const depEntry = this.entries.get(dep);
|
|
2389
|
-
var _context;
|
|
2390
|
-
depEntry && (_includesInstanceProperty(_context = depEntry.dependents).call(_context, entry.id) || depEntry.dependents.push(entry.id));
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
/**
|
|
2394
|
-
* Validate all themes
|
|
2395
|
-
*/ validate() {
|
|
2396
|
-
const errors = [];
|
|
2397
|
-
// Check for circular dependencies
|
|
2398
|
-
for (const themeId of this.entries.keys()) try {
|
|
2399
|
-
this.resolveDependencyOrder(themeId);
|
|
2400
|
-
} catch (error) {
|
|
2401
|
-
errors.push(error instanceof Error ? error.message : String(error));
|
|
2402
|
-
}
|
|
2403
|
-
// Check for missing dependencies
|
|
2404
|
-
for (const [themeId, entry] of this.entries.entries()) for (const dep of entry.dependencies) this.has(dep) || errors.push(`Theme "${themeId}" depends on non-existent theme: ${dep}`);
|
|
2405
|
-
return {
|
|
2406
|
-
valid: 0 === errors.length,
|
|
2407
|
-
errors: errors
|
|
2408
|
-
};
|
|
2409
|
-
}
|
|
2410
|
-
/**
|
|
2411
|
-
* Clear registry
|
|
2412
|
-
*/ clear() {
|
|
2413
|
-
this.entries.clear(), this.config = null, this.initialized = !1;
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
|
|
2417
|
-
/**
|
|
2418
|
-
* CSS Injection Utilities
|
|
2419
|
-
*
|
|
2420
|
-
* Inject CSS into HTML head via <style> element.
|
|
2421
|
-
*/
|
|
2422
|
-
/**
|
|
2423
|
-
* Check if running in browser environment
|
|
2424
|
-
*/ function isBrowser$1() {
|
|
2425
|
-
return "undefined" != typeof document;
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
/**
|
|
2429
|
-
* Inject CSS into HTML head via <style> element
|
|
2430
|
-
*
|
|
2431
|
-
* Creates or updates a style element in the document head.
|
|
2432
|
-
* If an element with the same ID exists, it will be updated.
|
|
2433
|
-
*
|
|
2434
|
-
* @param css - CSS string to inject
|
|
2435
|
-
* @param id - Style element ID (default: 'atomix-theme')
|
|
2436
|
-
*
|
|
2437
|
-
* @example
|
|
2438
|
-
* ```typescript
|
|
2439
|
-
* const css = ':root { --atomix-color-primary: #7AFFD7; }';
|
|
2440
|
-
* injectCSS(css);
|
|
2441
|
-
*
|
|
2442
|
-
* // With custom ID
|
|
2443
|
-
* injectCSS(css, 'my-custom-theme');
|
|
2444
|
-
* ```
|
|
2445
|
-
*/ function injectCSS$1(css, id = "atomix-theme") {
|
|
2446
|
-
if (!isBrowser$1()) return void console.warn("injectCSS: Not in browser environment, CSS not injected");
|
|
2447
|
-
let styleElement = document.getElementById(id);
|
|
2448
|
-
styleElement || (styleElement = document.createElement("style"), styleElement.id = id,
|
|
2449
|
-
styleElement.setAttribute("data-atomix-theme", "true"), document.head.appendChild(styleElement)),
|
|
2450
|
-
styleElement.textContent = css;
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
/**
|
|
2454
|
-
* Remove injected CSS from DOM
|
|
2455
|
-
*
|
|
2456
|
-
* Removes the style element with the given ID from the document head.
|
|
1660
|
+
* Removes the style element with the given ID from the document head.
|
|
2457
1661
|
*
|
|
2458
1662
|
* @param id - Style element ID to remove (default: 'atomix-theme')
|
|
2459
1663
|
*
|
|
@@ -2496,71 +1700,18 @@ class ThemeRegistry {
|
|
|
2496
1700
|
* const css = ':root { --atomix-color-primary: #7AFFD7; }';
|
|
2497
1701
|
* await saveCSSFile(css, './themes/custom.css');
|
|
2498
1702
|
* ```
|
|
2499
|
-
*/
|
|
2500
|
-
// Check if in browser environment
|
|
2501
|
-
if ("undefined" != typeof window) throw new Error("saveCSSFile can only be used in Node.js environment. Use injectCSS() for browser environments.");
|
|
2502
|
-
// Dynamic import to avoid bundling Node.js modules in browser builds
|
|
2503
|
-
const fs = await import("fs/promises"), dir = (await import("path")).dirname(filePath);
|
|
2504
|
-
await fs.mkdir(dir, {
|
|
2505
|
-
recursive: !0
|
|
2506
|
-
}),
|
|
2507
|
-
// Write file
|
|
2508
|
-
await fs.writeFile(filePath, css, "utf8");
|
|
2509
|
-
}
|
|
2510
|
-
|
|
2511
|
-
/**
|
|
2512
|
-
* Save CSS to file (synchronous version)
|
|
2513
|
-
*
|
|
2514
|
-
* Synchronous version of saveCSSFile. Only works in Node.js environment.
|
|
2515
|
-
*
|
|
2516
|
-
* @param css - CSS string to save
|
|
2517
|
-
* @param filePath - Output file path
|
|
2518
|
-
* @throws Error if called in browser environment
|
|
2519
|
-
*/ function saveCSSFileSync(css, filePath) {
|
|
2520
|
-
// Check if in browser environment
|
|
2521
|
-
if ("undefined" != typeof window) throw new Error("saveCSSFileSync can only be used in Node.js environment. Use injectCSS() for browser environments.");
|
|
2522
|
-
// Use require for synchronous file operations
|
|
2523
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
2524
|
-
const fs = require("fs"), dir = require("path").dirname(filePath);
|
|
2525
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
2526
|
-
fs.existsSync(dir) || fs.mkdirSync(dir, {
|
|
2527
|
-
recursive: !0
|
|
2528
|
-
}),
|
|
2529
|
-
// Write file
|
|
2530
|
-
fs.writeFileSync(filePath, css, "utf8");
|
|
2531
|
-
}
|
|
1703
|
+
*/ "undefined" != typeof process && process.env;
|
|
2532
1704
|
|
|
2533
1705
|
/**
|
|
2534
1706
|
* Check if code is running in a browser environment
|
|
2535
|
-
*/
|
|
1707
|
+
*/
|
|
1708
|
+
const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), buildThemePath = (themeName, basePath = "/themes", useMinified = !1, cdnPath = null) => {
|
|
2536
1709
|
// Validate theme name to prevent path injection
|
|
2537
1710
|
if (!isValidThemeName(themeName)) throw new Error(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens.`);
|
|
2538
1711
|
const fileName = `${themeName}${useMinified ? ".min.css" : ".css"}`;
|
|
2539
1712
|
return cdnPath ? `${cdnPath.replace(/[<>"']/g, "")}/${fileName}` : `${basePath.replace(/\/$/, "").replace(/[<>"']/g, "")}/${fileName.replace(/^\//, "")}`;
|
|
2540
1713
|
// Ensure basePath doesn't end with slash and fileName doesn't start with slash
|
|
2541
1714
|
// Also sanitize basePath to prevent path injection
|
|
2542
|
-
}, loadThemeCSS = (fullPath, linkId) => isServer() ? Promise.resolve() : new Promise(((resolve, reject) => {
|
|
2543
|
-
if (document.getElementById(linkId)) return void resolve();
|
|
2544
|
-
// Create link element
|
|
2545
|
-
const link = document.createElement("link");
|
|
2546
|
-
link.id = linkId, link.rel = "stylesheet", link.type = "text/css", link.href = fullPath,
|
|
2547
|
-
// Add data attribute for tracking
|
|
2548
|
-
link.setAttribute("data-atomix-theme", "true"),
|
|
2549
|
-
// Handle load success
|
|
2550
|
-
link.onload = () => {
|
|
2551
|
-
resolve();
|
|
2552
|
-
},
|
|
2553
|
-
// Handle load error
|
|
2554
|
-
link.onerror = () => {
|
|
2555
|
-
// Remove failed link element
|
|
2556
|
-
link.remove(), reject(new Error(`Failed to load theme CSS: ${fullPath}`));
|
|
2557
|
-
},
|
|
2558
|
-
// Append to head
|
|
2559
|
-
document.head.appendChild(link);
|
|
2560
|
-
})), isThemeLoaded = themeName => {
|
|
2561
|
-
if (isServer()) return !1;
|
|
2562
|
-
const linkId = getThemeLinkId(themeName);
|
|
2563
|
-
return null !== document.getElementById(linkId);
|
|
2564
1715
|
}, isValidThemeName = themeName => !(!themeName || "string" != typeof themeName) && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(themeName);
|
|
2565
1716
|
|
|
2566
1717
|
/**
|
|
@@ -3033,211 +2184,198 @@ function generateCSSVariables(theme, options = {}) {
|
|
|
3033
2184
|
styleElement.textContent = css;
|
|
3034
2185
|
}
|
|
3035
2186
|
/**
|
|
3036
|
-
*
|
|
2187
|
+
* Naming Utilities
|
|
3037
2188
|
*
|
|
3038
|
-
*
|
|
2189
|
+
* Provides consistent naming conventions across the theme system
|
|
3039
2190
|
*/
|
|
3040
2191
|
/**
|
|
3041
|
-
*
|
|
2192
|
+
* Generate consistent CSS class names following BEM methodology
|
|
3042
2193
|
*/ (css, styleId), css;
|
|
3043
2194
|
}
|
|
3044
2195
|
|
|
3045
|
-
|
|
2196
|
+
function generateClassName(block, element, modifiers) {
|
|
2197
|
+
let className = block;
|
|
2198
|
+
return element && (className += `__${element}`), modifiers && Object.entries(modifiers).forEach((([key, value]) => {
|
|
2199
|
+
value && (className += `--${key}`, "string" == typeof value && value !== key && (className += `-${value}`));
|
|
2200
|
+
})), className;
|
|
2201
|
+
}
|
|
3046
2202
|
|
|
3047
|
-
|
|
2203
|
+
/**
|
|
2204
|
+
* Generate consistent CSS variable names
|
|
2205
|
+
*/ function generateCSSVariableName(property, options = {}) {
|
|
2206
|
+
const {prefix: prefix = "atomix", component: component, variant: variant, state: state} = options, parts = [ prefix ];
|
|
2207
|
+
return component && parts.push(component), variant && parts.push(variant), state && parts.push(state),
|
|
2208
|
+
parts.push(property), `--${parts.join("-")}`;
|
|
2209
|
+
}
|
|
3048
2210
|
|
|
3049
2211
|
/**
|
|
3050
|
-
*
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
2212
|
+
* Normalize theme tokens to consistent naming convention
|
|
2213
|
+
*/ function normalizeThemeTokens(tokens) {
|
|
2214
|
+
const normalized = {};
|
|
2215
|
+
for (const [key, value] of Object.entries(tokens))
|
|
2216
|
+
// Recursively normalize nested objects
|
|
2217
|
+
normalized[key] = "object" == typeof value && null !== value ? normalizeThemeTokens(value) : value;
|
|
2218
|
+
return normalized;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
3057
2221
|
/**
|
|
3058
|
-
*
|
|
2222
|
+
* Convert camelCase to kebab-case for CSS custom properties
|
|
2223
|
+
*/ function camelToKebab(str) {
|
|
2224
|
+
return str.replace(/[A-Z]/g, (match => `-${match.toLowerCase()}`));
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
/**
|
|
2228
|
+
* Convert theme property to CSS variable name
|
|
2229
|
+
*/ function themePropertyToCSSVar(propertyPath, prefix = "atomix") {
|
|
2230
|
+
return `--${prefix}-${propertyPath.split(".").map((part => camelToKebab(part))).join("-")}`;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/**
|
|
2234
|
+
* Component Theming Utilities
|
|
3059
2235
|
*
|
|
3060
|
-
*
|
|
2236
|
+
* Provides consistent patterns for applying theme values to components
|
|
3061
2237
|
*/
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
*/ applyTheme(theme) {
|
|
3072
|
-
// Clear previously applied variables
|
|
3073
|
-
this.clearAppliedVars(),
|
|
3074
|
-
// Check if it's DesignTokens
|
|
3075
|
-
this.isDesignTokens(theme) ?
|
|
3076
|
-
// Direct DesignTokens - use unified theme system (with config support)
|
|
3077
|
-
this.applyDesignTokens(theme) : injectCSS$1(createTheme(theme, {
|
|
3078
|
-
selector: ":root",
|
|
3079
|
-
prefix: "atomix"
|
|
3080
|
-
}), this.styleId),
|
|
3081
|
-
// Apply component overrides (only for Theme objects)
|
|
3082
|
-
!this.isDesignTokens(theme) && theme.components && this.applyComponentOverrides(theme.components);
|
|
3083
|
-
}
|
|
3084
|
-
/**
|
|
3085
|
-
* Apply DesignTokens using unified theme system
|
|
3086
|
-
*
|
|
3087
|
-
* Uses createTheme() which automatically loads from atomix.config.ts
|
|
3088
|
-
* if no tokens are provided, ensuring config is always respected.
|
|
3089
|
-
*/ applyDesignTokens(tokens) {
|
|
3090
|
-
// Inject CSS into DOM
|
|
3091
|
-
injectCSS$1(createTheme(tokens, {
|
|
3092
|
-
selector: ":root",
|
|
3093
|
-
prefix: "atomix"
|
|
3094
|
-
}), this.styleId);
|
|
3095
|
-
}
|
|
3096
|
-
/**
|
|
3097
|
-
* Check if object is DesignTokens
|
|
3098
|
-
*/ isDesignTokens(obj) {
|
|
3099
|
-
// DesignTokens is a flat object with string keys, no nested structures
|
|
3100
|
-
return null !== obj && "object" == typeof obj && !("palette" in obj) && !("typography" in obj) && !("__isJSTheme" in obj);
|
|
3101
|
-
}
|
|
3102
|
-
/**
|
|
3103
|
-
* Apply global CSS variables (for component overrides)
|
|
3104
|
-
*/ applyGlobalCSSVars(vars) {
|
|
3105
|
-
Object.entries(vars).forEach((([key, value]) => {
|
|
3106
|
-
this.root.style.setProperty(key, String(value));
|
|
3107
|
-
}));
|
|
3108
|
-
}
|
|
3109
|
-
/**
|
|
3110
|
-
* Apply component-level overrides
|
|
3111
|
-
*/ applyComponentOverrides(overrides) {
|
|
3112
|
-
Object.entries(overrides).forEach((([componentName, override]) => {
|
|
3113
|
-
override && this.applyComponentOverride(componentName, override);
|
|
3114
|
-
}));
|
|
3115
|
-
}
|
|
3116
|
-
/**
|
|
3117
|
-
* Apply override for a specific component
|
|
3118
|
-
*/ applyComponentOverride(componentName, override) {
|
|
3119
|
-
const vars = {}, componentKey = componentName.toLowerCase();
|
|
3120
|
-
// Apply component-level CSS variables
|
|
3121
|
-
override.cssVars && Object.entries(override.cssVars).forEach((([key, value]) => {
|
|
3122
|
-
// If key doesn't start with --, add component prefix
|
|
3123
|
-
const varKey = key.startsWith("--") ? key : `--atomix-${componentKey}-${key}`;
|
|
3124
|
-
vars[varKey] = value;
|
|
3125
|
-
})),
|
|
3126
|
-
// Apply part-specific CSS variables
|
|
3127
|
-
override.parts && Object.entries(override.parts).forEach((([partName, partOverride]) => {
|
|
3128
|
-
partOverride.cssVars && Object.entries(partOverride.cssVars).forEach((([key, value]) => {
|
|
3129
|
-
const varKey = key.startsWith("--") ? key : `--atomix-${componentKey}-${partName}-${key}`;
|
|
3130
|
-
vars[varKey] = value;
|
|
3131
|
-
}));
|
|
3132
|
-
})),
|
|
3133
|
-
// Apply variant-specific CSS variables
|
|
3134
|
-
override.variants && Object.entries(override.variants).forEach((([variantName, variantOverride]) => {
|
|
3135
|
-
variantOverride.cssVars && Object.entries(variantOverride.cssVars).forEach((([key, value]) => {
|
|
3136
|
-
const varKey = key.startsWith("--") ? key : `--atomix-${componentKey}-${variantName}-${key}`;
|
|
3137
|
-
vars[varKey] = value;
|
|
3138
|
-
}));
|
|
3139
|
-
})), this.applyGlobalCSSVars(vars);
|
|
3140
|
-
}
|
|
3141
|
-
/**
|
|
3142
|
-
* Clear all applied CSS variables
|
|
3143
|
-
*/ clearAppliedVars() {
|
|
3144
|
-
removeCSS(this.styleId);
|
|
3145
|
-
}
|
|
3146
|
-
/**
|
|
3147
|
-
* Remove theme application
|
|
3148
|
-
*/ removeTheme() {
|
|
3149
|
-
this.clearAppliedVars(), removeCSS(this.styleId);
|
|
3150
|
-
}
|
|
3151
|
-
/**
|
|
3152
|
-
* Update specific CSS variables without clearing all
|
|
3153
|
-
*/ updateCSSVars(vars) {
|
|
3154
|
-
this.applyGlobalCSSVars(vars);
|
|
3155
|
-
}
|
|
2238
|
+
/**
|
|
2239
|
+
* Get a theme value for a specific component using CSS variables
|
|
2240
|
+
* This ensures all components access theme values consistently
|
|
2241
|
+
*/ function getComponentThemeValue(component, property, variant, size) {
|
|
2242
|
+
// Build CSS variable name following consistent pattern
|
|
2243
|
+
const parts = [ "atomix", component ];
|
|
2244
|
+
// Return CSS variable reference with fallback
|
|
2245
|
+
return variant && parts.push(variant), size && parts.push(size), parts.push(property),
|
|
2246
|
+
`var(--${parts.join("-")}, var(--atomix-${property}, initial))`;
|
|
3156
2247
|
}
|
|
3157
2248
|
|
|
3158
2249
|
/**
|
|
3159
|
-
*
|
|
3160
|
-
*/
|
|
2250
|
+
* Generate component-specific CSS variables from theme
|
|
2251
|
+
*/ function generateComponentCSSVars(component, theme, variant, size) {
|
|
2252
|
+
const vars = {}, prefixParts = [ "atomix", component ];
|
|
2253
|
+
// This is a simplified implementation - in a real system you'd have more
|
|
2254
|
+
// sophisticated logic to extract component-specific values from the theme
|
|
2255
|
+
variant && prefixParts.push(variant), size && prefixParts.push(size);
|
|
2256
|
+
const prefix = prefixParts.join("-");
|
|
2257
|
+
// Add common component properties
|
|
2258
|
+
if (theme.palette && (vars[`--${prefix}-color`] = theme.palette.primary?.main || "#7c3aed",
|
|
2259
|
+
vars[`--${prefix}-color-hover`] = theme.palette.primary?.dark || "#5b21b6", vars[`--${prefix}-color-active`] = theme.palette.primary?.main || "#7c3aed",
|
|
2260
|
+
vars[`--${prefix}-color-disabled`] = theme.palette.text?.disabled || "#9ca3af"),
|
|
2261
|
+
theme.typography && (vars[`--${prefix}-font-family`] = theme.typography.fontFamily || "Inter, sans-serif",
|
|
2262
|
+
vars[`--${prefix}-font-size`] = theme.typography.fontSize ? `${theme.typography.fontSize}px` : "16px"),
|
|
2263
|
+
theme.spacing) {
|
|
2264
|
+
const spacing = "function" == typeof theme.spacing ? theme.spacing : val => 8 * val;
|
|
2265
|
+
vars[`--${prefix}-spacing-unit`] = `${spacing(1)}px`, vars[`--${prefix}-spacing-sm`] = `${spacing(.5)}px`,
|
|
2266
|
+
vars[`--${prefix}-spacing-md`] = `${spacing(1)}px`, vars[`--${prefix}-spacing-lg`] = `${spacing(2)}px`;
|
|
2267
|
+
}
|
|
2268
|
+
return vars;
|
|
2269
|
+
}
|
|
3161
2270
|
|
|
3162
2271
|
/**
|
|
3163
|
-
*
|
|
3164
|
-
*/ function
|
|
3165
|
-
|
|
2272
|
+
* Apply consistent theme to component style object
|
|
2273
|
+
*/ function applyComponentTheme(component, style = {}, variant, size, theme) {
|
|
2274
|
+
// If no theme provided, return original style
|
|
2275
|
+
return theme ? {
|
|
2276
|
+
...generateComponentCSSVars(component, theme, variant, size),
|
|
2277
|
+
...style
|
|
2278
|
+
} : style;
|
|
2279
|
+
// Generate component-specific CSS variables
|
|
3166
2280
|
}
|
|
3167
2281
|
|
|
3168
2282
|
/**
|
|
3169
|
-
*
|
|
3170
|
-
*/ function
|
|
3171
|
-
|
|
2283
|
+
* Create a hook for consistent component theming
|
|
2284
|
+
*/ function useComponentTheme(component, variant, size, theme) {
|
|
2285
|
+
return property => getComponentThemeValue(component, property, variant, size);
|
|
3172
2286
|
}
|
|
3173
2287
|
|
|
3174
2288
|
/**
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
* Provides theme context to child components and manages theme state.
|
|
2289
|
+
* Theme Configuration Loader
|
|
3178
2290
|
*
|
|
3179
|
-
*
|
|
3180
|
-
*
|
|
2291
|
+
* Provides functions to load theme configurations from atomix.config.ts
|
|
2292
|
+
* Includes both sync and async versions, with automatic fallbacks
|
|
2293
|
+
*/
|
|
2294
|
+
/**
|
|
2295
|
+
* Load theme from config file (synchronous, Node.js only)
|
|
2296
|
+
* @param configPath - Path to config file (default: atomix.config.ts)
|
|
2297
|
+
* @returns DesignTokens from theme configuration
|
|
2298
|
+
* @throws Error if config loading is not available in browser environment
|
|
2299
|
+
*/ function loadThemeFromConfigSync(options) {
|
|
2300
|
+
// Check if we're in a browser environment
|
|
2301
|
+
if ("undefined" != typeof window) throw new Error("loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.");
|
|
2302
|
+
// Use dynamic import to load the config loader
|
|
2303
|
+
// This allows bundlers to handle external dependencies properly
|
|
2304
|
+
let loadAtomixConfig;
|
|
2305
|
+
try {
|
|
2306
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
2307
|
+
const {loadAtomixConfig: loader} = require("../../config/loader");
|
|
2308
|
+
loadAtomixConfig = loader;
|
|
2309
|
+
} catch (error) {
|
|
2310
|
+
if (!1 !== options?.required) throw new Error("Config loader module not available");
|
|
2311
|
+
// Return empty tokens if config is not required
|
|
2312
|
+
return createTokens({});
|
|
2313
|
+
}
|
|
2314
|
+
const config = loadAtomixConfig({
|
|
2315
|
+
configPath: options?.configPath || "atomix.config.ts",
|
|
2316
|
+
required: !1 !== options?.required
|
|
2317
|
+
});
|
|
2318
|
+
return config?.theme ? isThemeObject$1(config.theme) ? createDesignTokensFromTheme(config.theme) : createTokens(config.theme) : createTokens({});
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
/**
|
|
2322
|
+
* Load theme from config file (asynchronous)
|
|
2323
|
+
* @param configPath - Path to config file (default: atomix.config.ts)
|
|
2324
|
+
* @returns Promise resolving to DesignTokens from theme configuration
|
|
2325
|
+
*/ async function loadThemeFromConfig(options) {
|
|
2326
|
+
// Check if we're in a browser environment
|
|
2327
|
+
if ("undefined" != typeof window) throw new Error("loadThemeFromConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.");
|
|
2328
|
+
// Dynamic import for config loader
|
|
2329
|
+
const {loadAtomixConfig: loadAtomixConfig} = await import("../lib/config/loader"), config = await loadAtomixConfig({
|
|
2330
|
+
configPath: options?.configPath || "atomix.config.ts",
|
|
2331
|
+
required: !1 !== options?.required
|
|
2332
|
+
});
|
|
2333
|
+
return config?.theme ? isThemeObject$1(config.theme) ? createDesignTokensFromTheme(config.theme) : createTokens(config.theme) : createTokens({});
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
/**
|
|
2337
|
+
* Check if the provided object is a Theme object
|
|
2338
|
+
* @param theme - Object to check
|
|
2339
|
+
* @returns True if the object is a Theme object, false otherwise
|
|
2340
|
+
*/ function isThemeObject$1(theme) {
|
|
2341
|
+
return "object" == typeof theme && null !== theme;
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
/**
|
|
2345
|
+
* Theme Context
|
|
3181
2346
|
*
|
|
3182
|
-
*
|
|
3183
|
-
|
|
3184
|
-
|
|
2347
|
+
* React context for theme management
|
|
2348
|
+
*/
|
|
2349
|
+
/**
|
|
2350
|
+
* Theme context with default values
|
|
2351
|
+
*/ const ThemeContext = createContext(null);
|
|
2352
|
+
|
|
2353
|
+
ThemeContext.displayName = "ThemeContext";
|
|
2354
|
+
|
|
2355
|
+
/**
|
|
2356
|
+
* Theme Provider
|
|
3185
2357
|
*
|
|
3186
|
-
*
|
|
3187
|
-
*
|
|
3188
|
-
*
|
|
3189
|
-
*
|
|
3190
|
-
*
|
|
3191
|
-
* </ThemeProvider>
|
|
3192
|
-
* );
|
|
3193
|
-
* }
|
|
2358
|
+
* React context provider for theme management with separated concerns.
|
|
2359
|
+
* Simplified version focusing on core functionality:
|
|
2360
|
+
* - String-based themes (CSS files)
|
|
2361
|
+
* - JS Theme objects
|
|
2362
|
+
* - Persistence via localStorage
|
|
3194
2363
|
*
|
|
3195
|
-
*
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
* <ThemeProvider defaultTheme="dark">
|
|
3199
|
-
* <YourApp />
|
|
3200
|
-
* </ThemeProvider>
|
|
3201
|
-
* );
|
|
3202
|
-
* }
|
|
3203
|
-
* ```
|
|
3204
|
-
*/ const ThemeProvider = ({children: children, defaultTheme: defaultTheme, themes: themes = {}, basePath: basePath = "/themes", cdnPath: cdnPath = null, preload: preload = [], lazy: lazy = !0, storageKey: storageKey = "atomix-theme", dataAttribute: dataAttribute = "data-theme", enablePersistence: enablePersistence = !0, useMinified: useMinified = !1, onThemeChange: onThemeChange, onError: onError}) => {
|
|
2364
|
+
* Falls back to 'default' theme if no configuration is found.
|
|
2365
|
+
*/
|
|
2366
|
+
const ThemeProvider = ({children: children, defaultTheme: defaultTheme, themes: themes = {}, basePath: basePath = "/themes", cdnPath: cdnPath = null, useMinified: useMinified = !1, storageKey: storageKey = "atomix-theme", dataAttribute: dataAttribute = "data-theme", enablePersistence: enablePersistence = !0, onThemeChange: onThemeChange, onError: onError}) => {
|
|
3205
2367
|
// Store callbacks in refs to avoid recreating when they change
|
|
3206
2368
|
const onThemeChangeRef = useRef(onThemeChange), onErrorRef = useRef(onError);
|
|
3207
|
-
// Update
|
|
2369
|
+
// Update ref when callback changes
|
|
3208
2370
|
useEffect((() => {
|
|
3209
2371
|
onThemeChangeRef.current = onThemeChange, onErrorRef.current = onError;
|
|
3210
2372
|
}), [ onThemeChange, onError ]);
|
|
3211
|
-
// Create stable wrapper functions that read from
|
|
2373
|
+
// Create stable wrapper functions that read from ref
|
|
3212
2374
|
const handleThemeChange = useCallback((theme => {
|
|
3213
2375
|
onThemeChangeRef.current?.(theme);
|
|
3214
2376
|
}), []), handleError = useCallback(((error, themeName) => {
|
|
3215
2377
|
onErrorRef.current?.(error, themeName);
|
|
3216
|
-
}), []),
|
|
3217
|
-
// Only update if themes object actually changed (shallow comparison)
|
|
3218
|
-
const currentKeys = Object.keys(themes), prevKeys = Object.keys(themesRef.current);
|
|
3219
|
-
return currentKeys.length !== prevKeys.length || currentKeys.some((key => themes[key] !== themesRef.current[key])) ? (themesRef.current = themes,
|
|
3220
|
-
themes) : themesRef.current;
|
|
3221
|
-
}), [ themes ]), logger = useMemo((() => getLogger()), []), registry = useMemo((() => {
|
|
3222
|
-
const reg = new ThemeRegistry;
|
|
3223
|
-
// Register themes from props
|
|
3224
|
-
if (themesStable && Object.keys(themesStable).length > 0) for (const [themeId, metadata] of Object.entries(themesStable)) reg.has(themeId) || reg.register(themeId, {
|
|
3225
|
-
type: "css",
|
|
3226
|
-
name: metadata.name,
|
|
3227
|
-
class: metadata.class || themeId,
|
|
3228
|
-
description: metadata.description,
|
|
3229
|
-
author: metadata.author,
|
|
3230
|
-
version: metadata.version,
|
|
3231
|
-
tags: metadata.tags,
|
|
3232
|
-
supportsDarkMode: metadata.supportsDarkMode,
|
|
3233
|
-
status: metadata.status,
|
|
3234
|
-
a11y: metadata.a11y,
|
|
3235
|
-
color: metadata.color,
|
|
3236
|
-
features: metadata.features,
|
|
3237
|
-
dependencies: metadata.dependencies
|
|
3238
|
-
});
|
|
3239
|
-
return reg;
|
|
3240
|
-
}), [ themesStable ]), storageAdapter = useMemo((() => ({
|
|
2378
|
+
}), []), storageAdapter = useMemo((() => ({
|
|
3241
2379
|
getItem: key => {
|
|
3242
2380
|
if (isServer()) return null;
|
|
3243
2381
|
try {
|
|
@@ -3269,7 +2407,7 @@ class ThemeApplicator {
|
|
|
3269
2407
|
return !1;
|
|
3270
2408
|
}
|
|
3271
2409
|
}
|
|
3272
|
-
})), []),
|
|
2410
|
+
})), []), initialDefaultTheme = useMemo((() => {
|
|
3273
2411
|
// Check storage first
|
|
3274
2412
|
if (enablePersistence && storageAdapter.isAvailable()) {
|
|
3275
2413
|
const stored = storageAdapter.getItem(storageKey);
|
|
@@ -3277,236 +2415,368 @@ class ThemeApplicator {
|
|
|
3277
2415
|
}
|
|
3278
2416
|
// If defaultTheme is provided, use it
|
|
3279
2417
|
if (null != defaultTheme) return defaultTheme;
|
|
3280
|
-
//
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
version: meta.version,
|
|
3291
|
-
tags: meta.tags,
|
|
3292
|
-
supportsDarkMode: meta.supportsDarkMode,
|
|
3293
|
-
status: meta.status,
|
|
3294
|
-
a11y: meta.a11y,
|
|
3295
|
-
color: meta.color,
|
|
3296
|
-
features: meta.features,
|
|
3297
|
-
dependencies: meta.dependencies
|
|
3298
|
-
}))))), [isLoading, setIsLoading] = useState(!1), [error, setError] = useState(null), loadedThemesRef = useRef(new Set), previousThemeRef = useRef(null);
|
|
3299
|
-
// Get default theme (with automatic config loading)
|
|
3300
|
-
useCallback((() => {
|
|
3301
|
-
// Check storage first
|
|
3302
|
-
if (enablePersistence && storageAdapter.isAvailable()) {
|
|
3303
|
-
const stored = storageAdapter.getItem(storageKey);
|
|
3304
|
-
if (stored) return stored;
|
|
2418
|
+
// Try to load from atomix.config.ts as fallback, but only in Node.js/SSR environments
|
|
2419
|
+
if ("undefined" == typeof window) try {
|
|
2420
|
+
// Dynamically import the config loader to avoid bundling issues in browser
|
|
2421
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
2422
|
+
const {loadThemeFromConfigSync: loadThemeFromConfigSync} = require("../config/configLoader"), configTokens = loadThemeFromConfigSync();
|
|
2423
|
+
if (configTokens && Object.keys(configTokens).length > 0)
|
|
2424
|
+
// For simplicity, we'll treat config tokens as a special theme name
|
|
2425
|
+
return "config-theme";
|
|
2426
|
+
} catch (error) {
|
|
2427
|
+
console.warn("Failed to load theme from config, using default");
|
|
3305
2428
|
}
|
|
3306
|
-
//
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
// Use ThemeApplicator for Theme objects
|
|
3326
|
-
themeApplicator?.applyTheme(theme) : injectCSS$1(createTheme(theme), "atomix-theme"));
|
|
3327
|
-
}), [ activeTheme, themeApplicator ]), setTheme = useCallback((async (theme, options) => {
|
|
3328
|
-
const {removePrevious: removePrevious = !0, fallbackOnError: fallbackOnError = !0, customPath: customPath} = options || {};
|
|
2429
|
+
// Default fallback
|
|
2430
|
+
return "default";
|
|
2431
|
+
}), [ defaultTheme, enablePersistence, storageKey ]), [currentTheme, setCurrentTheme] = useState((() => initialDefaultTheme)), [activeTheme, setActiveTheme] = useState(null), [isLoading, setIsLoading] = useState(!1), [error, setError] = useState(null), loadedThemesRef = useRef(new Set), themePromisesRef = useRef({});
|
|
2432
|
+
// Apply initial theme attributes to document element
|
|
2433
|
+
useEffect((() => {
|
|
2434
|
+
isServer() || ((dataAttribute, themeName) => {
|
|
2435
|
+
isServer() || (
|
|
2436
|
+
// Set data attribute on body
|
|
2437
|
+
document.body.setAttribute(dataAttribute, themeName),
|
|
2438
|
+
// Also set on documentElement for broader compatibility
|
|
2439
|
+
document.documentElement.setAttribute(dataAttribute, themeName));
|
|
2440
|
+
})(String(currentTheme), dataAttribute);
|
|
2441
|
+
}), [ currentTheme, dataAttribute ]),
|
|
2442
|
+
// Handle theme persistence
|
|
2443
|
+
useEffect((() => {
|
|
2444
|
+
enablePersistence && storageAdapter.isAvailable() && storageAdapter.setItem(storageKey, String(currentTheme));
|
|
2445
|
+
}), [ currentTheme, storageKey, enablePersistence ]);
|
|
2446
|
+
// Function to set theme with proper type handling
|
|
2447
|
+
const setTheme = useCallback((async (theme, options) => {
|
|
3329
2448
|
setIsLoading(!0), setError(null);
|
|
3330
2449
|
try {
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
if (
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
// Try as link ID first, then as theme name
|
|
3370
|
-
let link = document.getElementById(themeNameOrLinkId);
|
|
3371
|
-
if (!link) {
|
|
3372
|
-
const linkId = getThemeLinkId(themeNameOrLinkId);
|
|
3373
|
-
link = document.getElementById(linkId);
|
|
3374
|
-
}
|
|
3375
|
-
link && link.remove();
|
|
3376
|
-
})(previousThemeRef.current),
|
|
3377
|
-
// Load CSS if not already loaded
|
|
3378
|
-
isThemeLoaded(theme) || (await loadThemeCSS(themePath, linkId), loadedThemesRef.current.add(theme)),
|
|
3379
|
-
// Apply theme attributes
|
|
3380
|
-
((dataAttribute, themeName) => {
|
|
3381
|
-
isServer() || (
|
|
3382
|
-
// Set data attribute on body
|
|
3383
|
-
document.body.setAttribute(dataAttribute, themeName),
|
|
3384
|
-
// Also set on documentElement for broader compatibility
|
|
3385
|
-
document.documentElement.setAttribute(dataAttribute, themeName));
|
|
3386
|
-
})(dataAttribute, theme),
|
|
3387
|
-
// Update state
|
|
3388
|
-
previousThemeRef.current = currentTheme, setCurrentTheme(theme), setActiveTheme(null),
|
|
3389
|
-
previousThemeRef.current, Date.now(), handleThemeChange(theme),
|
|
3390
|
-
// Persist to storage
|
|
3391
|
-
enablePersistence && storageAdapter.isAvailable() && storageAdapter.setItem(storageKey, theme),
|
|
3392
|
-
setIsLoading(!1);
|
|
2450
|
+
let themeName, themeObj = null;
|
|
2451
|
+
// If it's a string theme name, load the associated CSS
|
|
2452
|
+
if ("string" == typeof theme ? themeName = theme :
|
|
2453
|
+
// If it's a Theme object or DesignTokens, we need to process it
|
|
2454
|
+
function(theme) {
|
|
2455
|
+
return "object" == typeof theme && null !== theme && "__isJSTheme" in theme && !0 === theme.__isJSTheme;
|
|
2456
|
+
}(theme) ? (themeObj = theme,
|
|
2457
|
+
// For JS themes, we use a generic name
|
|
2458
|
+
themeName = "js-theme", setActiveTheme(themeObj)) :
|
|
2459
|
+
// For DesignTokens, we might create a theme from tokens
|
|
2460
|
+
themeName = "tokens-theme", "string" == typeof theme && themes[theme]) {
|
|
2461
|
+
// Check if theme is already loading
|
|
2462
|
+
if (themePromisesRef.current[theme]) return await themePromisesRef.current[theme],
|
|
2463
|
+
setCurrentTheme(theme), setActiveTheme(null), void handleThemeChange(theme);
|
|
2464
|
+
// Load CSS theme
|
|
2465
|
+
const themeLoadPromise = new Promise((async (resolve, reject) => {
|
|
2466
|
+
try {
|
|
2467
|
+
if (!themes[theme]) throw new Error(`Theme metadata not found for theme: ${theme}`);
|
|
2468
|
+
{
|
|
2469
|
+
// Build CSS path using utility function
|
|
2470
|
+
const cssPath = buildThemePath(theme, basePath, useMinified, cdnPath);
|
|
2471
|
+
// Remove any previously loaded theme CSS
|
|
2472
|
+
removeCSS(`theme-${String(currentTheme)}`),
|
|
2473
|
+
// Inject new theme CSS
|
|
2474
|
+
await injectCSS$1(cssPath, `theme-${theme}`), loadedThemesRef.current.add(theme),
|
|
2475
|
+
setCurrentTheme(theme), setActiveTheme(null), handleThemeChange(theme), resolve();
|
|
2476
|
+
}
|
|
2477
|
+
} catch (err) {
|
|
2478
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
2479
|
+
setError(error), handleError(error, String(theme)), reject(error);
|
|
2480
|
+
}
|
|
2481
|
+
}));
|
|
2482
|
+
themePromisesRef.current[theme] = themeLoadPromise, await themeLoadPromise;
|
|
2483
|
+
} else themeObj ? (
|
|
2484
|
+
// For JS themes, set them directly
|
|
2485
|
+
setCurrentTheme(themeName), setActiveTheme(themeObj), handleThemeChange(themeObj)) : (
|
|
2486
|
+
// For string theme that isn't in our themes record, just set the name
|
|
2487
|
+
setCurrentTheme(themeName), setActiveTheme(null), handleThemeChange(themeName));
|
|
3393
2488
|
} catch (err) {
|
|
3394
2489
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
3395
|
-
|
|
3396
|
-
|
|
2490
|
+
setError(error), handleError(error, String(theme));
|
|
2491
|
+
} finally {
|
|
2492
|
+
setIsLoading(!1);
|
|
3397
2493
|
}
|
|
3398
|
-
}), [
|
|
3399
|
-
if (
|
|
2494
|
+
}), [ themes, currentTheme, handleThemeChange, handleError, basePath, useMinified, cdnPath ]), isThemeLoaded = useCallback((themeName => loadedThemesRef.current.has(themeName)), []), preloadTheme = useCallback((async themeName => {
|
|
2495
|
+
if (themes[themeName] && !isThemeLoaded(themeName)) {
|
|
3400
2496
|
setIsLoading(!0);
|
|
3401
2497
|
try {
|
|
3402
|
-
|
|
3403
|
-
const
|
|
3404
|
-
|
|
2498
|
+
// Build CSS path using utility function
|
|
2499
|
+
const cssPath = buildThemePath(themeName, basePath, useMinified, cdnPath);
|
|
2500
|
+
// Preload CSS by fetching it
|
|
2501
|
+
await fetch(cssPath), loadedThemesRef.current.add(themeName);
|
|
3405
2502
|
} catch (err) {
|
|
3406
2503
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
3407
|
-
|
|
2504
|
+
setError(error), handleError(error, themeName);
|
|
3408
2505
|
} finally {
|
|
3409
2506
|
setIsLoading(!1);
|
|
3410
2507
|
}
|
|
3411
2508
|
}
|
|
3412
|
-
}), [
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
2509
|
+
}), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), themeManager = useMemo((() => ({})), []), contextValue = useMemo((() => ({
|
|
2510
|
+
theme: "string" == typeof currentTheme ? currentTheme : "js-theme",
|
|
2511
|
+
activeTheme: activeTheme,
|
|
2512
|
+
setTheme: setTheme,
|
|
2513
|
+
availableThemes: Object.entries(themes).map((([name, metadata]) => ({
|
|
2514
|
+
...metadata
|
|
2515
|
+
}))),
|
|
2516
|
+
isLoading: isLoading,
|
|
2517
|
+
error: error,
|
|
2518
|
+
isThemeLoaded: isThemeLoaded,
|
|
2519
|
+
preloadTheme: preloadTheme,
|
|
2520
|
+
themeManager: themeManager
|
|
2521
|
+
})), [ currentTheme, activeTheme, setTheme, themes, isLoading, error, isThemeLoaded, preloadTheme, themeManager ]);
|
|
2522
|
+
// Check if theme is loaded
|
|
2523
|
+
return jsx(ThemeContext.Provider, {
|
|
2524
|
+
value: contextValue,
|
|
2525
|
+
children: children
|
|
2526
|
+
});
|
|
2527
|
+
};
|
|
2528
|
+
|
|
2529
|
+
/**
|
|
2530
|
+
* useTheme Hook
|
|
2531
|
+
*
|
|
2532
|
+
* React hook for accessing theme context
|
|
2533
|
+
*/
|
|
2534
|
+
/**
|
|
2535
|
+
* useTheme hook
|
|
2536
|
+
*
|
|
2537
|
+
* Access theme context and theme management functions
|
|
2538
|
+
*
|
|
2539
|
+
* @example
|
|
2540
|
+
* ```tsx
|
|
2541
|
+
* function MyComponent() {
|
|
2542
|
+
* const { theme, setTheme, availableThemes } = useTheme();
|
|
2543
|
+
*
|
|
2544
|
+
* return (
|
|
2545
|
+
* <div>
|
|
2546
|
+
* <p>Current theme: {theme}</p>
|
|
2547
|
+
* <button onClick={() => setTheme('dark-theme')}>
|
|
2548
|
+
* Switch to Dark
|
|
2549
|
+
* </button>
|
|
2550
|
+
* </div>
|
|
2551
|
+
* );
|
|
2552
|
+
* }
|
|
2553
|
+
* ```
|
|
2554
|
+
*/ function useTheme() {
|
|
2555
|
+
const context = useContext(ThemeContext);
|
|
2556
|
+
if (!context) throw new Error("useTheme must be used within a ThemeProvider");
|
|
2557
|
+
return {
|
|
2558
|
+
theme: context.theme,
|
|
2559
|
+
activeTheme: context.activeTheme,
|
|
2560
|
+
setTheme: context.setTheme,
|
|
2561
|
+
availableThemes: context.availableThemes,
|
|
2562
|
+
isLoading: context.isLoading,
|
|
2563
|
+
error: context.error,
|
|
2564
|
+
isThemeLoaded: context.isThemeLoaded,
|
|
2565
|
+
preloadTheme: context.preloadTheme
|
|
2566
|
+
};
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
function useThemeTokens() {
|
|
2570
|
+
const {theme: theme, activeTheme: activeTheme} = useTheme(), getToken = useCallback(((tokenName, fallback) => {
|
|
2571
|
+
if ("undefined" == typeof window) return fallback || "";
|
|
2572
|
+
const cssVarName = `--atomix-${tokenName}`;
|
|
2573
|
+
return getComputedStyle(document.documentElement).getPropertyValue(cssVarName).trim() || fallback || "";
|
|
2574
|
+
}), []), getThemeValue = useCallback(((path, fallback) => {
|
|
2575
|
+
var _context;
|
|
2576
|
+
return activeTheme && _reduceInstanceProperty(_context = path.split(".")).call(_context, ((obj, key) => obj?.[key]), activeTheme) || fallback;
|
|
2577
|
+
// Navigate through nested theme object using dot notation
|
|
2578
|
+
}), [ activeTheme ]);
|
|
2579
|
+
// Helper function to get CSS variable value
|
|
2580
|
+
// Return unified API for accessing theme values
|
|
2581
|
+
return {
|
|
2582
|
+
theme: theme,
|
|
2583
|
+
activeTheme: activeTheme,
|
|
2584
|
+
getToken: getToken,
|
|
2585
|
+
getThemeValue: getThemeValue,
|
|
2586
|
+
// Commonly used tokens with fallbacks
|
|
2587
|
+
colors: {
|
|
2588
|
+
primary: getToken("primary", "#3b82f6"),
|
|
2589
|
+
secondary: getToken("secondary", "#10b981"),
|
|
2590
|
+
error: getToken("error", "#ef4444"),
|
|
2591
|
+
success: getToken("success", "#22c55e"),
|
|
2592
|
+
warning: getToken("warning", "#eab308"),
|
|
2593
|
+
info: getToken("info", "#3b82f6"),
|
|
2594
|
+
light: getToken("light", "#f9fafb"),
|
|
2595
|
+
dark: getToken("dark", "#111827")
|
|
2596
|
+
},
|
|
2597
|
+
spacing: {
|
|
2598
|
+
1: getToken("spacing-1", "0.25rem"),
|
|
2599
|
+
2: getToken("spacing-2", "0.5rem"),
|
|
2600
|
+
3: getToken("spacing-3", "0.75rem"),
|
|
2601
|
+
4: getToken("spacing-4", "1rem"),
|
|
2602
|
+
5: getToken("spacing-5", "1.25rem"),
|
|
2603
|
+
6: getToken("spacing-6", "1.5rem"),
|
|
2604
|
+
8: getToken("spacing-8", "2rem"),
|
|
2605
|
+
10: getToken("spacing-10", "2.5rem"),
|
|
2606
|
+
12: getToken("spacing-12", "3rem"),
|
|
2607
|
+
16: getToken("spacing-16", "4rem"),
|
|
2608
|
+
20: getToken("spacing-20", "5rem")
|
|
2609
|
+
},
|
|
2610
|
+
borderRadius: {
|
|
2611
|
+
sm: getToken("border-radius-sm", "0.25rem"),
|
|
2612
|
+
md: getToken("border-radius-md", "0.5rem"),
|
|
2613
|
+
lg: getToken("border-radius-lg", "0.75rem"),
|
|
2614
|
+
xl: getToken("border-radius-xl", "1rem"),
|
|
2615
|
+
full: getToken("border-radius-full", "9999px")
|
|
2616
|
+
},
|
|
2617
|
+
typography: {
|
|
2618
|
+
fontFamily: {
|
|
2619
|
+
sans: getToken("font-sans-serif", "Inter, system-ui, sans-serif"),
|
|
2620
|
+
serif: getToken("font-serif", "Georgia, serif"),
|
|
2621
|
+
mono: getToken("font-monospace", "Fira Code, monospace")
|
|
2622
|
+
},
|
|
2623
|
+
fontSize: {
|
|
2624
|
+
xs: getToken("font-size-xs", "0.75rem"),
|
|
2625
|
+
sm: getToken("font-size-sm", "0.875rem"),
|
|
2626
|
+
md: getToken("font-size-md", "1rem"),
|
|
2627
|
+
lg: getToken("font-size-lg", "1.125rem"),
|
|
2628
|
+
xl: getToken("font-size-xl", "1.25rem"),
|
|
2629
|
+
"2xl": getToken("font-size-2xl", "1.5rem"),
|
|
2630
|
+
"3xl": getToken("font-size-3xl", "1.875rem"),
|
|
2631
|
+
"4xl": getToken("font-size-4xl", "2.25rem")
|
|
2632
|
+
},
|
|
2633
|
+
fontWeight: {
|
|
2634
|
+
light: getToken("font-weight-light", "300"),
|
|
2635
|
+
normal: getToken("font-weight-normal", "400"),
|
|
2636
|
+
medium: getToken("font-weight-medium", "500"),
|
|
2637
|
+
semibold: getToken("font-weight-semibold", "600"),
|
|
2638
|
+
bold: getToken("font-weight-bold", "700")
|
|
3437
2639
|
}
|
|
3438
|
-
}
|
|
2640
|
+
},
|
|
2641
|
+
shadows: {
|
|
2642
|
+
sm: getToken("box-shadow-sm", "0 1px 2px 0 rgba(0, 0, 0, 0.05)"),
|
|
2643
|
+
md: getToken("box-shadow-md", "0 4px 6px -1px rgba(0, 0, 0, 0.1)"),
|
|
2644
|
+
lg: getToken("box-shadow-lg", "0 10px 15px -3px rgba(0, 0, 0, 0.1)"),
|
|
2645
|
+
xl: getToken("box-shadow-xl", "0 20px 25px -5px rgba(0, 0, 0, 0.1)"),
|
|
2646
|
+
inset: getToken("box-shadow-inset", "inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)")
|
|
2647
|
+
},
|
|
2648
|
+
transitions: {
|
|
2649
|
+
fast: getToken("transition-fast", "150ms"),
|
|
2650
|
+
base: getToken("transition-base", "200ms"),
|
|
2651
|
+
slow: getToken("transition-slow", "300ms")
|
|
2652
|
+
}
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
/**
|
|
2657
|
+
* Theme System Error Handling
|
|
2658
|
+
*
|
|
2659
|
+
* Centralized error handling for the Atomix theme system.
|
|
2660
|
+
* Provides custom error classes and logging utilities.
|
|
2661
|
+
*/
|
|
2662
|
+
/**
|
|
2663
|
+
* Theme error codes
|
|
2664
|
+
*/ var ThemeErrorCode, LogLevel;
|
|
2665
|
+
|
|
2666
|
+
!function(ThemeErrorCode) {
|
|
2667
|
+
/** Theme not found in registry */
|
|
2668
|
+
ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
|
|
2669
|
+
/** Theme failed to load */
|
|
2670
|
+
ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
|
|
2671
|
+
/** Theme validation failed */
|
|
2672
|
+
ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
|
|
2673
|
+
/** Configuration loading failed */
|
|
2674
|
+
ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
|
|
2675
|
+
/** Configuration validation failed */
|
|
2676
|
+
ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
|
|
2677
|
+
/** Circular dependency detected */
|
|
2678
|
+
ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
|
|
2679
|
+
/** Missing dependency */
|
|
2680
|
+
ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
|
|
2681
|
+
/** Storage operation failed */
|
|
2682
|
+
ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
|
|
2683
|
+
/** Invalid theme name */
|
|
2684
|
+
ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
|
|
2685
|
+
/** CSS injection failed */
|
|
2686
|
+
ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
|
|
2687
|
+
/** Unknown error */
|
|
2688
|
+
ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
|
2689
|
+
}(ThemeErrorCode || (ThemeErrorCode = {}));
|
|
2690
|
+
|
|
2691
|
+
/**
|
|
2692
|
+
* Custom error class for theme-related errors
|
|
2693
|
+
*/
|
|
2694
|
+
class ThemeError extends Error {
|
|
2695
|
+
constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
|
|
2696
|
+
super(message), this.name = "ThemeError", this.code = code, this.context = context,
|
|
2697
|
+
this.timestamp = Date.now(),
|
|
2698
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
2699
|
+
Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Convert error to JSON for logging
|
|
2703
|
+
*/ toJSON() {
|
|
2704
|
+
return {
|
|
2705
|
+
name: this.name,
|
|
2706
|
+
message: this.message,
|
|
2707
|
+
code: this.code,
|
|
2708
|
+
context: this.context,
|
|
2709
|
+
timestamp: this.timestamp,
|
|
2710
|
+
stack: this.stack
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
/**
|
|
2716
|
+
* Log level
|
|
2717
|
+
*/ !function(LogLevel) {
|
|
2718
|
+
LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
|
|
2719
|
+
LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
|
|
2720
|
+
}(LogLevel || (LogLevel = {}));
|
|
2721
|
+
|
|
2722
|
+
/**
|
|
2723
|
+
* Theme Logger
|
|
2724
|
+
*
|
|
2725
|
+
* Centralized logging for the theme system.
|
|
2726
|
+
* Replaces console statements with structured logging.
|
|
2727
|
+
*/
|
|
2728
|
+
class ThemeLogger {
|
|
2729
|
+
constructor(config = {}) {
|
|
2730
|
+
this.config = {
|
|
2731
|
+
level: config.level ?? ("undefined" != typeof process && "production" === process.env?.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
|
|
2732
|
+
enableConsole: config.enableConsole ?? !0,
|
|
2733
|
+
onError: config.onError,
|
|
2734
|
+
onWarn: config.onWarn,
|
|
2735
|
+
onInfo: config.onInfo,
|
|
2736
|
+
onDebug: config.onDebug
|
|
2737
|
+
};
|
|
3439
2738
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
}
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Log an error
|
|
2741
|
+
*/ error(message, error, context) {
|
|
2742
|
+
if (this.config.level < LogLevel.ERROR) return;
|
|
2743
|
+
const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
|
|
2744
|
+
this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
|
|
2745
|
+
error: errorObj,
|
|
2746
|
+
context: {
|
|
2747
|
+
...context,
|
|
2748
|
+
...themeError.context
|
|
2749
|
+
},
|
|
2750
|
+
code: themeError.code
|
|
2751
|
+
}), this.config.onError?.(themeError, context);
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Log a warning
|
|
2755
|
+
*/ warn(message, context) {
|
|
2756
|
+
this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
|
|
2757
|
+
this.config.onWarn?.(message, context));
|
|
2758
|
+
}
|
|
2759
|
+
/**
|
|
2760
|
+
* Log an info message
|
|
2761
|
+
*/ info(message, context) {
|
|
2762
|
+
this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
|
|
2763
|
+
this.config.onInfo?.(message, context));
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Log a debug message
|
|
2767
|
+
*/ debug(message, context) {
|
|
2768
|
+
this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
3471
2771
|
|
|
3472
2772
|
/**
|
|
3473
|
-
*
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
*/
|
|
2773
|
+
* Default logger instance
|
|
2774
|
+
*/ let defaultLogger = null;
|
|
2775
|
+
|
|
3477
2776
|
/**
|
|
3478
|
-
*
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
*
|
|
3482
|
-
* @example
|
|
3483
|
-
* ```tsx
|
|
3484
|
-
* function MyComponent() {
|
|
3485
|
-
* const { theme, setTheme, availableThemes } = useTheme();
|
|
3486
|
-
*
|
|
3487
|
-
* return (
|
|
3488
|
-
* <div>
|
|
3489
|
-
* <p>Current theme: {theme}</p>
|
|
3490
|
-
* <button onClick={() => setTheme('dark-theme')}>
|
|
3491
|
-
* Switch to Dark
|
|
3492
|
-
* </button>
|
|
3493
|
-
* </div>
|
|
3494
|
-
* );
|
|
3495
|
-
* }
|
|
3496
|
-
* ```
|
|
3497
|
-
*/ function useTheme() {
|
|
3498
|
-
const context = useContext(ThemeContext);
|
|
3499
|
-
if (!context) throw new Error("useTheme must be used within a ThemeProvider");
|
|
3500
|
-
return {
|
|
3501
|
-
theme: context.theme,
|
|
3502
|
-
activeTheme: context.activeTheme,
|
|
3503
|
-
setTheme: context.setTheme,
|
|
3504
|
-
availableThemes: context.availableThemes,
|
|
3505
|
-
isLoading: context.isLoading,
|
|
3506
|
-
error: context.error,
|
|
3507
|
-
isThemeLoaded: context.isThemeLoaded,
|
|
3508
|
-
preloadTheme: context.preloadTheme
|
|
3509
|
-
};
|
|
2777
|
+
* Get or create default logger
|
|
2778
|
+
*/ function getLogger() {
|
|
2779
|
+
return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
|
|
3510
2780
|
}
|
|
3511
2781
|
|
|
3512
2782
|
/**
|
|
@@ -3557,7 +2827,7 @@ class ThemeApplicator {
|
|
|
3557
2827
|
},
|
|
3558
2828
|
children: JSON.stringify(context, null, 2)
|
|
3559
2829
|
}) ]
|
|
3560
|
-
}), "development" === process.env
|
|
2830
|
+
}), ("undefined" == typeof process || "development" === process.env?.NODE_ENV) && errorInfo && jsxs("details", {
|
|
3561
2831
|
style: {
|
|
3562
2832
|
marginTop: "1rem"
|
|
3563
2833
|
},
|
|
@@ -3653,6 +2923,130 @@ class ThemeApplicator {
|
|
|
3653
2923
|
}
|
|
3654
2924
|
}
|
|
3655
2925
|
|
|
2926
|
+
/**
|
|
2927
|
+
* Theme Applicator
|
|
2928
|
+
*
|
|
2929
|
+
* Applies theme configurations to the DOM, including CSS variables,
|
|
2930
|
+
* component overrides, typography, spacing, and color palettes.
|
|
2931
|
+
*
|
|
2932
|
+
* Uses the unified theme system for CSS generation and injection.
|
|
2933
|
+
*/
|
|
2934
|
+
/**
|
|
2935
|
+
* Theme applicator class for runtime theme application
|
|
2936
|
+
*
|
|
2937
|
+
* Uses the unified theme system for efficient CSS variable generation and injection.
|
|
2938
|
+
*/ class ThemeApplicator {
|
|
2939
|
+
constructor(root = document.documentElement) {
|
|
2940
|
+
this.styleId = "atomix-theme-applicator", this.root = root;
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Apply a complete theme configuration
|
|
2944
|
+
*
|
|
2945
|
+
* Uses the unified theme system to convert Theme to DesignTokens and inject CSS.
|
|
2946
|
+
* Automatically respects atomix.config.ts when using DesignTokens.
|
|
2947
|
+
*/ applyTheme(theme) {
|
|
2948
|
+
// Clear previously applied variables
|
|
2949
|
+
this.clearAppliedVars(),
|
|
2950
|
+
// Check if it's DesignTokens
|
|
2951
|
+
this.isDesignTokens(theme) ?
|
|
2952
|
+
// Direct DesignTokens - use unified theme system (with config support)
|
|
2953
|
+
this.applyDesignTokens(theme) : injectCSS$1(createTheme(theme, {
|
|
2954
|
+
selector: ":root",
|
|
2955
|
+
prefix: "atomix"
|
|
2956
|
+
}), this.styleId),
|
|
2957
|
+
// Apply component overrides (only for Theme objects)
|
|
2958
|
+
!this.isDesignTokens(theme) && theme.components && this.applyComponentOverrides(theme.components);
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Apply DesignTokens using unified theme system
|
|
2962
|
+
*
|
|
2963
|
+
* Uses createTheme() which automatically loads from atomix.config.ts
|
|
2964
|
+
* if no tokens are provided, ensuring config is always respected.
|
|
2965
|
+
*/ applyDesignTokens(tokens) {
|
|
2966
|
+
// Inject CSS into DOM
|
|
2967
|
+
injectCSS$1(createTheme(tokens, {
|
|
2968
|
+
selector: ":root",
|
|
2969
|
+
prefix: "atomix"
|
|
2970
|
+
}), this.styleId);
|
|
2971
|
+
}
|
|
2972
|
+
/**
|
|
2973
|
+
* Check if object is DesignTokens
|
|
2974
|
+
*/ isDesignTokens(obj) {
|
|
2975
|
+
// DesignTokens is a flat object with string keys, no nested structures
|
|
2976
|
+
return null !== obj && "object" == typeof obj && !("palette" in obj) && !("typography" in obj) && !("__isJSTheme" in obj);
|
|
2977
|
+
}
|
|
2978
|
+
/**
|
|
2979
|
+
* Apply global CSS variables (for component overrides)
|
|
2980
|
+
*/ applyGlobalCSSVars(vars) {
|
|
2981
|
+
Object.entries(vars).forEach((([key, value]) => {
|
|
2982
|
+
this.root.style.setProperty(key, String(value));
|
|
2983
|
+
}));
|
|
2984
|
+
}
|
|
2985
|
+
/**
|
|
2986
|
+
* Apply component-level overrides
|
|
2987
|
+
*/ applyComponentOverrides(overrides) {
|
|
2988
|
+
Object.entries(overrides).forEach((([componentName, override]) => {
|
|
2989
|
+
override && this.applyComponentOverride(componentName, override);
|
|
2990
|
+
}));
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Apply override for a specific component
|
|
2994
|
+
*/ applyComponentOverride(componentName, override) {
|
|
2995
|
+
const vars = {}, componentKey = componentName.toLowerCase();
|
|
2996
|
+
// Apply component-level CSS variables
|
|
2997
|
+
override.cssVars && Object.entries(override.cssVars).forEach((([key, value]) => {
|
|
2998
|
+
// If key doesn't start with --, add component prefix
|
|
2999
|
+
const varKey = key.startsWith("--") ? key : `--atomix-${componentKey}-${key}`;
|
|
3000
|
+
vars[varKey] = value;
|
|
3001
|
+
})),
|
|
3002
|
+
// Apply part-specific CSS variables
|
|
3003
|
+
override.parts && Object.entries(override.parts).forEach((([partName, partOverride]) => {
|
|
3004
|
+
partOverride.cssVars && Object.entries(partOverride.cssVars).forEach((([key, value]) => {
|
|
3005
|
+
const varKey = key.startsWith("--") ? key : `--atomix-${componentKey}-${partName}-${key}`;
|
|
3006
|
+
vars[varKey] = value;
|
|
3007
|
+
}));
|
|
3008
|
+
})),
|
|
3009
|
+
// Apply variant-specific CSS variables
|
|
3010
|
+
override.variants && Object.entries(override.variants).forEach((([variantName, variantOverride]) => {
|
|
3011
|
+
variantOverride.cssVars && Object.entries(variantOverride.cssVars).forEach((([key, value]) => {
|
|
3012
|
+
const varKey = key.startsWith("--") ? key : `--atomix-${componentKey}-${variantName}-${key}`;
|
|
3013
|
+
vars[varKey] = value;
|
|
3014
|
+
}));
|
|
3015
|
+
})), this.applyGlobalCSSVars(vars);
|
|
3016
|
+
}
|
|
3017
|
+
/**
|
|
3018
|
+
* Clear all applied CSS variables
|
|
3019
|
+
*/ clearAppliedVars() {
|
|
3020
|
+
removeCSS(this.styleId);
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Remove theme application
|
|
3024
|
+
*/ removeTheme() {
|
|
3025
|
+
this.clearAppliedVars(), removeCSS(this.styleId);
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* Update specific CSS variables without clearing all
|
|
3029
|
+
*/ updateCSSVars(vars) {
|
|
3030
|
+
this.applyGlobalCSSVars(vars);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
/**
|
|
3035
|
+
* Global theme applicator instance
|
|
3036
|
+
*/ let globalApplicator = null;
|
|
3037
|
+
|
|
3038
|
+
/**
|
|
3039
|
+
* Get or create global theme applicator
|
|
3040
|
+
*/ function getThemeApplicator() {
|
|
3041
|
+
return globalApplicator || (globalApplicator = new ThemeApplicator), globalApplicator;
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
/**
|
|
3045
|
+
* Apply theme using global applicator
|
|
3046
|
+
*/ function applyTheme(theme) {
|
|
3047
|
+
getThemeApplicator().applyTheme(theme);
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3656
3050
|
const VIEWPORT_PRESETS = {
|
|
3657
3051
|
mobile: {
|
|
3658
3052
|
width: 375,
|
|
@@ -5601,62 +4995,6 @@ const ThemeLiveEditor = ({initialTheme: initialTheme, onChange: onChange, classN
|
|
|
5601
4995
|
});
|
|
5602
4996
|
};
|
|
5603
4997
|
|
|
5604
|
-
/**
|
|
5605
|
-
* CSS Variable Mapper
|
|
5606
|
-
*
|
|
5607
|
-
* Utilities for generating and managing CSS custom properties from SCSS tokens
|
|
5608
|
-
* and component configurations.
|
|
5609
|
-
*/
|
|
5610
|
-
/**
|
|
5611
|
-
* Generate CSS variable name from parts
|
|
5612
|
-
*
|
|
5613
|
-
* @example
|
|
5614
|
-
* generateCSSVariableName('button', 'bg', { prefix: 'atomix' })
|
|
5615
|
-
* // Returns: '--atomix-button-bg'
|
|
5616
|
-
*/ function generateCSSVariableName(component, property, options = {}) {
|
|
5617
|
-
const {prefix: prefix = "atomix", separator: separator = "-", includeComponent: includeComponent = !0} = options, parts = [ prefix ];
|
|
5618
|
-
return includeComponent && parts.push(component), parts.push(property), `--${parts.join(separator)}`;
|
|
5619
|
-
}
|
|
5620
|
-
|
|
5621
|
-
/**
|
|
5622
|
-
* Generate CSS variables object from configuration
|
|
5623
|
-
*
|
|
5624
|
-
* @example
|
|
5625
|
-
* const vars = generateComponentCSSVars({
|
|
5626
|
-
* component: 'button',
|
|
5627
|
-
* properties: { bg: '#000', color: '#fff' }
|
|
5628
|
-
* })
|
|
5629
|
-
* // Returns: { '--atomix-button-bg': '#000', '--atomix-button-color': '#fff' }
|
|
5630
|
-
*/ function generateComponentCSSVars(config, options = {}) {
|
|
5631
|
-
const vars = {}, {component: component, properties: properties, parts: parts, states: states, variants: variants} = config;
|
|
5632
|
-
// Base properties
|
|
5633
|
-
return Object.entries(properties).forEach((([key, value]) => {
|
|
5634
|
-
const varName = generateCSSVariableName(component, key, options);
|
|
5635
|
-
vars[varName] = String(value);
|
|
5636
|
-
})),
|
|
5637
|
-
// Part properties
|
|
5638
|
-
parts && Object.entries(parts).forEach((([partName, partProps]) => {
|
|
5639
|
-
Object.entries(partProps).forEach((([key, value]) => {
|
|
5640
|
-
const varName = generateCSSVariableName(component, `${partName}-${key}`, options);
|
|
5641
|
-
vars[varName] = String(value);
|
|
5642
|
-
}));
|
|
5643
|
-
})),
|
|
5644
|
-
// State properties
|
|
5645
|
-
states && Object.entries(states).forEach((([stateName, stateProps]) => {
|
|
5646
|
-
Object.entries(stateProps).forEach((([key, value]) => {
|
|
5647
|
-
const varName = generateCSSVariableName(component, `${stateName}-${key}`, options);
|
|
5648
|
-
vars[varName] = String(value);
|
|
5649
|
-
}));
|
|
5650
|
-
})),
|
|
5651
|
-
// Variant properties
|
|
5652
|
-
variants && Object.entries(variants).forEach((([variantName, variantProps]) => {
|
|
5653
|
-
Object.entries(variantProps).forEach((([key, value]) => {
|
|
5654
|
-
const varName = generateCSSVariableName(component, `${variantName}-${key}`, options);
|
|
5655
|
-
vars[varName] = String(value);
|
|
5656
|
-
}));
|
|
5657
|
-
})), vars;
|
|
5658
|
-
}
|
|
5659
|
-
|
|
5660
4998
|
/**
|
|
5661
4999
|
* Map SCSS tokens to CSS custom properties
|
|
5662
5000
|
*
|
|
@@ -6022,63 +5360,26 @@ class RTLManager {
|
|
|
6022
5360
|
/**
|
|
6023
5361
|
* Save theme to CSS file
|
|
6024
5362
|
*/ async function saveTheme(css, filePath) {
|
|
6025
|
-
await
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
5363
|
+
await async function(css, filePath) {
|
|
5364
|
+
// Check if in browser environment
|
|
5365
|
+
if ("undefined" != typeof window) throw new Error("saveCSSFile can only be used in Node.js environment. Use injectCSS() for browser environments.");
|
|
5366
|
+
// Dynamic import to avoid bundling Node.js modules in browser builds
|
|
5367
|
+
const fs = await import("fs/promises"), dir = (await import("path")).dirname(filePath);
|
|
5368
|
+
await fs.mkdir(dir, {
|
|
5369
|
+
recursive: !0
|
|
5370
|
+
}),
|
|
5371
|
+
// Write file
|
|
5372
|
+
await fs.writeFile(filePath, css, "utf8");
|
|
5373
|
+
}
|
|
5374
|
+
/**
|
|
5375
|
+
* Theme System Constants
|
|
6030
5376
|
*
|
|
6031
|
-
*
|
|
6032
|
-
* Similar to how Tailwind loads tailwind.config.js
|
|
5377
|
+
* Centralized constants for the theme system to avoid magic numbers and strings.
|
|
6033
5378
|
*/
|
|
6034
|
-
/**
|
|
6035
|
-
*
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
* Falls back to default config if file doesn't exist.
|
|
6039
|
-
*
|
|
6040
|
-
* @param options - Loader options
|
|
6041
|
-
* @returns Loaded configuration or default
|
|
6042
|
-
*
|
|
6043
|
-
* @example
|
|
6044
|
-
* ```typescript
|
|
6045
|
-
* import { loadAtomixConfig } from '@shohojdhara/atomix/config';
|
|
6046
|
-
* import { createTheme } from '@shohojdhara/atomix/theme';
|
|
6047
|
-
*
|
|
6048
|
-
* const config = loadAtomixConfig();
|
|
6049
|
-
* const theme = createTheme(config.theme?.tokens || {});
|
|
6050
|
-
* ```
|
|
6051
|
-
*/ const loader = Object.freeze( Object.defineProperty({
|
|
6052
|
-
__proto__: null,
|
|
6053
|
-
loadAtomixConfig: function(options = {}) {
|
|
6054
|
-
const {configPath: configPath = "atomix.config.ts", required: required = !1} = options, defaultConfig = {
|
|
6055
|
-
prefix: "atomix",
|
|
6056
|
-
theme: {
|
|
6057
|
-
extend: {}
|
|
6058
|
-
}
|
|
6059
|
-
};
|
|
6060
|
-
// Default config
|
|
6061
|
-
// In browser environments, config loading is not supported
|
|
6062
|
-
if ("undefined" != typeof window) {
|
|
6063
|
-
if (required) throw new Error("Config loading not supported in browser environment");
|
|
6064
|
-
return defaultConfig;
|
|
6065
|
-
}
|
|
6066
|
-
// Try to load config file
|
|
6067
|
-
try {
|
|
6068
|
-
// Use dynamic import for ESM compatibility
|
|
6069
|
-
const configModule = require(configPath), config = configModule.default || configModule;
|
|
6070
|
-
// Validate it's an AtomixConfig
|
|
6071
|
-
if (config && "object" == typeof config) return config;
|
|
6072
|
-
throw new Error("Invalid config format");
|
|
6073
|
-
} catch (error) {
|
|
6074
|
-
if (required) throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
|
|
6075
|
-
// Return default config if not required
|
|
6076
|
-
return defaultConfig;
|
|
6077
|
-
}
|
|
6078
|
-
}
|
|
6079
|
-
}, Symbol.toStringTag, {
|
|
6080
|
-
value: "Module"
|
|
6081
|
-
}));
|
|
5379
|
+
/**
|
|
5380
|
+
* Default storage key for theme persistence
|
|
5381
|
+
*/ (css, filePath);
|
|
5382
|
+
}
|
|
6082
5383
|
|
|
6083
|
-
export { RTLManager, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider,
|
|
5384
|
+
export { RTLManager, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeValidator, applyCSSVariables, applyComponentTheme, applyTheme, camelToKebab, clearThemes, createDesignTokensFromTheme, createTheme, createThemeObject, createThemeRegistry, createTokens, cssVarsToStyle, deepMerge, defaultTokens, designTokensToCSSVars, designTokensToTheme, extendTheme, extractComponentName, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, getAllThemes, getCSSVariable, getComponentThemeValue, getDesignTokensFromTheme, getTheme, getThemeApplicator, getThemeCount, getThemeIds, hasTheme, injectCSS$1 as injectCSS, injectTheme, isCSSInjected, isDesignTokens, isThemeObject, isValidCSSVariableName, loadThemeFromConfig, loadThemeFromConfigSync, mapSCSSTokensToCSSVars, mergeCSSVars, mergeTheme, normalizeThemeTokens, registerTheme, removeCSS, removeCSSVariables, removeTheme, saveTheme, themePropertyToCSSVar, themeToDesignTokens, unregisterTheme, useComponentTheme, useHistory, useTheme, useThemeTokens };
|
|
6084
5385
|
//# sourceMappingURL=theme.js.map
|