@shohojdhara/atomix 0.3.7 → 0.3.8
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/dist/atomix.css +77 -0
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +77 -0
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +2 -2
- package/dist/core.js.map +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +578 -515
- package/dist/index.esm.js +3157 -2626
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +10496 -9973
- 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 +237 -420
- package/dist/theme.js +1629 -1701
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DataTable/DataTable.stories.tsx +238 -0
- package/src/components/DataTable/DataTable.test.tsx +450 -0
- package/src/components/DataTable/DataTable.tsx +384 -61
- package/src/components/DatePicker/DatePicker.tsx +29 -38
- package/src/components/Upload/Upload.tsx +539 -40
- package/src/lib/composables/useDataTable.ts +355 -15
- package/src/lib/composables/useDatePicker.ts +19 -0
- package/src/lib/constants/components.ts +10 -0
- package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
- package/src/lib/theme/adapters/index.ts +1 -4
- package/src/lib/theme/config/configLoader.ts +53 -35
- package/src/lib/theme/core/composeTheme.ts +22 -30
- package/src/lib/theme/core/createTheme.ts +49 -26
- package/src/lib/theme/core/index.ts +0 -1
- package/src/lib/theme/generators/generateCSSNested.ts +4 -3
- package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
- package/src/lib/theme/index.ts +10 -17
- package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +3 -3
- package/src/lib/theme/runtime/ThemeProvider.tsx +186 -44
- package/src/lib/theme/runtime/useTheme.ts +1 -1
- package/src/lib/theme/runtime/useThemeTokens.ts +7 -16
- package/src/lib/theme/test/testTheme.ts +2 -1
- package/src/lib/theme/types.ts +14 -14
- package/src/lib/theme/utils/componentTheming.ts +35 -27
- package/src/lib/theme/utils/domUtils.ts +57 -15
- package/src/lib/theme/utils/injectCSS.ts +0 -1
- package/src/lib/theme/utils/themeHelpers.ts +1 -39
- package/src/lib/theme/utils/themeUtils.ts +1 -170
- package/src/lib/types/components.ts +145 -0
- package/src/lib/utils/dataTableExport.ts +143 -0
- package/src/styles/06-components/_components.data-table.scss +95 -0
- package/src/lib/hooks/useThemeTokens.ts +0 -105
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* Includes both sync and async versions, with automatic fallbacks
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { Theme } from '../types';
|
|
10
9
|
import type { DesignTokens } from '../tokens/tokens';
|
|
11
10
|
import { createTokens } from '../tokens/tokens';
|
|
12
|
-
import {
|
|
11
|
+
import { loadAtomixConfig as loadAtomixConfigStatic } from '../../config/loader';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Load theme from config file (synchronous, Node.js only)
|
|
@@ -23,14 +22,14 @@ export function loadThemeFromConfigSync(options?: { configPath?: string; require
|
|
|
23
22
|
throw new Error('loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.');
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
// Use
|
|
27
|
-
|
|
28
|
-
let loadAtomixConfig: any;
|
|
25
|
+
// Use static import - the function handles browser environment checks internally
|
|
26
|
+
let config;
|
|
29
27
|
|
|
30
28
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
config = loadAtomixConfigStatic({
|
|
30
|
+
configPath: options?.configPath || 'atomix.config.ts',
|
|
31
|
+
required: options?.required !== false,
|
|
32
|
+
});
|
|
34
33
|
} catch (error) {
|
|
35
34
|
if (options?.required !== false) {
|
|
36
35
|
throw new Error('Config loader module not available');
|
|
@@ -39,20 +38,26 @@ export function loadThemeFromConfigSync(options?: { configPath?: string; require
|
|
|
39
38
|
return createTokens({});
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
const config = loadAtomixConfig({
|
|
43
|
-
configPath: options?.configPath || 'atomix.config.ts',
|
|
44
|
-
required: options?.required !== false,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
41
|
if (!config?.theme) {
|
|
48
42
|
return createTokens({});
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
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>);
|
|
53
55
|
}
|
|
54
|
-
|
|
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({});
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
/**
|
|
@@ -66,30 +71,43 @@ export async function loadThemeFromConfig(options?: { configPath?: string; requi
|
|
|
66
71
|
throw new Error('loadThemeFromConfig: Not available in browser environment. Config loading requires Node.js/SSR environment.');
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
//
|
|
70
|
-
|
|
74
|
+
// Use static import with runtime check
|
|
75
|
+
// The function will handle browser environment checks internally
|
|
76
|
+
let config;
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
+
}
|
|
76
91
|
|
|
77
92
|
if (!config?.theme) {
|
|
78
93
|
return createTokens({});
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
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>);
|
|
83
106
|
}
|
|
84
|
-
|
|
85
|
-
|
|
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({});
|
|
86
112
|
}
|
|
87
113
|
|
|
88
|
-
/**
|
|
89
|
-
* Check if the provided object is a Theme object
|
|
90
|
-
* @param theme - Object to check
|
|
91
|
-
* @returns True if the object is a Theme object, false otherwise
|
|
92
|
-
*/
|
|
93
|
-
function isThemeObject(theme: any): theme is Theme {
|
|
94
|
-
return typeof theme === 'object' && theme !== null;
|
|
95
|
-
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme Composition Utilities
|
|
3
3
|
*
|
|
4
|
-
* Simplified utilities for composing
|
|
4
|
+
* Simplified utilities for composing and merging DesignTokens.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { Theme, ThemeOptions } from '../types';
|
|
8
|
-
import { createThemeObject } from './createThemeObject';
|
|
9
|
-
|
|
10
7
|
// ============================================================================
|
|
11
8
|
// Deep Merge Utility
|
|
12
9
|
// ============================================================================
|
|
@@ -52,50 +49,45 @@ export function deepMerge<T extends Record<string, unknown>>(...objects: Partial
|
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
// ============================================================================
|
|
55
|
-
//
|
|
52
|
+
// DesignTokens Merging
|
|
56
53
|
// ============================================================================
|
|
57
54
|
|
|
55
|
+
import type { DesignTokens } from '../tokens/tokens';
|
|
56
|
+
|
|
58
57
|
/**
|
|
59
|
-
* Merge multiple
|
|
58
|
+
* Merge multiple DesignTokens objects into a single DesignTokens object
|
|
60
59
|
*
|
|
61
|
-
* @param
|
|
62
|
-
* @returns Merged
|
|
60
|
+
* @param tokens - DesignTokens objects to merge
|
|
61
|
+
* @returns Merged DesignTokens object
|
|
63
62
|
*
|
|
64
63
|
* @example
|
|
65
64
|
* ```typescript
|
|
66
|
-
* const
|
|
67
|
-
* const
|
|
68
|
-
* const merged = mergeTheme(
|
|
65
|
+
* const baseTokens = { 'primary': '#000', 'spacing-4': '1rem' };
|
|
66
|
+
* const customTokens = { 'secondary': '#fff', 'spacing-4': '1.5rem' };
|
|
67
|
+
* const merged = mergeTheme(baseTokens, customTokens);
|
|
68
|
+
* // Returns: { 'primary': '#000', 'secondary': '#fff', 'spacing-4': '1.5rem' }
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
|
-
export function mergeTheme(...
|
|
72
|
-
return deepMerge({}, ...
|
|
71
|
+
export function mergeTheme(...tokens: Partial<DesignTokens>[]): Partial<DesignTokens> {
|
|
72
|
+
return deepMerge({}, ...tokens);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Extend
|
|
76
|
+
* Extend DesignTokens with additional tokens
|
|
77
77
|
*
|
|
78
|
-
* @param
|
|
79
|
-
* @param extension -
|
|
80
|
-
* @returns
|
|
78
|
+
* @param baseTokens - Base DesignTokens to extend
|
|
79
|
+
* @param extension - Additional DesignTokens to merge
|
|
80
|
+
* @returns Extended DesignTokens object
|
|
81
81
|
*
|
|
82
82
|
* @example
|
|
83
83
|
* ```typescript
|
|
84
|
-
* const base =
|
|
85
|
-
* const extended = extendTheme(base, {
|
|
86
|
-
*
|
|
87
|
-
* });
|
|
84
|
+
* const base = { 'primary': '#000' };
|
|
85
|
+
* const extended = extendTheme(base, { 'secondary': '#fff' });
|
|
86
|
+
* // Returns: { 'primary': '#000', 'secondary': '#fff' }
|
|
88
87
|
* ```
|
|
89
88
|
*/
|
|
90
|
-
export function extendTheme(
|
|
91
|
-
|
|
92
|
-
const baseOptions: ThemeOptions = (baseTheme as Theme & { __isJSTheme?: boolean }).__isJSTheme
|
|
93
|
-
? { ...baseTheme } as ThemeOptions
|
|
94
|
-
: baseTheme;
|
|
95
|
-
|
|
96
|
-
// Merge and create new theme
|
|
97
|
-
const merged = mergeTheme(baseOptions, extension);
|
|
98
|
-
return createThemeObject(merged);
|
|
89
|
+
export function extendTheme(baseTokens: Partial<DesignTokens>, extension: Partial<DesignTokens>): Partial<DesignTokens> {
|
|
90
|
+
return mergeTheme(baseTokens, extension);
|
|
99
91
|
}
|
|
100
92
|
|
|
101
93
|
export default {
|
|
@@ -1,32 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core Theme Functions
|
|
3
3
|
*
|
|
4
|
-
* Simplified theme system
|
|
4
|
+
* Simplified theme system using DesignTokens only.
|
|
5
5
|
* Config-first approach: loads from atomix.config.ts when no input is provided.
|
|
6
|
-
* Config file is required for automatic loading.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import type { DesignTokens } from '../tokens/tokens';
|
|
10
|
-
import type { Theme } from '../types';
|
|
11
9
|
import type { GenerateCSSVariablesOptions } from '../generators/generateCSS';
|
|
12
10
|
import { createTokens } from '../tokens/tokens';
|
|
13
11
|
import { generateCSSVariables } from '../generators/generateCSS';
|
|
14
|
-
import {
|
|
12
|
+
import { ThemeError, ThemeErrorCode } from '../errors/errors';
|
|
15
13
|
|
|
16
14
|
/**
|
|
17
|
-
* Create theme CSS from
|
|
15
|
+
* Create theme CSS from DesignTokens
|
|
18
16
|
*
|
|
19
17
|
* **Config-First Approach**: If no input is provided, loads from `atomix.config.ts`.
|
|
20
|
-
* Config file is required for automatic loading.
|
|
21
18
|
*
|
|
22
|
-
* @param input - DesignTokens (partial)
|
|
19
|
+
* @param input - DesignTokens (partial) or undefined (loads from config)
|
|
23
20
|
* @param options - CSS generation options (prefix is automatically read from config if not provided)
|
|
24
21
|
* @returns CSS string with custom properties
|
|
25
22
|
* @throws Error if config loading fails when no input is provided
|
|
26
23
|
*
|
|
27
24
|
* @example
|
|
28
25
|
* ```typescript
|
|
29
|
-
* // Loads from atomix.config.ts
|
|
26
|
+
* // Loads from atomix.config.ts
|
|
30
27
|
* const css = createTheme();
|
|
31
28
|
*
|
|
32
29
|
* // Using DesignTokens
|
|
@@ -35,35 +32,56 @@ import { themeToDesignTokens } from '../adapters/themeAdapter';
|
|
|
35
32
|
* 'spacing-4': '1rem',
|
|
36
33
|
* });
|
|
37
34
|
*
|
|
38
|
-
* // Using Theme object
|
|
39
|
-
* const theme = createThemeObject({ palette: { primary: { main: '#7c3aed' } } });
|
|
40
|
-
* const css = createTheme(theme);
|
|
41
|
-
*
|
|
42
35
|
* // With custom options
|
|
43
36
|
* const css = createTheme(undefined, { prefix: 'myapp', selector: ':root' });
|
|
44
37
|
* ```
|
|
45
38
|
*/
|
|
46
39
|
export function createTheme(
|
|
47
|
-
input?: Partial<DesignTokens
|
|
40
|
+
input?: Partial<DesignTokens>,
|
|
48
41
|
options?: GenerateCSSVariablesOptions
|
|
49
42
|
): string {
|
|
43
|
+
// Validate options if provided
|
|
44
|
+
if (options?.prefix) {
|
|
45
|
+
const prefixPattern = /^[a-z][a-z0-9-]*$/;
|
|
46
|
+
if (!prefixPattern.test(options.prefix)) {
|
|
47
|
+
throw new ThemeError(
|
|
48
|
+
`Invalid CSS variable prefix: "${options.prefix}". Prefix must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens (e.g., "atomix", "my-app").`,
|
|
49
|
+
ThemeErrorCode.THEME_VALIDATION_FAILED,
|
|
50
|
+
{ prefix: options.prefix, pattern: prefixPattern.toString() }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate selector if provided
|
|
56
|
+
if (options?.selector) {
|
|
57
|
+
// Basic validation - selector should be a valid CSS selector
|
|
58
|
+
if (typeof options.selector !== 'string' || options.selector.trim().length === 0) {
|
|
59
|
+
throw new ThemeError(
|
|
60
|
+
`Invalid CSS selector: "${options.selector}". Selector must be a non-empty string (e.g., ":root", ".my-theme").`,
|
|
61
|
+
ThemeErrorCode.THEME_VALIDATION_FAILED,
|
|
62
|
+
{ selector: options.selector }
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
// Determine tokens based on input
|
|
51
68
|
let tokens: Partial<DesignTokens>;
|
|
52
69
|
|
|
53
70
|
if (!input) {
|
|
54
71
|
// Check if we're in a browser environment
|
|
55
72
|
if (typeof window !== 'undefined') {
|
|
56
|
-
throw new
|
|
73
|
+
throw new ThemeError(
|
|
74
|
+
'No input provided and config loading is not available in browser environment. Please provide tokens explicitly or use Node.js/SSR environment.',
|
|
75
|
+
ThemeErrorCode.CONFIG_LOAD_FAILED,
|
|
76
|
+
{ environment: 'browser' }
|
|
77
|
+
);
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
// Load from config when no input provided
|
|
60
|
-
// Using dynamic import in a way that's more compatible with bundlers
|
|
61
81
|
let loadThemeFromConfigSync: any;
|
|
62
82
|
let loadAtomixConfig: any;
|
|
63
83
|
|
|
64
84
|
try {
|
|
65
|
-
// Use dynamic require but only in Node.js environments
|
|
66
|
-
// This approach allows bundlers to properly handle external dependencies
|
|
67
85
|
const configLoaderModule = require('../config/configLoader');
|
|
68
86
|
const loaderModule = require('../../config/loader');
|
|
69
87
|
|
|
@@ -83,12 +101,22 @@ export function createTheme(
|
|
|
83
101
|
}
|
|
84
102
|
}
|
|
85
103
|
} catch (error) {
|
|
86
|
-
throw new
|
|
104
|
+
throw new ThemeError(
|
|
105
|
+
'No input provided and config loading is not available in this environment. Please provide tokens explicitly.',
|
|
106
|
+
ThemeErrorCode.CONFIG_LOAD_FAILED,
|
|
107
|
+
{ error: error instanceof Error ? error.message : String(error) }
|
|
108
|
+
);
|
|
87
109
|
}
|
|
88
|
-
} else if (isThemeObject(input)) {
|
|
89
|
-
// Convert Theme object to DesignTokens
|
|
90
|
-
tokens = themeToDesignTokens(input);
|
|
91
110
|
} else {
|
|
111
|
+
// Validate input tokens structure
|
|
112
|
+
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
113
|
+
throw new ThemeError(
|
|
114
|
+
`Invalid tokens input. Expected an object with DesignTokens, but received: ${typeof input}.`,
|
|
115
|
+
ThemeErrorCode.THEME_VALIDATION_FAILED,
|
|
116
|
+
{ inputType: typeof input }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
92
120
|
// Use DesignTokens directly
|
|
93
121
|
tokens = input;
|
|
94
122
|
}
|
|
@@ -100,9 +128,4 @@ export function createTheme(
|
|
|
100
128
|
const prefix = options?.prefix ?? 'atomix';
|
|
101
129
|
|
|
102
130
|
return generateCSSVariables(allTokens, { ...options, prefix });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Helper functions to simplify main function
|
|
106
|
-
function isThemeObject(input: any): input is Theme {
|
|
107
|
-
return input?.__isJSTheme === true || (input?.palette && input?.typography);
|
|
108
131
|
}
|
|
@@ -69,7 +69,8 @@ export function generateNestedCSSVariables(
|
|
|
69
69
|
const flattened = flatten ? flattenTokens(tokens, separator) : tokens;
|
|
70
70
|
|
|
71
71
|
// Generate CSS variables using the original function
|
|
72
|
-
|
|
72
|
+
// Cast to DesignTokens since generateCSSVariables filters out undefined values
|
|
73
|
+
return generateCSSVariables(flattened as DesignTokens, { selector, prefix });
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
/**
|
|
@@ -79,8 +80,8 @@ export function generateNestedCSSVariables(
|
|
|
79
80
|
* @param separator - Separator for nested keys
|
|
80
81
|
* @returns Flattened token object
|
|
81
82
|
*/
|
|
82
|
-
function flattenTokens(tokens: DesignTokens, separator: string): DesignTokens {
|
|
83
|
-
const result: DesignTokens = {};
|
|
83
|
+
function flattenTokens(tokens: DesignTokens, separator: string): Partial<DesignTokens> {
|
|
84
|
+
const result: Partial<DesignTokens> = {};
|
|
84
85
|
|
|
85
86
|
for (const [key, value] of Object.entries(tokens)) {
|
|
86
87
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
@@ -40,28 +40,36 @@ export interface GenerateCSSVariablesOptions {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Convert a nested object to flat CSS variable declarations
|
|
43
|
+
* Uses iterative approach for better performance with large objects
|
|
43
44
|
*/
|
|
44
45
|
function flattenObject(
|
|
45
46
|
obj: Record<string, any>,
|
|
46
47
|
prefix: string = '',
|
|
47
48
|
result: Record<string, string> = {}
|
|
48
49
|
): Record<string, string> {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
// Use iterative approach with stack to avoid deep recursion
|
|
51
|
+
const stack: Array<{ obj: Record<string, any>; prefix: string }> = [{ obj, prefix }];
|
|
52
|
+
|
|
53
|
+
while (stack.length > 0) {
|
|
54
|
+
const { obj: currentObj, prefix: currentPrefix } = stack.pop()!;
|
|
55
|
+
|
|
56
|
+
for (const key in currentObj) {
|
|
57
|
+
if (!Object.prototype.hasOwnProperty.call(currentObj, key)) continue;
|
|
58
|
+
|
|
59
|
+
const value = currentObj[key];
|
|
60
|
+
const newKey = currentPrefix ? `${currentPrefix}-${key}` : key;
|
|
61
|
+
|
|
62
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
63
|
+
// Skip special objects like functions
|
|
64
|
+
if (typeof value === 'function') continue;
|
|
65
|
+
|
|
66
|
+
// Add to stack for iterative processing
|
|
67
|
+
stack.push({ obj: value, prefix: newKey });
|
|
68
|
+
} else if (typeof value === 'string' || typeof value === 'number') {
|
|
69
|
+
// Convert camelCase to kebab-case
|
|
70
|
+
const kebabKey = newKey.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
71
|
+
result[kebabKey] = String(value);
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
|
package/src/lib/theme/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme System Exports
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Simplified theme system using DesignTokens only.
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```typescript
|
|
8
8
|
* import { createTheme, injectTheme } from '@shohojdhara/atomix/theme';
|
|
9
9
|
*
|
|
10
|
-
* // Using DesignTokens
|
|
10
|
+
* // Using DesignTokens
|
|
11
11
|
* const css = createTheme({ 'primary': '#7AFFD7', 'spacing-4': '1rem' });
|
|
12
12
|
* injectTheme(css);
|
|
13
13
|
*
|
|
@@ -22,12 +22,9 @@
|
|
|
22
22
|
// Core Theme Functions
|
|
23
23
|
// ============================================================================
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Create theme CSS from DesignTokens
|
|
26
26
|
export { createTheme } from './core';
|
|
27
27
|
|
|
28
|
-
// Theme object creation
|
|
29
|
-
export { createThemeObject } from './core';
|
|
30
|
-
|
|
31
28
|
// Theme composition
|
|
32
29
|
export { deepMerge, mergeTheme, extendTheme } from './core';
|
|
33
30
|
|
|
@@ -142,19 +139,12 @@ export { ThemeApplicator, getThemeApplicator, applyTheme } from './runtime/Theme
|
|
|
142
139
|
// DevTools (for development and debugging)
|
|
143
140
|
export * from './devtools';
|
|
144
141
|
|
|
145
|
-
//
|
|
146
|
-
export {
|
|
147
|
-
themeToDesignTokens,
|
|
148
|
-
designTokensToCSSVars,
|
|
149
|
-
createDesignTokensFromTheme,
|
|
150
|
-
designTokensToTheme,
|
|
151
|
-
} from './adapters';
|
|
142
|
+
// CSS variable utilities
|
|
143
|
+
export { designTokensToCSSVars } from './adapters';
|
|
152
144
|
|
|
153
|
-
// Theme helpers (utilities for working with
|
|
145
|
+
// Theme helpers (utilities for working with DesignTokens)
|
|
154
146
|
export {
|
|
155
|
-
getDesignTokensFromTheme,
|
|
156
147
|
isDesignTokens,
|
|
157
|
-
isThemeObject,
|
|
158
148
|
} from './utils/themeHelpers';
|
|
159
149
|
|
|
160
150
|
// CSS variable utilities
|
|
@@ -175,7 +165,6 @@ export { RTLManager } from './i18n/rtl';
|
|
|
175
165
|
|
|
176
166
|
// Types
|
|
177
167
|
export type {
|
|
178
|
-
Theme,
|
|
179
168
|
ThemeChangeEvent,
|
|
180
169
|
ThemeLoadOptions,
|
|
181
170
|
ThemeValidationResult,
|
|
@@ -186,6 +175,10 @@ export type {
|
|
|
186
175
|
ThemeComponentOverrides,
|
|
187
176
|
} from './types';
|
|
188
177
|
|
|
178
|
+
// Note: Theme type is deprecated - use DesignTokens instead
|
|
179
|
+
// Keeping for backward compatibility with devtools and internal use only
|
|
180
|
+
export type { Theme } from './types';
|
|
181
|
+
|
|
189
182
|
export type { ThemeErrorBoundaryProps } from './runtime/ThemeErrorBoundary';
|
|
190
183
|
|
|
191
184
|
export type {
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* Uses the unified theme system for CSS generation and injection.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { Theme, ThemeComponentOverrides, ComponentThemeOverride } from '../types';
|
|
11
10
|
import type { DesignTokens } from '../tokens/tokens';
|
|
12
11
|
import { createTheme } from '../core/createTheme';
|
|
13
12
|
import { injectCSS, removeCSS } from '../utils/injectCSS';
|
|
@@ -26,42 +25,15 @@ export class ThemeApplicator {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
* Apply a complete theme configuration
|
|
28
|
+
* Apply a complete theme configuration using DesignTokens
|
|
30
29
|
*
|
|
31
|
-
* Uses the unified theme system to
|
|
30
|
+
* Uses the unified theme system to generate and inject CSS.
|
|
32
31
|
* Automatically respects atomix.config.ts when using DesignTokens.
|
|
33
32
|
*/
|
|
34
|
-
applyTheme(
|
|
33
|
+
applyTheme(tokens: Partial<DesignTokens>): void {
|
|
35
34
|
// Clear previously applied variables
|
|
36
35
|
this.clearAppliedVars();
|
|
37
36
|
|
|
38
|
-
// Check if it's DesignTokens
|
|
39
|
-
if (this.isDesignTokens(theme)) {
|
|
40
|
-
// Direct DesignTokens - use unified theme system (with config support)
|
|
41
|
-
this.applyDesignTokens(theme);
|
|
42
|
-
} else {
|
|
43
|
-
// Theme object - use createTheme which handles Theme objects
|
|
44
|
-
// createTheme automatically converts Theme to DesignTokens internally
|
|
45
|
-
const css = createTheme(theme, {
|
|
46
|
-
selector: ':root',
|
|
47
|
-
prefix: 'atomix',
|
|
48
|
-
});
|
|
49
|
-
injectCSS(css, this.styleId);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Apply component overrides (only for Theme objects)
|
|
53
|
-
if (!this.isDesignTokens(theme) && (theme as any).components) {
|
|
54
|
-
this.applyComponentOverrides((theme as any).components);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Apply DesignTokens using unified theme system
|
|
60
|
-
*
|
|
61
|
-
* Uses createTheme() which automatically loads from atomix.config.ts
|
|
62
|
-
* if no tokens are provided, ensuring config is always respected.
|
|
63
|
-
*/
|
|
64
|
-
private applyDesignTokens(tokens: Partial<DesignTokens>): void {
|
|
65
37
|
// Use createTheme() which handles config loading automatically
|
|
66
38
|
// If tokens is empty, it will load from config
|
|
67
39
|
const css = createTheme(tokens, {
|
|
@@ -74,19 +46,7 @@ export class ThemeApplicator {
|
|
|
74
46
|
}
|
|
75
47
|
|
|
76
48
|
/**
|
|
77
|
-
*
|
|
78
|
-
*/
|
|
79
|
-
private isDesignTokens(obj: Theme | DesignTokens): obj is DesignTokens {
|
|
80
|
-
// DesignTokens is a flat object with string keys, no nested structures
|
|
81
|
-
return obj !== null &&
|
|
82
|
-
typeof obj === 'object' &&
|
|
83
|
-
!('palette' in obj) &&
|
|
84
|
-
!('typography' in obj) &&
|
|
85
|
-
!('__isJSTheme' in obj);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Apply global CSS variables (for component overrides)
|
|
49
|
+
* Apply global CSS variables
|
|
90
50
|
*/
|
|
91
51
|
private applyGlobalCSSVars(vars: Record<string, string | number>): void {
|
|
92
52
|
Object.entries(vars).forEach(([key, value]) => {
|
|
@@ -94,69 +54,6 @@ export class ThemeApplicator {
|
|
|
94
54
|
});
|
|
95
55
|
}
|
|
96
56
|
|
|
97
|
-
/**
|
|
98
|
-
* Apply component-level overrides
|
|
99
|
-
*/
|
|
100
|
-
private applyComponentOverrides(overrides: ThemeComponentOverrides): void {
|
|
101
|
-
Object.entries(overrides).forEach(([componentName, override]) => {
|
|
102
|
-
if (override) {
|
|
103
|
-
this.applyComponentOverride(componentName, override);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Apply override for a specific component
|
|
110
|
-
*/
|
|
111
|
-
private applyComponentOverride(
|
|
112
|
-
componentName: string,
|
|
113
|
-
override: ComponentThemeOverride
|
|
114
|
-
): void {
|
|
115
|
-
const vars: Record<string, string | number> = {};
|
|
116
|
-
const componentKey = componentName.toLowerCase();
|
|
117
|
-
|
|
118
|
-
// Apply component-level CSS variables
|
|
119
|
-
if (override.cssVars) {
|
|
120
|
-
Object.entries(override.cssVars).forEach(([key, value]) => {
|
|
121
|
-
// If key doesn't start with --, add component prefix
|
|
122
|
-
const varKey = key.startsWith('--')
|
|
123
|
-
? key
|
|
124
|
-
: `--atomix-${componentKey}-${key}`;
|
|
125
|
-
vars[varKey] = value;
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Apply part-specific CSS variables
|
|
130
|
-
if (override.parts) {
|
|
131
|
-
Object.entries(override.parts).forEach(([partName, partOverride]) => {
|
|
132
|
-
if (partOverride.cssVars) {
|
|
133
|
-
Object.entries(partOverride.cssVars).forEach(([key, value]) => {
|
|
134
|
-
const varKey = key.startsWith('--')
|
|
135
|
-
? key
|
|
136
|
-
: `--atomix-${componentKey}-${partName}-${key}`;
|
|
137
|
-
vars[varKey] = value;
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Apply variant-specific CSS variables
|
|
144
|
-
if (override.variants) {
|
|
145
|
-
Object.entries(override.variants).forEach(([variantName, variantOverride]) => {
|
|
146
|
-
if (variantOverride.cssVars) {
|
|
147
|
-
Object.entries(variantOverride.cssVars).forEach(([key, value]) => {
|
|
148
|
-
const varKey = key.startsWith('--')
|
|
149
|
-
? key
|
|
150
|
-
: `--atomix-${componentKey}-${variantName}-${key}`;
|
|
151
|
-
vars[varKey] = value;
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
this.applyGlobalCSSVars(vars);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
57
|
/**
|
|
161
58
|
* Clear all applied CSS variables
|
|
162
59
|
*/
|
|
@@ -198,8 +95,8 @@ export function getThemeApplicator(): ThemeApplicator {
|
|
|
198
95
|
/**
|
|
199
96
|
* Apply theme using global applicator
|
|
200
97
|
*/
|
|
201
|
-
export function applyTheme(
|
|
202
|
-
getThemeApplicator().applyTheme(
|
|
98
|
+
export function applyTheme(tokens: Partial<DesignTokens>): void {
|
|
99
|
+
getThemeApplicator().applyTheme(tokens);
|
|
203
100
|
}
|
|
204
101
|
|
|
205
102
|
/**
|