@entrancekit/react 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,551 @@
1
+ "use client";
2
+ import { useEffect, useRef, useState, useCallback, useReducer, useMemo, useSyncExternalStore } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ // src/useEntrance.ts
7
+
8
+ // src/lifecycle/reducer.ts
9
+ var ACTIVE_PHASES = ["idle", "preload", "play"];
10
+ var GRACEFUL_REASONS = ["completed", "skipped", "timeout"];
11
+ var isActive = (phase) => ACTIVE_PHASES.includes(phase);
12
+ function makeResolve(reason, error = null) {
13
+ return { phase: "resolve", reason, error };
14
+ }
15
+ var initialMachineState = {
16
+ phase: "idle",
17
+ reason: null,
18
+ error: null
19
+ };
20
+ function shouldRunOutro(reason, hasOutro) {
21
+ return hasOutro && GRACEFUL_REASONS.includes(reason);
22
+ }
23
+ function entranceReducer(state, action) {
24
+ switch (action.type) {
25
+ case "BEGIN":
26
+ return state.phase === "idle" ? { ...state, phase: "preload" } : state;
27
+ case "PRELOADED":
28
+ return state.phase === "preload" ? { ...state, phase: "play" } : state;
29
+ case "COMPLETE":
30
+ return state.phase === "preload" || state.phase === "play" ? makeResolve("completed") : state;
31
+ case "SKIP":
32
+ return state.phase === "preload" || state.phase === "play" ? makeResolve("skipped") : state;
33
+ case "TIMEOUT":
34
+ return state.phase === "preload" || state.phase === "play" ? makeResolve("timeout") : state;
35
+ case "FAIL":
36
+ return state.phase === "preload" || state.phase === "play" ? makeResolve("error", {
37
+ kind: action.kind ?? "unknown",
38
+ cause: action.cause
39
+ }) : state;
40
+ case "REDUCED_MOTION":
41
+ return isActive(state.phase) ? makeResolve("reduced-motion") : state;
42
+ case "ENTER_OUTRO":
43
+ return state.phase === "resolve" ? { ...state, phase: "outro" } : state;
44
+ case "ENTER_DONE":
45
+ return state.phase === "resolve" || state.phase === "outro" ? { ...state, phase: "done" } : state;
46
+ default: {
47
+ return state;
48
+ }
49
+ }
50
+ }
51
+
52
+ // src/lifecycle/reducedMotion.ts
53
+ var QUERY = "(prefers-reduced-motion: reduce)";
54
+ function getMediaQueryList() {
55
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
56
+ return null;
57
+ }
58
+ try {
59
+ return window.matchMedia(QUERY);
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+ function prefersReducedMotion() {
65
+ return getMediaQueryList()?.matches ?? false;
66
+ }
67
+ function subscribeReducedMotion(onChange) {
68
+ const mql = getMediaQueryList();
69
+ if (!mql) return () => {
70
+ };
71
+ const handler = (event) => onChange(event.matches);
72
+ if (typeof mql.addEventListener === "function") {
73
+ mql.addEventListener("change", handler);
74
+ return () => mql.removeEventListener("change", handler);
75
+ }
76
+ const legacy = mql;
77
+ legacy.addListener?.(handler);
78
+ return () => legacy.removeListener?.(handler);
79
+ }
80
+
81
+ // src/lifecycle/useEntranceMachine.ts
82
+ function useEntranceMachine(config) {
83
+ const [state, dispatch] = useReducer(entranceReducer, initialMachineState);
84
+ const { phase, reason } = state;
85
+ const { reducedMotion, fallbackAfter, preloadTimeout, outroTimeout, hasOutro } = config;
86
+ const actions = useMemo(
87
+ () => ({
88
+ preloaded: () => dispatch({ type: "PRELOADED" }),
89
+ complete: () => dispatch({ type: "COMPLETE" }),
90
+ skip: () => dispatch({ type: "SKIP" }),
91
+ fail: (kind, cause) => dispatch({ type: "FAIL", kind, cause }),
92
+ outroComplete: () => dispatch({ type: "ENTER_DONE" })
93
+ }),
94
+ []
95
+ );
96
+ useEffect(() => {
97
+ if (reducedMotion === "skip" && prefersReducedMotion()) {
98
+ dispatch({ type: "REDUCED_MOTION" });
99
+ } else {
100
+ dispatch({ type: "BEGIN" });
101
+ }
102
+ if (reducedMotion !== "skip") return;
103
+ return subscribeReducedMotion((reduced) => {
104
+ if (reduced) dispatch({ type: "REDUCED_MOTION" });
105
+ });
106
+ }, [reducedMotion]);
107
+ useEffect(() => {
108
+ let id;
109
+ if (phase === "preload") {
110
+ id = setTimeout(() => dispatch({ type: "TIMEOUT" }), preloadTimeout);
111
+ } else if (phase === "play") {
112
+ id = setTimeout(() => dispatch({ type: "TIMEOUT" }), fallbackAfter);
113
+ } else if (phase === "outro") {
114
+ id = setTimeout(() => dispatch({ type: "ENTER_DONE" }), outroTimeout);
115
+ }
116
+ return () => {
117
+ if (id !== void 0) clearTimeout(id);
118
+ };
119
+ }, [phase, preloadTimeout, fallbackAfter, outroTimeout]);
120
+ useEffect(() => {
121
+ if (phase !== "resolve" || reason === null) return;
122
+ dispatch(
123
+ shouldRunOutro(reason, hasOutro) ? { type: "ENTER_OUTRO" } : { type: "ENTER_DONE" }
124
+ );
125
+ }, [phase, reason, hasOutro]);
126
+ return { state, actions };
127
+ }
128
+
129
+ // src/lifecycle/defaults.ts
130
+ var DEFAULT_FALLBACK_AFTER = 4e3;
131
+ var DEFAULT_OUTRO_TIMEOUT = 1500;
132
+ var DEFAULT_REDUCED_MOTION = "skip";
133
+ function resolveConfig(input = {}) {
134
+ const fallbackAfter = input.fallbackAfter ?? DEFAULT_FALLBACK_AFTER;
135
+ return {
136
+ fallbackAfter,
137
+ preloadTimeout: input.preloadTimeout ?? fallbackAfter,
138
+ outroTimeout: input.outroTimeout ?? DEFAULT_OUTRO_TIMEOUT,
139
+ reducedMotion: input.reducedMotion ?? DEFAULT_REDUCED_MOTION,
140
+ hasOutro: input.hasOutro ?? false
141
+ };
142
+ }
143
+
144
+ // src/internal/config.ts
145
+ function normalizeOutro(outro) {
146
+ if (outro == null) return { hasOutro: false, durationMs: DEFAULT_OUTRO_TIMEOUT };
147
+ if (typeof outro === "number") return { hasOutro: true, durationMs: outro };
148
+ return { hasOutro: true, durationMs: outro.durationMs, render: outro.render };
149
+ }
150
+ function configFromOptions(input) {
151
+ const outro = normalizeOutro(input.outro);
152
+ return resolveConfig({
153
+ fallbackAfter: input.fallbackAfter,
154
+ reducedMotion: input.reducedMotion,
155
+ hasOutro: outro.hasOutro,
156
+ outroTimeout: outro.durationMs
157
+ });
158
+ }
159
+
160
+ // src/internal/deriveRenderProps.ts
161
+ function deriveRenderProps(state, actions) {
162
+ const { phase, reason } = state;
163
+ return {
164
+ // The transient `outro` phase is surfaced publicly as `'resolve'`.
165
+ state: phase === "outro" ? "resolve" : phase,
166
+ reason,
167
+ shouldAnimate: phase === "play",
168
+ shouldPlayOutro: phase === "outro",
169
+ isOverlayMounted: phase !== "done",
170
+ isPlaying: phase === "play",
171
+ isDone: phase === "done",
172
+ complete: actions.complete,
173
+ skip: actions.skip,
174
+ // Public error path: kind defaults to 'unknown'; the wrappers classify their
175
+ // own errors (load vs playback) via the raw machine action instead.
176
+ fail: (error) => actions.fail(void 0, error),
177
+ outroComplete: actions.outroComplete
178
+ };
179
+ }
180
+ function useEntranceCallbacks(id, state, { onResolve, onError, onStateChange }) {
181
+ const resolveFired = useRef(false);
182
+ useEffect(() => {
183
+ if (state.reason === null || resolveFired.current) return;
184
+ resolveFired.current = true;
185
+ onResolve?.({ id, reason: state.reason });
186
+ if (state.reason === "error") {
187
+ onError?.({ id, kind: state.error?.kind ?? "unknown", cause: state.error?.cause });
188
+ }
189
+ }, [state.reason, state.error, id, onResolve, onError]);
190
+ const publicState = state.phase === "outro" ? "resolve" : state.phase;
191
+ const lastState = useRef(null);
192
+ useEffect(() => {
193
+ if (lastState.current === publicState) return;
194
+ lastState.current = publicState;
195
+ onStateChange?.(publicState);
196
+ }, [publicState, onStateChange]);
197
+ }
198
+
199
+ // src/useEntrance.ts
200
+ function useEntrance(options) {
201
+ const { id, fallbackAfter, reducedMotion, outro, onResolve, onError, onStateChange } = options;
202
+ const config = configFromOptions({ fallbackAfter, reducedMotion, outro });
203
+ const { state, actions } = useEntranceMachine(config);
204
+ useEffect(() => {
205
+ actions.preloaded();
206
+ }, [actions]);
207
+ useEntranceCallbacks(id, state, { onResolve, onError, onStateChange });
208
+ return deriveRenderProps(state, actions);
209
+ }
210
+ var emptySubscribe = () => () => {
211
+ };
212
+ function useIsClient() {
213
+ return useSyncExternalStore(
214
+ emptySubscribe,
215
+ () => true,
216
+ // client snapshot
217
+ () => false
218
+ // server / first-render snapshot
219
+ );
220
+ }
221
+ function useOverlayHost({
222
+ enabled,
223
+ container,
224
+ zIndex
225
+ }) {
226
+ const [host, setHost] = useState(null);
227
+ useEffect(() => {
228
+ if (!enabled) {
229
+ setHost(null);
230
+ return;
231
+ }
232
+ if (container) {
233
+ setHost(container);
234
+ return;
235
+ }
236
+ const el = document.createElement("div");
237
+ el.setAttribute("data-entrancekit-host", "");
238
+ el.style.position = "fixed";
239
+ el.style.inset = "0";
240
+ el.style.pointerEvents = "none";
241
+ el.style.zIndex = zIndex !== void 0 ? String(zIndex) : "var(--entrancekit-z, 2147483647)";
242
+ document.body.appendChild(el);
243
+ setHost(el);
244
+ return () => {
245
+ el.remove();
246
+ setHost(null);
247
+ };
248
+ }, [enabled, container, zIndex]);
249
+ return host;
250
+ }
251
+ var DEFAULT_SKIP_LABEL = "Skip intro";
252
+ var defaultSkipStyle = {
253
+ position: "absolute",
254
+ top: 16,
255
+ right: 16,
256
+ pointerEvents: "auto",
257
+ padding: "6px 12px",
258
+ font: "inherit",
259
+ fontSize: 14,
260
+ lineHeight: 1.2,
261
+ color: "#fff",
262
+ background: "rgba(0, 0, 0, 0.55)",
263
+ border: "1px solid rgba(255, 255, 255, 0.35)",
264
+ borderRadius: 6,
265
+ cursor: "pointer"
266
+ };
267
+ function EntranceOverlay({
268
+ api,
269
+ children,
270
+ outro,
271
+ skipControl = true,
272
+ skipLabel = DEFAULT_SKIP_LABEL,
273
+ zIndex,
274
+ container,
275
+ className,
276
+ style
277
+ }) {
278
+ const isClient = useIsClient();
279
+ const host = useOverlayHost({
280
+ enabled: isClient && api.isOverlayMounted,
281
+ container,
282
+ zIndex
283
+ });
284
+ const previouslyFocused = useRef(null);
285
+ useEffect(() => {
286
+ previouslyFocused.current = document.activeElement;
287
+ return () => {
288
+ const prev = previouslyFocused.current;
289
+ const active = document.activeElement;
290
+ if (prev instanceof HTMLElement && active instanceof HTMLElement && active.closest("[data-entrancekit-overlay]")) {
291
+ prev.focus();
292
+ }
293
+ };
294
+ }, []);
295
+ if (!isClient || !host || !api.isOverlayMounted) return null;
296
+ const showVisuals = api.state !== "idle" && api.reason !== "reduced-motion";
297
+ const showSkip = skipControl !== false && (api.state === "preload" || api.state === "play");
298
+ const stageStyle = {
299
+ position: "absolute",
300
+ inset: 0,
301
+ pointerEvents: "none",
302
+ opacity: api.shouldPlayOutro ? 0 : 1,
303
+ transition: api.shouldPlayOutro ? `opacity ${outro.durationMs}ms ease` : void 0,
304
+ ...style
305
+ };
306
+ const visuals = api.shouldPlayOutro && outro.render && api.reason ? outro.render({ reason: api.reason }) : children;
307
+ return createPortal(
308
+ /* @__PURE__ */ jsxs("div", { "data-entrancekit-overlay": "", className, style: stageStyle, children: [
309
+ showVisuals && /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0 }, children: visuals }),
310
+ showSkip && (typeof skipControl === "function" ? skipControl(api.skip) : /* @__PURE__ */ jsx(
311
+ "button",
312
+ {
313
+ type: "button",
314
+ onClick: api.skip,
315
+ "aria-label": skipLabel,
316
+ "data-entrancekit-skip": "",
317
+ style: defaultSkipStyle,
318
+ children: skipLabel
319
+ }
320
+ ))
321
+ ] }),
322
+ host
323
+ );
324
+ }
325
+ function EntranceController(props) {
326
+ const {
327
+ children,
328
+ skipControl,
329
+ skipLabel,
330
+ zIndex,
331
+ container,
332
+ className,
333
+ style,
334
+ ...lifecycle
335
+ } = props;
336
+ const api = useEntrance(lifecycle);
337
+ return /* @__PURE__ */ jsx(
338
+ EntranceOverlay,
339
+ {
340
+ api,
341
+ outro: normalizeOutro(props.outro),
342
+ skipControl,
343
+ skipLabel,
344
+ zIndex,
345
+ container,
346
+ className,
347
+ style,
348
+ children: children(api)
349
+ }
350
+ );
351
+ }
352
+
353
+ // src/internal/useEntranceEngine.ts
354
+ function useEntranceEngine(options) {
355
+ const config = configFromOptions(options);
356
+ const { state, actions } = useEntranceMachine(config);
357
+ useEntranceCallbacks(options.id, state, options);
358
+ return { state, actions, api: deriveRenderProps(state, actions) };
359
+ }
360
+ var videoStyle = {
361
+ position: "absolute",
362
+ inset: 0,
363
+ width: "100%",
364
+ height: "100%",
365
+ objectFit: "cover",
366
+ display: "block"
367
+ };
368
+ function EntranceVideo(props) {
369
+ const {
370
+ id,
371
+ src,
372
+ sources,
373
+ poster,
374
+ muted = true,
375
+ playsInline = true,
376
+ preload = "auto",
377
+ fallbackAfter,
378
+ reducedMotion,
379
+ outro,
380
+ skipControl,
381
+ skipLabel,
382
+ zIndex,
383
+ container,
384
+ className,
385
+ style,
386
+ onResolve,
387
+ onError,
388
+ onStateChange
389
+ } = props;
390
+ const { state, actions, api } = useEntranceEngine({
391
+ id,
392
+ fallbackAfter,
393
+ reducedMotion,
394
+ outro,
395
+ onResolve,
396
+ onError,
397
+ onStateChange
398
+ });
399
+ const videoRef = useRef(null);
400
+ const endedFired = useRef(false);
401
+ useEffect(() => {
402
+ if (state.phase !== "play") return;
403
+ const video = videoRef.current;
404
+ if (!video) return;
405
+ try {
406
+ video.muted = muted;
407
+ const result = video.play();
408
+ if (result && typeof result.then === "function") {
409
+ result.catch((err) => actions.fail("playback", err));
410
+ }
411
+ } catch (err) {
412
+ actions.fail("playback", err);
413
+ }
414
+ }, [state.phase, actions, muted]);
415
+ return /* @__PURE__ */ jsx(
416
+ EntranceOverlay,
417
+ {
418
+ api,
419
+ outro: normalizeOutro(outro),
420
+ skipControl,
421
+ skipLabel,
422
+ zIndex,
423
+ container,
424
+ className,
425
+ style,
426
+ children: /* @__PURE__ */ jsx(
427
+ "video",
428
+ {
429
+ ref: videoRef,
430
+ ...src ? { src } : {},
431
+ poster,
432
+ muted,
433
+ playsInline,
434
+ preload,
435
+ onCanPlayThrough: () => actions.preloaded(),
436
+ onLoadedData: () => actions.preloaded(),
437
+ onEnded: () => {
438
+ if (endedFired.current) return;
439
+ endedFired.current = true;
440
+ actions.complete();
441
+ },
442
+ onError: () => actions.fail("load"),
443
+ style: videoStyle,
444
+ children: sources?.map((source) => /* @__PURE__ */ jsx("source", { src: source.src, type: source.type }, source.src))
445
+ }
446
+ )
447
+ }
448
+ );
449
+ }
450
+ var PEER = "@lottiefiles/dotlottie-react";
451
+ var playerStyle = {
452
+ position: "absolute",
453
+ inset: 0,
454
+ width: "100%",
455
+ height: "100%",
456
+ display: "block"
457
+ };
458
+ function EntranceLottie(props) {
459
+ const {
460
+ id,
461
+ src,
462
+ animationData,
463
+ onComplete,
464
+ fallbackAfter,
465
+ reducedMotion,
466
+ outro,
467
+ skipControl,
468
+ skipLabel,
469
+ zIndex,
470
+ container,
471
+ className,
472
+ style,
473
+ onResolve,
474
+ onError,
475
+ onStateChange
476
+ } = props;
477
+ const { state, actions, api } = useEntranceEngine({
478
+ id,
479
+ fallbackAfter,
480
+ reducedMotion,
481
+ outro,
482
+ onResolve,
483
+ onError,
484
+ onStateChange
485
+ });
486
+ const [Player, setPlayer] = useState(null);
487
+ const shouldLoad = state.phase === "preload" || state.phase === "play";
488
+ useEffect(() => {
489
+ if (!shouldLoad) return;
490
+ let alive = true;
491
+ const reportMissingPeer = (err) => {
492
+ if (typeof console !== "undefined") {
493
+ console.error(
494
+ `[EntranceKit] EntranceLottie requires the optional peer "${PEER}". Install it with: npm i ${PEER}`
495
+ );
496
+ }
497
+ actions.fail("missing-peer", err);
498
+ };
499
+ import('@lottiefiles/dotlottie-react').then((mod) => {
500
+ if (!alive) return;
501
+ const Comp = mod.DotLottieReact;
502
+ if (Comp) setPlayer(() => Comp);
503
+ else reportMissingPeer(new Error(`${PEER} did not export DotLottieReact`));
504
+ }).catch((err) => {
505
+ if (alive) reportMissingPeer(err);
506
+ });
507
+ return () => {
508
+ alive = false;
509
+ };
510
+ }, [shouldLoad, actions]);
511
+ const handleRef = useCallback(
512
+ (instance) => {
513
+ if (!instance) return;
514
+ instance.addEventListener("load", () => actions.preloaded());
515
+ instance.addEventListener("complete", () => {
516
+ actions.complete();
517
+ onComplete?.();
518
+ });
519
+ instance.addEventListener("loadError", () => actions.fail("load"));
520
+ },
521
+ [actions, onComplete]
522
+ );
523
+ return /* @__PURE__ */ jsx(
524
+ EntranceOverlay,
525
+ {
526
+ api,
527
+ outro: normalizeOutro(outro),
528
+ skipControl,
529
+ skipLabel,
530
+ zIndex,
531
+ container,
532
+ className,
533
+ style,
534
+ children: Player ? /* @__PURE__ */ jsx(
535
+ Player,
536
+ {
537
+ ...src ? { src } : {},
538
+ data: animationData,
539
+ loop: false,
540
+ autoplay: true,
541
+ dotLottieRefCallback: handleRef,
542
+ style: playerStyle
543
+ }
544
+ ) : null
545
+ }
546
+ );
547
+ }
548
+
549
+ export { EntranceController, EntranceLottie, EntranceVideo, useEntrance };
550
+ //# sourceMappingURL=index.js.map
551
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lifecycle/reducer.ts","../src/lifecycle/reducedMotion.ts","../src/lifecycle/useEntranceMachine.ts","../src/lifecycle/defaults.ts","../src/internal/config.ts","../src/internal/deriveRenderProps.ts","../src/internal/useEntranceCallbacks.ts","../src/useEntrance.ts","../src/ssr/useIsClient.ts","../src/overlay/useOverlayHost.ts","../src/overlay/EntranceOverlay.tsx","../src/EntranceController.tsx","../src/internal/useEntranceEngine.ts","../src/EntranceVideo.tsx","../src/EntranceLottie.tsx"],"names":["useEffect","useRef","jsx","useState"],"mappings":"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","file":"index.js","sourcesContent":["// ============================================================================\n// EntranceKit — the pure lifecycle reducer.\n//\n// This is the single source of lifecycle truth. It is PURE (same input ⇒ same\n// output, no timers, no globals, no side effects) — every timer, matchMedia\n// subscription, and outro/done routing decision lives in the effect layer (M2).\n//\n// Every transition is guarded by the current phase, which makes each action\n// IDEMPOTENT: re-dispatching it (React StrictMode double-invoke, a late timer\n// that fires after a done-signal already won) is a harmless no-op. This is what\n// guarantees the spec's \"deterministic teardown ... on every path\".\n// ============================================================================\n\nimport type {\n EntranceAction,\n EntranceMachineState,\n EntranceErrorInfo,\n Phase,\n ResolveReason,\n} from './types';\n\n/** Phases from which a terminal trigger may still resolve the entrance. */\nconst ACTIVE_PHASES = ['idle', 'preload', 'play'] as const satisfies readonly Phase[];\n\n/** Reasons that earn a graceful outro; `error`/`reduced-motion` hard-cut to done. */\nconst GRACEFUL_REASONS = ['completed', 'skipped', 'timeout'] as const satisfies readonly ResolveReason[];\n\nconst isActive = (phase: Phase): boolean =>\n (ACTIVE_PHASES as readonly Phase[]).includes(phase);\n\n/**\n * Construct the single `resolve` state every terminal trigger funnels into.\n * Callers are responsible for the phase guard (each `case` below only resolves\n * from an active phase); once `resolve`/`outro`/`done`, every trigger is an\n * idempotent no-op, which is what makes double-dispatch and late timers safe.\n */\nfunction makeResolve(\n reason: ResolveReason,\n error: EntranceErrorInfo | null = null\n): EntranceMachineState {\n return { phase: 'resolve', reason, error };\n}\n\nexport const initialMachineState: EntranceMachineState = {\n phase: 'idle',\n reason: null,\n error: null,\n};\n\n/**\n * Policy predicate read by the M2 router when the machine enters `resolve`:\n * the outro runs only for graceful reasons, and only if one is configured.\n * An errored or reduced-motion entrance hard-cuts straight to `done`.\n */\nexport function shouldRunOutro(reason: ResolveReason, hasOutro: boolean): boolean {\n return hasOutro && (GRACEFUL_REASONS as readonly ResolveReason[]).includes(reason);\n}\n\nexport function entranceReducer(\n state: EntranceMachineState,\n action: EntranceAction\n): EntranceMachineState {\n switch (action.type) {\n case 'BEGIN':\n return state.phase === 'idle' ? { ...state, phase: 'preload' } : state;\n\n case 'PRELOADED':\n return state.phase === 'preload' ? { ...state, phase: 'play' } : state;\n\n case 'COMPLETE':\n // The animation's OWN done-signal — the preferred completion source.\n return state.phase === 'preload' || state.phase === 'play'\n ? makeResolve('completed')\n : state;\n\n case 'SKIP':\n return state.phase === 'preload' || state.phase === 'play'\n ? makeResolve('skipped')\n : state;\n\n case 'TIMEOUT':\n // A watchdog fired. No-op if a done-signal already left the active phase.\n return state.phase === 'preload' || state.phase === 'play'\n ? makeResolve('timeout')\n : state;\n\n case 'FAIL':\n return state.phase === 'preload' || state.phase === 'play'\n ? makeResolve('error', {\n kind: action.kind ?? 'unknown',\n cause: action.cause,\n })\n : state;\n\n case 'REDUCED_MOTION':\n // Honored at init or on a live matchMedia flip — may fire from idle too.\n return isActive(state.phase) ? makeResolve('reduced-motion') : state;\n\n case 'ENTER_OUTRO':\n // Effect-layer router decision; only valid coming out of `resolve`.\n return state.phase === 'resolve' ? { ...state, phase: 'outro' } : state;\n\n case 'ENTER_DONE':\n // Reached directly from `resolve` (hard-cut / no outro) or from `outro`\n // (developer outroComplete() or the outro watchdog).\n return state.phase === 'resolve' || state.phase === 'outro'\n ? { ...state, phase: 'done' }\n : state;\n\n default: {\n // Exhaustiveness guard — a new action type without a case is a type error.\n // Falls back to the unchanged state at runtime if an untyped action leaks in.\n const _exhaustive: never = action;\n void _exhaustive;\n return state;\n }\n }\n}\n","// ============================================================================\n// Reduced-motion detection — client-only, defensive.\n//\n// Client-only does NOT mean every client has a modern matchMedia: jsdom omits\n// it, very old engines only expose the deprecated addListener/removeListener\n// API, and some embedded webviews lack it entirely. Absence is treated as\n// \"no preference\" and never throws — failing here would itself violate the\n// graceful-degradation constraint.\n// ============================================================================\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\nfunction getMediaQueryList(): MediaQueryList | null {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return null;\n }\n try {\n return window.matchMedia(QUERY);\n } catch {\n return null;\n }\n}\n\n/** `true` iff the user has requested reduced motion. Safe everywhere. */\nexport function prefersReducedMotion(): boolean {\n return getMediaQueryList()?.matches ?? false;\n}\n\n/**\n * Subscribe to live `prefers-reduced-motion` changes. Returns an unsubscribe\n * function. No-ops (and unsubscribes cleanly) when matchMedia is unavailable.\n */\nexport function subscribeReducedMotion(onChange: (reduced: boolean) => void): () => void {\n const mql = getMediaQueryList();\n if (!mql) return () => {};\n\n const handler = (event: MediaQueryListEvent): void => onChange(event.matches);\n\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n }\n\n // Safari < 14 and other legacy engines only expose the deprecated API.\n const legacy = mql as MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent) => void) => void;\n };\n legacy.addListener?.(handler);\n return () => legacy.removeListener?.(handler);\n}\n","// ============================================================================\n// useEntranceMachine — the ONE effect layer around the pure reducer.\n//\n// It owns every side effect the lifecycle needs and nothing else:\n// • INIT: read prefers-reduced-motion (client-only) and either suppress the\n// entrance (reduce + 'skip' → resolve) or BEGIN it; subscribe to live flips.\n// • TIMERS: exactly one phase-keyed watchdog at a time (preload stall, play\n// fallback, outro), cleared on every transition and on unmount.\n// • ROUTER: on entering `resolve`, dispatch ENTER_OUTRO or ENTER_DONE.\n//\n// StrictMode safety comes for free: the reducer's guards make repeated dispatch\n// idempotent, and clearTimeout in each effect's cleanup makes a stale timer fire\n// impossible. Async cancellation (the dynamic Lottie import) is handled at the\n// call site in M4 with an alive-ref, since a pending import cannot be cleared.\n// ============================================================================\n\nimport { useEffect, useMemo, useReducer } from 'react';\nimport { entranceReducer, initialMachineState, shouldRunOutro } from './reducer';\nimport { prefersReducedMotion, subscribeReducedMotion } from './reducedMotion';\nimport type {\n EntranceErrorKind,\n EntranceMachineState,\n ResolvedEntranceConfig,\n} from './types';\n\nexport interface EntranceMachineActions {\n /** Signal the asset is ready to play (preload → play). */\n preloaded: () => void;\n /** The animation's OWN done-signal — the preferred completion path. */\n complete: () => void;\n /** User-exit / built-in skip control. */\n skip: () => void;\n /** Load/playback error → graceful resolve('error'). */\n fail: (kind?: EntranceErrorKind, cause?: unknown) => void;\n /** Signal a developer outro has finished (outro → done). */\n outroComplete: () => void;\n}\n\nexport interface UseEntranceMachineResult {\n state: EntranceMachineState;\n actions: EntranceMachineActions;\n}\n\nexport function useEntranceMachine(\n config: ResolvedEntranceConfig\n): UseEntranceMachineResult {\n const [state, dispatch] = useReducer(entranceReducer, initialMachineState);\n const { phase, reason } = state;\n const { reducedMotion, fallbackAfter, preloadTimeout, outroTimeout, hasOutro } = config;\n\n // dispatch identity is stable, and a dispatch after unmount is a no-op in\n // React 18, so these callbacks are stable for the component's whole lifetime.\n const actions = useMemo<EntranceMachineActions>(\n () => ({\n preloaded: () => dispatch({ type: 'PRELOADED' }),\n complete: () => dispatch({ type: 'COMPLETE' }),\n skip: () => dispatch({ type: 'SKIP' }),\n fail: (kind, cause) => dispatch({ type: 'FAIL', kind, cause }),\n outroComplete: () => dispatch({ type: 'ENTER_DONE' }),\n }),\n []\n );\n\n // INIT — client-only (effects never run during SSR). Because the machine only\n // BEGINs after this reads reduced motion, `shouldAnimate` (derived as phase ===\n // 'play' downstream) is necessarily false until the preference is known: an\n // idle/preload machine never reports shouldAnimate, so a reduce-preference user\n // can never get a motion flash.\n useEffect(() => {\n if (reducedMotion === 'skip' && prefersReducedMotion()) {\n dispatch({ type: 'REDUCED_MOTION' });\n } else {\n dispatch({ type: 'BEGIN' });\n }\n // 'play' deliberately honors the asset regardless of the OS setting, so we\n // only watch for live flips when the behavior is 'skip'.\n if (reducedMotion !== 'skip') return;\n return subscribeReducedMotion((reduced) => {\n if (reduced) dispatch({ type: 'REDUCED_MOTION' });\n });\n }, [reducedMotion]);\n\n // TIMERS — one watchdog per phase. The done-signal normally wins each race;\n // when it is missing/late the watchdog resolves so the user is never trapped.\n useEffect(() => {\n let id: ReturnType<typeof setTimeout> | undefined;\n if (phase === 'preload') {\n id = setTimeout(() => dispatch({ type: 'TIMEOUT' }), preloadTimeout);\n } else if (phase === 'play') {\n id = setTimeout(() => dispatch({ type: 'TIMEOUT' }), fallbackAfter);\n } else if (phase === 'outro') {\n id = setTimeout(() => dispatch({ type: 'ENTER_DONE' }), outroTimeout);\n }\n return () => {\n if (id !== undefined) clearTimeout(id);\n };\n }, [phase, preloadTimeout, fallbackAfter, outroTimeout]);\n\n // ROUTER — collapse `resolve` to either the outro (graceful reasons) or done.\n useEffect(() => {\n if (phase !== 'resolve' || reason === null) return;\n dispatch(\n shouldRunOutro(reason, hasOutro) ? { type: 'ENTER_OUTRO' } : { type: 'ENTER_DONE' }\n );\n }, [phase, reason, hasOutro]);\n\n return { state, actions };\n}\n","// ============================================================================\n// Resolved-config defaults. A single source of truth so the hook, the tests,\n// and the docs/demo all agree on the same numbers.\n// ============================================================================\n\nimport type { ReducedMotionBehavior, ResolvedEntranceConfig } from './types';\n\n/** Play-stall safety net (ms). The animation's own done-signal should win this race. */\nexport const DEFAULT_FALLBACK_AFTER = 4000;\n\n/** Watchdog (ms) that forces the outro to converge to `done`. */\nexport const DEFAULT_OUTRO_TIMEOUT = 1500;\n\nexport const DEFAULT_REDUCED_MOTION: ReducedMotionBehavior = 'skip';\n\nexport interface EntranceConfigInput {\n fallbackAfter?: number;\n /** Internal-only knob (not exposed publicly in v0.1.0); defaults to fallbackAfter. */\n preloadTimeout?: number;\n outroTimeout?: number;\n reducedMotion?: ReducedMotionBehavior;\n hasOutro?: boolean;\n}\n\n/** Normalize partial options into the fully-resolved config the machine reads. */\nexport function resolveConfig(input: EntranceConfigInput = {}): ResolvedEntranceConfig {\n const fallbackAfter = input.fallbackAfter ?? DEFAULT_FALLBACK_AFTER;\n return {\n fallbackAfter,\n preloadTimeout: input.preloadTimeout ?? fallbackAfter,\n outroTimeout: input.outroTimeout ?? DEFAULT_OUTRO_TIMEOUT,\n reducedMotion: input.reducedMotion ?? DEFAULT_REDUCED_MOTION,\n hasOutro: input.hasOutro ?? false,\n };\n}\n","// Shared option normalization used by both useEntrance and the wrapper/controller\n// components, so the headless and presentational paths resolve identically.\n\nimport type { ReactNode } from 'react';\nimport { DEFAULT_OUTRO_TIMEOUT, resolveConfig } from '../lifecycle/defaults';\nimport type {\n ReducedMotionBehavior,\n ResolveReason,\n ResolvedEntranceConfig,\n} from '../lifecycle/types';\nimport type { EntranceOutro } from '../types';\n\nexport interface NormalizedOutro {\n hasOutro: boolean;\n /** The outro window (ms). Doubles as the machine's outro watchdog. */\n durationMs: number;\n render?: (ctx: { reason: ResolveReason }) => ReactNode;\n}\n\nexport function normalizeOutro(outro: EntranceOutro | undefined): NormalizedOutro {\n if (outro == null) return { hasOutro: false, durationMs: DEFAULT_OUTRO_TIMEOUT };\n if (typeof outro === 'number') return { hasOutro: true, durationMs: outro };\n return { hasOutro: true, durationMs: outro.durationMs, render: outro.render };\n}\n\nexport interface ConfigFromOptionsInput {\n fallbackAfter?: number;\n reducedMotion?: ReducedMotionBehavior;\n outro?: EntranceOutro;\n}\n\n/** Resolve public-ish options (and the normalized outro) into machine config. */\nexport function configFromOptions(input: ConfigFromOptionsInput): ResolvedEntranceConfig {\n const outro = normalizeOutro(input.outro);\n return resolveConfig({\n fallbackAfter: input.fallbackAfter,\n reducedMotion: input.reducedMotion,\n hasOutro: outro.hasOutro,\n outroTimeout: outro.durationMs,\n });\n}\n","// Pure projection of internal machine state onto the public render-prop surface.\n// Shared by useEntrance, EntranceController, and the media wrappers so all entry\n// points expose an identical contract.\n\nimport type { EntranceMachineActions } from '../lifecycle/useEntranceMachine';\nimport type { EntranceMachineState } from '../lifecycle/types';\nimport type { EntranceRenderProps } from '../types';\n\nexport function deriveRenderProps(\n state: EntranceMachineState,\n actions: EntranceMachineActions\n): EntranceRenderProps {\n const { phase, reason } = state;\n return {\n // The transient `outro` phase is surfaced publicly as `'resolve'`.\n state: phase === 'outro' ? 'resolve' : phase,\n reason,\n shouldAnimate: phase === 'play',\n shouldPlayOutro: phase === 'outro',\n isOverlayMounted: phase !== 'done',\n isPlaying: phase === 'play',\n isDone: phase === 'done',\n complete: actions.complete,\n skip: actions.skip,\n // Public error path: kind defaults to 'unknown'; the wrappers classify their\n // own errors (load vs playback) via the raw machine action instead.\n fail: (error?: unknown) => actions.fail(undefined, error),\n outroComplete: actions.outroComplete,\n };\n}\n","import { useEffect, useRef } from 'react';\nimport type { EntranceMachineState, EntranceState } from '../lifecycle/types';\nimport type { EntranceError, EntranceResult } from '../types';\n\nexport interface EntranceCallbacks {\n onResolve?: (result: EntranceResult) => void;\n onError?: (error: EntranceError) => void;\n onStateChange?: (state: EntranceState) => void;\n}\n\n/**\n * Fires the public lifecycle callbacks. Shared by useEntrance and the wrapper\n * components so headless and presentational consumers behave identically:\n * onResolve/onError fire exactly once on first resolution; onStateChange fires\n * on each distinct public state (the transient `outro` folds into `resolve`).\n */\nexport function useEntranceCallbacks(\n id: string,\n state: EntranceMachineState,\n { onResolve, onError, onStateChange }: EntranceCallbacks\n): void {\n const resolveFired = useRef(false);\n useEffect(() => {\n if (state.reason === null || resolveFired.current) return;\n resolveFired.current = true;\n onResolve?.({ id, reason: state.reason });\n if (state.reason === 'error') {\n onError?.({ id, kind: state.error?.kind ?? 'unknown', cause: state.error?.cause });\n }\n }, [state.reason, state.error, id, onResolve, onError]);\n\n const publicState: EntranceState = state.phase === 'outro' ? 'resolve' : state.phase;\n const lastState = useRef<EntranceState | null>(null);\n useEffect(() => {\n if (lastState.current === publicState) return;\n lastState.current = publicState;\n onStateChange?.(publicState);\n }, [publicState, onStateChange]);\n}\n","import { useEffect } from 'react';\nimport { useEntranceMachine } from './lifecycle/useEntranceMachine';\nimport { configFromOptions } from './internal/config';\nimport { deriveRenderProps } from './internal/deriveRenderProps';\nimport { useEntranceCallbacks } from './internal/useEntranceCallbacks';\nimport type { UseEntranceOptions, UseEntranceResult } from './types';\n\n/**\n * Headless access to the entrance lifecycle, for building a custom overlay (or\n * driving an animation directly). Returns the same contract `EntranceController`\n * hands its render-prop. Auto-advances out of `preload` on mount, since a\n * headless consumer has nothing to preload; the play-stall watchdog still guards\n * the case where the animation never signals completion.\n */\nexport function useEntrance(options: UseEntranceOptions): UseEntranceResult {\n const { id, fallbackAfter, reducedMotion, outro, onResolve, onError, onStateChange } =\n options;\n\n const config = configFromOptions({ fallbackAfter, reducedMotion, outro });\n const { state, actions } = useEntranceMachine(config);\n\n // Nothing to load in the headless path → advance preload → play immediately.\n useEffect(() => {\n actions.preloaded();\n }, [actions]);\n\n useEntranceCallbacks(id, state, { onResolve, onError, onStateChange });\n\n return deriveRenderProps(state, actions);\n}\n","import { useSyncExternalStore } from 'react';\n\nconst emptySubscribe = (): (() => void) => () => {};\n\n/**\n * `false` on the server AND on the first client render (to match the SSR markup),\n * then `true` after hydration. Built on `useSyncExternalStore` so React drives\n * the post-hydration update without a manual effect — gating overlay rendering on\n * this guarantees no hydration mismatch.\n */\nexport function useIsClient(): boolean {\n return useSyncExternalStore(\n emptySubscribe,\n () => true, // client snapshot\n () => false // server / first-render snapshot\n );\n}\n","/*\n * This hook intentionally manages an effect-created DOM node as state: the host\n * cannot exist during render or SSR, so it must be created in an effect and then\n * committed via setState to trigger the portal render. That is the correct\n * pattern here, so the set-state-in-effect heuristic is disabled for this file.\n */\n/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useState } from 'react';\n\nexport interface UseOverlayHostOptions {\n /** Create/keep the host only while true (false once the entrance is done). */\n enabled: boolean;\n /** Developer-supplied portal target. When set, no host is created or removed. */\n container?: HTMLElement;\n /** z-index for the managed host; falls back to `--entrancekit-z` then a high value. */\n zIndex?: number;\n}\n\n/**\n * Returns the DOM node to portal the overlay into, or `null` when there is none\n * yet. The managed host is a single full-viewport, `pointer-events:none` div\n * appended to `document.body` — the structural guarantee that the app underneath\n * stays interactive (the entrance is never a gate). Because the host is created\n * in an effect (never on the server, never on the first hydration render) and\n * removed in that effect's cleanup, there is exactly one teardown owner and no\n * hydration mismatch.\n */\nexport function useOverlayHost({\n enabled,\n container,\n zIndex,\n}: UseOverlayHostOptions): HTMLElement | null {\n const [host, setHost] = useState<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n setHost(null);\n return;\n }\n if (container) {\n setHost(container);\n return;\n }\n const el = document.createElement('div');\n el.setAttribute('data-entrancekit-host', '');\n el.style.position = 'fixed';\n el.style.inset = '0';\n el.style.pointerEvents = 'none';\n el.style.zIndex = zIndex !== undefined ? String(zIndex) : 'var(--entrancekit-z, 2147483647)';\n document.body.appendChild(el);\n setHost(el);\n return () => {\n el.remove();\n setHost(null);\n };\n }, [enabled, container, zIndex]);\n\n return host;\n}\n","import {\n useEffect,\n useRef,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { useIsClient } from '../ssr/useIsClient';\nimport { useOverlayHost } from './useOverlayHost';\nimport type { EntranceRenderProps, SkipControl } from '../types';\nimport type { NormalizedOutro } from '../internal/config';\n\nexport interface EntranceOverlayProps {\n api: EntranceRenderProps;\n /** The developer's visuals (a render-prop result or a media element). */\n children: ReactNode;\n outro: NormalizedOutro;\n skipControl?: SkipControl;\n skipLabel?: string;\n zIndex?: number;\n container?: HTMLElement;\n className?: string;\n style?: CSSProperties;\n}\n\nconst DEFAULT_SKIP_LABEL = 'Skip intro';\n\nconst defaultSkipStyle: CSSProperties = {\n position: 'absolute',\n top: 16,\n right: 16,\n pointerEvents: 'auto',\n padding: '6px 12px',\n font: 'inherit',\n fontSize: 14,\n lineHeight: 1.2,\n color: '#fff',\n background: 'rgba(0, 0, 0, 0.55)',\n border: '1px solid rgba(255, 255, 255, 0.35)',\n borderRadius: 6,\n cursor: 'pointer',\n};\n\n/**\n * Renders the developer's visuals as a non-blocking, self-removing overlay.\n *\n * The component itself always returns `null` into the React tree — the visuals\n * live in a portal on a managed `document.body` host — so it is SSR-safe and\n * never causes a hydration mismatch. The host is `pointer-events:none`, so the\n * app underneath stays interactive; only the skip control re-enables pointer\n * events. On a reduced-motion suppression the visuals never mount at all.\n */\nexport function EntranceOverlay({\n api,\n children,\n outro,\n skipControl = true,\n skipLabel = DEFAULT_SKIP_LABEL,\n zIndex,\n container,\n className,\n style,\n}: EntranceOverlayProps): ReactNode {\n const isClient = useIsClient();\n const host = useOverlayHost({\n enabled: isClient && api.isOverlayMounted,\n container,\n zIndex,\n });\n\n // Restore focus to whatever was focused before, but only if focus currently\n // sits inside our overlay (e.g. the skip button) when it tears down — we never\n // steal focus on mount, so most teardowns are no-ops here.\n const previouslyFocused = useRef<Element | null>(null);\n useEffect(() => {\n previouslyFocused.current = document.activeElement;\n return () => {\n const prev = previouslyFocused.current;\n const active = document.activeElement;\n if (\n prev instanceof HTMLElement &&\n active instanceof HTMLElement &&\n active.closest('[data-entrancekit-overlay]')\n ) {\n prev.focus();\n }\n };\n }, []);\n\n if (!isClient || !host || !api.isOverlayMounted) return null;\n\n // Suppress visuals entirely when reduced motion was honored, and outside the\n // active window — this is what keeps a reduce-preference user from seeing any\n // flash, since the machine routes idle → resolve → done without `play`.\n const showVisuals = api.state !== 'idle' && api.reason !== 'reduced-motion';\n const showSkip =\n skipControl !== false &&\n (api.state === 'preload' || api.state === 'play');\n\n const stageStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n pointerEvents: 'none',\n opacity: api.shouldPlayOutro ? 0 : 1,\n transition: api.shouldPlayOutro\n ? `opacity ${outro.durationMs}ms ease`\n : undefined,\n ...style,\n };\n\n const visuals =\n api.shouldPlayOutro && outro.render && api.reason\n ? outro.render({ reason: api.reason })\n : children;\n\n return createPortal(\n <div data-entrancekit-overlay=\"\" className={className} style={stageStyle}>\n {showVisuals && (\n <div aria-hidden=\"true\" style={{ position: 'absolute', inset: 0 }}>\n {visuals}\n </div>\n )}\n {showSkip &&\n (typeof skipControl === 'function' ? (\n skipControl(api.skip)\n ) : (\n <button\n type=\"button\"\n onClick={api.skip}\n aria-label={skipLabel}\n data-entrancekit-skip=\"\"\n style={defaultSkipStyle}\n >\n {skipLabel}\n </button>\n ))}\n </div>,\n host\n );\n}\n","import type { ReactNode } from 'react';\nimport { useEntrance } from './useEntrance';\nimport { EntranceOverlay } from './overlay/EntranceOverlay';\nimport { normalizeOutro } from './internal/config';\nimport type { EntranceBaseProps, EntranceRenderProps } from './types';\n\nexport interface EntranceControllerProps extends EntranceBaseProps {\n children: (api: EntranceRenderProps) => ReactNode;\n}\n\n/**\n * The core entry point: hands your bring-your-own animation the entrance\n * contract via a render-prop, and wraps it in the non-blocking, self-removing\n * overlay (skip control + optional outro chrome). You own the visuals; EntranceKit\n * owns the flow.\n */\nexport function EntranceController(props: EntranceControllerProps): ReactNode {\n const {\n children,\n skipControl,\n skipLabel,\n zIndex,\n container,\n className,\n style,\n ...lifecycle\n } = props;\n\n const api = useEntrance(lifecycle);\n\n return (\n <EntranceOverlay\n api={api}\n outro={normalizeOutro(props.outro)}\n skipControl={skipControl}\n skipLabel={skipLabel}\n zIndex={zIndex}\n container={container}\n className={className}\n style={style}\n >\n {children(api)}\n </EntranceOverlay>\n );\n}\n","import { useEntranceMachine } from '../lifecycle/useEntranceMachine';\nimport type { EntranceMachineActions } from '../lifecycle/useEntranceMachine';\nimport { configFromOptions } from './config';\nimport { deriveRenderProps } from './deriveRenderProps';\nimport { useEntranceCallbacks } from './useEntranceCallbacks';\nimport type { EntranceMachineState } from '../lifecycle/types';\nimport type { EntranceBaseProps, EntranceRenderProps } from '../types';\n\nexport type EntranceEngineOptions = Pick<\n EntranceBaseProps,\n 'id' | 'fallbackAfter' | 'reducedMotion' | 'outro' | 'onResolve' | 'onError' | 'onStateChange'\n>;\n\nexport interface EntranceEngine {\n state: EntranceMachineState;\n actions: EntranceMachineActions;\n api: EntranceRenderProps;\n}\n\n/**\n * The shared lifecycle engine the media wrappers build on: it wires the machine\n * and the public callbacks but, unlike `useEntrance`, does NOT auto-preload —\n * the wrappers drive `preloaded()` from their own native ready-events so a slow\n * or failed asset load is reflected as a real preload phase (and watchdog).\n */\nexport function useEntranceEngine(options: EntranceEngineOptions): EntranceEngine {\n const config = configFromOptions(options);\n const { state, actions } = useEntranceMachine(config);\n useEntranceCallbacks(options.id, state, options);\n return { state, actions, api: deriveRenderProps(state, actions) };\n}\n","import { useEffect, useRef, type CSSProperties, type ReactNode } from 'react';\nimport { EntranceOverlay } from './overlay/EntranceOverlay';\nimport { normalizeOutro } from './internal/config';\nimport { useEntranceEngine } from './internal/useEntranceEngine';\nimport type { EntranceBaseProps } from './types';\n\nexport interface EntranceVideoSource {\n src: string;\n /** MIME type, e.g. `'video/webm'` or `'video/mp4'`. */\n type: string;\n}\n\nexport interface EntranceVideoProps extends EntranceBaseProps {\n src?: string;\n /** Multiple sources for format fallback (e.g. webm then mp4). */\n sources?: EntranceVideoSource[];\n poster?: string;\n /** Default `true` — required for reliable autoplay. */\n muted?: boolean;\n /** Default `true`. */\n playsInline?: boolean;\n preload?: 'auto' | 'metadata' | 'none';\n}\n\nconst videoStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n display: 'block',\n};\n\n/**\n * Convenience entry point for an MP4/WebM entrance. A thin wrapper over the same\n * lifecycle: it prefers the video's own `ended` done-signal, preloads via\n * `canplaythrough`, skips gracefully on a load/playback error, and routes a\n * blocked-autoplay rejection to the error path so the entrance never hangs.\n */\nexport function EntranceVideo(props: EntranceVideoProps): ReactNode {\n const {\n id,\n src,\n sources,\n poster,\n muted = true,\n playsInline = true,\n preload = 'auto',\n fallbackAfter,\n reducedMotion,\n outro,\n skipControl,\n skipLabel,\n zIndex,\n container,\n className,\n style,\n onResolve,\n onError,\n onStateChange,\n } = props;\n\n const { state, actions, api } = useEntranceEngine({\n id,\n fallbackAfter,\n reducedMotion,\n outro,\n onResolve,\n onError,\n onStateChange,\n });\n\n const videoRef = useRef<HTMLVideoElement>(null);\n const endedFired = useRef(false);\n\n // Play imperatively on entering `play`, so a rejected autoplay promise (or a\n // throwing play()) becomes a graceful error rather than a hung entrance.\n useEffect(() => {\n if (state.phase !== 'play') return;\n const video = videoRef.current;\n if (!video) return;\n try {\n video.muted = muted;\n const result = video.play() as Promise<void> | undefined;\n if (result && typeof result.then === 'function') {\n result.catch((err: unknown) => actions.fail('playback', err));\n }\n } catch (err) {\n actions.fail('playback', err);\n }\n }, [state.phase, actions, muted]);\n\n return (\n <EntranceOverlay\n api={api}\n outro={normalizeOutro(outro)}\n skipControl={skipControl}\n skipLabel={skipLabel}\n zIndex={zIndex}\n container={container}\n className={className}\n style={style}\n >\n <video\n ref={videoRef}\n {...(src ? { src } : {})}\n poster={poster}\n muted={muted}\n playsInline={playsInline}\n preload={preload}\n onCanPlayThrough={() => actions.preloaded()}\n onLoadedData={() => actions.preloaded()}\n onEnded={() => {\n if (endedFired.current) return;\n endedFired.current = true;\n actions.complete();\n }}\n onError={() => actions.fail('load')}\n style={videoStyle}\n >\n {sources?.map((source) => (\n <source key={source.src} src={source.src} type={source.type} />\n ))}\n </video>\n </EntranceOverlay>\n );\n}\n","import {\n useCallback,\n useEffect,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { EntranceOverlay } from './overlay/EntranceOverlay';\nimport { normalizeOutro } from './internal/config';\nimport { useEntranceEngine } from './internal/useEntranceEngine';\nimport type { EntranceBaseProps } from './types';\n\n// ---------------------------------------------------------------------------\n// A LOCAL, minimal shape for the optional peer. The package never imports the\n// peer's types, so it type-checks and ships its own .d.ts even when the peer is\n// absent; the real player is loaded lazily at runtime only when this mounts.\n// ---------------------------------------------------------------------------\ninterface DotLottieInstance {\n addEventListener(type: string, listener: () => void): void;\n removeEventListener(type: string, listener?: () => void): void;\n}\ninterface DotLottieReactLikeProps {\n src?: string;\n data?: unknown;\n loop?: boolean;\n autoplay?: boolean;\n dotLottieRefCallback?: (instance: DotLottieInstance | null) => void;\n style?: CSSProperties;\n}\ntype DotLottieReactLike = (props: DotLottieReactLikeProps) => ReactNode;\n\nconst PEER = '@lottiefiles/dotlottie-react';\n\nexport interface EntranceLottieProps extends EntranceBaseProps {\n src?: string;\n animationData?: object;\n /** Entrance semantics: a Lottie entrance never loops. */\n loop?: false;\n onComplete?: () => void;\n}\n\nconst playerStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n display: 'block',\n};\n\n/**\n * Convenience entry point for a Lottie entrance. The runtime player\n * (`@lottiefiles/dotlottie-react`) is an OPTIONAL peer, dynamically imported only\n * when this mounts and only once the entrance is actually proceeding — so the\n * core, the video path, and reduced-motion users never pay for the WASM. A\n * missing peer degrades gracefully (skip) with an actionable message; the\n * player's own one-shot `complete` event is the preferred done-signal.\n */\nexport function EntranceLottie(props: EntranceLottieProps): ReactNode {\n const {\n id,\n src,\n animationData,\n onComplete,\n fallbackAfter,\n reducedMotion,\n outro,\n skipControl,\n skipLabel,\n zIndex,\n container,\n className,\n style,\n onResolve,\n onError,\n onStateChange,\n } = props;\n\n const { state, actions, api } = useEntranceEngine({\n id,\n fallbackAfter,\n reducedMotion,\n outro,\n onResolve,\n onError,\n onStateChange,\n });\n\n const [Player, setPlayer] = useState<DotLottieReactLike | null>(null);\n\n // Load only while the entrance is actually proceeding (never under a\n // reduced-motion suppression, which routes idle → resolve → done). The\n // per-run `alive` flag drops a pending import that resolves after unmount or a\n // StrictMode remount, so it can never setState on a torn-down tree.\n const shouldLoad = state.phase === 'preload' || state.phase === 'play';\n useEffect(() => {\n if (!shouldLoad) return;\n let alive = true;\n const reportMissingPeer = (err: unknown): void => {\n if (typeof console !== 'undefined') {\n console.error(\n `[EntranceKit] EntranceLottie requires the optional peer \"${PEER}\". ` +\n `Install it with: npm i ${PEER}`\n );\n }\n actions.fail('missing-peer', err);\n };\n import('@lottiefiles/dotlottie-react')\n .then((mod) => {\n if (!alive) return;\n const Comp = (mod as { DotLottieReact?: DotLottieReactLike }).DotLottieReact;\n if (Comp) setPlayer(() => Comp);\n else reportMissingPeer(new Error(`${PEER} did not export DotLottieReact`));\n })\n .catch((err: unknown) => {\n if (alive) reportMissingPeer(err);\n });\n return () => {\n alive = false;\n };\n }, [shouldLoad, actions]);\n\n const handleRef = useCallback(\n (instance: DotLottieInstance | null): void => {\n if (!instance) return;\n instance.addEventListener('load', () => actions.preloaded());\n instance.addEventListener('complete', () => {\n actions.complete();\n onComplete?.();\n });\n instance.addEventListener('loadError', () => actions.fail('load'));\n },\n [actions, onComplete]\n );\n\n return (\n <EntranceOverlay\n api={api}\n outro={normalizeOutro(outro)}\n skipControl={skipControl}\n skipLabel={skipLabel}\n zIndex={zIndex}\n container={container}\n className={className}\n style={style}\n >\n {Player ? (\n <Player\n {...(src ? { src } : {})}\n data={animationData}\n loop={false}\n autoplay\n dotLottieRefCallback={handleRef}\n style={playerStyle}\n />\n ) : null}\n </EntranceOverlay>\n );\n}\n"]}