@depup/lottie-react 3.1.0-depup.0 → 3.1.2-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/README.md CHANGED
@@ -13,8 +13,8 @@ npm install @depup/lottie-react
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [lottie-react](https://www.npmjs.com/package/lottie-react) @ 3.1.0 |
17
- | Processed | 2026-08-17 |
16
+ | Original | [lottie-react](https://www.npmjs.com/package/lottie-react) @ 3.1.2 |
17
+ | Processed | 2026-09-08 |
18
18
  | Smoke test | passed |
19
19
  | Deps updated | 0 |
20
20
 
@@ -15,6 +15,30 @@ function toError(cause) {
15
15
  return cause instanceof Error ? cause : new Error(String(cause));
16
16
  }
17
17
  /**
18
+ * Takes back the two hidden spans lottie-web appends to `document.body` to
19
+ * measure a font with. The engine appends a pair per font and removes them
20
+ * only on the path where it watches a downloaded font arrive, so a font with
21
+ * nothing to download keeps its pair and every load adds another.
22
+ *
23
+ * Only a font already marked loaded may have its pair taken. A pending one is
24
+ * still being measured through those spans, and a span out of the document
25
+ * measures zero, which the engine reads as the font arriving and then fails on
26
+ * a parent that is no longer there. A download that never arrives therefore
27
+ * keeps its pair, which stays the engine's to collect.
28
+ *
29
+ * `renderer` is untyped upstream, so the path to the fonts is checked rather
30
+ * than trusted.
31
+ */
32
+ function removeMeasurementSpans(item) {
33
+ const measured = item.renderer?.globalData?.fontManager?.fonts;
34
+ const fonts = Array.isArray(measured) ? measured : [];
35
+ for (const font of fonts) {
36
+ if (!font.loaded) continue;
37
+ font.sansCase?.parent.remove();
38
+ font.monoCase?.parent.remove();
39
+ }
40
+ }
41
+ /**
18
42
  * Everything the animation does, with no opinion about how it is rendered.
19
43
  *
20
44
  * It takes the engine as an argument so the same logic serves every build.
@@ -309,6 +333,7 @@ function useLottieAnimation(engine, options) {
309
333
  fail(toError(cause));
310
334
  return;
311
335
  }
336
+ removeMeasurementSpans(item);
312
337
  itemRef.current = item;
313
338
  setAnimationItem(item);
314
339
  item.setSpeed(valuesRef.current.speed);
@@ -373,6 +398,7 @@ function useLottieAnimation(engine, options) {
373
398
  for (const [name, handler] of listeners) item.addEventListener(name, handler);
374
399
  return () => {
375
400
  for (const [name, handler] of listeners) item.removeEventListener(name, handler);
401
+ removeMeasurementSpans(item);
376
402
  item.destroy();
377
403
  itemRef.current = null;
378
404
  setAnimationItem(null);
@@ -15,6 +15,30 @@ function toError(cause) {
15
15
  return cause instanceof Error ? cause : new Error(String(cause));
16
16
  }
17
17
  /**
18
+ * Takes back the two hidden spans lottie-web appends to `document.body` to
19
+ * measure a font with. The engine appends a pair per font and removes them
20
+ * only on the path where it watches a downloaded font arrive, so a font with
21
+ * nothing to download keeps its pair and every load adds another.
22
+ *
23
+ * Only a font already marked loaded may have its pair taken. A pending one is
24
+ * still being measured through those spans, and a span out of the document
25
+ * measures zero, which the engine reads as the font arriving and then fails on
26
+ * a parent that is no longer there. A download that never arrives therefore
27
+ * keeps its pair, which stays the engine's to collect.
28
+ *
29
+ * `renderer` is untyped upstream, so the path to the fonts is checked rather
30
+ * than trusted.
31
+ */
32
+ function removeMeasurementSpans(item) {
33
+ const measured = item.renderer?.globalData?.fontManager?.fonts;
34
+ const fonts = Array.isArray(measured) ? measured : [];
35
+ for (const font of fonts) {
36
+ if (!font.loaded) continue;
37
+ font.sansCase?.parent.remove();
38
+ font.monoCase?.parent.remove();
39
+ }
40
+ }
41
+ /**
18
42
  * Everything the animation does, with no opinion about how it is rendered.
19
43
  *
20
44
  * It takes the engine as an argument so the same logic serves every build.
@@ -309,6 +333,7 @@ function useLottieAnimation(engine, options) {
309
333
  fail(toError(cause));
310
334
  return;
311
335
  }
336
+ removeMeasurementSpans(item);
312
337
  itemRef.current = item;
313
338
  setAnimationItem(item);
314
339
  item.setSpeed(valuesRef.current.speed);
@@ -373,6 +398,7 @@ function useLottieAnimation(engine, options) {
373
398
  for (const [name, handler] of listeners) item.addEventListener(name, handler);
374
399
  return () => {
375
400
  for (const [name, handler] of listeners) item.removeEventListener(name, handler);
401
+ removeMeasurementSpans(item);
376
402
  item.destroy();
377
403
  itemRef.current = null;
378
404
  setAnimationItem(null);
@@ -5,20 +5,25 @@
5
5
  * An element has a single `ref` slot, so any element that both this library and
6
6
  * the person using it want a handle on needs one callback that feeds both.
7
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.
8
+ * Teardown is where the two supported React versions differ. React 19 treats a
9
+ * function returned from a ref callback as its cleanup and never calls that ref
10
+ * with `null`; React 18 always calls with `null` and, in development, reports a
11
+ * returned function. So a combined teardown is returned only when a ref returned
12
+ * a cleanup of its own, since that ref must be torn down by it rather than by a
13
+ * `null` call. Otherwise nothing is returned and both versions call this
14
+ * callback with `null`, which clears every ref.
15
15
  */
16
16
  function mergeRefs(...refs) {
17
17
  return (node) => {
18
+ let returned = false;
18
19
  const teardowns = refs.map((ref) => {
19
20
  if (typeof ref === "function") {
20
21
  const cleanup = ref(node);
21
- return typeof cleanup === "function" ? cleanup : () => ref(null);
22
+ if (typeof cleanup === "function") {
23
+ returned = true;
24
+ return cleanup;
25
+ }
26
+ return () => ref(null);
22
27
  }
23
28
  if (ref) {
24
29
  ref.current = node;
@@ -28,6 +33,7 @@ function mergeRefs(...refs) {
28
33
  }
29
34
  return () => void 0;
30
35
  });
36
+ if (!returned) return;
31
37
  return () => {
32
38
  for (const teardown of teardowns) teardown();
33
39
  };
@@ -5,20 +5,25 @@
5
5
  * An element has a single `ref` slot, so any element that both this library and
6
6
  * the person using it want a handle on needs one callback that feeds both.
7
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.
8
+ * Teardown is where the two supported React versions differ. React 19 treats a
9
+ * function returned from a ref callback as its cleanup and never calls that ref
10
+ * with `null`; React 18 always calls with `null` and, in development, reports a
11
+ * returned function. So a combined teardown is returned only when a ref returned
12
+ * a cleanup of its own, since that ref must be torn down by it rather than by a
13
+ * `null` call. Otherwise nothing is returned and both versions call this
14
+ * callback with `null`, which clears every ref.
15
15
  */
16
16
  function mergeRefs(...refs) {
17
17
  return (node) => {
18
+ let returned = false;
18
19
  const teardowns = refs.map((ref) => {
19
20
  if (typeof ref === "function") {
20
21
  const cleanup = ref(node);
21
- return typeof cleanup === "function" ? cleanup : () => ref(null);
22
+ if (typeof cleanup === "function") {
23
+ returned = true;
24
+ return cleanup;
25
+ }
26
+ return () => ref(null);
22
27
  }
23
28
  if (ref) {
24
29
  ref.current = node;
@@ -28,6 +33,7 @@ function mergeRefs(...refs) {
28
33
  }
29
34
  return () => void 0;
30
35
  });
36
+ if (!returned) return;
31
37
  return () => {
32
38
  for (const teardown of teardowns) teardown();
33
39
  };
package/changes.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "bumped": {},
3
- "timestamp": "2026-08-17T00:14:39.892Z",
3
+ "timestamp": "2026-09-08T00:33:31.334Z",
4
4
  "totalUpdated": 0
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depup/lottie-react",
3
- "version": "3.1.0-depup.0",
3
+ "version": "3.1.2-depup.0",
4
4
  "description": "Lottie animations in React: one component for the easy path, the whole engine when you need control. (with updated dependencies)",
5
5
  "keywords": [
6
6
  "lottie-react",
@@ -74,7 +74,7 @@
74
74
  "lottie-web"
75
75
  ],
76
76
  "gzip": true,
77
- "limit": "4.9 KB"
77
+ "limit": "5 KB"
78
78
  },
79
79
  {
80
80
  "name": "import { useLottie }",
@@ -86,7 +86,7 @@
86
86
  "lottie-web"
87
87
  ],
88
88
  "gzip": true,
89
- "limit": "3.85 KB"
89
+ "limit": "3.95 KB"
90
90
  },
91
91
  {
92
92
  "name": "import { Lottie, LottieControls }",
@@ -110,7 +110,7 @@
110
110
  "lottie-web/build/player/lottie_svg.js"
111
111
  ],
112
112
  "gzip": true,
113
- "limit": "4.9 KB"
113
+ "limit": "5 KB"
114
114
  },
115
115
  {
116
116
  "name": "import { LottieLight, useLottieLight }, only its own engine external",
@@ -122,7 +122,7 @@
122
122
  "lottie-web/build/player/lottie_light.js"
123
123
  ],
124
124
  "gzip": true,
125
- "limit": "4.9 KB"
125
+ "limit": "5 KB"
126
126
  }
127
127
  ],
128
128
  "scripts": {
@@ -174,8 +174,8 @@
174
174
  "changes": {},
175
175
  "depsUpdated": 0,
176
176
  "originalPackage": "lottie-react",
177
- "originalVersion": "3.1.0",
178
- "processedAt": "2026-08-17T00:14:54.679Z",
177
+ "originalVersion": "3.1.2",
178
+ "processedAt": "2026-09-08T00:33:40.802Z",
179
179
  "smokeTest": "passed"
180
180
  }
181
181
  }