@depup/lottie-react 2.4.1-depup.0 → 3.0.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 +39 -0
- package/build/animation/Lottie.d.cts +47 -0
- package/build/animation/Lottie.d.ts +47 -0
- package/build/animation/Lottie.js +37 -0
- package/build/animation/LottieDisplay.cjs +55 -0
- package/build/animation/LottieDisplay.d.cts +46 -0
- package/build/animation/LottieDisplay.d.ts +46 -0
- package/build/animation/LottieDisplay.js +53 -0
- package/build/animation/LottieInstanceContext.cjs +16 -0
- package/build/animation/LottieInstanceContext.js +17 -0
- package/build/animation/LottieLight.cjs +19 -0
- package/build/animation/LottieLight.d.cts +23 -0
- package/build/animation/LottieLight.d.ts +23 -0
- package/build/animation/LottieLight.js +17 -0
- package/build/animation/LottieRegistryContext.cjs +36 -0
- package/build/animation/LottieRegistryContext.js +35 -0
- package/build/animation/LottieSvg.cjs +20 -0
- package/build/animation/LottieSvg.d.cts +24 -0
- package/build/animation/LottieSvg.d.ts +24 -0
- package/build/animation/LottieSvg.js +18 -0
- package/build/animation/collectMarkerCrossings.cjs +38 -0
- package/build/animation/collectMarkerCrossings.js +38 -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 +461 -0
- package/build/animation/types.d.ts +461 -0
- package/build/animation/types.js +68 -0
- package/build/animation/useLottie.cjs +40 -0
- package/build/animation/useLottie.d.cts +36 -0
- package/build/animation/useLottie.d.ts +36 -0
- package/build/animation/useLottie.js +38 -0
- package/build/animation/useLottieAnimation.cjs +434 -0
- package/build/animation/useLottieAnimation.d.cts +40 -0
- package/build/animation/useLottieAnimation.d.ts +40 -0
- package/build/animation/useLottieAnimation.js +434 -0
- package/build/animation/useLottieInstance.cjs +20 -0
- package/build/animation/useLottieInstance.d.cts +14 -0
- package/build/animation/useLottieInstance.d.ts +14 -0
- package/build/animation/useLottieInstance.js +20 -0
- package/build/animation/useLottieLight.cjs +20 -0
- package/build/animation/useLottieLight.d.cts +16 -0
- package/build/animation/useLottieLight.d.ts +16 -0
- package/build/animation/useLottieLight.js +18 -0
- package/build/animation/useLottieSvg.cjs +18 -0
- package/build/animation/useLottieSvg.d.cts +14 -0
- package/build/animation/useLottieSvg.d.ts +14 -0
- package/build/animation/useLottieSvg.js +16 -0
- package/build/controls/LottieControls.cjs +180 -0
- package/build/controls/LottieControls.d.cts +58 -0
- package/build/controls/LottieControls.d.ts +58 -0
- package/build/controls/LottieControls.js +178 -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 +36 -0
- package/build/index.d.cts +19 -0
- package/build/index.d.ts +19 -80
- package/build/index.js +17 -667
- package/build/interactions/LottieInteractions.cjs +41 -0
- package/build/interactions/LottieInteractions.d.cts +37 -0
- package/build/interactions/LottieInteractions.d.ts +37 -0
- package/build/interactions/LottieInteractions.js +41 -0
- package/build/interactions/coverProgress.cjs +55 -0
- package/build/interactions/coverProgress.js +53 -0
- package/build/interactions/lottieInView.cjs +109 -0
- package/build/interactions/lottieInView.d.cts +44 -0
- package/build/interactions/lottieInView.d.ts +44 -0
- package/build/interactions/lottieInView.js +109 -0
- package/build/interactions/lottieScrollScrub.cjs +152 -0
- package/build/interactions/lottieScrollScrub.d.cts +47 -0
- package/build/interactions/lottieScrollScrub.d.ts +47 -0
- package/build/interactions/lottieScrollScrub.js +152 -0
- package/build/interactions/types.d.cts +46 -0
- package/build/interactions/types.d.ts +46 -0
- package/build/interactions/useInteractionsRunner.cjs +142 -0
- package/build/interactions/useInteractionsRunner.js +141 -0
- package/build/interactions/useLottieInteractions.cjs +21 -0
- package/build/interactions/useLottieInteractions.d.cts +19 -0
- package/build/interactions/useLottieInteractions.d.ts +19 -0
- package/build/interactions/useLottieInteractions.js +21 -0
- package/build/overlays/LottieError.cjs +74 -0
- package/build/overlays/LottieError.d.cts +40 -0
- package/build/overlays/LottieError.d.ts +40 -0
- package/build/overlays/LottieError.js +72 -0
- package/build/overlays/LottieLoading.cjs +89 -0
- package/build/overlays/LottieLoading.d.cts +45 -0
- package/build/overlays/LottieLoading.d.ts +45 -0
- package/build/overlays/LottieLoading.js +87 -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,87 @@
|
|
|
1
|
+
import { renderStyledElement } from "../animation/renderStyledElement.js";
|
|
2
|
+
import { useLottieInstance } from "../animation/useLottieInstance.js";
|
|
3
|
+
import { LottieState } from "../animation/types.js";
|
|
4
|
+
import { overlayStyles } from "./overlayStyles.js";
|
|
5
|
+
import { forwardRef } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/overlays/LottieLoading.tsx
|
|
8
|
+
/**
|
|
9
|
+
* The class the overlay carries, and the name React deduplicates its stylesheet
|
|
10
|
+
* by. One string does both, so two components can only collide in the document
|
|
11
|
+
* if they already collide in CSS.
|
|
12
|
+
*/
|
|
13
|
+
const lottieLoadingClass = "lottie-loading";
|
|
14
|
+
/** The class the built-in indicator carries. */
|
|
15
|
+
const spinnerClass = "lottie-spinner";
|
|
16
|
+
const spinKeyframes = "lottie-spin";
|
|
17
|
+
const loadingInKeyframes = "lottie-loading-in";
|
|
18
|
+
/** The two knobs a consumer's stylesheet can set. `showAfter` writes the first. */
|
|
19
|
+
const delayProperty = "--lottie-loading-delay";
|
|
20
|
+
/**
|
|
21
|
+
* The overlay's own defaults, at zero specificity and inside the library's
|
|
22
|
+
* cascade layer, so that any rule the consumer
|
|
23
|
+
* writes beats them per property.
|
|
24
|
+
*
|
|
25
|
+
* The indicator is drawn in `currentColor` and sized in `em`, so it takes the
|
|
26
|
+
* surrounding text's colour and size rather than any this library picked.
|
|
27
|
+
*
|
|
28
|
+
* Three of these are load-bearing.
|
|
29
|
+
*
|
|
30
|
+
* The wait is expressed as a fade **in** from a keyframe, never as a hidden
|
|
31
|
+
* element the animation reveals. The two look equivalent and are not: with the
|
|
32
|
+
* element hidden by its own rule, anything that switches the animation off
|
|
33
|
+
* leaves it hidden for good, while written this way the same switch shows it
|
|
34
|
+
* immediately, which is the right way round for an indicator.
|
|
35
|
+
*
|
|
36
|
+
* Both durations read a custom property with the default in the fallback, which
|
|
37
|
+
* is what lets a stylesheet change either one, at any level from the element to
|
|
38
|
+
* the document. `showAfter` sets the same property, so the prop and the CSS are
|
|
39
|
+
* one mechanism rather than two that can disagree.
|
|
40
|
+
*
|
|
41
|
+
* Under a reduced-motion preference the indicator is **slowed rather than
|
|
42
|
+
* stopped**, because the preference asks for less motion and a stationary
|
|
43
|
+
* indicator no longer says the page is working.
|
|
44
|
+
*/
|
|
45
|
+
const lottieLoadingStyles = `${overlayStyles(lottieLoadingClass)}:where(.${lottieLoadingClass}) > :where(.${spinnerClass}){box-sizing:border-box;width:2em;height:2em;border:0.15em solid currentColor;border-top-color:transparent;border-radius:50%;animation-name:${spinKeyframes};animation-duration:0.8s;animation-timing-function:linear;animation-iteration-count:infinite}:where(.${lottieLoadingClass}){animation-name:${loadingInKeyframes};animation-duration:var(--lottie-loading-fade,150ms);animation-delay:var(${delayProperty},400ms);animation-fill-mode:both}@media (prefers-reduced-motion:reduce){:where(.${lottieLoadingClass}) > :where(.${spinnerClass}){animation-duration:1.6s}}@keyframes ${spinKeyframes}{to{transform:rotate(360deg)}}@keyframes ${loadingInKeyframes}{from{opacity:0}}`;
|
|
46
|
+
/**
|
|
47
|
+
* What to show while the animation loads.
|
|
48
|
+
*
|
|
49
|
+
* Render it among the children of a component that publishes an animation, or
|
|
50
|
+
* anywhere at all with the result of `useLottie`. It covers the box it sits in
|
|
51
|
+
* while the animation is loading and renders nothing the rest of the time.
|
|
52
|
+
*
|
|
53
|
+
* ```jsx
|
|
54
|
+
* <Lottie src="/hero.json">
|
|
55
|
+
* <LottieDisplay />
|
|
56
|
+
* <LottieLoading />
|
|
57
|
+
* </Lottie>
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* It takes every attribute of the `div` it renders, and `ref` names that same
|
|
61
|
+
* element. Its own rules are all zero-specificity, so any of them can be
|
|
62
|
+
* replaced one property at a time from your own stylesheet. If your CSS lives
|
|
63
|
+
* in cascade layers, declare `@layer lottie-react;` before your own styles so
|
|
64
|
+
* the library's layer ranks below them.
|
|
65
|
+
*/
|
|
66
|
+
const LottieLoading = forwardRef(function LottieLoading({ lottie, className, children, showAfter, style, ...rest }, ref) {
|
|
67
|
+
const { state } = useLottieInstance(lottie);
|
|
68
|
+
if (state !== LottieState.loading) return null;
|
|
69
|
+
return renderStyledElement({
|
|
70
|
+
tag: "div",
|
|
71
|
+
styleClass: lottieLoadingClass,
|
|
72
|
+
styles: lottieLoadingStyles,
|
|
73
|
+
className,
|
|
74
|
+
attributes: {
|
|
75
|
+
role: "status",
|
|
76
|
+
...rest,
|
|
77
|
+
style: showAfter === void 0 ? style : {
|
|
78
|
+
[delayProperty]: `${String(showAfter)}ms`,
|
|
79
|
+
...style
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
ref,
|
|
83
|
+
children: children ?? /* @__PURE__ */ jsx("div", { className: spinnerClass })
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//#endregion
|
|
87
|
+
export { LottieLoading, lottieLoadingClass, lottieLoadingStyles };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/overlays/overlayStyles.ts
|
|
2
|
+
/**
|
|
3
|
+
* The rules that put an overlay over the animation, scoped to the class it is
|
|
4
|
+
* given.
|
|
5
|
+
*
|
|
6
|
+
* Both overlays cover the same box in the same way, and each still ships its
|
|
7
|
+
* own copy under its own name, because the class and the stylesheet's `href`
|
|
8
|
+
* are one string. Sharing the class instead would mean two names on one element
|
|
9
|
+
* and a second stylesheet to order against the first.
|
|
10
|
+
*
|
|
11
|
+
* The overlay anchors to whichever ancestor is positioned, which is the element
|
|
12
|
+
* `<Lottie>` renders around its children.
|
|
13
|
+
*
|
|
14
|
+
* Three details are not free choices. The four edges are written out rather than
|
|
15
|
+
* as `inset`, because the test environment does not expand that shorthand, so a
|
|
16
|
+
* rule written with it could never be shown to apply. And `z-index` is what
|
|
17
|
+
* decides the overlay wins against the animation whichever order the two are
|
|
18
|
+
* written in, since without it a display rendered afterwards paints on top.
|
|
19
|
+
*
|
|
20
|
+
* The third is the background. An overlay spans everything the animation's box
|
|
21
|
+
* holds, the control bar included, so with no background of its own the buttons
|
|
22
|
+
* and the seek bar read straight through the message it is there to show. It is
|
|
23
|
+
* mixed from `Canvas`, the system colour for page background, rather than from
|
|
24
|
+
* `currentColor`, because a scrim has to move towards the surface behind it and
|
|
25
|
+
* `currentColor` is the text, which would lighten the overlay on a dark page and
|
|
26
|
+
* darken it on a light one. Partly transparent, so what is underneath stays
|
|
27
|
+
* visible as context rather than disappearing.
|
|
28
|
+
*/
|
|
29
|
+
function overlayStyles(styleClass) {
|
|
30
|
+
return `:where(.${styleClass}){position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,Canvas 80%,transparent)}`;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
exports.overlayStyles = overlayStyles;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/overlays/overlayStyles.ts
|
|
2
|
+
/**
|
|
3
|
+
* The rules that put an overlay over the animation, scoped to the class it is
|
|
4
|
+
* given.
|
|
5
|
+
*
|
|
6
|
+
* Both overlays cover the same box in the same way, and each still ships its
|
|
7
|
+
* own copy under its own name, because the class and the stylesheet's `href`
|
|
8
|
+
* are one string. Sharing the class instead would mean two names on one element
|
|
9
|
+
* and a second stylesheet to order against the first.
|
|
10
|
+
*
|
|
11
|
+
* The overlay anchors to whichever ancestor is positioned, which is the element
|
|
12
|
+
* `<Lottie>` renders around its children.
|
|
13
|
+
*
|
|
14
|
+
* Three details are not free choices. The four edges are written out rather than
|
|
15
|
+
* as `inset`, because the test environment does not expand that shorthand, so a
|
|
16
|
+
* rule written with it could never be shown to apply. And `z-index` is what
|
|
17
|
+
* decides the overlay wins against the animation whichever order the two are
|
|
18
|
+
* written in, since without it a display rendered afterwards paints on top.
|
|
19
|
+
*
|
|
20
|
+
* The third is the background. An overlay spans everything the animation's box
|
|
21
|
+
* holds, the control bar included, so with no background of its own the buttons
|
|
22
|
+
* and the seek bar read straight through the message it is there to show. It is
|
|
23
|
+
* mixed from `Canvas`, the system colour for page background, rather than from
|
|
24
|
+
* `currentColor`, because a scrim has to move towards the surface behind it and
|
|
25
|
+
* `currentColor` is the text, which would lighten the overlay on a dark page and
|
|
26
|
+
* darken it on a light one. Partly transparent, so what is underneath stays
|
|
27
|
+
* visible as context rather than disappearing.
|
|
28
|
+
*/
|
|
29
|
+
function overlayStyles(styleClass) {
|
|
30
|
+
return `:where(.${styleClass}){position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,Canvas 80%,transparent)}`;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { overlayStyles };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/utils/SubscriptionManager.ts
|
|
2
|
+
/**
|
|
3
|
+
* A typed event emitter, small enough to avoid a dependency and precise enough
|
|
4
|
+
* that a wrong payload is a compile error rather than a runtime surprise.
|
|
5
|
+
*
|
|
6
|
+
* The map it is given must be declared as a `type` rather than an `interface`,
|
|
7
|
+
* because an interface has no implicit index signature and cannot satisfy the
|
|
8
|
+
* constraint above.
|
|
9
|
+
*
|
|
10
|
+
* Both methods are fields rather than prototype methods so they can be handed
|
|
11
|
+
* out on their own, which is how `subscribe` reaches a consumer.
|
|
12
|
+
*/
|
|
13
|
+
var SubscriptionManager = class {
|
|
14
|
+
listeners = {};
|
|
15
|
+
/** Registers a handler and returns the function that removes it. */
|
|
16
|
+
subscribe = (type, handler) => {
|
|
17
|
+
let handlers = this.listeners[type];
|
|
18
|
+
if (!handlers) {
|
|
19
|
+
handlers = /* @__PURE__ */ new Set();
|
|
20
|
+
this.listeners[type] = handlers;
|
|
21
|
+
}
|
|
22
|
+
handlers.add(handler);
|
|
23
|
+
return () => {
|
|
24
|
+
this.listeners[type]?.delete(handler);
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Calls every handler registered for an event.
|
|
29
|
+
*
|
|
30
|
+
* The set is walked in place rather than copied, because `frame` is notified
|
|
31
|
+
* once per rendered frame and a copy per notification would allocate at that
|
|
32
|
+
* rate. A handler that removes itself while being called is safe; one that
|
|
33
|
+
* subscribes during its own notification may be called in the same pass.
|
|
34
|
+
*/
|
|
35
|
+
notify = (type, ...args) => {
|
|
36
|
+
this.listeners[type]?.forEach((handler) => {
|
|
37
|
+
handler(...args);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
exports.SubscriptionManager = SubscriptionManager;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/utils/SubscriptionManager.d.ts
|
|
2
|
+
/** The shape a subscription map has to have: a handler per event name. */
|
|
3
|
+
type HandlerMap = Record<string, (...args: never[]) => void>;
|
|
4
|
+
/**
|
|
5
|
+
* A typed event emitter, small enough to avoid a dependency and precise enough
|
|
6
|
+
* that a wrong payload is a compile error rather than a runtime surprise.
|
|
7
|
+
*
|
|
8
|
+
* The map it is given must be declared as a `type` rather than an `interface`,
|
|
9
|
+
* because an interface has no implicit index signature and cannot satisfy the
|
|
10
|
+
* constraint above.
|
|
11
|
+
*
|
|
12
|
+
* Both methods are fields rather than prototype methods so they can be handed
|
|
13
|
+
* out on their own, which is how `subscribe` reaches a consumer.
|
|
14
|
+
*/
|
|
15
|
+
declare class SubscriptionManager<Subscriptions extends HandlerMap> {
|
|
16
|
+
private readonly listeners;
|
|
17
|
+
/** Registers a handler and returns the function that removes it. */
|
|
18
|
+
subscribe: <Type extends keyof Subscriptions>(type: Type, handler: Subscriptions[Type]) => (() => void);
|
|
19
|
+
/**
|
|
20
|
+
* Calls every handler registered for an event.
|
|
21
|
+
*
|
|
22
|
+
* The set is walked in place rather than copied, because `frame` is notified
|
|
23
|
+
* once per rendered frame and a copy per notification would allocate at that
|
|
24
|
+
* rate. A handler that removes itself while being called is safe; one that
|
|
25
|
+
* subscribes during its own notification may be called in the same pass.
|
|
26
|
+
*/
|
|
27
|
+
notify: <Type extends keyof Subscriptions>(type: Type, ...args: Parameters<Subscriptions[Type]>) => void;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { SubscriptionManager };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/utils/SubscriptionManager.d.ts
|
|
2
|
+
/** The shape a subscription map has to have: a handler per event name. */
|
|
3
|
+
type HandlerMap = Record<string, (...args: never[]) => void>;
|
|
4
|
+
/**
|
|
5
|
+
* A typed event emitter, small enough to avoid a dependency and precise enough
|
|
6
|
+
* that a wrong payload is a compile error rather than a runtime surprise.
|
|
7
|
+
*
|
|
8
|
+
* The map it is given must be declared as a `type` rather than an `interface`,
|
|
9
|
+
* because an interface has no implicit index signature and cannot satisfy the
|
|
10
|
+
* constraint above.
|
|
11
|
+
*
|
|
12
|
+
* Both methods are fields rather than prototype methods so they can be handed
|
|
13
|
+
* out on their own, which is how `subscribe` reaches a consumer.
|
|
14
|
+
*/
|
|
15
|
+
declare class SubscriptionManager<Subscriptions extends HandlerMap> {
|
|
16
|
+
private readonly listeners;
|
|
17
|
+
/** Registers a handler and returns the function that removes it. */
|
|
18
|
+
subscribe: <Type extends keyof Subscriptions>(type: Type, handler: Subscriptions[Type]) => (() => void);
|
|
19
|
+
/**
|
|
20
|
+
* Calls every handler registered for an event.
|
|
21
|
+
*
|
|
22
|
+
* The set is walked in place rather than copied, because `frame` is notified
|
|
23
|
+
* once per rendered frame and a copy per notification would allocate at that
|
|
24
|
+
* rate. A handler that removes itself while being called is safe; one that
|
|
25
|
+
* subscribes during its own notification may be called in the same pass.
|
|
26
|
+
*/
|
|
27
|
+
notify: <Type extends keyof Subscriptions>(type: Type, ...args: Parameters<Subscriptions[Type]>) => void;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { SubscriptionManager };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/utils/SubscriptionManager.ts
|
|
2
|
+
/**
|
|
3
|
+
* A typed event emitter, small enough to avoid a dependency and precise enough
|
|
4
|
+
* that a wrong payload is a compile error rather than a runtime surprise.
|
|
5
|
+
*
|
|
6
|
+
* The map it is given must be declared as a `type` rather than an `interface`,
|
|
7
|
+
* because an interface has no implicit index signature and cannot satisfy the
|
|
8
|
+
* constraint above.
|
|
9
|
+
*
|
|
10
|
+
* Both methods are fields rather than prototype methods so they can be handed
|
|
11
|
+
* out on their own, which is how `subscribe` reaches a consumer.
|
|
12
|
+
*/
|
|
13
|
+
var SubscriptionManager = class {
|
|
14
|
+
listeners = {};
|
|
15
|
+
/** Registers a handler and returns the function that removes it. */
|
|
16
|
+
subscribe = (type, handler) => {
|
|
17
|
+
let handlers = this.listeners[type];
|
|
18
|
+
if (!handlers) {
|
|
19
|
+
handlers = /* @__PURE__ */ new Set();
|
|
20
|
+
this.listeners[type] = handlers;
|
|
21
|
+
}
|
|
22
|
+
handlers.add(handler);
|
|
23
|
+
return () => {
|
|
24
|
+
this.listeners[type]?.delete(handler);
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Calls every handler registered for an event.
|
|
29
|
+
*
|
|
30
|
+
* The set is walked in place rather than copied, because `frame` is notified
|
|
31
|
+
* once per rendered frame and a copy per notification would allocate at that
|
|
32
|
+
* rate. A handler that removes itself while being called is safe; one that
|
|
33
|
+
* subscribes during its own notification may be called in the same pass.
|
|
34
|
+
*/
|
|
35
|
+
notify = (type, ...args) => {
|
|
36
|
+
this.listeners[type]?.forEach((handler) => {
|
|
37
|
+
handler(...args);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { SubscriptionManager };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/utils/createLogger.ts
|
|
2
|
+
const PREFIX = "[lottie-react]";
|
|
3
|
+
const noop = () => void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Builds a logger that says nothing unless the caller asked for it.
|
|
6
|
+
*
|
|
7
|
+
* This is tracing rather than diagnosis, and it deliberately reports only what
|
|
8
|
+
* no subscription can: the configuration actually handed to the engine, and the
|
|
9
|
+
* teardown. Anything a consumer can already subscribe to is not repeated here,
|
|
10
|
+
* and nothing per frame is ever written.
|
|
11
|
+
*
|
|
12
|
+
* There is no `warn`, because everything worth warning about reaches the
|
|
13
|
+
* consumer through the `error` subscription with the reason attached.
|
|
14
|
+
*
|
|
15
|
+
* Unlike a development-only warning this survives into a production build on
|
|
16
|
+
* purpose, so a problem that appears only in one can still be looked at.
|
|
17
|
+
*/
|
|
18
|
+
function createLogger(debug) {
|
|
19
|
+
if (!debug) return { log: noop };
|
|
20
|
+
return { log: (message, ...rest) => {
|
|
21
|
+
console.log(`${PREFIX} ${message}`, ...rest);
|
|
22
|
+
} };
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
exports.createLogger = createLogger;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/utils/createLogger.ts
|
|
2
|
+
const PREFIX = "[lottie-react]";
|
|
3
|
+
const noop = () => void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Builds a logger that says nothing unless the caller asked for it.
|
|
6
|
+
*
|
|
7
|
+
* This is tracing rather than diagnosis, and it deliberately reports only what
|
|
8
|
+
* no subscription can: the configuration actually handed to the engine, and the
|
|
9
|
+
* teardown. Anything a consumer can already subscribe to is not repeated here,
|
|
10
|
+
* and nothing per frame is ever written.
|
|
11
|
+
*
|
|
12
|
+
* There is no `warn`, because everything worth warning about reaches the
|
|
13
|
+
* consumer through the `error` subscription with the reason attached.
|
|
14
|
+
*
|
|
15
|
+
* Unlike a development-only warning this survives into a production build on
|
|
16
|
+
* purpose, so a problem that appears only in one can still be looked at.
|
|
17
|
+
*/
|
|
18
|
+
function createLogger(debug) {
|
|
19
|
+
if (!debug) return { log: noop };
|
|
20
|
+
return { log: (message, ...rest) => {
|
|
21
|
+
console.log(`${PREFIX} ${message}`, ...rest);
|
|
22
|
+
} };
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { createLogger };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/utils/isSameJson.ts
|
|
2
|
+
/** Narrows without a cast: any array can be read as a list of unknowns. */
|
|
3
|
+
function isJsonArray(value) {
|
|
4
|
+
return Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
/** Narrows without a cast: any plain object can be read as unknown-valued. */
|
|
7
|
+
function isJsonObject(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Compares two JSON values by content: objects, arrays, and the primitives JSON
|
|
12
|
+
* can hold. Key order does not matter, and `NaN` equals itself.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately narrower than a general deep-equality function. It has no
|
|
15
|
+
* understanding of `Map`, `Set`, `Date`, `RegExp` or typed arrays, and treating
|
|
16
|
+
* one as a plain object would report two different values as the same. Animation
|
|
17
|
+
* data is parsed JSON, so none of them can appear in it.
|
|
18
|
+
*
|
|
19
|
+
* It recurses, so a value that refers to itself overflows the stack rather than
|
|
20
|
+
* returning. JSON cannot express one.
|
|
21
|
+
*/
|
|
22
|
+
function isSameJson(a, b) {
|
|
23
|
+
if (Object.is(a, b)) return true;
|
|
24
|
+
if (isJsonArray(a)) {
|
|
25
|
+
if (!isJsonArray(b) || a.length !== b.length) return false;
|
|
26
|
+
return a.every((item, index) => isSameJson(item, b[index]));
|
|
27
|
+
}
|
|
28
|
+
if (isJsonObject(a)) {
|
|
29
|
+
if (!isJsonObject(b)) return false;
|
|
30
|
+
const keys = Object.keys(a);
|
|
31
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
32
|
+
return keys.every((key) => Object.hasOwn(b, key) && isSameJson(a[key], b[key]));
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
exports.isSameJson = isSameJson;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/utils/isSameJson.ts
|
|
2
|
+
/** Narrows without a cast: any array can be read as a list of unknowns. */
|
|
3
|
+
function isJsonArray(value) {
|
|
4
|
+
return Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
/** Narrows without a cast: any plain object can be read as unknown-valued. */
|
|
7
|
+
function isJsonObject(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Compares two JSON values by content: objects, arrays, and the primitives JSON
|
|
12
|
+
* can hold. Key order does not matter, and `NaN` equals itself.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately narrower than a general deep-equality function. It has no
|
|
15
|
+
* understanding of `Map`, `Set`, `Date`, `RegExp` or typed arrays, and treating
|
|
16
|
+
* one as a plain object would report two different values as the same. Animation
|
|
17
|
+
* data is parsed JSON, so none of them can appear in it.
|
|
18
|
+
*
|
|
19
|
+
* It recurses, so a value that refers to itself overflows the stack rather than
|
|
20
|
+
* returning. JSON cannot express one.
|
|
21
|
+
*/
|
|
22
|
+
function isSameJson(a, b) {
|
|
23
|
+
if (Object.is(a, b)) return true;
|
|
24
|
+
if (isJsonArray(a)) {
|
|
25
|
+
if (!isJsonArray(b) || a.length !== b.length) return false;
|
|
26
|
+
return a.every((item, index) => isSameJson(item, b[index]));
|
|
27
|
+
}
|
|
28
|
+
if (isJsonObject(a)) {
|
|
29
|
+
if (!isJsonObject(b)) return false;
|
|
30
|
+
const keys = Object.keys(a);
|
|
31
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
32
|
+
return keys.every((key) => Object.hasOwn(b, key) && isSameJson(a[key], b[key]));
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { isSameJson };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/utils/mergeRefs.ts
|
|
2
|
+
/**
|
|
3
|
+
* Sends one element to several refs.
|
|
4
|
+
*
|
|
5
|
+
* An element has a single `ref` slot, so any element that both this library and
|
|
6
|
+
* the person using it want a handle on needs one callback that feeds both.
|
|
7
|
+
*
|
|
8
|
+
* Teardown is the part that is not boilerplate, because the two supported React
|
|
9
|
+
* versions differ. React 19 treats a function returned from a ref callback as
|
|
10
|
+
* its cleanup and then never calls that ref with `null`, while React 18
|
|
11
|
+
* discards the return and calls with `null`. So each ref gets its own cleanup
|
|
12
|
+
* where it returned one and a `null` call where it did not, and the combined
|
|
13
|
+
* teardown is returned for React 19 to run. React 18 ignores it and calls this
|
|
14
|
+
* callback with `null` instead, which does the same work.
|
|
15
|
+
*/
|
|
16
|
+
function mergeRefs(...refs) {
|
|
17
|
+
return (node) => {
|
|
18
|
+
const teardowns = refs.map((ref) => {
|
|
19
|
+
if (typeof ref === "function") {
|
|
20
|
+
const cleanup = ref(node);
|
|
21
|
+
return typeof cleanup === "function" ? cleanup : () => ref(null);
|
|
22
|
+
}
|
|
23
|
+
if (ref) {
|
|
24
|
+
ref.current = node;
|
|
25
|
+
return () => {
|
|
26
|
+
ref.current = null;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return () => void 0;
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
for (const teardown of teardowns) teardown();
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
exports.mergeRefs = mergeRefs;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/utils/mergeRefs.ts
|
|
2
|
+
/**
|
|
3
|
+
* Sends one element to several refs.
|
|
4
|
+
*
|
|
5
|
+
* An element has a single `ref` slot, so any element that both this library and
|
|
6
|
+
* the person using it want a handle on needs one callback that feeds both.
|
|
7
|
+
*
|
|
8
|
+
* Teardown is the part that is not boilerplate, because the two supported React
|
|
9
|
+
* versions differ. React 19 treats a function returned from a ref callback as
|
|
10
|
+
* its cleanup and then never calls that ref with `null`, while React 18
|
|
11
|
+
* discards the return and calls with `null`. So each ref gets its own cleanup
|
|
12
|
+
* where it returned one and a `null` call where it did not, and the combined
|
|
13
|
+
* teardown is returned for React 19 to run. React 18 ignores it and calls this
|
|
14
|
+
* callback with `null` instead, which does the same work.
|
|
15
|
+
*/
|
|
16
|
+
function mergeRefs(...refs) {
|
|
17
|
+
return (node) => {
|
|
18
|
+
const teardowns = refs.map((ref) => {
|
|
19
|
+
if (typeof ref === "function") {
|
|
20
|
+
const cleanup = ref(node);
|
|
21
|
+
return typeof cleanup === "function" ? cleanup : () => ref(null);
|
|
22
|
+
}
|
|
23
|
+
if (ref) {
|
|
24
|
+
ref.current = node;
|
|
25
|
+
return () => {
|
|
26
|
+
ref.current = null;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return () => void 0;
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
for (const teardown of teardowns) teardown();
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { mergeRefs };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const require_isSameJson = require("./isSameJson.cjs");
|
|
2
|
+
let react = require("react");
|
|
3
|
+
//#region src/utils/useStableValue.ts
|
|
4
|
+
/**
|
|
5
|
+
* Keeps a value stable until its **content** changes, so it can be depended on.
|
|
6
|
+
*
|
|
7
|
+
* A value written inline at a call site is a new value on every render, and an
|
|
8
|
+
* effect that depends on it therefore re-runs on every render. Where that
|
|
9
|
+
* effect also sets state, the component re-renders, the value is built again,
|
|
10
|
+
* and it never settles.
|
|
11
|
+
*
|
|
12
|
+
* The reference is compared first, so a caller who passes something they
|
|
13
|
+
* already hold pays nothing at all. Only a caller who rebuilds an equal value
|
|
14
|
+
* pays for the comparison, which is exactly the case being rescued.
|
|
15
|
+
*
|
|
16
|
+
* Assigning state during render is React's own answer to adjusting state when a
|
|
17
|
+
* prop changes: the component renders again immediately, before anything is
|
|
18
|
+
* committed, so the intermediate result is never seen.
|
|
19
|
+
*/
|
|
20
|
+
function useStableValue(value) {
|
|
21
|
+
const [stable, setStable] = (0, react.useState)(value);
|
|
22
|
+
if (stable !== value && !require_isSameJson.isSameJson(stable, value)) setStable(value);
|
|
23
|
+
return stable;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
exports.useStableValue = useStableValue;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isSameJson } from "./isSameJson.js";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
//#region src/utils/useStableValue.ts
|
|
4
|
+
/**
|
|
5
|
+
* Keeps a value stable until its **content** changes, so it can be depended on.
|
|
6
|
+
*
|
|
7
|
+
* A value written inline at a call site is a new value on every render, and an
|
|
8
|
+
* effect that depends on it therefore re-runs on every render. Where that
|
|
9
|
+
* effect also sets state, the component re-renders, the value is built again,
|
|
10
|
+
* and it never settles.
|
|
11
|
+
*
|
|
12
|
+
* The reference is compared first, so a caller who passes something they
|
|
13
|
+
* already hold pays nothing at all. Only a caller who rebuilds an equal value
|
|
14
|
+
* pays for the comparison, which is exactly the case being rescued.
|
|
15
|
+
*
|
|
16
|
+
* Assigning state during render is React's own answer to adjusting state when a
|
|
17
|
+
* prop changes: the component renders again immediately, before anything is
|
|
18
|
+
* committed, so the intermediate result is never seen.
|
|
19
|
+
*/
|
|
20
|
+
function useStableValue(value) {
|
|
21
|
+
const [stable, setStable] = useState(value);
|
|
22
|
+
if (stable !== value && !isSameJson(stable, value)) setStable(value);
|
|
23
|
+
return stable;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { useStableValue };
|
package/changes.json
CHANGED