@yh-ui/theme 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/theme.cjs ADDED
@@ -0,0 +1,1261 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ThemePlugin = exports.ThemeManager = exports.THEME_INJECTION_KEY = void 0;
7
+ exports.adjustLightness = adjustLightness;
8
+ exports.adjustSaturation = adjustSaturation;
9
+ exports.breakpoints = void 0;
10
+ exports.checkContrast = checkContrast;
11
+ exports.densityConfig = exports.default = exports.colorBlindPalettes = void 0;
12
+ exports.ensureContrast = ensureContrast;
13
+ exports.generatePaletteFromPrimary = generatePaletteFromPrimary;
14
+ exports.getAnalogousColors = getAnalogousColors;
15
+ exports.getComplementaryColor = getComplementaryColor;
16
+ exports.getContrastRatio = getContrastRatio;
17
+ exports.getRelativeLuminance = getRelativeLuminance;
18
+ exports.getTextColorForBackground = getTextColorForBackground;
19
+ exports.getTriadicColors = getTriadicColors;
20
+ exports.hexToRgb = hexToRgb;
21
+ exports.hslToRgb = hslToRgb;
22
+ exports.initTheme = initTheme;
23
+ exports.mixColor = mixColor;
24
+ exports.presetThemes = void 0;
25
+ exports.rgbToHex = rgbToHex;
26
+ exports.rgbToHsl = rgbToHsl;
27
+ exports.setThemeColor = setThemeColor;
28
+ exports.setThemePreset = setThemePreset;
29
+ exports.toggleDarkMode = toggleDarkMode;
30
+ exports.useTheme = useTheme;
31
+ exports.useThemeVars = useThemeVars;
32
+ var _vue = require("vue");
33
+ var _tokens = require("./tokens/index.cjs");
34
+ const presetThemes = exports.presetThemes = {
35
+ default: {
36
+ primary: "#409eff",
37
+ success: "#67c23a",
38
+ warning: "#e6a23c",
39
+ danger: "#f56c6c",
40
+ info: "#909399"
41
+ },
42
+ dark: {
43
+ primary: "#409eff",
44
+ success: "#67c23a",
45
+ warning: "#e6a23c",
46
+ danger: "#f56c6c",
47
+ info: "#909399"
48
+ },
49
+ blue: {
50
+ primary: "#1890ff",
51
+ success: "#52c41a",
52
+ warning: "#faad14",
53
+ danger: "#ff4d4f",
54
+ info: "#909399"
55
+ },
56
+ green: {
57
+ primary: "#10b981",
58
+ success: "#22c55e",
59
+ warning: "#f59e0b",
60
+ danger: "#ef4444",
61
+ info: "#6b7280"
62
+ },
63
+ purple: {
64
+ primary: "#8b5cf6",
65
+ success: "#22c55e",
66
+ warning: "#f59e0b",
67
+ danger: "#ef4444",
68
+ info: "#6b7280"
69
+ },
70
+ orange: {
71
+ primary: "#f97316",
72
+ success: "#22c55e",
73
+ warning: "#eab308",
74
+ danger: "#ef4444",
75
+ info: "#6b7280"
76
+ },
77
+ rose: {
78
+ primary: "#f43f5e",
79
+ success: "#22c55e",
80
+ warning: "#f59e0b",
81
+ danger: "#dc2626",
82
+ info: "#6b7280"
83
+ },
84
+ amber: {
85
+ primary: "#f59e0b",
86
+ success: "#22c55e",
87
+ warning: "#eab308",
88
+ danger: "#ef4444",
89
+ info: "#6b7280"
90
+ },
91
+ teal: {
92
+ primary: "#14b8a6",
93
+ success: "#22c55e",
94
+ warning: "#f59e0b",
95
+ danger: "#ef4444",
96
+ info: "#6b7280"
97
+ },
98
+ indigo: {
99
+ primary: "#6366f1",
100
+ success: "#22c55e",
101
+ warning: "#f59e0b",
102
+ danger: "#ef4444",
103
+ info: "#6b7280"
104
+ },
105
+ slate: {
106
+ primary: "#475569",
107
+ success: "#22c55e",
108
+ warning: "#f59e0b",
109
+ danger: "#ef4444",
110
+ info: "#64748b"
111
+ },
112
+ zinc: {
113
+ primary: "#52525b",
114
+ success: "#22c55e",
115
+ warning: "#f59e0b",
116
+ danger: "#ef4444",
117
+ info: "#71717a"
118
+ }
119
+ };
120
+ function hexToRgb(hex) {
121
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
122
+ return result ? {
123
+ r: parseInt(result[1], 16),
124
+ g: parseInt(result[2], 16),
125
+ b: parseInt(result[3], 16)
126
+ } : null;
127
+ }
128
+ function rgbToHex(r, g, b) {
129
+ return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
130
+ }
131
+ function rgbToHsl(r, g, b) {
132
+ r /= 255;
133
+ g /= 255;
134
+ b /= 255;
135
+ const max = Math.max(r, g, b);
136
+ const min = Math.min(r, g, b);
137
+ let h = 0;
138
+ let s = 0;
139
+ const l = (max + min) / 2;
140
+ if (max !== min) {
141
+ const d = max - min;
142
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
143
+ switch (max) {
144
+ case r:
145
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
146
+ break;
147
+ case g:
148
+ h = ((b - r) / d + 2) / 6;
149
+ break;
150
+ case b:
151
+ h = ((r - g) / d + 4) / 6;
152
+ break;
153
+ }
154
+ }
155
+ return {
156
+ h: h * 360,
157
+ s: s * 100,
158
+ l: l * 100
159
+ };
160
+ }
161
+ function hslToRgb(h, s, l) {
162
+ h /= 360;
163
+ s /= 100;
164
+ l /= 100;
165
+ let r, g, b;
166
+ if (s === 0) {
167
+ r = g = b = l;
168
+ } else {
169
+ const hue2rgb = (p2, q2, t) => {
170
+ if (t < 0) t += 1;
171
+ if (t > 1) t -= 1;
172
+ if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
173
+ if (t < 1 / 2) return q2;
174
+ if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
175
+ return p2;
176
+ };
177
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
178
+ const p = 2 * l - q;
179
+ r = hue2rgb(p, q, h + 1 / 3);
180
+ g = hue2rgb(p, q, h);
181
+ b = hue2rgb(p, q, h - 1 / 3);
182
+ }
183
+ return {
184
+ r: Math.round(r * 255),
185
+ g: Math.round(g * 255),
186
+ b: Math.round(b * 255)
187
+ };
188
+ }
189
+ function getRelativeLuminance(r, g, b) {
190
+ const [rs, gs, bs] = [r, g, b].map(c => {
191
+ c /= 255;
192
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
193
+ });
194
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
195
+ }
196
+ function getContrastRatio(color1, color2) {
197
+ const rgb1 = hexToRgb(color1);
198
+ const rgb2 = hexToRgb(color2);
199
+ if (!rgb1 || !rgb2) return 1;
200
+ const l1 = getRelativeLuminance(rgb1.r, rgb1.g, rgb1.b);
201
+ const l2 = getRelativeLuminance(rgb2.r, rgb2.g, rgb2.b);
202
+ const lighter = Math.max(l1, l2);
203
+ const darker = Math.min(l1, l2);
204
+ return (lighter + 0.05) / (darker + 0.05);
205
+ }
206
+ function ensureContrast(foreground, background, minRatio = 4.5) {
207
+ const ratio = getContrastRatio(foreground, background);
208
+ if (ratio >= minRatio) return foreground;
209
+ const fgRgb = hexToRgb(foreground);
210
+ if (!fgRgb) return foreground;
211
+ const hsl = rgbToHsl(fgRgb.r, fgRgb.g, fgRgb.b);
212
+ const bgRgb = hexToRgb(background);
213
+ if (!bgRgb) return foreground;
214
+ const bgLuminance = getRelativeLuminance(bgRgb.r, bgRgb.g, bgRgb.b);
215
+ if (bgLuminance > 0.5) {
216
+ while (hsl.l > 0 && getContrastRatio(rgbToHex(...Object.values(hslToRgb(hsl.h, hsl.s, hsl.l))), background) < minRatio) {
217
+ hsl.l -= 5;
218
+ }
219
+ } else {
220
+ while (hsl.l < 100 && getContrastRatio(rgbToHex(...Object.values(hslToRgb(hsl.h, hsl.s, hsl.l))), background) < minRatio) {
221
+ hsl.l += 5;
222
+ }
223
+ }
224
+ const adjusted = hslToRgb(hsl.h, hsl.s, hsl.l);
225
+ return rgbToHex(adjusted.r, adjusted.g, adjusted.b);
226
+ }
227
+ function mixColor(color1, color2, weight) {
228
+ const rgb1 = hexToRgb(color1);
229
+ const rgb2 = hexToRgb(color2);
230
+ if (!rgb1 || !rgb2) return color1;
231
+ const w = weight / 100;
232
+ const r = Math.round(rgb1.r * w + rgb2.r * (1 - w));
233
+ const g = Math.round(rgb1.g * w + rgb2.g * (1 - w));
234
+ const b = Math.round(rgb1.b * w + rgb2.b * (1 - w));
235
+ return rgbToHex(r, g, b);
236
+ }
237
+ function adjustSaturation(color, amount) {
238
+ const rgb = hexToRgb(color);
239
+ if (!rgb) return color;
240
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
241
+ hsl.s = Math.max(0, Math.min(100, hsl.s + amount));
242
+ const adjusted = hslToRgb(hsl.h, hsl.s, hsl.l);
243
+ return rgbToHex(adjusted.r, adjusted.g, adjusted.b);
244
+ }
245
+ function adjustLightness(color, amount) {
246
+ const rgb = hexToRgb(color);
247
+ if (!rgb) return color;
248
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
249
+ hsl.l = Math.max(0, Math.min(100, hsl.l + amount));
250
+ const adjusted = hslToRgb(hsl.h, hsl.s, hsl.l);
251
+ return rgbToHex(adjusted.r, adjusted.g, adjusted.b);
252
+ }
253
+ function generateColorScaleWithAlgorithm(baseColor, isDark = false, algorithm = "default") {
254
+ const white = "#ffffff";
255
+ const black = "#000000";
256
+ let adjustedBase = baseColor;
257
+ switch (algorithm) {
258
+ case "vibrant":
259
+ adjustedBase = adjustSaturation(baseColor, 15);
260
+ break;
261
+ case "muted":
262
+ adjustedBase = adjustSaturation(baseColor, -20);
263
+ break;
264
+ case "pastel":
265
+ adjustedBase = adjustSaturation(adjustLightness(baseColor, 15), -30);
266
+ break;
267
+ }
268
+ if (isDark) {
269
+ return {
270
+ "": adjustedBase,
271
+ "light-3": mixColor(adjustedBase, black, 70),
272
+ "light-5": mixColor(adjustedBase, black, 50),
273
+ "light-7": mixColor(adjustedBase, black, 30),
274
+ "light-8": mixColor(adjustedBase, black, 20),
275
+ "light-9": mixColor(adjustedBase, black, 10),
276
+ "dark-2": mixColor(adjustedBase, white, 80)
277
+ };
278
+ }
279
+ return {
280
+ "": adjustedBase,
281
+ "light-1": mixColor(adjustedBase, white, 90),
282
+ "light-2": mixColor(adjustedBase, white, 80),
283
+ "light-3": mixColor(adjustedBase, white, 70),
284
+ "light-4": mixColor(adjustedBase, white, 60),
285
+ "light-5": mixColor(adjustedBase, white, 50),
286
+ "light-6": mixColor(adjustedBase, white, 40),
287
+ "light-7": mixColor(adjustedBase, white, 30),
288
+ "light-8": mixColor(adjustedBase, white, 20),
289
+ "light-9": mixColor(adjustedBase, white, 10),
290
+ "dark-2": mixColor(adjustedBase, black, 80)
291
+ };
292
+ }
293
+ function generateSemanticColors(baseColor, isDark = false) {
294
+ return {
295
+ hover: isDark ? adjustLightness(baseColor, 10) : adjustLightness(baseColor, -5),
296
+ active: isDark ? adjustLightness(baseColor, -5) : adjustLightness(baseColor, -10),
297
+ disabled: adjustSaturation(adjustLightness(baseColor, isDark ? -20 : 30), -40),
298
+ focus: baseColor
299
+ };
300
+ }
301
+ function setCssVar(name, value, el = null) {
302
+ const target = el || (typeof document !== "undefined" ? document.documentElement : null);
303
+ if (target) {
304
+ target.style.setProperty(name, value);
305
+ }
306
+ }
307
+ function getCssVar(name, el = null) {
308
+ const target = el || (typeof document !== "undefined" ? document.documentElement : null);
309
+ if (target) {
310
+ return getComputedStyle(target).getPropertyValue(name).trim();
311
+ }
312
+ return "";
313
+ }
314
+ function removeCssVar(name, el = null) {
315
+ const target = el || (typeof document !== "undefined" ? document.documentElement : null);
316
+ if (target) {
317
+ target.style.removeProperty(name);
318
+ }
319
+ }
320
+ function getTargetElement(scope) {
321
+ if (typeof document === "undefined") return null;
322
+ if (!scope) return document.documentElement;
323
+ if (typeof scope === "string") {
324
+ return document.querySelector(scope);
325
+ }
326
+ return scope;
327
+ }
328
+ const breakpoints = exports.breakpoints = {
329
+ xs: 0,
330
+ sm: 576,
331
+ md: 768,
332
+ lg: 992,
333
+ xl: 1200,
334
+ xxl: 1400
335
+ };
336
+ const densityConfig = exports.densityConfig = {
337
+ comfortable: {
338
+ "--yh-density-factor": "1",
339
+ "--yh-component-size-default": "32px",
340
+ "--yh-component-size-small": "24px",
341
+ "--yh-component-size-large": "40px",
342
+ "--yh-padding-default": "12px 16px",
343
+ "--yh-padding-small": "8px 12px",
344
+ "--yh-padding-large": "16px 20px",
345
+ "--yh-spacing-unit": "8px",
346
+ "--yh-font-size-base": "14px",
347
+ "--yh-line-height-base": "1.5"
348
+ },
349
+ compact: {
350
+ "--yh-density-factor": "0.85",
351
+ "--yh-component-size-default": "28px",
352
+ "--yh-component-size-small": "20px",
353
+ "--yh-component-size-large": "36px",
354
+ "--yh-padding-default": "8px 12px",
355
+ "--yh-padding-small": "4px 8px",
356
+ "--yh-padding-large": "12px 16px",
357
+ "--yh-spacing-unit": "6px",
358
+ "--yh-font-size-base": "13px",
359
+ "--yh-line-height-base": "1.4"
360
+ },
361
+ dense: {
362
+ "--yh-density-factor": "0.7",
363
+ "--yh-component-size-default": "24px",
364
+ "--yh-component-size-small": "18px",
365
+ "--yh-component-size-large": "32px",
366
+ "--yh-padding-default": "4px 8px",
367
+ "--yh-padding-small": "2px 6px",
368
+ "--yh-padding-large": "8px 12px",
369
+ "--yh-spacing-unit": "4px",
370
+ "--yh-font-size-base": "12px",
371
+ "--yh-line-height-base": "1.35"
372
+ }
373
+ };
374
+ const colorBlindPalettes = exports.colorBlindPalettes = {
375
+ none: {},
376
+ // 红色盲(无法区分红绿)
377
+ protanopia: {
378
+ primary: "#0072B2",
379
+ // 蓝色
380
+ success: "#009E73",
381
+ // 蓝绿色
382
+ warning: "#E69F00",
383
+ // 橙色
384
+ danger: "#D55E00",
385
+ // 深橙色
386
+ info: "#56B4E9"
387
+ // 浅蓝色
388
+ },
389
+ // 绿色盲(无法区分红绿)
390
+ deuteranopia: {
391
+ primary: "#0072B2",
392
+ success: "#009E73",
393
+ warning: "#E69F00",
394
+ danger: "#CC79A7",
395
+ // 粉紫色
396
+ info: "#56B4E9"
397
+ },
398
+ // 蓝色盲(无法区分蓝黄)
399
+ tritanopia: {
400
+ primary: "#CC79A7",
401
+ // 粉紫色
402
+ success: "#009E73",
403
+ // 蓝绿色
404
+ warning: "#D55E00",
405
+ // 深橙色
406
+ danger: "#E69F00",
407
+ // 橙色
408
+ info: "#999999"
409
+ // 灰色
410
+ },
411
+ // 全色盲(只能看到灰度)
412
+ achromatopsia: {
413
+ primary: "#404040",
414
+ success: "#606060",
415
+ warning: "#808080",
416
+ danger: "#202020",
417
+ info: "#a0a0a0"
418
+ }
419
+ };
420
+ const THEME_TRANSITION_CLASS = "yh-theme-transitioning";
421
+ const DEFAULT_TRANSITION_DURATION = 300;
422
+ const DEFAULT_TRANSITION_TIMING = "cubic-bezier(0.4, 0, 0.2, 1)";
423
+ function enableThemeTransition(duration = DEFAULT_TRANSITION_DURATION, timing = DEFAULT_TRANSITION_TIMING) {
424
+ if (typeof document === "undefined") return;
425
+ const style = document.createElement("style");
426
+ style.id = "yh-theme-transition";
427
+ style.textContent = `
428
+ .${THEME_TRANSITION_CLASS},
429
+ .${THEME_TRANSITION_CLASS} *,
430
+ .${THEME_TRANSITION_CLASS} *::before,
431
+ .${THEME_TRANSITION_CLASS} *::after {
432
+ transition:
433
+ background-color ${duration}ms ${timing},
434
+ border-color ${duration}ms ${timing},
435
+ color ${duration}ms ${timing},
436
+ fill ${duration}ms ${timing},
437
+ stroke ${duration}ms ${timing},
438
+ box-shadow ${duration}ms ${timing} !important;
439
+ }
440
+ `;
441
+ document.head.appendChild(style);
442
+ document.documentElement.classList.add(THEME_TRANSITION_CLASS);
443
+ setTimeout(() => {
444
+ document.documentElement.classList.remove(THEME_TRANSITION_CLASS);
445
+ style.remove();
446
+ }, duration);
447
+ }
448
+ function getComplementaryColor(hex) {
449
+ const rgb = hexToRgb(hex);
450
+ if (!rgb) return hex;
451
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
452
+ hsl.h = (hsl.h + 180) % 360;
453
+ const result = hslToRgb(hsl.h, hsl.s, hsl.l);
454
+ return rgbToHex(result.r, result.g, result.b);
455
+ }
456
+ function getAnalogousColors(hex) {
457
+ const rgb = hexToRgb(hex);
458
+ if (!rgb) return [hex, hex];
459
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
460
+ const hsl1 = {
461
+ ...hsl,
462
+ h: (hsl.h + 30) % 360
463
+ };
464
+ const hsl2 = {
465
+ ...hsl,
466
+ h: (hsl.h - 30 + 360) % 360
467
+ };
468
+ const result1 = hslToRgb(hsl1.h, hsl1.s, hsl1.l);
469
+ const result2 = hslToRgb(hsl2.h, hsl2.s, hsl2.l);
470
+ return [rgbToHex(result1.r, result1.g, result1.b), rgbToHex(result2.r, result2.g, result2.b)];
471
+ }
472
+ function getTriadicColors(hex) {
473
+ const rgb = hexToRgb(hex);
474
+ if (!rgb) return [hex, hex];
475
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
476
+ const hsl1 = {
477
+ ...hsl,
478
+ h: (hsl.h + 120) % 360
479
+ };
480
+ const hsl2 = {
481
+ ...hsl,
482
+ h: (hsl.h + 240) % 360
483
+ };
484
+ const result1 = hslToRgb(hsl1.h, hsl1.s, hsl1.l);
485
+ const result2 = hslToRgb(hsl2.h, hsl2.s, hsl2.l);
486
+ return [rgbToHex(result1.r, result1.g, result1.b), rgbToHex(result2.r, result2.g, result2.b)];
487
+ }
488
+ function generatePaletteFromPrimary(primaryColor) {
489
+ const rgb = hexToRgb(primaryColor);
490
+ if (!rgb) return {
491
+ primary: primaryColor
492
+ };
493
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
494
+ const successHsl = {
495
+ h: 142,
496
+ s: Math.min(hsl.s, 70),
497
+ l: 45
498
+ };
499
+ const successRgb = hslToRgb(successHsl.h, successHsl.s, successHsl.l);
500
+ const warningHsl = {
501
+ h: 36,
502
+ s: Math.min(hsl.s + 10, 85),
503
+ l: 50
504
+ };
505
+ const warningRgb = hslToRgb(warningHsl.h, warningHsl.s, warningHsl.l);
506
+ const dangerHsl = {
507
+ h: 0,
508
+ s: Math.min(hsl.s + 5, 75),
509
+ l: 55
510
+ };
511
+ const dangerRgb = hslToRgb(dangerHsl.h, dangerHsl.s, dangerHsl.l);
512
+ const infoHsl = {
513
+ h: hsl.h,
514
+ s: Math.max(hsl.s - 40, 10),
515
+ l: 60
516
+ };
517
+ const infoRgb = hslToRgb(infoHsl.h, infoHsl.s, infoHsl.l);
518
+ return {
519
+ primary: primaryColor,
520
+ success: rgbToHex(successRgb.r, successRgb.g, successRgb.b),
521
+ warning: rgbToHex(warningRgb.r, warningRgb.g, warningRgb.b),
522
+ danger: rgbToHex(dangerRgb.r, dangerRgb.g, dangerRgb.b),
523
+ info: rgbToHex(infoRgb.r, infoRgb.g, infoRgb.b)
524
+ };
525
+ }
526
+ function getCurrentBreakpoint() {
527
+ if (typeof window === "undefined") return "md";
528
+ const width = window.innerWidth;
529
+ if (width >= breakpoints.xxl) return "xxl";
530
+ if (width >= breakpoints.xl) return "xl";
531
+ if (width >= breakpoints.lg) return "lg";
532
+ if (width >= breakpoints.md) return "md";
533
+ if (width >= breakpoints.sm) return "sm";
534
+ return "xs";
535
+ }
536
+ class ThemeManager {
537
+ currentTheme = "default";
538
+ customColors = {};
539
+ isDark = false;
540
+ targetEl = null;
541
+ persistKey = "yh-ui-theme";
542
+ algorithm = "default";
543
+ responsiveConfig = {};
544
+ currentBreakpoint = "md";
545
+ resizeHandler = null;
546
+ systemDarkQuery = null;
547
+ systemDarkHandler = null;
548
+ followSystem = false;
549
+ currentDensity = "comfortable";
550
+ colorBlindMode = "none";
551
+ componentOverrides = {};
552
+ transitionEnabled = false;
553
+ transitionConfig = {
554
+ duration: DEFAULT_TRANSITION_DURATION,
555
+ timing: DEFAULT_TRANSITION_TIMING
556
+ };
557
+ themeHistory = [];
558
+ maxHistoryLength = 10;
559
+ // 响应式状态
560
+ state = (0, _vue.reactive)({
561
+ theme: "default",
562
+ dark: false,
563
+ colors: {},
564
+ breakpoint: "md",
565
+ density: "comfortable",
566
+ colorBlindMode: "none"
567
+ });
568
+ constructor(options) {
569
+ this.initTheme(options);
570
+ }
571
+ /** 初始化主题 */
572
+ initTheme(options) {
573
+ if (options?.persist !== false) {
574
+ this.persistKey = options?.persistKey || "yh-ui-theme";
575
+ this.restoreFromStorage();
576
+ }
577
+ this.apply({
578
+ preset: "default",
579
+ ...options
580
+ });
581
+ if (options?.responsive) {
582
+ this.setResponsiveTheme(options.responsive);
583
+ }
584
+ if (options?.followSystem) {
585
+ this.enableSystemFollow();
586
+ }
587
+ this.applyTokens();
588
+ }
589
+ /** 应用主题 */
590
+ apply(options) {
591
+ const {
592
+ preset,
593
+ colors,
594
+ dark,
595
+ scope,
596
+ algorithm
597
+ } = options;
598
+ this.targetEl = getTargetElement(scope);
599
+ if (algorithm) {
600
+ this.algorithm = algorithm;
601
+ }
602
+ if (dark !== void 0) {
603
+ this.setDarkMode(dark);
604
+ }
605
+ if (preset) {
606
+ this.setPreset(preset);
607
+ }
608
+ if (colors) {
609
+ this.setColors(colors);
610
+ }
611
+ }
612
+ /** 设置预设主题 */
613
+ setPreset(preset) {
614
+ const colors = presetThemes[preset];
615
+ if (!colors) {
616
+ console.warn(`[YH-UI Theme] Invalid preset: "${preset}"`);
617
+ return;
618
+ }
619
+ this.currentTheme = preset;
620
+ this.state.theme = preset;
621
+ this.applyColors(colors);
622
+ this.saveToStorage();
623
+ }
624
+ /** 设置自定义颜色 */
625
+ setColors(colors) {
626
+ this.customColors = {
627
+ ...this.customColors,
628
+ ...colors
629
+ };
630
+ this.state.colors = this.customColors;
631
+ this.applyColors(colors);
632
+ this.saveToStorage();
633
+ }
634
+ /** 设置主色 */
635
+ setPrimaryColor(color) {
636
+ this.setColors({
637
+ primary: color
638
+ });
639
+ }
640
+ /** 设置主题色 (别名) */
641
+ setThemeColor(color) {
642
+ this.setPrimaryColor(color);
643
+ }
644
+ /** 设置预设主题 (别名) */
645
+ setThemePreset(preset) {
646
+ this.setPreset(preset);
647
+ }
648
+ /** 设置颜色算法 */
649
+ setAlgorithm(algorithm) {
650
+ this.algorithm = algorithm;
651
+ const currentColors = {
652
+ ...presetThemes[this.currentTheme],
653
+ ...this.customColors
654
+ };
655
+ this.applyColors(currentColors);
656
+ this.saveToStorage();
657
+ }
658
+ /** 设置暗色模式 */
659
+ setDarkMode(dark) {
660
+ this.isDark = dark;
661
+ this.state.dark = dark;
662
+ if (typeof document !== "undefined") {
663
+ if (dark) {
664
+ document.documentElement.classList.add("dark");
665
+ } else {
666
+ document.documentElement.classList.remove("dark");
667
+ }
668
+ }
669
+ const currentColors = {
670
+ ...presetThemes[this.currentTheme],
671
+ ...this.customColors
672
+ };
673
+ this.applyColors(currentColors);
674
+ this.saveToStorage();
675
+ }
676
+ /** 切换暗色模式 */
677
+ toggleDarkMode() {
678
+ this.setDarkMode(!this.isDark);
679
+ return this.isDark;
680
+ }
681
+ /** 启用系统主题跟随 */
682
+ enableSystemFollow() {
683
+ if (typeof window === "undefined") return;
684
+ this.followSystem = true;
685
+ this.systemDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
686
+ this.setDarkMode(this.systemDarkQuery.matches);
687
+ this.systemDarkHandler = e => {
688
+ if (this.followSystem) {
689
+ this.setDarkMode(e.matches);
690
+ }
691
+ };
692
+ this.systemDarkQuery.addEventListener("change", this.systemDarkHandler);
693
+ }
694
+ /** 禁用系统主题跟随 */
695
+ disableSystemFollow() {
696
+ this.followSystem = false;
697
+ if (this.systemDarkQuery && this.systemDarkHandler) {
698
+ this.systemDarkQuery.removeEventListener("change", this.systemDarkHandler);
699
+ }
700
+ }
701
+ /** 设置响应式主题 */
702
+ setResponsiveTheme(config) {
703
+ this.responsiveConfig = config;
704
+ if (typeof window === "undefined") return;
705
+ this.currentBreakpoint = getCurrentBreakpoint();
706
+ this.state.breakpoint = this.currentBreakpoint;
707
+ this.applyResponsiveTheme();
708
+ this.resizeHandler = () => {
709
+ const newBreakpoint = getCurrentBreakpoint();
710
+ if (newBreakpoint !== this.currentBreakpoint) {
711
+ this.currentBreakpoint = newBreakpoint;
712
+ this.state.breakpoint = newBreakpoint;
713
+ this.applyResponsiveTheme();
714
+ }
715
+ };
716
+ window.addEventListener("resize", this.resizeHandler);
717
+ }
718
+ /** 应用响应式主题 */
719
+ applyResponsiveTheme() {
720
+ const config = this.responsiveConfig[this.currentBreakpoint];
721
+ if (config) {
722
+ this.apply(config);
723
+ }
724
+ }
725
+ /** 获取当前是否暗色模式 */
726
+ get dark() {
727
+ return this.isDark;
728
+ }
729
+ /** 获取当前主题 */
730
+ get theme() {
731
+ return this.currentTheme;
732
+ }
733
+ /** 获取所有可用预设 */
734
+ get presets() {
735
+ return Object.keys(presetThemes);
736
+ }
737
+ /** 获取当前断点 */
738
+ get breakpoint() {
739
+ return this.currentBreakpoint;
740
+ }
741
+ /** 应用颜色到 CSS 变量 */
742
+ applyColors(colors) {
743
+ const el = this.targetEl || (typeof document !== "undefined" ? document.documentElement : null);
744
+ if (!el) return;
745
+ const styles = this.getThemeStyles(colors);
746
+ Object.entries(styles).forEach(([name, value]) => {
747
+ setCssVar(name, value, el);
748
+ });
749
+ }
750
+ /** 获取当前主题的 CSS 变量对象 */
751
+ getThemeStyles(colors = {}) {
752
+ const styles = {};
753
+ const themeColors = {
754
+ ...presetThemes[this.currentTheme],
755
+ ...this.customColors,
756
+ ...colors
757
+ };
758
+ const colorTypes = [{
759
+ key: "primary",
760
+ cssVar: "primary"
761
+ }, {
762
+ key: "success",
763
+ cssVar: "success"
764
+ }, {
765
+ key: "warning",
766
+ cssVar: "warning"
767
+ }, {
768
+ key: "danger",
769
+ cssVar: "danger"
770
+ }, {
771
+ key: "info",
772
+ cssVar: "info"
773
+ }];
774
+ colorTypes.forEach(({
775
+ key,
776
+ cssVar
777
+ }) => {
778
+ const baseColor = themeColors[key];
779
+ if (baseColor) {
780
+ const colorScale = generateColorScaleWithAlgorithm(baseColor, this.isDark, this.algorithm);
781
+ Object.entries(colorScale).forEach(([suffix, value]) => {
782
+ const varName = suffix ? `--yh-color-${cssVar}-${suffix}` : `--yh-color-${cssVar}`;
783
+ styles[varName] = value;
784
+ });
785
+ const semanticColors = generateSemanticColors(baseColor, this.isDark);
786
+ Object.entries(semanticColors).forEach(([state, value]) => {
787
+ styles[`--yh-color-${cssVar}-${state}`] = value;
788
+ });
789
+ const rgb = hexToRgb(baseColor);
790
+ if (rgb) {
791
+ styles[`--yh-color-${cssVar}-rgb`] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
792
+ }
793
+ }
794
+ });
795
+ return styles;
796
+ }
797
+ /** 获取 CSS 变量值 */
798
+ getCssVar(name) {
799
+ return getCssVar(name, this.targetEl);
800
+ }
801
+ /** 设置 CSS 变量值 */
802
+ setCssVar(name, value) {
803
+ setCssVar(name, value, this.targetEl);
804
+ }
805
+ /** 应用所有设计令牌 */
806
+ applyTokens() {
807
+ const el = this.targetEl || (typeof document !== "undefined" ? document.documentElement : null);
808
+ if (!el) return;
809
+ Object.entries(_tokens.designTokens.colors).forEach(([type, colors]) => {
810
+ const colorObj = colors;
811
+ setCssVar(`--yh-color-${type}`, colorObj.DEFAULT, el);
812
+ if (colorObj.light) {
813
+ Object.entries(colorObj.light).forEach(([level, value]) => {
814
+ setCssVar(`--yh-color-${type}-light-${level}`, value, el);
815
+ });
816
+ }
817
+ if (colorObj.dark) {
818
+ Object.entries(colorObj.dark).forEach(([level, value]) => {
819
+ setCssVar(`--yh-color-${type}-dark-${level}`, value, el);
820
+ });
821
+ }
822
+ });
823
+ Object.entries(_tokens.designTokens.textColors).forEach(([key, value]) => {
824
+ setCssVar(`--yh-text-color-${key}`, value, el);
825
+ });
826
+ Object.entries(_tokens.designTokens.borderColors).forEach(([key, value]) => {
827
+ const name = key === "DEFAULT" ? "--yh-border-color" : `--yh-border-color-${key}`;
828
+ setCssVar(name, value, el);
829
+ });
830
+ Object.entries(_tokens.designTokens.bgColors).forEach(([key, value]) => {
831
+ const name = key === "DEFAULT" ? "--yh-bg-color" : `--yh-bg-color-${key}`;
832
+ setCssVar(name, value, el);
833
+ });
834
+ Object.entries(_tokens.designTokens.radius).forEach(([key, value]) => {
835
+ setCssVar(`--yh-radius-${key}`, value, el);
836
+ });
837
+ Object.entries(_tokens.designTokens.zIndex).forEach(([key, value]) => {
838
+ setCssVar(`--yh-z-index-${key}`, value, el);
839
+ });
840
+ }
841
+ // ==================== 持久化 ====================
842
+ /** 保存到存储 */
843
+ saveToStorage() {
844
+ if (typeof localStorage === "undefined") return;
845
+ const snapshot = {
846
+ preset: this.currentTheme,
847
+ colors: this.customColors,
848
+ dark: this.isDark,
849
+ radius: "md",
850
+ algorithm: this.algorithm,
851
+ density: this.currentDensity,
852
+ timestamp: Date.now(),
853
+ version: "1.0.0"
854
+ };
855
+ try {
856
+ localStorage.setItem(this.persistKey, JSON.stringify(snapshot));
857
+ } catch (e) {
858
+ console.warn("[YH-UI Theme] Failed to persist theme:", e);
859
+ }
860
+ }
861
+ /** 从存储恢复 */
862
+ restoreFromStorage() {
863
+ if (typeof localStorage === "undefined") return false;
864
+ try {
865
+ const stored = localStorage.getItem(this.persistKey);
866
+ if (!stored) return false;
867
+ const snapshot = JSON.parse(stored);
868
+ this.currentTheme = snapshot.preset;
869
+ this.customColors = snapshot.colors || {};
870
+ this.isDark = snapshot.dark;
871
+ this.algorithm = snapshot.algorithm || "default";
872
+ this.currentDensity = snapshot.density || "comfortable";
873
+ this.state.theme = this.currentTheme;
874
+ this.state.dark = this.isDark;
875
+ this.state.colors = this.customColors;
876
+ this.state.density = this.currentDensity;
877
+ return true;
878
+ } catch (e) {
879
+ console.warn("[YH-UI Theme] Failed to restore theme:", e);
880
+ return false;
881
+ }
882
+ }
883
+ /** 导出主题配置 */
884
+ exportTheme(options) {
885
+ const snapshot = {
886
+ preset: this.currentTheme,
887
+ colors: this.customColors,
888
+ dark: this.isDark,
889
+ radius: "md",
890
+ algorithm: this.algorithm,
891
+ density: this.currentDensity,
892
+ timestamp: Date.now(),
893
+ version: "1.0.0",
894
+ name: options?.name,
895
+ author: options?.author
896
+ };
897
+ return JSON.stringify(snapshot, null, 2);
898
+ }
899
+ /** 导入主题配置 */
900
+ importTheme(json) {
901
+ try {
902
+ const snapshot = JSON.parse(json);
903
+ if (this.transitionEnabled) {
904
+ enableThemeTransition(this.transitionConfig.duration, this.transitionConfig.timing);
905
+ }
906
+ this.setPreset(snapshot.preset);
907
+ if (snapshot.colors && Object.keys(snapshot.colors).length > 0) {
908
+ this.setColors(snapshot.colors);
909
+ }
910
+ this.setDarkMode(snapshot.dark);
911
+ if (snapshot.algorithm) {
912
+ this.setAlgorithm(snapshot.algorithm);
913
+ }
914
+ if (snapshot.density) {
915
+ this.setDensity(snapshot.density);
916
+ }
917
+ return true;
918
+ } catch (e) {
919
+ console.error("[YH-UI Theme] Failed to import theme:", e);
920
+ return false;
921
+ }
922
+ }
923
+ /** 导出为纯 CSS */
924
+ exportAsCss() {
925
+ const styles = this.getThemeStyles();
926
+ let css = ":root {\n";
927
+ Object.entries(styles).forEach(([name, value]) => {
928
+ css += ` ${name}: ${value};
929
+ `;
930
+ });
931
+ css += "}\n";
932
+ return css;
933
+ }
934
+ /** 重置为默认主题 */
935
+ reset() {
936
+ if (typeof document === "undefined") return;
937
+ this.currentTheme = "default";
938
+ this.customColors = {};
939
+ this.isDark = false;
940
+ this.algorithm = "default";
941
+ this.currentDensity = "comfortable";
942
+ this.colorBlindMode = "none";
943
+ this.componentOverrides = {};
944
+ this.state.theme = "default";
945
+ this.state.dark = false;
946
+ this.state.colors = {};
947
+ this.state.density = "comfortable";
948
+ this.state.colorBlindMode = "none";
949
+ document.documentElement.classList.remove("dark");
950
+ const el = this.targetEl || document.documentElement;
951
+ const colorTypes = ["primary", "success", "warning", "danger", "info"];
952
+ const suffixes = ["", "light-1", "light-2", "light-3", "light-4", "light-5", "light-6", "light-7", "light-8", "light-9", "dark-2", "hover", "active", "disabled", "focus", "rgb"];
953
+ colorTypes.forEach(type => {
954
+ suffixes.forEach(suffix => {
955
+ const varName = suffix ? `--yh-color-${type}-${suffix}` : `--yh-color-${type}`;
956
+ removeCssVar(varName, el);
957
+ });
958
+ });
959
+ Object.keys(densityConfig.comfortable).forEach(key => {
960
+ removeCssVar(key, el);
961
+ });
962
+ this.saveToStorage();
963
+ }
964
+ // ==================== 密度/紧凑度控制 ====================
965
+ /** 设置密度 */
966
+ setDensity(density) {
967
+ if (this.transitionEnabled) {
968
+ enableThemeTransition(this.transitionConfig.duration, this.transitionConfig.timing);
969
+ }
970
+ this.currentDensity = density;
971
+ this.state.density = density;
972
+ const el = this.targetEl || (typeof document !== "undefined" ? document.documentElement : null);
973
+ if (!el) return;
974
+ const config = densityConfig[density];
975
+ Object.entries(config).forEach(([name, value]) => {
976
+ setCssVar(name, value, el);
977
+ });
978
+ this.saveToStorage();
979
+ }
980
+ /** 获取当前密度 */
981
+ get density() {
982
+ return this.currentDensity;
983
+ }
984
+ // ==================== 色盲模式 ====================
985
+ /** 设置色盲友好模式 */
986
+ setColorBlindMode(mode) {
987
+ if (this.transitionEnabled) {
988
+ enableThemeTransition(this.transitionConfig.duration, this.transitionConfig.timing);
989
+ }
990
+ this.colorBlindMode = mode;
991
+ this.state.colorBlindMode = mode;
992
+ if (mode === "none") {
993
+ const currentColors = {
994
+ ...presetThemes[this.currentTheme],
995
+ ...this.customColors
996
+ };
997
+ this.applyColors(currentColors);
998
+ } else {
999
+ const palette = colorBlindPalettes[mode];
1000
+ this.applyColors(palette);
1001
+ }
1002
+ this.saveToStorage();
1003
+ }
1004
+ /** 获取当前色盲模式 */
1005
+ get colorBlind() {
1006
+ return this.colorBlindMode;
1007
+ }
1008
+ // ==================== 组件级主题覆盖 ====================
1009
+ /** 设置组件级主题覆盖 */
1010
+ setComponentTheme(componentName, overrides) {
1011
+ this.componentOverrides[componentName] = {
1012
+ ...this.componentOverrides[componentName],
1013
+ ...overrides
1014
+ };
1015
+ const el = this.targetEl || (typeof document !== "undefined" ? document.documentElement : null);
1016
+ if (!el) return;
1017
+ Object.entries(overrides).forEach(([name, value]) => {
1018
+ setCssVar(`--yh-${componentName}-${name}`, value, el);
1019
+ });
1020
+ }
1021
+ /** 获取组件主题覆盖 */
1022
+ getComponentTheme(componentName) {
1023
+ return this.componentOverrides[componentName] || {};
1024
+ }
1025
+ /** 清除组件级覆盖 */
1026
+ clearComponentTheme(componentName) {
1027
+ const overrides = this.componentOverrides[componentName];
1028
+ if (!overrides) return;
1029
+ const el = this.targetEl || (typeof document !== "undefined" ? document.documentElement : null);
1030
+ if (!el) return;
1031
+ Object.keys(overrides).forEach(name => {
1032
+ removeCssVar(`--yh-${componentName}-${name}`, el);
1033
+ });
1034
+ delete this.componentOverrides[componentName];
1035
+ }
1036
+ // ==================== 主题切换动画 ====================
1037
+ /** 启用主题切换动画 */
1038
+ enableTransition(config) {
1039
+ this.transitionEnabled = true;
1040
+ if (config) {
1041
+ this.transitionConfig = {
1042
+ duration: config.duration ?? DEFAULT_TRANSITION_DURATION,
1043
+ timing: config.timing ?? DEFAULT_TRANSITION_TIMING
1044
+ };
1045
+ }
1046
+ }
1047
+ /** 禁用主题切换动画 */
1048
+ disableTransition() {
1049
+ this.transitionEnabled = false;
1050
+ }
1051
+ // ==================== 主题继承与合并 ====================
1052
+ /** 创建继承主题 */
1053
+ createTheme(config) {
1054
+ let baseColors = {};
1055
+ if (config.extends) {
1056
+ if (typeof config.extends === "string") {
1057
+ baseColors = {
1058
+ ...presetThemes[config.extends]
1059
+ };
1060
+ } else {
1061
+ const parentSnapshot = this.createTheme(config.extends);
1062
+ baseColors = {
1063
+ ...parentSnapshot.colors
1064
+ };
1065
+ }
1066
+ }
1067
+ const mergedColors = {
1068
+ ...baseColors,
1069
+ ...config.colors
1070
+ };
1071
+ return {
1072
+ preset: config.preset || "default",
1073
+ colors: mergedColors,
1074
+ dark: config.dark || false,
1075
+ radius: config.radius || "md",
1076
+ algorithm: config.algorithm || "default",
1077
+ density: config.density || "comfortable",
1078
+ timestamp: Date.now(),
1079
+ version: "1.0.0",
1080
+ name: config.name,
1081
+ author: config.author
1082
+ };
1083
+ }
1084
+ /** 应用完整主题配置 */
1085
+ applyFullConfig(config) {
1086
+ if (this.transitionEnabled || config.transition) {
1087
+ const transitionConfig = typeof config.transition === "object" ? config.transition : {
1088
+ duration: DEFAULT_TRANSITION_DURATION,
1089
+ timing: DEFAULT_TRANSITION_TIMING
1090
+ };
1091
+ enableThemeTransition(transitionConfig.duration, transitionConfig.timing);
1092
+ }
1093
+ this.pushHistory();
1094
+ const snapshot = this.createTheme(config);
1095
+ this.setPreset(snapshot.preset);
1096
+ if (snapshot.colors && Object.keys(snapshot.colors).length > 0) {
1097
+ this.setColors(snapshot.colors);
1098
+ }
1099
+ this.setDarkMode(snapshot.dark);
1100
+ if (snapshot.algorithm) {
1101
+ this.setAlgorithm(snapshot.algorithm);
1102
+ }
1103
+ if (config.density) {
1104
+ this.setDensity(config.density);
1105
+ }
1106
+ if (config.colorBlindMode) {
1107
+ this.setColorBlindMode(config.colorBlindMode);
1108
+ }
1109
+ if (config.components) {
1110
+ Object.entries(config.components).forEach(([componentName, overrides]) => {
1111
+ this.setComponentTheme(componentName, overrides);
1112
+ });
1113
+ }
1114
+ }
1115
+ // ==================== 主题历史 ====================
1116
+ /** 保存当前状态到历史 */
1117
+ pushHistory() {
1118
+ const snapshot = {
1119
+ preset: this.currentTheme,
1120
+ colors: {
1121
+ ...this.customColors
1122
+ },
1123
+ dark: this.isDark,
1124
+ radius: "md",
1125
+ algorithm: this.algorithm,
1126
+ density: this.currentDensity,
1127
+ timestamp: Date.now(),
1128
+ version: "1.0.0"
1129
+ };
1130
+ this.themeHistory.push(snapshot);
1131
+ if (this.themeHistory.length > this.maxHistoryLength) {
1132
+ this.themeHistory.shift();
1133
+ }
1134
+ }
1135
+ /** 撤销到上一个主题状态 */
1136
+ undo() {
1137
+ const previousSnapshot = this.themeHistory.pop();
1138
+ if (!previousSnapshot) return false;
1139
+ this.importTheme(JSON.stringify(previousSnapshot));
1140
+ return true;
1141
+ }
1142
+ /** 获取主题历史 */
1143
+ getHistory() {
1144
+ return [...this.themeHistory];
1145
+ }
1146
+ /** 清除主题历史 */
1147
+ clearHistory() {
1148
+ this.themeHistory = [];
1149
+ }
1150
+ // ==================== 智能色彩生成 ====================
1151
+ /** 从主色生成完整调色板 */
1152
+ generateFromPrimary(primaryColor) {
1153
+ return generatePaletteFromPrimary(primaryColor);
1154
+ }
1155
+ /** 应用从主色生成的调色板 */
1156
+ applyFromPrimary(primaryColor) {
1157
+ const palette = this.generateFromPrimary(primaryColor);
1158
+ this.setColors(palette);
1159
+ }
1160
+ /** 获取互补色 */
1161
+ getComplementary(hex) {
1162
+ return getComplementaryColor(hex);
1163
+ }
1164
+ /** 获取类似色 */
1165
+ getAnalogous(hex) {
1166
+ return getAnalogousColors(hex);
1167
+ }
1168
+ /** 获取三角色 */
1169
+ getTriadic(hex) {
1170
+ return getTriadicColors(hex);
1171
+ }
1172
+ // ==================== 响应式主题变量 ====================
1173
+ /** 设置响应式变量 (根据断点自动切换) */
1174
+ setResponsiveVar(name, values) {
1175
+ if (typeof document === "undefined") return;
1176
+ let style = document.getElementById("yh-responsive-vars");
1177
+ if (!style) {
1178
+ style = document.createElement("style");
1179
+ style.id = "yh-responsive-vars";
1180
+ document.head.appendChild(style);
1181
+ }
1182
+ let css = "";
1183
+ const orderedBreakpoints = ["xs", "sm", "md", "lg", "xl", "xxl"];
1184
+ orderedBreakpoints.forEach(bp => {
1185
+ if (values[bp]) {
1186
+ if (bp === "xs") {
1187
+ css += `
1188
+ :root { ${name}: ${values[bp]}; }
1189
+ `;
1190
+ } else {
1191
+ css += `
1192
+ @media (min-width: ${breakpoints[bp]}px) {
1193
+ :root { ${name}: ${values[bp]}; }
1194
+ }
1195
+ `;
1196
+ }
1197
+ }
1198
+ });
1199
+ style.textContent += css;
1200
+ }
1201
+ /** 销毁 */
1202
+ destroy() {
1203
+ if (this.resizeHandler && typeof window !== "undefined") {
1204
+ window.removeEventListener("resize", this.resizeHandler);
1205
+ }
1206
+ this.disableSystemFollow();
1207
+ const style = document.getElementById("yh-responsive-vars");
1208
+ if (style) {
1209
+ style.remove();
1210
+ }
1211
+ }
1212
+ }
1213
+ exports.ThemeManager = ThemeManager;
1214
+ let globalThemeManager = null;
1215
+ function useTheme() {
1216
+ if (!globalThemeManager) {
1217
+ globalThemeManager = new ThemeManager();
1218
+ }
1219
+ return globalThemeManager;
1220
+ }
1221
+ function setThemeColor(color) {
1222
+ useTheme().setPrimaryColor(color);
1223
+ }
1224
+ function toggleDarkMode() {
1225
+ return useTheme().toggleDarkMode();
1226
+ }
1227
+ function setThemePreset(preset) {
1228
+ useTheme().setPreset(preset);
1229
+ }
1230
+ function initTheme(options) {
1231
+ globalThemeManager = new ThemeManager(options);
1232
+ return globalThemeManager;
1233
+ }
1234
+ function useThemeVars() {
1235
+ const theme = useTheme();
1236
+ return {
1237
+ ...(0, _vue.toRefs)(theme.state),
1238
+ getCssVar: name => theme.getCssVar(name),
1239
+ setCssVar: (name, value) => theme.setCssVar(name, value)
1240
+ };
1241
+ }
1242
+ function checkContrast(foreground, background, level = "AA") {
1243
+ const ratio = getContrastRatio(foreground, background);
1244
+ return level === "AAA" ? ratio >= 7 : ratio >= 4.5;
1245
+ }
1246
+ function getTextColorForBackground(background) {
1247
+ const rgb = hexToRgb(background);
1248
+ if (!rgb) return "#000000";
1249
+ const luminance = getRelativeLuminance(rgb.r, rgb.g, rgb.b);
1250
+ return luminance > 0.5 ? "#000000" : "#ffffff";
1251
+ }
1252
+ const THEME_INJECTION_KEY = exports.THEME_INJECTION_KEY = Symbol("theme");
1253
+ const ThemePlugin = exports.ThemePlugin = {
1254
+ install(app, options) {
1255
+ const themeManager = initTheme(options);
1256
+ app.config.globalProperties.$theme = themeManager;
1257
+ app.provide(THEME_INJECTION_KEY, themeManager);
1258
+ app.provide("theme", themeManager);
1259
+ }
1260
+ };
1261
+ module.exports = ThemePlugin;