@shohojdhara/atomix 0.5.2 → 0.5.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/atomix.config.ts +33 -33
- package/dist/config.d.ts +187 -112
- package/dist/config.js +7 -49
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1958 -900
- package/dist/index.esm.js +2275 -383
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2327 -417
- 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 +1390 -276
- package/dist/theme.js +2129 -621
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/scripts/cli/internal/config-loader.js +30 -20
- package/src/lib/config/index.ts +38 -362
- package/src/lib/config/loader.ts +419 -0
- package/src/lib/config/public-api.ts +43 -0
- package/src/lib/config/types.ts +389 -0
- package/src/lib/config/validator.ts +305 -0
- package/src/lib/theme/adapters/index.ts +1 -1
- package/src/lib/theme/adapters/themeAdapter.ts +358 -229
- package/src/lib/theme/components/ThemeToggle.tsx +276 -0
- package/src/lib/theme/config/configLoader.ts +351 -0
- package/src/lib/theme/config/loader.ts +221 -0
- package/src/lib/theme/core/createTheme.ts +126 -50
- package/src/lib/theme/core/createThemeObject.ts +7 -4
- package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
- package/src/lib/theme/index.ts +322 -38
- package/src/lib/theme/runtime/ThemeProvider.tsx +44 -10
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
- package/src/lib/theme/runtime/useTheme.ts +1 -0
- package/src/lib/theme/tokens/tokens.ts +101 -1
- package/src/lib/theme/types.ts +91 -0
- package/src/lib/theme/utils/performanceMonitor.ts +315 -0
- package/src/lib/theme/utils/responsive.ts +280 -0
- package/src/lib/theme/utils/themeUtils.ts +531 -117
- package/src/styles/05-objects/_objects.masonry-grid.scss +3 -3
package/dist/theme.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import "fs";
|
|
2
|
+
|
|
3
|
+
import "path";
|
|
4
|
+
|
|
1
5
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
6
|
|
|
3
|
-
import { createContext, useRef, useEffect, useCallback, useMemo, useState, useContext, Component } from "react";
|
|
7
|
+
import React, { createContext, useRef, useEffect, useCallback, useMemo, useState, useContext, Component } from "react";
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* Design Tokens
|
|
@@ -296,7 +300,51 @@ import { createContext, useRef, useEffect, useCallback, useMemo, useState, useCo
|
|
|
296
300
|
"breakpoint-md": "768px",
|
|
297
301
|
"breakpoint-lg": "992px",
|
|
298
302
|
"breakpoint-xl": "1200px",
|
|
299
|
-
"breakpoint-xxl": "1440px"
|
|
303
|
+
"breakpoint-xxl": "1440px",
|
|
304
|
+
// Advanced Features - Interactive Effects (Phase 2)
|
|
305
|
+
"interactive-vortex-enabled": "false",
|
|
306
|
+
"interactive-vortex-strength": "0.5",
|
|
307
|
+
"interactive-vortex-radius": "100",
|
|
308
|
+
"interactive-vortex-decay": "0.8",
|
|
309
|
+
"interactive-chromatic-enabled": "false",
|
|
310
|
+
"interactive-chromatic-mode": "lateral",
|
|
311
|
+
"interactive-chromatic-red-shift": "0.02",
|
|
312
|
+
"interactive-chromatic-green-shift": "0",
|
|
313
|
+
"interactive-chromatic-blue-shift": "-0.02",
|
|
314
|
+
"interactive-chromatic-edge-only": "false",
|
|
315
|
+
"interactive-chromatic-edge-threshold": "0.5",
|
|
316
|
+
"interactive-mouse-sensitivity": "1.0",
|
|
317
|
+
"interactive-mouse-trail-effect": "false",
|
|
318
|
+
"interactive-animation-speed-base": "1.0",
|
|
319
|
+
"interactive-animation-speed-multiplier": "1.0",
|
|
320
|
+
// Advanced Features - Optimization (Phase 3)
|
|
321
|
+
"optimization-breakpoint-mobile": "0px",
|
|
322
|
+
"optimization-breakpoint-tablet": "768px",
|
|
323
|
+
"optimization-breakpoint-desktop": "1024px",
|
|
324
|
+
"optimization-breakpoint-wide": "1440px",
|
|
325
|
+
"optimization-device-scaling-mobile": "0.5",
|
|
326
|
+
"optimization-device-scaling-tablet": "0.75",
|
|
327
|
+
"optimization-device-scaling-desktop": "1.0",
|
|
328
|
+
"optimization-performance-fps-target": "60",
|
|
329
|
+
"optimization-auto-scaling-enabled": "false",
|
|
330
|
+
"optimization-auto-scaling-low-end": "0.5",
|
|
331
|
+
"optimization-auto-scaling-mid-range": "0.75",
|
|
332
|
+
"optimization-auto-scaling-high-end": "1.0",
|
|
333
|
+
// Advanced Features - Visual Polish (Phase 4)
|
|
334
|
+
"visual-polish-border-iridescent-glow": "false",
|
|
335
|
+
"visual-polish-border-shimmer-effect": "false",
|
|
336
|
+
"visual-polish-border-beveled-edges": "false",
|
|
337
|
+
"visual-polish-border-pulsing-glow": "false",
|
|
338
|
+
"visual-polish-content-aware-blur-enabled": "false",
|
|
339
|
+
"visual-polish-content-aware-depth-detection": "false",
|
|
340
|
+
"visual-polish-content-aware-edge-preservation": "false",
|
|
341
|
+
"visual-polish-content-aware-variable-radius": "false",
|
|
342
|
+
"visual-polish-holographic-enabled": "false",
|
|
343
|
+
"visual-polish-holographic-rainbow-diffraction": "false",
|
|
344
|
+
"visual-polish-holographic-scanline-animation": "false",
|
|
345
|
+
"visual-polish-holographic-grid-overlay": "false",
|
|
346
|
+
"visual-polish-holographic-data-stream": "false",
|
|
347
|
+
"visual-polish-holographic-pulse-rings": "false"
|
|
300
348
|
};
|
|
301
349
|
|
|
302
350
|
/**
|
|
@@ -376,412 +424,278 @@ import { createContext, useRef, useEffect, useCallback, useMemo, useState, useCo
|
|
|
376
424
|
}
|
|
377
425
|
|
|
378
426
|
/**
|
|
379
|
-
* Theme
|
|
427
|
+
* Theme Adapter
|
|
380
428
|
*
|
|
381
|
-
*
|
|
382
|
-
* Provides custom error classes and logging utilities.
|
|
383
|
-
*/
|
|
384
|
-
/**
|
|
385
|
-
* Theme error codes
|
|
386
|
-
*/ var ThemeErrorCode, LogLevel;
|
|
387
|
-
|
|
388
|
-
!function(ThemeErrorCode) {
|
|
389
|
-
/** Theme not found in registry */
|
|
390
|
-
ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
|
|
391
|
-
/** Theme failed to load */
|
|
392
|
-
ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
|
|
393
|
-
/** Theme validation failed */
|
|
394
|
-
ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
|
|
395
|
-
/** Configuration loading failed */
|
|
396
|
-
ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
|
|
397
|
-
/** Configuration validation failed */
|
|
398
|
-
ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
|
|
399
|
-
/** Circular dependency detected */
|
|
400
|
-
ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
|
|
401
|
-
/** Missing dependency */
|
|
402
|
-
ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
|
|
403
|
-
/** Storage operation failed */
|
|
404
|
-
ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
|
|
405
|
-
/** Invalid theme name */
|
|
406
|
-
ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
|
|
407
|
-
/** CSS injection failed */
|
|
408
|
-
ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
|
|
409
|
-
/** Invalid color format */
|
|
410
|
-
ThemeErrorCode.INVALID_COLOR_FORMAT = "INVALID_COLOR_FORMAT",
|
|
411
|
-
/** Missing required token */
|
|
412
|
-
ThemeErrorCode.MISSING_REQUIRED_TOKEN = "MISSING_REQUIRED_TOKEN",
|
|
413
|
-
/** Accessibility contrast violation */
|
|
414
|
-
ThemeErrorCode.CONTRAST_VIOLATION = "CONTRAST_VIOLATION",
|
|
415
|
-
/** Invalid token type */
|
|
416
|
-
ThemeErrorCode.INVALID_TOKEN_TYPE = "INVALID_TOKEN_TYPE",
|
|
417
|
-
/** Unknown error */
|
|
418
|
-
ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
|
419
|
-
}(ThemeErrorCode || (ThemeErrorCode = {}));
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Custom error class for theme-related errors
|
|
429
|
+
* Converts between Theme objects and DesignTokens
|
|
423
430
|
*/
|
|
424
|
-
class ThemeError extends Error {
|
|
425
|
-
constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
|
|
426
|
-
super(message), this.name = "ThemeError", this.code = code, this.context = context,
|
|
427
|
-
this.timestamp = Date.now(),
|
|
428
|
-
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
429
|
-
Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Convert error to JSON for logging
|
|
433
|
-
*/ toJSON() {
|
|
434
|
-
return {
|
|
435
|
-
name: this.name,
|
|
436
|
-
message: this.message,
|
|
437
|
-
code: this.code,
|
|
438
|
-
context: this.context,
|
|
439
|
-
timestamp: this.timestamp,
|
|
440
|
-
stack: this.stack
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Log level
|
|
447
|
-
*/ !function(LogLevel) {
|
|
448
|
-
LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
|
|
449
|
-
LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
|
|
450
|
-
}(LogLevel || (LogLevel = {}));
|
|
451
|
-
|
|
452
431
|
/**
|
|
453
|
-
* Theme
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
* Log a warning
|
|
485
|
-
*/ warn(message, context) {
|
|
486
|
-
this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
|
|
487
|
-
this.config.onWarn?.(message, context));
|
|
432
|
+
* Convert a Theme object to DesignTokens
|
|
433
|
+
*/ function themeToDesignTokens(theme) {
|
|
434
|
+
const tokens = {};
|
|
435
|
+
// Convert colors
|
|
436
|
+
if (theme.palette) {
|
|
437
|
+
// Primary color
|
|
438
|
+
if (theme.palette.primary) {
|
|
439
|
+
const primaryMain = theme.palette.primary.main;
|
|
440
|
+
tokens.primary = primaryMain;
|
|
441
|
+
const rgb = hexToRgb$2(primaryMain);
|
|
442
|
+
rgb && (tokens["primary-rgb"] = rgb);
|
|
443
|
+
}
|
|
444
|
+
// Secondary color
|
|
445
|
+
if (theme.palette.secondary) {
|
|
446
|
+
const secondaryMain = theme.palette.secondary.main;
|
|
447
|
+
tokens.secondary = secondaryMain;
|
|
448
|
+
const rgb = hexToRgb$2(secondaryMain);
|
|
449
|
+
rgb && (tokens["secondary-rgb"] = rgb);
|
|
450
|
+
}
|
|
451
|
+
// Other colors
|
|
452
|
+
const colorKeys = [ "error", "warning", "info", "success" ];
|
|
453
|
+
for (const key of colorKeys) if (theme.palette[key]) {
|
|
454
|
+
const colorMain = theme.palette[key].main;
|
|
455
|
+
tokens[key] = colorMain;
|
|
456
|
+
const rgb = hexToRgb$2(colorMain);
|
|
457
|
+
rgb && (tokens[`${key}-rgb`] = rgb);
|
|
458
|
+
}
|
|
459
|
+
// Background colors
|
|
460
|
+
theme.palette.background && (tokens["body-bg"] = theme.palette.background.default),
|
|
461
|
+
// Text colors
|
|
462
|
+
theme.palette.text && (tokens["body-color"] = theme.palette.text.primary);
|
|
488
463
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
464
|
+
// Convert typography
|
|
465
|
+
// Convert border radius
|
|
466
|
+
if (theme.typography && (tokens["body-font-family"] = theme.typography.fontFamily,
|
|
467
|
+
tokens["body-font-size"] = `${theme.typography.fontSize}px`, tokens["font-weight-normal"] = `${theme.typography.fontWeightRegular}`,
|
|
468
|
+
tokens["font-weight-bold"] = `${theme.typography.fontWeightBold}`),
|
|
469
|
+
// Convert spacing
|
|
470
|
+
"function" == typeof theme.spacing && (
|
|
471
|
+
// If spacing is a function, call it with some values to get results
|
|
472
|
+
tokens["spacing-1"] = theme.spacing(1), tokens["spacing-2"] = theme.spacing(2),
|
|
473
|
+
tokens["spacing-4"] = theme.spacing(4)),
|
|
474
|
+
// Convert breakpoints
|
|
475
|
+
theme.breakpoints?.values && (tokens["breakpoint-xs"] = `${theme.breakpoints.values.xs}px`,
|
|
476
|
+
tokens["breakpoint-sm"] = `${theme.breakpoints.values.sm}px`, tokens["breakpoint-md"] = `${theme.breakpoints.values.md}px`,
|
|
477
|
+
tokens["breakpoint-lg"] = `${theme.breakpoints.values.lg}px`, tokens["breakpoint-xl"] = `${theme.breakpoints.values.xl}px`),
|
|
478
|
+
// Convert shadows
|
|
479
|
+
theme.shadows && (tokens["box-shadow"] = theme.shadows[2], // Use a moderate shadow
|
|
480
|
+
tokens["box-shadow-sm"] = theme.shadows[1], tokens["box-shadow-lg"] = theme.shadows[3]),
|
|
481
|
+
// Convert transitions
|
|
482
|
+
theme.transitions && (tokens["transition-duration-base"] = `${theme.transitions.duration.standard}ms`),
|
|
483
|
+
// Convert z-index
|
|
484
|
+
theme.zIndex && (tokens["z-modal"] = `${theme.zIndex.modal}`, tokens["z-popover"] = `${theme.zIndex.popover}`,
|
|
485
|
+
tokens["z-tooltip"] = `${theme.zIndex.tooltip}`), theme.borderRadius) {
|
|
486
|
+
const baseRadius = theme.borderRadius.base;
|
|
487
|
+
tokens["border-radius"] = "number" == typeof baseRadius ? `${baseRadius}px` : baseRadius;
|
|
494
488
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
489
|
+
// Add advanced feature tokens if available in theme
|
|
490
|
+
if (theme.custom) {
|
|
491
|
+
// Interactive Effects (Phase 2)
|
|
492
|
+
if (theme.custom.interactiveEffects) {
|
|
493
|
+
const ie = theme.custom.interactiveEffects;
|
|
494
|
+
// Vortex effects
|
|
495
|
+
ie.vortex && (tokens["interactive-vortex-enabled"] = String(ie.vortex.enabled ?? !1),
|
|
496
|
+
tokens["interactive-vortex-strength"] = String(ie.vortex.strength ?? .5), tokens["interactive-vortex-radius"] = String(ie.vortex.radius ?? 100),
|
|
497
|
+
tokens["interactive-vortex-decay"] = String(ie.vortex.decay ?? .8)),
|
|
498
|
+
// Chromatic aberration
|
|
499
|
+
ie.chromaticAberration && (tokens["interactive-chromatic-enabled"] = String(ie.chromaticAberration.enabled ?? !1),
|
|
500
|
+
tokens["interactive-chromatic-mode"] = ie.chromaticAberration.mode ?? "lateral",
|
|
501
|
+
tokens["interactive-chromatic-red-shift"] = String(ie.chromaticAberration.redShift ?? .02),
|
|
502
|
+
tokens["interactive-chromatic-green-shift"] = String(ie.chromaticAberration.greenShift ?? 0),
|
|
503
|
+
tokens["interactive-chromatic-blue-shift"] = String(ie.chromaticAberration.blueShift ?? -.02),
|
|
504
|
+
tokens["interactive-chromatic-edge-only"] = String(ie.chromaticAberration.edgeOnly ?? !1),
|
|
505
|
+
tokens["interactive-chromatic-edge-threshold"] = String(ie.chromaticAberration.edgeThreshold ?? .5)),
|
|
506
|
+
// Mouse interaction
|
|
507
|
+
ie.mouseInteraction && (tokens["interactive-mouse-sensitivity"] = String(ie.mouseInteraction.sensitivity ?? 1),
|
|
508
|
+
tokens["interactive-mouse-trail-effect"] = String(ie.mouseInteraction.trailEffect ?? !1)),
|
|
509
|
+
// Animation speed
|
|
510
|
+
ie.animationSpeed && (tokens["interactive-animation-speed-base"] = String(ie.animationSpeed.base ?? 1),
|
|
511
|
+
tokens["interactive-animation-speed-multiplier"] = String(ie.animationSpeed.timeMultiplier ?? 1));
|
|
512
|
+
}
|
|
513
|
+
// Optimization (Phase 3)
|
|
514
|
+
if (theme.custom.optimization) {
|
|
515
|
+
const opt = theme.custom.optimization;
|
|
516
|
+
// Responsive breakpoints
|
|
517
|
+
opt.responsive && (opt.responsive.breakpoints && (tokens["optimization-breakpoint-mobile"] = opt.responsive.breakpoints.mobile ?? "0px",
|
|
518
|
+
tokens["optimization-breakpoint-tablet"] = opt.responsive.breakpoints.tablet ?? "768px",
|
|
519
|
+
tokens["optimization-breakpoint-desktop"] = opt.responsive.breakpoints.desktop ?? "1024px",
|
|
520
|
+
tokens["optimization-breakpoint-wide"] = opt.responsive.breakpoints.wide ?? "1440px"),
|
|
521
|
+
opt.responsive.deviceScaling && (tokens["optimization-device-scaling-mobile"] = String(opt.responsive.deviceScaling.mobile ?? .5),
|
|
522
|
+
tokens["optimization-device-scaling-tablet"] = String(opt.responsive.deviceScaling.tablet ?? .75),
|
|
523
|
+
tokens["optimization-device-scaling-desktop"] = String(opt.responsive.deviceScaling.desktop ?? 1))),
|
|
524
|
+
// Performance settings
|
|
525
|
+
opt.performance && (tokens["optimization-performance-fps-target"] = String(opt.performance.fpsTarget ?? 60),
|
|
526
|
+
tokens["optimization-auto-scaling-enabled"] = String(opt.performance.autoScaling ?? !1)),
|
|
527
|
+
// Auto-scaling settings
|
|
528
|
+
opt.autoScaling && (tokens["optimization-auto-scaling-enabled"] = String(opt.autoScaling.enabled ?? !1),
|
|
529
|
+
tokens["optimization-auto-scaling-low-end"] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? .5),
|
|
530
|
+
tokens["optimization-auto-scaling-mid-range"] = String(opt.autoScaling.qualityThresholds?.midRange ?? .75),
|
|
531
|
+
tokens["optimization-auto-scaling-high-end"] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1));
|
|
532
|
+
}
|
|
533
|
+
// Visual Polish (Phase 4)
|
|
534
|
+
if (theme.custom.visualPolish) {
|
|
535
|
+
const vp = theme.custom.visualPolish;
|
|
536
|
+
vp.borders && (tokens["visual-polish-border-iridescent-glow"] = String(vp.borders.iridescentGlow ?? !1),
|
|
537
|
+
tokens["visual-polish-border-shimmer-effect"] = String(vp.borders.shimmerEffect ?? !1),
|
|
538
|
+
tokens["visual-polish-border-beveled-edges"] = String(vp.borders.beveledEdges ?? !1),
|
|
539
|
+
tokens["visual-polish-border-pulsing-glow"] = String(vp.borders.pulsingGlow ?? !1)),
|
|
540
|
+
vp.contentAwareBlur && (tokens["visual-polish-content-aware-blur-enabled"] = String(vp.contentAwareBlur.enabled ?? !1),
|
|
541
|
+
tokens["visual-polish-content-aware-depth-detection"] = String(vp.contentAwareBlur.depthDetection ?? !1),
|
|
542
|
+
tokens["visual-polish-content-aware-edge-preservation"] = String(vp.contentAwareBlur.edgePreservation ?? !1),
|
|
543
|
+
tokens["visual-polish-content-aware-variable-radius"] = String(vp.contentAwareBlur.variableRadius ?? !1)),
|
|
544
|
+
vp.holographicEffects && (tokens["visual-polish-holographic-enabled"] = String(vp.holographicEffects.enabled ?? !1),
|
|
545
|
+
tokens["visual-polish-holographic-rainbow-diffraction"] = String(vp.holographicEffects.rainbowDiffraction ?? !1),
|
|
546
|
+
tokens["visual-polish-holographic-scanline-animation"] = String(vp.holographicEffects.scanlineAnimation ?? !1),
|
|
547
|
+
tokens["visual-polish-holographic-grid-overlay"] = String(vp.holographicEffects.gridOverlay ?? !1),
|
|
548
|
+
tokens["visual-polish-holographic-data-stream"] = String(vp.holographicEffects.dataStream ?? !1),
|
|
549
|
+
tokens["visual-polish-holographic-pulse-rings"] = String(vp.holographicEffects.pulseRings ?? !1));
|
|
550
|
+
}
|
|
499
551
|
}
|
|
552
|
+
// Create full tokens object with defaults
|
|
553
|
+
return createTokens(tokens);
|
|
500
554
|
}
|
|
501
555
|
|
|
502
556
|
/**
|
|
503
|
-
*
|
|
504
|
-
*/ let defaultLogger = null;
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Get or create default logger
|
|
508
|
-
*/ function getLogger() {
|
|
509
|
-
return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Core Theme Functions
|
|
514
|
-
*
|
|
515
|
-
* Simplified theme system using DesignTokens only.
|
|
516
|
-
* Config-first approach: loads from atomix.config.ts when no input is provided.
|
|
517
|
-
*/
|
|
518
|
-
/**
|
|
519
|
-
* Create theme CSS from DesignTokens
|
|
557
|
+
* Converts an AtomixConfig to DesignTokens
|
|
520
558
|
*
|
|
521
|
-
*
|
|
559
|
+
* This function maps the configuration from the user-facing format
|
|
560
|
+
* to the internal DesignTokens format used by the theme system.
|
|
522
561
|
*
|
|
523
|
-
* @param
|
|
524
|
-
* @
|
|
525
|
-
* @returns CSS string with custom properties
|
|
526
|
-
* @throws Error if config loading fails when no input is provided
|
|
562
|
+
* @param config - The configuration object to convert
|
|
563
|
+
* @returns DesignTokens object ready for theme generation
|
|
527
564
|
*
|
|
528
565
|
* @example
|
|
529
566
|
* ```typescript
|
|
530
|
-
*
|
|
531
|
-
* const css = createTheme();
|
|
532
|
-
*
|
|
533
|
-
* // Using DesignTokens
|
|
534
|
-
* const css = createTheme({
|
|
535
|
-
* 'primary': '#7c3aed',
|
|
536
|
-
* 'spacing-4': '1rem',
|
|
537
|
-
* });
|
|
567
|
+
* import { configToTokens } from '@shohojdhara/atomix/theme';
|
|
538
568
|
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
569
|
+
* const config = {
|
|
570
|
+
* prefix: 'myapp',
|
|
571
|
+
* theme: { extend: { colors: { primary: { main: '#7AFFD7' } } } }
|
|
572
|
+
* };
|
|
573
|
+
* const tokens = configToTokens(config);
|
|
541
574
|
* ```
|
|
542
|
-
*/ function
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
prefix: options.prefix,
|
|
548
|
-
pattern: prefixPattern.toString()
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
// Validate selector if provided
|
|
552
|
-
if (options?.selector && ("string" != typeof options.selector || 0 === options.selector.trim().length)) throw new ThemeError(`Invalid CSS selector: "${options.selector}". Selector must be a non-empty string (e.g., ":root", ".my-theme").`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
|
|
553
|
-
selector: options.selector
|
|
554
|
-
});
|
|
555
|
-
// Determine tokens based on input
|
|
556
|
-
let tokens;
|
|
557
|
-
if (input) {
|
|
558
|
-
// Validate input tokens structure
|
|
559
|
-
if ("object" != typeof input || null === input || Array.isArray(input)) throw new ThemeError(`Invalid tokens input. Expected an object with DesignTokens, but received: ${typeof input}.`, ThemeErrorCode.THEME_VALIDATION_FAILED, {
|
|
560
|
-
inputType: typeof input
|
|
561
|
-
});
|
|
562
|
-
// Use DesignTokens directly
|
|
563
|
-
tokens = input;
|
|
564
|
-
}
|
|
565
|
-
// Merge with defaults and generate CSS
|
|
566
|
-
else
|
|
567
|
-
// Auto-loading config from file system is removed for browser compatibility.
|
|
568
|
-
// If no input is provided, we return an empty theme (using defaults only) or user must provide tokens.
|
|
569
|
-
// This allows createTheme to be isomorphic.
|
|
570
|
-
// Warn in development if no input provided
|
|
571
|
-
"production" !== process.env.NODE_ENV && "undefined" != typeof window && console.warn("Atomix: createTheme() called without tokens. Using default tokens only."),
|
|
572
|
-
tokens = {};
|
|
573
|
-
const allTokens = createTokens(tokens), prefix = options?.prefix ?? "atomix";
|
|
574
|
-
// Get prefix from options or use default
|
|
575
|
-
return generateCSSVariables$1(allTokens, {
|
|
576
|
-
...options,
|
|
577
|
-
prefix: prefix
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Theme Composition Utilities
|
|
583
|
-
*
|
|
584
|
-
* Simplified utilities for composing and merging DesignTokens.
|
|
585
|
-
*/
|
|
586
|
-
// ============================================================================
|
|
587
|
-
// Deep Merge Utility
|
|
588
|
-
// ============================================================================
|
|
589
|
-
/**
|
|
590
|
-
* Check if value is an object
|
|
591
|
-
*/ function isObject$6(item) {
|
|
592
|
-
return item && "object" == typeof item && !Array.isArray(item) && "function" != typeof item;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Deep merge multiple objects
|
|
597
|
-
* Later objects override earlier ones
|
|
598
|
-
*/ function deepMerge(...objects) {
|
|
599
|
-
if (0 === objects.length) return {};
|
|
600
|
-
if (1 === objects.length) return objects[0];
|
|
601
|
-
const [target, ...sources] = objects, result = {
|
|
602
|
-
...target
|
|
575
|
+
*/ function configToTokens(config) {
|
|
576
|
+
const prefix = config.prefix || "atomix", theme = config.theme || {};
|
|
577
|
+
// Start with default tokens
|
|
578
|
+
let tokens = {
|
|
579
|
+
...defaultTokens
|
|
603
580
|
};
|
|
604
|
-
|
|
605
|
-
if
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
581
|
+
// Apply theme extensions
|
|
582
|
+
// Apply advanced features if available in config
|
|
583
|
+
if (theme.extend &&
|
|
584
|
+
// Apply extensions to tokens
|
|
585
|
+
Object.entries(theme.extend).forEach((([category, values]) => {
|
|
586
|
+
"object" == typeof values && null !== values && Object.entries(values).forEach((([key, value]) => {
|
|
587
|
+
// Map theme categories to token names
|
|
588
|
+
const tokenName = `${category}-${key}`;
|
|
589
|
+
"string" == typeof value || "number" == typeof value ? tokens[tokenName] = String(value) : "object" == typeof value && null !== value &&
|
|
590
|
+
// Handle nested objects like color scales
|
|
591
|
+
Object.entries(value).forEach((([nestedKey, nestedValue]) => {
|
|
592
|
+
"string" != typeof nestedValue && "number" != typeof nestedValue || (tokens[`${tokenName}-${nestedKey}`] = String(nestedValue));
|
|
593
|
+
}));
|
|
594
|
+
}));
|
|
595
|
+
})),
|
|
596
|
+
// Apply theme tokens if provided (completely replacing defaults)
|
|
597
|
+
theme.tokens && (tokens = {
|
|
598
|
+
...tokens,
|
|
599
|
+
...theme.tokens
|
|
600
|
+
}), config) {
|
|
601
|
+
// Interactive Effects (Phase 2)
|
|
602
|
+
if (config.interactiveEffects) {
|
|
603
|
+
const ie = config.interactiveEffects;
|
|
604
|
+
// Vortex effects
|
|
605
|
+
ie.vortex && (tokens["interactive-vortex-enabled"] = String(ie.vortex.enabled ?? !1),
|
|
606
|
+
tokens["interactive-vortex-strength"] = String(ie.vortex.strength ?? .5), tokens["interactive-vortex-radius"] = String(ie.vortex.radius ?? 100),
|
|
607
|
+
tokens["interactive-vortex-decay"] = String(ie.vortex.decay ?? .8), tokens["interactive-vortex-curl-noise"] = String(ie.vortex.curlNoise ?? !1),
|
|
608
|
+
tokens["interactive-vortex-velocity-tracking"] = String(ie.vortex.velocityTracking ?? !1)),
|
|
609
|
+
// Chromatic aberration
|
|
610
|
+
ie.chromaticAberration && (tokens["interactive-chromatic-enabled"] = String(ie.chromaticAberration.enabled ?? !1),
|
|
611
|
+
tokens["interactive-chromatic-mode"] = ie.chromaticAberration.mode ?? "lateral",
|
|
612
|
+
tokens["interactive-chromatic-red-shift"] = String(ie.chromaticAberration.redShift ?? .02),
|
|
613
|
+
tokens["interactive-chromatic-green-shift"] = String(ie.chromaticAberration.greenShift ?? 0),
|
|
614
|
+
tokens["interactive-chromatic-blue-shift"] = String(ie.chromaticAberration.blueShift ?? -.02),
|
|
615
|
+
tokens["interactive-chromatic-edge-only"] = String(ie.chromaticAberration.edgeOnly ?? !1),
|
|
616
|
+
tokens["interactive-chromatic-edge-threshold"] = String(ie.chromaticAberration.edgeThreshold ?? .5)),
|
|
617
|
+
// Mouse interaction
|
|
618
|
+
ie.mouseInteraction && (tokens["interactive-mouse-sensitivity"] = String(ie.mouseInteraction.sensitivity ?? 1),
|
|
619
|
+
tokens["interactive-mouse-trail-effect"] = String(ie.mouseInteraction.trailEffect ?? !1),
|
|
620
|
+
tokens["interactive-mouse-pressure-sensitivity"] = String(ie.mouseInteraction.pressureSensitivity ?? !1)),
|
|
621
|
+
// Animation speed
|
|
622
|
+
ie.animationSpeed && (tokens["interactive-animation-speed-base"] = String(ie.animationSpeed.base ?? 1),
|
|
623
|
+
tokens["interactive-animation-speed-multiplier"] = String(ie.animationSpeed.timeMultiplier ?? 1));
|
|
624
|
+
}
|
|
625
|
+
// Optimization (Phase 3)
|
|
626
|
+
if (config.optimization) {
|
|
627
|
+
const opt = config.optimization;
|
|
628
|
+
// Responsive breakpoints
|
|
629
|
+
opt.responsive && (opt.responsive.breakpoints && (tokens["optimization-breakpoint-mobile"] = opt.responsive.breakpoints.mobile ?? "0px",
|
|
630
|
+
tokens["optimization-breakpoint-tablet"] = opt.responsive.breakpoints.tablet ?? "768px",
|
|
631
|
+
tokens["optimization-breakpoint-desktop"] = opt.responsive.breakpoints.desktop ?? "1024px",
|
|
632
|
+
tokens["optimization-breakpoint-wide"] = opt.responsive.breakpoints.wide ?? "1440px"),
|
|
633
|
+
opt.responsive.deviceScaling && (tokens["optimization-device-scaling-mobile"] = String(opt.responsive.deviceScaling.mobile ?? .5),
|
|
634
|
+
tokens["optimization-device-scaling-tablet"] = String(opt.responsive.deviceScaling.tablet ?? .75),
|
|
635
|
+
tokens["optimization-device-scaling-desktop"] = String(opt.responsive.deviceScaling.desktop ?? 1))),
|
|
636
|
+
// Performance settings
|
|
637
|
+
opt.performance && (tokens["optimization-performance-fps-target"] = String(opt.performance.fpsTarget ?? 60),
|
|
638
|
+
tokens["optimization-auto-scaling-enabled"] = String(opt.performance.autoScaling ?? !1),
|
|
639
|
+
tokens["optimization-monitor-dashboard-enabled"] = String(opt.performance.monitorDashboard ?? !1)),
|
|
640
|
+
// Auto-scaling settings
|
|
641
|
+
opt.autoScaling && (tokens["optimization-auto-scaling-enabled"] = String(opt.autoScaling.enabled ?? !1),
|
|
642
|
+
tokens["optimization-auto-scaling-low-end"] = String(opt.autoScaling.qualityThresholds?.lowEnd ?? .5),
|
|
643
|
+
tokens["optimization-auto-scaling-mid-range"] = String(opt.autoScaling.qualityThresholds?.midRange ?? .75),
|
|
644
|
+
tokens["optimization-auto-scaling-high-end"] = String(opt.autoScaling.qualityThresholds?.highEnd ?? 1));
|
|
645
|
+
}
|
|
646
|
+
// Visual Polish (Phase 4)
|
|
647
|
+
if (config.visualPolish) {
|
|
648
|
+
const vp = config.visualPolish;
|
|
649
|
+
vp.borders && (tokens["visual-polish-border-iridescent-glow"] = String(vp.borders.iridescentGlow ?? !1),
|
|
650
|
+
tokens["visual-polish-border-shimmer-effect"] = String(vp.borders.shimmerEffect ?? !1),
|
|
651
|
+
tokens["visual-polish-border-beveled-edges"] = String(vp.borders.beveledEdges ?? !1),
|
|
652
|
+
tokens["visual-polish-border-pulsing-glow"] = String(vp.borders.pulsingGlow ?? !1)),
|
|
653
|
+
vp.contentAwareBlur && (tokens["visual-polish-content-aware-blur-enabled"] = String(vp.contentAwareBlur.enabled ?? !1),
|
|
654
|
+
tokens["visual-polish-content-aware-depth-detection"] = String(vp.contentAwareBlur.depthDetection ?? !1),
|
|
655
|
+
tokens["visual-polish-content-aware-edge-preservation"] = String(vp.contentAwareBlur.edgePreservation ?? !1),
|
|
656
|
+
tokens["visual-polish-content-aware-variable-radius"] = String(vp.contentAwareBlur.variableRadius ?? !1)),
|
|
657
|
+
vp.holographicEffects && (tokens["visual-polish-holographic-enabled"] = String(vp.holographicEffects.enabled ?? !1),
|
|
658
|
+
tokens["visual-polish-holographic-rainbow-diffraction"] = String(vp.holographicEffects.rainbowDiffraction ?? !1),
|
|
659
|
+
tokens["visual-polish-holographic-scanline-animation"] = String(vp.holographicEffects.scanlineAnimation ?? !1),
|
|
660
|
+
tokens["visual-polish-holographic-grid-overlay"] = String(vp.holographicEffects.gridOverlay ?? !1),
|
|
661
|
+
tokens["visual-polish-holographic-data-stream"] = String(vp.holographicEffects.dataStream ?? !1),
|
|
662
|
+
tokens["visual-polish-holographic-pulse-rings"] = String(vp.holographicEffects.pulseRings ?? !1));
|
|
663
|
+
}
|
|
612
664
|
}
|
|
613
|
-
|
|
665
|
+
// Apply prefix to all tokens
|
|
666
|
+
const prefixedTokens = {};
|
|
667
|
+
return Object.entries(tokens).forEach((([key, value]) => {
|
|
668
|
+
// If the token key already starts with the prefix, use as-is
|
|
669
|
+
// Otherwise, add the prefix
|
|
670
|
+
const prefixedKey = key.startsWith(prefix) ? key : `${prefix}-${key}`;
|
|
671
|
+
prefixedTokens[prefixedKey] = value;
|
|
672
|
+
})), prefixedTokens;
|
|
614
673
|
}
|
|
615
674
|
|
|
616
675
|
/**
|
|
617
|
-
*
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
* @example
|
|
623
|
-
* ```typescript
|
|
624
|
-
* const baseTokens = { 'primary': '#000', 'spacing-4': '1rem' };
|
|
625
|
-
* const customTokens = { 'secondary': '#fff', 'spacing-4': '1.5rem' };
|
|
626
|
-
* const merged = mergeTheme(baseTokens, customTokens);
|
|
627
|
-
* // Returns: { 'primary': '#000', 'secondary': '#fff', 'spacing-4': '1.5rem' }
|
|
628
|
-
* ```
|
|
629
|
-
*/ function mergeTheme(...tokens) {
|
|
630
|
-
return deepMerge({}, ...tokens);
|
|
676
|
+
* Convert hex color to RGB
|
|
677
|
+
*/ function hexToRgb$2(hex) {
|
|
678
|
+
hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, ((m, r, g, b) => r + r + g + g + b + b));
|
|
679
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
680
|
+
return result && result[1] && result[2] && result[3] ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}` : "0, 0, 0";
|
|
631
681
|
}
|
|
632
682
|
|
|
633
683
|
/**
|
|
634
|
-
*
|
|
684
|
+
* Converts DesignTokens to CSS variables
|
|
635
685
|
*
|
|
636
|
-
* @param
|
|
637
|
-
* @
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
* const extended = extendTheme(base, { 'secondary': '#fff' });
|
|
644
|
-
* // Returns: { 'primary': '#000', 'secondary': '#fff' }
|
|
645
|
-
* ```
|
|
646
|
-
*/ function extendTheme(baseTokens, extension) {
|
|
647
|
-
return mergeTheme(baseTokens, extension);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Create a new theme registry
|
|
652
|
-
*/ function createThemeRegistry() {
|
|
653
|
-
return {};
|
|
686
|
+
* @param tokens - The tokens to convert
|
|
687
|
+
* @returns A record of CSS variable names and values
|
|
688
|
+
*/ function designTokensToCSSVars(tokens) {
|
|
689
|
+
const cssVars = {};
|
|
690
|
+
return Object.entries(tokens).forEach((([key, value]) => {
|
|
691
|
+
void 0 !== value && (cssVars[`--atomix-${key}`] = String(value));
|
|
692
|
+
})), cssVars;
|
|
654
693
|
}
|
|
655
694
|
|
|
656
|
-
|
|
657
|
-
* Register a theme
|
|
658
|
-
* @param registry - Theme registry object
|
|
659
|
-
* @param id - Theme identifier
|
|
660
|
-
* @param metadata - Theme metadata
|
|
661
|
-
*/ function registerTheme(registry, id, metadata) {
|
|
662
|
-
registry[id] = metadata;
|
|
663
|
-
}
|
|
695
|
+
var commonjsGlobal = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {};
|
|
664
696
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
* @param registry - Theme registry object
|
|
668
|
-
* @param id - Theme identifier
|
|
669
|
-
*/ function unregisterTheme(registry, id) {
|
|
670
|
-
const exists = id in registry;
|
|
671
|
-
return delete registry[id], exists;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Check if a theme is registered
|
|
676
|
-
* @param registry - Theme registry object
|
|
677
|
-
* @param id - Theme identifier
|
|
678
|
-
*/ function hasTheme(registry, id) {
|
|
679
|
-
return id in registry;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* Get theme metadata
|
|
684
|
-
* @param registry - Theme registry object
|
|
685
|
-
* @param id - Theme identifier
|
|
686
|
-
*/ function getTheme(registry, id) {
|
|
687
|
-
return registry[id];
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Get all registered theme metadata
|
|
692
|
-
* @param registry - Theme registry object
|
|
693
|
-
*/ function getAllThemes(registry) {
|
|
694
|
-
return Object.values(registry);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/**
|
|
698
|
-
* Get all registered theme IDs
|
|
699
|
-
* @param registry - Theme registry object
|
|
700
|
-
*/ function getThemeIds(registry) {
|
|
701
|
-
return Object.keys(registry);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* Clear all registered themes
|
|
706
|
-
* @param registry - Theme registry object
|
|
707
|
-
*/ function clearThemes(registry) {
|
|
708
|
-
Object.keys(registry).forEach((key => delete registry[key]));
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
/**
|
|
712
|
-
* Get the number of registered themes
|
|
713
|
-
* @param registry - Theme registry object
|
|
714
|
-
*/ function getThemeCount(registry) {
|
|
715
|
-
return Object.keys(registry).length;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* CSS Injection Utilities
|
|
720
|
-
*
|
|
721
|
-
* Inject CSS into HTML head via <style> element.
|
|
722
|
-
*/
|
|
723
|
-
/**
|
|
724
|
-
* Check if running in browser environment
|
|
725
|
-
*/ function isBrowser$1() {
|
|
726
|
-
return "undefined" != typeof document;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Inject CSS into HTML head via <style> element
|
|
731
|
-
*
|
|
732
|
-
* Creates or updates a style element in the document head.
|
|
733
|
-
* If an element with the same ID exists, it will be updated.
|
|
734
|
-
*
|
|
735
|
-
* @param css - CSS string to inject
|
|
736
|
-
* @param id - Style element ID (default: 'atomix-theme')
|
|
737
|
-
*
|
|
738
|
-
* @example
|
|
739
|
-
* ```typescript
|
|
740
|
-
* const css = ':root { --atomix-color-primary: #7AFFD7; }';
|
|
741
|
-
* injectCSS(css);
|
|
742
|
-
*
|
|
743
|
-
* // With custom ID
|
|
744
|
-
* injectCSS(css, 'my-custom-theme');
|
|
745
|
-
* ```
|
|
746
|
-
*/ function injectCSS$1(css, id = "atomix-theme") {
|
|
747
|
-
if (!isBrowser$1()) return;
|
|
748
|
-
let styleElement = document.getElementById(id);
|
|
749
|
-
styleElement || (styleElement = document.createElement("style"), styleElement.id = id,
|
|
750
|
-
styleElement.setAttribute("data-atomix-theme", "true"), document.head.appendChild(styleElement)),
|
|
751
|
-
styleElement.textContent = css;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Remove injected CSS from DOM
|
|
756
|
-
*
|
|
757
|
-
* Removes the style element with the given ID from the document head.
|
|
758
|
-
*
|
|
759
|
-
* @param id - Style element ID to remove (default: 'atomix-theme')
|
|
760
|
-
*
|
|
761
|
-
* @example
|
|
762
|
-
* ```typescript
|
|
763
|
-
* removeCSS(); // Removes default 'atomix-theme'
|
|
764
|
-
* removeCSS('my-custom-theme'); // Removes custom ID
|
|
765
|
-
* ```
|
|
766
|
-
*/ function removeCSS(id = "atomix-theme") {
|
|
767
|
-
if (!isBrowser$1()) return;
|
|
768
|
-
const styleElement = document.getElementById(id);
|
|
769
|
-
styleElement && styleElement.remove();
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/**
|
|
773
|
-
* Check if CSS is already injected
|
|
774
|
-
*
|
|
775
|
-
* @param id - Style element ID to check (default: 'atomix-theme')
|
|
776
|
-
* @returns True if style element exists
|
|
777
|
-
*/ function isCSSInjected(id = "atomix-theme") {
|
|
778
|
-
return !!isBrowser$1() && null !== document.getElementById(id);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
var commonjsGlobal = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {};
|
|
782
|
-
|
|
783
|
-
function getDefaultExportFromCjs(x) {
|
|
784
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
|
|
697
|
+
function getDefaultExportFromCjs(x) {
|
|
698
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
|
|
785
699
|
}
|
|
786
700
|
|
|
787
701
|
var fails$9 = function(exec) {
|
|
@@ -862,7 +776,7 @@ var match, version, createPropertyDescriptor$2 = function(bitmap, value) {
|
|
|
862
776
|
return it;
|
|
863
777
|
}, IndexedObject$1 = indexedObject, requireObjectCoercible$2 = requireObjectCoercible$3, toIndexedObject$2 = function(it) {
|
|
864
778
|
return IndexedObject$1(requireObjectCoercible$2(it));
|
|
865
|
-
}, isCallable$7 = isCallable$8, isObject$
|
|
779
|
+
}, isCallable$7 = isCallable$8, isObject$6 = function(it) {
|
|
866
780
|
return "object" == typeof it ? null !== it : isCallable$7(it);
|
|
867
781
|
}, path$3 = {}, path$2 = path$3, globalThis$b = globalThis_1, isCallable$6 = isCallable$8, aFunction = function(variable) {
|
|
868
782
|
return isCallable$6(variable) ? variable : void 0;
|
|
@@ -901,7 +815,7 @@ var environmentV8Version = version, V8_VERSION = environmentV8Version, fails$5 =
|
|
|
901
815
|
return "Object";
|
|
902
816
|
}
|
|
903
817
|
}(argument) + " is not a function");
|
|
904
|
-
}, aCallable$2 = aCallable$3, isNullOrUndefined = isNullOrUndefined$2, call$2 = functionCall, isCallable$3 = isCallable$8, isObject$
|
|
818
|
+
}, aCallable$2 = aCallable$3, isNullOrUndefined = isNullOrUndefined$2, call$2 = functionCall, isCallable$3 = isCallable$8, isObject$5 = isObject$6, $TypeError$5 = TypeError, sharedStore = {
|
|
905
819
|
exports: {}
|
|
906
820
|
}, globalThis$7 = globalThis_1, defineProperty = Object.defineProperty, globalThis$6 = globalThis_1, store$1 = sharedStore.exports = globalThis$6["__core-js_shared__"] || function(key, value) {
|
|
907
821
|
try {
|
|
@@ -933,25 +847,25 @@ var key, value, store = sharedStore.exports, requireObjectCoercible$1 = requireO
|
|
|
933
847
|
}, wellKnownSymbol$5 = function(name) {
|
|
934
848
|
return hasOwn$2(WellKnownSymbolsStore, name) || (WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn$2(Symbol$1, name) ? Symbol$1[name] : createWellKnownSymbol("Symbol." + name)),
|
|
935
849
|
WellKnownSymbolsStore[name];
|
|
936
|
-
}, call$1 = functionCall, isObject$
|
|
937
|
-
if (!isObject$
|
|
850
|
+
}, call$1 = functionCall, isObject$4 = isObject$6, isSymbol$1 = isSymbol$2, $TypeError$4 = TypeError, TO_PRIMITIVE = wellKnownSymbol$5("toPrimitive"), toPrimitive = function(input, pref) {
|
|
851
|
+
if (!isObject$4(input) || isSymbol$1(input)) return input;
|
|
938
852
|
var result, func, exoticToPrim = (func = input[TO_PRIMITIVE], isNullOrUndefined(func) ? void 0 : aCallable$2(func));
|
|
939
853
|
if (exoticToPrim) {
|
|
940
854
|
if (void 0 === pref && (pref = "default"), result = call$1(exoticToPrim, input, pref),
|
|
941
|
-
!isObject$
|
|
855
|
+
!isObject$4(result) || isSymbol$1(result)) return result;
|
|
942
856
|
throw new $TypeError$4("Can't convert object to primitive value");
|
|
943
857
|
}
|
|
944
858
|
return void 0 === pref && (pref = "number"), function(input, pref) {
|
|
945
859
|
var fn, val;
|
|
946
|
-
if ("string" === pref && isCallable$3(fn = input.toString) && !isObject$
|
|
947
|
-
if (isCallable$3(fn = input.valueOf) && !isObject$
|
|
948
|
-
if ("string" !== pref && isCallable$3(fn = input.toString) && !isObject$
|
|
860
|
+
if ("string" === pref && isCallable$3(fn = input.toString) && !isObject$5(val = call$2(fn, input))) return val;
|
|
861
|
+
if (isCallable$3(fn = input.valueOf) && !isObject$5(val = call$2(fn, input))) return val;
|
|
862
|
+
if ("string" !== pref && isCallable$3(fn = input.toString) && !isObject$5(val = call$2(fn, input))) return val;
|
|
949
863
|
throw new $TypeError$5("Can't convert object to primitive value");
|
|
950
864
|
}(input, pref);
|
|
951
865
|
}, isSymbol = isSymbol$2, toPropertyKey$2 = function(argument) {
|
|
952
866
|
var key = toPrimitive(argument, "string");
|
|
953
867
|
return isSymbol(key) ? key : key + "";
|
|
954
|
-
}, isObject$
|
|
868
|
+
}, isObject$3 = isObject$6, document$1 = globalThis_1.document, EXISTS = isObject$3(document$1) && isObject$3(document$1.createElement), ie8DomDefine = !descriptors && !fails$9((function() {
|
|
955
869
|
// eslint-disable-next-line es/no-object-defineproperty -- required for testing
|
|
956
870
|
return 7 !== Object.defineProperty((it = "div", EXISTS ? document$1.createElement(it) : {}), "a", {
|
|
957
871
|
get: function() {
|
|
@@ -981,8 +895,8 @@ var fails$3 = fails$9, isCallable$2 = isCallable$8, replacement = /#|\.prototype
|
|
|
981
895
|
value: 42,
|
|
982
896
|
writable: !1
|
|
983
897
|
}).prototype;
|
|
984
|
-
})), isObject$
|
|
985
|
-
if (isObject$
|
|
898
|
+
})), isObject$2 = isObject$6, $String$1 = String, $TypeError$3 = TypeError, DESCRIPTORS$1 = descriptors, IE8_DOM_DEFINE = ie8DomDefine, V8_PROTOTYPE_DEFINE_BUG = v8PrototypeDefineBug, anObject = function(argument) {
|
|
899
|
+
if (isObject$2(argument)) return argument;
|
|
986
900
|
throw new $TypeError$3($String$1(argument) + " is not an object");
|
|
987
901
|
}, toPropertyKey = toPropertyKey$2, $TypeError$2 = TypeError, $defineProperty = Object.defineProperty, $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
988
902
|
|
|
@@ -1099,7 +1013,7 @@ var globalThis$2 = globalThis_1, path = path$3, getBuiltInPrototypeMethod$3 = fu
|
|
|
1099
1013
|
if (pureMethod) return pureMethod;
|
|
1100
1014
|
var NativeConstructor = globalThis$2[CONSTRUCTOR], NativePrototype = NativeConstructor && NativeConstructor.prototype;
|
|
1101
1015
|
return NativePrototype && NativePrototype[METHOD];
|
|
1102
|
-
}, includes$4 = getBuiltInPrototypeMethod$3("Array", "includes"), isObject = isObject$
|
|
1016
|
+
}, includes$4 = getBuiltInPrototypeMethod$3("Array", "includes"), isObject$1 = isObject$6, classof$3 = classofRaw$2, MATCH$1 = wellKnownSymbol$5("match"), $TypeError$1 = TypeError, test = {};
|
|
1103
1017
|
|
|
1104
1018
|
test[wellKnownSymbol$5("toStringTag")] = "z";
|
|
1105
1019
|
|
|
@@ -1115,7 +1029,7 @@ var TO_STRING_TAG_SUPPORT = "[object z]" === String(test), isCallable = isCallab
|
|
|
1115
1029
|
}, $String = String, MATCH = wellKnownSymbol$5("match"), $$1 = _export, notARegExp = function(it) {
|
|
1116
1030
|
if (function(it) {
|
|
1117
1031
|
var isRegExp;
|
|
1118
|
-
return isObject(it) && (void 0 !== (isRegExp = it[MATCH$1]) ? !!isRegExp : "RegExp" === classof$3(it));
|
|
1032
|
+
return isObject$1(it) && (void 0 !== (isRegExp = it[MATCH$1]) ? !!isRegExp : "RegExp" === classof$3(it));
|
|
1119
1033
|
}(it)) throw new $TypeError$1("The method doesn't accept regular expressions");
|
|
1120
1034
|
return it;
|
|
1121
1035
|
}, requireObjectCoercible = requireObjectCoercible$3, toString = function(argument) {
|
|
@@ -1153,18 +1067,595 @@ const _includesInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
1153
1067
|
}));
|
|
1154
1068
|
|
|
1155
1069
|
/**
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1070
|
+
* Load theme from config file (synchronous, Node.js only)
|
|
1071
|
+
* @param configPath - Path to config file (default: atomix.config.ts)
|
|
1072
|
+
* @returns DesignTokens from theme configuration
|
|
1073
|
+
* @throws Error if config loading is not available in browser environment
|
|
1074
|
+
*/
|
|
1075
|
+
/**
|
|
1076
|
+
* Helper function to load config from a specific path
|
|
1077
|
+
*/
|
|
1078
|
+
function loadConfigAtPath(path, required, defaultConfig) {
|
|
1079
|
+
try {
|
|
1080
|
+
// Use dynamic import for ESM compatibility
|
|
1081
|
+
const configModule = require(path), config = configModule.default || configModule;
|
|
1082
|
+
// Validate it's an AtomixConfig
|
|
1083
|
+
if (config && "object" == typeof config) return config;
|
|
1084
|
+
throw new Error("Invalid config format");
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
if (required) throw new Error(`Failed to load config from ${path}: ${error.message}`);
|
|
1087
|
+
// Return default config if not required
|
|
1088
|
+
return defaultConfig;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Core Theme Functions
|
|
1094
|
+
*
|
|
1095
|
+
* Unified theme system that handles both DesignTokens and Theme objects.
|
|
1096
|
+
* Config-first approach: loads from atomix.config.ts when no input is provided.
|
|
1097
|
+
* Config-first approach: loads advanced features from config when available.
|
|
1098
|
+
*/
|
|
1099
|
+
/**
|
|
1100
|
+
* Create theme CSS from tokens or Theme object
|
|
1101
|
+
*
|
|
1102
|
+
* **Config-First Approach**: If no input is provided, loads from `atomix.config.ts`.
|
|
1103
|
+
* Config file is required for automatic loading.
|
|
1104
|
+
*
|
|
1105
|
+
* @param input - DesignTokens (partial), Theme object, or undefined (loads from config)
|
|
1106
|
+
* @param options - CSS generation options (prefix is automatically read from config if not provided)
|
|
1107
|
+
* @returns CSS string with custom properties
|
|
1108
|
+
* @throws Error if config loading fails when no input is provided
|
|
1109
|
+
*
|
|
1110
|
+
* @example
|
|
1111
|
+
* ```typescript
|
|
1112
|
+
* // Loads from atomix.config.ts (config file required)
|
|
1113
|
+
* const css = createTheme();
|
|
1114
|
+
*
|
|
1115
|
+
* // Using DesignTokens
|
|
1116
|
+
* const css = createTheme({
|
|
1117
|
+
* 'primary': '#7c3aed',
|
|
1118
|
+
* 'spacing-4': '1rem',
|
|
1119
|
+
* });
|
|
1120
|
+
*
|
|
1121
|
+
* // Using Theme object
|
|
1122
|
+
* const theme = createThemeObject({ palette: { primary: { main: '#7c3aed' } } });
|
|
1123
|
+
* const css = createTheme(theme);
|
|
1124
|
+
*
|
|
1125
|
+
* // With custom options
|
|
1126
|
+
* const css = createTheme(undefined, { prefix: 'myapp', selector: ':root' });
|
|
1127
|
+
* ```
|
|
1128
|
+
*/ function createTheme(input, options) {
|
|
1129
|
+
let tokens, configPrefix;
|
|
1130
|
+
// If no input provided, load from config (required)
|
|
1131
|
+
if (input)
|
|
1132
|
+
// Convert Theme object to DesignTokens
|
|
1133
|
+
tokens = !0 === input.__isJSTheme || input.palette && input.typography ? themeToDesignTokens(input) : input; else {
|
|
1134
|
+
const configTokens = function() {
|
|
1135
|
+
// Check if we're in a browser environment
|
|
1136
|
+
if ("undefined" != typeof window) throw new Error("loadThemeFromConfigSync: Not available in browser environment. Config loading requires Node.js/SSR environment.");
|
|
1137
|
+
// Use dynamic import to load the config loader
|
|
1138
|
+
// This allows bundlers to handle external dependencies properly
|
|
1139
|
+
let loadAtomixConfig;
|
|
1140
|
+
try {
|
|
1141
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1142
|
+
const {loadAtomixConfig: loader} = require("../../config/loader");
|
|
1143
|
+
loadAtomixConfig = loader;
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
throw new Error("Config loader module not available");
|
|
1146
|
+
}
|
|
1147
|
+
const config = loadAtomixConfig({
|
|
1148
|
+
configPath: undefined,
|
|
1149
|
+
required: !0
|
|
1150
|
+
});
|
|
1151
|
+
if (!config?.theme) return createTokens({});
|
|
1152
|
+
if ((obj = config.theme) && "object" == typeof obj && (obj.palette || obj.typography || obj.spacing || obj.breakpoints || obj.colors)) return themeToDesignTokens(config.theme);
|
|
1153
|
+
// Handle the config.theme object which has extend/tokens/themes properties
|
|
1154
|
+
// Extract the actual tokens from the theme configuration
|
|
1155
|
+
// Helper type guard function
|
|
1156
|
+
var obj;
|
|
1157
|
+
/**
|
|
1158
|
+
* Load Atomix configuration from project root
|
|
1159
|
+
*
|
|
1160
|
+
* Attempts to load atomix.config.ts, atomix.config.js, or atomix.config.json from the current working directory.
|
|
1161
|
+
* Falls back to default config if file doesn't exist.
|
|
1162
|
+
*
|
|
1163
|
+
* @param options - Loader options
|
|
1164
|
+
* @returns Loaded configuration or default
|
|
1165
|
+
*
|
|
1166
|
+
* @example
|
|
1167
|
+
* ```typescript
|
|
1168
|
+
* import { loadAtomixConfig } from '@shohojdhara/atomix/config';
|
|
1169
|
+
* import { createTheme } from '@shohojdhara/atomix/theme';
|
|
1170
|
+
*
|
|
1171
|
+
* const config = loadAtomixConfig();
|
|
1172
|
+
* const theme = createTheme(config.theme?.tokens || {});
|
|
1173
|
+
* ```
|
|
1174
|
+
*/ const themeConfig = config.theme;
|
|
1175
|
+
let tokensToApply = {};
|
|
1176
|
+
return themeConfig.tokens ?
|
|
1177
|
+
// If tokens is provided, use it as the base
|
|
1178
|
+
tokensToApply = themeConfig.tokens : themeConfig.extend && (
|
|
1179
|
+
// If only extend is provided, use it as overrides
|
|
1180
|
+
tokensToApply = themeConfig.extend),
|
|
1181
|
+
// Apply advanced feature configurations as tokens
|
|
1182
|
+
config.interactiveEffects && (
|
|
1183
|
+
// Vortex effects
|
|
1184
|
+
config.interactiveEffects.vortex && (tokensToApply = {
|
|
1185
|
+
...tokensToApply,
|
|
1186
|
+
"interactive-vortex-enabled": String(config.interactiveEffects.vortex.enabled ?? !1),
|
|
1187
|
+
"interactive-vortex-strength": String(config.interactiveEffects.vortex.strength ?? .5),
|
|
1188
|
+
"interactive-vortex-radius": String(config.interactiveEffects.vortex.radius ?? 100),
|
|
1189
|
+
"interactive-vortex-decay": String(config.interactiveEffects.vortex.decay ?? .8)
|
|
1190
|
+
}),
|
|
1191
|
+
// Chromatic aberration
|
|
1192
|
+
config.interactiveEffects.chromaticAberration && (tokensToApply = {
|
|
1193
|
+
...tokensToApply,
|
|
1194
|
+
"interactive-chromatic-enabled": String(config.interactiveEffects.chromaticAberration.enabled ?? !1),
|
|
1195
|
+
"interactive-chromatic-mode": config.interactiveEffects.chromaticAberration.mode ?? "lateral",
|
|
1196
|
+
"interactive-chromatic-red-shift": String(config.interactiveEffects.chromaticAberration.redShift ?? .02),
|
|
1197
|
+
"interactive-chromatic-green-shift": String(config.interactiveEffects.chromaticAberration.greenShift ?? 0),
|
|
1198
|
+
"interactive-chromatic-blue-shift": String(config.interactiveEffects.chromaticAberration.blueShift ?? -.02),
|
|
1199
|
+
"interactive-chromatic-edge-only": String(config.interactiveEffects.chromaticAberration.edgeOnly ?? !1),
|
|
1200
|
+
"interactive-chromatic-edge-threshold": String(config.interactiveEffects.chromaticAberration.edgeThreshold ?? .5)
|
|
1201
|
+
}),
|
|
1202
|
+
// Mouse interaction
|
|
1203
|
+
config.interactiveEffects.mouseInteraction && (tokensToApply = {
|
|
1204
|
+
...tokensToApply,
|
|
1205
|
+
"interactive-mouse-sensitivity": String(config.interactiveEffects.mouseInteraction.sensitivity ?? 1),
|
|
1206
|
+
"interactive-mouse-trail-effect": String(config.interactiveEffects.mouseInteraction.trailEffect ?? !1)
|
|
1207
|
+
}),
|
|
1208
|
+
// Animation speed
|
|
1209
|
+
config.interactiveEffects.animationSpeed && (tokensToApply = {
|
|
1210
|
+
...tokensToApply,
|
|
1211
|
+
"interactive-animation-speed-base": String(config.interactiveEffects.animationSpeed.base ?? 1),
|
|
1212
|
+
"interactive-animation-speed-multiplier": String(config.interactiveEffects.animationSpeed.timeMultiplier ?? 1)
|
|
1213
|
+
})),
|
|
1214
|
+
// Apply optimization configurations as tokens
|
|
1215
|
+
config.optimization && (
|
|
1216
|
+
// Responsive breakpoints
|
|
1217
|
+
config.optimization.responsive && (config.optimization.responsive.breakpoints && (tokensToApply = {
|
|
1218
|
+
...tokensToApply,
|
|
1219
|
+
"optimization-breakpoint-mobile": config.optimization.responsive.breakpoints.mobile ?? "0px",
|
|
1220
|
+
"optimization-breakpoint-tablet": config.optimization.responsive.breakpoints.tablet ?? "768px",
|
|
1221
|
+
"optimization-breakpoint-desktop": config.optimization.responsive.breakpoints.desktop ?? "1024px",
|
|
1222
|
+
"optimization-breakpoint-wide": config.optimization.responsive.breakpoints.wide ?? "1440px"
|
|
1223
|
+
}), config.optimization.responsive.deviceScaling && (tokensToApply = {
|
|
1224
|
+
...tokensToApply,
|
|
1225
|
+
"optimization-device-scaling-mobile": String(config.optimization.responsive.deviceScaling.mobile ?? .5),
|
|
1226
|
+
"optimization-device-scaling-tablet": String(config.optimization.responsive.deviceScaling.tablet ?? .75),
|
|
1227
|
+
"optimization-device-scaling-desktop": String(config.optimization.responsive.deviceScaling.desktop ?? 1)
|
|
1228
|
+
})),
|
|
1229
|
+
// Performance settings
|
|
1230
|
+
config.optimization.performance && (tokensToApply = {
|
|
1231
|
+
...tokensToApply,
|
|
1232
|
+
"optimization-performance-fps-target": String(config.optimization.performance.fpsTarget ?? 60),
|
|
1233
|
+
"optimization-auto-scaling-enabled": String(config.optimization.performance.autoScaling ?? !1)
|
|
1234
|
+
}),
|
|
1235
|
+
// Auto-scaling settings
|
|
1236
|
+
config.optimization.autoScaling && (tokensToApply = {
|
|
1237
|
+
...tokensToApply,
|
|
1238
|
+
"optimization-auto-scaling-enabled": String(config.optimization.autoScaling.enabled ?? !1),
|
|
1239
|
+
"optimization-auto-scaling-low-end": String(config.optimization.autoScaling.qualityThresholds?.lowEnd ?? .5),
|
|
1240
|
+
"optimization-auto-scaling-mid-range": String(config.optimization.autoScaling.qualityThresholds?.midRange ?? .75),
|
|
1241
|
+
"optimization-auto-scaling-high-end": String(config.optimization.autoScaling.qualityThresholds?.highEnd ?? 1)
|
|
1242
|
+
})),
|
|
1243
|
+
// Apply visual polish configurations as tokens
|
|
1244
|
+
config.visualPolish && (config.visualPolish.borders && (tokensToApply = {
|
|
1245
|
+
...tokensToApply,
|
|
1246
|
+
"visual-polish-border-iridescent-glow": String(config.visualPolish.borders.iridescentGlow ?? !1),
|
|
1247
|
+
"visual-polish-border-shimmer-effect": String(config.visualPolish.borders.shimmerEffect ?? !1),
|
|
1248
|
+
"visual-polish-border-beveled-edges": String(config.visualPolish.borders.beveledEdges ?? !1),
|
|
1249
|
+
"visual-polish-border-pulsing-glow": String(config.visualPolish.borders.pulsingGlow ?? !1)
|
|
1250
|
+
}), config.visualPolish.contentAwareBlur && (tokensToApply = {
|
|
1251
|
+
...tokensToApply,
|
|
1252
|
+
"visual-polish-content-aware-blur-enabled": String(config.visualPolish.contentAwareBlur.enabled ?? !1),
|
|
1253
|
+
"visual-polish-content-aware-depth-detection": String(config.visualPolish.contentAwareBlur.depthDetection ?? !1),
|
|
1254
|
+
"visual-polish-content-aware-edge-preservation": String(config.visualPolish.contentAwareBlur.edgePreservation ?? !1),
|
|
1255
|
+
"visual-polish-content-aware-variable-radius": String(config.visualPolish.contentAwareBlur.variableRadius ?? !1)
|
|
1256
|
+
}), config.visualPolish.holographicEffects && (tokensToApply = {
|
|
1257
|
+
...tokensToApply,
|
|
1258
|
+
"visual-polish-holographic-enabled": String(config.visualPolish.holographicEffects.enabled ?? !1),
|
|
1259
|
+
"visual-polish-holographic-rainbow-diffraction": String(config.visualPolish.holographicEffects.rainbowDiffraction ?? !1),
|
|
1260
|
+
"visual-polish-holographic-scanline-animation": String(config.visualPolish.holographicEffects.scanlineAnimation ?? !1),
|
|
1261
|
+
"visual-polish-holographic-grid-overlay": String(config.visualPolish.holographicEffects.gridOverlay ?? !1),
|
|
1262
|
+
"visual-polish-holographic-data-stream": String(config.visualPolish.holographicEffects.dataStream ?? !1),
|
|
1263
|
+
"visual-polish-holographic-pulse-rings": String(config.visualPolish.holographicEffects.pulseRings ?? !1)
|
|
1264
|
+
})), createTokens(tokensToApply);
|
|
1265
|
+
}();
|
|
1266
|
+
// Get prefix from config
|
|
1267
|
+
try {
|
|
1268
|
+
// Use the imported function directly instead of require to avoid bundling issues
|
|
1269
|
+
const config = function(options = {}) {
|
|
1270
|
+
const {configPath: configPath, required: required = !1} = options, defaultConfig = {
|
|
1271
|
+
prefix: "atomix",
|
|
1272
|
+
theme: {
|
|
1273
|
+
extend: {}
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
// Default config
|
|
1277
|
+
// In browser environments, config loading is not supported
|
|
1278
|
+
if ("undefined" != typeof window) {
|
|
1279
|
+
if (required) throw new Error('Config loading requires Node.js file system access.\n\nSolutions:\n1. Provide tokens explicitly to createTheme():\n const css = createTheme({ "--brand-primary": "#6366f1" });\n\n2. Use SSR framework (Next.js, Remix, Astro)\n\n3. Load config on server and pass to client\n\nSee examples/config-examples/browser-only.config.ts');
|
|
1280
|
+
return defaultConfig;
|
|
1281
|
+
}
|
|
1282
|
+
// If a specific config path is provided, try to load it directly
|
|
1283
|
+
if (configPath) return loadConfigAtPath(configPath, required, defaultConfig);
|
|
1284
|
+
// Otherwise, try standard locations in order of preference
|
|
1285
|
+
const possiblePaths = [ "atomix.config.ts", "atomix.config.js", "atomix.config.json" ];
|
|
1286
|
+
for (const path of possiblePaths) {
|
|
1287
|
+
const config = loadConfigAtPath(path, !1, defaultConfig);
|
|
1288
|
+
// If we found a valid config, return it
|
|
1289
|
+
if (JSON.stringify(config) !== JSON.stringify(defaultConfig)) return config;
|
|
1290
|
+
}
|
|
1291
|
+
// If no config file was found or all contained only defaults, return default config
|
|
1292
|
+
if (required) throw new Error('No Atomix configuration file found in project root.\n\nExpected one of:\n - atomix.config.ts (recommended)\n - atomix.config.js\n - atomix.config.json\n\nQuick Fix:\n1. Create a config file in your project root:\n touch atomix.config.ts\n\n2. Add basic configuration:\n import { defineConfig } from "@shohojdhara/atomix/config";\n export default defineConfig({ prefix: "myapp" });\n\n3. Or copy an example:\n cp node_modules/@shohojdhara/atomix/examples/config-examples/standard.config.ts ./atomix.config.ts');
|
|
1293
|
+
return defaultConfig;
|
|
1294
|
+
}({
|
|
1295
|
+
configPath: "atomix.config.ts",
|
|
1296
|
+
required: !0
|
|
1297
|
+
});
|
|
1298
|
+
configPrefix = config?.prefix;
|
|
1299
|
+
} catch (error) {
|
|
1300
|
+
// Prefix loading failed, but tokens were loaded, so continue
|
|
1301
|
+
}
|
|
1302
|
+
tokens = configTokens;
|
|
1303
|
+
}
|
|
1304
|
+
// Merge with defaults and generate CSS
|
|
1305
|
+
const allTokens = createTokens(tokens), prefix = options?.prefix ?? configPrefix ?? "atomix";
|
|
1306
|
+
// Get prefix from options, config, or use default
|
|
1307
|
+
return generateCSSVariables$1(allTokens, {
|
|
1308
|
+
...options,
|
|
1309
|
+
prefix: prefix
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Theme Composition Utilities
|
|
1315
|
+
*
|
|
1316
|
+
* Simplified utilities for composing and merging DesignTokens.
|
|
1317
|
+
*/
|
|
1318
|
+
// ============================================================================
|
|
1319
|
+
// Deep Merge Utility
|
|
1320
|
+
// ============================================================================
|
|
1321
|
+
/**
|
|
1322
|
+
* Check if value is an object
|
|
1323
|
+
*/ function isObject(item) {
|
|
1324
|
+
return item && "object" == typeof item && !Array.isArray(item) && "function" != typeof item;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* Deep merge multiple objects
|
|
1329
|
+
* Later objects override earlier ones
|
|
1330
|
+
*/ function deepMerge(...objects) {
|
|
1331
|
+
if (0 === objects.length) return {};
|
|
1332
|
+
if (1 === objects.length) return objects[0];
|
|
1333
|
+
const [target, ...sources] = objects, result = {
|
|
1334
|
+
...target
|
|
1335
|
+
};
|
|
1336
|
+
for (const source of sources) if (source) for (const key in source) {
|
|
1337
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
|
|
1338
|
+
const targetValue = result[key], sourceValue = source[key];
|
|
1339
|
+
isObject(targetValue) && isObject(sourceValue) ?
|
|
1340
|
+
// Recursively merge objects
|
|
1341
|
+
result[key] = deepMerge(targetValue, sourceValue) :
|
|
1342
|
+
// Override with source value
|
|
1343
|
+
result[key] = sourceValue;
|
|
1344
|
+
}
|
|
1345
|
+
return result;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Merge multiple DesignTokens objects into a single DesignTokens object
|
|
1350
|
+
*
|
|
1351
|
+
* @param tokens - DesignTokens objects to merge
|
|
1352
|
+
* @returns Merged DesignTokens object
|
|
1353
|
+
*
|
|
1354
|
+
* @example
|
|
1355
|
+
* ```typescript
|
|
1356
|
+
* const baseTokens = { 'primary': '#000', 'spacing-4': '1rem' };
|
|
1357
|
+
* const customTokens = { 'secondary': '#fff', 'spacing-4': '1.5rem' };
|
|
1358
|
+
* const merged = mergeTheme(baseTokens, customTokens);
|
|
1359
|
+
* // Returns: { 'primary': '#000', 'secondary': '#fff', 'spacing-4': '1.5rem' }
|
|
1360
|
+
* ```
|
|
1361
|
+
*/ function mergeTheme(...tokens) {
|
|
1362
|
+
return deepMerge({}, ...tokens);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Extend DesignTokens with additional tokens
|
|
1367
|
+
*
|
|
1368
|
+
* @param baseTokens - Base DesignTokens to extend
|
|
1369
|
+
* @param extension - Additional DesignTokens to merge
|
|
1370
|
+
* @returns Extended DesignTokens object
|
|
1371
|
+
*
|
|
1372
|
+
* @example
|
|
1373
|
+
* ```typescript
|
|
1374
|
+
* const base = { 'primary': '#000' };
|
|
1375
|
+
* const extended = extendTheme(base, { 'secondary': '#fff' });
|
|
1376
|
+
* // Returns: { 'primary': '#000', 'secondary': '#fff' }
|
|
1377
|
+
* ```
|
|
1378
|
+
*/ function extendTheme(baseTokens, extension) {
|
|
1379
|
+
return mergeTheme(baseTokens, extension);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Create a new theme registry
|
|
1384
|
+
*/ function createThemeRegistry() {
|
|
1385
|
+
return {};
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* Register a theme
|
|
1390
|
+
* @param registry - Theme registry object
|
|
1391
|
+
* @param id - Theme identifier
|
|
1392
|
+
* @param metadata - Theme metadata
|
|
1393
|
+
*/ function registerTheme(registry, id, metadata) {
|
|
1394
|
+
registry[id] = metadata;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* Unregister a theme
|
|
1399
|
+
* @param registry - Theme registry object
|
|
1400
|
+
* @param id - Theme identifier
|
|
1401
|
+
*/ function unregisterTheme(registry, id) {
|
|
1402
|
+
const exists = id in registry;
|
|
1403
|
+
return delete registry[id], exists;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* Check if a theme is registered
|
|
1408
|
+
* @param registry - Theme registry object
|
|
1409
|
+
* @param id - Theme identifier
|
|
1410
|
+
*/ function hasTheme(registry, id) {
|
|
1411
|
+
return id in registry;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Get theme metadata
|
|
1416
|
+
* @param registry - Theme registry object
|
|
1417
|
+
* @param id - Theme identifier
|
|
1418
|
+
*/ function getTheme(registry, id) {
|
|
1419
|
+
return registry[id];
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* Get all registered theme metadata
|
|
1424
|
+
* @param registry - Theme registry object
|
|
1425
|
+
*/ function getAllThemes(registry) {
|
|
1426
|
+
return Object.values(registry);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* Get all registered theme IDs
|
|
1431
|
+
* @param registry - Theme registry object
|
|
1432
|
+
*/ function getThemeIds(registry) {
|
|
1433
|
+
return Object.keys(registry);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* Clear all registered themes
|
|
1438
|
+
* @param registry - Theme registry object
|
|
1439
|
+
*/ function clearThemes(registry) {
|
|
1440
|
+
Object.keys(registry).forEach((key => delete registry[key]));
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
/**
|
|
1444
|
+
* Get the number of registered themes
|
|
1445
|
+
* @param registry - Theme registry object
|
|
1446
|
+
*/ function getThemeCount(registry) {
|
|
1447
|
+
return Object.keys(registry).length;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* CSS Injection Utilities
|
|
1452
|
+
*
|
|
1453
|
+
* Inject CSS into HTML head via <style> element.
|
|
1454
|
+
*/
|
|
1455
|
+
/**
|
|
1456
|
+
* Check if running in browser environment
|
|
1457
|
+
*/ function isBrowser$1() {
|
|
1458
|
+
return "undefined" != typeof document;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* Inject CSS into HTML head via <style> element
|
|
1463
|
+
*
|
|
1464
|
+
* Creates or updates a style element in the document head.
|
|
1465
|
+
* If an element with the same ID exists, it will be updated.
|
|
1466
|
+
*
|
|
1467
|
+
* @param css - CSS string to inject
|
|
1468
|
+
* @param id - Style element ID (default: 'atomix-theme')
|
|
1469
|
+
*
|
|
1470
|
+
* @example
|
|
1471
|
+
* ```typescript
|
|
1472
|
+
* const css = ':root { --atomix-color-primary: #7AFFD7; }';
|
|
1473
|
+
* injectCSS(css);
|
|
1474
|
+
*
|
|
1475
|
+
* // With custom ID
|
|
1476
|
+
* injectCSS(css, 'my-custom-theme');
|
|
1477
|
+
* ```
|
|
1478
|
+
*/ function injectCSS$1(css, id = "atomix-theme") {
|
|
1479
|
+
if (!isBrowser$1()) return;
|
|
1480
|
+
let styleElement = document.getElementById(id);
|
|
1481
|
+
styleElement || (styleElement = document.createElement("style"), styleElement.id = id,
|
|
1482
|
+
styleElement.setAttribute("data-atomix-theme", "true"), document.head.appendChild(styleElement)),
|
|
1483
|
+
styleElement.textContent = css;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Remove injected CSS from DOM
|
|
1488
|
+
*
|
|
1489
|
+
* Removes the style element with the given ID from the document head.
|
|
1490
|
+
*
|
|
1491
|
+
* @param id - Style element ID to remove (default: 'atomix-theme')
|
|
1492
|
+
*
|
|
1493
|
+
* @example
|
|
1494
|
+
* ```typescript
|
|
1495
|
+
* removeCSS(); // Removes default 'atomix-theme'
|
|
1496
|
+
* removeCSS('my-custom-theme'); // Removes custom ID
|
|
1497
|
+
* ```
|
|
1498
|
+
*/ function removeCSS(id = "atomix-theme") {
|
|
1499
|
+
if (!isBrowser$1()) return;
|
|
1500
|
+
const styleElement = document.getElementById(id);
|
|
1501
|
+
styleElement && styleElement.remove();
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* Check if CSS is already injected
|
|
1506
|
+
*
|
|
1507
|
+
* @param id - Style element ID to check (default: 'atomix-theme')
|
|
1508
|
+
* @returns True if style element exists
|
|
1509
|
+
*/ function isCSSInjected(id = "atomix-theme") {
|
|
1510
|
+
return !!isBrowser$1() && null !== document.getElementById(id);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Theme System Constants
|
|
1515
|
+
*
|
|
1516
|
+
* Centralized constants for the theme system to avoid magic numbers and strings.
|
|
1517
|
+
*/
|
|
1518
|
+
/**
|
|
1519
|
+
* Default storage key for theme persistence
|
|
1520
|
+
*/
|
|
1521
|
+
/**
|
|
1522
|
+
* Theme System Error Handling
|
|
1523
|
+
*
|
|
1524
|
+
* Centralized error handling for the Atomix theme system.
|
|
1525
|
+
* Provides custom error classes and logging utilities.
|
|
1526
|
+
*/
|
|
1527
|
+
/**
|
|
1528
|
+
* Theme error codes
|
|
1529
|
+
*/
|
|
1530
|
+
var ThemeErrorCode, LogLevel;
|
|
1531
|
+
|
|
1532
|
+
"undefined" != typeof process && process.env, function(ThemeErrorCode) {
|
|
1533
|
+
/** Theme not found in registry */
|
|
1534
|
+
ThemeErrorCode.THEME_NOT_FOUND = "THEME_NOT_FOUND",
|
|
1535
|
+
/** Theme failed to load */
|
|
1536
|
+
ThemeErrorCode.THEME_LOAD_FAILED = "THEME_LOAD_FAILED",
|
|
1537
|
+
/** Theme validation failed */
|
|
1538
|
+
ThemeErrorCode.THEME_VALIDATION_FAILED = "THEME_VALIDATION_FAILED",
|
|
1539
|
+
/** Configuration loading failed */
|
|
1540
|
+
ThemeErrorCode.CONFIG_LOAD_FAILED = "CONFIG_LOAD_FAILED",
|
|
1541
|
+
/** Configuration validation failed */
|
|
1542
|
+
ThemeErrorCode.CONFIG_VALIDATION_FAILED = "CONFIG_VALIDATION_FAILED",
|
|
1543
|
+
/** Circular dependency detected */
|
|
1544
|
+
ThemeErrorCode.CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
|
|
1545
|
+
/** Missing dependency */
|
|
1546
|
+
ThemeErrorCode.MISSING_DEPENDENCY = "MISSING_DEPENDENCY",
|
|
1547
|
+
/** Storage operation failed */
|
|
1548
|
+
ThemeErrorCode.STORAGE_ERROR = "STORAGE_ERROR",
|
|
1549
|
+
/** Invalid theme name */
|
|
1550
|
+
ThemeErrorCode.INVALID_THEME_NAME = "INVALID_THEME_NAME",
|
|
1551
|
+
/** CSS injection failed */
|
|
1552
|
+
ThemeErrorCode.CSS_INJECTION_FAILED = "CSS_INJECTION_FAILED",
|
|
1553
|
+
/** Invalid color format */
|
|
1554
|
+
ThemeErrorCode.INVALID_COLOR_FORMAT = "INVALID_COLOR_FORMAT",
|
|
1555
|
+
/** Missing required token */
|
|
1556
|
+
ThemeErrorCode.MISSING_REQUIRED_TOKEN = "MISSING_REQUIRED_TOKEN",
|
|
1557
|
+
/** Accessibility contrast violation */
|
|
1558
|
+
ThemeErrorCode.CONTRAST_VIOLATION = "CONTRAST_VIOLATION",
|
|
1559
|
+
/** Invalid token type */
|
|
1560
|
+
ThemeErrorCode.INVALID_TOKEN_TYPE = "INVALID_TOKEN_TYPE",
|
|
1561
|
+
/** Unknown error */
|
|
1562
|
+
ThemeErrorCode.UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
|
1563
|
+
}(ThemeErrorCode || (ThemeErrorCode = {}));
|
|
1564
|
+
|
|
1565
|
+
/**
|
|
1566
|
+
* Custom error class for theme-related errors
|
|
1567
|
+
*/
|
|
1568
|
+
class ThemeError extends Error {
|
|
1569
|
+
constructor(message, code = ThemeErrorCode.UNKNOWN_ERROR, context) {
|
|
1570
|
+
super(message), this.name = "ThemeError", this.code = code, this.context = context,
|
|
1571
|
+
this.timestamp = Date.now(),
|
|
1572
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
1573
|
+
Error.captureStackTrace && Error.captureStackTrace(this, ThemeError);
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Convert error to JSON for logging
|
|
1577
|
+
*/ toJSON() {
|
|
1578
|
+
return {
|
|
1579
|
+
name: this.name,
|
|
1580
|
+
message: this.message,
|
|
1581
|
+
code: this.code,
|
|
1582
|
+
context: this.context,
|
|
1583
|
+
timestamp: this.timestamp,
|
|
1584
|
+
stack: this.stack
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* Log level
|
|
1591
|
+
*/ !function(LogLevel) {
|
|
1592
|
+
LogLevel[LogLevel.ERROR = 0] = "ERROR", LogLevel[LogLevel.WARN = 1] = "WARN", LogLevel[LogLevel.INFO = 2] = "INFO",
|
|
1593
|
+
LogLevel[LogLevel.DEBUG = 3] = "DEBUG";
|
|
1594
|
+
}(LogLevel || (LogLevel = {}));
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Theme Logger
|
|
1598
|
+
*
|
|
1599
|
+
* Centralized logging for the theme system.
|
|
1600
|
+
* Replaces console statements with structured logging.
|
|
1601
|
+
*/
|
|
1602
|
+
class ThemeLogger {
|
|
1603
|
+
constructor(config = {}) {
|
|
1604
|
+
this.config = {
|
|
1605
|
+
level: config.level ?? ("undefined" != typeof process && "production" === process.env?.NODE_ENV ? LogLevel.WARN : LogLevel.INFO),
|
|
1606
|
+
enableConsole: config.enableConsole ?? !0,
|
|
1607
|
+
onError: config.onError,
|
|
1608
|
+
onWarn: config.onWarn,
|
|
1609
|
+
onInfo: config.onInfo,
|
|
1610
|
+
onDebug: config.onDebug
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
* Log an error
|
|
1615
|
+
*/ error(message, error, context) {
|
|
1616
|
+
if (this.config.level < LogLevel.ERROR) return;
|
|
1617
|
+
const errorObj = error instanceof Error ? error : new Error(message), themeError = error instanceof ThemeError ? error : new ThemeError(message, ThemeErrorCode.UNKNOWN_ERROR, context);
|
|
1618
|
+
this.config.enableConsole && console.error(`[ThemeError] ${message}`, {
|
|
1619
|
+
error: errorObj,
|
|
1620
|
+
context: {
|
|
1621
|
+
...context,
|
|
1622
|
+
...themeError.context
|
|
1623
|
+
},
|
|
1624
|
+
code: themeError.code
|
|
1625
|
+
}), this.config.onError?.(themeError, context);
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Log a warning
|
|
1629
|
+
*/ warn(message, context) {
|
|
1630
|
+
this.config.level < LogLevel.WARN || (this.config.enableConsole && console.warn(`[ThemeWarning] ${message}`, context || {}),
|
|
1631
|
+
this.config.onWarn?.(message, context));
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Log an info message
|
|
1635
|
+
*/ info(message, context) {
|
|
1636
|
+
this.config.level < LogLevel.INFO || (this.config.enableConsole && console.info(`[ThemeInfo] ${message}`, context || {}),
|
|
1637
|
+
this.config.onInfo?.(message, context));
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* Log a debug message
|
|
1641
|
+
*/ debug(message, context) {
|
|
1642
|
+
this.config.level < LogLevel.DEBUG || (this.config.enableConsole, this.config.onDebug?.(message, context));
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
/**
|
|
1647
|
+
* Default logger instance
|
|
1648
|
+
*/ let defaultLogger = null;
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* Get or create default logger
|
|
1652
|
+
*/ function getLogger() {
|
|
1653
|
+
return defaultLogger || (defaultLogger = new ThemeLogger), defaultLogger;
|
|
1654
|
+
}
|
|
1163
1655
|
|
|
1164
1656
|
/**
|
|
1165
1657
|
* Check if code is running in a browser environment
|
|
1166
|
-
*/
|
|
1167
|
-
const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), sanitizePath = path => path.replace(/[<>"']/g, "").replace(/\.\./g, "").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "") // Trim leading/trailing slashes
|
|
1658
|
+
*/ const isBrowser = () => "undefined" != typeof window && "undefined" != typeof document, isServer = () => !isBrowser(), sanitizePath = path => path.replace(/[<>"']/g, "").replace(/\.\./g, "").replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "") // Trim leading/trailing slashes
|
|
1168
1659
|
, buildThemePath = (themeName, basePath = "/themes", useMinified = !1, cdnPath = null) => {
|
|
1169
1660
|
// Validate theme name to prevent path injection
|
|
1170
1661
|
if (!isValidThemeName(themeName)) throw new ThemeError(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`, ThemeErrorCode.INVALID_THEME_NAME, {
|
|
@@ -1248,107 +1739,481 @@ const isBrowser = () => "undefined" != typeof window && "undefined" != typeof do
|
|
|
1248
1739
|
|
|
1249
1740
|
/**
|
|
1250
1741
|
* Check if code is running on the server (SSR)
|
|
1251
|
-
*/
|
|
1742
|
+
*/ var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
|
|
1743
|
+
return function(that, callbackfn, argumentsLength, memo) {
|
|
1744
|
+
var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
|
|
1745
|
+
if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
|
|
1746
|
+
var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
|
|
1747
|
+
if (argumentsLength < 2) for (;;) {
|
|
1748
|
+
if (index in self) {
|
|
1749
|
+
memo = self[index], index += i;
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
|
|
1753
|
+
}
|
|
1754
|
+
for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
|
|
1755
|
+
return memo;
|
|
1756
|
+
};
|
|
1757
|
+
}, arrayReduce = {
|
|
1758
|
+
// `Array.prototype.reduce` method
|
|
1759
|
+
// https://tc39.es/ecma262/#sec-array.prototype.reduce
|
|
1760
|
+
left: createMethod(!1),
|
|
1761
|
+
// `Array.prototype.reduceRight` method
|
|
1762
|
+
// https://tc39.es/ecma262/#sec-array.prototype.reduceright
|
|
1763
|
+
right: createMethod(!0)
|
|
1764
|
+
}, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
|
|
1765
|
+
return userAgent.slice(0, string.length) === string;
|
|
1766
|
+
}, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
|
|
1767
|
+
|
|
1768
|
+
// `Array.prototype.reduce` method
|
|
1769
|
+
// https://tc39.es/ecma262/#sec-array.prototype.reduce
|
|
1770
|
+
_export({
|
|
1771
|
+
target: "Array",
|
|
1772
|
+
proto: !0,
|
|
1773
|
+
forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
|
|
1774
|
+
var method = [][METHOD_NAME];
|
|
1775
|
+
return !!method && fails((function() {
|
|
1776
|
+
// eslint-disable-next-line no-useless-call -- required for testing
|
|
1777
|
+
method.call(null, argument || function() {
|
|
1778
|
+
return 1;
|
|
1779
|
+
}, 1);
|
|
1780
|
+
}));
|
|
1781
|
+
}("reduce")
|
|
1782
|
+
}, {
|
|
1783
|
+
reduce: function(callbackfn /* , initialValue */) {
|
|
1784
|
+
var length = arguments.length;
|
|
1785
|
+
return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
|
|
1786
|
+
}
|
|
1787
|
+
});
|
|
1788
|
+
|
|
1789
|
+
var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype;
|
|
1790
|
+
|
|
1791
|
+
const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
1792
|
+
var own = it.reduce;
|
|
1793
|
+
return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
|
|
1794
|
+
}));
|
|
1795
|
+
|
|
1796
|
+
// ============================================================================
|
|
1797
|
+
// Theme Mode Switching
|
|
1798
|
+
// ============================================================================
|
|
1252
1799
|
/**
|
|
1253
|
-
*
|
|
1800
|
+
* Switch between light and dark themes
|
|
1254
1801
|
*
|
|
1255
|
-
*
|
|
1256
|
-
*
|
|
1257
|
-
|
|
1802
|
+
* Automatically toggles a class on the root element and persists the choice.
|
|
1803
|
+
*
|
|
1804
|
+
* @param mode - Theme mode ('light', 'dark', or 'system')
|
|
1805
|
+
* @param options - Configuration options
|
|
1806
|
+
*
|
|
1807
|
+
* @example
|
|
1808
|
+
* ```typescript
|
|
1809
|
+
* import { switchTheme } from '@shohojdhara/atomix/theme/utils';
|
|
1810
|
+
*
|
|
1811
|
+
* // Switch to dark mode
|
|
1812
|
+
* switchTheme('dark');
|
|
1813
|
+
*
|
|
1814
|
+
* // Toggle between light/dark
|
|
1815
|
+
* const current = getCurrentTheme();
|
|
1816
|
+
* switchTheme(current === 'dark' ? 'light' : 'dark');
|
|
1817
|
+
* ```
|
|
1818
|
+
*/ function switchTheme(mode, options = {}) {
|
|
1819
|
+
const {selector: selector = ":root", storageKey: storageKey = "atomix-theme", enableTransition: enableTransition = !0, transitionDuration: transitionDuration = 300} = options, resolvedMode = "system" === mode ? getSystemTheme() : mode, root = ":root" === selector ? document.documentElement : document.querySelector(selector);
|
|
1820
|
+
// Determine actual mode (resolve 'system')
|
|
1821
|
+
if (root) {
|
|
1822
|
+
// Add transition class if enabled
|
|
1823
|
+
if (enableTransition) {
|
|
1824
|
+
const htmlRoot = root;
|
|
1825
|
+
htmlRoot.style.transition = `all ${transitionDuration}ms ease-in-out`,
|
|
1826
|
+
// Remove transition after it completes
|
|
1827
|
+
setTimeout((() => {
|
|
1828
|
+
htmlRoot.style.transition = "";
|
|
1829
|
+
}), transitionDuration);
|
|
1830
|
+
}
|
|
1831
|
+
// Apply theme class
|
|
1832
|
+
root.classList.remove("atomix-theme-light", "atomix-theme-dark"), root.classList.add(`atomix-theme-${resolvedMode}`),
|
|
1833
|
+
// Set data attribute for CSS selectors
|
|
1834
|
+
root.setAttribute("data-theme", resolvedMode),
|
|
1835
|
+
// Persist choice
|
|
1836
|
+
persistTheme(resolvedMode, {
|
|
1837
|
+
storageKey: storageKey
|
|
1838
|
+
}),
|
|
1839
|
+
// Dispatch custom event for listeners
|
|
1840
|
+
window.dispatchEvent(new CustomEvent("atomix-theme-change", {
|
|
1841
|
+
detail: {
|
|
1842
|
+
mode: resolvedMode
|
|
1843
|
+
}
|
|
1844
|
+
}));
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
/**
|
|
1849
|
+
* Toggle between light and dark themes
|
|
1850
|
+
*
|
|
1851
|
+
* @param options - Configuration options
|
|
1852
|
+
* @returns The new theme mode
|
|
1853
|
+
*
|
|
1854
|
+
* @example
|
|
1855
|
+
* ```typescript
|
|
1856
|
+
* const newMode = toggleTheme();
|
|
1857
|
+
* console.log('Switched to:', newMode);
|
|
1858
|
+
* ```
|
|
1859
|
+
*/ function toggleTheme(options = {}) {
|
|
1860
|
+
const next = "dark" === getCurrentTheme(options.storageKey) ? "light" : "dark";
|
|
1861
|
+
return switchTheme(next, options), next;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* Get current theme mode
|
|
1866
|
+
*
|
|
1867
|
+
* @param storageKey - Storage key (default: 'atomix-theme')
|
|
1868
|
+
* @returns Current theme mode or 'light' if not set
|
|
1869
|
+
*/ function getCurrentTheme(storageKey = "atomix-theme") {
|
|
1870
|
+
return "undefined" == typeof window ? "light" : localStorage.getItem(storageKey) || "light";
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/**
|
|
1874
|
+
* Get system theme preference
|
|
1875
|
+
*
|
|
1876
|
+
* @returns 'dark' if system prefers dark mode, 'light' otherwise
|
|
1877
|
+
*/ function getSystemTheme() {
|
|
1878
|
+
return "undefined" == typeof window ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* Initialize theme based on saved preference or system preference
|
|
1883
|
+
*
|
|
1884
|
+
* Call this once at app startup.
|
|
1885
|
+
*
|
|
1886
|
+
* @param options - Configuration options
|
|
1887
|
+
* @returns The initialized theme mode
|
|
1888
|
+
*
|
|
1889
|
+
* @example
|
|
1890
|
+
* ```typescript
|
|
1891
|
+
* // In your app entry point
|
|
1892
|
+
* import { initializeTheme } from '@shohojdhara/atomix/theme/utils';
|
|
1893
|
+
*
|
|
1894
|
+
* const theme = initializeTheme();
|
|
1895
|
+
* console.log('Theme initialized:', theme);
|
|
1896
|
+
* ```
|
|
1897
|
+
*/ function initializeTheme(options = {}) {
|
|
1898
|
+
const saved = getCurrentTheme(options.storageKey);
|
|
1899
|
+
// If no saved preference, use system preference
|
|
1900
|
+
if (!saved || "system" === saved) {
|
|
1901
|
+
const system = getSystemTheme();
|
|
1902
|
+
return switchTheme(system, options), system;
|
|
1903
|
+
}
|
|
1904
|
+
// Use saved preference
|
|
1905
|
+
return switchTheme(saved, options), saved;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
/**
|
|
1909
|
+
* Listen for system theme changes
|
|
1910
|
+
*
|
|
1911
|
+
* @param callback - Function to call when system theme changes
|
|
1912
|
+
* @returns Cleanup function to stop listening
|
|
1913
|
+
*
|
|
1914
|
+
* @example
|
|
1915
|
+
* ```typescript
|
|
1916
|
+
* const cleanup = listenToSystemTheme((mode) => {
|
|
1917
|
+
* console.log('System theme changed to:', mode);
|
|
1918
|
+
* switchTheme(mode);
|
|
1919
|
+
* });
|
|
1920
|
+
*
|
|
1921
|
+
* // Later, when component unmounts
|
|
1922
|
+
* cleanup();
|
|
1923
|
+
* ```
|
|
1924
|
+
*/ function listenToSystemTheme(callback) {
|
|
1925
|
+
if ("undefined" == typeof window) return () => {};
|
|
1926
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"), handler = e => {
|
|
1927
|
+
callback(e.matches ? "dark" : "light");
|
|
1928
|
+
};
|
|
1929
|
+
// Modern browsers
|
|
1930
|
+
return mediaQuery.addEventListener ? (mediaQuery.addEventListener("change", handler),
|
|
1931
|
+
() => mediaQuery.removeEventListener("change", handler)) : (
|
|
1932
|
+
// Fallback for older browsers
|
|
1933
|
+
mediaQuery.addListener(handler), () => mediaQuery.removeListener(handler));
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1258
1936
|
// ============================================================================
|
|
1259
|
-
//
|
|
1937
|
+
// Theme Persistence
|
|
1260
1938
|
// ============================================================================
|
|
1261
1939
|
/**
|
|
1262
|
-
*
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1940
|
+
* Save theme preference to storage
|
|
1941
|
+
*
|
|
1942
|
+
* @param mode - Theme mode to save
|
|
1943
|
+
* @param options - Persistence options
|
|
1944
|
+
*/ function persistTheme(mode, options = {}) {
|
|
1945
|
+
if ("undefined" == typeof window) return;
|
|
1946
|
+
const {storageKey: storageKey = "atomix-theme", storageType: storageType = "localStorage"} = options;
|
|
1947
|
+
("localStorage" === storageType ? localStorage : sessionStorage).setItem(storageKey, mode);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
/**
|
|
1951
|
+
* Clear saved theme preference
|
|
1952
|
+
*
|
|
1953
|
+
* @param options - Persistence options
|
|
1954
|
+
*/ function clearThemePreference(options = {}) {
|
|
1955
|
+
if ("undefined" == typeof window) return;
|
|
1956
|
+
const {storageKey: storageKey = "atomix-theme", storageType: storageType = "localStorage"} = options;
|
|
1957
|
+
("localStorage" === storageType ? localStorage : sessionStorage).removeItem(storageKey);
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// ============================================================================
|
|
1961
|
+
// Theme Tokens Manipulation
|
|
1962
|
+
// ============================================================================
|
|
1963
|
+
/**
|
|
1964
|
+
* Merge multiple token sets
|
|
1965
|
+
*
|
|
1966
|
+
* Deep merges token objects, with later tokens overriding earlier ones.
|
|
1967
|
+
*
|
|
1968
|
+
* @param tokens - Token objects to merge
|
|
1969
|
+
* @returns Merged tokens
|
|
1970
|
+
*
|
|
1971
|
+
* @example
|
|
1972
|
+
* ```typescript
|
|
1973
|
+
* const merged = mergeTokens(
|
|
1974
|
+
* baseTokens,
|
|
1975
|
+
* { colors: { primary: { main: '#custom' } } }
|
|
1976
|
+
* );
|
|
1977
|
+
* ```
|
|
1978
|
+
*/ function mergeTokens(...tokens) {
|
|
1979
|
+
return _reduceInstanceProperty(tokens).call(tokens, ((acc, current) => deepMerge(acc, current)), {});
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* Override specific tokens
|
|
1984
|
+
*
|
|
1985
|
+
* Creates a new token object with specific overrides.
|
|
1986
|
+
*
|
|
1987
|
+
* @param base - Base tokens
|
|
1988
|
+
* @param overrides - Tokens to override
|
|
1989
|
+
* @returns New tokens with overrides applied
|
|
1990
|
+
*
|
|
1991
|
+
* @example
|
|
1992
|
+
* ```typescript
|
|
1993
|
+
* const customized = overrideTokens(defaultTokens, {
|
|
1994
|
+
* colors: {
|
|
1995
|
+
* primary: { main: '#ff0000' }
|
|
1996
|
+
* }
|
|
1997
|
+
* });
|
|
1998
|
+
* ```
|
|
1999
|
+
*/ function overrideTokens(base, overrides) {
|
|
2000
|
+
return deepMerge({
|
|
2001
|
+
...base
|
|
2002
|
+
}, overrides);
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
/**
|
|
2006
|
+
* Pick specific token categories
|
|
2007
|
+
*
|
|
2008
|
+
* Extracts only the specified categories from tokens.
|
|
2009
|
+
*
|
|
2010
|
+
* @param tokens - Source tokens
|
|
2011
|
+
* @param categories - Categories to pick
|
|
2012
|
+
* @returns Tokens with only selected categories
|
|
2013
|
+
*
|
|
2014
|
+
* @example
|
|
2015
|
+
* ```typescript
|
|
2016
|
+
* const colorTokens = pickTokens(allTokens, ['colors']);
|
|
2017
|
+
* ```
|
|
2018
|
+
*/ function pickTokens(tokens, categories) {
|
|
2019
|
+
const result = {};
|
|
2020
|
+
return categories.forEach((category => {
|
|
2021
|
+
tokens[category] && (result[category] = tokens[category]);
|
|
2022
|
+
})), result;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
/**
|
|
2026
|
+
* Omit specific token categories
|
|
2027
|
+
*
|
|
2028
|
+
* Removes specified categories from tokens.
|
|
2029
|
+
*
|
|
2030
|
+
* @param tokens - Source tokens
|
|
2031
|
+
* @param categories - Categories to omit
|
|
2032
|
+
* @returns Tokens without omitted categories
|
|
2033
|
+
*
|
|
2034
|
+
* @example
|
|
2035
|
+
* ```typescript
|
|
2036
|
+
* const withoutColors = omitTokens(allTokens, ['colors']);
|
|
2037
|
+
* ```
|
|
2038
|
+
*/ function omitTokens(tokens, categories) {
|
|
2039
|
+
const result = {
|
|
2040
|
+
...tokens
|
|
2041
|
+
};
|
|
2042
|
+
return categories.forEach((category => {
|
|
2043
|
+
delete result[category];
|
|
2044
|
+
})), result;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// ============================================================================
|
|
2048
|
+
// Color Utilities
|
|
2049
|
+
// ============================================================================
|
|
2050
|
+
/**
|
|
2051
|
+
* Convert hex color to RGB
|
|
2052
|
+
*
|
|
2053
|
+
* @param hex - Hex color (with or without #)
|
|
2054
|
+
* @returns RGB object { r, g, b }
|
|
2055
|
+
*/ function hexToRgb$1(hex) {
|
|
2056
|
+
// Validate
|
|
2057
|
+
if (
|
|
2058
|
+
// Remove # if present
|
|
2059
|
+
// Handle shorthand hex
|
|
2060
|
+
3 === (hex = hex.replace(/^#/, "")).length && (hex = hex.split("").map((c => c + c)).join("")),
|
|
2061
|
+
6 !== hex.length) return null;
|
|
2062
|
+
const num = parseInt(hex, 16);
|
|
2063
|
+
return {
|
|
2064
|
+
r: num >> 16 & 255,
|
|
2065
|
+
g: num >> 8 & 255,
|
|
2066
|
+
b: 255 & num
|
|
2067
|
+
};
|
|
1271
2068
|
}
|
|
1272
2069
|
|
|
1273
2070
|
/**
|
|
1274
|
-
* Convert RGB to hex
|
|
2071
|
+
* Convert RGB to hex
|
|
2072
|
+
*
|
|
2073
|
+
* @param r - Red (0-255)
|
|
2074
|
+
* @param g - Green (0-255)
|
|
2075
|
+
* @param b - Blue (0-255)
|
|
2076
|
+
* @returns Hex color with #
|
|
1275
2077
|
*/ function rgbToHex(r, g, b) {
|
|
1276
|
-
|
|
1277
|
-
|
|
2078
|
+
return "#" + [ r, g, b ].map((x => {
|
|
2079
|
+
const hex = x.toString(16);
|
|
2080
|
+
return 1 === hex.length ? "0" + hex : hex;
|
|
2081
|
+
})).join("");
|
|
1278
2082
|
}
|
|
1279
2083
|
|
|
1280
2084
|
/**
|
|
1281
|
-
* Calculate
|
|
1282
|
-
*
|
|
1283
|
-
|
|
1284
|
-
|
|
2085
|
+
* Calculate luminance of a color
|
|
2086
|
+
*
|
|
2087
|
+
* Used for determining contrast ratios.
|
|
2088
|
+
*
|
|
2089
|
+
* @param hex - Hex color
|
|
2090
|
+
* @returns Luminance value (0-1)
|
|
2091
|
+
*/ function getLuminance(hex) {
|
|
2092
|
+
const rgb = hexToRgb$1(hex);
|
|
1285
2093
|
if (!rgb) return 0;
|
|
1286
|
-
const
|
|
1287
|
-
|
|
1288
|
-
return val <= .03928 ? val / 12.92 : Math.pow((val + .055) / 1.055, 2.4);
|
|
1289
|
-
}));
|
|
1290
|
-
return .2126 * (rs ?? 0) + .7152 * (gs ?? 0) + .0722 * (bs ?? 0);
|
|
2094
|
+
const [r, g, b] = [ rgb.r, rgb.g, rgb.b ].map((v => (v /= 255) <= .03928 ? v / 12.92 : Math.pow((v + .055) / 1.055, 2.4)));
|
|
2095
|
+
return .2126 * (r ?? 0) + .7152 * (g ?? 0) + .0722 * (b ?? 0);
|
|
1291
2096
|
}
|
|
1292
2097
|
|
|
1293
2098
|
/**
|
|
1294
2099
|
* Calculate contrast ratio between two colors
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
2100
|
+
*
|
|
2101
|
+
* @param hex1 - First hex color
|
|
2102
|
+
* @param hex2 - Second hex color
|
|
2103
|
+
* @returns Contrast ratio (1-21)
|
|
2104
|
+
*/ function getContrastRatio(hex1, hex2) {
|
|
2105
|
+
const lum1 = getLuminance(hex1), lum2 = getLuminance(hex2);
|
|
2106
|
+
return (Math.max(lum1, lum2) + .05) / (Math.min(lum1, lum2) + .05);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
/**
|
|
2110
|
+
* Check if text color passes WCAG AA standard
|
|
2111
|
+
*
|
|
2112
|
+
* @param textColor - Text color hex
|
|
2113
|
+
* @param backgroundColor - Background color hex
|
|
2114
|
+
* @param size - Font size ('small' or 'large')
|
|
2115
|
+
* @returns true if passes WCAG AA
|
|
2116
|
+
*/ function isAccessible(textColor, backgroundColor, size = "small") {
|
|
2117
|
+
return getContrastRatio(textColor, backgroundColor) >= ("large" === size ? 3 : 4.5);
|
|
1298
2118
|
}
|
|
1299
2119
|
|
|
1300
2120
|
/**
|
|
1301
|
-
* Get appropriate
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
2121
|
+
* Get appropriate text color (black or white) for a background
|
|
2122
|
+
*
|
|
2123
|
+
* @param backgroundColor - Background hex color
|
|
2124
|
+
* @param threshold - Contrast threshold (default: 3)
|
|
2125
|
+
* @returns '#000000' or '#FFFFFF'
|
|
2126
|
+
*/ function getContrastText(backgroundColor, threshold = 3) {
|
|
2127
|
+
return getContrastRatio(backgroundColor, "#FFFFFF") >= threshold ? "#FFFFFF" : "#000000";
|
|
1305
2128
|
}
|
|
1306
2129
|
|
|
1307
2130
|
/**
|
|
1308
|
-
* Lighten a color
|
|
2131
|
+
* Lighten a color
|
|
1309
2132
|
*
|
|
1310
|
-
* @param
|
|
1311
|
-
* @param amount - Amount to lighten (0-1)
|
|
2133
|
+
* @param hex - Base hex color
|
|
2134
|
+
* @param amount - Amount to lighten (0-1)
|
|
1312
2135
|
* @returns Lightened hex color
|
|
1313
|
-
*/ function lighten(
|
|
1314
|
-
const rgb = hexToRgb$1(
|
|
1315
|
-
if (!rgb) return
|
|
1316
|
-
|
|
1317
|
-
|
|
2136
|
+
*/ function lighten(hex, amount = 0) {
|
|
2137
|
+
const rgb = hexToRgb$1(hex);
|
|
2138
|
+
if (!rgb) return hex;
|
|
2139
|
+
// Use amount directly as factor (0-1)
|
|
2140
|
+
const factor = Math.max(0, Math.min(1, amount)), r = Math.round(rgb.r + (255 - rgb.r) * factor), g = Math.round(rgb.g + (255 - rgb.g) * factor), b = Math.round(rgb.b + (255 - rgb.b) * factor);
|
|
2141
|
+
return rgbToHex(Math.min(255, r), Math.min(255, g), Math.min(255, b));
|
|
1318
2142
|
}
|
|
1319
2143
|
|
|
1320
2144
|
/**
|
|
1321
|
-
* Darken a color
|
|
2145
|
+
* Darken a color
|
|
1322
2146
|
*
|
|
1323
|
-
* @param
|
|
1324
|
-
* @param amount - Amount to darken (0-1)
|
|
2147
|
+
* @param hex - Base hex color
|
|
2148
|
+
* @param amount - Amount to darken (0-1)
|
|
1325
2149
|
* @returns Darkened hex color
|
|
1326
|
-
*/ function darken(
|
|
1327
|
-
const rgb = hexToRgb$1(
|
|
1328
|
-
if (!rgb) return
|
|
1329
|
-
|
|
1330
|
-
|
|
2150
|
+
*/ function darken(hex, amount = 0) {
|
|
2151
|
+
const rgb = hexToRgb$1(hex);
|
|
2152
|
+
if (!rgb) return hex;
|
|
2153
|
+
// Use amount directly as factor (0-1)
|
|
2154
|
+
const factor = Math.max(0, Math.min(1, amount)), r = Math.round(rgb.r * (1 - factor)), g = Math.round(rgb.g * (1 - factor)), b = Math.round(rgb.b * (1 - factor));
|
|
2155
|
+
return rgbToHex(Math.max(0, r), Math.max(0, g), Math.max(0, b));
|
|
1331
2156
|
}
|
|
1332
2157
|
|
|
1333
2158
|
/**
|
|
1334
|
-
* Add alpha
|
|
2159
|
+
* Add alpha to a color
|
|
1335
2160
|
*
|
|
1336
|
-
* @param
|
|
2161
|
+
* @param hex - Hex color
|
|
1337
2162
|
* @param opacity - Opacity value (0-1)
|
|
1338
2163
|
* @returns RGBA color string
|
|
1339
|
-
*/ function alpha(
|
|
1340
|
-
const rgb = hexToRgb$1(
|
|
1341
|
-
if (!rgb) return
|
|
1342
|
-
const
|
|
1343
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
2164
|
+
*/ function alpha(hex, opacity) {
|
|
2165
|
+
const rgb = hexToRgb$1(hex);
|
|
2166
|
+
if (!rgb) return hex;
|
|
2167
|
+
const validOpacity = Math.max(0, Math.min(1, opacity));
|
|
2168
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${validOpacity})`;
|
|
1344
2169
|
}
|
|
1345
2170
|
|
|
1346
2171
|
/**
|
|
1347
2172
|
* Emphasize a color (lighten if dark, darken if light)
|
|
1348
2173
|
*
|
|
1349
|
-
* @param
|
|
1350
|
-
* @param
|
|
2174
|
+
* @param hex - Hex color
|
|
2175
|
+
* @param amount - Amount to emphasize (0-1)
|
|
1351
2176
|
* @returns Emphasized hex color
|
|
2177
|
+
*/ function emphasize(hex, amount = .15) {
|
|
2178
|
+
return getLuminance(hex) > .5 ? darken(hex, amount) : lighten(hex, amount);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/**
|
|
2182
|
+
* Create a spacing utility
|
|
2183
|
+
*
|
|
2184
|
+
* @param spacingInput - Spacing configuration
|
|
2185
|
+
* @returns Spacing function
|
|
2186
|
+
*/ function createSpacing(spacingInput = 4) {
|
|
2187
|
+
return (...values) => 0 === values.length ? "0px" : "function" == typeof spacingInput ? spacingInput(...values) : values.map((value => {
|
|
2188
|
+
if ("number" == typeof spacingInput) return value * spacingInput + "px";
|
|
2189
|
+
if (Array.isArray(spacingInput)) {
|
|
2190
|
+
const scaled = spacingInput[value];
|
|
2191
|
+
return "number" == typeof scaled ? `${scaled}px` : `${value}px`;
|
|
2192
|
+
}
|
|
2193
|
+
return `${value}px`;
|
|
2194
|
+
})).join(" ");
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
/**
|
|
2198
|
+
* CSS Variable Generator
|
|
2199
|
+
*
|
|
2200
|
+
* Generates CSS custom properties from theme objects and injects them into the DOM.
|
|
2201
|
+
*
|
|
2202
|
+
* **Token Naming Alignment:**
|
|
2203
|
+
* This generator produces CSS variables that match the SCSS token naming pattern exactly:
|
|
2204
|
+
* - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
|
|
2205
|
+
* - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
|
|
2206
|
+
* - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
|
|
2207
|
+
* - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
|
|
2208
|
+
*
|
|
2209
|
+
* All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
|
|
2210
|
+
* This ensures compatibility between SCSS themes and JavaScript themes.
|
|
2211
|
+
*
|
|
2212
|
+
* @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
|
|
2213
|
+
*/
|
|
2214
|
+
/**
|
|
2215
|
+
* Convert a nested object to flat CSS variable declarations
|
|
2216
|
+
* Uses iterative approach for better performance with large objects
|
|
1352
2217
|
*/
|
|
1353
2218
|
/**
|
|
1354
2219
|
* Generate a color scale from a base color (1-10 steps)
|
|
@@ -1416,18 +2281,7 @@ function generateCSSVariables(theme, options = {}) {
|
|
|
1416
2281
|
color.dark && (vars[`${prefix}-${key}-hover`] = color.dark),
|
|
1417
2282
|
// Generate semantic color variants (matches SCSS patterns)
|
|
1418
2283
|
// Text emphasis: emphasized version of the color for text (--atomix-primary-text-emphasis)
|
|
1419
|
-
vars[`${prefix}-${key}-text-emphasis`] =
|
|
1420
|
-
return getLuminance(color) > .5 ? darken(color, coefficient) : lighten(color, coefficient);
|
|
1421
|
-
}
|
|
1422
|
-
// ============================================================================
|
|
1423
|
-
// Spacing Utilities
|
|
1424
|
-
// ============================================================================
|
|
1425
|
-
/**
|
|
1426
|
-
* Create a spacing function from various input types
|
|
1427
|
-
*
|
|
1428
|
-
* @param spacingInput - Spacing configuration (number, array, or function), default 4
|
|
1429
|
-
* @returns Spacing function
|
|
1430
|
-
*/ (color.main, .15),
|
|
2284
|
+
vars[`${prefix}-${key}-text-emphasis`] = emphasize(color.main, .15),
|
|
1431
2285
|
// Background subtle: very light version for backgrounds (--atomix-primary-bg-subtle)
|
|
1432
2286
|
vars[`${prefix}-${key}-bg-subtle`] = alpha(color.main, .1),
|
|
1433
2287
|
// Border subtle: light version for borders (--atomix-primary-border-subtle)
|
|
@@ -1757,44 +2611,22 @@ function generateCSSVariables(theme, options = {}) {
|
|
|
1757
2611
|
// 2X large border radius (maps to spacing-4 = 16px)
|
|
1758
2612
|
vars[`${prefix}-border-radius-xxl`] = formatValue(borderRadius.xxl, "1rem"),
|
|
1759
2613
|
// 3X large border radius (maps to spacing-6 = 24px)
|
|
1760
|
-
vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius["3xl"], "1.5rem"),
|
|
1761
|
-
// 4X large border radius (maps to spacing-8 = 32px)
|
|
1762
|
-
vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius["4xl"], "2rem"),
|
|
1763
|
-
// Pill shape (fully rounded, maps to spacing-200 = 800px)
|
|
1764
|
-
vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, "50rem"),
|
|
1765
|
-
vars;
|
|
1766
|
-
}
|
|
1767
|
-
/**
|
|
1768
|
-
* Generate focus ring CSS variables
|
|
1769
|
-
*/ (theme.borderRadius, prefix)), Object.assign(variables, function(palette, prefix) {
|
|
1770
|
-
const vars = {};
|
|
1771
|
-
// Focus ring properties
|
|
1772
|
-
return vars[`${prefix}-focus-ring-width`] = "3px", vars[`${prefix}-focus-ring-offset`] = "2px",
|
|
1773
|
-
vars[`${prefix}-focus-ring-opacity`] = "0.25", vars;
|
|
1774
|
-
}(theme.palette, prefix)), theme.custom && Object.keys(theme.custom).length > 0) {
|
|
1775
|
-
const customVars =
|
|
1776
|
-
/**
|
|
1777
|
-
* CSS Variable Generator
|
|
1778
|
-
*
|
|
1779
|
-
* Generates CSS custom properties from theme objects and injects them into the DOM.
|
|
1780
|
-
*
|
|
1781
|
-
* **Token Naming Alignment:**
|
|
1782
|
-
* This generator produces CSS variables that match the SCSS token naming pattern exactly:
|
|
1783
|
-
* - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
|
|
1784
|
-
* - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
|
|
1785
|
-
* - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
|
|
1786
|
-
* - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
|
|
1787
|
-
*
|
|
1788
|
-
* All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
|
|
1789
|
-
* This ensures compatibility between SCSS themes and JavaScript themes.
|
|
1790
|
-
*
|
|
1791
|
-
* @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
|
|
1792
|
-
*/
|
|
1793
|
-
/**
|
|
1794
|
-
* Convert a nested object to flat CSS variable declarations
|
|
1795
|
-
* Uses iterative approach for better performance with large objects
|
|
1796
|
-
*/
|
|
1797
|
-
function(obj, prefix = "", result = {}) {
|
|
2614
|
+
vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius["3xl"], "1.5rem"),
|
|
2615
|
+
// 4X large border radius (maps to spacing-8 = 32px)
|
|
2616
|
+
vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius["4xl"], "2rem"),
|
|
2617
|
+
// Pill shape (fully rounded, maps to spacing-200 = 800px)
|
|
2618
|
+
vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, "50rem"),
|
|
2619
|
+
vars;
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Generate focus ring CSS variables
|
|
2623
|
+
*/ (theme.borderRadius, prefix)), Object.assign(variables, function(palette, prefix) {
|
|
2624
|
+
const vars = {};
|
|
2625
|
+
// Focus ring properties
|
|
2626
|
+
return vars[`${prefix}-focus-ring-width`] = "3px", vars[`${prefix}-focus-ring-offset`] = "2px",
|
|
2627
|
+
vars[`${prefix}-focus-ring-opacity`] = "0.25", vars;
|
|
2628
|
+
}(theme.palette, prefix)), theme.custom && Object.keys(theme.custom).length > 0) {
|
|
2629
|
+
const customVars = function(obj, prefix = "", result = {}) {
|
|
1798
2630
|
// Use iterative approach with stack to avoid deep recursion
|
|
1799
2631
|
const stack = [ {
|
|
1800
2632
|
obj: obj,
|
|
@@ -2248,19 +3080,29 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
|
|
|
2248
3080
|
// Check storage first
|
|
2249
3081
|
if (enablePersistence && storageAdapter.isAvailable()) {
|
|
2250
3082
|
const stored = storageAdapter.getItem(storageKey);
|
|
2251
|
-
if (stored)
|
|
3083
|
+
if (stored) {
|
|
3084
|
+
// If it looks like a JSON object, parse it
|
|
3085
|
+
if (stored.trim().startsWith("{")) try {
|
|
3086
|
+
return JSON.parse(stored);
|
|
3087
|
+
} catch (e) {
|
|
3088
|
+
return logger.error("Failed to parse stored theme tokens", e), stored;
|
|
3089
|
+
}
|
|
3090
|
+
return stored;
|
|
3091
|
+
}
|
|
2252
3092
|
}
|
|
2253
3093
|
// If defaultTheme is provided, use it
|
|
2254
3094
|
return null != defaultTheme ? defaultTheme : "default";
|
|
2255
3095
|
// Default fallback
|
|
2256
3096
|
}), [ defaultTheme, enablePersistence, storageKey, storageAdapter ]), [currentTheme, setCurrentTheme] = useState((() => "string" == typeof initialDefaultTheme ? initialDefaultTheme : "tokens-theme")), [activeTokens, setActiveTokens] = useState((() => {
|
|
2257
|
-
//
|
|
2258
|
-
if (
|
|
3097
|
+
// 1. Check if initialDefaultTheme (from storage) is an object
|
|
3098
|
+
if (initialDefaultTheme && "string" != typeof initialDefaultTheme) {
|
|
3099
|
+
const {tokens: tokens, validation: validation} = validateAndMergeTokens(initialDefaultTheme);
|
|
3100
|
+
if (validation.valid) return tokens;
|
|
3101
|
+
}
|
|
3102
|
+
// 2. Check if defaultTheme prop is an object
|
|
3103
|
+
if (defaultTheme && "string" != typeof defaultTheme) {
|
|
2259
3104
|
const {tokens: tokens, validation: validation} = validateAndMergeTokens(defaultTheme);
|
|
2260
|
-
|
|
2261
|
-
errors: validation.errors,
|
|
2262
|
-
warnings: validation.warnings
|
|
2263
|
-
}), createTokens({}));
|
|
3105
|
+
if (validation.valid) return tokens;
|
|
2264
3106
|
}
|
|
2265
3107
|
return null;
|
|
2266
3108
|
})), [isLoading, setIsLoading] = useState(!1), [error, setError] = useState(null), loadedThemesRef = useRef(new Set), themePromisesRef = useRef({}), abortControllerRef = useRef(null);
|
|
@@ -2272,10 +3114,14 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
|
|
|
2272
3114
|
useEffect((() => {
|
|
2273
3115
|
isServer() || applyThemeAttributes(String(currentTheme), dataAttribute);
|
|
2274
3116
|
}), [ currentTheme, dataAttribute ]),
|
|
2275
|
-
// Handle
|
|
3117
|
+
// Handle persistence
|
|
2276
3118
|
useEffect((() => {
|
|
2277
|
-
enablePersistence && storageAdapter.isAvailable() &&
|
|
2278
|
-
|
|
3119
|
+
enablePersistence && storageAdapter.isAvailable() && ("tokens-theme" === currentTheme ?
|
|
3120
|
+
// Only persist if we have actual tokens to store
|
|
3121
|
+
activeTokens && storageAdapter.setItem(storageKey, JSON.stringify(activeTokens)) :
|
|
3122
|
+
// Persist named theme string
|
|
3123
|
+
storageAdapter.setItem(storageKey, String(currentTheme)));
|
|
3124
|
+
}), [ currentTheme, activeTokens, enablePersistence, storageKey, storageAdapter ]),
|
|
2279
3125
|
// Cleanup: Remove completed promises and abort controllers on unmount
|
|
2280
3126
|
useEffect((() => () => {
|
|
2281
3127
|
// Cancel any in-flight theme loads
|
|
@@ -2414,20 +3260,21 @@ const logger = getLogger(), ThemeProvider = ({children: children, defaultTheme:
|
|
|
2414
3260
|
setIsLoading(!1);
|
|
2415
3261
|
}
|
|
2416
3262
|
}
|
|
2417
|
-
}), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), themeManager = useMemo((() => ({})), []), availableThemes = useMemo((() => Object.entries(themes).map((([name, metadata]) => ({
|
|
3263
|
+
}), [ themes, isThemeLoaded, handleError, basePath, useMinified, cdnPath ]), updateTheme = useCallback((async (sectionOrTokens, values) => setTheme("string" == typeof sectionOrTokens && values ? values : sectionOrTokens)), [ setTheme ]), themeManager = useMemo((() => ({})), []), availableThemes = useMemo((() => Object.entries(themes).map((([name, metadata]) => ({
|
|
2418
3264
|
...metadata,
|
|
2419
3265
|
name: name
|
|
2420
3266
|
})))), [ themes ]), contextValue = useMemo((() => ({
|
|
2421
3267
|
theme: currentTheme,
|
|
2422
3268
|
activeTokens: activeTokens,
|
|
2423
3269
|
setTheme: setTheme,
|
|
3270
|
+
updateTheme: updateTheme,
|
|
2424
3271
|
availableThemes: availableThemes,
|
|
2425
3272
|
isLoading: isLoading,
|
|
2426
3273
|
error: error,
|
|
2427
3274
|
isThemeLoaded: isThemeLoaded,
|
|
2428
3275
|
preloadTheme: preloadTheme,
|
|
2429
3276
|
themeManager: themeManager
|
|
2430
|
-
})), [ currentTheme, activeTokens, setTheme, availableThemes,
|
|
3277
|
+
})), [ currentTheme, activeTokens, setTheme, updateTheme, availableThemes,
|
|
2431
3278
|
// Use memoized value
|
|
2432
3279
|
isLoading, error, isThemeLoaded, preloadTheme, themeManager ]);
|
|
2433
3280
|
// Check if theme is loaded
|
|
@@ -2481,6 +3328,7 @@ function useTheme() {
|
|
|
2481
3328
|
theme: context.theme,
|
|
2482
3329
|
activeTokens: context.activeTokens,
|
|
2483
3330
|
setTheme: context.setTheme,
|
|
3331
|
+
updateTheme: context.updateTheme,
|
|
2484
3332
|
availableThemes: context.availableThemes,
|
|
2485
3333
|
isLoading: context.isLoading,
|
|
2486
3334
|
error: context.error,
|
|
@@ -2716,6 +3564,389 @@ function useThemeTokens() {
|
|
|
2716
3564
|
}
|
|
2717
3565
|
}
|
|
2718
3566
|
|
|
3567
|
+
/**
|
|
3568
|
+
* useThemeSwitcher Hook
|
|
3569
|
+
*
|
|
3570
|
+
* React hook for managing theme switching with persistence and system preference detection.
|
|
3571
|
+
* Provides an easy-to-use API for dark/light mode toggling.
|
|
3572
|
+
*
|
|
3573
|
+
* @example
|
|
3574
|
+
* ```tsx
|
|
3575
|
+
* import { useThemeSwitcher } from '@shohojdhara/atomix/theme';
|
|
3576
|
+
*
|
|
3577
|
+
* function ThemeToggle() {
|
|
3578
|
+
* const { mode, toggle, setMode, isDark } = useThemeSwitcher();
|
|
3579
|
+
*
|
|
3580
|
+
* return (
|
|
3581
|
+
* <button onClick={toggle}>
|
|
3582
|
+
* {isDark ? '☀️ Light' : '🌙 Dark'}
|
|
3583
|
+
* </button>
|
|
3584
|
+
* );
|
|
3585
|
+
* }
|
|
3586
|
+
* ```
|
|
3587
|
+
*/
|
|
3588
|
+
/**
|
|
3589
|
+
* Hook for managing theme switching
|
|
3590
|
+
*
|
|
3591
|
+
* @param options - Configuration options
|
|
3592
|
+
* @returns Theme switcher controls
|
|
3593
|
+
*/ function useThemeSwitcher(options = {}) {
|
|
3594
|
+
const {initialMode: initialMode = "system", syncWithSystem: syncWithSystem = !1, storageKey: storageKey = "atomix-theme", enableTransition: enableTransition = !0, transitionDuration: transitionDuration = 300} = options, [mode, setModeState] = useState((() => {
|
|
3595
|
+
if ("undefined" == typeof window) return initialMode;
|
|
3596
|
+
// Check for saved preference first
|
|
3597
|
+
const saved = getCurrentTheme(storageKey);
|
|
3598
|
+
return saved && "system" !== saved ? saved : "system" === initialMode ? getSystemTheme() : initialMode;
|
|
3599
|
+
// Fall back to initial mode or system
|
|
3600
|
+
}));
|
|
3601
|
+
// State for current mode
|
|
3602
|
+
// Initialize theme on mount
|
|
3603
|
+
return useEffect((() => {
|
|
3604
|
+
"undefined" != typeof window && (
|
|
3605
|
+
// Initialize with proper theme application
|
|
3606
|
+
initializeTheme({
|
|
3607
|
+
storageKey: storageKey,
|
|
3608
|
+
enableTransition: enableTransition,
|
|
3609
|
+
transitionDuration: transitionDuration
|
|
3610
|
+
}),
|
|
3611
|
+
// Update state to match initialized theme
|
|
3612
|
+
setModeState(getCurrentTheme(storageKey)));
|
|
3613
|
+
}), [ storageKey, enableTransition, transitionDuration ]),
|
|
3614
|
+
// Listen for system theme changes if enabled
|
|
3615
|
+
useEffect((() => {
|
|
3616
|
+
if (syncWithSystem) return listenToSystemTheme((newMode => {
|
|
3617
|
+
setModeState(newMode), switchTheme(newMode, {
|
|
3618
|
+
storageKey: storageKey,
|
|
3619
|
+
enableTransition: enableTransition,
|
|
3620
|
+
transitionDuration: transitionDuration
|
|
3621
|
+
});
|
|
3622
|
+
}));
|
|
3623
|
+
}), [ syncWithSystem, storageKey, enableTransition, transitionDuration ]), {
|
|
3624
|
+
mode: mode,
|
|
3625
|
+
isDark: "dark" === mode,
|
|
3626
|
+
isLight: "light" === mode,
|
|
3627
|
+
toggle: useCallback((() => {
|
|
3628
|
+
const newMode = toggleTheme({
|
|
3629
|
+
storageKey: storageKey,
|
|
3630
|
+
enableTransition: enableTransition,
|
|
3631
|
+
transitionDuration: transitionDuration
|
|
3632
|
+
});
|
|
3633
|
+
return setModeState(newMode), newMode;
|
|
3634
|
+
}), [ storageKey, enableTransition, transitionDuration ]),
|
|
3635
|
+
setMode: useCallback((newMode => {
|
|
3636
|
+
switchTheme(newMode, {
|
|
3637
|
+
storageKey: storageKey,
|
|
3638
|
+
enableTransition: enableTransition,
|
|
3639
|
+
transitionDuration: transitionDuration
|
|
3640
|
+
}), setModeState(newMode);
|
|
3641
|
+
}), [ storageKey, enableTransition, transitionDuration ]),
|
|
3642
|
+
resetToSystem: useCallback((() => {
|
|
3643
|
+
const systemMode = getSystemTheme();
|
|
3644
|
+
switchTheme(systemMode, {
|
|
3645
|
+
storageKey: storageKey,
|
|
3646
|
+
enableTransition: enableTransition,
|
|
3647
|
+
transitionDuration: transitionDuration
|
|
3648
|
+
}), setModeState(systemMode);
|
|
3649
|
+
}), [ storageKey, enableTransition, transitionDuration ]),
|
|
3650
|
+
clearPreference: useCallback((() => {
|
|
3651
|
+
"undefined" != typeof window && localStorage.removeItem(storageKey);
|
|
3652
|
+
}), [ storageKey ])
|
|
3653
|
+
};
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
/**
|
|
3657
|
+
* ThemeToggle component with multiple variants
|
|
3658
|
+
*/ const ThemeToggle = ({className: className = "", showLabel: showLabel = !1, lightLabel: lightLabel = "Light", darkLabel: darkLabel = "Dark", iconSize: iconSize = 20, variant: variant = "icon", render: render, ariaLabel: ariaLabel = "Toggle theme", ...hookOptions}) => {
|
|
3659
|
+
const {mode: mode, isDark: isDark, toggle: toggle} = useThemeSwitcher(hookOptions);
|
|
3660
|
+
// Custom render
|
|
3661
|
+
return render ? jsx(Fragment, {
|
|
3662
|
+
children: render({
|
|
3663
|
+
isDark: isDark,
|
|
3664
|
+
toggle: toggle,
|
|
3665
|
+
mode: mode
|
|
3666
|
+
})
|
|
3667
|
+
}) :
|
|
3668
|
+
// Icon-only variant (default)
|
|
3669
|
+
"icon" === variant ? jsx("button", {
|
|
3670
|
+
onClick: toggle,
|
|
3671
|
+
className: `theme-toggle theme-toggle-icon ${className}`,
|
|
3672
|
+
"aria-label": ariaLabel,
|
|
3673
|
+
title: isDark ? darkLabel : lightLabel,
|
|
3674
|
+
style: {
|
|
3675
|
+
background: "none",
|
|
3676
|
+
border: "none",
|
|
3677
|
+
cursor: "pointer",
|
|
3678
|
+
padding: "8px",
|
|
3679
|
+
borderRadius: "50%",
|
|
3680
|
+
display: "flex",
|
|
3681
|
+
alignItems: "center",
|
|
3682
|
+
justifyContent: "center",
|
|
3683
|
+
transition: "all 0.3s ease-in-out"
|
|
3684
|
+
},
|
|
3685
|
+
children: isDark ? jsxs("svg", {
|
|
3686
|
+
width: iconSize,
|
|
3687
|
+
height: iconSize,
|
|
3688
|
+
viewBox: "0 0 24 24",
|
|
3689
|
+
fill: "none",
|
|
3690
|
+
stroke: "currentColor",
|
|
3691
|
+
strokeWidth: "2",
|
|
3692
|
+
strokeLinecap: "round",
|
|
3693
|
+
strokeLinejoin: "round",
|
|
3694
|
+
children: [ jsx("circle", {
|
|
3695
|
+
cx: "12",
|
|
3696
|
+
cy: "12",
|
|
3697
|
+
r: "5"
|
|
3698
|
+
}), jsx("line", {
|
|
3699
|
+
x1: "12",
|
|
3700
|
+
y1: "1",
|
|
3701
|
+
x2: "12",
|
|
3702
|
+
y2: "3"
|
|
3703
|
+
}), jsx("line", {
|
|
3704
|
+
x1: "12",
|
|
3705
|
+
y1: "21",
|
|
3706
|
+
x2: "12",
|
|
3707
|
+
y2: "23"
|
|
3708
|
+
}), jsx("line", {
|
|
3709
|
+
x1: "4.22",
|
|
3710
|
+
y1: "4.22",
|
|
3711
|
+
x2: "5.64",
|
|
3712
|
+
y2: "5.64"
|
|
3713
|
+
}), jsx("line", {
|
|
3714
|
+
x1: "18.36",
|
|
3715
|
+
y1: "18.36",
|
|
3716
|
+
x2: "19.78",
|
|
3717
|
+
y2: "19.78"
|
|
3718
|
+
}), jsx("line", {
|
|
3719
|
+
x1: "1",
|
|
3720
|
+
y1: "12",
|
|
3721
|
+
x2: "3",
|
|
3722
|
+
y2: "12"
|
|
3723
|
+
}), jsx("line", {
|
|
3724
|
+
x1: "21",
|
|
3725
|
+
y1: "12",
|
|
3726
|
+
x2: "23",
|
|
3727
|
+
y2: "12"
|
|
3728
|
+
}), jsx("line", {
|
|
3729
|
+
x1: "4.22",
|
|
3730
|
+
y1: "19.78",
|
|
3731
|
+
x2: "5.64",
|
|
3732
|
+
y2: "18.36"
|
|
3733
|
+
}), jsx("line", {
|
|
3734
|
+
x1: "18.36",
|
|
3735
|
+
y1: "5.64",
|
|
3736
|
+
x2: "19.78",
|
|
3737
|
+
y2: "4.22"
|
|
3738
|
+
}) ]
|
|
3739
|
+
}) : jsx("svg", {
|
|
3740
|
+
width: iconSize,
|
|
3741
|
+
height: iconSize,
|
|
3742
|
+
viewBox: "0 0 24 24",
|
|
3743
|
+
fill: "none",
|
|
3744
|
+
stroke: "currentColor",
|
|
3745
|
+
strokeWidth: "2",
|
|
3746
|
+
strokeLinecap: "round",
|
|
3747
|
+
strokeLinejoin: "round",
|
|
3748
|
+
children: jsx("path", {
|
|
3749
|
+
d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
|
|
3750
|
+
})
|
|
3751
|
+
})
|
|
3752
|
+
}) :
|
|
3753
|
+
// Button variant with text
|
|
3754
|
+
"button" === variant ? jsxs("button", {
|
|
3755
|
+
onClick: toggle,
|
|
3756
|
+
className: `theme-toggle theme-toggle-button ${className}`,
|
|
3757
|
+
"aria-label": ariaLabel,
|
|
3758
|
+
style: {
|
|
3759
|
+
display: "flex",
|
|
3760
|
+
alignItems: "center",
|
|
3761
|
+
gap: "8px",
|
|
3762
|
+
padding: "8px 16px",
|
|
3763
|
+
borderRadius: "8px",
|
|
3764
|
+
border: "1px solid currentColor",
|
|
3765
|
+
background: "transparent",
|
|
3766
|
+
color: "inherit",
|
|
3767
|
+
cursor: "pointer",
|
|
3768
|
+
fontSize: "14px",
|
|
3769
|
+
fontWeight: "500",
|
|
3770
|
+
transition: "all 0.3s ease-in-out"
|
|
3771
|
+
},
|
|
3772
|
+
children: [ isDark ? jsxs("svg", {
|
|
3773
|
+
width: iconSize,
|
|
3774
|
+
height: iconSize,
|
|
3775
|
+
viewBox: "0 0 24 24",
|
|
3776
|
+
fill: "none",
|
|
3777
|
+
stroke: "currentColor",
|
|
3778
|
+
strokeWidth: "2",
|
|
3779
|
+
strokeLinecap: "round",
|
|
3780
|
+
strokeLinejoin: "round",
|
|
3781
|
+
children: [ jsx("circle", {
|
|
3782
|
+
cx: "12",
|
|
3783
|
+
cy: "12",
|
|
3784
|
+
r: "5"
|
|
3785
|
+
}), jsx("line", {
|
|
3786
|
+
x1: "12",
|
|
3787
|
+
y1: "1",
|
|
3788
|
+
x2: "12",
|
|
3789
|
+
y2: "3"
|
|
3790
|
+
}), jsx("line", {
|
|
3791
|
+
x1: "12",
|
|
3792
|
+
y1: "21",
|
|
3793
|
+
x2: "12",
|
|
3794
|
+
y2: "23"
|
|
3795
|
+
}), jsx("line", {
|
|
3796
|
+
x1: "4.22",
|
|
3797
|
+
y1: "4.22",
|
|
3798
|
+
x2: "5.64",
|
|
3799
|
+
y2: "5.64"
|
|
3800
|
+
}), jsx("line", {
|
|
3801
|
+
x1: "18.36",
|
|
3802
|
+
y1: "18.36",
|
|
3803
|
+
x2: "19.78",
|
|
3804
|
+
y2: "19.78"
|
|
3805
|
+
}), jsx("line", {
|
|
3806
|
+
x1: "1",
|
|
3807
|
+
y1: "12",
|
|
3808
|
+
x2: "3",
|
|
3809
|
+
y2: "12"
|
|
3810
|
+
}), jsx("line", {
|
|
3811
|
+
x1: "21",
|
|
3812
|
+
y1: "12",
|
|
3813
|
+
x2: "23",
|
|
3814
|
+
y2: "12"
|
|
3815
|
+
}), jsx("line", {
|
|
3816
|
+
x1: "4.22",
|
|
3817
|
+
y1: "19.78",
|
|
3818
|
+
x2: "5.64",
|
|
3819
|
+
y2: "18.36"
|
|
3820
|
+
}), jsx("line", {
|
|
3821
|
+
x1: "18.36",
|
|
3822
|
+
y1: "5.64",
|
|
3823
|
+
x2: "19.78",
|
|
3824
|
+
y2: "4.22"
|
|
3825
|
+
}) ]
|
|
3826
|
+
}) : jsx("svg", {
|
|
3827
|
+
width: iconSize,
|
|
3828
|
+
height: iconSize,
|
|
3829
|
+
viewBox: "0 0 24 24",
|
|
3830
|
+
fill: "none",
|
|
3831
|
+
stroke: "currentColor",
|
|
3832
|
+
strokeWidth: "2",
|
|
3833
|
+
strokeLinecap: "round",
|
|
3834
|
+
strokeLinejoin: "round",
|
|
3835
|
+
children: jsx("path", {
|
|
3836
|
+
d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
|
|
3837
|
+
})
|
|
3838
|
+
}), showLabel && jsx("span", {
|
|
3839
|
+
children: isDark ? darkLabel : lightLabel
|
|
3840
|
+
}) ]
|
|
3841
|
+
}) :
|
|
3842
|
+
// Switch/toggle variant
|
|
3843
|
+
"switch" === variant ? jsx("div", {
|
|
3844
|
+
className: `theme-toggle theme-toggle-switch ${className}`,
|
|
3845
|
+
role: "button",
|
|
3846
|
+
tabIndex: 0,
|
|
3847
|
+
onClick: toggle,
|
|
3848
|
+
onKeyDown: e => "Enter" === e.key && toggle(),
|
|
3849
|
+
"aria-label": ariaLabel,
|
|
3850
|
+
style: {
|
|
3851
|
+
position: "relative",
|
|
3852
|
+
width: "56px",
|
|
3853
|
+
height: "28px",
|
|
3854
|
+
borderRadius: "14px",
|
|
3855
|
+
background: isDark ? "#4b5563" : "#d1d5db",
|
|
3856
|
+
cursor: "pointer",
|
|
3857
|
+
transition: "background 0.3s ease-in-out",
|
|
3858
|
+
display: "flex",
|
|
3859
|
+
alignItems: "center",
|
|
3860
|
+
padding: "2px"
|
|
3861
|
+
},
|
|
3862
|
+
children: jsx("div", {
|
|
3863
|
+
style: {
|
|
3864
|
+
position: "absolute",
|
|
3865
|
+
left: isDark ? "auto" : "2px",
|
|
3866
|
+
right: isDark ? "2px" : "auto",
|
|
3867
|
+
width: "24px",
|
|
3868
|
+
height: "24px",
|
|
3869
|
+
borderRadius: "50%",
|
|
3870
|
+
background: "white",
|
|
3871
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
|
|
3872
|
+
transition: "all 0.3s ease-in-out",
|
|
3873
|
+
display: "flex",
|
|
3874
|
+
alignItems: "center",
|
|
3875
|
+
justifyContent: "center"
|
|
3876
|
+
},
|
|
3877
|
+
children: isDark ? jsx("svg", {
|
|
3878
|
+
width: "14",
|
|
3879
|
+
height: "14",
|
|
3880
|
+
viewBox: "0 0 24 24",
|
|
3881
|
+
fill: "none",
|
|
3882
|
+
stroke: "#4b5563",
|
|
3883
|
+
strokeWidth: "2",
|
|
3884
|
+
strokeLinecap: "round",
|
|
3885
|
+
strokeLinejoin: "round",
|
|
3886
|
+
children: jsx("path", {
|
|
3887
|
+
d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"
|
|
3888
|
+
})
|
|
3889
|
+
}) : jsxs("svg", {
|
|
3890
|
+
width: "14",
|
|
3891
|
+
height: "14",
|
|
3892
|
+
viewBox: "0 0 24 24",
|
|
3893
|
+
fill: "none",
|
|
3894
|
+
stroke: "#f59e0b",
|
|
3895
|
+
strokeWidth: "2",
|
|
3896
|
+
strokeLinecap: "round",
|
|
3897
|
+
strokeLinejoin: "round",
|
|
3898
|
+
children: [ jsx("circle", {
|
|
3899
|
+
cx: "12",
|
|
3900
|
+
cy: "12",
|
|
3901
|
+
r: "5"
|
|
3902
|
+
}), jsx("line", {
|
|
3903
|
+
x1: "12",
|
|
3904
|
+
y1: "1",
|
|
3905
|
+
x2: "12",
|
|
3906
|
+
y2: "3"
|
|
3907
|
+
}), jsx("line", {
|
|
3908
|
+
x1: "12",
|
|
3909
|
+
y1: "21",
|
|
3910
|
+
x2: "12",
|
|
3911
|
+
y2: "23"
|
|
3912
|
+
}), jsx("line", {
|
|
3913
|
+
x1: "4.22",
|
|
3914
|
+
y1: "4.22",
|
|
3915
|
+
x2: "5.64",
|
|
3916
|
+
y2: "5.64"
|
|
3917
|
+
}), jsx("line", {
|
|
3918
|
+
x1: "18.36",
|
|
3919
|
+
y1: "18.36",
|
|
3920
|
+
x2: "19.78",
|
|
3921
|
+
y2: "19.78"
|
|
3922
|
+
}), jsx("line", {
|
|
3923
|
+
x1: "1",
|
|
3924
|
+
y1: "12",
|
|
3925
|
+
x2: "3",
|
|
3926
|
+
y2: "12"
|
|
3927
|
+
}), jsx("line", {
|
|
3928
|
+
x1: "21",
|
|
3929
|
+
y1: "12",
|
|
3930
|
+
x2: "23",
|
|
3931
|
+
y2: "12"
|
|
3932
|
+
}), jsx("line", {
|
|
3933
|
+
x1: "4.22",
|
|
3934
|
+
y1: "19.78",
|
|
3935
|
+
x2: "5.64",
|
|
3936
|
+
y2: "18.36"
|
|
3937
|
+
}), jsx("line", {
|
|
3938
|
+
x1: "18.36",
|
|
3939
|
+
y1: "5.64",
|
|
3940
|
+
x2: "19.78",
|
|
3941
|
+
y2: "4.22"
|
|
3942
|
+
}) ]
|
|
3943
|
+
})
|
|
3944
|
+
})
|
|
3945
|
+
}) : null;
|
|
3946
|
+
};
|
|
3947
|
+
|
|
3948
|
+
ThemeToggle.displayName = "ThemeToggle";
|
|
3949
|
+
|
|
2719
3950
|
/**
|
|
2720
3951
|
* Theme Applicator
|
|
2721
3952
|
*
|
|
@@ -2728,7 +3959,8 @@ function useThemeTokens() {
|
|
|
2728
3959
|
* Theme applicator class for runtime theme application
|
|
2729
3960
|
*
|
|
2730
3961
|
* Uses the unified theme system for efficient CSS variable generation and injection.
|
|
2731
|
-
*/
|
|
3962
|
+
*/
|
|
3963
|
+
class ThemeApplicator {
|
|
2732
3964
|
constructor(root = document.documentElement) {
|
|
2733
3965
|
this.styleId = "atomix-theme-applicator", this.root = root;
|
|
2734
3966
|
}
|
|
@@ -4268,65 +5500,7 @@ class ThemeValidator {
|
|
|
4268
5500
|
children: "\n .atomix-theme-comparator {\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n background: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n padding: 24px;\n }\n\n .comparator-header {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n gap: 24px;\n align-items: center;\n margin-bottom: 24px;\n padding-bottom: 24px;\n border-bottom: 2px solid #e0e0e0;\n }\n\n .theme-column {\n text-align: center;\n }\n\n .theme-column h3 {\n margin: 0;\n font-size: 20px;\n color: #333;\n }\n\n .version {\n display: inline-block;\n margin-top: 4px;\n padding: 2px 8px;\n background: #e3f2fd;\n color: #1976d2;\n border-radius: 4px;\n font-size: 12px;\n }\n\n .vs-divider {\n font-weight: bold;\n font-size: 24px;\n color: #666;\n }\n\n .filter-controls {\n display: flex;\n gap: 16px;\n align-items: center;\n margin-bottom: 24px;\n padding: 16px;\n background: #f5f5f5;\n border-radius: 8px;\n flex-wrap: wrap;\n }\n\n .filter-group {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .filter-group label {\n font-size: 14px;\n font-weight: 500;\n color: #666;\n }\n\n .search-input {\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 14px;\n width: 250px;\n }\n\n .search-input:focus {\n outline: none;\n border-color: #2196f3;\n }\n\n .clear-search {\n background: #f44336;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .clear-search:hover {\n background: #d32f2f;\n }\n\n .filter-select {\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n }\n\n .clear-filters {\n padding: 8px 16px;\n background: #666;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n }\n\n .clear-filters:hover {\n background: #555;\n }\n\n .search-highlight {\n background: #fff59d;\n padding: 2px 4px;\n border-radius: 2px;\n }\n\n .comparator-stats {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .stat {\n text-align: center;\n padding: 16px;\n background: #f5f5f5;\n border-radius: 8px;\n }\n\n .stat-added {\n border-left: 4px solid #4caf50;\n }\n\n .stat-removed {\n border-left: 4px solid #f44336;\n }\n\n .stat-changed {\n border-left: 4px solid #ff9800;\n }\n\n .stat-filtered {\n border-left: 4px solid #2196f3;\n }\n\n .stat-value {\n display: block;\n font-size: 32px;\n font-weight: bold;\n color: #2196f3;\n }\n\n .stat-label {\n display: block;\n font-size: 12px;\n color: #666;\n margin-top: 4px;\n }\n\n .no-differences {\n text-align: center;\n padding: 48px;\n font-size: 18px;\n color: #4caf50;\n background: #e8f5e9;\n border-radius: 8px;\n }\n\n .differences-list h4 {\n margin: 0 0 16px 0;\n font-size: 18px;\n color: #333;\n }\n\n .difference-item {\n margin-bottom: 16px;\n border: 2px solid;\n border-radius: 8px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .difference-added {\n border-color: #4caf50;\n }\n\n .difference-removed {\n border-color: #f44336;\n }\n\n .difference-changed {\n border-color: #ff9800;\n }\n\n .difference-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n background: rgba(0, 0, 0, 0.05);\n }\n\n .difference-type {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: bold;\n color: white;\n text-transform: uppercase;\n }\n\n .difference-path {\n font-family: 'Monaco', 'Menlo', monospace;\n font-size: 13px;\n color: #1976d2;\n flex: 1;\n }\n\n .difference-category {\n font-size: 11px;\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 4px;\n color: #666;\n text-transform: uppercase;\n }\n\n .difference-values {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n gap: 16px;\n padding: 16px;\n align-items: start;\n }\n\n .value-column {\n min-width: 0;\n }\n\n .value-column.value-empty {\n opacity: 0.5;\n }\n\n .value-label {\n font-size: 12px;\n font-weight: bold;\n color: #666;\n margin-bottom: 8px;\n }\n\n .value-content {\n margin: 0;\n padding: 12px;\n background: rgba(255, 255, 255, 0.7);\n border-radius: 4px;\n font-family: 'Monaco', 'Menlo', monospace;\n font-size: 12px;\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-all;\n }\n\n .value-empty {\n color: #999;\n font-style: italic;\n }\n\n .value-divider {\n font-size: 20px;\n color: #666;\n align-self: center;\n }\n "
|
|
4269
5501
|
}) ]
|
|
4270
5502
|
});
|
|
4271
|
-
}
|
|
4272
|
-
|
|
4273
|
-
/**
|
|
4274
|
-
* Theme Comparator Component
|
|
4275
|
-
*
|
|
4276
|
-
* Compares two themes and highlights differences
|
|
4277
|
-
*/ var aCallable = aCallable$3, toObject = toObject$2, IndexedObject = indexedObject, lengthOfArrayLike = lengthOfArrayLike$2, $TypeError = TypeError, REDUCE_EMPTY = "Reduce of empty array with no initial value", createMethod = function(IS_RIGHT) {
|
|
4278
|
-
return function(that, callbackfn, argumentsLength, memo) {
|
|
4279
|
-
var O = toObject(that), self = IndexedObject(O), length = lengthOfArrayLike(O);
|
|
4280
|
-
if (aCallable(callbackfn), 0 === length && argumentsLength < 2) throw new $TypeError(REDUCE_EMPTY);
|
|
4281
|
-
var index = IS_RIGHT ? length - 1 : 0, i = IS_RIGHT ? -1 : 1;
|
|
4282
|
-
if (argumentsLength < 2) for (;;) {
|
|
4283
|
-
if (index in self) {
|
|
4284
|
-
memo = self[index], index += i;
|
|
4285
|
-
break;
|
|
4286
|
-
}
|
|
4287
|
-
if (index += i, IS_RIGHT ? index < 0 : length <= index) throw new $TypeError(REDUCE_EMPTY);
|
|
4288
|
-
}
|
|
4289
|
-
for (;IS_RIGHT ? index >= 0 : length > index; index += i) index in self && (memo = callbackfn(memo, self[index], index, O));
|
|
4290
|
-
return memo;
|
|
4291
|
-
};
|
|
4292
|
-
}, arrayReduce = {
|
|
4293
|
-
// `Array.prototype.reduce` method
|
|
4294
|
-
// https://tc39.es/ecma262/#sec-array.prototype.reduce
|
|
4295
|
-
left: createMethod(!1),
|
|
4296
|
-
// `Array.prototype.reduceRight` method
|
|
4297
|
-
// https://tc39.es/ecma262/#sec-array.prototype.reduceright
|
|
4298
|
-
right: createMethod(!0)
|
|
4299
|
-
}, fails = fails$9, globalThis$1 = globalThis_1, userAgent = environmentUserAgent, classof = classofRaw$2, userAgentStartsWith = function(string) {
|
|
4300
|
-
return userAgent.slice(0, string.length) === string;
|
|
4301
|
-
}, environment = userAgentStartsWith("Bun/") ? "BUN" : userAgentStartsWith("Cloudflare-Workers") ? "CLOUDFLARE" : userAgentStartsWith("Deno/") ? "DENO" : userAgentStartsWith("Node.js/") ? "NODE" : globalThis$1.Bun && "string" == typeof Bun.version ? "BUN" : globalThis$1.Deno && "object" == typeof Deno.version ? "DENO" : "process" === classof(globalThis$1.process) ? "NODE" : globalThis$1.window && globalThis$1.document ? "BROWSER" : "REST", $reduce = arrayReduce.left;
|
|
4302
|
-
|
|
4303
|
-
// `Array.prototype.reduce` method
|
|
4304
|
-
// https://tc39.es/ecma262/#sec-array.prototype.reduce
|
|
4305
|
-
_export({
|
|
4306
|
-
target: "Array",
|
|
4307
|
-
proto: !0,
|
|
4308
|
-
forced: !("NODE" === environment) && environmentV8Version > 79 && environmentV8Version < 83 || !function(METHOD_NAME, argument) {
|
|
4309
|
-
var method = [][METHOD_NAME];
|
|
4310
|
-
return !!method && fails((function() {
|
|
4311
|
-
// eslint-disable-next-line no-useless-call -- required for testing
|
|
4312
|
-
method.call(null, argument || function() {
|
|
4313
|
-
return 1;
|
|
4314
|
-
}, 1);
|
|
4315
|
-
}));
|
|
4316
|
-
}("reduce")
|
|
4317
|
-
}, {
|
|
4318
|
-
reduce: function(callbackfn /* , initialValue */) {
|
|
4319
|
-
var length = arguments.length;
|
|
4320
|
-
return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : void 0);
|
|
4321
|
-
}
|
|
4322
|
-
});
|
|
4323
|
-
|
|
4324
|
-
var reduce$3 = getBuiltInPrototypeMethod$3("Array", "reduce"), isPrototypeOf = objectIsPrototypeOf, method = reduce$3, ArrayPrototype = Array.prototype;
|
|
4325
|
-
|
|
4326
|
-
const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
4327
|
-
var own = it.reduce;
|
|
4328
|
-
return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.reduce ? method : own;
|
|
4329
|
-
})), DEFAULT_PALETTE = {
|
|
5503
|
+
}, DEFAULT_PALETTE = {
|
|
4330
5504
|
primary: {
|
|
4331
5505
|
main: "#7c3aed",
|
|
4332
5506
|
// Primary-6
|
|
@@ -4511,9 +5685,11 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
4511
5685
|
pill: "50rem"
|
|
4512
5686
|
};
|
|
4513
5687
|
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
5688
|
+
/**
|
|
5689
|
+
* Theme Comparator Component
|
|
5690
|
+
*
|
|
5691
|
+
* Compares two themes and highlights differences
|
|
5692
|
+
*/
|
|
4517
5693
|
// ============================================================================
|
|
4518
5694
|
// Helper Functions
|
|
4519
5695
|
// ============================================================================
|
|
@@ -4523,13 +5699,13 @@ const _reduceInstanceProperty = getDefaultExportFromCjs((function(it) {
|
|
|
4523
5699
|
function createPaletteColor(color) {
|
|
4524
5700
|
return "string" == typeof color ? {
|
|
4525
5701
|
main: color,
|
|
4526
|
-
light: lighten(color),
|
|
4527
|
-
dark: darken(color),
|
|
5702
|
+
light: lighten(color, .15),
|
|
5703
|
+
dark: darken(color, .15),
|
|
4528
5704
|
contrastText: getContrastText(color)
|
|
4529
5705
|
} : {
|
|
4530
5706
|
main: color.main || "#000000",
|
|
4531
|
-
light: color.light || lighten(color.main || "#000000"),
|
|
4532
|
-
dark: color.dark || darken(color.main || "#000000"),
|
|
5707
|
+
light: color.light || lighten(color.main || "#000000", .15),
|
|
5708
|
+
dark: color.dark || darken(color.main || "#000000", .15),
|
|
4533
5709
|
contrastText: color.contrastText || getContrastText(color.main || "#000000")
|
|
4534
5710
|
};
|
|
4535
5711
|
}
|
|
@@ -4564,23 +5740,19 @@ function createThemeObject(...options) {
|
|
|
4564
5740
|
},
|
|
4565
5741
|
background: {
|
|
4566
5742
|
default: mergedOptions.palette?.background?.default || DEFAULT_PALETTE.background.default,
|
|
5743
|
+
paper: mergedOptions.palette?.background?.paper || DEFAULT_PALETTE.background.paper,
|
|
4567
5744
|
subtle: mergedOptions.palette?.background?.subtle || DEFAULT_PALETTE.background.subtle
|
|
4568
5745
|
},
|
|
4569
5746
|
text: {
|
|
4570
5747
|
primary: mergedOptions.palette?.text?.primary || DEFAULT_PALETTE.text.primary,
|
|
4571
5748
|
secondary: mergedOptions.palette?.text?.secondary || DEFAULT_PALETTE.text.secondary,
|
|
4572
5749
|
disabled: mergedOptions.palette?.text?.disabled || DEFAULT_PALETTE.text.disabled
|
|
4573
|
-
}
|
|
5750
|
+
},
|
|
5751
|
+
// Spread other palette properties
|
|
5752
|
+
...mergedOptions.palette
|
|
4574
5753
|
}, typography = deepMerge({
|
|
4575
5754
|
...DEFAULT_TYPOGRAPHY
|
|
4576
|
-
}, mergedOptions.typography || {}), spacing =
|
|
4577
|
-
// If it's already a function, return it
|
|
4578
|
-
return "function" == typeof spacingInput ? spacingInput :
|
|
4579
|
-
// If it's a number, create a function that multiplies by that number
|
|
4580
|
-
"number" == typeof spacingInput ? (...values) => 0 === values.length ? "0px" : values.map((value => value * spacingInput + "px")).join(" ") :
|
|
4581
|
-
// If it's an array, use it as a scale
|
|
4582
|
-
Array.isArray(spacingInput) ? (...values) => 0 === values.length ? "0px" : values.map((value => `${spacingInput[value] || value}px`)).join(" ") : (...values) => 0 === values.length ? "0px" : values.map((value => 4 * value + "px")).join(" ");
|
|
4583
|
-
}(mergedOptions.spacing), breakpoints = function(breakpointsInput) {
|
|
5755
|
+
}, mergedOptions.typography || {}), spacing = createSpacing(mergedOptions.spacing), breakpoints = function(breakpointsInput) {
|
|
4584
5756
|
const values = {
|
|
4585
5757
|
xs: 0,
|
|
4586
5758
|
sm: 576,
|
|
@@ -5403,24 +6575,6 @@ const ThemeLiveEditor = ({initialTheme: initialTheme, onChange: onChange, classN
|
|
|
5403
6575
|
/**
|
|
5404
6576
|
* Design Tokens Customizer Component
|
|
5405
6577
|
*/
|
|
5406
|
-
/**
|
|
5407
|
-
* Theme Adapter
|
|
5408
|
-
*
|
|
5409
|
-
* Converts between Theme objects and DesignTokens.
|
|
5410
|
-
*/
|
|
5411
|
-
/**
|
|
5412
|
-
* Convert DesignTokens to Theme-compatible CSS variables
|
|
5413
|
-
*
|
|
5414
|
-
* @param tokens - DesignTokens object
|
|
5415
|
-
* @returns CSS variables object compatible with Theme.cssVars
|
|
5416
|
-
*/
|
|
5417
|
-
function designTokensToCSSVars(tokens) {
|
|
5418
|
-
const cssVars = {};
|
|
5419
|
-
return Object.entries(tokens).forEach((([key, value]) => {
|
|
5420
|
-
void 0 !== value && (cssVars[`--atomix-${key}`] = String(value));
|
|
5421
|
-
})), cssVars;
|
|
5422
|
-
}
|
|
5423
|
-
|
|
5424
6578
|
/**
|
|
5425
6579
|
* Map SCSS tokens to CSS custom properties
|
|
5426
6580
|
*
|
|
@@ -5428,7 +6582,8 @@ function designTokensToCSSVars(tokens) {
|
|
|
5428
6582
|
* const tokens = { '$primary-color': '#7AFFD7', '$spacing-md': '16px' }
|
|
5429
6583
|
* const vars = mapSCSSTokensToCSSVars(tokens)
|
|
5430
6584
|
* // Returns: { '--primary-color': '#7AFFD7', '--spacing-md': '16px' }
|
|
5431
|
-
*/
|
|
6585
|
+
*/
|
|
6586
|
+
function mapSCSSTokensToCSSVars(tokens, options = {}) {
|
|
5432
6587
|
const vars = {}, {prefix: prefix = "atomix", separator: separator = "-"} = options;
|
|
5433
6588
|
return Object.entries(tokens).forEach((([key, value]) => {
|
|
5434
6589
|
// Remove $ prefix from SCSS variables
|
|
@@ -5541,6 +6696,335 @@ function designTokensToCSSVars(tokens) {
|
|
|
5541
6696
|
// Check if keys look like DesignTokens (kebab-case, no nesting)
|
|
5542
6697
|
}
|
|
5543
6698
|
|
|
6699
|
+
/**
|
|
6700
|
+
* Performance monitor class
|
|
6701
|
+
*/ class PerformanceMonitor {
|
|
6702
|
+
/**
|
|
6703
|
+
* Create a new performance monitor
|
|
6704
|
+
*
|
|
6705
|
+
* @param config Configuration options
|
|
6706
|
+
*/
|
|
6707
|
+
constructor(config) {
|
|
6708
|
+
this.frameCount = 0, this.lastSampleTime = 0, this.lastFpsUpdate = 0, this.frameTimes = [],
|
|
6709
|
+
this.animationFrameId = null, this.isActive = !1, this.startTime = 0, this.config = {
|
|
6710
|
+
fpsTarget: config?.fpsTarget ?? 60,
|
|
6711
|
+
sampleInterval: config?.sampleInterval ?? 500,
|
|
6712
|
+
onUpdate: config?.onUpdate ?? (() => {}),
|
|
6713
|
+
onDegraded: config?.onDegraded ?? (() => {}),
|
|
6714
|
+
enableMemoryMonitoring: config?.enableMemoryMonitoring ?? ("undefined" != typeof window && window.performance && window.performance.memory)
|
|
6715
|
+
};
|
|
6716
|
+
}
|
|
6717
|
+
/**
|
|
6718
|
+
* Start monitoring performance
|
|
6719
|
+
*/ start() {
|
|
6720
|
+
this.isActive || (this.isActive = !0, this.frameCount = 0, this.lastSampleTime = performance.now(),
|
|
6721
|
+
this.lastFpsUpdate = this.lastSampleTime, this.frameTimes = [], this.startTime = this.lastSampleTime,
|
|
6722
|
+
this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this)));
|
|
6723
|
+
}
|
|
6724
|
+
/**
|
|
6725
|
+
* Stop monitoring performance
|
|
6726
|
+
*/ stop() {
|
|
6727
|
+
this.animationFrameId && (cancelAnimationFrame(this.animationFrameId), this.animationFrameId = null),
|
|
6728
|
+
this.isActive = !1;
|
|
6729
|
+
}
|
|
6730
|
+
/**
|
|
6731
|
+
* Get current performance metrics
|
|
6732
|
+
*/ getMetrics() {
|
|
6733
|
+
var _context;
|
|
6734
|
+
const now = performance.now(), elapsed = now - this.lastFpsUpdate, fps = elapsed > 0 ? Math.round(this.frameCount / elapsed * 1e3) : 0, avgFrameTime = this.frameTimes.length > 0 ? _reduceInstanceProperty(_context = this.frameTimes).call(_context, ((a, b) => a + b), 0) / this.frameTimes.length : 0, peakFrameTime = this.frameTimes.length > 0 ? Math.max(...this.frameTimes) : 0;
|
|
6735
|
+
// Get memory stats if available
|
|
6736
|
+
let memory;
|
|
6737
|
+
if (this.config.enableMemoryMonitoring) {
|
|
6738
|
+
const perf = window.performance;
|
|
6739
|
+
perf && perf.memory && (memory = {
|
|
6740
|
+
usedJSHeapSize: perf.memory.usedJSHeapSize,
|
|
6741
|
+
totalJSHeapSize: perf.memory.totalJSHeapSize,
|
|
6742
|
+
jsHeapSizeLimit: perf.memory.jsHeapSizeLimit
|
|
6743
|
+
});
|
|
6744
|
+
}
|
|
6745
|
+
return {
|
|
6746
|
+
fps: fps,
|
|
6747
|
+
frameTime: avgFrameTime,
|
|
6748
|
+
peakFrameTime: peakFrameTime,
|
|
6749
|
+
memory: memory,
|
|
6750
|
+
timestamp: now,
|
|
6751
|
+
isDegraded: fps < .7 * this.config.fpsTarget
|
|
6752
|
+
};
|
|
6753
|
+
}
|
|
6754
|
+
/**
|
|
6755
|
+
* Get the current FPS
|
|
6756
|
+
*/ getFps() {
|
|
6757
|
+
return this.getMetrics().fps;
|
|
6758
|
+
}
|
|
6759
|
+
/**
|
|
6760
|
+
* Check if performance is degraded
|
|
6761
|
+
*/ isPerformanceDegraded() {
|
|
6762
|
+
return this.getMetrics().isDegraded;
|
|
6763
|
+
}
|
|
6764
|
+
/**
|
|
6765
|
+
* Private method called on each animation frame
|
|
6766
|
+
*/ onFrame(timestamp) {
|
|
6767
|
+
if (!this.isActive) return;
|
|
6768
|
+
// Calculate frame time
|
|
6769
|
+
const frameTime = timestamp - this.lastSampleTime;
|
|
6770
|
+
// Check if we need to update metrics
|
|
6771
|
+
if (this.frameTimes.push(frameTime),
|
|
6772
|
+
// Keep only the last 60 frame times for averaging
|
|
6773
|
+
this.frameTimes.length > 60 && this.frameTimes.shift(), this.frameCount++, this.lastSampleTime = timestamp,
|
|
6774
|
+
timestamp - this.lastFpsUpdate >= this.config.sampleInterval) {
|
|
6775
|
+
const metrics = this.getMetrics();
|
|
6776
|
+
// Call update callback
|
|
6777
|
+
this.config.onUpdate(metrics),
|
|
6778
|
+
// Check for degradation
|
|
6779
|
+
metrics.isDegraded && this.config.onDegraded(metrics),
|
|
6780
|
+
// Reset counters
|
|
6781
|
+
this.frameCount = 0, this.lastFpsUpdate = timestamp;
|
|
6782
|
+
}
|
|
6783
|
+
this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this));
|
|
6784
|
+
}
|
|
6785
|
+
/**
|
|
6786
|
+
* Run a performance test for a specific function
|
|
6787
|
+
*
|
|
6788
|
+
* @param fn Function to test
|
|
6789
|
+
* @param iterations Number of iterations (default: 100)
|
|
6790
|
+
* @returns Average execution time in ms
|
|
6791
|
+
*/ async testFunctionPerformance(fn, iterations = 100) {
|
|
6792
|
+
const times = [];
|
|
6793
|
+
for (let i = 0; i < iterations; i++) {
|
|
6794
|
+
const start = performance.now();
|
|
6795
|
+
fn();
|
|
6796
|
+
const end = performance.now();
|
|
6797
|
+
times.push(end - start);
|
|
6798
|
+
}
|
|
6799
|
+
return _reduceInstanceProperty(times).call(times, ((a, b) => a + b), 0) / times.length;
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6802
|
+
|
|
6803
|
+
/**
|
|
6804
|
+
* Create a performance monitor instance
|
|
6805
|
+
*
|
|
6806
|
+
* @param config Configuration options
|
|
6807
|
+
* @returns PerformanceMonitor instance
|
|
6808
|
+
*
|
|
6809
|
+
* @example
|
|
6810
|
+
* ```typescript
|
|
6811
|
+
* import { createPerformanceMonitor } from '@shohojdhara/atomix/theme';
|
|
6812
|
+
*
|
|
6813
|
+
* const monitor = createPerformanceMonitor({
|
|
6814
|
+
* fpsTarget: 60,
|
|
6815
|
+
* onUpdate: (metrics) => console.log('FPS:', metrics.fps),
|
|
6816
|
+
* onDegraded: (metrics) => console.warn('Performance degraded!', metrics),
|
|
6817
|
+
* });
|
|
6818
|
+
*
|
|
6819
|
+
* monitor.start();
|
|
6820
|
+
*
|
|
6821
|
+
* // Later...
|
|
6822
|
+
* monitor.stop();
|
|
6823
|
+
* ```
|
|
6824
|
+
*/ function createPerformanceMonitor(config) {
|
|
6825
|
+
return new PerformanceMonitor(config);
|
|
6826
|
+
}
|
|
6827
|
+
|
|
6828
|
+
/**
|
|
6829
|
+
* Hook for React components to monitor performance
|
|
6830
|
+
*
|
|
6831
|
+
* @param config Configuration options
|
|
6832
|
+
* @returns Performance metrics and monitor controls
|
|
6833
|
+
*
|
|
6834
|
+
* @example
|
|
6835
|
+
* ```typescript
|
|
6836
|
+
* import { usePerformanceMonitor } from '@shohojdhara/atomix/theme';
|
|
6837
|
+
*
|
|
6838
|
+
* function MyComponent() {
|
|
6839
|
+
* const { metrics, start, stop } = usePerformanceMonitor({ fpsTarget: 60 });
|
|
6840
|
+
*
|
|
6841
|
+
* useEffect(() => {
|
|
6842
|
+
* start();
|
|
6843
|
+
* return () => stop();
|
|
6844
|
+
* }, []);
|
|
6845
|
+
*
|
|
6846
|
+
* return <div>FPS: {metrics.fps}</div>;
|
|
6847
|
+
* }
|
|
6848
|
+
* ```
|
|
6849
|
+
*/ function usePerformanceMonitor(config) {
|
|
6850
|
+
const [monitor] = React.useState((() => createPerformanceMonitor(config))), [metrics, setMetrics] = React.useState((() => "undefined" != typeof window ? monitor.getMetrics() : {
|
|
6851
|
+
fps: 0,
|
|
6852
|
+
frameTime: 0,
|
|
6853
|
+
peakFrameTime: 0,
|
|
6854
|
+
timestamp: 0,
|
|
6855
|
+
isDegraded: !1
|
|
6856
|
+
})), start = React.useCallback((() => {
|
|
6857
|
+
"undefined" != typeof window && monitor.start();
|
|
6858
|
+
}), [ monitor ]), stop = React.useCallback((() => {
|
|
6859
|
+
"undefined" != typeof window && monitor.stop();
|
|
6860
|
+
}), [ monitor ]);
|
|
6861
|
+
return React.useEffect((() => {
|
|
6862
|
+
if ("undefined" == typeof window) return;
|
|
6863
|
+
// Update metrics when monitor callbacks fire
|
|
6864
|
+
const originalOnUpdate = config?.onUpdate;
|
|
6865
|
+
return monitor.config.onUpdate = newMetrics => {
|
|
6866
|
+
setMetrics(newMetrics), originalOnUpdate?.(newMetrics);
|
|
6867
|
+
}, () => {
|
|
6868
|
+
monitor.stop();
|
|
6869
|
+
};
|
|
6870
|
+
}), [ monitor, config?.onUpdate ]), {
|
|
6871
|
+
metrics: metrics,
|
|
6872
|
+
start: start,
|
|
6873
|
+
stop: stop
|
|
6874
|
+
};
|
|
6875
|
+
}
|
|
6876
|
+
|
|
6877
|
+
/**
|
|
6878
|
+
* Responsive Utility for Atomix Theme System
|
|
6879
|
+
*
|
|
6880
|
+
* Provides responsive breakpoint detection and device-aware parameter scaling
|
|
6881
|
+
* based on configuration from the advanced optimization features.
|
|
6882
|
+
*/
|
|
6883
|
+
/**
|
|
6884
|
+
* Responsive utility class
|
|
6885
|
+
*/ class ResponsiveUtil {
|
|
6886
|
+
constructor(config) {
|
|
6887
|
+
this.currentDevice = "desktop", // Default
|
|
6888
|
+
this.resizeHandler = null, this.observer = null, this.config = config, this.currentDevice = this.getCurrentDeviceType(),
|
|
6889
|
+
// Set up resize listener
|
|
6890
|
+
this.setupResizeListener();
|
|
6891
|
+
}
|
|
6892
|
+
/**
|
|
6893
|
+
* Get the current device type based on viewport width
|
|
6894
|
+
*/ getCurrentDeviceType() {
|
|
6895
|
+
if ("undefined" == typeof window) return "desktop";
|
|
6896
|
+
// SSR fallback
|
|
6897
|
+
const width = window.innerWidth;
|
|
6898
|
+
// Parse breakpoint values to numbers
|
|
6899
|
+
this.parsePxValue(this.config.breakpoints.mobile);
|
|
6900
|
+
const tabletWidth = this.parsePxValue(this.config.breakpoints.tablet), desktopWidth = this.parsePxValue(this.config.breakpoints.desktop), wideWidth = this.parsePxValue(this.config.breakpoints.wide);
|
|
6901
|
+
return width < tabletWidth ? "mobile" : width < desktopWidth ? "tablet" : width < wideWidth ? "desktop" : "wide";
|
|
6902
|
+
}
|
|
6903
|
+
/**
|
|
6904
|
+
* Get the scaling factor for the current device
|
|
6905
|
+
*/ getCurrentScalingFactor() {
|
|
6906
|
+
// 'wide' devices use the same scaling as 'desktop'
|
|
6907
|
+
const scalingKey = "wide" === this.currentDevice ? "desktop" : this.currentDevice;
|
|
6908
|
+
return this.config.deviceScaling[scalingKey] || 1;
|
|
6909
|
+
}
|
|
6910
|
+
/**
|
|
6911
|
+
* Scale a value based on the current device's scaling factor
|
|
6912
|
+
*/ scaleValue(value) {
|
|
6913
|
+
return value * this.getCurrentScalingFactor();
|
|
6914
|
+
}
|
|
6915
|
+
/**
|
|
6916
|
+
* Check if the current device matches a specific type
|
|
6917
|
+
*/ isDevice(device) {
|
|
6918
|
+
return this.currentDevice === device;
|
|
6919
|
+
}
|
|
6920
|
+
/**
|
|
6921
|
+
* Check if the current device is mobile or smaller
|
|
6922
|
+
*/ isMobileOrSmaller() {
|
|
6923
|
+
return "mobile" === this.currentDevice;
|
|
6924
|
+
}
|
|
6925
|
+
/**
|
|
6926
|
+
* Check if the current device is tablet or smaller
|
|
6927
|
+
*/ isTabletOrSmaller() {
|
|
6928
|
+
return "mobile" === this.currentDevice || "tablet" === this.currentDevice;
|
|
6929
|
+
}
|
|
6930
|
+
/**
|
|
6931
|
+
* Check if the current device is desktop or larger
|
|
6932
|
+
*/ isDesktopOrLarger() {
|
|
6933
|
+
return "desktop" === this.currentDevice || "wide" === this.currentDevice;
|
|
6934
|
+
}
|
|
6935
|
+
/**
|
|
6936
|
+
* Update the responsive configuration
|
|
6937
|
+
*/ updateConfig(config) {
|
|
6938
|
+
this.config = config, this.currentDevice = this.getCurrentDeviceType();
|
|
6939
|
+
}
|
|
6940
|
+
/**
|
|
6941
|
+
* Destroy the responsive utility and clean up listeners
|
|
6942
|
+
*/ destroy() {
|
|
6943
|
+
this.resizeHandler && (window.removeEventListener("resize", this.resizeHandler),
|
|
6944
|
+
this.resizeHandler = null), this.observer && (this.observer.disconnect(), this.observer = null);
|
|
6945
|
+
}
|
|
6946
|
+
/**
|
|
6947
|
+
* Parse a CSS value to pixels
|
|
6948
|
+
*/ parsePxValue(value) {
|
|
6949
|
+
return value.endsWith("px") ? parseFloat(value.slice(0, -2)) :
|
|
6950
|
+
// For other units, we'll use a rough conversion assuming 16px base
|
|
6951
|
+
value.endsWith("rem") ? 16 * parseFloat(value.slice(0, -3)) : value.endsWith("em") ? 16 * parseFloat(value.slice(0, -2)) : parseFloat(value) || 0;
|
|
6952
|
+
}
|
|
6953
|
+
/**
|
|
6954
|
+
* Set up the resize listener
|
|
6955
|
+
*/ setupResizeListener() {
|
|
6956
|
+
if ("undefined" == typeof window) return;
|
|
6957
|
+
// Throttled resize handler
|
|
6958
|
+
let resizeTimeout = null;
|
|
6959
|
+
const handleResize = () => {
|
|
6960
|
+
resizeTimeout && window.clearTimeout(resizeTimeout), resizeTimeout = window.setTimeout((() => {
|
|
6961
|
+
const newDeviceType = this.getCurrentDeviceType();
|
|
6962
|
+
newDeviceType !== this.currentDevice && (this.currentDevice = newDeviceType);
|
|
6963
|
+
}), 150);
|
|
6964
|
+
} // Throttle to 150ms
|
|
6965
|
+
;
|
|
6966
|
+
this.resizeHandler = handleResize, window.addEventListener("resize", handleResize),
|
|
6967
|
+
// Also observe the document body for size changes
|
|
6968
|
+
"undefined" != typeof ResizeObserver && (this.observer = new ResizeObserver(handleResize),
|
|
6969
|
+
this.observer.observe(document.body));
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
|
|
6973
|
+
/**
|
|
6974
|
+
* Create a responsive utility instance
|
|
6975
|
+
*
|
|
6976
|
+
* @param config Responsive configuration
|
|
6977
|
+
* @returns ResponsiveUtil instance
|
|
6978
|
+
*/ function createResponsiveUtil(config) {
|
|
6979
|
+
return new ResponsiveUtil(config);
|
|
6980
|
+
}
|
|
6981
|
+
|
|
6982
|
+
/**
|
|
6983
|
+
* Hook for React components to use responsive features
|
|
6984
|
+
*
|
|
6985
|
+
* @param config Responsive configuration
|
|
6986
|
+
* @returns Current device type and utility functions
|
|
6987
|
+
*/ function useResponsive(config) {
|
|
6988
|
+
const [util] = React.useState((() => createResponsiveUtil(config))), [deviceType, setDeviceType] = React.useState((() => "undefined" != typeof window ? util.getCurrentDeviceType() : "desktop"));
|
|
6989
|
+
return React.useEffect((() => {
|
|
6990
|
+
if ("undefined" == typeof window) return;
|
|
6991
|
+
const handleResize = () => {
|
|
6992
|
+
const newDeviceType = util.getCurrentDeviceType();
|
|
6993
|
+
newDeviceType !== deviceType && setDeviceType(newDeviceType);
|
|
6994
|
+
};
|
|
6995
|
+
// Update device type on mount
|
|
6996
|
+
return setDeviceType(util.getCurrentDeviceType()),
|
|
6997
|
+
// Listen for resize events
|
|
6998
|
+
window.addEventListener("resize", handleResize), () => {
|
|
6999
|
+
window.removeEventListener("resize", handleResize), util.destroy();
|
|
7000
|
+
};
|
|
7001
|
+
}), [ util, deviceType ]), "undefined" == typeof window ? {
|
|
7002
|
+
deviceType: "desktop",
|
|
7003
|
+
isMobile: !1,
|
|
7004
|
+
isTablet: !1,
|
|
7005
|
+
isDesktop: !0,
|
|
7006
|
+
isWide: !1,
|
|
7007
|
+
scaleValue: value => value,
|
|
7008
|
+
getCurrentDeviceType: () => "desktop",
|
|
7009
|
+
getCurrentScalingFactor: () => 1,
|
|
7010
|
+
isMobileOrSmaller: () => !1,
|
|
7011
|
+
isTabletOrSmaller: () => !0,
|
|
7012
|
+
isDesktopOrLarger: () => !0
|
|
7013
|
+
} : {
|
|
7014
|
+
deviceType: deviceType,
|
|
7015
|
+
isMobile: "mobile" === deviceType,
|
|
7016
|
+
isTablet: "tablet" === deviceType,
|
|
7017
|
+
isDesktop: "desktop" === deviceType,
|
|
7018
|
+
isWide: "wide" === deviceType,
|
|
7019
|
+
scaleValue: value => util.scaleValue(value),
|
|
7020
|
+
getCurrentDeviceType: () => util.getCurrentDeviceType(),
|
|
7021
|
+
getCurrentScalingFactor: () => util.getCurrentScalingFactor(),
|
|
7022
|
+
isMobileOrSmaller: () => util.isMobileOrSmaller(),
|
|
7023
|
+
isTabletOrSmaller: () => util.isTabletOrSmaller(),
|
|
7024
|
+
isDesktopOrLarger: () => util.isDesktopOrLarger()
|
|
7025
|
+
};
|
|
7026
|
+
}
|
|
7027
|
+
|
|
5544
7028
|
/**
|
|
5545
7029
|
* RTL (Right-to-Left) Support Utilities
|
|
5546
7030
|
*
|
|
@@ -5726,6 +7210,32 @@ class RTLManager {
|
|
|
5726
7210
|
}
|
|
5727
7211
|
}
|
|
5728
7212
|
|
|
7213
|
+
/**
|
|
7214
|
+
* Create RTL manager instance
|
|
7215
|
+
*/ function createRTLManager(config) {
|
|
7216
|
+
return new RTLManager(config);
|
|
7217
|
+
}
|
|
7218
|
+
|
|
7219
|
+
/**
|
|
7220
|
+
* Check if locale is RTL
|
|
7221
|
+
*/ function isRTLLocale(locale) {
|
|
7222
|
+
return RTL_LOCALES.has(locale.toLowerCase());
|
|
7223
|
+
}
|
|
7224
|
+
|
|
7225
|
+
/**
|
|
7226
|
+
* Get direction from locale
|
|
7227
|
+
*/ function getDirectionFromLocale(locale) {
|
|
7228
|
+
return isRTLLocale(locale) ? "rtl" : "ltr";
|
|
7229
|
+
}
|
|
7230
|
+
|
|
7231
|
+
/**
|
|
7232
|
+
* RTL-aware CSS helper
|
|
7233
|
+
*
|
|
7234
|
+
* Returns appropriate CSS based on direction
|
|
7235
|
+
*/ function rtlCSS(ltrCSS, rtlCSS, direction = "ltr") {
|
|
7236
|
+
return "rtl" === direction ? rtlCSS : ltrCSS;
|
|
7237
|
+
}
|
|
7238
|
+
|
|
5729
7239
|
/**
|
|
5730
7240
|
* Theme System Exports
|
|
5731
7241
|
*
|
|
@@ -5748,8 +7258,6 @@ class RTLManager {
|
|
|
5748
7258
|
// ============================================================================
|
|
5749
7259
|
// Core Theme Functions
|
|
5750
7260
|
// ============================================================================
|
|
5751
|
-
// Create theme CSS from DesignTokens
|
|
5752
|
-
// File saving utilities removed to prevent bundling Node.js modules in browser
|
|
5753
7261
|
/**
|
|
5754
7262
|
* Inject theme CSS into DOM
|
|
5755
7263
|
*/ function injectTheme(css, id = "atomix-theme") {
|
|
@@ -5762,5 +7270,5 @@ class RTLManager {
|
|
|
5762
7270
|
removeCSS(id);
|
|
5763
7271
|
}
|
|
5764
7272
|
|
|
5765
|
-
export { DesignTokensCustomizer, RTLManager, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeValidator, applyCSSVariables, applyComponentTheme, applyTheme, camelToKebab, clearThemes, createTheme, createThemeRegistry, createTokens, cssVarsToStyle, deepMerge, defaultTokens, designTokensToCSSVars, extendTheme, extractComponentName, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, getAllThemes, getCSSVariable, getComponentThemeValue, getTheme, getThemeApplicator, getThemeCount, getThemeIds, hasTheme, injectCSS$1 as injectCSS, injectTheme, isCSSInjected, isDesignTokens, isValidCSSVariableName, mapSCSSTokensToCSSVars, mergeCSSVars, mergeTheme, normalizeThemeTokens, registerTheme, removeCSS, removeCSSVariables, removeTheme, themePropertyToCSSVar, unregisterTheme, useComponentTheme, useHistory, useTheme, useThemeTokens };
|
|
7273
|
+
export { DesignTokensCustomizer, RTLManager, ThemeApplicator, ThemeComparator, ThemeContext, ThemeErrorBoundary, ThemeInspector, ThemeLiveEditor, ThemePreview, ThemeProvider, ThemeToggle, ThemeValidator, alpha, applyCSSVariables, applyComponentTheme, applyTheme, camelToKebab, clearThemePreference, clearThemes, configToTokens, createPerformanceMonitor, createRTLManager, createResponsiveUtil, createSpacing, createTheme, createThemeRegistry, createTokens, cssVarsToStyle, darken, deepMerge, defaultTokens, designTokensToCSSVars, emphasize, extendTheme, extractComponentName, generateCSSVariableName, generateCSSVariables$1 as generateCSSVariables, generateCSSVariablesForSelector, generateClassName, generateComponentCSSVars, getAllThemes, getCSSVariable, getComponentThemeValue, getContrastRatio, getContrastText, getCurrentTheme, getDirectionFromLocale, getLuminance, getSystemTheme, getTheme, getThemeApplicator, getThemeCount, getThemeIds, hasTheme, hexToRgb$1 as hexToRgb, initializeTheme, injectCSS$1 as injectCSS, injectTheme, isAccessible, isCSSInjected, isDesignTokens, isRTLLocale, isValidCSSVariableName, lighten, listenToSystemTheme, mapSCSSTokensToCSSVars, mergeCSSVars, mergeTheme, mergeTokens, normalizeThemeTokens, omitTokens, overrideTokens, persistTheme, pickTokens, registerTheme, removeCSS, removeCSSVariables, removeTheme, rgbToHex, rtlCSS, switchTheme, themePropertyToCSSVar, toggleTheme, unregisterTheme, useComponentTheme, useHistory, usePerformanceMonitor, useResponsive, useTheme, useThemeSwitcher, useThemeTokens };
|
|
5766
7274
|
//# sourceMappingURL=theme.js.map
|