@pyreon/runtime-dom 0.12.13 → 0.12.14

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/src/transition.ts CHANGED
@@ -93,17 +93,25 @@ export function Transition(props: TransitionProps): VNodeChild {
93
93
  requestAnimationFrame(() => {
94
94
  el.classList.remove(cls.ef)
95
95
  el.classList.add(cls.et)
96
+ let safetyTimer: ReturnType<typeof setTimeout> | null = null
96
97
  const done = () => {
97
98
  // Remove both listeners — only one fires, so clean up the other
98
99
  el.removeEventListener('transitionend', done)
99
100
  el.removeEventListener('animationend', done)
101
+ // Clear the safety timeout — without this, when transitionend fires
102
+ // normally the 5s timer would still fire later and re-invoke done(),
103
+ // leaking timer refs and re-firing onAfterEnter.
104
+ if (safetyTimer !== null) {
105
+ clearTimeout(safetyTimer)
106
+ safetyTimer = null
107
+ }
100
108
  el.classList.remove(cls.ea, cls.et)
101
109
  props.onAfterEnter?.(el)
102
110
  }
103
111
  el.addEventListener('transitionend', done, { once: true })
104
112
  el.addEventListener('animationend', done, { once: true })
105
113
  // Safety timeout: if CSS animation never fires (bad CSS, off-screen), force cleanup
106
- setTimeout(done, 5000)
114
+ safetyTimer = setTimeout(done, 5000)
107
115
  })
108
116
  }
109
117
 
@@ -114,10 +122,16 @@ export function Transition(props: TransitionProps): VNodeChild {
114
122
  requestAnimationFrame(() => {
115
123
  el.classList.remove(cls.lf)
116
124
  el.classList.add(cls.lt)
125
+ let safetyTimer: ReturnType<typeof setTimeout> | null = null
117
126
  const done = () => {
118
127
  // Remove both listeners — only one fires, so clean up the other
119
128
  el.removeEventListener('transitionend', done)
120
129
  el.removeEventListener('animationend', done)
130
+ // Clear the safety timeout (see applyEnter for rationale).
131
+ if (safetyTimer !== null) {
132
+ clearTimeout(safetyTimer)
133
+ safetyTimer = null
134
+ }
121
135
  el.classList.remove(cls.la, cls.lt)
122
136
  pendingLeaveCancel = null
123
137
  isMounted.set(false)
@@ -126,12 +140,16 @@ export function Transition(props: TransitionProps): VNodeChild {
126
140
  pendingLeaveCancel = () => {
127
141
  el.removeEventListener('transitionend', done)
128
142
  el.removeEventListener('animationend', done)
143
+ if (safetyTimer !== null) {
144
+ clearTimeout(safetyTimer)
145
+ safetyTimer = null
146
+ }
129
147
  el.classList.remove(cls.lf, cls.la, cls.lt)
130
148
  }
131
149
  el.addEventListener('transitionend', done, { once: true })
132
150
  el.addEventListener('animationend', done, { once: true })
133
151
  // Safety timeout: if CSS animation never fires, force cleanup
134
- setTimeout(done, 5000)
152
+ safetyTimer = setTimeout(done, 5000)
135
153
  })
136
154
  }
137
155