77// transition(this.element, false)
88export async function transition ( element , state , transitionOptions = { } ) {
99 if ( ! ! state ) {
10- enter ( element , transitionOptions )
10+ await enter ( element , transitionOptions )
1111 } else {
12- leave ( element , transitionOptions )
12+ await leave ( element , transitionOptions )
1313 }
1414}
1515
@@ -22,62 +22,121 @@ export async function transition(element, state, transitionOptions = {}) {
2222// data-transition-leave-to="bg-opacity-0"
2323export async function enter ( element , transitionOptions = { } ) {
2424 const transitionClasses = element . dataset . transitionEnter || transitionOptions . enter || 'enter'
25- const fromClasses =
26- element . dataset . transitionEnterFrom || transitionOptions . enterFrom || 'enter-from'
25+ const fromClasses = element . dataset . transitionEnterFrom || transitionOptions . enterFrom || 'enter-from'
2726 const toClasses = element . dataset . transitionEnterTo || transitionOptions . enterTo || 'enter-to'
2827 const toggleClass = element . dataset . toggleClass || transitionOptions . toggleClass || 'hidden'
2928
30- // Prepare transition
31- element . classList . add ( ...transitionClasses . split ( ' ' ) )
32- element . classList . add ( ...fromClasses . split ( ' ' ) )
33- element . classList . remove ( ...toClasses . split ( ' ' ) )
34- element . classList . remove ( ...toggleClass . split ( ' ' ) )
35-
36- await nextFrame ( )
37-
38- element . classList . remove ( ...fromClasses . split ( ' ' ) )
39- element . classList . add ( ...toClasses . split ( ' ' ) )
40-
41- try {
42- await afterTransition ( element )
43- } finally {
44- element . classList . remove ( ...transitionClasses . split ( ' ' ) )
45- }
29+ return performTransitions ( element , {
30+ firstFrame ( ) {
31+ element . classList . add ( ...transitionClasses . split ( ' ' ) )
32+ element . classList . add ( ...fromClasses . split ( ' ' ) )
33+ element . classList . remove ( ...toClasses . split ( ' ' ) )
34+ element . classList . remove ( ...toggleClass . split ( ' ' ) )
35+ } ,
36+ secondFrame ( ) {
37+ element . classList . remove ( ...fromClasses . split ( ' ' ) )
38+ element . classList . add ( ...toClasses . split ( ' ' ) )
39+ } ,
40+ ending ( ) {
41+ element . classList . remove ( ...transitionClasses . split ( ' ' ) )
42+ }
43+ } )
4644}
4745
4846export async function leave ( element , transitionOptions = { } ) {
4947 const transitionClasses = element . dataset . transitionLeave || transitionOptions . leave || 'leave'
50- const fromClasses =
51- element . dataset . transitionLeaveFrom || transitionOptions . leaveFrom || 'leave-from'
48+ const fromClasses = element . dataset . transitionLeaveFrom || transitionOptions . leaveFrom || 'leave-from'
5249 const toClasses = element . dataset . transitionLeaveTo || transitionOptions . leaveTo || 'leave-to'
5350 const toggleClass = element . dataset . toggleClass || transitionOptions . toggle || 'hidden'
5451
55- // Prepare transition
56- element . classList . add ( ...transitionClasses . split ( ' ' ) )
57- element . classList . add ( ...fromClasses . split ( ' ' ) )
58- element . classList . remove ( ...toClasses . split ( ' ' ) )
59-
60- await nextFrame ( )
52+ return performTransitions ( element , {
53+ firstFrame ( ) {
54+ element . classList . add ( ...fromClasses . split ( ' ' ) )
55+ element . classList . remove ( ...toClasses . split ( ' ' ) )
56+ element . classList . add ( ...transitionClasses . split ( ' ' ) )
57+ } ,
58+ secondFrame ( ) {
59+ element . classList . remove ( ...fromClasses . split ( ' ' ) )
60+ element . classList . add ( ...toClasses . split ( ' ' ) )
61+ } ,
62+ ending ( ) {
63+ element . classList . remove ( ...transitionClasses . split ( ' ' ) )
64+ element . classList . add ( ...toggleClass . split ( ' ' ) )
65+ }
66+ } )
67+ }
6168
62- element . classList . remove ( ...fromClasses . split ( ' ' ) )
63- element . classList . add ( ...toClasses . split ( ' ' ) )
69+ function setupTransition ( element ) {
70+ element . _stimulus_transition = {
71+ timeout : null ,
72+ interrupted : false
73+ }
74+ }
6475
65- try {
66- await afterTransition ( element )
67- } finally {
68- element . classList . remove ( ...transitionClasses . split ( ' ' ) )
69- element . classList . add ( ...toggleClass . split ( ' ' ) )
76+ export function cancelTransition ( element ) {
77+ if ( element . _stimulus_transition && element . _stimulus_transition . interrupt ) {
78+ element . _stimulus_transition . interrupt ( )
7079 }
7180}
7281
73- function nextFrame ( ) {
74- return new Promise ( resolve => {
82+ function performTransitions ( element , transitionStages ) {
83+ if ( element . _stimulus_transition ) cancelTransition ( element )
84+
85+ let interrupted , firstStageComplete , secondStageComplete
86+ setupTransition ( element )
87+
88+ element . _stimulus_transition . cleanup = ( ) => {
89+ if ( ! firstStageComplete ) transitionStages . firstFrame ( )
90+ if ( ! secondStageComplete ) transitionStages . secondFrame ( )
91+
92+ transitionStages . ending ( )
93+ element . _stimulus_transition = null
94+ }
95+
96+ element . _stimulus_transition . interrupt = ( ) => {
97+ interrupted = true
98+ if ( element . _stimulus_transition . timeout ) {
99+ clearTimeout ( element . _stimulus_transition . timeout )
100+ }
101+ element . _stimulus_transition . cleanup ( )
102+ }
103+
104+ return new Promise ( ( resolve ) => {
105+ if ( interrupted ) return
106+
75107 requestAnimationFrame ( ( ) => {
76- requestAnimationFrame ( resolve )
108+ if ( interrupted ) return
109+
110+ transitionStages . firstFrame ( )
111+ firstStageComplete = true
112+
113+ requestAnimationFrame ( ( ) => {
114+ if ( interrupted ) return
115+
116+ transitionStages . secondFrame ( )
117+ secondStageComplete = true
118+
119+ if ( element . _stimulus_transition ) {
120+ element . _stimulus_transition . timeout = setTimeout ( ( ) => {
121+ if ( interrupted ) {
122+ resolve ( )
123+ return
124+ }
125+
126+ element . _stimulus_transition . cleanup ( )
127+ resolve ( )
128+ } , getAnimationDuration ( element ) )
129+ }
130+ } )
77131 } )
78132 } )
79133}
80134
81- function afterTransition ( element ) {
82- return Promise . all ( element . getAnimations ( ) . map ( animation => animation . finished ) )
135+ function getAnimationDuration ( element ) {
136+ let duration = Number ( getComputedStyle ( element ) . transitionDuration . replace ( / , .* / , '' ) . replace ( 's' , '' ) ) * 1000
137+ let delay = Number ( getComputedStyle ( element ) . transitionDelay . replace ( / , .* / , '' ) . replace ( 's' , '' ) ) * 1000
138+
139+ if ( duration === 0 ) duration = Number ( getComputedStyle ( element ) . animationDuration . replace ( 's' , '' ) ) * 1000
140+
141+ return duration + delay
83142}
0 commit comments