@shohojdhara/atomix 0.3.2 → 0.3.4
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 +58 -21
- package/dist/atomix.css +96 -121
- package/dist/atomix.min.css +3 -3
- package/dist/index.d.ts +7937 -7765
- package/dist/index.esm.js +3677 -4031
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3648 -3952
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +44 -16
- package/scripts/atomix-cli.js +1764 -0
- package/scripts/build-themes.js +208 -0
- package/scripts/cli/interactive-init.js +520 -0
- package/scripts/cli/migration-tools.js +603 -0
- package/scripts/cli/theme-bridge.js +129 -0
- package/scripts/cli/token-manager.js +519 -0
- package/scripts/sync-theme-config.js +309 -0
- package/src/components/Button/Button.tsx +36 -1
- package/src/components/List/ListGroup.tsx +1 -2
- package/src/components/Popover/Popover.tsx +2 -2
- package/src/components/Tooltip/Tooltip.stories.tsx +49 -12
- package/src/components/Tooltip/Tooltip.tsx +32 -58
- package/src/lib/composables/useTooltip.ts +285 -0
- package/src/lib/config/index.ts +275 -0
- package/src/lib/config/loader.ts +105 -0
- package/src/lib/constants/cssVariables.ts +390 -0
- package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +151 -0
- package/src/lib/hooks/index.ts +19 -0
- package/src/lib/hooks/useComponentCustomization.ts +175 -0
- package/src/lib/index.ts +14 -1
- package/src/lib/patterns/__tests__/slots.test.ts +108 -0
- package/src/lib/patterns/index.ts +35 -0
- package/src/lib/patterns/slots.tsx +421 -0
- package/src/lib/theme/composeTheme.ts +0 -5
- package/src/lib/theme/config/index.ts +1 -1
- package/src/lib/theme/config/loader.ts +75 -41
- package/src/lib/theme/config/types.ts +21 -7
- package/src/lib/theme/config/validator.ts +1 -1
- package/src/lib/theme/constants.ts +12 -2
- package/src/lib/theme/createTheme.ts +2 -135
- package/src/lib/theme/createThemeFromConfig.ts +132 -0
- package/src/lib/theme/cssVariableMapper.ts +261 -0
- package/src/lib/theme/devtools/CLI.ts +161 -76
- package/src/lib/theme/devtools/Comparator.tsx +343 -0
- package/src/lib/theme/devtools/IMPROVEMENTS.md +429 -0
- package/src/lib/theme/devtools/Inspector.tsx +21 -6
- package/src/lib/theme/devtools/LiveEditor.tsx +393 -0
- package/src/lib/theme/devtools/README.md +433 -0
- package/src/lib/theme/devtools/index.ts +12 -11
- package/src/lib/theme/generateCSSVariables.ts +79 -38
- package/src/lib/theme/index.ts +45 -246
- package/src/lib/theme/runtime/ThemeApplicator.ts +252 -0
- package/src/lib/theme/runtime/ThemeManager.test.ts +17 -1
- package/src/lib/theme/runtime/ThemeManager.ts +7 -7
- package/src/lib/theme/themeUtils.ts +27 -5
- package/src/lib/theme/types.ts +59 -1
- package/src/lib/theme-tools.ts +125 -0
- package/src/lib/types/components.ts +260 -72
- package/src/lib/types/partProps.ts +426 -0
- package/src/lib/utils/__tests__/componentUtils.test.ts +144 -0
- package/src/lib/utils/componentUtils.ts +163 -0
- package/src/lib/utils/index.ts +17 -57
- package/src/styles/01-settings/_settings.colors.scss +10 -10
- package/src/styles/01-settings/_settings.navbar.scss +1 -1
- package/src/styles/01-settings/_settings.tooltip.scss +1 -1
- package/src/styles/03-generic/_generated-root.css +5 -0
- package/src/styles/06-components/_components.navbar.scss +12 -5
- package/src/styles/06-components/_components.tooltip.scss +31 -81
- package/src/themes/README.md +442 -0
- package/src/themes/themes.config.js +35 -0
- package/src/lib/theme/errors.test.ts +0 -207
- package/src/lib/theme/generators/CSSGenerator.ts +0 -311
- package/src/lib/theme/generators/ConfigGenerator.ts +0 -287
- package/src/lib/theme/generators/TypeGenerator.ts +0 -228
- package/src/lib/theme/generators/index.ts +0 -21
- package/src/lib/theme/monitoring/ThemeAnalytics.ts +0 -409
- package/src/lib/theme/monitoring/index.ts +0 -17
- package/src/lib/theme/overrides/ComponentOverrides.ts +0 -243
- package/src/lib/theme/overrides/index.ts +0 -15
- package/src/lib/theme/studio/ThemeStudio.tsx +0 -312
- package/src/lib/theme/studio/index.ts +0 -8
- package/src/lib/theme/whitelabel/WhiteLabelManager.ts +0 -364
- package/src/lib/theme/whitelabel/index.ts +0 -13
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot Pattern System
|
|
3
|
+
*
|
|
4
|
+
* Provides render props and slot-based customization for components.
|
|
5
|
+
* Allows complete control over component rendering and structure.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Slot configuration with multiple rendering options
|
|
12
|
+
*/
|
|
13
|
+
export interface SlotProps<T = any> {
|
|
14
|
+
/** Static children to render */
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
/** Render function with access to slot props */
|
|
17
|
+
render?: (props: T) => React.ReactNode;
|
|
18
|
+
/** Custom component to render */
|
|
19
|
+
component?: React.ComponentType<T>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Render a slot with the given props
|
|
24
|
+
*
|
|
25
|
+
* Priority order:
|
|
26
|
+
* 1. render function
|
|
27
|
+
* 2. component
|
|
28
|
+
* 3. children
|
|
29
|
+
* 4. fallback
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* renderSlot(
|
|
33
|
+
* { render: (props) => <CustomButton {...props} /> },
|
|
34
|
+
* { onClick: handleClick, children: 'Click me' }
|
|
35
|
+
* )
|
|
36
|
+
*/
|
|
37
|
+
export function renderSlot<T>(
|
|
38
|
+
slot: SlotProps<T> | React.ReactNode | undefined,
|
|
39
|
+
props: T,
|
|
40
|
+
fallback?: React.ReactNode
|
|
41
|
+
): React.ReactNode {
|
|
42
|
+
// No slot provided, use fallback
|
|
43
|
+
if (!slot) return fallback;
|
|
44
|
+
|
|
45
|
+
// Slot is a plain React node
|
|
46
|
+
if (React.isValidElement(slot) || typeof slot === 'string' || typeof slot === 'number') {
|
|
47
|
+
return slot;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Slot is an object with rendering options
|
|
51
|
+
if (typeof slot === 'object' && slot !== null) {
|
|
52
|
+
const slotObj = slot as SlotProps<T>;
|
|
53
|
+
|
|
54
|
+
// Priority 1: render function
|
|
55
|
+
if (slotObj.render && typeof slotObj.render === 'function') {
|
|
56
|
+
return slotObj.render(props);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Priority 2: component
|
|
60
|
+
if (slotObj.component) {
|
|
61
|
+
const Component = slotObj.component;
|
|
62
|
+
return <Component {...props} />;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Priority 3: children
|
|
66
|
+
if (slotObj.children !== undefined) {
|
|
67
|
+
return slotObj.children;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a value is a slot configuration
|
|
77
|
+
*/
|
|
78
|
+
export function isSlot<T>(value: any): value is SlotProps<T> {
|
|
79
|
+
return (
|
|
80
|
+
typeof value === 'object' &&
|
|
81
|
+
value !== null &&
|
|
82
|
+
('render' in value || 'component' in value || 'children' in value)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Merge multiple slot configurations
|
|
88
|
+
* Later slots override earlier ones
|
|
89
|
+
*/
|
|
90
|
+
export function mergeSlots<T>(
|
|
91
|
+
...slots: Array<SlotProps<T> | undefined>
|
|
92
|
+
): SlotProps<T> | undefined {
|
|
93
|
+
const filtered = slots.filter((s): s is SlotProps<T> => s !== undefined);
|
|
94
|
+
|
|
95
|
+
if (filtered.length === 0) return undefined;
|
|
96
|
+
if (filtered.length === 1) return filtered[0];
|
|
97
|
+
|
|
98
|
+
return filtered.reduce((acc, slot) => ({
|
|
99
|
+
...acc,
|
|
100
|
+
...slot,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a slot wrapper component
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* const ButtonSlot = createSlotComponent<ButtonSlotProps>('button')
|
|
109
|
+
*
|
|
110
|
+
* <ButtonSlot slot={customSlot} {...props}>
|
|
111
|
+
* Default content
|
|
112
|
+
* </ButtonSlot>
|
|
113
|
+
*/
|
|
114
|
+
export function createSlotComponent<T>(
|
|
115
|
+
defaultElement: keyof JSX.IntrinsicElements | React.ComponentType<T> = 'div'
|
|
116
|
+
) {
|
|
117
|
+
return function SlotComponent({
|
|
118
|
+
slot,
|
|
119
|
+
children,
|
|
120
|
+
...props
|
|
121
|
+
}: T & {
|
|
122
|
+
slot?: SlotProps<T>;
|
|
123
|
+
children?: React.ReactNode;
|
|
124
|
+
}) {
|
|
125
|
+
const slotProps = props as T;
|
|
126
|
+
|
|
127
|
+
if (slot) {
|
|
128
|
+
return <>{renderSlot(slot, slotProps, children)}</>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (typeof defaultElement === 'string') {
|
|
132
|
+
const Element = defaultElement;
|
|
133
|
+
return <Element {...(props as any)}>{children}</Element>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const Component = defaultElement;
|
|
137
|
+
return <Component {...slotProps}>{children}</Component>;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Slot props for Button component
|
|
143
|
+
*/
|
|
144
|
+
export interface ButtonRootSlotProps {
|
|
145
|
+
className: string;
|
|
146
|
+
style?: React.CSSProperties;
|
|
147
|
+
children: React.ReactNode;
|
|
148
|
+
disabled?: boolean;
|
|
149
|
+
loading?: boolean;
|
|
150
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
151
|
+
type?: 'button' | 'submit' | 'reset';
|
|
152
|
+
'aria-label'?: string;
|
|
153
|
+
'aria-disabled'?: boolean;
|
|
154
|
+
'aria-busy'?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface ButtonIconSlotProps {
|
|
158
|
+
className: string;
|
|
159
|
+
style?: React.CSSProperties;
|
|
160
|
+
children: React.ReactNode;
|
|
161
|
+
size?: 'sm' | 'md' | 'lg';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface ButtonLabelSlotProps {
|
|
165
|
+
className: string;
|
|
166
|
+
style?: React.CSSProperties;
|
|
167
|
+
children: React.ReactNode;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ButtonSpinnerSlotProps {
|
|
171
|
+
className: string;
|
|
172
|
+
style?: React.CSSProperties;
|
|
173
|
+
size?: 'sm' | 'md' | 'lg';
|
|
174
|
+
variant?: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Slot props for Card component
|
|
179
|
+
*/
|
|
180
|
+
export interface CardRootSlotProps {
|
|
181
|
+
className: string;
|
|
182
|
+
style?: React.CSSProperties;
|
|
183
|
+
children: React.ReactNode;
|
|
184
|
+
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
|
185
|
+
role?: string;
|
|
186
|
+
'aria-label'?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface CardHeaderSlotProps {
|
|
190
|
+
className: string;
|
|
191
|
+
style?: React.CSSProperties;
|
|
192
|
+
children: React.ReactNode;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface CardBodySlotProps {
|
|
196
|
+
className: string;
|
|
197
|
+
style?: React.CSSProperties;
|
|
198
|
+
children: React.ReactNode;
|
|
199
|
+
scrollable?: boolean;
|
|
200
|
+
maxHeight?: string | number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface CardFooterSlotProps {
|
|
204
|
+
className: string;
|
|
205
|
+
style?: React.CSSProperties;
|
|
206
|
+
children: React.ReactNode;
|
|
207
|
+
align?: 'start' | 'center' | 'end' | 'between';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Slot props for Modal component
|
|
212
|
+
*/
|
|
213
|
+
export interface ModalRootSlotProps {
|
|
214
|
+
className: string;
|
|
215
|
+
style?: React.CSSProperties;
|
|
216
|
+
children: React.ReactNode;
|
|
217
|
+
role: string;
|
|
218
|
+
'aria-modal': boolean;
|
|
219
|
+
'aria-hidden': boolean;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface ModalBackdropSlotProps {
|
|
223
|
+
className: string;
|
|
224
|
+
style?: React.CSSProperties;
|
|
225
|
+
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface ModalDialogSlotProps {
|
|
229
|
+
className: string;
|
|
230
|
+
style?: React.CSSProperties;
|
|
231
|
+
children: React.ReactNode;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface ModalContentSlotProps {
|
|
235
|
+
className: string;
|
|
236
|
+
style?: React.CSSProperties;
|
|
237
|
+
children: React.ReactNode;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Slot props for Input component
|
|
242
|
+
*/
|
|
243
|
+
export interface InputRootSlotProps {
|
|
244
|
+
className: string;
|
|
245
|
+
style?: React.CSSProperties;
|
|
246
|
+
children: React.ReactNode;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface InputElementSlotProps {
|
|
250
|
+
className: string;
|
|
251
|
+
style?: React.CSSProperties;
|
|
252
|
+
type?: string;
|
|
253
|
+
value?: string | number;
|
|
254
|
+
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
|
255
|
+
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
|
256
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
257
|
+
placeholder?: string;
|
|
258
|
+
disabled?: boolean;
|
|
259
|
+
required?: boolean;
|
|
260
|
+
readOnly?: boolean;
|
|
261
|
+
'aria-label'?: string;
|
|
262
|
+
'aria-invalid'?: boolean;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Slot props for Dropdown component
|
|
267
|
+
*/
|
|
268
|
+
export interface DropdownRootSlotProps {
|
|
269
|
+
className: string;
|
|
270
|
+
style?: React.CSSProperties;
|
|
271
|
+
children: React.ReactNode;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface DropdownToggleSlotProps {
|
|
275
|
+
className: string;
|
|
276
|
+
style?: React.CSSProperties;
|
|
277
|
+
children: React.ReactNode;
|
|
278
|
+
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
|
279
|
+
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
|
|
280
|
+
'aria-haspopup': string;
|
|
281
|
+
'aria-expanded': boolean;
|
|
282
|
+
'aria-controls': string;
|
|
283
|
+
tabIndex: number;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface DropdownMenuSlotProps {
|
|
287
|
+
className: string;
|
|
288
|
+
style?: React.CSSProperties;
|
|
289
|
+
children: React.ReactNode;
|
|
290
|
+
role: string;
|
|
291
|
+
'aria-orientation': string;
|
|
292
|
+
'aria-hidden': boolean;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Slot props for Badge component
|
|
297
|
+
*/
|
|
298
|
+
export interface BadgeRootSlotProps {
|
|
299
|
+
className: string;
|
|
300
|
+
style?: React.CSSProperties;
|
|
301
|
+
children: React.ReactNode;
|
|
302
|
+
onClick?: React.MouseEventHandler<HTMLSpanElement>;
|
|
303
|
+
role?: string;
|
|
304
|
+
'aria-label'?: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface BadgeIconSlotProps {
|
|
308
|
+
className: string;
|
|
309
|
+
style?: React.CSSProperties;
|
|
310
|
+
children: React.ReactNode;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export interface BadgeLabelSlotProps {
|
|
314
|
+
className: string;
|
|
315
|
+
style?: React.CSSProperties;
|
|
316
|
+
children: React.ReactNode;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Slot props for Progress component
|
|
321
|
+
*/
|
|
322
|
+
export interface ProgressRootSlotProps {
|
|
323
|
+
className: string;
|
|
324
|
+
style?: React.CSSProperties;
|
|
325
|
+
children: React.ReactNode;
|
|
326
|
+
role: string;
|
|
327
|
+
'aria-valuenow': number;
|
|
328
|
+
'aria-valuemin': number;
|
|
329
|
+
'aria-valuemax': number;
|
|
330
|
+
'aria-label'?: string;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface ProgressBarSlotProps {
|
|
334
|
+
className: string;
|
|
335
|
+
style?: React.CSSProperties;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Slot props for Checkbox component
|
|
340
|
+
*/
|
|
341
|
+
export interface CheckboxRootSlotProps {
|
|
342
|
+
className: string;
|
|
343
|
+
style?: React.CSSProperties;
|
|
344
|
+
children: React.ReactNode;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface CheckboxInputSlotProps {
|
|
348
|
+
className: string;
|
|
349
|
+
style?: React.CSSProperties;
|
|
350
|
+
type: 'checkbox';
|
|
351
|
+
checked?: boolean;
|
|
352
|
+
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
|
353
|
+
disabled?: boolean;
|
|
354
|
+
required?: boolean;
|
|
355
|
+
id?: string;
|
|
356
|
+
name?: string;
|
|
357
|
+
value?: string;
|
|
358
|
+
'aria-label'?: string;
|
|
359
|
+
'aria-describedby'?: string;
|
|
360
|
+
'aria-invalid'?: boolean;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export interface CheckboxLabelSlotProps {
|
|
364
|
+
className: string;
|
|
365
|
+
style?: React.CSSProperties;
|
|
366
|
+
children: React.ReactNode;
|
|
367
|
+
htmlFor?: string;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Slot props for Radio component
|
|
372
|
+
*/
|
|
373
|
+
export interface RadioRootSlotProps {
|
|
374
|
+
className: string;
|
|
375
|
+
style?: React.CSSProperties;
|
|
376
|
+
children: React.ReactNode;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export interface RadioInputSlotProps {
|
|
380
|
+
className: string;
|
|
381
|
+
style?: React.CSSProperties;
|
|
382
|
+
type: 'radio';
|
|
383
|
+
checked?: boolean;
|
|
384
|
+
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
|
385
|
+
disabled?: boolean;
|
|
386
|
+
required?: boolean;
|
|
387
|
+
id?: string;
|
|
388
|
+
name?: string;
|
|
389
|
+
value?: string;
|
|
390
|
+
'aria-label'?: string;
|
|
391
|
+
'aria-describedby'?: string;
|
|
392
|
+
'aria-invalid'?: boolean;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface RadioLabelSlotProps {
|
|
396
|
+
className: string;
|
|
397
|
+
style?: React.CSSProperties;
|
|
398
|
+
children: React.ReactNode;
|
|
399
|
+
htmlFor?: string;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Utility to create typed slot props
|
|
404
|
+
*/
|
|
405
|
+
export function createSlotProps<T>(props: T): T {
|
|
406
|
+
return props;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Hook to manage slot rendering
|
|
411
|
+
*/
|
|
412
|
+
export function useSlot<T>(
|
|
413
|
+
slot: SlotProps<T> | React.ReactNode | undefined,
|
|
414
|
+
props: T,
|
|
415
|
+
fallback?: React.ReactNode
|
|
416
|
+
): React.ReactNode {
|
|
417
|
+
return React.useMemo(
|
|
418
|
+
() => renderSlot(slot, props, fallback),
|
|
419
|
+
[slot, props, fallback]
|
|
420
|
+
);
|
|
421
|
+
}
|
|
@@ -185,7 +185,6 @@ export function createThemeVariants(baseTheme: ThemeOptions): {
|
|
|
185
185
|
...baseTheme.palette,
|
|
186
186
|
background: {
|
|
187
187
|
default: '#121212',
|
|
188
|
-
paper: '#1E1E1E',
|
|
189
188
|
subtle: '#2A2A2A',
|
|
190
189
|
...baseTheme.palette?.background,
|
|
191
190
|
},
|
|
@@ -304,7 +303,6 @@ export function createThemePreset(
|
|
|
304
303
|
secondary: { main: '#FFFFFF' },
|
|
305
304
|
background: {
|
|
306
305
|
default: '#FFFFFF',
|
|
307
|
-
paper: '#F5F5F5',
|
|
308
306
|
subtle: '#FAFAFA',
|
|
309
307
|
},
|
|
310
308
|
},
|
|
@@ -319,7 +317,6 @@ export function createThemePreset(
|
|
|
319
317
|
secondary: { main: '#FF5733' },
|
|
320
318
|
background: {
|
|
321
319
|
default: '#FAFAFA',
|
|
322
|
-
paper: '#FFFFFF',
|
|
323
320
|
subtle: '#F5F5F5',
|
|
324
321
|
},
|
|
325
322
|
},
|
|
@@ -334,7 +331,6 @@ export function createThemePreset(
|
|
|
334
331
|
secondary: { main: '#DC004E' },
|
|
335
332
|
background: {
|
|
336
333
|
default: '#FFFFFF',
|
|
337
|
-
paper: '#F5F5F5',
|
|
338
334
|
subtle: '#EEEEEE',
|
|
339
335
|
},
|
|
340
336
|
},
|
|
@@ -349,7 +345,6 @@ export function createThemePreset(
|
|
|
349
345
|
secondary: { main: '#4ECDC4' },
|
|
350
346
|
background: {
|
|
351
347
|
default: '#FFF8F0',
|
|
352
|
-
paper: '#FFFFFF',
|
|
353
348
|
subtle: '#FFF0E0',
|
|
354
349
|
},
|
|
355
350
|
},
|
|
@@ -13,7 +13,9 @@ import { validateConfig } from './validator';
|
|
|
13
13
|
import { ThemeError, ThemeErrorCode, getLogger } from '../errors';
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_CONFIG_PATH,
|
|
16
|
+
DEFAULT_ATOMIX_CONFIG_PATH,
|
|
16
17
|
DEFAULT_CONFIG_RELATIVE_PATH,
|
|
18
|
+
DEFAULT_LEGACY_CONFIG_RELATIVE_PATH,
|
|
17
19
|
DEFAULT_BASE_PATH,
|
|
18
20
|
DEFAULT_STORAGE_KEY,
|
|
19
21
|
DEFAULT_DATA_ATTRIBUTE,
|
|
@@ -50,7 +52,7 @@ export function loadThemeConfig(
|
|
|
50
52
|
options: ConfigLoaderOptions = {}
|
|
51
53
|
): LoadedThemeConfig {
|
|
52
54
|
const {
|
|
53
|
-
configPath =
|
|
55
|
+
configPath = DEFAULT_ATOMIX_CONFIG_PATH,
|
|
54
56
|
validate = true,
|
|
55
57
|
env = typeof process !== 'undefined' && process.env ? (process.env.NODE_ENV === 'production' ? 'production' : 'development') : 'development',
|
|
56
58
|
} = options;
|
|
@@ -62,67 +64,99 @@ export function loadThemeConfig(
|
|
|
62
64
|
|
|
63
65
|
// Try to load config dynamically
|
|
64
66
|
let config: LoadedThemeConfig;
|
|
65
|
-
|
|
67
|
+
|
|
66
68
|
try {
|
|
67
|
-
// In browser/Vite environment, we can't load
|
|
68
|
-
// This is expected and we'll use the fallback config
|
|
69
|
+
// In browser/Vite environment, we can't load config dynamically
|
|
69
70
|
if (typeof window !== 'undefined') {
|
|
70
71
|
throw new Error('Theme config loading not supported in browser environment');
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
// In
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
|
|
74
|
+
// In ESM environments, require might be undefined.
|
|
75
|
+
let nodeRequire: any;
|
|
76
|
+
try {
|
|
77
|
+
nodeRequire = require;
|
|
78
|
+
} catch {
|
|
79
|
+
// require is not defined
|
|
77
80
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
|
|
82
|
+
if (!nodeRequire) {
|
|
83
|
+
throw new Error('Theme config loading not supported in this environment (require is undefined)');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Type for config module
|
|
87
|
+
interface ConfigModule {
|
|
88
|
+
default?: any;
|
|
82
89
|
[key: string]: unknown;
|
|
83
90
|
}
|
|
84
|
-
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
// Try require (Node.js/CommonJS)
|
|
91
|
+
|
|
92
|
+
let configModule: ConfigModule;
|
|
93
|
+
|
|
94
|
+
// Try require (Node.js/CommonJS)
|
|
88
95
|
try {
|
|
89
|
-
// Try relative path first
|
|
96
|
+
// Try relative path first
|
|
90
97
|
try {
|
|
91
|
-
|
|
98
|
+
configModule = nodeRequire(DEFAULT_CONFIG_RELATIVE_PATH) as ConfigModule;
|
|
92
99
|
} catch {
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
// Try fallback to legacy relative path
|
|
101
|
+
try {
|
|
102
|
+
configModule = nodeRequire(DEFAULT_LEGACY_CONFIG_RELATIVE_PATH) as ConfigModule;
|
|
103
|
+
} catch {
|
|
104
|
+
// If relative paths fail, try to resolve from process.cwd()
|
|
105
|
+
const path = nodeRequire('path') as typeof import('path');
|
|
106
|
+
const fs = nodeRequire('fs') as typeof import('fs');
|
|
107
|
+
|
|
108
|
+
let configFilePath = path.resolve(process.cwd(), configPath);
|
|
109
|
+
|
|
110
|
+
// Fallback if atomix.config.ts not found
|
|
111
|
+
if (!fs.existsSync(configFilePath) && configPath === DEFAULT_ATOMIX_CONFIG_PATH) {
|
|
112
|
+
configFilePath = path.resolve(process.cwd(), DEFAULT_CONFIG_PATH);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (fs.existsSync(configFilePath)) {
|
|
116
|
+
const resolvedPath = nodeRequire.resolve(configFilePath);
|
|
117
|
+
if (nodeRequire.cache && nodeRequire.cache[resolvedPath]) {
|
|
118
|
+
delete nodeRequire.cache[resolvedPath];
|
|
119
|
+
}
|
|
120
|
+
configModule = nodeRequire(configFilePath) as ConfigModule;
|
|
121
|
+
} else {
|
|
122
|
+
throw new Error(`Config file not found: ${configFilePath}`);
|
|
104
123
|
}
|
|
105
|
-
themeConfigModule = require(configFilePath) as ThemeConfigModule;
|
|
106
|
-
} else {
|
|
107
|
-
throw new Error(`Config file not found: ${configFilePath}`);
|
|
108
124
|
}
|
|
109
125
|
}
|
|
110
126
|
} catch (requireError) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
? requireError.message
|
|
127
|
+
const errorMessage = requireError instanceof Error
|
|
128
|
+
? requireError.message
|
|
114
129
|
: String(requireError);
|
|
115
130
|
throw new ThemeError(
|
|
116
|
-
`Cannot load
|
|
131
|
+
`Cannot load config: ${errorMessage}`,
|
|
117
132
|
ThemeErrorCode.CONFIG_LOAD_FAILED,
|
|
118
133
|
{ configPath, error: errorMessage }
|
|
119
134
|
);
|
|
120
135
|
}
|
|
121
|
-
|
|
122
|
-
const rawConfig =
|
|
136
|
+
|
|
137
|
+
const rawConfig = configModule.default || configModule;
|
|
138
|
+
|
|
139
|
+
// Handle new AtomixConfig structure vs legacy ThemeConfig
|
|
140
|
+
let processedConfig: any;
|
|
141
|
+
if (rawConfig.theme && (rawConfig.theme.themes || rawConfig.theme.tokens || rawConfig.theme.extend)) {
|
|
142
|
+
// New AtomixConfig structure
|
|
143
|
+
processedConfig = {
|
|
144
|
+
themes: rawConfig.theme.themes || {},
|
|
145
|
+
build: rawConfig.build || {},
|
|
146
|
+
runtime: rawConfig.runtime || {},
|
|
147
|
+
integration: rawConfig.integration || {},
|
|
148
|
+
dependencies: rawConfig.dependencies || {},
|
|
149
|
+
// Store tokens for generator
|
|
150
|
+
__tokens: rawConfig.theme.tokens,
|
|
151
|
+
__extend: rawConfig.theme.extend,
|
|
152
|
+
};
|
|
153
|
+
} else {
|
|
154
|
+
// Legacy ThemeConfig structure
|
|
155
|
+
processedConfig = { ...rawConfig };
|
|
156
|
+
}
|
|
123
157
|
|
|
124
158
|
// Apply environment-specific overrides
|
|
125
|
-
|
|
159
|
+
processedConfig = applyEnvOverrides(processedConfig, env);
|
|
126
160
|
|
|
127
161
|
// Validate if requested
|
|
128
162
|
let validationResult: ConfigValidationResult | null = null;
|
|
@@ -143,7 +177,7 @@ export function loadThemeConfig(
|
|
|
143
177
|
configPath,
|
|
144
178
|
error: errorMessage,
|
|
145
179
|
});
|
|
146
|
-
|
|
180
|
+
|
|
147
181
|
config = {
|
|
148
182
|
themes: {},
|
|
149
183
|
build: {
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type {
|
|
8
|
-
|
|
9
|
-
ThemeDefinition,
|
|
10
|
-
CSSThemeDefinition,
|
|
11
|
-
JSThemeDefinition,
|
|
8
|
+
AtomixConfig,
|
|
12
9
|
BuildConfig,
|
|
13
10
|
RuntimeConfig,
|
|
14
11
|
IntegrationConfig,
|
|
15
|
-
|
|
12
|
+
ThemeDefinition,
|
|
13
|
+
CSSThemeDefinition,
|
|
14
|
+
JSThemeDefinition,
|
|
15
|
+
} from '../../config';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Configuration loader options
|
|
@@ -29,13 +29,27 @@ export interface ConfigLoaderOptions {
|
|
|
29
29
|
/**
|
|
30
30
|
* Loaded and validated theme configuration
|
|
31
31
|
*/
|
|
32
|
-
export interface LoadedThemeConfig
|
|
32
|
+
export interface LoadedThemeConfig {
|
|
33
|
+
/** Registered themes */
|
|
34
|
+
themes: Record<string, ThemeDefinition>;
|
|
35
|
+
/** Build configuration */
|
|
36
|
+
build: BuildConfig;
|
|
37
|
+
/** Runtime configuration */
|
|
38
|
+
runtime: RuntimeConfig;
|
|
39
|
+
/** Integration settings */
|
|
40
|
+
integration: IntegrationConfig;
|
|
41
|
+
/** Theme dependencies mapping */
|
|
42
|
+
dependencies: Record<string, string[]>;
|
|
33
43
|
/** Whether config was validated */
|
|
34
44
|
validated: boolean;
|
|
35
45
|
/** Validation errors (if any) */
|
|
36
46
|
errors?: string[];
|
|
37
47
|
/** Validation warnings (if any) */
|
|
38
48
|
warnings?: string[];
|
|
49
|
+
/** Internal tokens (for generator) */
|
|
50
|
+
__tokens?: any;
|
|
51
|
+
/** Internal extensions (for generator) */
|
|
52
|
+
__extend?: any;
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
/**
|
|
@@ -88,7 +102,7 @@ export interface ConfigValidationResult {
|
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
export type {
|
|
91
|
-
|
|
105
|
+
AtomixConfig,
|
|
92
106
|
ThemeDefinition,
|
|
93
107
|
CSSThemeDefinition,
|
|
94
108
|
JSThemeDefinition,
|