@shohojdhara/atomix 0.3.1 → 0.3.2
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 +0 -1
- package/README.md +3 -5
- package/dist/atomix.css +458 -552
- package/dist/atomix.min.css +3 -3
- package/dist/index.d.ts +2435 -358
- package/dist/index.esm.js +5758 -1901
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +5768 -1933
- 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 +1 -11
- package/src/lib/composables/useAtomixGlass.ts +46 -46
- package/src/lib/index.ts +1 -4
- package/src/lib/theme/config/index.ts +21 -0
- package/src/lib/theme/config/loader.ts +276 -0
- package/src/lib/theme/config/types.ts +98 -0
- package/src/lib/theme/config/validator.ts +326 -0
- package/src/lib/theme/constants.ts +183 -0
- package/src/lib/theme/core/ThemeCache.ts +283 -0
- package/src/lib/theme/core/ThemeEngine.test.ts +146 -0
- package/src/lib/theme/core/ThemeEngine.ts +657 -0
- package/src/lib/theme/core/ThemeRegistry.ts +284 -0
- package/src/lib/theme/core/ThemeValidator.ts +530 -0
- package/src/lib/theme/core/index.ts +24 -0
- package/src/lib/theme/createTheme.ts +81 -70
- package/src/lib/theme/devtools/CLI.ts +279 -0
- package/src/lib/theme/devtools/Inspector.tsx +594 -0
- package/src/lib/theme/devtools/Preview.tsx +392 -0
- package/src/lib/theme/devtools/index.ts +21 -0
- package/src/lib/theme/errors.test.ts +207 -0
- package/src/lib/theme/errors.ts +233 -0
- package/src/lib/theme/generateCSSVariables.ts +93 -9
- package/src/lib/theme/generators/CSSGenerator.ts +311 -0
- package/src/lib/theme/generators/ConfigGenerator.ts +287 -0
- package/src/lib/theme/generators/TypeGenerator.ts +228 -0
- package/src/lib/theme/generators/index.ts +21 -0
- package/src/lib/theme/i18n/index.ts +9 -0
- package/src/lib/theme/i18n/rtl.ts +325 -0
- package/src/lib/theme/index.ts +155 -11
- package/src/lib/theme/monitoring/ThemeAnalytics.ts +409 -0
- package/src/lib/theme/monitoring/index.ts +17 -0
- package/src/lib/theme/overrides/ComponentOverrides.ts +243 -0
- package/src/lib/theme/overrides/index.ts +15 -0
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +233 -0
- package/src/lib/theme/runtime/ThemeManager.test.ts +176 -0
- package/src/lib/theme/runtime/ThemeManager.ts +442 -0
- package/src/lib/theme/runtime/ThemeProvider.tsx +318 -0
- package/src/lib/theme/runtime/index.ts +17 -0
- package/src/lib/theme/runtime/useTheme.ts +52 -0
- package/src/lib/theme/studio/ThemeStudio.tsx +312 -0
- package/src/lib/theme/studio/index.ts +8 -0
- package/src/lib/theme/types.ts +3 -1
- package/src/lib/theme/utils.ts +23 -22
- package/src/lib/theme/whitelabel/WhiteLabelManager.ts +364 -0
- package/src/lib/theme/whitelabel/index.ts +13 -0
- package/src/styles/01-settings/_settings.badge.scss +1 -1
- package/src/styles/01-settings/_settings.callout.scss +1 -1
- package/src/styles/01-settings/_settings.card.scss +1 -1
- package/src/styles/01-settings/_settings.input.scss +1 -1
- package/src/styles/01-settings/_settings.navbar.scss +1 -1
- package/src/styles/01-settings/_settings.upload.scss +1 -1
- package/src/styles/06-components/_components.chart.scss +2 -2
- package/src/styles/99-utilities/_utilities.border.scss +27 -58
- package/src/styles/99-utilities/_utilities.gradient.scss +12 -0
- package/src/styles/99-utilities/_utilities.position.scss +8 -15
- package/src/styles/99-utilities/_utilities.scss +2 -0
- package/src/styles/99-utilities/_utilities.spacing.scss +76 -121
- package/src/styles/99-utilities/_utilities.text.scss +30 -49
- package/dist/themes/applemix.css +0 -15615
- package/dist/themes/applemix.min.css +0 -70
- package/dist/themes/boomdevs.css +0 -15193
- package/dist/themes/boomdevs.min.css +0 -403
- package/dist/themes/esrar.css +0 -17399
- package/dist/themes/esrar.min.css +0 -187
- package/dist/themes/flashtrade.css +0 -16613
- package/dist/themes/flashtrade.min.css +0 -190
- package/dist/themes/mashroom.css +0 -30104
- package/dist/themes/mashroom.min.css +0 -401
- package/dist/themes/shaj-default.css +0 -16228
- package/dist/themes/shaj-default.min.css +0 -498
- package/src/lib/theme/ThemeManager.integration.test.ts +0 -124
- package/src/lib/theme/ThemeManager.stories.tsx +0 -472
- package/src/lib/theme/ThemeManager.test.ts +0 -190
- package/src/lib/theme/ThemeManager.ts +0 -645
- package/src/lib/theme/ThemeProvider.tsx +0 -377
- package/src/lib/theme/createTheme.test.ts +0 -475
- package/src/lib/theme/useTheme.test.tsx +0 -67
- package/src/lib/theme/useTheme.ts +0 -64
- package/src/lib/theme/utils.test.ts +0 -140
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Analytics and Performance Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Tracks theme usage, performance metrics, and provides analytics
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getLogger } from '../errors';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Theme analytics event types
|
|
11
|
+
*/
|
|
12
|
+
export type ThemeAnalyticsEvent =
|
|
13
|
+
| 'theme_load'
|
|
14
|
+
| 'theme_switch'
|
|
15
|
+
| 'theme_error'
|
|
16
|
+
| 'theme_revert'
|
|
17
|
+
| 'css_load'
|
|
18
|
+
| 'performance_metric';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Theme analytics event
|
|
22
|
+
*/
|
|
23
|
+
export interface ThemeAnalyticsEventData {
|
|
24
|
+
/** Event type */
|
|
25
|
+
type: ThemeAnalyticsEvent;
|
|
26
|
+
/** Event timestamp */
|
|
27
|
+
timestamp: number;
|
|
28
|
+
/** Theme name */
|
|
29
|
+
themeName?: string;
|
|
30
|
+
/** Additional event data */
|
|
31
|
+
data?: Record<string, any>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Performance metric
|
|
36
|
+
*/
|
|
37
|
+
export interface PerformanceMetric {
|
|
38
|
+
/** Metric name */
|
|
39
|
+
name: string;
|
|
40
|
+
/** Metric value */
|
|
41
|
+
value: number;
|
|
42
|
+
/** Unit */
|
|
43
|
+
unit: 'ms' | 'bytes' | 'count';
|
|
44
|
+
/** Timestamp */
|
|
45
|
+
timestamp: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Analytics configuration
|
|
50
|
+
*/
|
|
51
|
+
export interface AnalyticsConfig {
|
|
52
|
+
/** Enable analytics */
|
|
53
|
+
enabled?: boolean;
|
|
54
|
+
/** Enable performance tracking */
|
|
55
|
+
trackPerformance?: boolean;
|
|
56
|
+
/** Enable error tracking */
|
|
57
|
+
trackErrors?: boolean;
|
|
58
|
+
/** Custom event handler */
|
|
59
|
+
onEvent?: (event: ThemeAnalyticsEventData) => void;
|
|
60
|
+
/** Custom performance handler */
|
|
61
|
+
onPerformance?: (metric: PerformanceMetric) => void;
|
|
62
|
+
/** Buffer size for events */
|
|
63
|
+
bufferSize?: number;
|
|
64
|
+
/** Flush interval (ms) */
|
|
65
|
+
flushInterval?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Default analytics configuration
|
|
70
|
+
*/
|
|
71
|
+
const DEFAULT_CONFIG: Required<Omit<AnalyticsConfig, 'onEvent' | 'onPerformance'>> = {
|
|
72
|
+
enabled: true,
|
|
73
|
+
trackPerformance: true,
|
|
74
|
+
trackErrors: true,
|
|
75
|
+
bufferSize: 100,
|
|
76
|
+
flushInterval: 5000,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Theme Analytics Manager
|
|
81
|
+
*
|
|
82
|
+
* Tracks theme usage, performance, and errors
|
|
83
|
+
*/
|
|
84
|
+
export class ThemeAnalytics {
|
|
85
|
+
private config: Required<Omit<AnalyticsConfig, 'onEvent' | 'onPerformance'>> & {
|
|
86
|
+
onEvent?: AnalyticsConfig['onEvent'];
|
|
87
|
+
onPerformance?: AnalyticsConfig['onPerformance'];
|
|
88
|
+
};
|
|
89
|
+
private events: ThemeAnalyticsEventData[] = [];
|
|
90
|
+
private metrics: PerformanceMetric[] = [];
|
|
91
|
+
private flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
92
|
+
private logger = getLogger();
|
|
93
|
+
|
|
94
|
+
constructor(config: AnalyticsConfig = {}) {
|
|
95
|
+
this.config = {
|
|
96
|
+
...DEFAULT_CONFIG,
|
|
97
|
+
...config,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (this.config.enabled) {
|
|
101
|
+
this.startFlushTimer();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Track theme load event
|
|
107
|
+
*/
|
|
108
|
+
trackThemeLoad(themeName: string, loadTime?: number): void {
|
|
109
|
+
if (!this.config.enabled) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const event: ThemeAnalyticsEventData = {
|
|
114
|
+
type: 'theme_load',
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
themeName,
|
|
117
|
+
data: loadTime ? { loadTime } : undefined,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this.addEvent(event);
|
|
121
|
+
|
|
122
|
+
if (loadTime !== undefined) {
|
|
123
|
+
this.trackPerformance('theme_load_time', loadTime, 'ms');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Track theme switch event
|
|
129
|
+
*/
|
|
130
|
+
trackThemeSwitch(fromTheme: string, toTheme: string, switchTime?: number): void {
|
|
131
|
+
if (!this.config.enabled) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const event: ThemeAnalyticsEventData = {
|
|
136
|
+
type: 'theme_switch',
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
themeName: toTheme,
|
|
139
|
+
data: {
|
|
140
|
+
fromTheme,
|
|
141
|
+
toTheme,
|
|
142
|
+
...(switchTime ? { switchTime } : {}),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
this.addEvent(event);
|
|
147
|
+
|
|
148
|
+
if (switchTime !== undefined) {
|
|
149
|
+
this.trackPerformance('theme_switch_time', switchTime, 'ms');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Track theme error
|
|
155
|
+
*/
|
|
156
|
+
trackError(themeName: string, error: Error | string): void {
|
|
157
|
+
if (!this.config.enabled || !this.config.trackErrors) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const event: ThemeAnalyticsEventData = {
|
|
162
|
+
type: 'theme_error',
|
|
163
|
+
timestamp: Date.now(),
|
|
164
|
+
themeName,
|
|
165
|
+
data: {
|
|
166
|
+
error: error instanceof Error ? error.message : error,
|
|
167
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
this.addEvent(event);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Track theme revert
|
|
176
|
+
*/
|
|
177
|
+
trackThemeRevert(attemptedTheme: string, revertedToTheme: string | null, error: Error): void {
|
|
178
|
+
if (!this.config.enabled || !this.config.trackErrors) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const event: ThemeAnalyticsEventData = {
|
|
183
|
+
type: 'theme_revert',
|
|
184
|
+
timestamp: Date.now(),
|
|
185
|
+
themeName: attemptedTheme,
|
|
186
|
+
data: {
|
|
187
|
+
attemptedTheme,
|
|
188
|
+
revertedToTheme,
|
|
189
|
+
error: error.message,
|
|
190
|
+
stack: error.stack,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
this.addEvent(event);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Track CSS load
|
|
199
|
+
*/
|
|
200
|
+
trackCSSLoad(themeName: string, loadTime: number, size?: number): void {
|
|
201
|
+
if (!this.config.enabled) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const event: ThemeAnalyticsEventData = {
|
|
206
|
+
type: 'css_load',
|
|
207
|
+
timestamp: Date.now(),
|
|
208
|
+
themeName,
|
|
209
|
+
data: {
|
|
210
|
+
loadTime,
|
|
211
|
+
...(size ? { size } : {}),
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
this.addEvent(event);
|
|
216
|
+
|
|
217
|
+
this.trackPerformance('css_load_time', loadTime, 'ms');
|
|
218
|
+
if (size !== undefined) {
|
|
219
|
+
this.trackPerformance('css_size', size, 'bytes');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Track performance metric
|
|
225
|
+
*/
|
|
226
|
+
trackPerformance(name: string, value: number, unit: PerformanceMetric['unit'] = 'ms'): void {
|
|
227
|
+
if (!this.config.enabled || !this.config.trackPerformance) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const metric: PerformanceMetric = {
|
|
232
|
+
name,
|
|
233
|
+
value,
|
|
234
|
+
unit,
|
|
235
|
+
timestamp: Date.now(),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
this.metrics.push(metric);
|
|
239
|
+
|
|
240
|
+
// Keep only last N metrics
|
|
241
|
+
if (this.metrics.length > this.config.bufferSize) {
|
|
242
|
+
this.metrics.shift();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Notify handler
|
|
246
|
+
if (this.config.onPerformance) {
|
|
247
|
+
try {
|
|
248
|
+
this.config.onPerformance(metric);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
this.logger.error(
|
|
251
|
+
'Error in performance handler',
|
|
252
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
253
|
+
{ metric }
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Add event to buffer
|
|
261
|
+
*/
|
|
262
|
+
private addEvent(event: ThemeAnalyticsEventData): void {
|
|
263
|
+
this.events.push(event);
|
|
264
|
+
|
|
265
|
+
// Keep only last N events
|
|
266
|
+
if (this.events.length > this.config.bufferSize) {
|
|
267
|
+
this.events.shift();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Notify handler immediately
|
|
271
|
+
if (this.config.onEvent) {
|
|
272
|
+
try {
|
|
273
|
+
this.config.onEvent(event);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
this.logger.error(
|
|
276
|
+
'Error in event handler',
|
|
277
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
278
|
+
{ event }
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get all events
|
|
286
|
+
*/
|
|
287
|
+
getEvents(): ThemeAnalyticsEventData[] {
|
|
288
|
+
return [...this.events];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get all metrics
|
|
293
|
+
*/
|
|
294
|
+
getMetrics(): PerformanceMetric[] {
|
|
295
|
+
return [...this.metrics];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Get events by type
|
|
300
|
+
*/
|
|
301
|
+
getEventsByType(type: ThemeAnalyticsEvent): ThemeAnalyticsEventData[] {
|
|
302
|
+
return this.events.filter(event => event.type === type);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get metrics by name
|
|
307
|
+
*/
|
|
308
|
+
getMetricsByName(name: string): PerformanceMetric[] {
|
|
309
|
+
return this.metrics.filter(metric => metric.name === name);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get average performance metric
|
|
314
|
+
*/
|
|
315
|
+
getAverageMetric(name: string): number | null {
|
|
316
|
+
const metrics = this.getMetricsByName(name);
|
|
317
|
+
if (metrics.length === 0) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const sum = metrics.reduce((acc, metric) => acc + metric.value, 0);
|
|
322
|
+
return sum / metrics.length;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Clear all events and metrics
|
|
327
|
+
*/
|
|
328
|
+
clear(): void {
|
|
329
|
+
this.events = [];
|
|
330
|
+
this.metrics = [];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Start flush timer
|
|
335
|
+
*/
|
|
336
|
+
private startFlushTimer(): void {
|
|
337
|
+
if (this.flushTimer) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this.flushTimer = setInterval(() => {
|
|
342
|
+
// Auto-flush can be implemented here if needed
|
|
343
|
+
// For now, events are sent immediately via onEvent callback
|
|
344
|
+
}, this.config.flushInterval);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Stop flush timer
|
|
349
|
+
*/
|
|
350
|
+
private stopFlushTimer(): void {
|
|
351
|
+
if (this.flushTimer) {
|
|
352
|
+
clearInterval(this.flushTimer);
|
|
353
|
+
this.flushTimer = null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Enable analytics
|
|
359
|
+
*/
|
|
360
|
+
enable(): void {
|
|
361
|
+
this.config.enabled = true;
|
|
362
|
+
this.startFlushTimer();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Disable analytics
|
|
367
|
+
*/
|
|
368
|
+
disable(): void {
|
|
369
|
+
this.config.enabled = false;
|
|
370
|
+
this.stopFlushTimer();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Destroy analytics instance
|
|
375
|
+
*/
|
|
376
|
+
destroy(): void {
|
|
377
|
+
this.stopFlushTimer();
|
|
378
|
+
this.clear();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Create theme analytics instance
|
|
384
|
+
*/
|
|
385
|
+
export function createThemeAnalytics(config?: AnalyticsConfig): ThemeAnalytics {
|
|
386
|
+
return new ThemeAnalytics(config);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Global analytics instance (optional)
|
|
391
|
+
*/
|
|
392
|
+
let globalAnalytics: ThemeAnalytics | null = null;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get or create global analytics instance
|
|
396
|
+
*/
|
|
397
|
+
export function getGlobalAnalytics(): ThemeAnalytics {
|
|
398
|
+
if (!globalAnalytics) {
|
|
399
|
+
globalAnalytics = createThemeAnalytics();
|
|
400
|
+
}
|
|
401
|
+
return globalAnalytics;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Set global analytics instance
|
|
406
|
+
*/
|
|
407
|
+
export function setGlobalAnalytics(analytics: ThemeAnalytics): void {
|
|
408
|
+
globalAnalytics = analytics;
|
|
409
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Monitoring and Analytics Module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './ThemeAnalytics';
|
|
6
|
+
export {
|
|
7
|
+
ThemeAnalytics,
|
|
8
|
+
createThemeAnalytics,
|
|
9
|
+
getGlobalAnalytics,
|
|
10
|
+
setGlobalAnalytics,
|
|
11
|
+
} from './ThemeAnalytics';
|
|
12
|
+
export type {
|
|
13
|
+
ThemeAnalyticsEvent,
|
|
14
|
+
ThemeAnalyticsEventData,
|
|
15
|
+
PerformanceMetric,
|
|
16
|
+
AnalyticsConfig,
|
|
17
|
+
} from './ThemeAnalytics';
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Override System
|
|
3
|
+
*
|
|
4
|
+
* Provides a comprehensive system for overriding component styles and behavior
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Theme, ThemeOptions } from '../types';
|
|
8
|
+
import { createTheme } from '../createTheme';
|
|
9
|
+
import { deepMerge } from '../composeTheme';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Component override configuration
|
|
13
|
+
*/
|
|
14
|
+
export interface ComponentOverride {
|
|
15
|
+
/** Component name */
|
|
16
|
+
component: string;
|
|
17
|
+
/** Style overrides */
|
|
18
|
+
styleOverrides?: Record<string, any>;
|
|
19
|
+
/** Default props */
|
|
20
|
+
defaultProps?: Record<string, any>;
|
|
21
|
+
/** CSS class overrides */
|
|
22
|
+
classOverrides?: Record<string, string>;
|
|
23
|
+
/** CSS variable overrides */
|
|
24
|
+
cssVariableOverrides?: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Component override map
|
|
29
|
+
*/
|
|
30
|
+
export type ComponentOverrides = Record<string, ComponentOverride>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Override options
|
|
34
|
+
*/
|
|
35
|
+
export interface OverrideOptions {
|
|
36
|
+
/** Merge with existing overrides */
|
|
37
|
+
merge?: boolean;
|
|
38
|
+
/** Validate overrides */
|
|
39
|
+
validate?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Component Override Manager
|
|
44
|
+
*
|
|
45
|
+
* Manages component-level overrides for themes
|
|
46
|
+
*/
|
|
47
|
+
export class ComponentOverrideManager {
|
|
48
|
+
private overrides: ComponentOverrides = {};
|
|
49
|
+
private theme: Theme | null = null;
|
|
50
|
+
|
|
51
|
+
constructor(theme?: Theme) {
|
|
52
|
+
if (theme) {
|
|
53
|
+
this.theme = theme;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Set theme
|
|
59
|
+
*/
|
|
60
|
+
setTheme(theme: Theme): void {
|
|
61
|
+
this.theme = theme;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get theme with overrides applied
|
|
66
|
+
*/
|
|
67
|
+
getThemeWithOverrides(): Theme | null {
|
|
68
|
+
if (!this.theme) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Apply overrides to theme
|
|
73
|
+
const overriddenTheme = this.applyOverrides(this.theme);
|
|
74
|
+
return overriddenTheme;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Add component override
|
|
79
|
+
*/
|
|
80
|
+
addOverride(
|
|
81
|
+
component: string,
|
|
82
|
+
override: Omit<ComponentOverride, 'component'>,
|
|
83
|
+
options: OverrideOptions = {}
|
|
84
|
+
): void {
|
|
85
|
+
const { merge = true } = options;
|
|
86
|
+
|
|
87
|
+
if (merge && this.overrides[component]) {
|
|
88
|
+
// Merge with existing override
|
|
89
|
+
this.overrides[component] = {
|
|
90
|
+
...this.overrides[component],
|
|
91
|
+
...override,
|
|
92
|
+
styleOverrides: {
|
|
93
|
+
...this.overrides[component].styleOverrides,
|
|
94
|
+
...override.styleOverrides,
|
|
95
|
+
},
|
|
96
|
+
defaultProps: {
|
|
97
|
+
...this.overrides[component].defaultProps,
|
|
98
|
+
...override.defaultProps,
|
|
99
|
+
},
|
|
100
|
+
classOverrides: {
|
|
101
|
+
...this.overrides[component].classOverrides,
|
|
102
|
+
...override.classOverrides,
|
|
103
|
+
},
|
|
104
|
+
cssVariableOverrides: {
|
|
105
|
+
...this.overrides[component].cssVariableOverrides,
|
|
106
|
+
...override.cssVariableOverrides,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
} else {
|
|
110
|
+
// Replace override
|
|
111
|
+
this.overrides[component] = {
|
|
112
|
+
component,
|
|
113
|
+
...override,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Remove component override
|
|
120
|
+
*/
|
|
121
|
+
removeOverride(component: string): void {
|
|
122
|
+
delete this.overrides[component];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get component override
|
|
127
|
+
*/
|
|
128
|
+
getOverride(component: string): ComponentOverride | undefined {
|
|
129
|
+
return this.overrides[component];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get all overrides
|
|
134
|
+
*/
|
|
135
|
+
getAllOverrides(): ComponentOverrides {
|
|
136
|
+
return { ...this.overrides };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Clear all overrides
|
|
141
|
+
*/
|
|
142
|
+
clearOverrides(): void {
|
|
143
|
+
this.overrides = {};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Apply overrides to theme
|
|
148
|
+
*/
|
|
149
|
+
private applyOverrides(theme: Theme): Theme {
|
|
150
|
+
if (Object.keys(this.overrides).length === 0) {
|
|
151
|
+
return theme;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Create theme options with component overrides
|
|
155
|
+
const themeOptions: ThemeOptions = {
|
|
156
|
+
...theme,
|
|
157
|
+
custom: {
|
|
158
|
+
...theme.custom,
|
|
159
|
+
componentOverrides: this.overrides,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return createTheme(themeOptions);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get CSS variables for component overrides
|
|
168
|
+
*/
|
|
169
|
+
getComponentCSSVariables(component: string): Record<string, string> {
|
|
170
|
+
const override = this.overrides[component];
|
|
171
|
+
if (!override || !override.cssVariableOverrides) {
|
|
172
|
+
return {};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const variables: Record<string, string> = {};
|
|
176
|
+
for (const [key, value] of Object.entries(override.cssVariableOverrides)) {
|
|
177
|
+
variables[`--atomix-${component}-${key}`] = value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return variables;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get all CSS variables for overrides
|
|
185
|
+
*/
|
|
186
|
+
getAllCSSVariables(): Record<string, string> {
|
|
187
|
+
const variables: Record<string, string> = {};
|
|
188
|
+
|
|
189
|
+
for (const component of Object.keys(this.overrides)) {
|
|
190
|
+
const componentVars = this.getComponentCSSVariables(component);
|
|
191
|
+
Object.assign(variables, componentVars);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return variables;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Export overrides as JSON
|
|
199
|
+
*/
|
|
200
|
+
exportOverrides(): string {
|
|
201
|
+
return JSON.stringify(this.overrides, null, 2);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Import overrides from JSON
|
|
206
|
+
*/
|
|
207
|
+
importOverrides(json: string, options: OverrideOptions = {}): void {
|
|
208
|
+
try {
|
|
209
|
+
const imported = JSON.parse(json) as ComponentOverrides;
|
|
210
|
+
const { merge = false } = options;
|
|
211
|
+
|
|
212
|
+
if (merge) {
|
|
213
|
+
// Merge with existing
|
|
214
|
+
this.overrides = deepMerge(this.overrides, imported) as ComponentOverrides;
|
|
215
|
+
} else {
|
|
216
|
+
// Replace
|
|
217
|
+
this.overrides = imported;
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
throw new Error(`Failed to import overrides: ${error instanceof Error ? error.message : String(error)}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create component override manager
|
|
227
|
+
*/
|
|
228
|
+
export function createComponentOverrideManager(theme?: Theme): ComponentOverrideManager {
|
|
229
|
+
return new ComponentOverrideManager(theme);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Helper to create component override
|
|
234
|
+
*/
|
|
235
|
+
export function createComponentOverride(
|
|
236
|
+
component: string,
|
|
237
|
+
override: Omit<ComponentOverride, 'component'>
|
|
238
|
+
): ComponentOverride {
|
|
239
|
+
return {
|
|
240
|
+
component,
|
|
241
|
+
...override,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Override System Module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './ComponentOverrides';
|
|
6
|
+
export {
|
|
7
|
+
ComponentOverrideManager,
|
|
8
|
+
createComponentOverrideManager,
|
|
9
|
+
createComponentOverride,
|
|
10
|
+
} from './ComponentOverrides';
|
|
11
|
+
export type {
|
|
12
|
+
ComponentOverride,
|
|
13
|
+
ComponentOverrides,
|
|
14
|
+
OverrideOptions,
|
|
15
|
+
} from './ComponentOverrides';
|