@space-man/react-theme-animation 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -15
- package/dist/cjs/hooks/use-theme-animation.js +75 -13
- package/dist/cjs/types/index.d.ts +8 -1
- package/dist/cjs/types/index.js +1 -0
- package/dist/cjs/utils/animations.d.ts +3 -0
- package/dist/cjs/utils/animations.js +34 -1
- package/dist/esm/hooks/use-theme-animation.js +76 -14
- package/dist/esm/types/index.d.ts +8 -1
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/utils/animations.d.ts +3 -0
- package/dist/esm/utils/animations.js +32 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# @space-man/react-theme-animation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
https://github.com/user-attachments/assets/2819cf70-c474-478e-b821-6b26457c8d4a
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
React theme switching with smooth view transition animations, multi-theme support, and synchronized state management.
|
|
6
6
|
|
|
7
|
-
<
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img width="50%" src="https://github.com/user-attachments/assets/98b7f5cc-705c-4d32-a2f6-47f79b298707" />
|
|
9
|
+
<img width="50%" src="https://github.com/user-attachments/assets/128cc7b0-5708-4b4c-884f-a8310d4e494f" />
|
|
10
|
+
</p>
|
|
8
11
|
|
|
9
12
|
## Live Demo
|
|
10
13
|
|
|
@@ -278,7 +281,7 @@ Standalone hook for theme management and animations.
|
|
|
278
281
|
const { theme, colorTheme, switchTheme, setColorTheme, toggleTheme, ref } = useThemeAnimation({
|
|
279
282
|
themes: ['light', 'dark', 'system'],
|
|
280
283
|
colorThemes: ['default', 'blue', 'green', 'purple'],
|
|
281
|
-
animationType: ThemeAnimationType.
|
|
284
|
+
animationType: ThemeAnimationType.SLIDE,
|
|
282
285
|
duration: 800,
|
|
283
286
|
onThemeChange: theme => console.log('Theme changed:', theme),
|
|
284
287
|
onColorThemeChange: colorTheme => console.log('Color changed:', colorTheme),
|
|
@@ -287,17 +290,18 @@ const { theme, colorTheme, switchTheme, setColorTheme, toggleTheme, ref } = useT
|
|
|
287
290
|
|
|
288
291
|
#### Options
|
|
289
292
|
|
|
290
|
-
| Property | Type | Default | Description
|
|
291
|
-
| -------------------- | ---------------------------------- | ----------------------------- |
|
|
292
|
-
| `themes` | `Theme[]` | `['light', 'dark', 'system']` | Available themes
|
|
293
|
-
| `colorThemes` | `ColorTheme[]` | `['default']` | Available color themes
|
|
294
|
-
| `theme` | `Theme` | `'system'` | Initial theme
|
|
295
|
-
| `colorTheme` | `ColorTheme` | `'default'` | Initial color theme
|
|
296
|
-
| `animationType` | `ThemeAnimationType` | `ThemeAnimationType.CIRCLE` | Animation type
|
|
297
|
-
| `duration` | `number` | `500` | Animation duration in ms
|
|
298
|
-
| `blurAmount` | `number` | `2` | Blur amount for blur-circle animation
|
|
299
|
-
| `onThemeChange` | `(theme: Theme) => void` | - | Theme change callback
|
|
300
|
-
| `onColorThemeChange` | `(colorTheme: ColorTheme) => void` | - | Color theme change callback
|
|
293
|
+
| Property | Type | Default | Description |
|
|
294
|
+
| -------------------- | ---------------------------------- | ----------------------------- | --------------------------------------------------- |
|
|
295
|
+
| `themes` | `Theme[]` | `['light', 'dark', 'system']` | Available themes |
|
|
296
|
+
| `colorThemes` | `ColorTheme[]` | `['default']` | Available color themes |
|
|
297
|
+
| `theme` | `Theme` | `'system'` | Initial theme |
|
|
298
|
+
| `colorTheme` | `ColorTheme` | `'default'` | Initial color theme |
|
|
299
|
+
| `animationType` | `ThemeAnimationType` | `ThemeAnimationType.CIRCLE` | Animation type. Options: CIRCLE, BLUR_CIRCLE, SLIDE |
|
|
300
|
+
| `duration` | `number` | `500` | Animation duration in ms |
|
|
301
|
+
| `blurAmount` | `number` | `2` | Blur amount for blur-circle animation |
|
|
302
|
+
| `onThemeChange` | `(theme: Theme) => void` | - | Theme change callback |
|
|
303
|
+
| `onColorThemeChange` | `(colorTheme: ColorTheme) => void` | - | Color theme change callback |
|
|
304
|
+
| `slideDirection` | `SlideDirection` | `left` | Animation slide direction |
|
|
301
305
|
|
|
302
306
|
#### Return
|
|
303
307
|
|
|
@@ -7,7 +7,9 @@ const types_1 = require("../types");
|
|
|
7
7
|
const animations_1 = require("../utils/animations");
|
|
8
8
|
const isBrowser = typeof window !== 'undefined';
|
|
9
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,
|
|
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;
|
|
11
13
|
const isHighResolution = isBrowser && (window.innerWidth >= 3000 || window.innerHeight >= 2000);
|
|
12
14
|
const duration = isHighResolution ? Math.max(propsDuration * 0.8, 500) : propsDuration;
|
|
13
15
|
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
@@ -100,21 +102,69 @@ const useThemeAnimation = (props = {}) => {
|
|
|
100
102
|
setTheme(newTheme);
|
|
101
103
|
return;
|
|
102
104
|
}
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
// Helper function to convert direction to from coordinates
|
|
106
|
+
const getSlideFromCoords = (direction) => {
|
|
107
|
+
switch (direction) {
|
|
108
|
+
case 'left':
|
|
109
|
+
return { a: -100, b: 0 };
|
|
110
|
+
case 'right':
|
|
111
|
+
return { a: 100, b: 0 };
|
|
112
|
+
case 'top':
|
|
113
|
+
return { a: 0, b: -100 };
|
|
114
|
+
case 'bottom':
|
|
115
|
+
return { a: 0, b: 100 };
|
|
116
|
+
case 'top-left':
|
|
117
|
+
return { a: -100, b: -100 };
|
|
118
|
+
case 'top-right':
|
|
119
|
+
return { a: 100, b: -100 };
|
|
120
|
+
case 'bottom-left':
|
|
121
|
+
return { a: -100, b: 100 };
|
|
122
|
+
case 'bottom-right':
|
|
123
|
+
return { a: 100, b: 100 };
|
|
124
|
+
default:
|
|
125
|
+
return { a: -100, b: 0 };
|
|
126
|
+
}
|
|
114
127
|
};
|
|
128
|
+
let animationConfig;
|
|
129
|
+
if (animationType === types_1.ThemeAnimationType.SLIDE) {
|
|
130
|
+
// Use custom coordinates if provided, otherwise use direction
|
|
131
|
+
const fromCoords = slideFromX !== undefined && slideFromY !== undefined
|
|
132
|
+
? { a: slideFromX, b: slideFromY }
|
|
133
|
+
: getSlideFromCoords(slideDirection);
|
|
134
|
+
animationConfig = {
|
|
135
|
+
a: fromCoords.a,
|
|
136
|
+
b: fromCoords.b,
|
|
137
|
+
x: slideToX,
|
|
138
|
+
y: slideToY,
|
|
139
|
+
duration,
|
|
140
|
+
easing,
|
|
141
|
+
animationType,
|
|
142
|
+
blurAmount,
|
|
143
|
+
styleId,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// For circle and blur circle animations, use button center
|
|
148
|
+
const { top, left, width, height } = ref.current.getBoundingClientRect();
|
|
149
|
+
const x = left + width / 2;
|
|
150
|
+
const y = top + height / 2;
|
|
151
|
+
animationConfig = {
|
|
152
|
+
x,
|
|
153
|
+
y,
|
|
154
|
+
duration,
|
|
155
|
+
easing,
|
|
156
|
+
animationType,
|
|
157
|
+
blurAmount,
|
|
158
|
+
styleId,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
115
161
|
if (animationType === types_1.ThemeAnimationType.BLUR_CIRCLE) {
|
|
116
162
|
(0, animations_1.createBlurCircleAnimation)(animationConfig);
|
|
117
163
|
}
|
|
164
|
+
if (animationType === types_1.ThemeAnimationType.SLIDE) {
|
|
165
|
+
(0, animations_1.createSlideAnimation)(animationConfig);
|
|
166
|
+
}
|
|
167
|
+
// Start the view transition
|
|
118
168
|
await document.startViewTransition(() => {
|
|
119
169
|
(0, react_dom_1.flushSync)(() => {
|
|
120
170
|
setTheme(newTheme);
|
|
@@ -123,7 +173,19 @@ const useThemeAnimation = (props = {}) => {
|
|
|
123
173
|
if (animationType === types_1.ThemeAnimationType.CIRCLE) {
|
|
124
174
|
(0, animations_1.createCircleAnimation)(animationConfig);
|
|
125
175
|
}
|
|
126
|
-
}, [
|
|
176
|
+
}, [
|
|
177
|
+
setTheme,
|
|
178
|
+
duration,
|
|
179
|
+
easing,
|
|
180
|
+
animationType,
|
|
181
|
+
blurAmount,
|
|
182
|
+
styleId,
|
|
183
|
+
slideDirection,
|
|
184
|
+
slideFromX,
|
|
185
|
+
slideFromY,
|
|
186
|
+
slideToX,
|
|
187
|
+
slideToY,
|
|
188
|
+
]);
|
|
127
189
|
const toggleTheme = (0, react_1.useCallback)(async () => {
|
|
128
190
|
const newTheme = resolvedTheme === 'dark' ? 'light' : 'dark';
|
|
129
191
|
await switchTheme(newTheme);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
export declare enum ThemeAnimationType {
|
|
3
3
|
CIRCLE = "circle",
|
|
4
|
-
BLUR_CIRCLE = "blur-circle"
|
|
4
|
+
BLUR_CIRCLE = "blur-circle",
|
|
5
|
+
SLIDE = "slide"
|
|
5
6
|
}
|
|
7
|
+
export type SlideDirection = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
6
8
|
export type Theme = 'light' | 'dark' | 'system';
|
|
7
9
|
export type ColorTheme = string;
|
|
8
10
|
export interface UseThemeAnimationProps {
|
|
@@ -23,6 +25,11 @@ export interface UseThemeAnimationProps {
|
|
|
23
25
|
colorTheme?: ColorTheme;
|
|
24
26
|
onThemeChange?: (theme: Theme) => void;
|
|
25
27
|
onColorThemeChange?: (colorTheme: ColorTheme) => void;
|
|
28
|
+
slideDirection?: SlideDirection;
|
|
29
|
+
slideFromX?: number;
|
|
30
|
+
slideFromY?: number;
|
|
31
|
+
slideToX?: number;
|
|
32
|
+
slideToY?: number;
|
|
26
33
|
}
|
|
27
34
|
export interface UseThemeAnimationReturn {
|
|
28
35
|
ref: RefObject<HTMLButtonElement | null>;
|
package/dist/cjs/types/index.js
CHANGED
|
@@ -5,4 +5,5 @@ var ThemeAnimationType;
|
|
|
5
5
|
(function (ThemeAnimationType) {
|
|
6
6
|
ThemeAnimationType["CIRCLE"] = "circle";
|
|
7
7
|
ThemeAnimationType["BLUR_CIRCLE"] = "blur-circle";
|
|
8
|
+
ThemeAnimationType["SLIDE"] = "slide";
|
|
8
9
|
})(ThemeAnimationType || (exports.ThemeAnimationType = ThemeAnimationType = {}));
|
|
@@ -6,6 +6,8 @@ export declare const resolveTheme: (theme: string) => "light" | "dark";
|
|
|
6
6
|
export declare const supportsViewTransitions: () => boolean;
|
|
7
7
|
export declare const prefersReducedMotion: () => boolean;
|
|
8
8
|
export interface AnimationConfig {
|
|
9
|
+
a?: number;
|
|
10
|
+
b?: number;
|
|
9
11
|
x: number;
|
|
10
12
|
y: number;
|
|
11
13
|
duration: number;
|
|
@@ -15,4 +17,5 @@ export interface AnimationConfig {
|
|
|
15
17
|
styleId: string;
|
|
16
18
|
}
|
|
17
19
|
export declare const createCircleAnimation: (config: AnimationConfig) => void;
|
|
20
|
+
export declare const createSlideAnimation: (config: AnimationConfig) => void;
|
|
18
21
|
export declare const createBlurCircleAnimation: (config: AnimationConfig) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createBlurCircleAnimation = exports.createCircleAnimation = exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.createBlurCircleMask = exports.injectBaseStyles = void 0;
|
|
3
|
+
exports.createBlurCircleAnimation = exports.createSlideAnimation = exports.createCircleAnimation = exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.createBlurCircleMask = exports.injectBaseStyles = void 0;
|
|
4
4
|
const isBrowser = typeof window !== 'undefined';
|
|
5
5
|
const injectBaseStyles = () => {
|
|
6
6
|
if (isBrowser) {
|
|
@@ -81,6 +81,39 @@ const createCircleAnimation = (config) => {
|
|
|
81
81
|
});
|
|
82
82
|
};
|
|
83
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;
|
|
84
117
|
const createBlurCircleAnimation = (config) => {
|
|
85
118
|
const { x, y, duration, easing, blurAmount, styleId } = config;
|
|
86
119
|
const existingStyle = document.getElementById(styleId);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
2
2
|
import { flushSync } from 'react-dom';
|
|
3
3
|
import { ThemeAnimationType, } from '../types';
|
|
4
|
-
import { injectBaseStyles, resolveTheme, getSystemTheme, supportsViewTransitions, prefersReducedMotion, createCircleAnimation, createBlurCircleAnimation, } from '../utils/animations';
|
|
4
|
+
import { injectBaseStyles, resolveTheme, getSystemTheme, supportsViewTransitions, prefersReducedMotion, createCircleAnimation, createBlurCircleAnimation, createSlideAnimation, } from '../utils/animations';
|
|
5
5
|
const isBrowser = typeof window !== 'undefined';
|
|
6
6
|
export const useThemeAnimation = (props = {}) => {
|
|
7
|
-
const { duration: propsDuration = 750, easing = 'ease-in-out', animationType = 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,
|
|
7
|
+
const { duration: propsDuration = 750, easing = 'ease-in-out', animationType = 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,
|
|
8
|
+
// Slide animation options
|
|
9
|
+
slideDirection = 'left', slideFromX, slideFromY, slideToX = 0, slideToY = 0, } = props;
|
|
8
10
|
const isHighResolution = isBrowser && (window.innerWidth >= 3000 || window.innerHeight >= 2000);
|
|
9
11
|
const duration = isHighResolution ? Math.max(propsDuration * 0.8, 500) : propsDuration;
|
|
10
12
|
const [mounted, setMounted] = useState(false);
|
|
@@ -97,21 +99,69 @@ export const useThemeAnimation = (props = {}) => {
|
|
|
97
99
|
setTheme(newTheme);
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
// Helper function to convert direction to from coordinates
|
|
103
|
+
const getSlideFromCoords = (direction) => {
|
|
104
|
+
switch (direction) {
|
|
105
|
+
case 'left':
|
|
106
|
+
return { a: -100, b: 0 };
|
|
107
|
+
case 'right':
|
|
108
|
+
return { a: 100, b: 0 };
|
|
109
|
+
case 'top':
|
|
110
|
+
return { a: 0, b: -100 };
|
|
111
|
+
case 'bottom':
|
|
112
|
+
return { a: 0, b: 100 };
|
|
113
|
+
case 'top-left':
|
|
114
|
+
return { a: -100, b: -100 };
|
|
115
|
+
case 'top-right':
|
|
116
|
+
return { a: 100, b: -100 };
|
|
117
|
+
case 'bottom-left':
|
|
118
|
+
return { a: -100, b: 100 };
|
|
119
|
+
case 'bottom-right':
|
|
120
|
+
return { a: 100, b: 100 };
|
|
121
|
+
default:
|
|
122
|
+
return { a: -100, b: 0 };
|
|
123
|
+
}
|
|
111
124
|
};
|
|
125
|
+
let animationConfig;
|
|
126
|
+
if (animationType === ThemeAnimationType.SLIDE) {
|
|
127
|
+
// Use custom coordinates if provided, otherwise use direction
|
|
128
|
+
const fromCoords = slideFromX !== undefined && slideFromY !== undefined
|
|
129
|
+
? { a: slideFromX, b: slideFromY }
|
|
130
|
+
: getSlideFromCoords(slideDirection);
|
|
131
|
+
animationConfig = {
|
|
132
|
+
a: fromCoords.a,
|
|
133
|
+
b: fromCoords.b,
|
|
134
|
+
x: slideToX,
|
|
135
|
+
y: slideToY,
|
|
136
|
+
duration,
|
|
137
|
+
easing,
|
|
138
|
+
animationType,
|
|
139
|
+
blurAmount,
|
|
140
|
+
styleId,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// For circle and blur circle animations, use button center
|
|
145
|
+
const { top, left, width, height } = ref.current.getBoundingClientRect();
|
|
146
|
+
const x = left + width / 2;
|
|
147
|
+
const y = top + height / 2;
|
|
148
|
+
animationConfig = {
|
|
149
|
+
x,
|
|
150
|
+
y,
|
|
151
|
+
duration,
|
|
152
|
+
easing,
|
|
153
|
+
animationType,
|
|
154
|
+
blurAmount,
|
|
155
|
+
styleId,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
112
158
|
if (animationType === ThemeAnimationType.BLUR_CIRCLE) {
|
|
113
159
|
createBlurCircleAnimation(animationConfig);
|
|
114
160
|
}
|
|
161
|
+
if (animationType === ThemeAnimationType.SLIDE) {
|
|
162
|
+
createSlideAnimation(animationConfig);
|
|
163
|
+
}
|
|
164
|
+
// Start the view transition
|
|
115
165
|
await document.startViewTransition(() => {
|
|
116
166
|
flushSync(() => {
|
|
117
167
|
setTheme(newTheme);
|
|
@@ -120,7 +170,19 @@ export const useThemeAnimation = (props = {}) => {
|
|
|
120
170
|
if (animationType === ThemeAnimationType.CIRCLE) {
|
|
121
171
|
createCircleAnimation(animationConfig);
|
|
122
172
|
}
|
|
123
|
-
}, [
|
|
173
|
+
}, [
|
|
174
|
+
setTheme,
|
|
175
|
+
duration,
|
|
176
|
+
easing,
|
|
177
|
+
animationType,
|
|
178
|
+
blurAmount,
|
|
179
|
+
styleId,
|
|
180
|
+
slideDirection,
|
|
181
|
+
slideFromX,
|
|
182
|
+
slideFromY,
|
|
183
|
+
slideToX,
|
|
184
|
+
slideToY,
|
|
185
|
+
]);
|
|
124
186
|
const toggleTheme = useCallback(async () => {
|
|
125
187
|
const newTheme = resolvedTheme === 'dark' ? 'light' : 'dark';
|
|
126
188
|
await switchTheme(newTheme);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
export declare enum ThemeAnimationType {
|
|
3
3
|
CIRCLE = "circle",
|
|
4
|
-
BLUR_CIRCLE = "blur-circle"
|
|
4
|
+
BLUR_CIRCLE = "blur-circle",
|
|
5
|
+
SLIDE = "slide"
|
|
5
6
|
}
|
|
7
|
+
export type SlideDirection = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
6
8
|
export type Theme = 'light' | 'dark' | 'system';
|
|
7
9
|
export type ColorTheme = string;
|
|
8
10
|
export interface UseThemeAnimationProps {
|
|
@@ -23,6 +25,11 @@ export interface UseThemeAnimationProps {
|
|
|
23
25
|
colorTheme?: ColorTheme;
|
|
24
26
|
onThemeChange?: (theme: Theme) => void;
|
|
25
27
|
onColorThemeChange?: (colorTheme: ColorTheme) => void;
|
|
28
|
+
slideDirection?: SlideDirection;
|
|
29
|
+
slideFromX?: number;
|
|
30
|
+
slideFromY?: number;
|
|
31
|
+
slideToX?: number;
|
|
32
|
+
slideToY?: number;
|
|
26
33
|
}
|
|
27
34
|
export interface UseThemeAnimationReturn {
|
|
28
35
|
ref: RefObject<HTMLButtonElement | null>;
|
package/dist/esm/types/index.js
CHANGED
|
@@ -6,6 +6,8 @@ export declare const resolveTheme: (theme: string) => "light" | "dark";
|
|
|
6
6
|
export declare const supportsViewTransitions: () => boolean;
|
|
7
7
|
export declare const prefersReducedMotion: () => boolean;
|
|
8
8
|
export interface AnimationConfig {
|
|
9
|
+
a?: number;
|
|
10
|
+
b?: number;
|
|
9
11
|
x: number;
|
|
10
12
|
y: number;
|
|
11
13
|
duration: number;
|
|
@@ -15,4 +17,5 @@ export interface AnimationConfig {
|
|
|
15
17
|
styleId: string;
|
|
16
18
|
}
|
|
17
19
|
export declare const createCircleAnimation: (config: AnimationConfig) => void;
|
|
20
|
+
export declare const createSlideAnimation: (config: AnimationConfig) => void;
|
|
18
21
|
export declare const createBlurCircleAnimation: (config: AnimationConfig) => void;
|
|
@@ -71,6 +71,38 @@ export const createCircleAnimation = (config) => {
|
|
|
71
71
|
pseudoElement: '::view-transition-new(root)',
|
|
72
72
|
});
|
|
73
73
|
};
|
|
74
|
+
export const createSlideAnimation = (config) => {
|
|
75
|
+
const { a = -100, b = 0, x = 0, y = 0, duration, styleId } = config;
|
|
76
|
+
const styleElement = document.createElement('style');
|
|
77
|
+
styleElement.id = styleId;
|
|
78
|
+
styleElement.textContent = `
|
|
79
|
+
::view-transition-old(root) {
|
|
80
|
+
animation-delay: ${duration}ms;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
::view-transition-new(root) {
|
|
84
|
+
animation: move-in ${duration}ms;
|
|
85
|
+
animation-timing-function: cubic-bezier(0.66, 0, 0.34, 1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@keyframes move-in {
|
|
89
|
+
from {
|
|
90
|
+
translate: ${a}% ${b}%;
|
|
91
|
+
}
|
|
92
|
+
to {
|
|
93
|
+
translate: ${x}% ${y}%;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
document.head.appendChild(styleElement);
|
|
98
|
+
// Clean up after animation
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
const styleElement = document.getElementById(styleId);
|
|
101
|
+
if (styleElement) {
|
|
102
|
+
styleElement.remove();
|
|
103
|
+
}
|
|
104
|
+
}, duration);
|
|
105
|
+
};
|
|
74
106
|
export const createBlurCircleAnimation = (config) => {
|
|
75
107
|
const { x, y, duration, easing, blurAmount, styleId } = config;
|
|
76
108
|
const existingStyle = document.getElementById(styleId);
|