@space-man/react-theme-animation 2.0.0 → 2.3.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/README.md +90 -464
- package/dist/core/index.cjs +74 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/{esm/types/index.d.ts → core/index.d.cts} +30 -12
- package/dist/{cjs/types → core}/index.d.ts +30 -12
- package/dist/core/index.js +66 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.cjs +1756 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1706 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +1742 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +224 -0
- package/dist/react/index.d.ts +224 -0
- package/dist/react/index.js +1697 -0
- package/dist/react/index.js.map +1 -0
- package/dist/tanstack/index.cjs +34 -0
- package/dist/tanstack/index.cjs.map +1 -0
- package/dist/tanstack/index.d.cts +20 -0
- package/dist/tanstack/index.d.ts +20 -0
- package/dist/tanstack/index.js +27 -0
- package/dist/tanstack/index.js.map +1 -0
- package/package.json +66 -37
- package/dist/cjs/components/SpacemanThemeProvider.d.ts +0 -57
- package/dist/cjs/components/SpacemanThemeProvider.js +0 -113
- package/dist/cjs/components/ThemeSelector.d.ts +0 -3
- package/dist/cjs/components/ThemeSelector.js +0 -39
- package/dist/cjs/components/ThemeSwitcher.d.ts +0 -3
- package/dist/cjs/components/ThemeSwitcher.js +0 -81
- package/dist/cjs/components/ViteThemeProvider.d.ts +0 -38
- package/dist/cjs/components/ViteThemeProvider.js +0 -117
- package/dist/cjs/components/ui/select.d.ts +0 -15
- package/dist/cjs/components/ui/select.js +0 -83
- package/dist/cjs/hooks/use-theme-animation.d.ts +0 -2
- package/dist/cjs/hooks/use-theme-animation.js +0 -234
- package/dist/cjs/index.d.ts +0 -8
- package/dist/cjs/index.js +0 -23
- package/dist/cjs/types/index.js +0 -9
- package/dist/cjs/utils/animations.d.ts +0 -21
- package/dist/cjs/utils/animations.js +0 -189
- package/dist/esm/components/SpacemanThemeProvider.d.ts +0 -57
- package/dist/esm/components/SpacemanThemeProvider.js +0 -108
- package/dist/esm/components/ThemeSelector.d.ts +0 -3
- package/dist/esm/components/ThemeSelector.js +0 -35
- package/dist/esm/components/ThemeSwitcher.d.ts +0 -3
- package/dist/esm/components/ThemeSwitcher.js +0 -77
- package/dist/esm/components/ViteThemeProvider.d.ts +0 -38
- package/dist/esm/components/ViteThemeProvider.js +0 -112
- package/dist/esm/components/ui/select.d.ts +0 -15
- package/dist/esm/components/ui/select.js +0 -39
- package/dist/esm/hooks/use-theme-animation.d.ts +0 -2
- package/dist/esm/hooks/use-theme-animation.js +0 -230
- package/dist/esm/index.d.ts +0 -8
- package/dist/esm/index.js +0 -7
- package/dist/esm/types/index.js +0 -6
- package/dist/esm/utils/animations.d.ts +0 -21
- package/dist/esm/utils/animations.js +0 -177
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useThemeAnimation = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const react_dom_1 = require("react-dom");
|
|
6
|
-
const types_1 = require("../types");
|
|
7
|
-
const animations_1 = require("../utils/animations");
|
|
8
|
-
const isBrowser = typeof window !== 'undefined';
|
|
9
|
-
const useThemeAnimation = (props = {}) => {
|
|
10
|
-
const { duration: propsDuration = 750, easing = 'ease-in-out', animationType = types_1.ThemeAnimationType.CIRCLE, blurAmount = 2, styleId = 'spaceman-theme-style', themes = ['light', 'dark', 'system'], colorThemes = ['default'], defaultTheme = 'system', defaultColorTheme = 'default', globalClassName = 'dark', colorThemePrefix = 'theme-', storageKey = 'theme', colorStorageKey = 'color-theme', theme: externalTheme, colorTheme: externalColorTheme, onThemeChange, onColorThemeChange,
|
|
11
|
-
// Slide animation options
|
|
12
|
-
slideDirection = 'left', slideFromX, slideFromY, slideToX = 0, slideToY = 0, } = props;
|
|
13
|
-
const isHighResolution = isBrowser && (window.innerWidth >= 3000 || window.innerHeight >= 2000);
|
|
14
|
-
const duration = isHighResolution ? Math.max(propsDuration * 0.8, 500) : propsDuration;
|
|
15
|
-
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
16
|
-
// Inject base styles once on mount
|
|
17
|
-
(0, react_1.useEffect)(() => {
|
|
18
|
-
(0, animations_1.injectBaseStyles)();
|
|
19
|
-
setMounted(true);
|
|
20
|
-
}, []);
|
|
21
|
-
const [internalTheme, setInternalTheme] = (0, react_1.useState)(() => {
|
|
22
|
-
if (!isBrowser)
|
|
23
|
-
return defaultTheme;
|
|
24
|
-
const saved = localStorage.getItem(storageKey);
|
|
25
|
-
return saved && themes.indexOf(saved) !== -1 ? saved : defaultTheme;
|
|
26
|
-
});
|
|
27
|
-
const [internalColorTheme, setInternalColorTheme] = (0, react_1.useState)(() => {
|
|
28
|
-
if (!isBrowser)
|
|
29
|
-
return defaultColorTheme;
|
|
30
|
-
const saved = localStorage.getItem(colorStorageKey);
|
|
31
|
-
return saved && colorThemes.indexOf(saved) !== -1 ? saved : defaultColorTheme;
|
|
32
|
-
});
|
|
33
|
-
const currentTheme = externalTheme ?? internalTheme;
|
|
34
|
-
const currentColorTheme = externalColorTheme ?? internalColorTheme;
|
|
35
|
-
const [, setSystemTheme] = (0, react_1.useState)(() => (0, animations_1.getSystemTheme)());
|
|
36
|
-
const resolvedTheme = (0, animations_1.resolveTheme)(currentTheme);
|
|
37
|
-
(0, react_1.useEffect)(() => {
|
|
38
|
-
if (!isBrowser)
|
|
39
|
-
return;
|
|
40
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
41
|
-
const handleChange = () => setSystemTheme((0, animations_1.getSystemTheme)());
|
|
42
|
-
mediaQuery.addEventListener('change', handleChange);
|
|
43
|
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
44
|
-
}, []);
|
|
45
|
-
// Apply theme classes to DOM
|
|
46
|
-
(0, react_1.useEffect)(() => {
|
|
47
|
-
if (!isBrowser || !mounted)
|
|
48
|
-
return;
|
|
49
|
-
if (resolvedTheme === 'dark') {
|
|
50
|
-
document.documentElement.classList.add(globalClassName);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
document.documentElement.classList.remove(globalClassName);
|
|
54
|
-
}
|
|
55
|
-
colorThemes.forEach(theme => {
|
|
56
|
-
document.documentElement.classList.remove(`${colorThemePrefix}${theme}`);
|
|
57
|
-
});
|
|
58
|
-
document.documentElement.classList.add(`${colorThemePrefix}${currentColorTheme}`);
|
|
59
|
-
}, [resolvedTheme, currentColorTheme, globalClassName, colorThemePrefix, colorThemes, mounted]);
|
|
60
|
-
const ref = (0, react_1.useRef)(null);
|
|
61
|
-
const setTheme = (0, react_1.useCallback)((newTheme) => {
|
|
62
|
-
// Always save to localStorage
|
|
63
|
-
if (isBrowser) {
|
|
64
|
-
localStorage.setItem(storageKey, newTheme);
|
|
65
|
-
}
|
|
66
|
-
// Update internal state if no external control
|
|
67
|
-
if (externalTheme === undefined) {
|
|
68
|
-
setInternalTheme(newTheme);
|
|
69
|
-
}
|
|
70
|
-
// Call external callback if provided
|
|
71
|
-
if (onThemeChange) {
|
|
72
|
-
onThemeChange(newTheme);
|
|
73
|
-
}
|
|
74
|
-
}, [onThemeChange, externalTheme, storageKey]);
|
|
75
|
-
const setColorTheme = (0, react_1.useCallback)((newColorTheme) => {
|
|
76
|
-
// Always save to localStorage
|
|
77
|
-
if (isBrowser) {
|
|
78
|
-
localStorage.setItem(colorStorageKey, newColorTheme);
|
|
79
|
-
}
|
|
80
|
-
// Update internal state if no external control
|
|
81
|
-
if (externalColorTheme === undefined) {
|
|
82
|
-
setInternalColorTheme(newColorTheme);
|
|
83
|
-
}
|
|
84
|
-
// Call external callback if provided
|
|
85
|
-
if (onColorThemeChange) {
|
|
86
|
-
onColorThemeChange(newColorTheme);
|
|
87
|
-
}
|
|
88
|
-
}, [onColorThemeChange, externalColorTheme, colorStorageKey]);
|
|
89
|
-
const switchTheme = (0, react_1.useCallback)(async (newTheme) => {
|
|
90
|
-
if (!ref.current || !(0, animations_1.supportsViewTransitions)() || (0, animations_1.prefersReducedMotion)()) {
|
|
91
|
-
setTheme(newTheme);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
// Helper function to convert direction to from coordinates
|
|
95
|
-
const getSlideFromCoords = (direction) => {
|
|
96
|
-
switch (direction) {
|
|
97
|
-
case 'left':
|
|
98
|
-
return { a: -100, b: 0 };
|
|
99
|
-
case 'right':
|
|
100
|
-
return { a: 100, b: 0 };
|
|
101
|
-
case 'top':
|
|
102
|
-
return { a: 0, b: -100 };
|
|
103
|
-
case 'bottom':
|
|
104
|
-
return { a: 0, b: 100 };
|
|
105
|
-
case 'top-left':
|
|
106
|
-
return { a: -100, b: -100 };
|
|
107
|
-
case 'top-right':
|
|
108
|
-
return { a: 100, b: -100 };
|
|
109
|
-
case 'bottom-left':
|
|
110
|
-
return { a: -100, b: 100 };
|
|
111
|
-
case 'bottom-right':
|
|
112
|
-
return { a: 100, b: 100 };
|
|
113
|
-
default:
|
|
114
|
-
return { a: -100, b: 0 };
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
let animationConfig;
|
|
118
|
-
if (animationType === types_1.ThemeAnimationType.SLIDE) {
|
|
119
|
-
// Use custom coordinates if provided, otherwise use direction
|
|
120
|
-
const fromCoords = slideFromX !== undefined && slideFromY !== undefined
|
|
121
|
-
? { a: slideFromX, b: slideFromY }
|
|
122
|
-
: getSlideFromCoords(slideDirection);
|
|
123
|
-
animationConfig = {
|
|
124
|
-
a: fromCoords.a,
|
|
125
|
-
b: fromCoords.b,
|
|
126
|
-
x: slideToX,
|
|
127
|
-
y: slideToY,
|
|
128
|
-
duration,
|
|
129
|
-
easing,
|
|
130
|
-
animationType,
|
|
131
|
-
blurAmount,
|
|
132
|
-
styleId,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
// For circle and blur circle animations, use button center
|
|
137
|
-
const { top, left, width, height } = ref.current.getBoundingClientRect();
|
|
138
|
-
const x = left + width / 2;
|
|
139
|
-
const y = top + height / 2;
|
|
140
|
-
animationConfig = {
|
|
141
|
-
x,
|
|
142
|
-
y,
|
|
143
|
-
duration,
|
|
144
|
-
easing,
|
|
145
|
-
animationType,
|
|
146
|
-
blurAmount,
|
|
147
|
-
styleId,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
if (animationType === types_1.ThemeAnimationType.BLUR_CIRCLE) {
|
|
151
|
-
(0, animations_1.createBlurCircleAnimation)(animationConfig);
|
|
152
|
-
}
|
|
153
|
-
if (animationType === types_1.ThemeAnimationType.SLIDE) {
|
|
154
|
-
(0, animations_1.createSlideAnimation)(animationConfig);
|
|
155
|
-
}
|
|
156
|
-
// Start the view transition
|
|
157
|
-
await document.startViewTransition(() => {
|
|
158
|
-
(0, react_dom_1.flushSync)(() => {
|
|
159
|
-
setTheme(newTheme);
|
|
160
|
-
});
|
|
161
|
-
}).ready;
|
|
162
|
-
if (animationType === types_1.ThemeAnimationType.CIRCLE) {
|
|
163
|
-
(0, animations_1.createCircleAnimation)(animationConfig);
|
|
164
|
-
}
|
|
165
|
-
}, [
|
|
166
|
-
setTheme,
|
|
167
|
-
duration,
|
|
168
|
-
easing,
|
|
169
|
-
animationType,
|
|
170
|
-
blurAmount,
|
|
171
|
-
styleId,
|
|
172
|
-
slideDirection,
|
|
173
|
-
slideFromX,
|
|
174
|
-
slideFromY,
|
|
175
|
-
slideToX,
|
|
176
|
-
slideToY,
|
|
177
|
-
]);
|
|
178
|
-
const switchColorTheme = (0, react_1.useCallback)((newColorTheme) => {
|
|
179
|
-
if (colorThemes.indexOf(newColorTheme) === -1) {
|
|
180
|
-
console.warn(`Color theme "${newColorTheme}" not found in available themes`);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
setColorTheme(newColorTheme);
|
|
184
|
-
}, [colorThemes, setColorTheme]);
|
|
185
|
-
const toggleTheme = (0, react_1.useCallback)(async () => {
|
|
186
|
-
const newTheme = resolvedTheme === 'dark' ? 'light' : 'dark';
|
|
187
|
-
await switchTheme(newTheme);
|
|
188
|
-
}, [resolvedTheme, switchTheme]);
|
|
189
|
-
const toggleLightTheme = (0, react_1.useCallback)(async () => {
|
|
190
|
-
if (resolvedTheme === 'light')
|
|
191
|
-
return;
|
|
192
|
-
await switchTheme('light');
|
|
193
|
-
}, [resolvedTheme, switchTheme]);
|
|
194
|
-
const toggleDarkTheme = (0, react_1.useCallback)(async () => {
|
|
195
|
-
if (resolvedTheme === 'dark')
|
|
196
|
-
return;
|
|
197
|
-
await switchTheme('dark');
|
|
198
|
-
}, [resolvedTheme, switchTheme]);
|
|
199
|
-
const toggleColorTheme = (0, react_1.useCallback)(() => {
|
|
200
|
-
const currentIndex = colorThemes.indexOf(currentColorTheme);
|
|
201
|
-
const nextIndex = (currentIndex + 1) % colorThemes.length;
|
|
202
|
-
const nextColorTheme = colorThemes[nextIndex];
|
|
203
|
-
setColorTheme(nextColorTheme);
|
|
204
|
-
}, [currentColorTheme, colorThemes, setColorTheme]);
|
|
205
|
-
const createColorThemeToggle = (0, react_1.useCallback)((targetColorTheme) => {
|
|
206
|
-
return () => {
|
|
207
|
-
if (colorThemes.indexOf(targetColorTheme) === -1) {
|
|
208
|
-
console.warn(`Color theme "${targetColorTheme}" not found in available themes`);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
setColorTheme(targetColorTheme);
|
|
212
|
-
};
|
|
213
|
-
}, [colorThemes, setColorTheme]);
|
|
214
|
-
const isColorThemeActive = (0, react_1.useCallback)((targetColorTheme) => {
|
|
215
|
-
return currentColorTheme === targetColorTheme;
|
|
216
|
-
}, [currentColorTheme]);
|
|
217
|
-
return {
|
|
218
|
-
ref,
|
|
219
|
-
theme: currentTheme,
|
|
220
|
-
colorTheme: currentColorTheme,
|
|
221
|
-
resolvedTheme,
|
|
222
|
-
setTheme,
|
|
223
|
-
setColorTheme,
|
|
224
|
-
switchTheme,
|
|
225
|
-
switchColorTheme,
|
|
226
|
-
toggleTheme,
|
|
227
|
-
toggleLightTheme,
|
|
228
|
-
toggleDarkTheme,
|
|
229
|
-
toggleColorTheme,
|
|
230
|
-
createColorThemeToggle,
|
|
231
|
-
isColorThemeActive,
|
|
232
|
-
};
|
|
233
|
-
};
|
|
234
|
-
exports.useThemeAnimation = useThemeAnimation;
|
package/dist/cjs/index.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export { useThemeAnimation } from './hooks/use-theme-animation';
|
|
2
|
-
export { ThemeSwitcher } from './components/ThemeSwitcher';
|
|
3
|
-
export { ThemeSelector } from './components/ThemeSelector';
|
|
4
|
-
export { SpacemanThemeProvider, useSpacemanTheme } from './components/SpacemanThemeProvider';
|
|
5
|
-
export { ViteThemeProvider, useViteTheme } from './components/ViteThemeProvider';
|
|
6
|
-
export type { UseThemeAnimationProps, UseThemeAnimationReturn, ThemeSwitcherProps, ThemeSelectorProps, Theme, ColorTheme, SlideDirection, } from './types';
|
|
7
|
-
export { ThemeAnimationType } from './types';
|
|
8
|
-
export { injectBaseStyles, getSystemTheme, resolveTheme, supportsViewTransitions, prefersReducedMotion, } from './utils/animations';
|
package/dist/cjs/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.injectBaseStyles = exports.ThemeAnimationType = exports.useViteTheme = exports.ViteThemeProvider = exports.useSpacemanTheme = exports.SpacemanThemeProvider = exports.ThemeSelector = exports.ThemeSwitcher = exports.useThemeAnimation = void 0;
|
|
4
|
-
var use_theme_animation_1 = require("./hooks/use-theme-animation");
|
|
5
|
-
Object.defineProperty(exports, "useThemeAnimation", { enumerable: true, get: function () { return use_theme_animation_1.useThemeAnimation; } });
|
|
6
|
-
var ThemeSwitcher_1 = require("./components/ThemeSwitcher");
|
|
7
|
-
Object.defineProperty(exports, "ThemeSwitcher", { enumerable: true, get: function () { return ThemeSwitcher_1.ThemeSwitcher; } });
|
|
8
|
-
var ThemeSelector_1 = require("./components/ThemeSelector");
|
|
9
|
-
Object.defineProperty(exports, "ThemeSelector", { enumerable: true, get: function () { return ThemeSelector_1.ThemeSelector; } });
|
|
10
|
-
var SpacemanThemeProvider_1 = require("./components/SpacemanThemeProvider");
|
|
11
|
-
Object.defineProperty(exports, "SpacemanThemeProvider", { enumerable: true, get: function () { return SpacemanThemeProvider_1.SpacemanThemeProvider; } });
|
|
12
|
-
Object.defineProperty(exports, "useSpacemanTheme", { enumerable: true, get: function () { return SpacemanThemeProvider_1.useSpacemanTheme; } });
|
|
13
|
-
var ViteThemeProvider_1 = require("./components/ViteThemeProvider");
|
|
14
|
-
Object.defineProperty(exports, "ViteThemeProvider", { enumerable: true, get: function () { return ViteThemeProvider_1.ViteThemeProvider; } });
|
|
15
|
-
Object.defineProperty(exports, "useViteTheme", { enumerable: true, get: function () { return ViteThemeProvider_1.useViteTheme; } });
|
|
16
|
-
var types_1 = require("./types");
|
|
17
|
-
Object.defineProperty(exports, "ThemeAnimationType", { enumerable: true, get: function () { return types_1.ThemeAnimationType; } });
|
|
18
|
-
var animations_1 = require("./utils/animations");
|
|
19
|
-
Object.defineProperty(exports, "injectBaseStyles", { enumerable: true, get: function () { return animations_1.injectBaseStyles; } });
|
|
20
|
-
Object.defineProperty(exports, "getSystemTheme", { enumerable: true, get: function () { return animations_1.getSystemTheme; } });
|
|
21
|
-
Object.defineProperty(exports, "resolveTheme", { enumerable: true, get: function () { return animations_1.resolveTheme; } });
|
|
22
|
-
Object.defineProperty(exports, "supportsViewTransitions", { enumerable: true, get: function () { return animations_1.supportsViewTransitions; } });
|
|
23
|
-
Object.defineProperty(exports, "prefersReducedMotion", { enumerable: true, get: function () { return animations_1.prefersReducedMotion; } });
|
package/dist/cjs/types/index.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ThemeAnimationType = void 0;
|
|
4
|
-
var ThemeAnimationType;
|
|
5
|
-
(function (ThemeAnimationType) {
|
|
6
|
-
ThemeAnimationType["CIRCLE"] = "circle";
|
|
7
|
-
ThemeAnimationType["BLUR_CIRCLE"] = "blur-circle";
|
|
8
|
-
ThemeAnimationType["SLIDE"] = "slide";
|
|
9
|
-
})(ThemeAnimationType || (exports.ThemeAnimationType = ThemeAnimationType = {}));
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ThemeAnimationType } from '../types';
|
|
2
|
-
export declare const injectBaseStyles: () => void;
|
|
3
|
-
export declare const createBlurCircleMask: (blur: number) => string;
|
|
4
|
-
export declare const getSystemTheme: () => "light" | "dark";
|
|
5
|
-
export declare const resolveTheme: (theme: string) => "light" | "dark";
|
|
6
|
-
export declare const supportsViewTransitions: () => boolean;
|
|
7
|
-
export declare const prefersReducedMotion: () => boolean;
|
|
8
|
-
export interface AnimationConfig {
|
|
9
|
-
a?: number;
|
|
10
|
-
b?: number;
|
|
11
|
-
x: number;
|
|
12
|
-
y: number;
|
|
13
|
-
duration: number;
|
|
14
|
-
easing: string;
|
|
15
|
-
animationType: ThemeAnimationType;
|
|
16
|
-
blurAmount: number;
|
|
17
|
-
styleId: string;
|
|
18
|
-
}
|
|
19
|
-
export declare const createCircleAnimation: (config: AnimationConfig) => void;
|
|
20
|
-
export declare const createSlideAnimation: (config: AnimationConfig) => void;
|
|
21
|
-
export declare const createBlurCircleAnimation: (config: AnimationConfig) => void;
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createBlurCircleAnimation = exports.createSlideAnimation = exports.createCircleAnimation = exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.createBlurCircleMask = exports.injectBaseStyles = void 0;
|
|
4
|
-
const isBrowser = typeof window !== 'undefined';
|
|
5
|
-
const injectBaseStyles = () => {
|
|
6
|
-
if (isBrowser) {
|
|
7
|
-
const styleId = 'spaceman-theme-base-style';
|
|
8
|
-
if (!document.getElementById(styleId)) {
|
|
9
|
-
const style = document.createElement('style');
|
|
10
|
-
style.id = styleId;
|
|
11
|
-
const isHighResolution = window.innerWidth >= 3000 || window.innerHeight >= 2000;
|
|
12
|
-
style.textContent = `
|
|
13
|
-
::view-transition-old(root),
|
|
14
|
-
::view-transition-new(root) {
|
|
15
|
-
animation: none;
|
|
16
|
-
mix-blend-mode: normal;
|
|
17
|
-
${isHighResolution ? 'transform: translateZ(0);' : ''}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
${isHighResolution
|
|
21
|
-
? `
|
|
22
|
-
::view-transition-group(root),
|
|
23
|
-
::view-transition-image-pair(root),
|
|
24
|
-
::view-transition-old(root),
|
|
25
|
-
::view-transition-new(root) {
|
|
26
|
-
backface-visibility: hidden;
|
|
27
|
-
perspective: 1000px;
|
|
28
|
-
transform: translate3d(0, 0, 0);
|
|
29
|
-
}
|
|
30
|
-
`
|
|
31
|
-
: ''}
|
|
32
|
-
`;
|
|
33
|
-
document.head.appendChild(style);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
exports.injectBaseStyles = injectBaseStyles;
|
|
38
|
-
const createBlurCircleMask = (blur) => {
|
|
39
|
-
const isHighResolution = isBrowser && (window.innerWidth >= 3000 || window.innerHeight >= 2000);
|
|
40
|
-
const blurFilter = `<filter id="blur"><feGaussianBlur stdDeviation="${blur}" /></filter>`;
|
|
41
|
-
const circleRadius = isHighResolution ? 20 : 25;
|
|
42
|
-
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>')`;
|
|
43
|
-
};
|
|
44
|
-
exports.createBlurCircleMask = createBlurCircleMask;
|
|
45
|
-
const getSystemTheme = () => {
|
|
46
|
-
if (!isBrowser)
|
|
47
|
-
return 'light';
|
|
48
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
49
|
-
};
|
|
50
|
-
exports.getSystemTheme = getSystemTheme;
|
|
51
|
-
const resolveTheme = (theme) => {
|
|
52
|
-
if (theme === 'system') {
|
|
53
|
-
return (0, exports.getSystemTheme)();
|
|
54
|
-
}
|
|
55
|
-
return theme === 'dark' ? 'dark' : 'light';
|
|
56
|
-
};
|
|
57
|
-
exports.resolveTheme = resolveTheme;
|
|
58
|
-
const supportsViewTransitions = () => {
|
|
59
|
-
return isBrowser && 'startViewTransition' in document;
|
|
60
|
-
};
|
|
61
|
-
exports.supportsViewTransitions = supportsViewTransitions;
|
|
62
|
-
const prefersReducedMotion = () => {
|
|
63
|
-
return isBrowser && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
64
|
-
};
|
|
65
|
-
exports.prefersReducedMotion = prefersReducedMotion;
|
|
66
|
-
const createCircleAnimation = (config) => {
|
|
67
|
-
const { x, y, duration, easing } = config;
|
|
68
|
-
// Calculate the distance to each corner of the viewport
|
|
69
|
-
const topLeft = Math.hypot(x, y);
|
|
70
|
-
const topRight = Math.hypot(window.innerWidth - x, y);
|
|
71
|
-
const bottomLeft = Math.hypot(x, window.innerHeight - y);
|
|
72
|
-
const bottomRight = Math.hypot(window.innerWidth - x, window.innerHeight - y);
|
|
73
|
-
// Find the maximum distance to ensure animation covers the entire viewport
|
|
74
|
-
const maxRadius = Math.max(topLeft, topRight, bottomLeft, bottomRight);
|
|
75
|
-
document.documentElement.animate({
|
|
76
|
-
clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${maxRadius}px at ${x}px ${y}px)`],
|
|
77
|
-
}, {
|
|
78
|
-
duration,
|
|
79
|
-
easing,
|
|
80
|
-
pseudoElement: '::view-transition-new(root)',
|
|
81
|
-
});
|
|
82
|
-
};
|
|
83
|
-
exports.createCircleAnimation = createCircleAnimation;
|
|
84
|
-
const createSlideAnimation = (config) => {
|
|
85
|
-
const { a = -100, b = 0, x = 0, y = 0, duration, styleId } = config;
|
|
86
|
-
const styleElement = document.createElement('style');
|
|
87
|
-
styleElement.id = styleId;
|
|
88
|
-
styleElement.textContent = `
|
|
89
|
-
::view-transition-old(root) {
|
|
90
|
-
animation-delay: ${duration}ms;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
::view-transition-new(root) {
|
|
94
|
-
animation: move-in ${duration}ms;
|
|
95
|
-
animation-timing-function: cubic-bezier(0.66, 0, 0.34, 1);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@keyframes move-in {
|
|
99
|
-
from {
|
|
100
|
-
translate: ${a}% ${b}%;
|
|
101
|
-
}
|
|
102
|
-
to {
|
|
103
|
-
translate: ${x}% ${y}%;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
`;
|
|
107
|
-
document.head.appendChild(styleElement);
|
|
108
|
-
// Clean up after animation
|
|
109
|
-
setTimeout(() => {
|
|
110
|
-
const styleElement = document.getElementById(styleId);
|
|
111
|
-
if (styleElement) {
|
|
112
|
-
styleElement.remove();
|
|
113
|
-
}
|
|
114
|
-
}, duration);
|
|
115
|
-
};
|
|
116
|
-
exports.createSlideAnimation = createSlideAnimation;
|
|
117
|
-
const createBlurCircleAnimation = (config) => {
|
|
118
|
-
const { x, y, duration, easing, blurAmount, styleId } = config;
|
|
119
|
-
const existingStyle = document.getElementById(styleId);
|
|
120
|
-
if (existingStyle) {
|
|
121
|
-
existingStyle.remove();
|
|
122
|
-
}
|
|
123
|
-
const viewportSize = Math.max(window.innerWidth, window.innerHeight) + 200;
|
|
124
|
-
const isHighResolution = window.innerWidth >= 3000 || window.innerHeight >= 2000;
|
|
125
|
-
const scaleFactor = isHighResolution ? 2.5 : 4;
|
|
126
|
-
const optimalMaskSize = isHighResolution
|
|
127
|
-
? Math.min(viewportSize * scaleFactor, 5000)
|
|
128
|
-
: viewportSize * scaleFactor;
|
|
129
|
-
const topLeft = Math.hypot(x, y);
|
|
130
|
-
const topRight = Math.hypot(window.innerWidth - x, y);
|
|
131
|
-
const bottomLeft = Math.hypot(x, window.innerHeight - y);
|
|
132
|
-
const bottomRight = Math.hypot(window.innerWidth - x, window.innerHeight - y);
|
|
133
|
-
const maxRadius = Math.max(topLeft, topRight, bottomLeft, bottomRight);
|
|
134
|
-
const styleElement = document.createElement('style');
|
|
135
|
-
styleElement.id = styleId;
|
|
136
|
-
const blurFactor = isHighResolution ? 1.5 : 1.2;
|
|
137
|
-
const finalMaskSize = Math.max(optimalMaskSize, maxRadius * 2.5);
|
|
138
|
-
styleElement.textContent = `
|
|
139
|
-
::view-transition-group(root) {
|
|
140
|
-
animation-duration: ${duration}ms;
|
|
141
|
-
animation-timing-function: ${isHighResolution
|
|
142
|
-
? 'cubic-bezier(0.2, 0, 0.2, 1)'
|
|
143
|
-
: 'linear(' +
|
|
144
|
-
'0 0%, 0.2342 12.49%, 0.4374 24.99%,' +
|
|
145
|
-
'0.6093 37.49%, 0.6835 43.74%,' +
|
|
146
|
-
'0.7499 49.99%, 0.8086 56.25%,' +
|
|
147
|
-
'0.8593 62.5%, 0.9023 68.75%, 0.9375 75%,' +
|
|
148
|
-
'0.9648 81.25%, 0.9844 87.5%,' +
|
|
149
|
-
'0.9961 93.75%, 1 100%' +
|
|
150
|
-
')'};
|
|
151
|
-
will-change: transform;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
::view-transition-new(root) {
|
|
155
|
-
mask: ${(0, exports.createBlurCircleMask)(blurAmount * blurFactor)} 0 0 / 100% 100% no-repeat;
|
|
156
|
-
mask-position: ${x}px ${y}px;
|
|
157
|
-
animation: spaceman-mask-scale ${duration}ms ${easing};
|
|
158
|
-
transform-origin: ${x}px ${y}px;
|
|
159
|
-
will-change: mask-size, mask-position;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
::view-transition-old(root),
|
|
163
|
-
.dark::view-transition-old(root) {
|
|
164
|
-
animation: spaceman-mask-scale ${duration}ms ${easing};
|
|
165
|
-
transform-origin: ${x}px ${y}px;
|
|
166
|
-
z-index: -1;
|
|
167
|
-
will-change: mask-size, mask-position;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
@keyframes spaceman-mask-scale {
|
|
171
|
-
0% {
|
|
172
|
-
mask-size: 0px;
|
|
173
|
-
mask-position: ${x}px ${y}px;
|
|
174
|
-
}
|
|
175
|
-
100% {
|
|
176
|
-
mask-size: ${finalMaskSize}px;
|
|
177
|
-
mask-position: ${x - finalMaskSize / 2}px ${y - finalMaskSize / 2}px;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
`;
|
|
181
|
-
document.head.appendChild(styleElement);
|
|
182
|
-
setTimeout(() => {
|
|
183
|
-
const styleElement = document.getElementById(styleId);
|
|
184
|
-
if (styleElement) {
|
|
185
|
-
styleElement.remove();
|
|
186
|
-
}
|
|
187
|
-
}, duration);
|
|
188
|
-
};
|
|
189
|
-
exports.createBlurCircleAnimation = createBlurCircleAnimation;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import React, { ReactNode } from 'react';
|
|
2
|
-
import { Theme, ColorTheme, ThemeAnimationType } from '../types';
|
|
3
|
-
/**
|
|
4
|
-
* Context type for the Spaceman Theme Provider
|
|
5
|
-
*/
|
|
6
|
-
interface SpacemanThemeContextType {
|
|
7
|
-
ref: React.RefObject<HTMLButtonElement | null>;
|
|
8
|
-
theme: Theme;
|
|
9
|
-
colorTheme: ColorTheme;
|
|
10
|
-
resolvedTheme: 'light' | 'dark';
|
|
11
|
-
setTheme: (theme: Theme) => void;
|
|
12
|
-
setColorTheme: (colorTheme: ColorTheme) => void;
|
|
13
|
-
switchTheme: (theme: Theme) => Promise<void>;
|
|
14
|
-
switchColorTheme: (colorTheme: string) => void;
|
|
15
|
-
toggleTheme: () => Promise<void>;
|
|
16
|
-
toggleLightTheme: () => Promise<void>;
|
|
17
|
-
toggleDarkTheme: () => Promise<void>;
|
|
18
|
-
toggleColorTheme: () => void;
|
|
19
|
-
createColorThemeToggle: (targetColorTheme: string) => () => void;
|
|
20
|
-
isColorThemeActive: (targetColorTheme: string) => boolean;
|
|
21
|
-
switchThemeFromElement: (theme: Theme, element: HTMLButtonElement) => Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Props for the Spaceman Theme Provider
|
|
25
|
-
*/
|
|
26
|
-
interface SpacemanThemeProviderProps {
|
|
27
|
-
children: ReactNode;
|
|
28
|
-
themes?: Theme[];
|
|
29
|
-
colorThemes?: ColorTheme[];
|
|
30
|
-
defaultTheme?: Theme;
|
|
31
|
-
defaultColorTheme?: ColorTheme;
|
|
32
|
-
animationType?: ThemeAnimationType;
|
|
33
|
-
duration?: number;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Spaceman Theme Provider - Provides centralized theme state management
|
|
37
|
-
*
|
|
38
|
-
* This provider creates a single source of truth for theme state using the useThemeAnimation hook.
|
|
39
|
-
* All theme-related components should consume from this context to ensure synchronization.
|
|
40
|
-
*
|
|
41
|
-
* @param children - React children to wrap with theme context
|
|
42
|
-
* @param themes - Available theme options (default: ['light', 'dark', 'system'])
|
|
43
|
-
* @param colorThemes - Available color theme options (default: ['default'])
|
|
44
|
-
* @param defaultTheme - Default theme to use (default: 'system')
|
|
45
|
-
* @param defaultColorTheme - Default color theme to use (default: 'default')
|
|
46
|
-
* @param animationType - Animation type for theme transitions (default: CIRCLE)
|
|
47
|
-
* @param duration - Animation duration in milliseconds (default: 750)
|
|
48
|
-
*/
|
|
49
|
-
export declare const SpacemanThemeProvider: React.FC<SpacemanThemeProviderProps>;
|
|
50
|
-
/**
|
|
51
|
-
* Hook to consume the Spaceman Theme context
|
|
52
|
-
*
|
|
53
|
-
* @returns Theme context value with all theme state and methods
|
|
54
|
-
* @throws Error if used outside of SpacemanThemeProvider
|
|
55
|
-
*/
|
|
56
|
-
export declare const useSpacemanTheme: () => SpacemanThemeContextType;
|
|
57
|
-
export {};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext, useState, useEffect } from 'react';
|
|
3
|
-
import { useThemeAnimation } from '../hooks/use-theme-animation';
|
|
4
|
-
import { ThemeAnimationType } from '../types';
|
|
5
|
-
const SpacemanThemeContext = createContext(undefined);
|
|
6
|
-
/**
|
|
7
|
-
* Spaceman Theme Provider - Provides centralized theme state management
|
|
8
|
-
*
|
|
9
|
-
* This provider creates a single source of truth for theme state using the useThemeAnimation hook.
|
|
10
|
-
* All theme-related components should consume from this context to ensure synchronization.
|
|
11
|
-
*
|
|
12
|
-
* @param children - React children to wrap with theme context
|
|
13
|
-
* @param themes - Available theme options (default: ['light', 'dark', 'system'])
|
|
14
|
-
* @param colorThemes - Available color theme options (default: ['default'])
|
|
15
|
-
* @param defaultTheme - Default theme to use (default: 'system')
|
|
16
|
-
* @param defaultColorTheme - Default color theme to use (default: 'default')
|
|
17
|
-
* @param animationType - Animation type for theme transitions (default: CIRCLE)
|
|
18
|
-
* @param duration - Animation duration in milliseconds (default: 750)
|
|
19
|
-
*/
|
|
20
|
-
export const SpacemanThemeProvider = ({ children, themes = ['light', 'dark', 'system'], colorThemes = ['default'], defaultTheme = 'system', defaultColorTheme = 'default', animationType = ThemeAnimationType.CIRCLE, duration = 750, }) => {
|
|
21
|
-
const [mounted, setMounted] = useState(false);
|
|
22
|
-
const themeState = useThemeAnimation({
|
|
23
|
-
themes,
|
|
24
|
-
colorThemes,
|
|
25
|
-
defaultTheme,
|
|
26
|
-
defaultColorTheme,
|
|
27
|
-
animationType,
|
|
28
|
-
duration,
|
|
29
|
-
});
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
setMounted(true);
|
|
32
|
-
}, []);
|
|
33
|
-
const switchThemeFromElement = async (theme, element) => {
|
|
34
|
-
if (themeState.ref.current) {
|
|
35
|
-
const originalRef = themeState.ref.current;
|
|
36
|
-
Object.defineProperty(themeState.ref, 'current', {
|
|
37
|
-
value: element,
|
|
38
|
-
writable: true,
|
|
39
|
-
configurable: true,
|
|
40
|
-
});
|
|
41
|
-
await themeState.switchTheme(theme);
|
|
42
|
-
Object.defineProperty(themeState.ref, 'current', {
|
|
43
|
-
value: originalRef,
|
|
44
|
-
writable: true,
|
|
45
|
-
configurable: true,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
Object.defineProperty(themeState.ref, 'current', {
|
|
50
|
-
value: element,
|
|
51
|
-
writable: true,
|
|
52
|
-
configurable: true,
|
|
53
|
-
});
|
|
54
|
-
await themeState.switchTheme(theme);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
if (!mounted) {
|
|
58
|
-
const loadingContextValue = {
|
|
59
|
-
ref: { current: null },
|
|
60
|
-
theme: defaultTheme,
|
|
61
|
-
colorTheme: defaultColorTheme,
|
|
62
|
-
resolvedTheme: defaultTheme === 'dark' ? 'dark' : 'light',
|
|
63
|
-
setTheme: () => { },
|
|
64
|
-
setColorTheme: () => { },
|
|
65
|
-
switchTheme: async () => { },
|
|
66
|
-
switchThemeFromElement: async () => { },
|
|
67
|
-
switchColorTheme: () => { },
|
|
68
|
-
toggleTheme: async () => { },
|
|
69
|
-
toggleLightTheme: async () => { },
|
|
70
|
-
toggleDarkTheme: async () => { },
|
|
71
|
-
toggleColorTheme: () => { },
|
|
72
|
-
createColorThemeToggle: () => () => { },
|
|
73
|
-
isColorThemeActive: () => false,
|
|
74
|
-
};
|
|
75
|
-
return (_jsx(SpacemanThemeContext.Provider, { value: loadingContextValue, children: children }));
|
|
76
|
-
}
|
|
77
|
-
const contextValue = {
|
|
78
|
-
ref: themeState.ref,
|
|
79
|
-
theme: themeState.theme,
|
|
80
|
-
colorTheme: themeState.colorTheme,
|
|
81
|
-
resolvedTheme: themeState.resolvedTheme,
|
|
82
|
-
setTheme: themeState.setTheme,
|
|
83
|
-
setColorTheme: themeState.setColorTheme,
|
|
84
|
-
switchTheme: themeState.switchTheme,
|
|
85
|
-
switchThemeFromElement,
|
|
86
|
-
switchColorTheme: themeState.switchColorTheme,
|
|
87
|
-
toggleTheme: themeState.toggleTheme,
|
|
88
|
-
toggleLightTheme: themeState.toggleLightTheme,
|
|
89
|
-
toggleDarkTheme: themeState.toggleDarkTheme,
|
|
90
|
-
toggleColorTheme: themeState.toggleColorTheme,
|
|
91
|
-
createColorThemeToggle: themeState.createColorThemeToggle,
|
|
92
|
-
isColorThemeActive: themeState.isColorThemeActive,
|
|
93
|
-
};
|
|
94
|
-
return (_jsx(SpacemanThemeContext.Provider, { value: contextValue, children: children }));
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* Hook to consume the Spaceman Theme context
|
|
98
|
-
*
|
|
99
|
-
* @returns Theme context value with all theme state and methods
|
|
100
|
-
* @throws Error if used outside of SpacemanThemeProvider
|
|
101
|
-
*/
|
|
102
|
-
export const useSpacemanTheme = () => {
|
|
103
|
-
const context = useContext(SpacemanThemeContext);
|
|
104
|
-
if (context === undefined) {
|
|
105
|
-
throw new Error('useSpacemanTheme must be used within a SpacemanThemeProvider');
|
|
106
|
-
}
|
|
107
|
-
return context;
|
|
108
|
-
};
|