@ssgoi/core
Version:
Core animation engine for SSGOI - Native app-like page transitions with spring physics
1,129 lines (1,128 loc) • 30.8 kB
JavaScript
var tt = Object.defineProperty;
var et = (e, n, t) => n in e ? tt(e, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[n] = t;
var m = (e, n, t) => et(e, typeof n != "symbol" ? n + "" : n, t);
import { normalizeToMultiAnimation as M, normalizeSchedule as X } from "./types.js";
import { isCssAnimation as Nt, isMultiAnimation as Ut, isSingleAnimation as Ht, isTickAnimation as Xt } from "./types.js";
import { S as $, I as it, a as nt } from "./provider-Cu4NSF1Q.js";
class rt {
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 n = Date.now();
let i = n - this.lastUpdate;
i > this.lagThreshold || i < 0 ? (this.startTime += i - this.adjustedLag, i = this.adjustedLag) : i > this.maxDeltaTime && (i = this.maxDeltaTime), this.lastUpdate = n;
const r = n - this.startTime, o = r - this.nextTime;
if (o > 0) {
const l = i / 1e3;
this.elapsed = r / 1e3, this.nextTime += o + (o >= this.gap ? 4 : this.gap - o), this.listeners.forEach((c) => {
c(l, this.elapsed);
});
}
this.listeners.size > 0 ? this.rafId = requestAnimationFrame(this.tick) : (this.isRunning = !1, this.rafId = null);
});
}
subscribe(n) {
return this.listeners.add(n), this.isRunning || (this.isRunning = !0, this.rafId = requestAnimationFrame(this.tick)), () => {
this.unsubscribe(n);
};
}
unsubscribe(n) {
this.listeners.delete(n), 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(n, t) {
this.lagThreshold = n, this.adjustedLag = Math.min(t, n);
}
/**
* Set maximum FPS
* @param fps - Target FPS (default: 240)
*/
fps(n) {
this.gap = 1e3 / n, this.nextTime = this.elapsed * 1e3 + this.gap;
}
getListenerCount() {
return this.listeners.size;
}
getElapsed() {
return this.elapsed;
}
}
const _ = new rt();
function ot(e) {
const {
integrator: n,
from: t,
to: i,
velocity: r = 0,
onUpdate: o,
onComplete: l,
onStart: c
} = e;
let s = { position: t, velocity: r }, f = !0, h = 0, p = !1;
const w = (v) => {
if (f) {
if (p || (p = !0, c == null || c()), s = n.step(s, i, v), n.isSettled(s, i)) {
if (h += Math.min(v, 0.033), h >= $) {
s = { position: i, velocity: 0 }, f = !1, o(s.position), _.unsubscribe(w), l();
return;
}
} else
h = 0;
o(s.position);
}
};
return _.subscribe(w), {
stop: () => {
f && (f = !1, _.unsubscribe(w));
},
getPosition: () => s.position,
getVelocity: () => s.velocity,
isRunning: () => f
};
}
const x = 1e3 / 60;
function st(e, n, t, i = 0) {
let o = { position: n, velocity: i }, l = 0;
const c = [];
for (let s = 0; s < 600; s++) {
const f = s * x;
if (c.push({
time: f,
position: o.position,
velocity: o.velocity
}), o = e.step(o, t, x / 1e3), e.isSettled(o, t)) {
if (l += x / 1e3, l >= $) {
c.push({
time: (s + 1) * x,
position: t,
velocity: 0
});
break;
}
} else
l = 0;
}
return c;
}
function Y(e, n) {
if (e.length === 0)
return { position: 0, velocity: 0 };
const t = e[0], i = e[e.length - 1];
if (n <= 0)
return { position: t.position, velocity: t.velocity };
if (n >= i.time)
return { position: i.position, velocity: i.velocity };
let r = 0, o = e.length - 1;
for (; r < o - 1; ) {
const f = Math.floor((r + o) / 2);
e[f].time <= n ? r = f : o = f;
}
const l = e[r], c = e[o], s = (n - l.time) / (c.time - l.time);
return {
position: l.position + (c.position - l.position) * s,
velocity: l.velocity + (c.velocity - l.velocity) * s
};
}
function at(e, n) {
return e.map((t) => n(t.position));
}
function ct(e) {
const {
integrator: n,
element: t,
from: i,
to: r,
velocity: o = 0,
style: l,
onComplete: c,
onStart: s
} = e, f = st(n, i, r, o);
if (f.length === 0)
return s == null || s(), c(), {
stop: () => {
},
getPosition: () => r,
getVelocity: () => 0,
isRunning: () => !1
};
const h = at(f, l), p = f[f.length - 1], w = p.time, v = h[0];
if (v)
for (const y of Object.keys(v))
t.style[y] = "";
const d = t.animate(h, {
duration: w,
fill: "forwards",
easing: "linear",
composite: "replace"
});
let u = !0;
const C = performance.now();
return s == null || s(), d.onfinish = () => {
u && (u = !1, c());
}, {
stop: () => {
u && (u = !1, d.cancel());
},
getPosition: () => {
if (!u)
return p.position;
const y = performance.now() - C;
return Y(f, y).position;
},
getVelocity: () => {
if (!u)
return 0;
const y = performance.now() - C;
return Y(f, y).velocity;
},
isRunning: () => u
};
}
function lt(e) {
const { to: n, onStart: t, onComplete: i } = e;
return t == null || t(), i(), {
stop: () => {
},
getPosition: () => n,
getVelocity: () => 0,
isRunning: () => !1
};
}
class ut {
/**
* Get bound runner based on animation mode
*
* @param options Animation mode options (tick or css)
* @returns Bound runner function (empty runner if no animation mode specified)
* @throws Error if both tick and css are provided
*/
static from(n) {
const { tick: t, css: i } = n;
if (t && i)
throw new Error("Cannot use both 'tick' and 'css' options together");
return i ? (r) => ct({
...r,
element: i.element,
style: i.style
}) : t ? (r) => ot({
...r,
onUpdate: t
}) : lt;
}
}
class K {
}
const ft = { stiffness: 300, damping: 30 };
class j extends K {
constructor(t) {
super();
m(this, "options");
m(this, "runner");
m(this, "controls", null);
m(this, "isAnimating", !1);
m(this, "currentValue");
m(this, "currentVelocity", 0);
m(this, "updateFn");
if (this.options = {
from: t.from ?? 0,
to: t.to ?? 1,
physics: t.physics,
onComplete: t.onComplete ?? (() => {
}),
onStart: t.onStart
}, this.currentValue = this.options.from, t.tick)
this.updateFn = (i) => {
var r;
return (r = t.tick) == null ? void 0 : r.call(t, i);
};
else if (t.css) {
const { element: i, style: r } = t.css;
this.updateFn = (o) => {
const l = r(o);
for (const [c, s] of Object.entries(l))
i.style[c] = typeof s == "number" ? String(s) : s;
};
} else
this.updateFn = () => {
};
this.runner = ut.from({
tick: t.tick,
css: t.css
});
}
/**
* Sync element state to current progress value
* Uses the update callback if provided
*/
syncState() {
var t;
(t = this.updateFn) == null || t.call(this, this.currentValue);
}
/**
* Create Integrator from physics options
* Priority: integrator factory > inertia/spring config > default spring
*/
createIntegrator() {
const t = this.options.physics;
return t != null && t.integrator ? t.integrator() : t != null && t.spring || t != null && t.inertia ? it.from({
spring: t.spring,
inertia: t.inertia
}) : new nt(ft);
}
runAnimation(t) {
this.isAnimating = !0, this.controls = this.runner({
integrator: this.createIntegrator(),
from: this.currentValue,
to: t,
velocity: this.currentVelocity,
onStart: this.options.onStart,
onComplete: () => {
this.currentValue = t, this.currentVelocity = 0, this.isAnimating = !1, this.controls = null, this.options.onComplete();
}
});
}
forward() {
this.stop(), this.runAnimation(this.options.to);
}
backward() {
this.stop(), this.runAnimation(this.options.from);
}
reverse() {
const t = this.options.from;
this.options.from = this.options.to, this.options.to = t, this.isAnimating && (this.stop(), this.runAnimation(this.options.to));
}
stop() {
this.controls && (this.currentValue = this.controls.getPosition(), this.currentVelocity = this.controls.getVelocity(), this.controls.stop(), this.controls = null), this.isAnimating = !1;
}
getVelocity() {
var t;
return ((t = this.controls) == null ? void 0 : t.getVelocity()) ?? this.currentVelocity;
}
getCurrentValue() {
var t;
return ((t = this.controls) == null ? void 0 : t.getPosition()) ?? this.currentValue;
}
getIsAnimating() {
return this.isAnimating;
}
getCurrentState() {
return {
position: this.getCurrentValue(),
velocity: this.getVelocity(),
from: this.options.from,
to: this.options.to
};
}
setValue(t) {
this.currentValue = t;
}
setVelocity(t) {
this.currentVelocity = t;
}
updateOptions(t) {
this.options = { ...this.options, ...t };
}
}
class D extends K {
constructor(t) {
super();
m(this, "config");
m(this, "animators", /* @__PURE__ */ new Map());
m(this, "itemOrder", []);
m(this, "completedCount", 0);
m(this, "completedAnimators", /* @__PURE__ */ new Set());
m(this, "direction", "forward");
m(this, "idCounter", 0);
m(this, "rafId", null);
m(this, "from");
m(this, "to");
m(this, "initialState");
this.config = t.config, this.from = t.from, this.to = t.to, this.initialState = t.state ?? {
position: t.from,
velocity: 0
}, this.initializeAnimators();
}
generateId() {
return `spring_${this.idCounter++}`;
}
initializeAnimators() {
this.config.items.forEach((t, i) => {
const r = this.generateId(), o = new j({
from: this.from,
to: this.to,
physics: t.physics,
tick: t.tick,
css: t.css,
onComplete: () => this.onAnimatorComplete(r),
onStart: t.onStart
});
(i === 0 || t.normalizedOffset === 0) && (o.setValue(this.initialState.position), o.setVelocity(this.initialState.velocity), o.syncState()), this.animators.set(r, {
id: r,
item: t,
animator: o,
started: !1
}), this.itemOrder.push(r);
});
}
/**
* Calculate progress of an animator (0-1)
* Progress = |position - from| / |to - from|
*/
getAnimatorProgress(t) {
const i = t.animator.getCurrentState(), r = Math.abs(i.to - i.from);
return r === 0 ? 1 : Math.abs(i.position - i.from) / r;
}
/**
* Get order of springs based on direction
* Forward: 0, 1, 2, ...
* Backward: n-1, n-2, ..., 0
*/
getOrderedIds() {
return this.direction === "forward" ? this.itemOrder : [...this.itemOrder].reverse();
}
onAnimatorComplete(t) {
var r, o, l, c, s, f;
const i = this.animators.get(t);
!i || this.completedAnimators.has(t) || (this.completedAnimators.add(t), this.completedCount++, (o = (r = i.item).onComplete) == null || o.call(r), (c = (l = this.config).onProgress) == null || c.call(l, this.completedCount, this.config.items.length), this.completedCount === this.config.items.length && (this.stopScheduler(), (f = (s = this.config).onEnd) == null || f.call(s)));
}
/**
* Check and start springs based on previous spring's progress
* Called on each frame while animation is running
*/
checkAndStartSprings() {
const t = this.getOrderedIds();
for (let i = 0; i < t.length; i++) {
const r = t[i], o = this.animators.get(r);
if (!o || o.started) continue;
if (i === 0) {
this.startAnimator(r);
continue;
}
const l = t[i - 1], c = this.animators.get(l);
if (!c || !c.started) continue;
const s = this.getAnimatorProgress(c), f = o.item.normalizedOffset;
s >= f && this.startAnimator(r);
}
}
startAnimator(t) {
const i = this.animators.get(t);
!i || i.started || (i.started = !0, this.direction === "forward" ? i.animator.forward() : i.animator.backward());
}
/**
* Start the scheduler loop that checks spring progress
*/
startScheduler() {
const t = () => {
this.checkAndStartSprings(), this.getIsAnimating() && (this.rafId = requestAnimationFrame(t));
};
t();
}
stopScheduler() {
this.rafId !== null && (cancelAnimationFrame(this.rafId), this.rafId = null);
}
getFirstAnimator() {
var i;
const t = this.itemOrder[0];
return t ? ((i = this.animators.get(t)) == null ? void 0 : i.animator) ?? null : null;
}
forward() {
var t, i;
this.stop(), this.direction = "forward", this.completedCount = 0, this.completedAnimators.clear(), this.resetStartedFlags(), (i = (t = this.config).onStart) == null || i.call(t), this.startScheduler();
}
backward() {
var t, i;
this.stop(), this.direction = "backward", this.completedCount = 0, this.completedAnimators.clear(), this.resetStartedFlags(), (i = (t = this.config).onStart) == null || i.call(t), this.startScheduler();
}
resetStartedFlags() {
this.animators.forEach((t) => {
t.started = !1;
});
}
stop() {
this.stopScheduler(), this.animators.forEach((t) => {
t.animator.stop(), t.started = !1;
});
}
reverse() {
this.direction = this.direction === "forward" ? "backward" : "forward", this.animators.forEach((t) => {
if (!t.started)
return;
const i = t.animator.getCurrentState();
if (i.position === i.to) {
const o = new j({
from: this.to,
to: this.from,
physics: t.item.physics,
tick: t.item.tick,
css: t.item.css,
onComplete: () => this.onAnimatorComplete(t.id),
onStart: t.item.onStart
});
o.setValue(this.to), o.setVelocity(0), o.syncState(), o.forward(), t.animator = o;
} else
t.animator.reverse();
});
}
getCurrentState() {
const t = this.getFirstAnimator();
return t ? t.getCurrentState() : {
position: 0,
velocity: 0,
from: 0,
to: 1
};
}
/**
* Get current position value from first animator
*/
getCurrentValue() {
const t = this.getFirstAnimator();
return (t == null ? void 0 : t.getCurrentValue()) ?? 0;
}
/**
* Get current velocity from first animator
*/
getVelocity() {
const t = this.getFirstAnimator();
return (t == null ? void 0 : t.getVelocity()) ?? 0;
}
/**
* Set position value on first animator
* TODO: Support per-spring control
*/
setValue(t) {
const i = this.getFirstAnimator();
i == null || i.setValue(t);
}
/**
* Set velocity on first animator
* TODO: Support per-spring control
*/
setVelocity(t) {
const i = this.getFirstAnimator();
i == null || i.setVelocity(t);
}
/**
* Check if any animation is currently running
*/
getIsAnimating() {
for (const t of this.animators.values())
if (t.animator.getIsAnimating())
return !0;
return !1;
}
/**
* Create MultiAnimator from existing state
* Uses first animator's state for initialization
*/
static fromState(t, i) {
return new D({ ...i, state: t });
}
}
const B = Symbol.for("TRANSITION_STRATEGY"), mt = (e) => ({
runIn: async (n) => {
const { currentAnimation: t } = e;
if (t && t.direction === "out") {
const r = t.controller.getCurrentValue(), o = t.controller.getVelocity();
if (t.controller.stop(), n.out)
return {
config: await n.out,
state: { position: r, velocity: o },
from: 1,
to: 0,
direction: "backward"
// Will actually go 0→1 (backward means toward 'from')
};
}
return n.in ? {
config: await n.in,
state: {
position: 0,
velocity: 0
},
from: 0,
to: 1,
direction: "forward"
} : {
state: {
position: 0,
velocity: 0
},
from: 0,
to: 1,
direction: "forward"
};
},
runOut: async (n) => {
const { currentAnimation: t } = e;
if (t && t.direction === "in") {
const r = t.controller.getCurrentValue(), o = t.controller.getVelocity();
if (t.controller.stop(), n.in)
return {
config: await n.in,
state: { position: r, velocity: o },
from: 0,
to: 1,
direction: "backward"
// Will actually go 1→0 (backward means toward 'from')
};
}
return n.out ? {
config: await n.out,
state: {
position: 1,
velocity: 0
},
from: 1,
to: 0,
direction: "forward"
} : {
state: {
position: 1,
velocity: 0
},
from: 1,
to: 0,
direction: "forward"
};
}
}), dt = () => ({
runIn: async (e) => e.in ? {
config: await e.in,
state: {
position: 0,
velocity: 0
},
from: 0,
to: 1,
direction: "forward"
} : {
state: {
position: 0,
velocity: 0
},
from: 0,
to: 1,
direction: "forward"
},
runOut: async (e) => e.out ? {
config: await e.out,
state: {
position: 1,
velocity: 0
},
from: 1,
to: 0,
direction: "forward"
} : {
state: {
position: 1,
velocity: 0
},
from: 1,
to: 0,
direction: "forward"
}
}), J = "data-ssgoi-scope", Q = "data-ssgoi-scope-ready";
function Ft() {
return (e) => {
if (e)
return e.setAttribute(J, ""), queueMicrotask(() => {
queueMicrotask(() => {
e.setAttribute(Q, "true");
});
}), () => {
};
};
}
function ht(e) {
return e.hasAttribute(Q);
}
function gt(e) {
return e.closest(`[${J}]`);
}
const pt = (e) => {
let n = e.parentElement;
for (; n && n !== document.body; ) {
const t = window.getComputedStyle(n), i = t.overflow + t.overflowY + t.overflowX;
if (i.includes("auto") || i.includes("scroll") || n.scrollHeight > n.clientHeight || n.scrollWidth > n.clientWidth)
return n;
n = n.parentElement;
}
return document.documentElement;
}, wt = (e) => {
let n = e.parentElement;
for (; n && n !== document.body; ) {
const t = window.getComputedStyle(n).position;
if (t === "relative" || t === "absolute" || t === "fixed" || t === "sticky")
return n;
n = n.parentElement;
}
return document.body;
};
function W(e) {
return new Promise((n) => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
n();
});
});
});
}
function yt(e, n) {
var v;
let t = null, i = null, r = null, o = null;
const l = {
get currentAnimation() {
return t;
}
}, c = ((v = n == null ? void 0 : n.strategy) == null ? void 0 : v.call(n, l)) || mt(l), s = async (d) => {
var R, P;
i && (i.remove(), i = null);
const u = e(), C = u.in && u.in(d), y = !(n != null && n.strategy) && u.out ? u.out(d) : void 0;
if (!C)
return;
const V = {
in: M(C),
out: y ? M(y) : void 0
}, g = await c.runIn(V);
if (!g.config) {
t && (t.direction = "in");
return;
}
const a = g.config;
(R = a.prepare) == null || R.call(a);
const S = X(
{
...a,
onEnd: () => {
var E;
t = null, (E = a.onEnd) == null || E.call(a);
}
},
d
);
a.wait && await a.wait();
const A = D.fromState(g.state, {
config: S,
from: g.from,
to: g.to
});
t = { controller: A, direction: "in" }, (P = a.onReady) == null || P.call(a), await W(), g.direction === "forward" ? A.forward() : A.backward();
}, f = async (d) => {
var P, E;
i = d;
const u = e(), C = !(n != null && n.strategy) && u.in ? u.in(d) : void 0, y = u.out && u.out(d);
if (!y)
return;
const V = {
in: C ? M(C) : void 0,
out: M(y)
}, g = await c.runOut(V);
if (!g.config) {
t && (t.direction = "out"), i && (i.remove(), i = null);
return;
}
const a = g.config;
R(), (P = a.prepare) == null || P.call(a);
const S = X(
{
...a,
onEnd: () => {
var I, k;
(I = a.onEnd) == null || I.call(a), i && (i.remove(), i = null), t = null, (k = n == null ? void 0 : n.onCleanupEnd) == null || k.call(n);
}
},
d
);
a.wait && await a.wait();
const A = D.fromState(g.state, {
config: S,
from: g.from,
to: g.to
});
t = { controller: A, direction: "out" }, (E = a.onReady) == null || E.call(a), await W(), g.direction === "forward" ? A.forward() : A.backward();
function R() {
!r || !i || (o && r.contains(o) ? r.insertBefore(i, o) : r.appendChild(i));
}
};
let h = null, p = !1;
const w = (d) => () => {
if (p) return;
p = !0;
const u = d.cloneNode(!0);
h ? queueMicrotask(() => {
document.contains(h) && f(u);
}) : f(u);
};
return (d) => {
if (d)
return requestAnimationFrame(() => {
r = d.parentElement, o = d.nextElementSibling;
}), p = !1, (n == null ? void 0 : n.scope) === "local" ? queueMicrotask(() => {
h = gt(d), !(h && !ht(h)) && s(d);
}) : s(d), w(d);
};
}
const b = /* @__PURE__ */ new Map();
let T = null, N = !1;
function Z(e) {
if (e instanceof HTMLElement && b.has(e)) {
const t = b.get(e);
b.delete(e), queueMicrotask(() => {
t();
});
}
const n = e.childNodes;
for (let t = 0; t < n.length; t++) {
const i = n[t];
i && Z(i);
}
}
function vt() {
N || typeof document > "u" || (N = !0, T = new MutationObserver((e) => {
for (const n of e) {
const t = n.removedNodes;
for (let i = 0; i < t.length; i++) {
const r = t[i];
r && Z(r);
}
}
}), document.body ? T.observe(document.body, {
childList: !0,
subtree: !0
}) : document.addEventListener(
"DOMContentLoaded",
() => {
T == null || T.observe(document.body, {
childList: !0,
subtree: !0
});
},
{ once: !0 }
));
}
function St(e, n) {
vt(), b.set(e, n);
}
function Mt(e) {
return b.has(e);
}
function xt() {
return b.size;
}
function Ot(e) {
const n = b.get(e);
return n ? (b.delete(e), n(), !0) : !1;
}
function Lt() {
b.clear(), T == null || T.disconnect(), T = null, N = !1;
}
const L = /* @__PURE__ */ new Map(), U = /* @__PURE__ */ new Map(), H = /* @__PURE__ */ new Map();
function Ct(e, n, t) {
L.set(e, n);
let i = U.get(e);
if (i)
return i;
const r = n;
return i = yt(
() => {
const o = L.get(e);
return o || (console.warn(`Transition "${String(e)}" not found`), {});
},
{
strategy: t == null ? void 0 : t.strategy,
scope: t == null ? void 0 : t.scope,
onCleanupEnd: () => {
L.get(e) === r && At(e);
}
}
), U.set(e, i), i;
}
function At(e) {
L.delete(e), U.delete(e), H.delete(e);
}
function Dt(e, n = "manual") {
const t = Ct(
e.key,
{
in: e.in,
out: e.out
},
{
strategy: e[B],
scope: e.scope
}
);
if (n === "manual")
return t;
let i = H.get(e.key);
return i || (i = (r) => {
if (r) {
const o = t(r);
o && St(r, o);
}
}, H.set(e.key, i), i);
}
function Et(e) {
const n = e.filter((t) => t.symmetric).map((t) => ({
from: t.to,
to: t.from,
transition: t.transition
}));
return [...e, ...n];
}
function Tt(e) {
let n = !1, t = 0, i = 0, r = !1;
const o = 50, l = (d) => {
if (!e) return;
const u = d.touches[0];
u && (r = u.clientX < o, r && (t = u.clientX, i = u.clientY));
}, c = (d) => {
if (!e || !r) return;
const u = d.touches[0];
if (!u) return;
const C = u.clientX - t, y = u.clientY - i;
C > window.outerWidth / 2 - 5 && Math.abs(C) > Math.abs(y) ? n = !0 : n = !1;
}, s = () => {
e && (r = !1, t = 0, i = 0);
}, f = () => n, h = () => {
typeof window > "u" || e && (window.addEventListener("touchstart", l, { passive: !0 }), window.addEventListener("touchmove", c, { passive: !0 }), window.addEventListener("touchend", s, { passive: !0 }));
}, p = () => {
typeof window > "u" || (window.removeEventListener("touchstart", l), window.removeEventListener("touchmove", c), window.removeEventListener("touchend", s));
};
let w = 0;
return {
initialize: h,
cleanup: p,
isSwipePending: f,
resetSwipeDetection: () => {
requestAnimationFrame(() => {
w++, w > 1 && (w = 0, n = !1);
});
}
};
}
function bt() {
let e = null, n = null;
const t = /* @__PURE__ */ new Map();
let i = null;
const r = () => {
e && i && t.set(i, {
x: e.scrollLeft,
y: e.scrollTop
});
};
return {
initializeContext: (h, p) => {
n = h, e || (e = pt(h), (e === document.documentElement ? window : e).addEventListener("scroll", r, {
passive: !0
})), i = p;
},
calculateScrollOffset: (h, p) => {
const w = h && t.has(h) ? t.get(h) : { x: 0, y: 0 }, v = p && t.has(p) ? t.get(p) : { x: 0, y: 0 };
return {
x: -v.x + w.x,
y: -v.y + w.y
};
},
getScrollContainer: () => e,
getPositionedParentElement: () => n ? wt(n) : document.body,
getScrollPosition: (h) => h && t.has(h) ? t.get(h) : { x: 0, y: 0 }
};
}
function O(e, n) {
if (n === "*")
return !0;
if (n.endsWith("/*")) {
const t = n.slice(0, -2);
return e === t || e.startsWith(t + "/");
}
return e === n;
}
function G(e, n, t) {
for (const i of t)
if (O(e, i.from) && O(n, i.to))
return i.transition;
for (const i of t)
if ((i.from === "*" || O(e, i.from)) && (i.to === "*" || O(n, i.to)))
return i.transition;
return null;
}
function kt() {
let e = null;
function n() {
if (e != null && e.from && (e != null && e.to) && (e != null && e.outResolve) && (e != null && e.inResolve)) {
const t = { from: e.from, to: e.to };
e.outResolve(t), e.inResolve(t), e = null;
}
}
return {
trigger(t, i) {
e || (e = {}), i === "out" ? e.from = t : e.to = t;
},
get(t) {
return t === "in" && (!e || !e.from) ? Promise.resolve(null) : new Promise((i) => {
e || (e = {}), t === "out" ? e.outResolve = i : e.inResolve = i, n();
});
}
};
}
function Rt() {
let e = null;
function n() {
var i, r;
e && ((i = e.outResolve) == null || i.call(e, null), (r = e.inResolve) == null || r.call(e, null), e = null);
}
function t() {
if (e != null && e.from && (e != null && e.to) && (e != null && e.outResolve) && (e != null && e.inResolve)) {
const i = { from: e.from, to: e.to };
e.outResolve(i), e.inResolve(i), e = null;
}
}
return {
trigger(i, r) {
e && (r === "out" && e.from && e.from !== i || r === "in" && e.to && e.to !== i) && n(), e || (e = {}), r === "out" ? e.from = i : e.to = i;
},
get(i) {
return new Promise((r) => {
e || (e = {}), i === "out" ? e.outResolve = r : e.inResolve = r, t();
});
}
};
}
function zt(e, n) {
const {
transitions: t = [],
defaultTransition: i,
middleware: r = (g, a) => ({ from: g, to: a }),
// Identity function as default
skipOnIosSwipe: o = !0
// Default to true - skip animations on iOS swipe
} = e, { outFirst: l = !0, createNavigationDetector: c } = n || {}, s = (c == null ? void 0 : c()) ?? (l ? kt() : Rt()), f = Et(t), {
initializeContext: h,
calculateScrollOffset: p,
getScrollContainer: w,
getPositionedParentElement: v,
getScrollPosition: d
} = bt(), u = Tt(o);
u.initialize();
const C = async (g, a) => {
if (u.isSwipePending())
return u.resetSwipeDetection(), a === "in" ? async (k) => ({
onReady: () => {
k.style.visibility = "visible";
}
}) : () => ({});
s.trigger(g, a);
const S = await s.get(a);
if (!S) return () => ({});
const { from: A, to: R } = r(
S.from,
S.to
), E = G(
A,
R,
f
) || i;
if (!E) return () => ({});
const I = p(S.from, S.to);
if (a === "out") {
const k = {
scrollOffset: I,
scroll: d(S.from),
get scrollingElement() {
return w() || document.documentElement;
},
get positionedParent() {
return v();
}
};
return (F) => E.out(F, k);
} else {
const k = {
scrollOffset: I,
get scroll() {
return d(S.to);
},
get scrollingElement() {
return w() || document.documentElement;
},
get positionedParent() {
return v();
}
};
return async (F) => {
const z = await Promise.resolve(E.in(F, k)), q = z.onReady;
return z.onReady = () => {
F.style.visibility = "visible", q == null || q();
}, z;
};
}
}, y = (g) => ({
key: g,
in: async (a) => (h(a, g), (await C(g, "in"))(a)),
out: async (a) => (await C(g, "out"))(a),
// Add page transition strategy for page-level transitions
[B]: dt
}), V = (g, a) => {
if (u.isSwipePending())
return !1;
const { from: S, to: A } = r(g, a);
return !!(G(
S,
A,
f
) || i);
};
return c ? {
getTransition: y,
hasMatchingTransition: V
} : y;
}
export {
J as SCOPE_ATTR,
Q as SCOPE_READY_ATTR,
B as TRANSITION_STRATEGY,
Rt as createAnyOrderDetector,
mt as createDefaultStrategy,
kt as createOutFirstDetector,
dt as createPageTransitionStrategy,
zt as createSggoiTransitionContext,
Ft as createTransitionScope,
gt as findScope,
xt as getWatchedCount,
Nt as isCssAnimation,
Ut as isMultiAnimation,
ht as isScopeReady,
Ht as isSingleAnimation,
Xt as isTickAnimation,
Mt as isWatched,
X as normalizeSchedule,
M as normalizeToMultiAnimation,
Lt as resetObserver,
Dt as transition,
Ot as triggerUnmount,
St as watchUnmount
};