@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
JavaScript
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
};