1
- import { type ExtractPropTypes , type PropType , type VNode , computed , nextTick , onUpdated , reactive } from 'vue'
1
+ import {
2
+ type ExtractPropTypes ,
3
+ type PropType ,
4
+ type VNode ,
5
+ computed ,
6
+ nextTick ,
7
+ onUpdated ,
8
+ reactive ,
9
+ } from 'vue'
2
10
import type { LooseRequired } from '@vue/shared'
3
11
import defu from 'defu'
4
12
import * as presets from '../presets'
5
13
import type { MotionInstance } from '../types/instance'
6
- import type { MotionVariants , StyleProperties , Variant } from '../types/variants'
14
+ import type {
15
+ MotionVariants ,
16
+ StyleProperties ,
17
+ Variant ,
18
+ } from '../types/variants'
7
19
import { useMotion } from '../useMotion'
8
20
9
21
/**
@@ -78,15 +90,44 @@ export const MotionComponentProps = {
78
90
} ,
79
91
}
80
92
93
+ function isObject ( val : unknown ) : val is Record < any , any > {
94
+ return Object . prototype . toString . call ( val ) === '[object Object]'
95
+ }
96
+
97
+ /**
98
+ * Deep clone object/array
99
+ */
100
+ function clone < T > ( v : T ) : any {
101
+ if ( Array . isArray ( v ) ) {
102
+ return v . map ( clone )
103
+ }
104
+
105
+ if ( isObject ( v ) ) {
106
+ const res : any = { }
107
+ for ( const key in v ) {
108
+ res [ key ] = clone ( v [ key as keyof typeof v ] )
109
+ }
110
+ return res
111
+ }
112
+
113
+ return v
114
+ }
115
+
81
116
/**
82
117
* Shared logic for <Motion> and <MotionGroup>
83
118
*/
84
- export function setupMotionComponent ( props : LooseRequired < ExtractPropTypes < typeof MotionComponentProps > > ) {
119
+ export function setupMotionComponent (
120
+ props : LooseRequired < ExtractPropTypes < typeof MotionComponentProps > > ,
121
+ ) {
85
122
// Motion instance map
86
- const instances = reactive < { [ key : number ] : MotionInstance < string , MotionVariants < string > > } > ( { } )
123
+ const instances = reactive < {
124
+ [ key : number ] : MotionInstance < string , MotionVariants < string > >
125
+ } > ( { } )
87
126
88
127
// Preset variant or empty object if none is provided
89
- const preset = computed ( ( ) => ( props . preset ? structuredClone ( presets [ props . preset ] ) : { } ) )
128
+ const preset = computed ( ( ) =>
129
+ props . preset ? structuredClone ( presets [ props . preset ] ) : { } ,
130
+ )
90
131
91
132
// Motion configuration using inline prop variants (`:initial` ...)
92
133
const propsConfig = computed ( ( ) => ( {
@@ -100,17 +141,19 @@ export function setupMotionComponent(props: LooseRequired<ExtractPropTypes<typeo
100
141
focused : props . focused ,
101
142
} ) )
102
143
103
- // Merged motion configuration using `props.preset`, inline prop variants (`:initial` ...), and `props.variants`
104
- const motionConfig = computed ( ( ) => {
105
- const config = defu ( { } , propsConfig . value , preset . value , props . variants || { } )
106
-
144
+ // Applies transition shorthand helpers to passed config
145
+ function applyTransitionHelpers (
146
+ config : typeof propsConfig . value ,
147
+ values : Partial < Pick < typeof props , 'delay' | 'duration' > > ,
148
+ ) {
107
149
for ( const transitionKey of [ 'delay' , 'duration' ] as const ) {
108
- if ( ! props [ transitionKey ] )
150
+ if ( values [ transitionKey ] == null )
109
151
continue
110
152
111
- const transitionValueParsed = Number . parseInt ( props [ transitionKey ] as string )
153
+ const transitionValueParsed = Number . parseInt (
154
+ values [ transitionKey ] as string ,
155
+ )
112
156
113
- // TODO: extract to utility function
114
157
// Apply transition property to existing variants where applicable
115
158
for ( const variantKey of [ 'enter' , 'visible' , 'visibleOnce' ] as const ) {
116
159
const variantConfig = config [ variantKey ]
@@ -125,6 +168,18 @@ export function setupMotionComponent(props: LooseRequired<ExtractPropTypes<typeo
125
168
}
126
169
127
170
return config
171
+ }
172
+
173
+ // Merged motion configuration using `props.preset`, inline prop variants (`:initial` ...), and `props.variants`
174
+ const motionConfig = computed ( ( ) => {
175
+ const config = defu (
176
+ { } ,
177
+ propsConfig . value ,
178
+ preset . value ,
179
+ props . variants || { } ,
180
+ )
181
+
182
+ return applyTransitionHelpers ( { ...config } , props )
128
183
} )
129
184
130
185
// Replay animations on component update Vue
@@ -159,9 +214,18 @@ export function setupMotionComponent(props: LooseRequired<ExtractPropTypes<typeo
159
214
// Merge node style with variant style
160
215
node . props . style = { ...node . props . style , ...style }
161
216
217
+ // Apply transition helpers, this may differ if `node` is a child node
218
+ const elementMotionConfig = applyTransitionHelpers (
219
+ clone ( motionConfig . value ) ,
220
+ node . props as Partial < Pick < typeof props , 'delay' | 'duration' > > ,
221
+ )
222
+
162
223
// Track motion instance locally using `instances`
163
224
node . props . onVnodeMounted = ( { el } ) => {
164
- instances [ index ] = useMotion < string , MotionVariants < string > > ( el as any , motionConfig . value )
225
+ instances [ index ] = useMotion < string , MotionVariants < string > > (
226
+ el as any ,
227
+ elementMotionConfig ,
228
+ )
165
229
}
166
230
167
231
return node
0 commit comments