@depup/lottie-react 2.4.1-depup.0 → 3.1.0-depup.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/LICENSE.md +9 -0
- package/README.md +4 -10
- package/build/_virtual/_rolldown/runtime.cjs +23 -0
- package/build/animation/Lottie.cjs +38 -0
- package/build/animation/Lottie.d.cts +47 -0
- package/build/animation/Lottie.d.ts +47 -0
- package/build/animation/Lottie.js +38 -0
- package/build/animation/LottieDisplay.cjs +56 -0
- package/build/animation/LottieDisplay.d.cts +46 -0
- package/build/animation/LottieDisplay.d.ts +46 -0
- package/build/animation/LottieDisplay.js +54 -0
- package/build/animation/LottieInstanceContext.cjs +16 -0
- package/build/animation/LottieInstanceContext.js +17 -0
- package/build/animation/LottieLight.cjs +18 -0
- package/build/animation/LottieLight.d.cts +23 -0
- package/build/animation/LottieLight.d.ts +23 -0
- package/build/animation/LottieLight.js +18 -0
- package/build/animation/LottieRegistryContext.cjs +36 -0
- package/build/animation/LottieRegistryContext.js +35 -0
- package/build/animation/LottieSvg.cjs +19 -0
- package/build/animation/LottieSvg.d.cts +24 -0
- package/build/animation/LottieSvg.d.ts +24 -0
- package/build/animation/LottieSvg.js +19 -0
- package/build/animation/collectMarkerCrossings.cjs +38 -0
- package/build/animation/collectMarkerCrossings.js +38 -0
- package/build/animation/configureLottie.cjs +50 -0
- package/build/animation/configureLottie.d.cts +38 -0
- package/build/animation/configureLottie.d.ts +38 -0
- package/build/animation/configureLottie.js +48 -0
- package/build/animation/createLottieComponent.cjs +131 -0
- package/build/animation/createLottieComponent.d.cts +45 -0
- package/build/animation/createLottieComponent.d.ts +45 -0
- package/build/animation/createLottieComponent.js +131 -0
- package/build/animation/hasExpressions.cjs +25 -0
- package/build/animation/hasExpressions.js +25 -0
- package/build/animation/normalizeAnimationSource.cjs +38 -0
- package/build/animation/normalizeAnimationSource.js +38 -0
- package/build/animation/polymorphicForwardRef.cjs +18 -0
- package/build/animation/polymorphicForwardRef.js +19 -0
- package/build/animation/renderStyledElement.cjs +31 -0
- package/build/animation/renderStyledElement.js +31 -0
- package/build/animation/resolveSeekTarget.cjs +97 -0
- package/build/animation/resolveSeekTarget.js +95 -0
- package/build/animation/styleLayer.cjs +24 -0
- package/build/animation/styleLayer.js +24 -0
- package/build/animation/stylePrecedence.cjs +12 -0
- package/build/animation/stylePrecedence.js +12 -0
- package/build/animation/types.cjs +71 -0
- package/build/animation/types.d.cts +477 -0
- package/build/animation/types.d.ts +477 -0
- package/build/animation/types.js +68 -0
- package/build/animation/useLottie.cjs +48 -0
- package/build/animation/useLottie.d.cts +7 -0
- package/build/animation/useLottie.d.ts +7 -0
- package/build/animation/useLottie.js +45 -0
- package/build/animation/useLottieAnimation.cjs +436 -0
- package/build/animation/useLottieAnimation.d.cts +40 -0
- package/build/animation/useLottieAnimation.d.ts +40 -0
- package/build/animation/useLottieAnimation.js +436 -0
- package/build/animation/useLottieInstance.cjs +21 -0
- package/build/animation/useLottieInstance.d.cts +14 -0
- package/build/animation/useLottieInstance.d.ts +14 -0
- package/build/animation/useLottieInstance.js +21 -0
- package/build/animation/useLottieLight.cjs +28 -0
- package/build/animation/useLottieLight.d.cts +7 -0
- package/build/animation/useLottieLight.d.ts +7 -0
- package/build/animation/useLottieLight.js +25 -0
- package/build/animation/useLottieSvg.cjs +26 -0
- package/build/animation/useLottieSvg.d.cts +7 -0
- package/build/animation/useLottieSvg.d.ts +7 -0
- package/build/animation/useLottieSvg.js +23 -0
- package/build/controls/LottieControls.cjs +181 -0
- package/build/controls/LottieControls.d.cts +58 -0
- package/build/controls/LottieControls.d.ts +58 -0
- package/build/controls/LottieControls.js +179 -0
- package/build/controls/LottieDirectionButton.cjs +36 -0
- package/build/controls/LottieDirectionButton.js +35 -0
- package/build/controls/LottieFullscreenButton.cjs +35 -0
- package/build/controls/LottieFullscreenButton.js +34 -0
- package/build/controls/LottieLoopButton.cjs +32 -0
- package/build/controls/LottieLoopButton.js +31 -0
- package/build/controls/LottiePlayButton.cjs +34 -0
- package/build/controls/LottiePlayButton.js +33 -0
- package/build/controls/LottieReadout.cjs +61 -0
- package/build/controls/LottieReadout.d.cts +13 -0
- package/build/controls/LottieReadout.d.ts +13 -0
- package/build/controls/LottieReadout.js +60 -0
- package/build/controls/LottieSeekBar.cjs +87 -0
- package/build/controls/LottieSeekBar.js +86 -0
- package/build/controls/LottieSpeedSelect.cjs +51 -0
- package/build/controls/LottieSpeedSelect.js +50 -0
- package/build/controls/LottieStopButton.cjs +29 -0
- package/build/controls/LottieStopButton.js +28 -0
- package/build/controls/controlIcon.cjs +25 -0
- package/build/controls/controlIcon.js +25 -0
- package/build/controls/useFullscreen.cjs +46 -0
- package/build/controls/useFullscreen.js +46 -0
- package/build/controls/useShortcuts.cjs +70 -0
- package/build/controls/useShortcuts.js +70 -0
- package/build/index.cjs +38 -0
- package/build/index.d.cts +20 -0
- package/build/index.d.ts +20 -80
- package/build/index.js +18 -667
- package/build/interactions/LottieInteractions.cjs +42 -0
- package/build/interactions/LottieInteractions.d.cts +37 -0
- package/build/interactions/LottieInteractions.d.ts +37 -0
- package/build/interactions/LottieInteractions.js +42 -0
- package/build/interactions/coverProgress.cjs +55 -0
- package/build/interactions/coverProgress.js +53 -0
- package/build/interactions/lottieInView.cjs +110 -0
- package/build/interactions/lottieInView.d.cts +44 -0
- package/build/interactions/lottieInView.d.ts +44 -0
- package/build/interactions/lottieInView.js +110 -0
- package/build/interactions/lottieScrollScrub.cjs +166 -0
- package/build/interactions/lottieScrollScrub.d.cts +48 -0
- package/build/interactions/lottieScrollScrub.d.ts +48 -0
- package/build/interactions/lottieScrollScrub.js +166 -0
- package/build/interactions/types.d.cts +50 -0
- package/build/interactions/types.d.ts +50 -0
- package/build/interactions/useInteractionsRunner.cjs +154 -0
- package/build/interactions/useInteractionsRunner.js +153 -0
- package/build/interactions/useLottieInteractions.cjs +22 -0
- package/build/interactions/useLottieInteractions.d.cts +19 -0
- package/build/interactions/useLottieInteractions.d.ts +19 -0
- package/build/interactions/useLottieInteractions.js +22 -0
- package/build/overlays/LottieError.cjs +75 -0
- package/build/overlays/LottieError.d.cts +40 -0
- package/build/overlays/LottieError.d.ts +40 -0
- package/build/overlays/LottieError.js +73 -0
- package/build/overlays/LottieLoading.cjs +90 -0
- package/build/overlays/LottieLoading.d.cts +45 -0
- package/build/overlays/LottieLoading.d.ts +45 -0
- package/build/overlays/LottieLoading.js +88 -0
- package/build/overlays/overlayStyles.cjs +33 -0
- package/build/overlays/overlayStyles.js +33 -0
- package/build/utils/SubscriptionManager.cjs +42 -0
- package/build/utils/SubscriptionManager.d.cts +30 -0
- package/build/utils/SubscriptionManager.d.ts +30 -0
- package/build/utils/SubscriptionManager.js +42 -0
- package/build/utils/createLogger.cjs +25 -0
- package/build/utils/createLogger.js +25 -0
- package/build/utils/isSameJson.cjs +37 -0
- package/build/utils/isSameJson.js +37 -0
- package/build/utils/mergeRefs.cjs +37 -0
- package/build/utils/mergeRefs.js +37 -0
- package/build/utils/useStableValue.cjs +26 -0
- package/build/utils/useStableValue.js +26 -0
- package/changes.json +3 -8
- package/package.json +144 -89
- package/LICENSE +0 -46
- package/build/index.es.js +0 -653
- package/build/index.es.js.map +0 -1
- package/build/index.es.min.js +0 -3
- package/build/index.js.map +0 -1
- package/build/index.min.js +0 -3
- package/build/index.umd.js +0 -670
- package/build/index.umd.js.map +0 -1
- package/build/index.umd.min.js +0 -3
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { mergeRefs } from "../utils/mergeRefs.js";
|
|
2
|
+
import { polymorphicForwardRef } from "./polymorphicForwardRef.js";
|
|
3
|
+
import { renderStyledElement } from "./renderStyledElement.js";
|
|
4
|
+
import { LottieInstanceContext } from "./LottieInstanceContext.js";
|
|
5
|
+
import { lottieDisplayClass, lottieDisplayStyles } from "./LottieDisplay.js";
|
|
6
|
+
import { useLottieAnimation } from "./useLottieAnimation.js";
|
|
7
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
//#region src/animation/createLottieComponent.tsx
|
|
10
|
+
/**
|
|
11
|
+
* The class the element around the animation carries when you place the
|
|
12
|
+
* animation yourself, and the name React deduplicates its stylesheet by.
|
|
13
|
+
*/
|
|
14
|
+
const lottieRootClass = "lottie-root";
|
|
15
|
+
/**
|
|
16
|
+
* The rules that element carries, at zero specificity and inside the library's
|
|
17
|
+
* cascade layer, so any rule the consumer writes beats them per property.
|
|
18
|
+
*
|
|
19
|
+
* The column is what puts the controls below the animation, and `position` is
|
|
20
|
+
* what the overlays anchor to. The second rule is what lets the animation take
|
|
21
|
+
* the space the controls leave, since the display's own default height is a
|
|
22
|
+
* full 100% that would otherwise push them out of the box.
|
|
23
|
+
*
|
|
24
|
+
* The descendant rule is written as two separate `:where()` selectors rather
|
|
25
|
+
* than one wrapping the combinator. The two are identical CSS at identical zero
|
|
26
|
+
* specificity, so the split form costs nothing and reads as two independent
|
|
27
|
+
* claims about specificity.
|
|
28
|
+
*
|
|
29
|
+
* The third rule is what this element looks like once it fills the screen. A
|
|
30
|
+
* browser paints a black backdrop behind whatever goes fullscreen and leaves
|
|
31
|
+
* the element itself transparent, so without a surface of its own the animation
|
|
32
|
+
* sits on black and the control bar, whose colours are all inherited, keeps the
|
|
33
|
+
* page's dark text and becomes close to unreadable. Measured in Chromium
|
|
34
|
+
* against the real bar. `Canvas` and `CanvasText` are the system page colours
|
|
35
|
+
* and are taken as a pair, so the two can never contradict each other the way a
|
|
36
|
+
* background without a matching foreground can; they follow the consumer's
|
|
37
|
+
* declared colour scheme rather than a colour this library picked, and the rule
|
|
38
|
+
* is zero-specificity like every other, so one line of their own CSS replaces
|
|
39
|
+
* either. It cannot be verified here, since this environment has no Fullscreen
|
|
40
|
+
* API at all.
|
|
41
|
+
*/
|
|
42
|
+
const lottieRootStyles = `:where(.${lottieRootClass}){position:relative;display:flex;flex-direction:column}:where(.${lottieRootClass}) > :where(.${lottieDisplayClass}){flex:1;height:auto}:where(.${lottieRootClass}:fullscreen){background:Canvas;color:CanvasText}`;
|
|
43
|
+
/**
|
|
44
|
+
* Builds the component all three public ones are: the full, svg and light
|
|
45
|
+
* builds differ in the engine they load and in the renderers they admit, and in
|
|
46
|
+
* nothing else.
|
|
47
|
+
*
|
|
48
|
+
* `Renderers` is what narrows a smaller build to the renderers it actually
|
|
49
|
+
* contains, so asking it for one it does not have is a compile error rather
|
|
50
|
+
* than a blank animation and a runtime throw.
|
|
51
|
+
*/
|
|
52
|
+
function createLottieComponent(engine) {
|
|
53
|
+
return polymorphicForwardRef(function Lottie({ as, children, className, lottieRef, src, renderer, rendererSettings, loop, speed, direction, autoplay, segment, assetsPath, debug, subscriptions, ...rest }, ref) {
|
|
54
|
+
const instance = useLottieAnimation(engine, {
|
|
55
|
+
src,
|
|
56
|
+
renderer,
|
|
57
|
+
rendererSettings,
|
|
58
|
+
loop,
|
|
59
|
+
speed,
|
|
60
|
+
direction,
|
|
61
|
+
autoplay,
|
|
62
|
+
segment,
|
|
63
|
+
assetsPath,
|
|
64
|
+
debug,
|
|
65
|
+
subscriptions
|
|
66
|
+
});
|
|
67
|
+
const { setDisplayRef, setRootRef } = instance;
|
|
68
|
+
useImperativeHandle(lottieRef, () => ({
|
|
69
|
+
reload: instance.reload,
|
|
70
|
+
play: instance.play,
|
|
71
|
+
pause: instance.pause,
|
|
72
|
+
stop: instance.stop,
|
|
73
|
+
seek: instance.seek,
|
|
74
|
+
scrubStart: instance.scrubStart,
|
|
75
|
+
scrubTo: instance.scrubTo,
|
|
76
|
+
scrubEnd: instance.scrubEnd,
|
|
77
|
+
playSegments: instance.playSegments,
|
|
78
|
+
resetSegments: instance.resetSegments,
|
|
79
|
+
setSpeed: instance.setSpeed,
|
|
80
|
+
setDirection: instance.setDirection,
|
|
81
|
+
setLoop: instance.setLoop,
|
|
82
|
+
animationItem: instance.animationItem
|
|
83
|
+
}));
|
|
84
|
+
const attached = useRef(0);
|
|
85
|
+
const countDisplay = useCallback((node) => {
|
|
86
|
+
attached.current += node === null ? -1 : 1;
|
|
87
|
+
setDisplayRef(node);
|
|
88
|
+
}, [setDisplayRef]);
|
|
89
|
+
const published = useMemo(() => ({
|
|
90
|
+
...instance,
|
|
91
|
+
setDisplayRef: countDisplay
|
|
92
|
+
}), [instance, countDisplay]);
|
|
93
|
+
const hasChildren = children !== void 0;
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
96
|
+
if (!hasChildren || attached.current === 1) return;
|
|
97
|
+
if (attached.current === 0) console.warn("[lottie-react] <Lottie> was given children and none of them is a <LottieDisplay>, so the animation has nowhere to be drawn. Add one where the animation should go. A display rendered conditionally has not appeared yet on this pass, which is the one case where this warning is expected.");
|
|
98
|
+
else console.warn(`[lottie-react] <Lottie> was given ${attached.current} <LottieDisplay> children and an animation can only be drawn in one of them, so the last to attach is the one that gets it. Render one display per animation.`);
|
|
99
|
+
}
|
|
100
|
+
}, [hasChildren]);
|
|
101
|
+
const attachDisplay = useMemo(() => mergeRefs(ref, countDisplay, setRootRef), [
|
|
102
|
+
ref,
|
|
103
|
+
countDisplay,
|
|
104
|
+
setRootRef
|
|
105
|
+
]);
|
|
106
|
+
const attachWrapper = useMemo(() => mergeRefs(ref, setRootRef), [ref, setRootRef]);
|
|
107
|
+
const tag = typeof as === "string" ? as : "div";
|
|
108
|
+
if (!hasChildren) return renderStyledElement({
|
|
109
|
+
tag,
|
|
110
|
+
styleClass: lottieDisplayClass,
|
|
111
|
+
styles: lottieDisplayStyles,
|
|
112
|
+
className,
|
|
113
|
+
attributes: rest,
|
|
114
|
+
ref: attachDisplay
|
|
115
|
+
});
|
|
116
|
+
return /* @__PURE__ */ jsx(LottieInstanceContext.Provider, {
|
|
117
|
+
value: published,
|
|
118
|
+
children: renderStyledElement({
|
|
119
|
+
tag,
|
|
120
|
+
styleClass: lottieRootClass,
|
|
121
|
+
styles: lottieRootStyles,
|
|
122
|
+
className,
|
|
123
|
+
attributes: rest,
|
|
124
|
+
ref: attachWrapper,
|
|
125
|
+
children
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { createLottieComponent };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/animation/hasExpressions.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Whether a parsed animation carries an expression anywhere in it.
|
|
7
|
+
*
|
|
8
|
+
* An expression is a non-empty string under the key `x` on an animatable
|
|
9
|
+
* property, which is also how the engine finds them. The type of the value is
|
|
10
|
+
* what decides, not the key: inside keyframes `x` names the numeric easing
|
|
11
|
+
* handles, and on a mask it names the expansion property, an object which may
|
|
12
|
+
* itself carry an expression one level down. The walk covers objects and
|
|
13
|
+
* arrays across the whole document rather than `layers` alone, because a
|
|
14
|
+
* precomp's layers live under `assets`, and it stops at the first find. It
|
|
15
|
+
* assumes the tree shape parsed JSON always has: an object that referenced
|
|
16
|
+
* itself would recurse without end.
|
|
17
|
+
*/
|
|
18
|
+
function hasExpressions(value) {
|
|
19
|
+
if (Array.isArray(value)) return value.some(hasExpressions);
|
|
20
|
+
if (!isRecord(value)) return false;
|
|
21
|
+
if (typeof value.x === "string" && value.x !== "") return true;
|
|
22
|
+
return Object.values(value).some(hasExpressions);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
exports.hasExpressions = hasExpressions;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/animation/hasExpressions.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Whether a parsed animation carries an expression anywhere in it.
|
|
7
|
+
*
|
|
8
|
+
* An expression is a non-empty string under the key `x` on an animatable
|
|
9
|
+
* property, which is also how the engine finds them. The type of the value is
|
|
10
|
+
* what decides, not the key: inside keyframes `x` names the numeric easing
|
|
11
|
+
* handles, and on a mask it names the expansion property, an object which may
|
|
12
|
+
* itself carry an expression one level down. The walk covers objects and
|
|
13
|
+
* arrays across the whole document rather than `layers` alone, because a
|
|
14
|
+
* precomp's layers live under `assets`, and it stops at the first find. It
|
|
15
|
+
* assumes the tree shape parsed JSON always has: an object that referenced
|
|
16
|
+
* itself would recurse without end.
|
|
17
|
+
*/
|
|
18
|
+
function hasExpressions(value) {
|
|
19
|
+
if (Array.isArray(value)) return value.some(hasExpressions);
|
|
20
|
+
if (!isRecord(value)) return false;
|
|
21
|
+
if (typeof value.x === "string" && value.x !== "") return true;
|
|
22
|
+
return Object.values(value).some(hasExpressions);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { hasExpressions };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/animation/normalizeAnimationSource.ts
|
|
2
|
+
/**
|
|
3
|
+
* Turns the `src` prop into the pair of fields lottie-web understands, or
|
|
4
|
+
* `null` when it is not something an animation could be loaded from.
|
|
5
|
+
*
|
|
6
|
+
* A non-empty string is a URL or path that the engine fetches and parses as
|
|
7
|
+
* JSON. A `.lottie` archive is **not** supported: it is a zip, and nothing here
|
|
8
|
+
* or in lottie-web unpacks one.
|
|
9
|
+
*
|
|
10
|
+
* An object is the parsed animation itself, and it is **always** shallow-copied
|
|
11
|
+
* before the engine sees it. lottie-web writes `__complete` onto whatever it is
|
|
12
|
+
* handed, so a frozen object throws `TypeError: Cannot add property __complete`
|
|
13
|
+
* and takes the whole load down with it. That happens with `import * as data
|
|
14
|
+
* from "./animation.json"` under Node's own JSON modules, whose namespace is
|
|
15
|
+
* not extensible.
|
|
16
|
+
*
|
|
17
|
+
* The copy is unconditional rather than guarded by `Object.isExtensible`,
|
|
18
|
+
* because the shape of that namespace object differs by toolchain: Node's is
|
|
19
|
+
* not extensible while a bundler's usually is. A guard would therefore behave
|
|
20
|
+
* differently depending on who built the consumer's application, and a copy
|
|
21
|
+
* behaves the same everywhere. It costs about 70 nanoseconds and does not grow
|
|
22
|
+
* with the animation, since only the top level is copied.
|
|
23
|
+
*
|
|
24
|
+
* The copy does not protect the objects inside `layers`, which the engine
|
|
25
|
+
* annotates in place. That is harmless, because those writes are idempotent
|
|
26
|
+
* flags, and avoiding it would mean deep-copying the entire animation on every
|
|
27
|
+
* load.
|
|
28
|
+
*/
|
|
29
|
+
function normalizeAnimationSource(source) {
|
|
30
|
+
if (typeof source === "string") {
|
|
31
|
+
const path = source.trim();
|
|
32
|
+
return path.length > 0 ? { path } : null;
|
|
33
|
+
}
|
|
34
|
+
if (typeof source === "object" && source !== null && !Array.isArray(source)) return { animationData: { ...source } };
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.normalizeAnimationSource = normalizeAnimationSource;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/animation/normalizeAnimationSource.ts
|
|
2
|
+
/**
|
|
3
|
+
* Turns the `src` prop into the pair of fields lottie-web understands, or
|
|
4
|
+
* `null` when it is not something an animation could be loaded from.
|
|
5
|
+
*
|
|
6
|
+
* A non-empty string is a URL or path that the engine fetches and parses as
|
|
7
|
+
* JSON. A `.lottie` archive is **not** supported: it is a zip, and nothing here
|
|
8
|
+
* or in lottie-web unpacks one.
|
|
9
|
+
*
|
|
10
|
+
* An object is the parsed animation itself, and it is **always** shallow-copied
|
|
11
|
+
* before the engine sees it. lottie-web writes `__complete` onto whatever it is
|
|
12
|
+
* handed, so a frozen object throws `TypeError: Cannot add property __complete`
|
|
13
|
+
* and takes the whole load down with it. That happens with `import * as data
|
|
14
|
+
* from "./animation.json"` under Node's own JSON modules, whose namespace is
|
|
15
|
+
* not extensible.
|
|
16
|
+
*
|
|
17
|
+
* The copy is unconditional rather than guarded by `Object.isExtensible`,
|
|
18
|
+
* because the shape of that namespace object differs by toolchain: Node's is
|
|
19
|
+
* not extensible while a bundler's usually is. A guard would therefore behave
|
|
20
|
+
* differently depending on who built the consumer's application, and a copy
|
|
21
|
+
* behaves the same everywhere. It costs about 70 nanoseconds and does not grow
|
|
22
|
+
* with the animation, since only the top level is copied.
|
|
23
|
+
*
|
|
24
|
+
* The copy does not protect the objects inside `layers`, which the engine
|
|
25
|
+
* annotates in place. That is harmless, because those writes are idempotent
|
|
26
|
+
* flags, and avoiding it would mean deep-copying the entire animation on every
|
|
27
|
+
* load.
|
|
28
|
+
*/
|
|
29
|
+
function normalizeAnimationSource(source) {
|
|
30
|
+
if (typeof source === "string") {
|
|
31
|
+
const path = source.trim();
|
|
32
|
+
return path.length > 0 ? { path } : null;
|
|
33
|
+
}
|
|
34
|
+
if (typeof source === "object" && source !== null && !Array.isArray(source)) return { animationData: { ...source } };
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { normalizeAnimationSource };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/animation/polymorphicForwardRef.ts
|
|
2
|
+
/**
|
|
3
|
+
* `forwardRef`, with the render function's own type parameters kept.
|
|
4
|
+
*
|
|
5
|
+
* A component that chooses its element through `as` is generic in that tag, and
|
|
6
|
+
* React's `forwardRef` is not: its signature settles the props at one type, so
|
|
7
|
+
* the tag is lost and `as="button" type="button"` stops typechecking while
|
|
8
|
+
* invented props start passing. Handing the same runtime function a signature
|
|
9
|
+
* that carries the parameters through is what restores per-element props.
|
|
10
|
+
*
|
|
11
|
+
* The assertion lives here, alone, so that there is one of it and so that what
|
|
12
|
+
* removes it is written beside it. React 18 is the whole reason it exists,
|
|
13
|
+
* because a function component receives a `ref` only through `forwardRef`
|
|
14
|
+
* there.
|
|
15
|
+
*/
|
|
16
|
+
const polymorphicForwardRef = require("react").forwardRef;
|
|
17
|
+
//#endregion
|
|
18
|
+
exports.polymorphicForwardRef = polymorphicForwardRef;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
//#region src/animation/polymorphicForwardRef.ts
|
|
3
|
+
/**
|
|
4
|
+
* `forwardRef`, with the render function's own type parameters kept.
|
|
5
|
+
*
|
|
6
|
+
* A component that chooses its element through `as` is generic in that tag, and
|
|
7
|
+
* React's `forwardRef` is not: its signature settles the props at one type, so
|
|
8
|
+
* the tag is lost and `as="button" type="button"` stops typechecking while
|
|
9
|
+
* invented props start passing. Handing the same runtime function a signature
|
|
10
|
+
* that carries the parameters through is what restores per-element props.
|
|
11
|
+
*
|
|
12
|
+
* The assertion lives here, alone, so that there is one of it and so that what
|
|
13
|
+
* removes it is written beside it. React 18 is the whole reason it exists,
|
|
14
|
+
* because a function component receives a `ref` only through `forwardRef`
|
|
15
|
+
* there.
|
|
16
|
+
*/
|
|
17
|
+
const polymorphicForwardRef = forwardRef;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { polymorphicForwardRef };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const require_styleLayer = require("./styleLayer.cjs");
|
|
2
|
+
const require_stylePrecedence = require("./stylePrecedence.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
//#region src/animation/renderStyledElement.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Renders one element carrying a library class, together with the stylesheet
|
|
8
|
+
* that class names.
|
|
9
|
+
*
|
|
10
|
+
* The class and the `href` are one string, so React deduplicates the rules by
|
|
11
|
+
* the same name the rules are scoped to: two components can only collide in the
|
|
12
|
+
* document if they already collide in CSS, which is visible rather than silent.
|
|
13
|
+
*
|
|
14
|
+
* Built through `createElement` rather than as JSX, because JSX cannot take an
|
|
15
|
+
* element type that is still generic: `<Tag />` where the tag comes from `as`
|
|
16
|
+
* is `TS2604, no construct or call signatures`. Narrowing to one concrete tag
|
|
17
|
+
* instead needs an assertion, which this does not.
|
|
18
|
+
*/
|
|
19
|
+
function renderStyledElement({ tag, styleClass, styles, className, attributes, ref, children }) {
|
|
20
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react.createElement)(tag, {
|
|
21
|
+
...attributes,
|
|
22
|
+
className: className ? `${styleClass} ${className}` : styleClass,
|
|
23
|
+
ref
|
|
24
|
+
}, children), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", {
|
|
25
|
+
href: styleClass,
|
|
26
|
+
precedence: require_stylePrecedence.stylePrecedence,
|
|
27
|
+
children: `@layer ${require_styleLayer.styleLayer}{${styles}}`
|
|
28
|
+
})] });
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
exports.renderStyledElement = renderStyledElement;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { styleLayer } from "./styleLayer.js";
|
|
2
|
+
import { stylePrecedence } from "./stylePrecedence.js";
|
|
3
|
+
import { createElement } from "react";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/animation/renderStyledElement.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Renders one element carrying a library class, together with the stylesheet
|
|
8
|
+
* that class names.
|
|
9
|
+
*
|
|
10
|
+
* The class and the `href` are one string, so React deduplicates the rules by
|
|
11
|
+
* the same name the rules are scoped to: two components can only collide in the
|
|
12
|
+
* document if they already collide in CSS, which is visible rather than silent.
|
|
13
|
+
*
|
|
14
|
+
* Built through `createElement` rather than as JSX, because JSX cannot take an
|
|
15
|
+
* element type that is still generic: `<Tag />` where the tag comes from `as`
|
|
16
|
+
* is `TS2604, no construct or call signatures`. Narrowing to one concrete tag
|
|
17
|
+
* instead needs an assertion, which this does not.
|
|
18
|
+
*/
|
|
19
|
+
function renderStyledElement({ tag, styleClass, styles, className, attributes, ref, children }) {
|
|
20
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [createElement(tag, {
|
|
21
|
+
...attributes,
|
|
22
|
+
className: className ? `${styleClass} ${className}` : styleClass,
|
|
23
|
+
ref
|
|
24
|
+
}, children), /* @__PURE__ */ jsx("style", {
|
|
25
|
+
href: styleClass,
|
|
26
|
+
precedence: stylePrecedence,
|
|
27
|
+
children: `@layer ${styleLayer}{${styles}}`
|
|
28
|
+
})] });
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { renderStyledElement };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
//#region src/animation/resolveSeekTarget.ts
|
|
2
|
+
/**
|
|
3
|
+
* Narrows an unknown value to a marker we can use.
|
|
4
|
+
*
|
|
5
|
+
* A marker whose After Effects comment parsed as JSON without a `name` fails
|
|
6
|
+
* here, which matches the engine: such a marker cannot be addressed by name at
|
|
7
|
+
* all, so a designer can label something the code has no way to reach.
|
|
8
|
+
*/
|
|
9
|
+
function isMarker(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null) return false;
|
|
11
|
+
if (!("time" in value) || typeof value.time !== "number") return false;
|
|
12
|
+
if ("duration" in value) {
|
|
13
|
+
const { duration } = value;
|
|
14
|
+
if (duration !== void 0 && typeof duration !== "number") return false;
|
|
15
|
+
}
|
|
16
|
+
if (!("payload" in value)) return false;
|
|
17
|
+
const { payload } = value;
|
|
18
|
+
if (typeof payload !== "object" || payload === null) return false;
|
|
19
|
+
return "name" in payload && typeof payload.name === "string";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The animation's markers, or an empty list when it has none we can read.
|
|
23
|
+
*
|
|
24
|
+
* The `in` check is what makes the undeclared property readable without a cast:
|
|
25
|
+
* it narrows to a record whose value is `unknown`, which the guard above then
|
|
26
|
+
* takes the rest of the way.
|
|
27
|
+
*/
|
|
28
|
+
function readMarkers(item) {
|
|
29
|
+
if (!("markers" in item)) return [];
|
|
30
|
+
const { markers } = item;
|
|
31
|
+
if (!Array.isArray(markers)) return [];
|
|
32
|
+
return markers.filter(isMarker);
|
|
33
|
+
}
|
|
34
|
+
function findMarker(item, name) {
|
|
35
|
+
for (const marker of readMarkers(item)) if (marker.payload.name === name) return marker;
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/** The frame a target asks for, before it is held inside the playable range. */
|
|
39
|
+
function requestedFrame(item, target) {
|
|
40
|
+
if (typeof target === "number") return target;
|
|
41
|
+
const { frame, percent, seconds, marker } = target;
|
|
42
|
+
if (frame !== void 0) return frame;
|
|
43
|
+
if (percent !== void 0) return item.totalFrames * percent / 100;
|
|
44
|
+
if (seconds !== void 0) return seconds * item.frameRate;
|
|
45
|
+
const found = marker === void 0 ? null : findMarker(item, marker);
|
|
46
|
+
return found === null ? null : found.time - item.firstFrame;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Turns a seek target into a frame inside the playable range.
|
|
50
|
+
*
|
|
51
|
+
* Returns `null` when the target names nothing the animation has, which is an
|
|
52
|
+
* unknown marker or a number that is not finite. `requested` is what was asked
|
|
53
|
+
* for and `frame` is what is reachable, so a caller can tell that a position
|
|
54
|
+
* was pulled back to the range without this having to say anything about it.
|
|
55
|
+
*/
|
|
56
|
+
function resolveSeekTarget(item, target) {
|
|
57
|
+
const requested = requestedFrame(item, target);
|
|
58
|
+
if (requested === null || !Number.isFinite(requested)) return null;
|
|
59
|
+
const last = Math.max(item.totalFrames - .001, 0);
|
|
60
|
+
return {
|
|
61
|
+
frame: Math.min(Math.max(requested, 0), last),
|
|
62
|
+
requested
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Distinguishes one range from a list of them, which share an array type. */
|
|
66
|
+
function isSingleRange(segments) {
|
|
67
|
+
return typeof segments[0] === "number";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Turns what a caller passed into the list of ranges the engine takes.
|
|
71
|
+
*
|
|
72
|
+
* Returns `null` when nothing playable was described: a range whose ends are
|
|
73
|
+
* equal or not finite, an empty list, an unknown marker, or a marker carrying
|
|
74
|
+
* no duration. That last one is a labelled position rather than a labelled
|
|
75
|
+
* span, and the engine would respond to it by playing the entire animation.
|
|
76
|
+
*
|
|
77
|
+
* A marker's frames are used as they are. Unlike a seek, a range endpoint is
|
|
78
|
+
* absolute, so the correction that seeking needs would be wrong here.
|
|
79
|
+
*
|
|
80
|
+
* The result is always freshly built, never the caller's own arrays, because
|
|
81
|
+
* the engine queues whatever it is handed and the input is typed `readonly`.
|
|
82
|
+
*/
|
|
83
|
+
function resolveSegments(item, segments) {
|
|
84
|
+
if ("marker" in segments) {
|
|
85
|
+
const marker = findMarker(item, segments.marker);
|
|
86
|
+
if (marker === null || !marker.duration) return null;
|
|
87
|
+
return [[marker.time, marker.time + marker.duration]];
|
|
88
|
+
}
|
|
89
|
+
const ranges = isSingleRange(segments) ? [segments] : segments;
|
|
90
|
+
if (ranges.length === 0) return null;
|
|
91
|
+
for (const [start, end] of ranges) if (!Number.isFinite(start) || !Number.isFinite(end) || start === end) return null;
|
|
92
|
+
return ranges.map(([start, end]) => [start, end]);
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
exports.readMarkers = readMarkers;
|
|
96
|
+
exports.resolveSeekTarget = resolveSeekTarget;
|
|
97
|
+
exports.resolveSegments = resolveSegments;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//#region src/animation/resolveSeekTarget.ts
|
|
2
|
+
/**
|
|
3
|
+
* Narrows an unknown value to a marker we can use.
|
|
4
|
+
*
|
|
5
|
+
* A marker whose After Effects comment parsed as JSON without a `name` fails
|
|
6
|
+
* here, which matches the engine: such a marker cannot be addressed by name at
|
|
7
|
+
* all, so a designer can label something the code has no way to reach.
|
|
8
|
+
*/
|
|
9
|
+
function isMarker(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null) return false;
|
|
11
|
+
if (!("time" in value) || typeof value.time !== "number") return false;
|
|
12
|
+
if ("duration" in value) {
|
|
13
|
+
const { duration } = value;
|
|
14
|
+
if (duration !== void 0 && typeof duration !== "number") return false;
|
|
15
|
+
}
|
|
16
|
+
if (!("payload" in value)) return false;
|
|
17
|
+
const { payload } = value;
|
|
18
|
+
if (typeof payload !== "object" || payload === null) return false;
|
|
19
|
+
return "name" in payload && typeof payload.name === "string";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The animation's markers, or an empty list when it has none we can read.
|
|
23
|
+
*
|
|
24
|
+
* The `in` check is what makes the undeclared property readable without a cast:
|
|
25
|
+
* it narrows to a record whose value is `unknown`, which the guard above then
|
|
26
|
+
* takes the rest of the way.
|
|
27
|
+
*/
|
|
28
|
+
function readMarkers(item) {
|
|
29
|
+
if (!("markers" in item)) return [];
|
|
30
|
+
const { markers } = item;
|
|
31
|
+
if (!Array.isArray(markers)) return [];
|
|
32
|
+
return markers.filter(isMarker);
|
|
33
|
+
}
|
|
34
|
+
function findMarker(item, name) {
|
|
35
|
+
for (const marker of readMarkers(item)) if (marker.payload.name === name) return marker;
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/** The frame a target asks for, before it is held inside the playable range. */
|
|
39
|
+
function requestedFrame(item, target) {
|
|
40
|
+
if (typeof target === "number") return target;
|
|
41
|
+
const { frame, percent, seconds, marker } = target;
|
|
42
|
+
if (frame !== void 0) return frame;
|
|
43
|
+
if (percent !== void 0) return item.totalFrames * percent / 100;
|
|
44
|
+
if (seconds !== void 0) return seconds * item.frameRate;
|
|
45
|
+
const found = marker === void 0 ? null : findMarker(item, marker);
|
|
46
|
+
return found === null ? null : found.time - item.firstFrame;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Turns a seek target into a frame inside the playable range.
|
|
50
|
+
*
|
|
51
|
+
* Returns `null` when the target names nothing the animation has, which is an
|
|
52
|
+
* unknown marker or a number that is not finite. `requested` is what was asked
|
|
53
|
+
* for and `frame` is what is reachable, so a caller can tell that a position
|
|
54
|
+
* was pulled back to the range without this having to say anything about it.
|
|
55
|
+
*/
|
|
56
|
+
function resolveSeekTarget(item, target) {
|
|
57
|
+
const requested = requestedFrame(item, target);
|
|
58
|
+
if (requested === null || !Number.isFinite(requested)) return null;
|
|
59
|
+
const last = Math.max(item.totalFrames - .001, 0);
|
|
60
|
+
return {
|
|
61
|
+
frame: Math.min(Math.max(requested, 0), last),
|
|
62
|
+
requested
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Distinguishes one range from a list of them, which share an array type. */
|
|
66
|
+
function isSingleRange(segments) {
|
|
67
|
+
return typeof segments[0] === "number";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Turns what a caller passed into the list of ranges the engine takes.
|
|
71
|
+
*
|
|
72
|
+
* Returns `null` when nothing playable was described: a range whose ends are
|
|
73
|
+
* equal or not finite, an empty list, an unknown marker, or a marker carrying
|
|
74
|
+
* no duration. That last one is a labelled position rather than a labelled
|
|
75
|
+
* span, and the engine would respond to it by playing the entire animation.
|
|
76
|
+
*
|
|
77
|
+
* A marker's frames are used as they are. Unlike a seek, a range endpoint is
|
|
78
|
+
* absolute, so the correction that seeking needs would be wrong here.
|
|
79
|
+
*
|
|
80
|
+
* The result is always freshly built, never the caller's own arrays, because
|
|
81
|
+
* the engine queues whatever it is handed and the input is typed `readonly`.
|
|
82
|
+
*/
|
|
83
|
+
function resolveSegments(item, segments) {
|
|
84
|
+
if ("marker" in segments) {
|
|
85
|
+
const marker = findMarker(item, segments.marker);
|
|
86
|
+
if (marker === null || !marker.duration) return null;
|
|
87
|
+
return [[marker.time, marker.time + marker.duration]];
|
|
88
|
+
}
|
|
89
|
+
const ranges = isSingleRange(segments) ? [segments] : segments;
|
|
90
|
+
if (ranges.length === 0) return null;
|
|
91
|
+
for (const [start, end] of ranges) if (!Number.isFinite(start) || !Number.isFinite(end) || start === end) return null;
|
|
92
|
+
return ranges.map(([start, end]) => [start, end]);
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { readMarkers, resolveSeekTarget, resolveSegments };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/animation/styleLayer.ts
|
|
2
|
+
/**
|
|
3
|
+
* The cascade layer every library stylesheet is wrapped in.
|
|
4
|
+
*
|
|
5
|
+
* The wrapper exists for consumers whose own CSS lives in cascade layers,
|
|
6
|
+
* Tailwind 4 being the common case. Unlayered rules outrank all layered rules,
|
|
7
|
+
* so without a layer of our own even a zero-specificity default beats every
|
|
8
|
+
* utility class. Inside a layer, the defaults can be ranked below the
|
|
9
|
+
* consumer's layers with one declaration written before their own styles load:
|
|
10
|
+
*
|
|
11
|
+
* ```css
|
|
12
|
+
* @layer lottie-react;
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* A consumer whose CSS is unlayered needs nothing: unlayered rules beat
|
|
16
|
+
* layered ones, so their styles keep winning as before.
|
|
17
|
+
*
|
|
18
|
+
* Named after the package, like the style precedence, because a name a
|
|
19
|
+
* consumer already associates with the library cannot collide with a layer
|
|
20
|
+
* ladder of their own.
|
|
21
|
+
*/
|
|
22
|
+
const styleLayer = "lottie-react";
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.styleLayer = styleLayer;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/animation/styleLayer.ts
|
|
2
|
+
/**
|
|
3
|
+
* The cascade layer every library stylesheet is wrapped in.
|
|
4
|
+
*
|
|
5
|
+
* The wrapper exists for consumers whose own CSS lives in cascade layers,
|
|
6
|
+
* Tailwind 4 being the common case. Unlayered rules outrank all layered rules,
|
|
7
|
+
* so without a layer of our own even a zero-specificity default beats every
|
|
8
|
+
* utility class. Inside a layer, the defaults can be ranked below the
|
|
9
|
+
* consumer's layers with one declaration written before their own styles load:
|
|
10
|
+
*
|
|
11
|
+
* ```css
|
|
12
|
+
* @layer lottie-react;
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* A consumer whose CSS is unlayered needs nothing: unlayered rules beat
|
|
16
|
+
* layered ones, so their styles keep winning as before.
|
|
17
|
+
*
|
|
18
|
+
* Named after the package, like the style precedence, because a name a
|
|
19
|
+
* consumer already associates with the library cannot collide with a layer
|
|
20
|
+
* ladder of their own.
|
|
21
|
+
*/
|
|
22
|
+
const styleLayer = "lottie-react";
|
|
23
|
+
//#endregion
|
|
24
|
+
export { styleLayer };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/animation/stylePrecedence.ts
|
|
2
|
+
/**
|
|
3
|
+
* How React ranks every stylesheet this library renders.
|
|
4
|
+
*
|
|
5
|
+
* One value for all of them, so they sit together in a block a consumer can
|
|
6
|
+
* rank their own CSS against as a unit. It names the package rather than a
|
|
7
|
+
* position, because React treats a precedence it meets first as lower, so a
|
|
8
|
+
* position would only be right by accident of render order.
|
|
9
|
+
*/
|
|
10
|
+
const stylePrecedence = "lottie-react";
|
|
11
|
+
//#endregion
|
|
12
|
+
exports.stylePrecedence = stylePrecedence;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/animation/stylePrecedence.ts
|
|
2
|
+
/**
|
|
3
|
+
* How React ranks every stylesheet this library renders.
|
|
4
|
+
*
|
|
5
|
+
* One value for all of them, so they sit together in a block a consumer can
|
|
6
|
+
* rank their own CSS against as a unit. It names the package rather than a
|
|
7
|
+
* position, because React treats a precedence it meets first as lower, so a
|
|
8
|
+
* position would only be right by accident of render order.
|
|
9
|
+
*/
|
|
10
|
+
const stylePrecedence = "lottie-react";
|
|
11
|
+
//#endregion
|
|
12
|
+
export { stylePrecedence };
|