@shohojdhara/atomix 0.3.10 → 0.3.12
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/CHANGELOG.md +9 -1
- package/dist/atomix.css +9 -6
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +9 -6
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +82 -60
- package/dist/charts.js.map +1 -1
- package/dist/core.js +82 -60
- package/dist/core.js.map +1 -1
- package/dist/forms.js +82 -60
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +82 -60
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +11 -107
- package/dist/index.esm.js +165 -407
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +169 -412
- 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 +1 -32
- package/dist/theme.js +12 -207
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +124 -127
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +28 -32
- package/src/components/AtomixGlass/GlassFilter.tsx +15 -4
- package/src/components/EdgePanel/EdgePanel.stories.tsx +2 -7
- package/src/components/EdgePanel/EdgePanel.tsx +0 -10
- package/src/components/Form/Radio.stories.tsx +235 -103
- package/src/components/Navigation/Nav/NavDropdown.tsx +8 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +2 -22
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +11 -15
- package/src/lib/config/index.ts +5 -5
- package/src/lib/theme/config/index.ts +1 -1
- package/src/lib/theme/core/createTheme.ts +11 -40
- package/src/lib/theme/generators/index.ts +1 -4
- package/src/lib/theme/index.ts +4 -16
- package/src/lib/theme/runtime/ThemeProvider.tsx +1 -16
- package/src/lib/types/components.ts +2 -26
- package/src/styles/06-components/_components.edge-panel.scss +4 -4
- package/src/styles/06-components/_components.nav.scss +3 -0
- package/src/lib/config/loader.ts +0 -147
- package/src/lib/theme/config/__tests__/configLoader.test.ts +0 -207
- package/src/lib/theme/config/configLoader.ts +0 -113
- package/src/lib/theme/config/loader.ts +0 -293
- package/src/lib/theme/generators/cssFile.ts +0 -79
package/src/lib/theme/index.ts
CHANGED
|
@@ -29,7 +29,7 @@ export { createTheme } from './core';
|
|
|
29
29
|
export { deepMerge, mergeTheme, extendTheme } from './core';
|
|
30
30
|
|
|
31
31
|
// Simplified Theme Registry
|
|
32
|
-
export {
|
|
32
|
+
export {
|
|
33
33
|
createThemeRegistry,
|
|
34
34
|
registerTheme,
|
|
35
35
|
unregisterTheme,
|
|
@@ -46,7 +46,7 @@ export {
|
|
|
46
46
|
// ============================================================================
|
|
47
47
|
|
|
48
48
|
import { injectCSS, removeCSS, isCSSInjected } from './utils/injectCSS';
|
|
49
|
-
|
|
49
|
+
// File saving utilities removed to prevent bundling Node.js modules in browser
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Inject theme CSS into DOM
|
|
@@ -62,12 +62,7 @@ export function removeTheme(id: string = 'atomix-theme'): void {
|
|
|
62
62
|
removeCSS(id);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
* Save theme to CSS file
|
|
67
|
-
*/
|
|
68
|
-
export async function saveTheme(css: string, filePath: string): Promise<void> {
|
|
69
|
-
await saveCSSFile(css, filePath);
|
|
70
|
-
}
|
|
65
|
+
|
|
71
66
|
|
|
72
67
|
// ============================================================================
|
|
73
68
|
// Token Utilities
|
|
@@ -113,14 +108,7 @@ export {
|
|
|
113
108
|
export { injectCSS, removeCSS, isCSSInjected } from './utils/injectCSS';
|
|
114
109
|
|
|
115
110
|
|
|
116
|
-
//
|
|
117
|
-
// Config Loader
|
|
118
|
-
// ============================================================================
|
|
119
|
-
|
|
120
|
-
export {
|
|
121
|
-
loadThemeFromConfig,
|
|
122
|
-
loadThemeFromConfigSync,
|
|
123
|
-
} from './config/configLoader';
|
|
111
|
+
// Config loader removed to prevent bundling Node.js modules in browser
|
|
124
112
|
|
|
125
113
|
// ============================================================================
|
|
126
114
|
// React Integration
|
|
@@ -86,22 +86,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|
|
86
86
|
return defaultTheme;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
if (typeof window === 'undefined') {
|
|
91
|
-
try {
|
|
92
|
-
// Dynamically import the config loader to avoid bundling issues in browser
|
|
93
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
94
|
-
const { loadThemeFromConfigSync } = require('../config/configLoader');
|
|
95
|
-
|
|
96
|
-
const configTokens = loadThemeFromConfigSync();
|
|
97
|
-
if (configTokens && Object.keys(configTokens).length > 0) {
|
|
98
|
-
// For simplicity, we'll treat config tokens as a special theme name
|
|
99
|
-
return 'config-theme';
|
|
100
|
-
}
|
|
101
|
-
} catch (error) {
|
|
102
|
-
// Failed to load theme from config, using default
|
|
103
|
-
}
|
|
104
|
-
}
|
|
89
|
+
|
|
105
90
|
|
|
106
91
|
// Default fallback
|
|
107
92
|
return 'default';
|
|
@@ -1418,19 +1418,7 @@ export interface SideMenuProps extends BaseComponentProps {
|
|
|
1418
1418
|
* <SideMenu LinkComponent={Link} />
|
|
1419
1419
|
* ```
|
|
1420
1420
|
*/
|
|
1421
|
-
LinkComponent?: React.
|
|
1422
|
-
href?: string;
|
|
1423
|
-
to?: string;
|
|
1424
|
-
children: React.ReactNode;
|
|
1425
|
-
className?: string;
|
|
1426
|
-
onClick?: (event: React.MouseEvent) => void;
|
|
1427
|
-
target?: string;
|
|
1428
|
-
rel?: string;
|
|
1429
|
-
'aria-disabled'?: boolean;
|
|
1430
|
-
'aria-current'?: string;
|
|
1431
|
-
tabIndex?: number;
|
|
1432
|
-
ref?: React.Ref<HTMLAnchorElement>;
|
|
1433
|
-
}>;
|
|
1421
|
+
LinkComponent?: React.ElementType;
|
|
1434
1422
|
|
|
1435
1423
|
/**
|
|
1436
1424
|
* Menu items
|
|
@@ -1524,19 +1512,7 @@ export interface SideMenuItemProps extends BaseComponentProps {
|
|
|
1524
1512
|
* <SideMenuItem href="/about" LinkComponent={Link}>About</SideMenuItem>
|
|
1525
1513
|
* ```
|
|
1526
1514
|
*/
|
|
1527
|
-
LinkComponent?: React.
|
|
1528
|
-
href?: string;
|
|
1529
|
-
to?: string;
|
|
1530
|
-
children: React.ReactNode;
|
|
1531
|
-
className?: string;
|
|
1532
|
-
onClick?: (event: React.MouseEvent) => void;
|
|
1533
|
-
target?: string;
|
|
1534
|
-
rel?: string;
|
|
1535
|
-
'aria-disabled'?: boolean;
|
|
1536
|
-
'aria-current'?: string;
|
|
1537
|
-
tabIndex?: number;
|
|
1538
|
-
ref?: React.Ref<HTMLAnchorElement>;
|
|
1539
|
-
}>;
|
|
1515
|
+
LinkComponent?: React.ElementType;
|
|
1540
1516
|
}
|
|
1541
1517
|
|
|
1542
1518
|
/**
|
|
@@ -416,7 +416,7 @@
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
// Remove borders for glass variant on all positions
|
|
419
|
-
&.c-edge-panel--start .c-edge-panel__glass-
|
|
419
|
+
&.c-edge-panel--start .c-edge-panel__glass-content {
|
|
420
420
|
animation: slideInStart 0.3s ease forwards;
|
|
421
421
|
|
|
422
422
|
&.is-animating-out {
|
|
@@ -424,7 +424,7 @@
|
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
&.c-edge-panel--end .c-edge-panel__glass-
|
|
427
|
+
&.c-edge-panel--end .c-edge-panel__glass-content {
|
|
428
428
|
animation: slideInEnd 0.3s ease forwards;
|
|
429
429
|
|
|
430
430
|
&.is-animating-out {
|
|
@@ -432,11 +432,11 @@
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
&.c-edge-panel--top .c-edge-panel__glass-
|
|
435
|
+
&.c-edge-panel--top .c-edge-panel__glass-content{
|
|
436
436
|
animation: slideInTop 0.3s ease forwards;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
&.c-edge-panel--bottom .c-edge-panel__glass-
|
|
439
|
+
&.c-edge-panel--bottom .c-edge-panel__glass-content {
|
|
440
440
|
animation: slideInBottom 0.3s ease forwards;
|
|
441
441
|
}
|
|
442
442
|
|
package/src/lib/config/loader.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Atomix Config Loader
|
|
3
|
-
*
|
|
4
|
-
* Helper functions to load atomix.config.ts from external projects.
|
|
5
|
-
* Similar to how Tailwind loads tailwind.config.js
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { AtomixConfig } from './index';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Load Atomix configuration from project root
|
|
12
|
-
*
|
|
13
|
-
* Attempts to load atomix.config.ts from the current working directory.
|
|
14
|
-
* Falls back to default config if file doesn't exist.
|
|
15
|
-
*
|
|
16
|
-
* @param options - Loader options
|
|
17
|
-
* @returns Loaded configuration or default
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* import { loadAtomixConfig } from '@shohojdhara/atomix/config';
|
|
22
|
-
* import { createTheme } from '@shohojdhara/atomix/theme';
|
|
23
|
-
*
|
|
24
|
-
* const config = loadAtomixConfig();
|
|
25
|
-
* const theme = createTheme(config.theme?.tokens || {});
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function loadAtomixConfig(
|
|
29
|
-
options: {
|
|
30
|
-
/** Custom config path (default: 'atomix.config.ts') */
|
|
31
|
-
configPath?: string;
|
|
32
|
-
/** Whether to throw error if config not found (default: false) */
|
|
33
|
-
required?: boolean;
|
|
34
|
-
} = {}
|
|
35
|
-
): AtomixConfig {
|
|
36
|
-
const { configPath = 'atomix.config.ts', required = false } = options;
|
|
37
|
-
|
|
38
|
-
// Default config
|
|
39
|
-
const defaultConfig: AtomixConfig = {
|
|
40
|
-
prefix: 'atomix',
|
|
41
|
-
theme: {
|
|
42
|
-
extend: {},
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// In browser environments, config loading is not supported
|
|
47
|
-
if (typeof window !== 'undefined') {
|
|
48
|
-
if (required) {
|
|
49
|
-
throw new Error('loadAtomixConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.');
|
|
50
|
-
}
|
|
51
|
-
return defaultConfig;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Try to load config file
|
|
55
|
-
try {
|
|
56
|
-
// Use dynamic import for ESM compatibility
|
|
57
|
-
const configModule = require(configPath);
|
|
58
|
-
const config = configModule.default || configModule;
|
|
59
|
-
|
|
60
|
-
// Validate it's an AtomixConfig
|
|
61
|
-
if (config && typeof config === 'object') {
|
|
62
|
-
return config as AtomixConfig;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
throw new Error('Invalid config format');
|
|
66
|
-
} catch (error: any) {
|
|
67
|
-
if (required) {
|
|
68
|
-
throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Return default config if not required
|
|
72
|
-
return defaultConfig;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Resolve config path
|
|
78
|
-
*
|
|
79
|
-
* Finds atomix.config.ts in the project, checking common locations.
|
|
80
|
-
* Returns null in browser environments where file system access is not available.
|
|
81
|
-
*
|
|
82
|
-
* This function is designed to work in Node.js environments only.
|
|
83
|
-
* In browser builds, it will always return null without attempting to access Node.js modules.
|
|
84
|
-
*
|
|
85
|
-
* @internal This function uses Node.js modules and should not be called in browser environments.
|
|
86
|
-
*/
|
|
87
|
-
export function resolveConfigPath(): string | null {
|
|
88
|
-
// Early return for browser environments - prevents any Node.js module access
|
|
89
|
-
// This check happens before any require() calls, preventing bundlers from analyzing them
|
|
90
|
-
if (typeof window !== 'undefined' || typeof process === 'undefined' || !process.cwd) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Only attempt to load Node.js modules in Node.js runtime
|
|
95
|
-
// Use a lazy-loading pattern that prevents static analysis by bundlers
|
|
96
|
-
try {
|
|
97
|
-
// Create a function that only executes in Node.js runtime
|
|
98
|
-
// Use string-based module names to prevent static analysis by bundlers
|
|
99
|
-
const loadNodeModules = () => {
|
|
100
|
-
// These requires are only executed at runtime in Node.js environments
|
|
101
|
-
// They are marked as external in Rollup config and should not be bundled
|
|
102
|
-
// Using string concatenation and computed property access to prevent static analysis
|
|
103
|
-
if (typeof require === 'undefined') {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Use a try-catch wrapper to safely access require
|
|
108
|
-
try {
|
|
109
|
-
// Build module names dynamically to prevent static analysis
|
|
110
|
-
const moduleNames: [string, string] = ['f' + 's', 'p' + 'a' + 't' + 'h'];
|
|
111
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
112
|
-
const fs = require(moduleNames[0]);
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
114
|
-
const path = require(moduleNames[1]);
|
|
115
|
-
return { fs, path };
|
|
116
|
-
} catch {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const modules = loadNodeModules();
|
|
122
|
-
if (!modules) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const { fs, path } = modules;
|
|
127
|
-
const cwd = process.cwd();
|
|
128
|
-
const possiblePaths = [
|
|
129
|
-
path.join(cwd, 'atomix.config.ts'),
|
|
130
|
-
path.join(cwd, 'atomix.config.js'),
|
|
131
|
-
path.join(cwd, 'atomix.config.mjs'),
|
|
132
|
-
];
|
|
133
|
-
|
|
134
|
-
for (const configPath of possiblePaths) {
|
|
135
|
-
if (fs.existsSync(configPath)) {
|
|
136
|
-
return configPath;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
// Silently fail in browser environments or when modules are unavailable
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export default loadAtomixConfig;
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config Loader Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for automatic config loading from atomix.config.ts
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
8
|
-
import { loadThemeFromConfigSync, loadThemeFromConfig } from '../configLoader';
|
|
9
|
-
import { createTheme } from '../../core/createTheme';
|
|
10
|
-
import type { DesignTokens } from '../../tokens/tokens';
|
|
11
|
-
|
|
12
|
-
describe('Config Loader', () => {
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
// Clear any cached configs
|
|
15
|
-
vi.clearAllMocks();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
vi.restoreAllMocks();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('loadThemeFromConfigSync', () => {
|
|
23
|
-
it('should return empty object in browser environment', () => {
|
|
24
|
-
// Mock window object
|
|
25
|
-
const originalWindow = global.window;
|
|
26
|
-
// @ts-expect-error - intentionally setting window for test
|
|
27
|
-
global.window = {};
|
|
28
|
-
|
|
29
|
-
const result = loadThemeFromConfigSync();
|
|
30
|
-
expect(result).toEqual({});
|
|
31
|
-
|
|
32
|
-
// Restore
|
|
33
|
-
global.window = originalWindow;
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should handle missing config file gracefully', () => {
|
|
37
|
-
// In Node.js environment, if config doesn't exist, should return empty object
|
|
38
|
-
const result = loadThemeFromConfigSync('non-existent-config.ts');
|
|
39
|
-
expect(result).toEqual({});
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('loadThemeFromConfig', () => {
|
|
44
|
-
it('should return empty object in browser environment', async () => {
|
|
45
|
-
// Mock window object
|
|
46
|
-
const originalWindow = global.window;
|
|
47
|
-
// @ts-expect-error - intentionally setting window for test
|
|
48
|
-
global.window = {};
|
|
49
|
-
|
|
50
|
-
const result = await loadThemeFromConfig();
|
|
51
|
-
expect(result).toEqual({});
|
|
52
|
-
|
|
53
|
-
// Restore
|
|
54
|
-
global.window = originalWindow;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should handle missing config file gracefully', async () => {
|
|
58
|
-
const result = await loadThemeFromConfig('non-existent-config.ts');
|
|
59
|
-
expect(result).toEqual({});
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('Token Flattening', () => {
|
|
64
|
-
it('should correctly map simple color strings', () => {
|
|
65
|
-
// This tests the internal flattenConfigTokens function indirectly
|
|
66
|
-
// by checking that createTheme can handle config structure
|
|
67
|
-
const mockConfig = {
|
|
68
|
-
theme: {
|
|
69
|
-
extend: {
|
|
70
|
-
colors: {
|
|
71
|
-
primary: '#7AFFD7',
|
|
72
|
-
secondary: '#FF5733',
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Since we can't directly test the private function,
|
|
79
|
-
// we test through createTheme which uses it
|
|
80
|
-
// This is an integration test
|
|
81
|
-
expect(typeof createTheme).toBe('function');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should correctly map palette color objects', () => {
|
|
85
|
-
const mockConfig = {
|
|
86
|
-
theme: {
|
|
87
|
-
extend: {
|
|
88
|
-
colors: {
|
|
89
|
-
primary: {
|
|
90
|
-
main: '#7AFFD7',
|
|
91
|
-
light: '#B3FFE9',
|
|
92
|
-
dark: '#00E6C3',
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// Integration test through createTheme
|
|
100
|
-
expect(typeof createTheme).toBe('function');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should correctly map color scales', () => {
|
|
104
|
-
const mockConfig = {
|
|
105
|
-
theme: {
|
|
106
|
-
extend: {
|
|
107
|
-
colors: {
|
|
108
|
-
primary: {
|
|
109
|
-
1: '#f0f9ff',
|
|
110
|
-
6: '#3b82f6',
|
|
111
|
-
10: '#1e3a8a',
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// Integration test
|
|
119
|
-
expect(typeof createTheme).toBe('function');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should correctly map spacing tokens', () => {
|
|
123
|
-
const mockConfig = {
|
|
124
|
-
theme: {
|
|
125
|
-
extend: {
|
|
126
|
-
spacing: {
|
|
127
|
-
'4': '1rem',
|
|
128
|
-
'8': '2rem',
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// Integration test
|
|
135
|
-
expect(typeof createTheme).toBe('function');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should correctly map typography tokens', () => {
|
|
139
|
-
const mockConfig = {
|
|
140
|
-
theme: {
|
|
141
|
-
extend: {
|
|
142
|
-
typography: {
|
|
143
|
-
fontFamilies: {
|
|
144
|
-
sans: ['Inter', 'sans-serif'],
|
|
145
|
-
},
|
|
146
|
-
fontSizes: {
|
|
147
|
-
'2xl': '1.5rem',
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// Integration test
|
|
155
|
-
expect(typeof createTheme).toBe('function');
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('createTheme with Config Loading', () => {
|
|
161
|
-
it('should work without input (will use defaults if config not available)', () => {
|
|
162
|
-
// createTheme() should work even if config is not available
|
|
163
|
-
// It will fall back to default tokens
|
|
164
|
-
const css = createTheme();
|
|
165
|
-
|
|
166
|
-
expect(typeof css).toBe('string');
|
|
167
|
-
expect(css).toContain(':root');
|
|
168
|
-
expect(css).toContain('--atomix');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should accept DesignTokens input', () => {
|
|
172
|
-
const tokens: Partial<DesignTokens> = {
|
|
173
|
-
'primary': '#7AFFD7',
|
|
174
|
-
'spacing-4': '1rem',
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const css = createTheme(tokens);
|
|
178
|
-
|
|
179
|
-
expect(typeof css).toBe('string');
|
|
180
|
-
expect(css).toContain('--atomix-primary');
|
|
181
|
-
expect(css).toContain('#7AFFD7');
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should respect prefix option', () => {
|
|
185
|
-
const tokens: Partial<DesignTokens> = {
|
|
186
|
-
'primary': '#7AFFD7',
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const css = createTheme(tokens, { prefix: 'myapp' });
|
|
190
|
-
|
|
191
|
-
expect(css).toContain('--myapp-primary');
|
|
192
|
-
expect(css).not.toContain('--atomix-primary');
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('should respect selector option', () => {
|
|
196
|
-
const tokens: Partial<DesignTokens> = {
|
|
197
|
-
'primary': '#7AFFD7',
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const css = createTheme(tokens, { selector: '[data-theme="dark"]' });
|
|
201
|
-
|
|
202
|
-
expect(css).toContain('[data-theme="dark"]');
|
|
203
|
-
expect(css).not.toContain(':root');
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Theme Configuration Loader
|
|
4
|
-
*
|
|
5
|
-
* Provides functions to load theme configurations from atomix.config.ts
|
|
6
|
-
* Includes both sync and async versions, with automatic fallbacks
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { DesignTokens } from '../tokens/tokens';
|
|
10
|
-
import { createTokens } from '../tokens/tokens';
|
|
11
|
-
import { loadAtomixConfig as loadAtomixConfigStatic } from '../../config/loader';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Load theme from config file (synchronous, Node.js only)
|
|
15
|
-
* @param configPath - Path to config file (default: atomix.config.ts)
|
|
16
|
-
* @returns DesignTokens from theme configuration
|
|
17
|
-
* @throws Error if config loading is not available in browser environment
|
|
18
|
-
*/
|
|
19
|
-
export function loadThemeFromConfigSync(options?: { configPath?: string; required?: boolean }): DesignTokens {
|
|
20
|
-
// Check if we're in a browser environment
|
|
21
|
-
if (typeof window !== 'undefined') {
|
|
22
|
-
throw new Error('loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Use static import - the function handles browser environment checks internally
|
|
26
|
-
let config;
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
config = loadAtomixConfigStatic({
|
|
30
|
-
configPath: options?.configPath || 'atomix.config.ts',
|
|
31
|
-
required: options?.required !== false,
|
|
32
|
-
});
|
|
33
|
-
} catch (error) {
|
|
34
|
-
if (options?.required !== false) {
|
|
35
|
-
throw new Error('Config loader module not available');
|
|
36
|
-
}
|
|
37
|
-
// Return empty tokens if config is not required
|
|
38
|
-
return createTokens({});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!config?.theme) {
|
|
42
|
-
return createTokens({});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Extract tokens from config.theme structure
|
|
46
|
-
// config.theme can have: { extend?: ThemeTokens, tokens?: ThemeTokens, themes?: ... }
|
|
47
|
-
// We need to extract the actual DesignTokens (flat structure)
|
|
48
|
-
const themeConfig = config.theme;
|
|
49
|
-
|
|
50
|
-
// Check if theme is directly a flat object (DesignTokens format)
|
|
51
|
-
// This handles the case where config.theme might be passed as DesignTokens directly
|
|
52
|
-
if (themeConfig && typeof themeConfig === 'object' && !('extend' in themeConfig) && !('tokens' in themeConfig) && !('themes' in themeConfig)) {
|
|
53
|
-
// It's likely already a flat DesignTokens object
|
|
54
|
-
return createTokens(themeConfig as Partial<DesignTokens>);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// If theme has nested structure (extend/tokens/themes), we can't directly use it
|
|
58
|
-
// Return empty tokens - the theme system will use defaults
|
|
59
|
-
// TODO: Add proper conversion from ThemeTokens to DesignTokens if needed
|
|
60
|
-
return createTokens({});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Load theme from config file (asynchronous)
|
|
65
|
-
* @param configPath - Path to config file (default: atomix.config.ts)
|
|
66
|
-
* @returns Promise resolving to DesignTokens from theme configuration
|
|
67
|
-
*/
|
|
68
|
-
export async function loadThemeFromConfig(options?: { configPath?: string; required?: boolean }): Promise<DesignTokens> {
|
|
69
|
-
// Check if we're in a browser environment
|
|
70
|
-
if (typeof window !== 'undefined') {
|
|
71
|
-
throw new Error('loadThemeFromConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Use static import with runtime check
|
|
75
|
-
// The function will handle browser environment checks internally
|
|
76
|
-
let config;
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
// loadAtomixConfig is synchronous, not async
|
|
80
|
-
config = loadAtomixConfigStatic({
|
|
81
|
-
configPath: options?.configPath || 'atomix.config.ts',
|
|
82
|
-
required: options?.required !== false,
|
|
83
|
-
});
|
|
84
|
-
} catch (error) {
|
|
85
|
-
if (options?.required !== false) {
|
|
86
|
-
throw new Error('Config loader module not available');
|
|
87
|
-
}
|
|
88
|
-
// Return empty tokens if config is not required
|
|
89
|
-
return createTokens({});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (!config?.theme) {
|
|
93
|
-
return createTokens({});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Extract tokens from config.theme structure
|
|
97
|
-
// config.theme can have: { extend?: ThemeTokens, tokens?: ThemeTokens, themes?: ... }
|
|
98
|
-
// We need to extract the actual DesignTokens (flat structure)
|
|
99
|
-
const themeConfig = config.theme;
|
|
100
|
-
|
|
101
|
-
// Check if theme is directly a flat object (DesignTokens format)
|
|
102
|
-
// This handles the case where config.theme might be passed as DesignTokens directly
|
|
103
|
-
if (themeConfig && typeof themeConfig === 'object' && !('extend' in themeConfig) && !('tokens' in themeConfig) && !('themes' in themeConfig)) {
|
|
104
|
-
// It's likely already a flat DesignTokens object
|
|
105
|
-
return createTokens(themeConfig as Partial<DesignTokens>);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// If theme has nested structure (extend/tokens/themes), we can't directly use it
|
|
109
|
-
// Return empty tokens - the theme system will use defaults
|
|
110
|
-
// TODO: Add proper conversion from ThemeTokens to DesignTokens if needed
|
|
111
|
-
return createTokens({});
|
|
112
|
-
}
|
|
113
|
-
|