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