UNPKG

@ssgoi/core

Version:

Core animation engine for SSGOI - Native app-like page transitions with spring physics

1,084 lines (1,083 loc) 31.1 kB
var _ = Object.defineProperty; var F = (n, t, e) => t in n ? _(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e; var m = (n, t, e) => F(n, typeof t != "symbol" ? t + "" : t, e); import { isMultiSpring as V } from "./types.js"; import { isSingleSpring as at } from "./types.js"; import { j as lt } from "./jaemin-BWkrXq0V.js"; class $ { constructor() { m(this, "listeners", /* @__PURE__ */ new Set()); m(this, "rafId", null); m(this, "isRunning", !1); // Timing m(this, "startTime", Date.now()); m(this, "lastUpdate", this.startTime); m(this, "elapsed", 0); // Lag smoothing (mobile optimization) m(this, "lagThreshold", 500); // ms - detect tab switches, backgrounding m(this, "adjustedLag", 33); // ms - max time jump to prevent animation skip m(this, "maxDeltaTime", 33); // ms - clamp for physics stability (30fps minimum) // FPS throttling m(this, "gap", 1e3 / 240); // max 240fps m(this, "nextTime", this.gap); m(this, "tick", () => { const t = Date.now(); let i = t - this.lastUpdate; i > this.lagThreshold || i < 0 ? (this.startTime += i - this.adjustedLag, i = this.adjustedLag) : i > this.maxDeltaTime && (i = this.maxDeltaTime), this.lastUpdate = t; const r = t - this.startTime, o = r - this.nextTime; if (o > 0) { const s = i / 1e3; this.elapsed = r / 1e3, this.nextTime += o + (o >= this.gap ? 4 : this.gap - o), this.listeners.forEach((d) => { d(s, this.elapsed); }); } this.listeners.size > 0 ? this.rafId = requestAnimationFrame(this.tick) : (this.isRunning = !1, this.rafId = null); }); } subscribe(t) { return this.listeners.add(t), this.isRunning || (this.isRunning = !0, this.rafId = requestAnimationFrame(this.tick)), () => { this.unsubscribe(t); }; } unsubscribe(t) { this.listeners.delete(t), this.listeners.size === 0 && this.rafId !== null && (cancelAnimationFrame(this.rafId), this.isRunning = !1, this.rafId = null); } /** * Configure lag smoothing behavior * @param threshold - Time threshold to detect lag (default: 500ms) * @param adjustedLag - Max time jump when lag detected (default: 33ms) */ lagSmoothing(t, e) { this.lagThreshold = t, this.adjustedLag = Math.min(e, t); } /** * Set maximum FPS * @param fps - Target FPS (default: 240) */ fps(t) { this.gap = 1e3 / t, this.nextTime = this.elapsed * 1e3 + this.gap; } getListenerCount() { return this.listeners.size; } getElapsed() { return this.elapsed; } } const I = new $(); function k(n) { const { from: t, to: e, stiffness: i, damping: r, mass: o, velocity: s = 0, onUpdate: d, onComplete: l } = n; let h = t, u = s, a = !0; const p = Math.sqrt(i / o), c = r / (2 * Math.sqrt(i * o)), g = 0.01, w = 0.01; let f = 0; const y = 0.05, S = (T) => { if (!a) return; const C = Math.min(T, 0.033), v = -2 * C * c * p * u, b = C * p * p * (e - h); u += v + b, h += C * u; const E = Math.abs(e - h), A = Math.abs(u); if (E < g && A < w) { if (f += C, f >= y) { h = e, u = 0, a = !1, d(h), I.unsubscribe(S), l(); return; } } else f = 0; d(h); }; return I.subscribe(S), { stop: () => { a && (a = !1, I.unsubscribe(S)); } }; } class O { constructor(t) { m(this, "options"); m(this, "currentValue"); m(this, "velocity"); m(this, "isAnimating", !1); m(this, "controls", null); // For object animations m(this, "animationMap", null); m(this, "activeAnimations", /* @__PURE__ */ new Set()); m(this, "completedAnimations", /* @__PURE__ */ new Set()); m(this, "updatedProperties", /* @__PURE__ */ new Set()); m(this, "animate", (t = !1) => { !this.isAnimating && this.options.onStart && this.options.onStart(), this.isAnimating = !0; const e = t ? this.options.from : this.options.to; if (typeof this.currentValue == "object" && this.currentValue !== null) { this.animateObject(e); return; } let i = this.currentValue, r = performance.now(); const o = { from: this.currentValue, to: e, stiffness: this.options.spring.stiffness, damping: this.options.spring.damping, mass: 1 }; typeof this.velocity == "number" && (o.velocity = this.velocity * 1e3), this.controls = k({ ...o, onUpdate: (s) => { const d = performance.now(), l = (d - r) / 1e3; if (l > 0) { if (typeof s == "number" && typeof i == "number") { const h = (s - i) / l; this.velocity = h / 1e3; } else if (typeof s == "object" && s !== null) { const h = {}; Object.keys(s).forEach((u) => { const a = s[u], p = i[u]; if (typeof a == "number" && typeof p == "number") { const c = (a - p) / l; h[u] = c / 1e3; } }), this.velocity = h; } i = s, r = d; } this.currentValue = s, this.options.onUpdate(s); }, onComplete: () => { this.currentValue = e, this.isAnimating = !1, this.controls = null, typeof this.velocity == "object" && this.velocity !== null ? Object.keys(this.velocity).forEach((s) => { this.velocity[s] = 0; }) : this.velocity = 0, this.options.onComplete(); } }); }); m(this, "animateObject", (t) => { const e = this.currentValue, i = Object.keys(e); this.animationMap = /* @__PURE__ */ new Map(), this.activeAnimations.clear(), this.completedAnimations.clear(), this.updatedProperties.clear(); const r = { ...e }, o = {}; let s = !1; const d = () => { const l = i.filter((u) => !this.completedAnimations.has(u)); if (l.every( (u) => this.updatedProperties.has(u) ) && l.length > 0) { if (typeof this.velocity == "object" && this.velocity) { const u = performance.now(); Object.keys(r).forEach((a) => { if (o[a]) { const p = (u - o[a]) / 1e3, c = this.currentValue[a], g = r[a]; typeof c == "number" && typeof g == "number" && p > 0 && (this.velocity[a] = (g - c) / p / 1e3); } o[a] = u; }); } this.currentValue = { ...r }, this.options.onUpdate(this.currentValue), this.updatedProperties.clear(), !s && this.options.onStart && (s = !0, this.options.onStart()); } }; i.forEach((l) => { this.activeAnimations.add(l); const h = { from: e[l], to: t[l], stiffness: this.options.spring.stiffness, damping: this.options.spring.damping, mass: 1 }; typeof this.velocity == "object" && this.velocity && l in this.velocity && (h.velocity = this.velocity[l] * 1e3); const u = k({ ...h, onUpdate: (a) => { r[l] = a, this.updatedProperties.add(l), d(); }, onComplete: () => { this.completedAnimations.add(l), this.updatedProperties.delete(l), this.completedAnimations.size === i.length && (this.currentValue = t, this.isAnimating = !1, this.animationMap = null, typeof this.velocity == "object" && this.velocity && Object.keys(this.velocity).forEach((a) => { this.velocity[a] = 0; }), this.options.onComplete()); } }); this.animationMap.set(l, u); }); }); if (this.options = { from: t.from ?? 0, to: t.to ?? 1, spring: t.spring ?? { stiffness: 100, damping: 10 }, onUpdate: t.onUpdate ?? (() => { }), onComplete: t.onComplete ?? (() => { }), onStart: t.onStart }, this.currentValue = this.options.from, typeof this.options.from == "object" && this.options.from !== null) { const e = {}; Object.keys(this.options.from).forEach((i) => { e[i] = 0; }), this.velocity = e; } else this.velocity = 0; } // Animation control methods forward() { this.stop(), this.animate(!1); } backward() { this.stop(), this.animate(!0); } reverse() { const t = this.options.from; if (this.options.from = this.options.to, this.options.to = t, this.isAnimating) { const e = this.shouldReverse(); this.stop(), this.animate(!e); } } shouldReverse() { return typeof this.currentValue == "number" && typeof this.options.from == "number" && typeof this.options.to == "number" ? this.currentValue > (this.options.from + this.options.to) / 2 : !1; } stop() { this.isAnimating = !1, this.controls && (this.controls.stop(), this.controls = null), this.animationMap && (this.animationMap.forEach((t) => { t.stop(); }), this.animationMap.clear(), this.animationMap = null), this.updatedProperties.clear(); } // State getters getVelocity() { return this.velocity; } getCurrentValue() { return this.currentValue; } getIsAnimating() { return this.isAnimating; } getCurrentState() { return { type: "single", position: this.currentValue, velocity: this.velocity, from: this.options.from, to: this.options.to }; } // State setters setVelocity(t) { this.velocity = t; } setValue(t) { this.currentValue = t; } // Configuration updateOptions(t) { if (this.options = { ...this.options, ...t }, this.isAnimating && this.controls) { const e = this.shouldReverse(); this.stop(), this.animate(!e); } } // Static factory method static fromState(t, e) { const i = new O(e); return i.setValue(t.position), i.setVelocity(t.velocity), i; } } class j { constructor(t) { m(this, "config"); m(this, "animators", /* @__PURE__ */ new Map()); m(this, "springOrder", []); m(this, "completedCount", 0); m(this, "completedAnimators", /* @__PURE__ */ new Set()); m(this, "direction", "forward"); m(this, "idCounter", 0); m(this, "timeoutIds", []); this.config = t, this.initializeAnimators(); } generateId() { return `spring_${this.idCounter++}`; } initializeAnimators() { this.config.springs.forEach((t) => { const e = this.generateId(), i = new O({ from: t.from, to: t.to, spring: t.spring, onUpdate: t.tick, onComplete: () => this.onAnimatorComplete(e), onStart: t.onStart }); this.animators.set(e, { id: e, item: t, animator: i, startTime: null }), this.springOrder.push(e); }); } /** * Normalize schedule configuration to unified internal representation * Uses discriminated union and early return pattern * * @param direction - "forward" or "backward" direction */ normalizeSchedule(t) { const e = this.config.schedule ?? "overlap"; return this.springOrder.map((i, r) => { const o = this.animators.get(i); if (!o) throw new Error( `AnimationScheduler: animator with id "${i}" not found during normalization` ); if (e === "overlap") return { type: "offset", id: i, delay: 0 }; if (e === "wait") return t === "forward" && r === 0 ? { type: "offset", id: i, delay: 0 } : t === "backward" && r === this.springOrder.length - 1 ? { type: "offset", id: i, delay: 0 } : { type: "wait", id: i }; const s = o.item.offset ?? 0; if (t === "forward") return { type: "offset", id: i, delay: s }; const d = this.animators.size === 0 ? 0 : Math.max( ...Array.from(this.animators.values()).map( (l) => l.item.offset ?? 0 ) ); return { type: "offset", id: i, delay: d - s }; }); } onAnimatorComplete(t) { var i, r, o, s, d, l; const e = this.animators.get(t); if (!e) { console.warn( `AnimationScheduler: animator with id "${t}" not found on completion` ); return; } this.completedAnimators.has(t) || (this.completedAnimators.add(t), this.completedCount++, (r = (i = e.item).onComplete) == null || r.call(i), (s = (o = this.config).onProgress) == null || s.call(o, this.completedCount, this.config.springs.length), this.handleWaitModeChaining(t), this.completedCount === this.config.springs.length && ((l = (d = this.config).onEnd) == null || l.call(d))); } /** * Handle wait mode chaining: start next/previous spring after completion */ handleWaitModeChaining(t) { if (this.config.schedule !== "wait") return; const e = this.springOrder.indexOf(t); if (this.direction === "forward") { const o = this.springOrder[e + 1]; if (!o) return; const s = this.animators.get(o); s && s.startTime === null && this.startAnimator(o); return; } const i = this.springOrder[e - 1]; if (!i) return; const r = this.animators.get(i); r && r.startTime === null && this.startBackwardAnimator(i); } startAnimator(t) { const e = this.animators.get(t); if (!e) { console.warn( `AnimationScheduler: animator with id "${t}" not found on start` ); return; } if (e.startTime !== null) { console.warn( `AnimationScheduler: animator with id "${t}" already started` ); return; } e.startTime = Date.now(), e.animator.forward(); } startBackwardAnimator(t) { const e = this.animators.get(t); if (!e) { console.warn( `AnimationScheduler: animator with id "${t}" not found on backward start` ); return; } if (e.startTime !== null) { console.warn( `AnimationScheduler: animator with id "${t}" already started` ); return; } e.startTime = Date.now(), e.animator.backward(); } forward() { var e, i; this.stop(), this.direction = "forward", this.completedCount = 0, this.completedAnimators.clear(), (i = (e = this.config).onStart) == null || i.call(e), this.normalizeSchedule("forward").forEach((r) => { if (r.type === "wait") return; if (r.delay === 0) { this.startAnimator(r.id); return; } const o = window.setTimeout( () => this.startAnimator(r.id), r.delay ); this.timeoutIds.push(o); }); } backward() { var e, i; this.stop(), this.direction = "backward", this.completedCount = 0, this.completedAnimators.clear(), (i = (e = this.config).onStart) == null || i.call(e), this.normalizeSchedule("backward").forEach((r) => { if (r.type === "wait") return; if (r.delay === 0) { this.startBackwardAnimator(r.id); return; } const o = window.setTimeout( () => this.startBackwardAnimator(r.id), r.delay ); this.timeoutIds.push(o); }); } stop() { this.timeoutIds.forEach((t) => clearTimeout(t)), this.timeoutIds = [], this.animators.forEach((t) => { t.animator.stop(), t.startTime = null; }); } reverse(t) { ((t == null ? void 0 : t.offsetMode) ?? "immediate") !== "immediate" && console.warn( 'AnimationScheduler.reverse(): Only "immediate" offsetMode is currently supported. Falling back to "immediate".' ), this.direction = this.direction === "forward" ? "backward" : "forward", this.animators.forEach((i) => { if (i.startTime === null) return; const r = i.animator.getCurrentState(); if (r.type !== "single") return; if (r.position === r.to) { const s = O.fromState( { position: 1, velocity: 0 }, { from: i.item.to ?? 1, to: i.item.from ?? 0, spring: i.item.spring, onUpdate: i.item.tick, onComplete: () => this.onAnimatorComplete(i.id), onStart: i.item.onStart } ); s.forward(), i.animator = s; return; } i.animator.reverse(); }); } getCurrentState() { return { type: "multi", completed: this.completedCount, total: this.config.springs.length, direction: this.direction }; } } const U = Symbol.for("TRANSITION_STRATEGY"), H = (n) => ({ runIn: async (t) => { const { currentAnimation: e } = n; if (e && e.direction === "out") { const s = e.controller.getCurrentState(); if (e.controller.stop(), s.type === "multi") return e.controller.reverse(), { state: { position: 0, velocity: 0 }, from: 0, to: 1, direction: "forward" }; if (t.out) { const d = await t.out, { from: l = 1, to: h = 0 } = d; return { config: d, state: { position: s.position, velocity: s.velocity }, from: l, // OUT animation's from to: h, // OUT animation's to direction: "backward" // Will actually go 0→1 }; } } const i = await t.in; if (!i) return { state: { position: 0, velocity: 0 }, from: 0, to: 1, direction: "forward" }; if ("springs" in i) return { config: i, state: { position: 0, velocity: 0 }, from: 0, to: 1, direction: "forward" }; const { from: r = 0, to: o = 1 } = i; return { config: i, state: { position: r, velocity: 0 }, from: r, to: o, direction: "forward" }; }, runOut: async (t) => { const { currentAnimation: e } = n; if (e && e.direction === "in") { const s = e.controller.getCurrentState(); if (e.controller.stop(), s.type === "multi") return e.controller.reverse(), { state: { position: 1, velocity: 0 }, from: 1, to: 0, direction: "forward" }; if (t.in) { const d = await t.in, { from: l = 0, to: h = 1 } = d; return { config: d, state: { position: s.position, velocity: s.velocity }, from: l, // IN animation's from to: h, // IN animation's to direction: "backward" // Will actually go 1→0 }; } } const i = await t.out; if (!i) return { state: { position: 1, velocity: 0 }, from: 1, to: 0, direction: "forward" }; if ("springs" in i) return { config: i, state: { position: 1, velocity: 0 }, from: 1, to: 0, direction: "forward" }; const { from: r = 1, to: o = 0 } = i; return { config: i, state: { position: r, velocity: 0 }, from: r, to: o, direction: "forward" }; } }), N = () => ({ runIn: async (n) => { const t = await n.in; if (!t) return { state: { position: 0, velocity: 0 }, from: 0, to: 1, direction: "forward" }; if ("springs" in t) return { config: t, state: { position: 0, velocity: 0 }, from: 0, to: 1, direction: "forward" }; const { from: e = 0, to: i = 1 } = t; return { config: t, state: { position: e, velocity: 0 }, from: e, to: i, direction: "forward" }; }, runOut: async (n) => { const t = await n.out; if (!t) return { state: { position: 1, velocity: 0 }, from: 1, to: 0, direction: "forward" }; if ("springs" in t) return { config: t, state: { position: 1, velocity: 0 }, from: 1, to: 0, direction: "forward" }; const { from: e = 1, to: i = 0 } = t; return { config: t, state: { position: e, velocity: 0 }, from: e, to: i, direction: "forward" }; } }); function W(n, t) { var u; let e = null, i = null, r = null, o = null; const s = { get currentAnimation() { return e; } }, d = ((u = t == null ? void 0 : t.strategy) == null ? void 0 : u.call(t, s)) || H(s), l = async (a) => { var S, T, C; i && (i.remove(), i = null); const p = n(), c = p.in && await p.in(a), g = !(t != null && t.strategy) && p.out ? p.out(a) : void 0; if (!c) return; if (V(c)) { const v = { in: Promise.resolve(c), out: g && Promise.resolve(g) }; if (!(await d.runIn(v)).config) { e && (e.direction = "in"); return; } (S = c.prepare) == null || S.call(c, a), c.wait && await c.wait(); const E = new j({ ...c, onEnd: () => { var A; e = null, (A = c.onEnd) == null || A.call(c); } }); e = { controller: E, direction: "in" }, E.forward(); return; } const w = { in: Promise.resolve(c), out: g && Promise.resolve(g) }, f = await d.runIn(w); if (!f.config) return; if ("springs" in f.config) { console.error("Unexpected MultiSpringConfig in single-spring path"); return; } (C = (T = f.config).prepare) == null || C.call(T, a), f.config.wait && await f.config.wait(); const y = O.fromState(f.state, { from: f.from, to: f.to, spring: f.config.spring, onStart: f.config.onStart, onUpdate: f.config.tick, onComplete: () => { var v, b; e = null, (b = (v = f.config) == null ? void 0 : v.onEnd) == null || b.call(v); } }); e = { controller: y, direction: "in" }, f.direction === "forward" ? y.forward() : y.backward(); }, h = async (a) => { var S, T, C; i = a; const p = n(), c = p.out && await p.out(a); if (!c) return; if (V(c)) { const v = { in: void 0, // Don't fetch IN config for exit transitions out: Promise.resolve(c) }; if (!(await d.runOut(v)).config) { e && (e.direction = "out"), i && (i.remove(), i = null); return; } (S = c.prepare) == null || S.call(c, a), y(), c.wait && await c.wait(); const E = new j({ ...c, onEnd: () => { var A, P; (A = c.onEnd) == null || A.call(c), i && (i.remove(), i = null), e = null, (P = t == null ? void 0 : t.onCleanupEnd) == null || P.call(t); } }); e = { controller: E, direction: "out" }, E.forward(); return; } const g = { in: void 0, // Don't fetch IN config for exit transitions out: Promise.resolve(c) }, w = await d.runOut(g); if (!w.config) return; if ("springs" in w.config) { console.error("Unexpected MultiSpringConfig in single-spring path"); return; } (C = (T = w.config).prepare) == null || C.call(T, a), y(), w.config.wait && await w.config.wait(); const f = O.fromState(w.state, { from: w.from, to: w.to, spring: w.config.spring, onStart: w.config.onStart, onUpdate: w.config.tick, onComplete: () => { var v, b, E; (b = (v = w.config) == null ? void 0 : v.onEnd) == null || b.call(v), i && (i.remove(), i = null), e = null, (E = t == null ? void 0 : t.onCleanupEnd) == null || E.call(t); } }); e = { controller: f, direction: "out" }, w.direction === "forward" ? f.forward() : f.backward(); function y() { !r || !i || (o && r.contains(o) ? r.insertBefore(i, o) : r.appendChild(i)); } }; return (a) => { if (a) return r = a.parentElement, o = a.nextElementSibling, l(a), () => { if (a.isConnected) { const p = a.cloneNode(!0); h(p); } else h(a); }; }; } const Y = 3, X = (n) => { if (!n) return null; const t = n.split(` `).map((e) => e.trim()); for (let e = Y; e < Math.min(t.length, 6); e++) { const i = t[e], r = i == null ? void 0 : i.match(/\(?([^):]+):(\d+):(\d+)\)?$/); if (!r) continue; const o = r[1] ?? "", s = r[2] ?? "0", d = r[3] ?? "0"; return { file: o.split(/[\\/]/).pop() || o, line: s, column: d }; } return null; }, M = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map(); function B(n, t, e) { M.set(n, t); let i = R.get(n); return i || (i = W( () => { const r = M.get(n); return r || (console.warn(`Transition "${String(n)}" not found`), {}); }, { strategy: e, onCleanupEnd: () => z(n) } ), R.set(n, i), i); } function z(n) { M.delete(n), R.delete(n); } function K() { const n = X(new Error().stack); return n ? `auto_${n.file}_${n.line}_${n.column}` : Symbol(`ssgoi_auto_${Date.now()}`); } const D = globalThis.FinalizationRegistry, L = D ? ( // eslint-disable-next-line @typescript-eslint/no-explicit-any new D((n) => { try { z(n); } catch { } }) ) : void 0; function nt(n) { const t = n.key ?? K(); if (n.ref && L) try { L.register(n.ref, t); } catch { } return B( t, { in: n.in, out: n.out }, n[U] ); } function q(n) { const t = n.filter((e) => e.symmetric).map((e) => ({ from: e.to, to: e.from, transition: e.transition })); return [...n, ...t]; } function G(n) { let t = !1, e = 0, i = 0, r = !1; const o = 50, s = 50, d = (c) => { if (!n) return; const g = c.touches[0]; g && (r = g.clientX < s, r && (e = g.clientX, i = g.clientY)); }, l = (c) => { if (!n || !r || t) return; const g = c.touches[0]; if (!g) return; const w = g.clientX - e, f = g.clientY - i; w > o && Math.abs(w) > Math.abs(f) && (t = !0, setTimeout(() => { t = !1; }, 500)); }, h = () => { n && (r = !1, e = 0, i = 0); }; return { initialize: () => { typeof window > "u" || n && (window.addEventListener("touchstart", d, { passive: !0 }), window.addEventListener("touchmove", l, { passive: !0 }), window.addEventListener("touchend", h, { passive: !0 })); }, cleanup: () => { typeof window > "u" || (window.removeEventListener("touchstart", d), window.removeEventListener("touchmove", l), window.removeEventListener("touchend", h)); }, isSwipePending: () => t }; } const J = (n) => { let t = n.parentElement; for (; t && t !== document.body; ) { const e = window.getComputedStyle(t), i = e.overflow + e.overflowY + e.overflowX; if (i.includes("auto") || i.includes("scroll") || t.scrollHeight > t.clientHeight || t.scrollWidth > t.clientWidth) return t; t = t.parentElement; } return document.documentElement; }, Q = (n) => { let t = n.parentElement; for (; t && t !== document.body; ) { const e = window.getComputedStyle(t).position; if (e === "relative" || e === "absolute" || e === "fixed" || e === "sticky") return t; t = t.parentElement; } return document.body; }; function Z() { let n = null, t = null; const e = /* @__PURE__ */ new Map(); let i = null; const r = () => { n && i && e.set(i, { x: n.scrollLeft, y: n.scrollTop }); }; return { initializeContext: (u, a) => { t = u, n || (n = J(u), (n === document.documentElement ? window : n).addEventListener("scroll", r, { passive: !0 })), i = a; }, calculateScrollOffset: (u, a) => { const p = u && e.has(u) ? e.get(u) : { x: 0, y: 0 }, c = a && e.has(a) ? e.get(a) : { x: 0, y: 0 }; return { x: -c.x + p.x, y: -c.y + p.y }; }, getScrollContainer: () => n, getPositionedParentElement: () => t ? Q(t) : document.body, getScrollPosition: (u) => u && e.has(u) ? e.get(u) : { x: 0, y: 0 } }; } function x(n, t) { if (t === "*") return !0; if (t.endsWith("/*")) { const e = t.slice(0, -2); return n === e || n.startsWith(e + "/"); } return n === t; } function tt(n, t, e) { for (const i of e) if (x(n, i.from) && x(t, i.to)) return i.transition; for (const i of e) if ((i.from === "*" || x(n, i.from)) && (i.to === "*" || x(t, i.to))) return i.transition; return null; } function ot(n) { const { transitions: t = [], defaultTransition: e, middleware: i = (f, y) => ({ from: f, to: y }), // Identity function as default skipOnIosSwipe: r = !0 // Default to true - skip animations on iOS swipe } = n; let o = null; const s = q(t), { initializeContext: d, calculateScrollOffset: l, getScrollContainer: h, getPositionedParentElement: u, getScrollPosition: a } = Z(), p = G(r); p.initialize(); function c() { if (o != null && o.from && (o != null && o.to)) { const { from: f, to: y } = i( o.from, o.to ), T = tt( f, y, s ) || e, C = l( o.from, o.to ), v = { scrollOffset: C, scroll: a(o.from), get scrollingElement() { return h() || document.documentElement; }, get positionedParent() { return u(); } }, b = { scrollOffset: C, get scroll() { return o ? a(o.to) : { x: 0, y: 0 }; }, get scrollingElement() { return h() || document.documentElement; }, get positionedParent() { return u(); } }; T && (T.out && o.outResolve && o.outResolve( (E) => T.out(E, v) ), T.in && o.inResolve && o.inResolve( (E) => T.in(E, b) )), o = null; } } const g = async (f, y) => p.isSwipePending() ? () => ({}) : y === "in" && (!o || !o.from) ? () => ({}) : (o || (o = {}), y === "out" ? (o.from = f, new Promise((S) => { o.outResolve = S, c(); })) : (o.to = f, new Promise((S) => { o.inResolve = S, c(); }))); return (f) => ({ key: f, in: async (y) => (d(y, f), (await g(f, "in"))(y)), out: async (y) => (await g(f, "out"))(y), // Add page transition strategy for page-level transitions [U]: N }); } export { U as TRANSITION_STRATEGY, H as createDefaultStrategy, N as createPageTransitionStrategy, ot as createSggoiTransitionContext, K as generateAutoKey, V as isMultiSpring, at as isSingleSpring, lt as jaeminInternal, nt as transition };