-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathuseAnimation.ts
More file actions
117 lines (104 loc) · 3.01 KB
/
Copy pathuseAnimation.ts
File metadata and controls
117 lines (104 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
* ObjectUI
* Copyright (c) 2024-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { useMemo } from 'react';
/** Animation trigger type */
export type AnimationTriggerType = 'enter' | 'exit' | 'hover' | 'focus' | 'click';
/** Easing presets aligned with EasingFunctionSchema */
export type EasingPreset =
| 'linear'
| 'ease'
| 'ease-in'
| 'ease-out'
| 'ease-in-out'
| 'spring';
/** Transition preset aligned with TransitionPresetSchema */
export type TransitionPresetType =
| 'fade'
| 'slide-up'
| 'slide-down'
| 'slide-left'
| 'slide-right'
| 'scale'
| 'scale-fade'
| 'none';
export interface AnimationConfig {
/** The animation/transition preset */
preset?: TransitionPresetType;
/** Duration in milliseconds */
duration?: number;
/** Delay in milliseconds */
delay?: number;
/** Easing function */
easing?: EasingPreset;
/** Whether animation is enabled */
enabled?: boolean;
}
/** Resolved CSS classes and styles for animation */
export interface AnimationResult {
/** Tailwind CSS classes for the animation */
className: string;
/** Inline style for custom durations/delays (only when needed) */
style: React.CSSProperties;
}
const EASING_MAP: Record<EasingPreset, string> = {
linear: 'linear',
ease: 'ease',
'ease-in': 'ease-in',
'ease-out': 'ease-out',
'ease-in-out': 'ease-in-out',
spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
};
const PRESET_CLASSES: Record<TransitionPresetType, string> = {
fade: 'animate-in fade-in',
'slide-up': 'animate-in slide-in-from-bottom-2',
'slide-down': 'animate-in slide-in-from-top-2',
'slide-left': 'animate-in slide-in-from-right-2',
'slide-right': 'animate-in slide-in-from-left-2',
scale: 'animate-in zoom-in-95',
'scale-fade': 'animate-in fade-in zoom-in-95',
none: '',
};
/**
* Hook for applying spec-driven animations to components.
* Implements ComponentAnimationSchema and TransitionPresetSchema
* from @objectstack/spec v2.0.7.
*
* @example
* ```tsx
* function Card() {
* const animation = useAnimation({ preset: 'fade', duration: 200, easing: 'ease-out' });
* return <div className={animation.className} style={animation.style}>...</div>;
* }
* ```
*/
export function useAnimation(config: AnimationConfig = {}): AnimationResult {
const {
preset = 'none',
duration,
delay,
easing = 'ease-out',
enabled = true,
} = config;
return useMemo(() => {
if (!enabled || preset === 'none') {
return { className: '', style: {} };
}
const className = PRESET_CLASSES[preset] || '';
const style: React.CSSProperties = {};
if (duration !== undefined) {
style.animationDuration = `${duration}ms`;
}
if (delay !== undefined) {
style.animationDelay = `${delay}ms`;
}
if (easing) {
style.animationTimingFunction = EASING_MAP[easing] || easing;
}
return { className, style };
}, [preset, duration, delay, easing, enabled]);
}