56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
/**
|
|
* Tiny framework-free store for the page transition („Umblättern“).
|
|
* TransitionLink calls `startTransition`; Curtain subscribes, animates,
|
|
* pushes the route and finally calls `endTransition`.
|
|
*/
|
|
export type TransitionPhase = "idle" | "cover" | "pushed" | "reveal";
|
|
|
|
export interface TransitionState {
|
|
phase: TransitionPhase;
|
|
href: string | null;
|
|
label?: string;
|
|
}
|
|
|
|
type Listener = (state: TransitionState) => void;
|
|
|
|
let state: TransitionState = { phase: "idle", href: null };
|
|
const listeners = new Set<Listener>();
|
|
|
|
function emit() {
|
|
listeners.forEach((cb) => cb(state));
|
|
}
|
|
|
|
/** Begins a transition. Returns false when one is already running (double clicks). */
|
|
export function startTransition(href: string, label?: string): boolean {
|
|
if (state.phase !== "idle") return false;
|
|
state = { phase: "cover", href, label };
|
|
emit();
|
|
return true;
|
|
}
|
|
|
|
/** Internal: Curtain advances the phase. */
|
|
export function setTransitionPhase(phase: TransitionPhase) {
|
|
state = { ...state, phase };
|
|
emit();
|
|
}
|
|
|
|
export function endTransition() {
|
|
state = { phase: "idle", href: null };
|
|
emit();
|
|
}
|
|
|
|
export function isTransitioning(): boolean {
|
|
return state.phase !== "idle";
|
|
}
|
|
|
|
export function getTransitionState(): TransitionState {
|
|
return state;
|
|
}
|
|
|
|
export function subscribe(cb: Listener): () => void {
|
|
listeners.add(cb);
|
|
return () => {
|
|
listeners.delete(cb);
|
|
};
|
|
}
|