@space-man/react-theme-animation 2.3.0 → 3.0.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/index.cjs DELETED
@@ -1,1756 +0,0 @@
1
- 'use strict';
2
-
3
- var React4 = require('react');
4
- var reactDom = require('react-dom');
5
- var react = require('motion/react');
6
- var clsx = require('clsx');
7
- var jsxRuntime = require('react/jsx-runtime');
8
- var SelectPrimitive = require('@radix-ui/react-select');
9
-
10
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
-
12
- function _interopNamespace(e) {
13
- if (e && e.__esModule) return e;
14
- var n = Object.create(null);
15
- if (e) {
16
- Object.keys(e).forEach(function (k) {
17
- if (k !== 'default') {
18
- var d = Object.getOwnPropertyDescriptor(e, k);
19
- Object.defineProperty(n, k, d.get ? d : {
20
- enumerable: true,
21
- get: function () { return e[k]; }
22
- });
23
- }
24
- });
25
- }
26
- n.default = e;
27
- return Object.freeze(n);
28
- }
29
-
30
- var React4__default = /*#__PURE__*/_interopDefault(React4);
31
- var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
32
-
33
- // src/react/hooks/use-theme-animation.ts
34
-
35
- // src/core/types/index.ts
36
- var ThemeAnimationType = /* @__PURE__ */ ((ThemeAnimationType2) => {
37
- ThemeAnimationType2["CIRCLE"] = "circle";
38
- ThemeAnimationType2["BLUR_CIRCLE"] = "blur-circle";
39
- ThemeAnimationType2["SLIDE"] = "slide";
40
- return ThemeAnimationType2;
41
- })(ThemeAnimationType || {});
42
-
43
- // src/core/utils/animations.ts
44
- var isBrowser = typeof window !== "undefined";
45
- var injectBaseStyles = () => {
46
- if (isBrowser) {
47
- const styleId = "spaceman-theme-base-style";
48
- if (!document.getElementById(styleId)) {
49
- const style = document.createElement("style");
50
- style.id = styleId;
51
- const isHighResolution = window.innerWidth >= 3e3 || window.innerHeight >= 2e3;
52
- style.textContent = `
53
- ::view-transition-old(root),
54
- ::view-transition-new(root) {
55
- animation: none;
56
- mix-blend-mode: normal;
57
- ${isHighResolution ? "transform: translateZ(0);" : ""}
58
- }
59
-
60
- ${isHighResolution ? `
61
- ::view-transition-group(root),
62
- ::view-transition-image-pair(root),
63
- ::view-transition-old(root),
64
- ::view-transition-new(root) {
65
- backface-visibility: hidden;
66
- perspective: 1000px;
67
- transform: translate3d(0, 0, 0);
68
- }
69
- ` : ""}
70
- `;
71
- document.head.appendChild(style);
72
- }
73
- }
74
- };
75
- var createBlurCircleMask = (blur) => {
76
- const isHighResolution = isBrowser && (window.innerWidth >= 3e3 || window.innerHeight >= 2e3);
77
- const blurFilter = `<filter id="blur"><feGaussianBlur stdDeviation="${blur}" /></filter>`;
78
- const circleRadius = isHighResolution ? 20 : 25;
79
- return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -50 100 100"><defs>${blurFilter}</defs><circle cx="0" cy="0" r="${circleRadius}" fill="white" filter="url(%23blur)"/></svg>')`;
80
- };
81
- var getSystemTheme = () => {
82
- if (!isBrowser) return "light";
83
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
84
- };
85
- var resolveTheme = (theme) => {
86
- if (theme === "system") {
87
- return getSystemTheme();
88
- }
89
- return theme === "dark" ? "dark" : "light";
90
- };
91
- var resolveThemeForServer = (theme) => {
92
- if (theme === "system") {
93
- return "system";
94
- }
95
- return theme === "dark" ? "dark" : "light";
96
- };
97
- var supportsViewTransitions = () => {
98
- return isBrowser && "startViewTransition" in document;
99
- };
100
- var prefersReducedMotion = () => {
101
- return isBrowser && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
102
- };
103
- var createCircleAnimation = (config) => {
104
- const { x, y, duration, easing } = config;
105
- const topLeft = Math.hypot(x, y);
106
- const topRight = Math.hypot(window.innerWidth - x, y);
107
- const bottomLeft = Math.hypot(x, window.innerHeight - y);
108
- const bottomRight = Math.hypot(window.innerWidth - x, window.innerHeight - y);
109
- const maxRadius = Math.max(topLeft, topRight, bottomLeft, bottomRight);
110
- document.documentElement.animate(
111
- {
112
- clipPath: [
113
- `circle(0px at ${x}px ${y}px)`,
114
- `circle(${maxRadius}px at ${x}px ${y}px)`
115
- ]
116
- },
117
- {
118
- duration,
119
- easing,
120
- pseudoElement: "::view-transition-new(root)"
121
- }
122
- );
123
- };
124
- var createSlideAnimation = (config) => {
125
- const { a = -100, b = 0, x = 0, y = 0, duration, styleId } = config;
126
- const styleElement = document.createElement("style");
127
- styleElement.id = styleId;
128
- styleElement.textContent = `
129
- ::view-transition-old(root) {
130
- animation-delay: ${duration}ms;
131
- }
132
-
133
- ::view-transition-new(root) {
134
- animation: move-in ${duration}ms;
135
- animation-timing-function: cubic-bezier(0.66, 0, 0.34, 1);
136
- }
137
-
138
- @keyframes move-in {
139
- from {
140
- translate: ${a}% ${b}%;
141
- }
142
- to {
143
- translate: ${x}% ${y}%;
144
- }
145
- }
146
- `;
147
- document.head.appendChild(styleElement);
148
- setTimeout(() => {
149
- const styleElement2 = document.getElementById(styleId);
150
- if (styleElement2) {
151
- styleElement2.remove();
152
- }
153
- }, duration);
154
- };
155
- var createBlurCircleAnimation = (config) => {
156
- const { x, y, duration, easing, blurAmount, styleId } = config;
157
- const existingStyle = document.getElementById(styleId);
158
- if (existingStyle) {
159
- existingStyle.remove();
160
- }
161
- const viewportSize = Math.max(window.innerWidth, window.innerHeight) + 200;
162
- const isHighResolution = window.innerWidth >= 3e3 || window.innerHeight >= 2e3;
163
- const scaleFactor = isHighResolution ? 2.5 : 4;
164
- const optimalMaskSize = isHighResolution ? Math.min(viewportSize * scaleFactor, 5e3) : viewportSize * scaleFactor;
165
- const topLeft = Math.hypot(x, y);
166
- const topRight = Math.hypot(window.innerWidth - x, y);
167
- const bottomLeft = Math.hypot(x, window.innerHeight - y);
168
- const bottomRight = Math.hypot(window.innerWidth - x, window.innerHeight - y);
169
- const maxRadius = Math.max(topLeft, topRight, bottomLeft, bottomRight);
170
- const styleElement = document.createElement("style");
171
- styleElement.id = styleId;
172
- const blurFactor = isHighResolution ? 1.5 : 1.2;
173
- const finalMaskSize = Math.max(optimalMaskSize, maxRadius * 2.5);
174
- styleElement.textContent = `
175
- ::view-transition-group(root) {
176
- animation-duration: ${duration}ms;
177
- animation-timing-function: ${isHighResolution ? "cubic-bezier(0.2, 0, 0.2, 1)" : "linear(0 0%, 0.2342 12.49%, 0.4374 24.99%,0.6093 37.49%, 0.6835 43.74%,0.7499 49.99%, 0.8086 56.25%,0.8593 62.5%, 0.9023 68.75%, 0.9375 75%,0.9648 81.25%, 0.9844 87.5%,0.9961 93.75%, 1 100%)"};
178
- will-change: transform;
179
- }
180
-
181
- ::view-transition-new(root) {
182
- mask: ${createBlurCircleMask(blurAmount * blurFactor)} 0 0 / 100% 100% no-repeat;
183
- mask-position: ${x}px ${y}px;
184
- animation: spaceman-mask-scale ${duration}ms ${easing};
185
- transform-origin: ${x}px ${y}px;
186
- will-change: mask-size, mask-position;
187
- }
188
-
189
- ::view-transition-old(root),
190
- .dark::view-transition-old(root) {
191
- animation: spaceman-mask-scale ${duration}ms ${easing};
192
- transform-origin: ${x}px ${y}px;
193
- z-index: -1;
194
- will-change: mask-size, mask-position;
195
- }
196
-
197
- @keyframes spaceman-mask-scale {
198
- 0% {
199
- mask-size: 0px;
200
- mask-position: ${x}px ${y}px;
201
- }
202
- 100% {
203
- mask-size: ${finalMaskSize}px;
204
- mask-position: ${x - finalMaskSize / 2}px ${y - finalMaskSize / 2}px;
205
- }
206
- }
207
- `;
208
- document.head.appendChild(styleElement);
209
- setTimeout(() => {
210
- const styleElement2 = document.getElementById(styleId);
211
- if (styleElement2) {
212
- styleElement2.remove();
213
- }
214
- }, duration);
215
- };
216
-
217
- // src/react/hooks/use-theme-animation.ts
218
- var isBrowser2 = typeof window !== "undefined";
219
- var useThemeAnimation = (props = {}) => {
220
- const {
221
- duration: propsDuration = 750,
222
- easing = "ease-in-out",
223
- animationType = "circle" /* CIRCLE */,
224
- blurAmount = 2,
225
- styleId = "spaceman-theme-style",
226
- themes = ["light", "dark", "system"],
227
- colorThemes = ["default"],
228
- defaultTheme = "system",
229
- defaultColorTheme = "default",
230
- globalClassName = "dark",
231
- colorThemePrefix = "theme-",
232
- attribute = "class",
233
- value,
234
- enableColorScheme = true,
235
- storageKey = "theme",
236
- colorStorageKey = "color-theme",
237
- theme: externalTheme,
238
- colorTheme: externalColorTheme,
239
- onThemeChange,
240
- onColorThemeChange,
241
- initialTheme,
242
- initialColorTheme,
243
- systemThemeMode = "js",
244
- // Slide animation options
245
- slideDirection = "left",
246
- slideFromX,
247
- slideFromY,
248
- slideToX = 0,
249
- slideToY = 0
250
- } = props;
251
- const isHighResolution = isBrowser2 && (window.innerWidth >= 3e3 || window.innerHeight >= 2e3);
252
- const duration = isHighResolution ? Math.max(propsDuration * 0.8, 500) : propsDuration;
253
- const [mounted, setMounted] = React4.useState(false);
254
- React4.useEffect(() => {
255
- injectBaseStyles();
256
- setMounted(true);
257
- }, []);
258
- const [internalTheme, setInternalTheme] = React4.useState(() => {
259
- if (initialTheme !== void 0) return initialTheme;
260
- if (!isBrowser2) return defaultTheme;
261
- const saved = localStorage.getItem(storageKey);
262
- return saved && themes.indexOf(saved) !== -1 ? saved : defaultTheme;
263
- });
264
- const [internalColorTheme, setInternalColorTheme] = React4.useState(() => {
265
- if (initialColorTheme !== void 0) return initialColorTheme;
266
- if (!isBrowser2) return defaultColorTheme;
267
- const saved = localStorage.getItem(colorStorageKey);
268
- return saved && colorThemes.indexOf(saved) !== -1 ? saved : defaultColorTheme;
269
- });
270
- const currentTheme = externalTheme ?? internalTheme;
271
- const currentColorTheme = externalColorTheme ?? internalColorTheme;
272
- const [, setSystemTheme] = React4.useState(() => getSystemTheme());
273
- const systemTheme = getSystemTheme();
274
- const resolvedTheme = resolveTheme(currentTheme);
275
- React4.useEffect(() => {
276
- if (!isBrowser2) return;
277
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
278
- const handleChange = () => setSystemTheme(getSystemTheme());
279
- mediaQuery.addEventListener("change", handleChange);
280
- return () => mediaQuery.removeEventListener("change", handleChange);
281
- }, []);
282
- React4.useEffect(() => {
283
- if (!isBrowser2 || !mounted) return;
284
- const element = document.documentElement;
285
- const lightValue = value?.light;
286
- const darkValue = value?.dark ?? globalClassName;
287
- const systemValue = value?.system ?? "system";
288
- if (attribute === "data-theme") {
289
- if (systemThemeMode === "css" && currentTheme === "system") {
290
- element.setAttribute("data-theme", systemValue);
291
- } else {
292
- element.setAttribute(
293
- "data-theme",
294
- resolvedTheme === "dark" ? darkValue : lightValue ?? "light"
295
- );
296
- }
297
- } else {
298
- if (lightValue) {
299
- element.classList.remove(lightValue);
300
- }
301
- element.classList.remove(darkValue, systemValue, "auto");
302
- if (systemThemeMode === "css" && currentTheme === "system") {
303
- element.classList.add(systemValue);
304
- } else if (resolvedTheme === "dark") {
305
- element.classList.add(darkValue);
306
- } else if (lightValue) {
307
- element.classList.add(lightValue);
308
- } else {
309
- element.classList.remove(darkValue);
310
- }
311
- }
312
- if (enableColorScheme) {
313
- element.style.colorScheme = systemThemeMode === "css" && currentTheme === "system" ? "" : resolvedTheme;
314
- } else {
315
- element.style.removeProperty("color-scheme");
316
- }
317
- colorThemes.forEach((theme) => {
318
- element.classList.remove(`${colorThemePrefix}${theme}`);
319
- });
320
- element.classList.add(`${colorThemePrefix}${currentColorTheme}`);
321
- }, [
322
- resolvedTheme,
323
- currentTheme,
324
- currentColorTheme,
325
- globalClassName,
326
- colorThemePrefix,
327
- colorThemes,
328
- attribute,
329
- value,
330
- enableColorScheme,
331
- mounted,
332
- systemThemeMode
333
- ]);
334
- const ref = React4.useRef(null);
335
- const setTheme = React4.useCallback(
336
- (newTheme) => {
337
- if (isBrowser2) {
338
- localStorage.setItem(storageKey, newTheme);
339
- }
340
- if (externalTheme === void 0) {
341
- setInternalTheme(newTheme);
342
- }
343
- if (onThemeChange) {
344
- onThemeChange(newTheme);
345
- }
346
- },
347
- [onThemeChange, externalTheme, storageKey]
348
- );
349
- const setColorTheme = React4.useCallback(
350
- (newColorTheme) => {
351
- if (isBrowser2) {
352
- localStorage.setItem(colorStorageKey, newColorTheme);
353
- }
354
- if (externalColorTheme === void 0) {
355
- setInternalColorTheme(newColorTheme);
356
- }
357
- if (onColorThemeChange) {
358
- onColorThemeChange(newColorTheme);
359
- }
360
- },
361
- [onColorThemeChange, externalColorTheme, colorStorageKey]
362
- );
363
- const switchTheme = React4.useCallback(
364
- async (newTheme, animationOff = false) => {
365
- if (!ref.current || !supportsViewTransitions() || prefersReducedMotion() || animationOff) {
366
- setTheme(newTheme);
367
- return;
368
- }
369
- const getSlideFromCoords = (direction) => {
370
- switch (direction) {
371
- case "left":
372
- return { a: -100, b: 0 };
373
- case "right":
374
- return { a: 100, b: 0 };
375
- case "top":
376
- return { a: 0, b: -100 };
377
- case "bottom":
378
- return { a: 0, b: 100 };
379
- case "top-left":
380
- return { a: -100, b: -100 };
381
- case "top-right":
382
- return { a: 100, b: -100 };
383
- case "bottom-left":
384
- return { a: -100, b: 100 };
385
- case "bottom-right":
386
- return { a: 100, b: 100 };
387
- default:
388
- return { a: -100, b: 0 };
389
- }
390
- };
391
- let animationConfig;
392
- if (animationType === "slide" /* SLIDE */) {
393
- const fromCoords = slideFromX !== void 0 && slideFromY !== void 0 ? { a: slideFromX, b: slideFromY } : getSlideFromCoords(slideDirection);
394
- animationConfig = {
395
- a: fromCoords.a,
396
- b: fromCoords.b,
397
- x: slideToX,
398
- y: slideToY,
399
- duration,
400
- easing,
401
- animationType,
402
- blurAmount,
403
- styleId
404
- };
405
- } else {
406
- const { top, left, width, height } = ref.current.getBoundingClientRect();
407
- const x = left + width / 2;
408
- const y = top + height / 2;
409
- animationConfig = {
410
- x,
411
- y,
412
- duration,
413
- easing,
414
- animationType,
415
- blurAmount,
416
- styleId
417
- };
418
- }
419
- if (animationType === "blur-circle" /* BLUR_CIRCLE */) {
420
- createBlurCircleAnimation(animationConfig);
421
- }
422
- if (animationType === "slide" /* SLIDE */) {
423
- createSlideAnimation(animationConfig);
424
- }
425
- await document.startViewTransition(() => {
426
- reactDom.flushSync(() => {
427
- setTheme(newTheme);
428
- });
429
- }).ready;
430
- if (animationType === "circle" /* CIRCLE */) {
431
- createCircleAnimation(animationConfig);
432
- }
433
- },
434
- [
435
- setTheme,
436
- duration,
437
- easing,
438
- animationType,
439
- blurAmount,
440
- styleId,
441
- slideDirection,
442
- slideFromX,
443
- slideFromY,
444
- slideToX,
445
- slideToY
446
- ]
447
- );
448
- const switchColorTheme = React4.useCallback(
449
- (newColorTheme) => {
450
- if (colorThemes.indexOf(newColorTheme) === -1) {
451
- console.warn(
452
- `Color theme "${newColorTheme}" not found in available themes`
453
- );
454
- return;
455
- }
456
- setColorTheme(newColorTheme);
457
- },
458
- [colorThemes, setColorTheme]
459
- );
460
- const toggleTheme = React4.useCallback(
461
- async (animationOff = false) => {
462
- const newTheme = resolvedTheme === "dark" ? "light" : "dark";
463
- await switchTheme(newTheme, animationOff);
464
- },
465
- [resolvedTheme, switchTheme]
466
- );
467
- const toggleLightTheme = React4.useCallback(
468
- async (animationOff = false) => {
469
- if (resolvedTheme === "light") return;
470
- await switchTheme("light", animationOff);
471
- },
472
- [resolvedTheme, switchTheme]
473
- );
474
- const toggleDarkTheme = React4.useCallback(
475
- async (animationOff = false) => {
476
- if (resolvedTheme === "dark") return;
477
- await switchTheme("dark", animationOff);
478
- },
479
- [resolvedTheme, switchTheme]
480
- );
481
- const toggleColorTheme = React4.useCallback(() => {
482
- const currentIndex = colorThemes.indexOf(currentColorTheme);
483
- const nextIndex = (currentIndex + 1) % colorThemes.length;
484
- const nextColorTheme = colorThemes[nextIndex];
485
- setColorTheme(nextColorTheme);
486
- }, [currentColorTheme, colorThemes, setColorTheme]);
487
- const createColorThemeToggle = React4.useCallback(
488
- (targetColorTheme) => {
489
- return () => {
490
- if (colorThemes.indexOf(targetColorTheme) === -1) {
491
- console.warn(
492
- `Color theme "${targetColorTheme}" not found in available themes`
493
- );
494
- return;
495
- }
496
- setColorTheme(targetColorTheme);
497
- };
498
- },
499
- [colorThemes, setColorTheme]
500
- );
501
- const isColorThemeActive = React4.useCallback(
502
- (targetColorTheme) => {
503
- return currentColorTheme === targetColorTheme;
504
- },
505
- [currentColorTheme]
506
- );
507
- return {
508
- ref,
509
- theme: currentTheme,
510
- colorTheme: currentColorTheme,
511
- resolvedTheme,
512
- systemTheme,
513
- setTheme,
514
- setColorTheme,
515
- switchTheme,
516
- switchColorTheme,
517
- toggleTheme,
518
- toggleLightTheme,
519
- toggleDarkTheme,
520
- toggleColorTheme,
521
- createColorThemeToggle,
522
- isColorThemeActive
523
- };
524
- };
525
- var SharedThemeContext = React4.createContext(
526
- null
527
- );
528
- var useSharedThemeContext = () => {
529
- return React4.useContext(SharedThemeContext);
530
- };
531
- var SunIcon = () => /* @__PURE__ */ jsxRuntime.jsxs(
532
- "svg",
533
- {
534
- width: "16",
535
- height: "16",
536
- viewBox: "0 0 24 24",
537
- fill: "none",
538
- stroke: "currentColor",
539
- strokeWidth: "2",
540
- strokeLinecap: "round",
541
- strokeLinejoin: "round",
542
- children: [
543
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "5" }),
544
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" })
545
- ]
546
- }
547
- );
548
- var MoonIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
549
- "svg",
550
- {
551
- width: "16",
552
- height: "16",
553
- viewBox: "0 0 24 24",
554
- fill: "none",
555
- stroke: "currentColor",
556
- strokeWidth: "2",
557
- strokeLinecap: "round",
558
- strokeLinejoin: "round",
559
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
560
- }
561
- );
562
- var MonitorIcon = () => /* @__PURE__ */ jsxRuntime.jsxs(
563
- "svg",
564
- {
565
- width: "16",
566
- height: "16",
567
- viewBox: "0 0 24 24",
568
- fill: "none",
569
- stroke: "currentColor",
570
- strokeWidth: "2",
571
- strokeLinecap: "round",
572
- strokeLinejoin: "round",
573
- children: [
574
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "20", height: "14", x: "2", y: "3", rx: "2" }),
575
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", x2: "16", y1: "21", y2: "21" }),
576
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "17", y2: "21" })
577
- ]
578
- }
579
- );
580
- var ThemeOption = ({
581
- icon,
582
- value,
583
- isActive,
584
- isHovered,
585
- onClick,
586
- onMouseEnter,
587
- onMouseLeave,
588
- buttonRef
589
- }) => {
590
- return /* @__PURE__ */ jsxRuntime.jsxs(
591
- "button",
592
- {
593
- ref: isActive ? buttonRef : void 0,
594
- className: clsx.clsx(
595
- "relative flex h-9 w-12 cursor-pointer items-center justify-center",
596
- "text-muted-foreground hover:text-foreground",
597
- isActive && "text-foreground font-medium"
598
- ),
599
- role: "radio",
600
- "aria-checked": isActive,
601
- "aria-label": `Switch to ${value} theme`,
602
- onClick: (event) => onClick(value, event),
603
- onMouseEnter,
604
- onMouseLeave,
605
- style: {
606
- borderRadius: "var(--radius)"
607
- },
608
- children: [
609
- isHovered && /* @__PURE__ */ jsxRuntime.jsx(
610
- react.motion.div,
611
- {
612
- layoutId: "theme-hover",
613
- className: "absolute inset-0 bg-muted",
614
- style: {
615
- borderRadius: "var(--radius)"
616
- },
617
- transition: {
618
- type: "spring",
619
- bounce: 0,
620
- stiffness: 100,
621
- damping: 10,
622
- duration: 0.3
623
- }
624
- }
625
- ),
626
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-10 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "[&_svg]:size-4", children: icon }) }),
627
- isActive && /* @__PURE__ */ jsxRuntime.jsx(
628
- "div",
629
- {
630
- className: "absolute bottom-0 left-1/2 h-0.5 w-6 -translate-x-1/2 rounded-full",
631
- style: {
632
- backgroundColor: "hsl(var(--primary))"
633
- }
634
- }
635
- )
636
- ]
637
- }
638
- );
639
- };
640
- var THEME_OPTIONS = [
641
- {
642
- icon: /* @__PURE__ */ jsxRuntime.jsx(MonitorIcon, {}),
643
- value: "system"
644
- },
645
- {
646
- icon: /* @__PURE__ */ jsxRuntime.jsx(SunIcon, {}),
647
- value: "light"
648
- },
649
- {
650
- icon: /* @__PURE__ */ jsxRuntime.jsx(MoonIcon, {}),
651
- value: "dark"
652
- }
653
- ];
654
- var ThemeSwitcherView = ({
655
- className,
656
- onSwitchTheme,
657
- theme,
658
- themes,
659
- themeRef
660
- }) => {
661
- const [isMounted, setIsMounted] = React4.useState(false);
662
- const [hoveredTheme, setHoveredTheme] = React4.useState(null);
663
- React4.useEffect(() => {
664
- setIsMounted(true);
665
- }, []);
666
- const handleThemeChange = async (newTheme, event) => {
667
- await onSwitchTheme(newTheme, event);
668
- };
669
- const filteredOptions = THEME_OPTIONS.filter(
670
- (option) => themes.includes(option.value)
671
- );
672
- return /* @__PURE__ */ jsxRuntime.jsx(
673
- "div",
674
- {
675
- className: clsx.clsx(
676
- "inline-flex h-9 items-center overflow-hidden bg-background shadow-sm border border-border",
677
- isMounted ? "opacity-100" : "opacity-0",
678
- className
679
- ),
680
- style: {
681
- borderRadius: "var(--radius)",
682
- backgroundColor: "hsl(var(--background))"
683
- },
684
- role: "radiogroup",
685
- onMouseLeave: () => setHoveredTheme(null),
686
- children: filteredOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
687
- ThemeOption,
688
- {
689
- icon: option.icon,
690
- value: option.value,
691
- isActive: theme === option.value,
692
- isHovered: hoveredTheme === option.value,
693
- onClick: handleThemeChange,
694
- onMouseEnter: () => setHoveredTheme(option.value),
695
- onMouseLeave: () => {
696
- },
697
- buttonRef: theme === option.value ? themeRef : void 0
698
- },
699
- option.value
700
- ))
701
- }
702
- );
703
- };
704
- var ThemeSwitcherWithContext = ({ className, contextTheme, themes = ["light", "dark", "system"] }) => {
705
- const handleSwitchTheme = async (theme, event) => {
706
- if (contextTheme.switchThemeFromElement && event) {
707
- await contextTheme.switchThemeFromElement(theme, event.currentTarget);
708
- return;
709
- }
710
- await contextTheme.switchTheme(theme);
711
- };
712
- return /* @__PURE__ */ jsxRuntime.jsx(
713
- ThemeSwitcherView,
714
- {
715
- className,
716
- onSwitchTheme: handleSwitchTheme,
717
- theme: contextTheme.theme,
718
- themes,
719
- themeRef: contextTheme.ref
720
- }
721
- );
722
- };
723
- var ThemeSwitcherStandalone = ({
724
- themes = ["light", "dark", "system"],
725
- currentTheme,
726
- onThemeChange,
727
- animationType,
728
- duration,
729
- className
730
- }) => {
731
- const standaloneHook = useThemeAnimation({
732
- animationType,
733
- duration,
734
- themes,
735
- ...currentTheme !== void 0 && { theme: currentTheme },
736
- onThemeChange
737
- });
738
- const handleSwitchTheme = async (theme) => {
739
- await standaloneHook.switchTheme(theme);
740
- };
741
- return /* @__PURE__ */ jsxRuntime.jsx(
742
- ThemeSwitcherView,
743
- {
744
- className,
745
- onSwitchTheme: handleSwitchTheme,
746
- theme: standaloneHook.theme,
747
- themes,
748
- themeRef: standaloneHook.ref
749
- }
750
- );
751
- };
752
- var ThemeSwitcher = (props) => {
753
- const contextTheme = useSharedThemeContext();
754
- if (contextTheme) {
755
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeSwitcherWithContext, { ...props, contextTheme });
756
- }
757
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeSwitcherStandalone, { ...props });
758
- };
759
- function Select({
760
- ...props
761
- }) {
762
- return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Root, { "data-slot": "select", ...props });
763
- }
764
- function SelectValue({
765
- ...props
766
- }) {
767
- return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Value, { "data-slot": "select-value", ...props });
768
- }
769
- var ChevronDownIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
770
- "svg",
771
- {
772
- width: "16",
773
- height: "16",
774
- viewBox: "0 0 24 24",
775
- fill: "none",
776
- stroke: "currentColor",
777
- strokeWidth: "2",
778
- strokeLinecap: "round",
779
- strokeLinejoin: "round",
780
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" })
781
- }
782
- );
783
- var CheckIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
784
- "svg",
785
- {
786
- width: "16",
787
- height: "16",
788
- viewBox: "0 0 24 24",
789
- fill: "none",
790
- stroke: "currentColor",
791
- strokeWidth: "2",
792
- strokeLinecap: "round",
793
- strokeLinejoin: "round",
794
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6 9 17l-5-5" })
795
- }
796
- );
797
- function SelectTrigger({
798
- className,
799
- size = "default",
800
- children,
801
- ...props
802
- }) {
803
- return /* @__PURE__ */ jsxRuntime.jsxs(
804
- SelectPrimitive__namespace.Trigger,
805
- {
806
- "data-slot": "select-trigger",
807
- "data-size": size,
808
- className: clsx.clsx(
809
- "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
810
- className
811
- ),
812
- ...props,
813
- children: [
814
- children,
815
- /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, {}) })
816
- ]
817
- }
818
- );
819
- }
820
- function SelectContent({
821
- className,
822
- children,
823
- position = "popper",
824
- ...props
825
- }) {
826
- return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
827
- SelectPrimitive__namespace.Content,
828
- {
829
- "data-slot": "select-content",
830
- className: clsx.clsx(
831
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
832
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
833
- className
834
- ),
835
- position,
836
- ...props,
837
- children: /* @__PURE__ */ jsxRuntime.jsx(
838
- SelectPrimitive__namespace.Viewport,
839
- {
840
- className: clsx.clsx(
841
- "p-1",
842
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
843
- ),
844
- children
845
- }
846
- )
847
- }
848
- ) });
849
- }
850
- function SelectItem({
851
- className,
852
- children,
853
- ...props
854
- }) {
855
- return /* @__PURE__ */ jsxRuntime.jsxs(
856
- SelectPrimitive__namespace.Item,
857
- {
858
- "data-slot": "select-item",
859
- className: clsx.clsx(
860
- "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
861
- className
862
- ),
863
- ...props,
864
- children: [
865
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) }) }),
866
- /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
867
- ]
868
- }
869
- );
870
- }
871
- var ThemeSelectorView = ({
872
- colorTheme,
873
- colorThemes,
874
- onSelectColorTheme
875
- }) => {
876
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: colorThemes.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
877
- Select,
878
- {
879
- value: colorTheme,
880
- onValueChange: (value) => onSelectColorTheme(value),
881
- children: [
882
- /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "capitalize", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Choose a color theme" }) }),
883
- /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: colorThemes.map((theme) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { className: "capitalize", value: theme, children: theme }, theme)) })
884
- ]
885
- }
886
- ) }) : null });
887
- };
888
- var ThemeSelectorWithContext = ({ colorThemes = ["default"], contextTheme }) => {
889
- return /* @__PURE__ */ jsxRuntime.jsx(
890
- ThemeSelectorView,
891
- {
892
- colorTheme: contextTheme.colorTheme,
893
- colorThemes,
894
- onSelectColorTheme: contextTheme.setColorTheme
895
- }
896
- );
897
- };
898
- var ThemeSelectorStandalone = ({
899
- themes = ["light", "dark", "system"],
900
- colorThemes = ["default"],
901
- currentColorTheme,
902
- onColorThemeChange,
903
- animationType,
904
- duration
905
- }) => {
906
- const standaloneHook = useThemeAnimation({
907
- animationType,
908
- duration,
909
- themes,
910
- colorThemes,
911
- ...currentColorTheme !== void 0 && { colorTheme: currentColorTheme },
912
- onColorThemeChange
913
- });
914
- return /* @__PURE__ */ jsxRuntime.jsx(
915
- ThemeSelectorView,
916
- {
917
- colorTheme: standaloneHook.colorTheme,
918
- colorThemes,
919
- onSelectColorTheme: standaloneHook.setColorTheme
920
- }
921
- );
922
- };
923
- var ThemeSelector = (props) => {
924
- const contextTheme = useSharedThemeContext();
925
- if (contextTheme) {
926
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeSelectorWithContext, { ...props, contextTheme });
927
- }
928
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeSelectorStandalone, { ...props });
929
- };
930
-
931
- // src/react/components/provider-helpers.ts
932
- var withElementAsRef = async (ref, element, run) => {
933
- if (ref.current) {
934
- const originalRef = ref.current;
935
- Object.defineProperty(ref, "current", {
936
- value: element,
937
- writable: true,
938
- configurable: true
939
- });
940
- await run();
941
- Object.defineProperty(ref, "current", {
942
- value: originalRef,
943
- writable: true,
944
- configurable: true
945
- });
946
- return;
947
- }
948
- Object.defineProperty(ref, "current", {
949
- value: element,
950
- writable: true,
951
- configurable: true
952
- });
953
- await run();
954
- };
955
- var getBrowserSystemTheme = () => {
956
- return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
957
- };
958
- var getNextResolvedTheme = (resolvedTheme) => {
959
- return resolvedTheme === "dark" ? "light" : "dark";
960
- };
961
- var SpacemanThemeContext = React4.createContext(void 0);
962
- var SpacemanThemeProvider = ({
963
- children,
964
- themes = ["light", "dark", "system"],
965
- colorThemes = ["default"],
966
- defaultTheme = "system",
967
- defaultColorTheme = "default",
968
- animationType = "circle" /* CIRCLE */,
969
- duration = 750
970
- }) => {
971
- const [mounted, setMounted] = React4.useState(false);
972
- const themeState = useThemeAnimation({
973
- themes,
974
- colorThemes,
975
- defaultTheme,
976
- defaultColorTheme,
977
- animationType,
978
- duration
979
- });
980
- React4.useEffect(() => {
981
- setMounted(true);
982
- }, []);
983
- const switchThemeFromElement = async (theme, element) => {
984
- await withElementAsRef(themeState.ref, element, async () => {
985
- await themeState.switchTheme(theme);
986
- });
987
- };
988
- if (!mounted) {
989
- const loadingContextValue = {
990
- ref: { current: null },
991
- theme: defaultTheme,
992
- colorTheme: defaultColorTheme,
993
- resolvedTheme: defaultTheme === "dark" ? "dark" : "light",
994
- setTheme: () => {
995
- },
996
- setColorTheme: () => {
997
- },
998
- switchTheme: async () => {
999
- },
1000
- switchThemeFromElement: async () => {
1001
- },
1002
- switchColorTheme: () => {
1003
- },
1004
- toggleTheme: async () => {
1005
- },
1006
- toggleLightTheme: async () => {
1007
- },
1008
- toggleDarkTheme: async () => {
1009
- },
1010
- toggleColorTheme: () => {
1011
- },
1012
- createColorThemeToggle: () => () => {
1013
- },
1014
- isColorThemeActive: () => false
1015
- };
1016
- return /* @__PURE__ */ jsxRuntime.jsx(SpacemanThemeContext.Provider, { value: loadingContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(SharedThemeContext.Provider, { value: loadingContextValue, children }) });
1017
- }
1018
- const contextValue = {
1019
- ref: themeState.ref,
1020
- theme: themeState.theme,
1021
- colorTheme: themeState.colorTheme,
1022
- resolvedTheme: themeState.resolvedTheme,
1023
- setTheme: themeState.setTheme,
1024
- setColorTheme: themeState.setColorTheme,
1025
- switchTheme: themeState.switchTheme,
1026
- switchThemeFromElement,
1027
- switchColorTheme: themeState.switchColorTheme,
1028
- toggleTheme: themeState.toggleTheme,
1029
- toggleLightTheme: themeState.toggleLightTheme,
1030
- toggleDarkTheme: themeState.toggleDarkTheme,
1031
- toggleColorTheme: themeState.toggleColorTheme,
1032
- createColorThemeToggle: themeState.createColorThemeToggle,
1033
- isColorThemeActive: themeState.isColorThemeActive
1034
- };
1035
- return /* @__PURE__ */ jsxRuntime.jsx(SpacemanThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(SharedThemeContext.Provider, { value: contextValue, children }) });
1036
- };
1037
- var useSpacemanTheme = () => {
1038
- const context = React4.useContext(SpacemanThemeContext);
1039
- if (context === void 0) {
1040
- throw new Error(
1041
- "useSpacemanTheme must be used within a SpacemanThemeProvider"
1042
- );
1043
- }
1044
- return context;
1045
- };
1046
- var NextThemeContext = React4.createContext(
1047
- void 0
1048
- );
1049
- var createDisableTransitions = () => {
1050
- const css = document.createElement("style");
1051
- css.textContent = "*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}";
1052
- document.head.appendChild(css);
1053
- window.getComputedStyle(document.body);
1054
- return () => {
1055
- setTimeout(() => {
1056
- if (css.parentNode) {
1057
- css.parentNode.removeChild(css);
1058
- }
1059
- }, 1);
1060
- };
1061
- };
1062
- var generatePreHydrationScript = ({
1063
- attribute,
1064
- colorStorageKey,
1065
- colorThemePrefix,
1066
- defaultColorTheme,
1067
- defaultTheme,
1068
- enableColorScheme,
1069
- enableSystem,
1070
- globalClassName,
1071
- storageKey,
1072
- value
1073
- }) => {
1074
- const lightValue = value?.light ?? "light";
1075
- const darkValue = value?.dark ?? globalClassName;
1076
- const systemValue = value?.system ?? "system";
1077
- return `(function(){try{var d=document.documentElement;var theme=localStorage.getItem('${storageKey}')||'${defaultTheme}';var colorTheme=localStorage.getItem('${colorStorageKey}')||'${defaultColorTheme}';if(!${JSON.stringify(
1078
- enableSystem
1079
- )}&&theme==='system'){theme='${defaultTheme === "system" ? "light" : defaultTheme}';}var resolved=theme==='system'?(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'):theme;var attr='${attribute}';var lightValue='${lightValue}';var darkValue='${darkValue}';var systemValue='${systemValue}';if(attr==='data-theme'){d.setAttribute('data-theme',theme==='system'?systemValue:(resolved==='dark'?darkValue:lightValue));}else{d.classList.remove(lightValue,darkValue,systemValue,'auto');d.classList.add(theme==='system'?systemValue:(resolved==='dark'?darkValue:lightValue));}if(${JSON.stringify(
1080
- enableColorScheme
1081
- )}){d.style.colorScheme=theme==='system'?'':resolved;}else{d.style.removeProperty('color-scheme');}d.classList.add('${colorThemePrefix}'+colorTheme);}catch(e){console.warn('Theme pre-hydration script failed:',e);}})();`;
1082
- };
1083
- var ThemePreHydrationScript = React4__default.default.memo(
1084
- ({
1085
- attribute,
1086
- colorStorageKey,
1087
- colorThemePrefix,
1088
- defaultColorTheme,
1089
- defaultTheme,
1090
- enableColorScheme,
1091
- enableSystem,
1092
- globalClassName,
1093
- nonce,
1094
- scriptProps,
1095
- storageKey,
1096
- value
1097
- }) => /* @__PURE__ */ jsxRuntime.jsx(
1098
- "script",
1099
- {
1100
- ...scriptProps,
1101
- nonce,
1102
- suppressHydrationWarning: true,
1103
- dangerouslySetInnerHTML: {
1104
- __html: generatePreHydrationScript({
1105
- attribute,
1106
- colorStorageKey,
1107
- colorThemePrefix,
1108
- defaultColorTheme,
1109
- defaultTheme,
1110
- enableColorScheme,
1111
- enableSystem,
1112
- globalClassName,
1113
- storageKey,
1114
- value
1115
- })
1116
- }
1117
- }
1118
- )
1119
- );
1120
- ThemePreHydrationScript.displayName = "ThemePreHydrationScript";
1121
- var NextThemeProvider = ({
1122
- attribute = "class",
1123
- children,
1124
- colorStorageKey = "color-theme",
1125
- colorThemePrefix = "theme-",
1126
- colorThemes = ["default"],
1127
- defaultColorTheme = "default",
1128
- defaultTheme = "system",
1129
- disableAnimationOnInit = true,
1130
- disablePreHydrationScript = false,
1131
- disableTransitionOnChange = false,
1132
- duration = 750,
1133
- enableColorScheme = true,
1134
- enableSystem = true,
1135
- forcedTheme,
1136
- globalClassName = "dark",
1137
- nonce,
1138
- onColorThemeChange,
1139
- onThemeChange,
1140
- scriptProps,
1141
- storageKey = "theme",
1142
- themes,
1143
- value,
1144
- animationType = "circle" /* CIRCLE */
1145
- }) => {
1146
- const allowedThemes = React4.useMemo(() => {
1147
- if (themes && themes.length > 0) {
1148
- return themes;
1149
- }
1150
- return enableSystem ? ["light", "dark", "system"] : ["light", "dark"];
1151
- }, [enableSystem, themes]);
1152
- const effectiveDefaultTheme = React4.useMemo(() => {
1153
- if (defaultTheme === "system" && !enableSystem) {
1154
- return "light";
1155
- }
1156
- return defaultTheme;
1157
- }, [defaultTheme, enableSystem]);
1158
- const [mounted, setMounted] = React4.useState(false);
1159
- const themeState = useThemeAnimation({
1160
- animationType,
1161
- attribute,
1162
- colorStorageKey,
1163
- colorTheme: forcedTheme ? void 0 : void 0,
1164
- colorThemePrefix,
1165
- colorThemes,
1166
- defaultColorTheme,
1167
- defaultTheme: effectiveDefaultTheme,
1168
- duration,
1169
- enableColorScheme,
1170
- globalClassName,
1171
- onColorThemeChange,
1172
- onThemeChange,
1173
- storageKey,
1174
- theme: forcedTheme,
1175
- themes: allowedThemes,
1176
- value
1177
- });
1178
- React4.useEffect(() => {
1179
- setMounted(true);
1180
- }, []);
1181
- const withTransitionsDisabled = React4.useCallback(
1182
- async (fn) => {
1183
- const cleanup = disableTransitionOnChange ? createDisableTransitions() : void 0;
1184
- try {
1185
- return await fn();
1186
- } finally {
1187
- cleanup?.();
1188
- }
1189
- },
1190
- [disableTransitionOnChange]
1191
- );
1192
- const setTheme = React4.useCallback(
1193
- (theme) => {
1194
- void withTransitionsDisabled(async () => {
1195
- themeState.setTheme(theme);
1196
- });
1197
- },
1198
- [themeState, withTransitionsDisabled]
1199
- );
1200
- const switchTheme = React4.useCallback(
1201
- async (theme, animationOff = false) => {
1202
- await withTransitionsDisabled(async () => {
1203
- if (!mounted && disableAnimationOnInit) {
1204
- themeState.setTheme(theme);
1205
- return;
1206
- }
1207
- await themeState.switchTheme(theme, animationOff);
1208
- });
1209
- },
1210
- [disableAnimationOnInit, mounted, themeState, withTransitionsDisabled]
1211
- );
1212
- const switchThemeFromElement = React4.useCallback(
1213
- async (theme, element) => {
1214
- await withTransitionsDisabled(async () => {
1215
- if (!mounted && disableAnimationOnInit) {
1216
- themeState.setTheme(theme);
1217
- return;
1218
- }
1219
- await withElementAsRef(themeState.ref, element, async () => {
1220
- await themeState.switchTheme(theme);
1221
- });
1222
- });
1223
- },
1224
- [disableAnimationOnInit, mounted, themeState, withTransitionsDisabled]
1225
- );
1226
- const contextValue = React4.useMemo(
1227
- () => ({
1228
- ref: themeState.ref,
1229
- theme: themeState.theme,
1230
- colorTheme: themeState.colorTheme,
1231
- resolvedTheme: themeState.resolvedTheme,
1232
- systemTheme: themeState.systemTheme,
1233
- setTheme,
1234
- setColorTheme: themeState.setColorTheme,
1235
- switchTheme,
1236
- switchColorTheme: themeState.switchColorTheme,
1237
- toggleTheme: async (animationOff = false) => switchTheme(
1238
- getNextResolvedTheme(themeState.resolvedTheme),
1239
- animationOff
1240
- ),
1241
- toggleLightTheme: async (animationOff = false) => {
1242
- if (themeState.resolvedTheme === "light") return;
1243
- await switchTheme("light", animationOff);
1244
- },
1245
- toggleDarkTheme: async (animationOff = false) => {
1246
- if (themeState.resolvedTheme === "dark") return;
1247
- await switchTheme("dark", animationOff);
1248
- },
1249
- toggleColorTheme: themeState.toggleColorTheme,
1250
- createColorThemeToggle: themeState.createColorThemeToggle,
1251
- isColorThemeActive: themeState.isColorThemeActive,
1252
- switchThemeFromElement
1253
- }),
1254
- [setTheme, switchTheme, switchThemeFromElement, themeState]
1255
- );
1256
- return /* @__PURE__ */ jsxRuntime.jsx(NextThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(SharedThemeContext.Provider, { value: contextValue, children: [
1257
- !disablePreHydrationScript && !forcedTheme ? /* @__PURE__ */ jsxRuntime.jsx(
1258
- ThemePreHydrationScript,
1259
- {
1260
- attribute,
1261
- colorStorageKey,
1262
- colorThemePrefix,
1263
- defaultColorTheme,
1264
- defaultTheme: effectiveDefaultTheme,
1265
- enableColorScheme,
1266
- enableSystem,
1267
- globalClassName,
1268
- nonce,
1269
- scriptProps,
1270
- storageKey,
1271
- value
1272
- }
1273
- ) : null,
1274
- children
1275
- ] }) });
1276
- };
1277
- var ThemeProvider = NextThemeProvider;
1278
- var useNextTheme = () => {
1279
- const context = React4.useContext(NextThemeContext);
1280
- if (context === void 0) {
1281
- throw new Error("useNextTheme must be used within a NextThemeProvider");
1282
- }
1283
- return context;
1284
- };
1285
- var useTheme = useNextTheme;
1286
- var isBrowser3 = typeof window !== "undefined";
1287
- var useSyncServerThemeStorage = ({
1288
- enabled,
1289
- theme,
1290
- colorTheme,
1291
- storageKey,
1292
- colorStorageKey
1293
- }) => {
1294
- React4.useEffect(() => {
1295
- if (!enabled || !isBrowser3) return;
1296
- if (theme) {
1297
- localStorage.setItem(storageKey, theme);
1298
- }
1299
- if (colorTheme) {
1300
- localStorage.setItem(colorStorageKey, colorTheme);
1301
- }
1302
- }, [enabled, theme, colorTheme, storageKey, colorStorageKey]);
1303
- };
1304
-
1305
- // src/tanstack/helpers.ts
1306
- var STORAGE_KEY = "theme";
1307
- var COLOR_STORAGE_KEY = "color-theme";
1308
- var GLOBAL_CLASS_NAME = "dark";
1309
- var COLOR_THEME_PREFIX = "theme-";
1310
- function buildServerThemeData(themeCookie, colorThemeCookie, options = {}) {
1311
- const { defaultTheme = "system", defaultColorTheme = "default" } = options;
1312
- const themePreference = themeCookie ?? defaultTheme;
1313
- const colorTheme = colorThemeCookie ?? defaultColorTheme;
1314
- return {
1315
- theme: resolveThemeForServer(themePreference),
1316
- themePreference,
1317
- colorTheme
1318
- };
1319
- }
1320
- var TanStackThemeContext = React4.createContext(void 0);
1321
- var generateTanStackPreHydrationScript = (storageKey, colorStorageKey, defaultTheme, defaultColorTheme, globalClassName, colorThemePrefix, systemThemeMode) => {
1322
- return `
1323
- (function() {
1324
- try {
1325
- var theme = localStorage.getItem('${storageKey}') || '${defaultTheme}';
1326
- var colorTheme = localStorage.getItem('${colorStorageKey}') || '${defaultColorTheme}';
1327
- var el = document.documentElement;
1328
- var resolved;
1329
- if (theme === 'system') {
1330
- if ('${systemThemeMode}' === 'css') {
1331
- el.classList.remove('${globalClassName}');
1332
- el.classList.remove('auto');
1333
- el.classList.remove('system');
1334
- el.classList.add('system');
1335
- el.style.colorScheme = '';
1336
- } else {
1337
- resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
1338
- if (resolved === 'dark') {
1339
- el.classList.add('${globalClassName}');
1340
- } else {
1341
- el.classList.remove('${globalClassName}');
1342
- }
1343
- el.classList.remove('system');
1344
- el.classList.remove('auto');
1345
- el.style.colorScheme = resolved;
1346
- }
1347
- } else {
1348
- resolved = theme;
1349
- el.classList.remove('system');
1350
- el.classList.remove('auto');
1351
- if (resolved === 'dark') {
1352
- el.classList.add('${globalClassName}');
1353
- } else {
1354
- el.classList.remove('${globalClassName}');
1355
- }
1356
- el.style.colorScheme = resolved;
1357
- }
1358
- if (colorTheme && colorTheme !== 'default') {
1359
- el.classList.add('${colorThemePrefix}' + colorTheme);
1360
- }
1361
- } catch (e) {
1362
- console.warn('Theme pre-hydration script failed:', e);
1363
- }
1364
- })();
1365
- `;
1366
- };
1367
- var TanStackStartThemeScript = React4__default.default.memo(
1368
- ({
1369
- storageKey = STORAGE_KEY,
1370
- colorStorageKey = COLOR_STORAGE_KEY,
1371
- defaultTheme = "system",
1372
- defaultColorTheme = "default",
1373
- globalClassName = GLOBAL_CLASS_NAME,
1374
- colorThemePrefix = COLOR_THEME_PREFIX,
1375
- nonce,
1376
- systemThemeMode = "js"
1377
- }) => {
1378
- const scriptContent = generateTanStackPreHydrationScript(
1379
- storageKey,
1380
- colorStorageKey,
1381
- defaultTheme,
1382
- defaultColorTheme,
1383
- globalClassName,
1384
- colorThemePrefix,
1385
- systemThemeMode
1386
- );
1387
- return /* @__PURE__ */ jsxRuntime.jsx(
1388
- "script",
1389
- {
1390
- nonce,
1391
- suppressHydrationWarning: true,
1392
- dangerouslySetInnerHTML: { __html: scriptContent }
1393
- }
1394
- );
1395
- }
1396
- );
1397
- TanStackStartThemeScript.displayName = "TanStackStartThemeScript";
1398
- var useHydrated = () => {
1399
- return React4.useSyncExternalStore(
1400
- () => () => {
1401
- },
1402
- () => true,
1403
- () => false
1404
- );
1405
- };
1406
- var getNextColorTheme = (colorThemes, currentColorTheme) => {
1407
- if (colorThemes.length === 0) return currentColorTheme;
1408
- const currentIndex = colorThemes.indexOf(currentColorTheme);
1409
- const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % colorThemes.length;
1410
- return colorThemes[nextIndex];
1411
- };
1412
- var TanStackThemeProvider = ({
1413
- children,
1414
- themes = ["light", "dark", "system"],
1415
- colorThemes = ["default"],
1416
- defaultTheme = "system",
1417
- defaultColorTheme = "default",
1418
- animationType = "circle" /* CIRCLE */,
1419
- duration = 750,
1420
- storageKey = STORAGE_KEY,
1421
- colorStorageKey = COLOR_STORAGE_KEY,
1422
- globalClassName = GLOBAL_CLASS_NAME,
1423
- colorThemePrefix = COLOR_THEME_PREFIX,
1424
- serverTheme,
1425
- serverColorTheme,
1426
- systemThemeMode = "css",
1427
- onServerThemeChange,
1428
- onServerColorThemeChange
1429
- }) => {
1430
- const isHydrated = useHydrated();
1431
- const hasServerTheme = serverTheme !== void 0;
1432
- const initialTheme = hasServerTheme ? serverTheme : void 0;
1433
- const themeState = useThemeAnimation({
1434
- themes,
1435
- colorThemes,
1436
- defaultTheme,
1437
- defaultColorTheme,
1438
- animationType,
1439
- duration,
1440
- storageKey,
1441
- colorStorageKey,
1442
- globalClassName,
1443
- colorThemePrefix,
1444
- initialTheme,
1445
- initialColorTheme: serverColorTheme,
1446
- systemThemeMode
1447
- });
1448
- useSyncServerThemeStorage({
1449
- enabled: hasServerTheme,
1450
- theme: initialTheme,
1451
- colorTheme: serverColorTheme,
1452
- storageKey,
1453
- colorStorageKey
1454
- });
1455
- const setThemeWithServer = React4.useCallback(
1456
- (newTheme) => {
1457
- themeState.setTheme(newTheme);
1458
- onServerThemeChange?.(newTheme);
1459
- },
1460
- [themeState, onServerThemeChange]
1461
- );
1462
- const setColorThemeWithServer = React4.useCallback(
1463
- (newColorTheme) => {
1464
- themeState.setColorTheme(newColorTheme);
1465
- onServerColorThemeChange?.(newColorTheme);
1466
- },
1467
- [themeState, onServerColorThemeChange]
1468
- );
1469
- const switchThemeWithHydrationAwareness = React4.useCallback(
1470
- async (theme, animationOff = false) => {
1471
- if (!isHydrated) {
1472
- setThemeWithServer(theme);
1473
- return;
1474
- }
1475
- await themeState.switchTheme(theme, animationOff);
1476
- onServerThemeChange?.(theme);
1477
- },
1478
- [isHydrated, themeState, setThemeWithServer, onServerThemeChange]
1479
- );
1480
- const toggleThemeWithHydrationAwareness = React4.useCallback(
1481
- async (animationOff = false) => {
1482
- const nextTheme = getNextResolvedTheme(themeState.resolvedTheme);
1483
- if (!isHydrated) {
1484
- setThemeWithServer(nextTheme);
1485
- return;
1486
- }
1487
- await themeState.toggleTheme(animationOff);
1488
- onServerThemeChange?.(nextTheme);
1489
- },
1490
- [isHydrated, themeState, setThemeWithServer, onServerThemeChange]
1491
- );
1492
- const toggleLightThemeWithHydrationAwareness = React4.useCallback(
1493
- async (animationOff = false) => {
1494
- if (!isHydrated) {
1495
- setThemeWithServer("light");
1496
- return;
1497
- }
1498
- await themeState.toggleLightTheme(animationOff);
1499
- onServerThemeChange?.("light");
1500
- },
1501
- [isHydrated, themeState, setThemeWithServer, onServerThemeChange]
1502
- );
1503
- const toggleDarkThemeWithHydrationAwareness = React4.useCallback(
1504
- async (animationOff = false) => {
1505
- if (!isHydrated) {
1506
- setThemeWithServer("dark");
1507
- return;
1508
- }
1509
- await themeState.toggleDarkTheme(animationOff);
1510
- onServerThemeChange?.("dark");
1511
- },
1512
- [isHydrated, themeState, setThemeWithServer, onServerThemeChange]
1513
- );
1514
- const switchColorThemeWithServer = React4.useCallback(
1515
- (newColorTheme) => {
1516
- themeState.switchColorTheme(newColorTheme);
1517
- onServerColorThemeChange?.(newColorTheme);
1518
- },
1519
- [themeState, onServerColorThemeChange]
1520
- );
1521
- const toggleColorThemeWithServer = React4.useCallback(() => {
1522
- const nextColorTheme = getNextColorTheme(colorThemes, themeState.colorTheme);
1523
- themeState.toggleColorTheme();
1524
- onServerColorThemeChange?.(nextColorTheme);
1525
- }, [colorThemes, themeState, onServerColorThemeChange]);
1526
- const createColorThemeToggleWithServer = React4.useCallback(
1527
- (targetColorTheme) => () => {
1528
- themeState.createColorThemeToggle(targetColorTheme)();
1529
- onServerColorThemeChange?.(targetColorTheme);
1530
- },
1531
- [themeState, onServerColorThemeChange]
1532
- );
1533
- const switchThemeFromElement = React4.useCallback(
1534
- async (theme, element) => {
1535
- if (!isHydrated) {
1536
- setThemeWithServer(theme);
1537
- return;
1538
- }
1539
- await withElementAsRef(themeState.ref, element, async () => {
1540
- await themeState.switchTheme(theme);
1541
- });
1542
- onServerThemeChange?.(theme);
1543
- },
1544
- [isHydrated, themeState, setThemeWithServer, onServerThemeChange]
1545
- );
1546
- const systemTheme = getBrowserSystemTheme();
1547
- const serverResolvedTheme = (() => {
1548
- if (hasServerTheme) {
1549
- if (serverTheme === "dark") return "dark";
1550
- if (serverTheme === "system") return systemTheme;
1551
- return "light";
1552
- }
1553
- return defaultTheme === "dark" ? "dark" : "light";
1554
- })();
1555
- if (!isHydrated) {
1556
- const loadingContextValue = {
1557
- ref: { current: null },
1558
- theme: initialTheme ?? defaultTheme,
1559
- colorTheme: serverColorTheme ?? defaultColorTheme,
1560
- resolvedTheme: serverResolvedTheme,
1561
- systemTheme: "light",
1562
- isHydrated: false,
1563
- setTheme: setThemeWithServer,
1564
- setColorTheme: setColorThemeWithServer,
1565
- switchTheme: async (theme) => setThemeWithServer(theme),
1566
- switchThemeFromElement: async (theme) => setThemeWithServer(theme),
1567
- switchColorTheme: switchColorThemeWithServer,
1568
- toggleTheme: async () => {
1569
- const nextTheme = serverResolvedTheme === "dark" ? "light" : "dark";
1570
- setThemeWithServer(nextTheme);
1571
- },
1572
- toggleLightTheme: async () => setThemeWithServer("light"),
1573
- toggleDarkTheme: async () => setThemeWithServer("dark"),
1574
- toggleColorTheme: toggleColorThemeWithServer,
1575
- createColorThemeToggle: createColorThemeToggleWithServer,
1576
- isColorThemeActive: themeState.isColorThemeActive
1577
- };
1578
- return /* @__PURE__ */ jsxRuntime.jsx(TanStackThemeContext.Provider, { value: loadingContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(SharedThemeContext.Provider, { value: loadingContextValue, children }) });
1579
- }
1580
- const contextValue = {
1581
- ref: themeState.ref,
1582
- theme: themeState.theme,
1583
- colorTheme: themeState.colorTheme,
1584
- resolvedTheme: themeState.resolvedTheme,
1585
- systemTheme,
1586
- isHydrated: true,
1587
- setTheme: setThemeWithServer,
1588
- setColorTheme: setColorThemeWithServer,
1589
- switchTheme: switchThemeWithHydrationAwareness,
1590
- switchThemeFromElement,
1591
- switchColorTheme: switchColorThemeWithServer,
1592
- toggleTheme: toggleThemeWithHydrationAwareness,
1593
- toggleLightTheme: toggleLightThemeWithHydrationAwareness,
1594
- toggleDarkTheme: toggleDarkThemeWithHydrationAwareness,
1595
- toggleColorTheme: toggleColorThemeWithServer,
1596
- createColorThemeToggle: createColorThemeToggleWithServer,
1597
- isColorThemeActive: themeState.isColorThemeActive
1598
- };
1599
- return /* @__PURE__ */ jsxRuntime.jsx(TanStackThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(SharedThemeContext.Provider, { value: contextValue, children }) });
1600
- };
1601
- var useTanStackTheme = () => {
1602
- const context = React4.useContext(TanStackThemeContext);
1603
- if (context === void 0) {
1604
- throw new Error(
1605
- "useTanStackTheme must be used within a TanStackThemeProvider"
1606
- );
1607
- }
1608
- return context;
1609
- };
1610
- var ViteThemeContext = React4.createContext(
1611
- void 0
1612
- );
1613
- var ViteThemeProvider = ({
1614
- children,
1615
- themes = ["light", "dark", "system"],
1616
- colorThemes = ["default"],
1617
- defaultTheme = "system",
1618
- defaultColorTheme = "default",
1619
- animationType = "circle" /* CIRCLE */,
1620
- duration = 750,
1621
- attribute = "class",
1622
- disableTransitionOnChange = false,
1623
- storageKey = "vite-theme",
1624
- colorStorageKey = "vite-color-theme",
1625
- globalClassName = "dark",
1626
- colorThemePrefix = "theme-"
1627
- }) => {
1628
- const themeState = useThemeAnimation({
1629
- themes,
1630
- colorThemes,
1631
- defaultTheme,
1632
- defaultColorTheme,
1633
- animationType,
1634
- duration,
1635
- storageKey,
1636
- colorStorageKey,
1637
- globalClassName: attribute === "class" ? globalClassName : void 0,
1638
- colorThemePrefix
1639
- });
1640
- const applyTransitionDisable = React4.useCallback(() => {
1641
- if (!disableTransitionOnChange) return () => {
1642
- };
1643
- const css = document.createElement("style");
1644
- css.textContent = `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`;
1645
- document.head.appendChild(css);
1646
- window.getComputedStyle(document.body);
1647
- return () => {
1648
- setTimeout(() => {
1649
- document.head.removeChild(css);
1650
- }, 1);
1651
- };
1652
- }, [disableTransitionOnChange]);
1653
- const wrappedSetTheme = React4.useCallback(
1654
- (theme) => {
1655
- const cleanup = applyTransitionDisable();
1656
- themeState.setTheme(theme);
1657
- cleanup();
1658
- },
1659
- [themeState.setTheme, applyTransitionDisable]
1660
- );
1661
- const wrappedSwitchTheme = React4.useCallback(
1662
- async (theme, animationOff = false) => {
1663
- const cleanup = applyTransitionDisable();
1664
- await themeState.switchTheme(theme, animationOff);
1665
- cleanup();
1666
- },
1667
- [themeState.switchTheme, applyTransitionDisable]
1668
- );
1669
- const wrappedToggleTheme = React4.useCallback(
1670
- async (animationOff = false) => {
1671
- const cleanup = applyTransitionDisable();
1672
- await themeState.toggleTheme(animationOff);
1673
- cleanup();
1674
- },
1675
- [themeState.toggleTheme, applyTransitionDisable]
1676
- );
1677
- const wrappedToggleLightTheme = React4.useCallback(
1678
- async (animationOff = false) => {
1679
- const cleanup = applyTransitionDisable();
1680
- await themeState.toggleLightTheme(animationOff);
1681
- cleanup();
1682
- },
1683
- [themeState.toggleLightTheme, applyTransitionDisable]
1684
- );
1685
- const wrappedToggleDarkTheme = React4.useCallback(
1686
- async (animationOff = false) => {
1687
- const cleanup = applyTransitionDisable();
1688
- await themeState.toggleDarkTheme(animationOff);
1689
- cleanup();
1690
- },
1691
- [themeState.toggleDarkTheme, applyTransitionDisable]
1692
- );
1693
- const switchThemeFromElement = async (theme, element) => {
1694
- const cleanup = applyTransitionDisable();
1695
- await withElementAsRef(themeState.ref, element, async () => {
1696
- await themeState.switchTheme(theme);
1697
- });
1698
- cleanup();
1699
- };
1700
- const systemTheme = getBrowserSystemTheme();
1701
- const contextValue = {
1702
- ref: themeState.ref,
1703
- theme: themeState.theme,
1704
- colorTheme: themeState.colorTheme,
1705
- resolvedTheme: themeState.resolvedTheme,
1706
- systemTheme,
1707
- setTheme: wrappedSetTheme,
1708
- setColorTheme: themeState.setColorTheme,
1709
- switchTheme: wrappedSwitchTheme,
1710
- switchThemeFromElement,
1711
- switchColorTheme: themeState.switchColorTheme,
1712
- toggleTheme: wrappedToggleTheme,
1713
- toggleLightTheme: wrappedToggleLightTheme,
1714
- toggleDarkTheme: wrappedToggleDarkTheme,
1715
- toggleColorTheme: themeState.toggleColorTheme,
1716
- createColorThemeToggle: themeState.createColorThemeToggle,
1717
- isColorThemeActive: themeState.isColorThemeActive
1718
- };
1719
- return /* @__PURE__ */ jsxRuntime.jsx(ViteThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(SharedThemeContext.Provider, { value: contextValue, children }) });
1720
- };
1721
- var useViteTheme = () => {
1722
- const context = React4.useContext(ViteThemeContext);
1723
- if (context === void 0) {
1724
- throw new Error("useViteTheme must be used within a ViteThemeProvider");
1725
- }
1726
- return context;
1727
- };
1728
-
1729
- exports.COLOR_STORAGE_KEY = COLOR_STORAGE_KEY;
1730
- exports.COLOR_THEME_PREFIX = COLOR_THEME_PREFIX;
1731
- exports.GLOBAL_CLASS_NAME = GLOBAL_CLASS_NAME;
1732
- exports.NextThemeProvider = NextThemeProvider;
1733
- exports.STORAGE_KEY = STORAGE_KEY;
1734
- exports.SpacemanThemeProvider = SpacemanThemeProvider;
1735
- exports.TanStackStartThemeScript = TanStackStartThemeScript;
1736
- exports.TanStackThemeProvider = TanStackThemeProvider;
1737
- exports.ThemeAnimationType = ThemeAnimationType;
1738
- exports.ThemeProvider = ThemeProvider;
1739
- exports.ThemeSelector = ThemeSelector;
1740
- exports.ThemeSwitcher = ThemeSwitcher;
1741
- exports.ViteThemeProvider = ViteThemeProvider;
1742
- exports.buildServerThemeData = buildServerThemeData;
1743
- exports.getSystemTheme = getSystemTheme;
1744
- exports.injectBaseStyles = injectBaseStyles;
1745
- exports.prefersReducedMotion = prefersReducedMotion;
1746
- exports.resolveTheme = resolveTheme;
1747
- exports.resolveThemeForServer = resolveThemeForServer;
1748
- exports.supportsViewTransitions = supportsViewTransitions;
1749
- exports.useNextTheme = useNextTheme;
1750
- exports.useSpacemanTheme = useSpacemanTheme;
1751
- exports.useTanStackTheme = useTanStackTheme;
1752
- exports.useTheme = useTheme;
1753
- exports.useThemeAnimation = useThemeAnimation;
1754
- exports.useViteTheme = useViteTheme;
1755
- //# sourceMappingURL=index.cjs.map
1756
- //# sourceMappingURL=index.cjs.map