@tamagui/animations-css 1.0.1-beta.148 → 1.0.1-beta.151

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.
@@ -21,6 +21,7 @@ __export(createAnimations_exports, {
21
21
  createAnimations: () => createAnimations
22
22
  });
23
23
  module.exports = __toCommonJS(createAnimations_exports);
24
+ var import_animate_presence = require("@tamagui/animate-presence");
24
25
  var import_core = require("@tamagui/core");
25
26
  var import_react = require("react");
26
27
  function createAnimations(animations) {
@@ -50,21 +51,45 @@ function createAnimations(animations) {
50
51
  useAnimatedNumberStyle(val, getStyle) {
51
52
  return getStyle(val.getValue());
52
53
  },
53
- useAnimations: (props, { getStyle }) => {
54
+ useAnimations: (props, { getStyle, state, hostRef }) => {
55
+ const [isPresent, sendExitComplete] = (0, import_animate_presence.usePresence)();
56
+ const presence = (0, import_react.useContext)(import_animate_presence.PresenceContext);
57
+ const isExiting = isPresent === false;
58
+ const isEntering = !state.mounted;
54
59
  const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation;
55
60
  const animation = animations[animationKey];
56
61
  if (!animation) {
57
- throw new Error(`no animation found: ${props.animation}`);
62
+ return null;
58
63
  }
59
64
  const keys = props.animateOnly ? props.animateOnly.join(" ") : "all";
65
+ (0, import_core.useIsomorphicLayoutEffect)(() => {
66
+ if (!sendExitComplete || !isExiting || !hostRef.current)
67
+ return;
68
+ const node = hostRef.current;
69
+ const onFinishAnimation = () => {
70
+ sendExitComplete == null ? void 0 : sendExitComplete();
71
+ };
72
+ node.addEventListener("transitionend", onFinishAnimation);
73
+ node.addEventListener("transitioncancel", onFinishAnimation);
74
+ return () => {
75
+ node.removeEventListener("transitionend", onFinishAnimation);
76
+ node.addEventListener("transitioncancel", onFinishAnimation);
77
+ };
78
+ }, [sendExitComplete, isExiting]);
60
79
  const style = {
61
- style: {
62
- transition: `${keys} ${animation}`,
63
- ...getStyle()
64
- }
80
+ transition: `${keys} ${animation}`,
81
+ ...getStyle({
82
+ isEntering,
83
+ isExiting,
84
+ exitVariant: presence == null ? void 0 : presence.exitVariant,
85
+ enterVariant: presence == null ? void 0 : presence.enterVariant
86
+ })
65
87
  };
88
+ if (process.env.NODE_ENV === "development" && props["debug"]) {
89
+ console.log("CSS animation", style, { isEntering, isExiting });
90
+ }
66
91
  return (0, import_react.useMemo)(() => {
67
- return style;
92
+ return { style };
68
93
  }, [JSON.stringify(style)]);
69
94
  }
70
95
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/createAnimations.tsx"],
4
- "sourcesContent": ["import { AnimationDriver, Stack, Text } from '@tamagui/core'\nimport { useMemo, useRef } from 'react'\n\nexport function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {\n return {\n avoidClasses: false,\n View: Stack,\n Text: Text,\n animations,\n\n useAnimatedNumber(initial) {\n const val = useRef(initial)\n return {\n getInstance() {\n return val\n },\n getValue() {\n return val.current\n },\n setValue(next) {\n val.current = next\n },\n stop() {},\n }\n },\n\n useAnimatedNumberReaction(val, reaction) {\n // TODO use event listeners, would need access to the corresponsing node...\n },\n\n useAnimatedNumberStyle(val, getStyle) {\n return getStyle(val.getValue())\n },\n\n useAnimations: (props, { getStyle }) => {\n const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation\n const animation = animations[animationKey as any]\n if (!animation) {\n throw new Error(`no animation found: ${props.animation}`)\n }\n\n const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'\n\n const style = {\n style: {\n transition: `${keys} ${animation}`,\n ...getStyle(),\n },\n }\n\n return useMemo(() => {\n return style\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(style)])\n },\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA6C;AAC7C,mBAAgC;AAEzB,0BAA4C,YAAmC;AACpF,SAAO;AAAA,IACL,cAAc;AAAA,IACd,MAAM;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IAEA,kBAAkB,SAAS;AACzB,YAAM,MAAM,yBAAO,OAAO;AAC1B,aAAO;AAAA,QACL,cAAc;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,WAAW;AACT,iBAAO,IAAI;AAAA,QACb;AAAA,QACA,SAAS,MAAM;AACb,cAAI,UAAU;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,0BAA0B,KAAK,UAAU;AAAA,IAEzC;AAAA,IAEA,uBAAuB,KAAK,UAAU;AACpC,aAAO,SAAS,IAAI,SAAS,CAAC;AAAA,IAChC;AAAA,IAEA,eAAe,CAAC,OAAO,EAAE,eAAe;AACtC,YAAM,eAAe,MAAM,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM;AACjF,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,uBAAuB,MAAM,WAAW;AAAA,MAC1D;AAEA,YAAM,OAAO,MAAM,cAAc,MAAM,YAAY,KAAK,GAAG,IAAI;AAE/D,YAAM,QAAQ;AAAA,QACZ,OAAO;AAAA,UACL,YAAY,GAAG,QAAQ;AAAA,UACvB,GAAG,SAAS;AAAA,QACd;AAAA,MACF;AAEA,aAAO,0BAAQ,MAAM;AACnB,eAAO;AAAA,MAET,GAAG,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IAC5B;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { PresenceContext, usePresence } from '@tamagui/animate-presence'\nimport { AnimationDriver, Stack, Text, useIsomorphicLayoutEffect } from '@tamagui/core'\nimport { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react'\n\nexport function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {\n return {\n avoidClasses: false,\n View: Stack,\n Text: Text,\n animations,\n\n useAnimatedNumber(initial) {\n const val = useRef(initial)\n return {\n getInstance() {\n return val\n },\n getValue() {\n return val.current\n },\n setValue(next) {\n val.current = next\n },\n stop() {},\n }\n },\n\n useAnimatedNumberReaction(val, reaction) {\n // TODO use event listeners, would need access to the corresponsing node...\n },\n\n useAnimatedNumberStyle(val, getStyle) {\n return getStyle(val.getValue())\n },\n\n useAnimations: (props, { getStyle, state, hostRef }) => {\n const [isPresent, sendExitComplete] = usePresence()\n const presence = useContext(PresenceContext)\n const isExiting = isPresent === false\n const isEntering = !state.mounted\n const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation\n const animation = animations[animationKey as any]\n if (!animation) {\n return null\n }\n\n const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'\n\n useIsomorphicLayoutEffect(() => {\n if (!sendExitComplete || !isExiting || !hostRef.current) return\n const node = hostRef.current as HTMLElement\n const onFinishAnimation = () => {\n sendExitComplete?.()\n }\n node.addEventListener('transitionend', onFinishAnimation)\n node.addEventListener('transitioncancel', onFinishAnimation)\n return () => {\n node.removeEventListener('transitionend', onFinishAnimation)\n node.addEventListener('transitioncancel', onFinishAnimation)\n }\n }, [sendExitComplete, isExiting])\n\n const style = {\n transition: `${keys} ${animation}`,\n ...getStyle({\n isEntering,\n isExiting,\n exitVariant: presence?.exitVariant,\n enterVariant: presence?.enterVariant,\n }),\n }\n\n if (process.env.NODE_ENV === 'development' && props['debug']) {\n console.log('CSS animation', style, { isEntering, isExiting })\n }\n\n return useMemo(() => {\n return { style }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(style)])\n },\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAA6C;AAC7C,kBAAwE;AACxE,mBAAwE;AAEjE,0BAA4C,YAAmC;AACpF,SAAO;AAAA,IACL,cAAc;AAAA,IACd,MAAM;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IAEA,kBAAkB,SAAS;AACzB,YAAM,MAAM,yBAAO,OAAO;AAC1B,aAAO;AAAA,QACL,cAAc;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,WAAW;AACT,iBAAO,IAAI;AAAA,QACb;AAAA,QACA,SAAS,MAAM;AACb,cAAI,UAAU;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,0BAA0B,KAAK,UAAU;AAAA,IAEzC;AAAA,IAEA,uBAAuB,KAAK,UAAU;AACpC,aAAO,SAAS,IAAI,SAAS,CAAC;AAAA,IAChC;AAAA,IAEA,eAAe,CAAC,OAAO,EAAE,UAAU,OAAO,cAAc;AACtD,YAAM,CAAC,WAAW,oBAAoB,yCAAY;AAClD,YAAM,WAAW,6BAAW,uCAAe;AAC3C,YAAM,YAAY,cAAc;AAChC,YAAM,aAAa,CAAC,MAAM;AAC1B,YAAM,eAAe,MAAM,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM;AACjF,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,MACT;AAEA,YAAM,OAAO,MAAM,cAAc,MAAM,YAAY,KAAK,GAAG,IAAI;AAE/D,iDAA0B,MAAM;AAC9B,YAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,QAAQ;AAAS;AACzD,cAAM,OAAO,QAAQ;AACrB,cAAM,oBAAoB,MAAM;AAC9B;AAAA,QACF;AACA,aAAK,iBAAiB,iBAAiB,iBAAiB;AACxD,aAAK,iBAAiB,oBAAoB,iBAAiB;AAC3D,eAAO,MAAM;AACX,eAAK,oBAAoB,iBAAiB,iBAAiB;AAC3D,eAAK,iBAAiB,oBAAoB,iBAAiB;AAAA,QAC7D;AAAA,MACF,GAAG,CAAC,kBAAkB,SAAS,CAAC;AAEhC,YAAM,QAAQ;AAAA,QACZ,YAAY,GAAG,QAAQ;AAAA,QACvB,GAAG,SAAS;AAAA,UACV;AAAA,UACA;AAAA,UACA,aAAa,qCAAU;AAAA,UACvB,cAAc,qCAAU;AAAA,QAC1B,CAAC;AAAA,MACH;AAEA,UAAI,QAAQ,IAAI,aAAa,iBAAiB,MAAM,UAAU;AAC5D,gBAAQ,IAAI,iBAAiB,OAAO,EAAE,YAAY,UAAU,CAAC;AAAA,MAC/D;AAEA,aAAO,0BAAQ,MAAM;AACnB,eAAO,EAAE,MAAM;AAAA,MAEjB,GAAG,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IAC5B;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
- import { Stack, Text } from "@tamagui/core";
2
- import { useMemo, useRef } from "react";
1
+ import { PresenceContext, usePresence } from "@tamagui/animate-presence";
2
+ import { Stack, Text, useIsomorphicLayoutEffect } from "@tamagui/core";
3
+ import { useContext, useMemo, useRef } from "react";
3
4
  function createAnimations(animations) {
4
5
  return {
5
6
  avoidClasses: false,
@@ -27,21 +28,45 @@ function createAnimations(animations) {
27
28
  useAnimatedNumberStyle(val, getStyle) {
28
29
  return getStyle(val.getValue());
29
30
  },
30
- useAnimations: (props, { getStyle }) => {
31
+ useAnimations: (props, { getStyle, state, hostRef }) => {
32
+ const [isPresent, sendExitComplete] = usePresence();
33
+ const presence = useContext(PresenceContext);
34
+ const isExiting = isPresent === false;
35
+ const isEntering = !state.mounted;
31
36
  const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation;
32
37
  const animation = animations[animationKey];
33
38
  if (!animation) {
34
- throw new Error(`no animation found: ${props.animation}`);
39
+ return null;
35
40
  }
36
41
  const keys = props.animateOnly ? props.animateOnly.join(" ") : "all";
42
+ useIsomorphicLayoutEffect(() => {
43
+ if (!sendExitComplete || !isExiting || !hostRef.current)
44
+ return;
45
+ const node = hostRef.current;
46
+ const onFinishAnimation = () => {
47
+ sendExitComplete == null ? void 0 : sendExitComplete();
48
+ };
49
+ node.addEventListener("transitionend", onFinishAnimation);
50
+ node.addEventListener("transitioncancel", onFinishAnimation);
51
+ return () => {
52
+ node.removeEventListener("transitionend", onFinishAnimation);
53
+ node.addEventListener("transitioncancel", onFinishAnimation);
54
+ };
55
+ }, [sendExitComplete, isExiting]);
37
56
  const style = {
38
- style: {
39
- transition: `${keys} ${animation}`,
40
- ...getStyle()
41
- }
57
+ transition: `${keys} ${animation}`,
58
+ ...getStyle({
59
+ isEntering,
60
+ isExiting,
61
+ exitVariant: presence == null ? void 0 : presence.exitVariant,
62
+ enterVariant: presence == null ? void 0 : presence.enterVariant
63
+ })
42
64
  };
65
+ if (process.env.NODE_ENV === "development" && props["debug"]) {
66
+ console.log("CSS animation", style, { isEntering, isExiting });
67
+ }
43
68
  return useMemo(() => {
44
- return style;
69
+ return { style };
45
70
  }, [JSON.stringify(style)]);
46
71
  }
47
72
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/createAnimations.tsx"],
4
- "sourcesContent": ["import { AnimationDriver, Stack, Text } from '@tamagui/core'\nimport { useMemo, useRef } from 'react'\n\nexport function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {\n return {\n avoidClasses: false,\n View: Stack,\n Text: Text,\n animations,\n\n useAnimatedNumber(initial) {\n const val = useRef(initial)\n return {\n getInstance() {\n return val\n },\n getValue() {\n return val.current\n },\n setValue(next) {\n val.current = next\n },\n stop() {},\n }\n },\n\n useAnimatedNumberReaction(val, reaction) {\n // TODO use event listeners, would need access to the corresponsing node...\n },\n\n useAnimatedNumberStyle(val, getStyle) {\n return getStyle(val.getValue())\n },\n\n useAnimations: (props, { getStyle }) => {\n const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation\n const animation = animations[animationKey as any]\n if (!animation) {\n throw new Error(`no animation found: ${props.animation}`)\n }\n\n const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'\n\n const style = {\n style: {\n transition: `${keys} ${animation}`,\n ...getStyle(),\n },\n }\n\n return useMemo(() => {\n return style\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(style)])\n },\n }\n}\n"],
5
- "mappings": "AAAA;AACA;AAEO,0BAA4C,YAAmC;AACpF,SAAO;AAAA,IACL,cAAc;AAAA,IACd,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IAEA,kBAAkB,SAAS;AACzB,YAAM,MAAM,OAAO,OAAO;AAC1B,aAAO;AAAA,QACL,cAAc;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,WAAW;AACT,iBAAO,IAAI;AAAA,QACb;AAAA,QACA,SAAS,MAAM;AACb,cAAI,UAAU;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,0BAA0B,KAAK,UAAU;AAAA,IAEzC;AAAA,IAEA,uBAAuB,KAAK,UAAU;AACpC,aAAO,SAAS,IAAI,SAAS,CAAC;AAAA,IAChC;AAAA,IAEA,eAAe,CAAC,OAAO,EAAE,eAAe;AACtC,YAAM,eAAe,MAAM,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM;AACjF,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,uBAAuB,MAAM,WAAW;AAAA,MAC1D;AAEA,YAAM,OAAO,MAAM,cAAc,MAAM,YAAY,KAAK,GAAG,IAAI;AAE/D,YAAM,QAAQ;AAAA,QACZ,OAAO;AAAA,UACL,YAAY,GAAG,QAAQ;AAAA,UACvB,GAAG,SAAS;AAAA,QACd;AAAA,MACF;AAEA,aAAO,QAAQ,MAAM;AACnB,eAAO;AAAA,MAET,GAAG,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IAC5B;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { PresenceContext, usePresence } from '@tamagui/animate-presence'\nimport { AnimationDriver, Stack, Text, useIsomorphicLayoutEffect } from '@tamagui/core'\nimport { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react'\n\nexport function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {\n return {\n avoidClasses: false,\n View: Stack,\n Text: Text,\n animations,\n\n useAnimatedNumber(initial) {\n const val = useRef(initial)\n return {\n getInstance() {\n return val\n },\n getValue() {\n return val.current\n },\n setValue(next) {\n val.current = next\n },\n stop() {},\n }\n },\n\n useAnimatedNumberReaction(val, reaction) {\n // TODO use event listeners, would need access to the corresponsing node...\n },\n\n useAnimatedNumberStyle(val, getStyle) {\n return getStyle(val.getValue())\n },\n\n useAnimations: (props, { getStyle, state, hostRef }) => {\n const [isPresent, sendExitComplete] = usePresence()\n const presence = useContext(PresenceContext)\n const isExiting = isPresent === false\n const isEntering = !state.mounted\n const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation\n const animation = animations[animationKey as any]\n if (!animation) {\n return null\n }\n\n const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'\n\n useIsomorphicLayoutEffect(() => {\n if (!sendExitComplete || !isExiting || !hostRef.current) return\n const node = hostRef.current as HTMLElement\n const onFinishAnimation = () => {\n sendExitComplete?.()\n }\n node.addEventListener('transitionend', onFinishAnimation)\n node.addEventListener('transitioncancel', onFinishAnimation)\n return () => {\n node.removeEventListener('transitionend', onFinishAnimation)\n node.addEventListener('transitioncancel', onFinishAnimation)\n }\n }, [sendExitComplete, isExiting])\n\n const style = {\n transition: `${keys} ${animation}`,\n ...getStyle({\n isEntering,\n isExiting,\n exitVariant: presence?.exitVariant,\n enterVariant: presence?.enterVariant,\n }),\n }\n\n if (process.env.NODE_ENV === 'development' && props['debug']) {\n console.log('CSS animation', style, { isEntering, isExiting })\n }\n\n return useMemo(() => {\n return { style }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(style)])\n },\n }\n}\n"],
5
+ "mappings": "AAAA;AACA;AACA;AAEO,0BAA4C,YAAmC;AACpF,SAAO;AAAA,IACL,cAAc;AAAA,IACd,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IAEA,kBAAkB,SAAS;AACzB,YAAM,MAAM,OAAO,OAAO;AAC1B,aAAO;AAAA,QACL,cAAc;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,WAAW;AACT,iBAAO,IAAI;AAAA,QACb;AAAA,QACA,SAAS,MAAM;AACb,cAAI,UAAU;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,0BAA0B,KAAK,UAAU;AAAA,IAEzC;AAAA,IAEA,uBAAuB,KAAK,UAAU;AACpC,aAAO,SAAS,IAAI,SAAS,CAAC;AAAA,IAChC;AAAA,IAEA,eAAe,CAAC,OAAO,EAAE,UAAU,OAAO,cAAc;AACtD,YAAM,CAAC,WAAW,oBAAoB,YAAY;AAClD,YAAM,WAAW,WAAW,eAAe;AAC3C,YAAM,YAAY,cAAc;AAChC,YAAM,aAAa,CAAC,MAAM;AAC1B,YAAM,eAAe,MAAM,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM;AACjF,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,MACT;AAEA,YAAM,OAAO,MAAM,cAAc,MAAM,YAAY,KAAK,GAAG,IAAI;AAE/D,gCAA0B,MAAM;AAC9B,YAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,QAAQ;AAAS;AACzD,cAAM,OAAO,QAAQ;AACrB,cAAM,oBAAoB,MAAM;AAC9B;AAAA,QACF;AACA,aAAK,iBAAiB,iBAAiB,iBAAiB;AACxD,aAAK,iBAAiB,oBAAoB,iBAAiB;AAC3D,eAAO,MAAM;AACX,eAAK,oBAAoB,iBAAiB,iBAAiB;AAC3D,eAAK,iBAAiB,oBAAoB,iBAAiB;AAAA,QAC7D;AAAA,MACF,GAAG,CAAC,kBAAkB,SAAS,CAAC;AAEhC,YAAM,QAAQ;AAAA,QACZ,YAAY,GAAG,QAAQ;AAAA,QACvB,GAAG,SAAS;AAAA,UACV;AAAA,UACA;AAAA,UACA,aAAa,qCAAU;AAAA,UACvB,cAAc,qCAAU;AAAA,QAC1B,CAAC;AAAA,MACH;AAEA,UAAI,QAAQ,IAAI,aAAa,iBAAiB,MAAM,UAAU;AAC5D,gBAAQ,IAAI,iBAAiB,OAAO,EAAE,YAAY,UAAU,CAAC;AAAA,MAC/D;AAEA,aAAO,QAAQ,MAAM;AACnB,eAAO,EAAE,MAAM;AAAA,MAEjB,GAAG,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IAC5B;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
- import { Stack, Text } from "@tamagui/core";
2
- import { useMemo, useRef } from "react";
1
+ import { PresenceContext, usePresence } from "@tamagui/animate-presence";
2
+ import { Stack, Text, useIsomorphicLayoutEffect } from "@tamagui/core";
3
+ import { useContext, useMemo, useRef } from "react";
3
4
  function createAnimations(animations) {
4
5
  return {
5
6
  avoidClasses: false,
@@ -27,21 +28,45 @@ function createAnimations(animations) {
27
28
  useAnimatedNumberStyle(val, getStyle) {
28
29
  return getStyle(val.getValue());
29
30
  },
30
- useAnimations: (props, { getStyle }) => {
31
+ useAnimations: (props, { getStyle, state, hostRef }) => {
32
+ const [isPresent, sendExitComplete] = usePresence();
33
+ const presence = useContext(PresenceContext);
34
+ const isExiting = isPresent === false;
35
+ const isEntering = !state.mounted;
31
36
  const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation;
32
37
  const animation = animations[animationKey];
33
38
  if (!animation) {
34
- throw new Error(`no animation found: ${props.animation}`);
39
+ return null;
35
40
  }
36
41
  const keys = props.animateOnly ? props.animateOnly.join(" ") : "all";
42
+ useIsomorphicLayoutEffect(() => {
43
+ if (!sendExitComplete || !isExiting || !hostRef.current)
44
+ return;
45
+ const node = hostRef.current;
46
+ const onFinishAnimation = () => {
47
+ sendExitComplete?.();
48
+ };
49
+ node.addEventListener("transitionend", onFinishAnimation);
50
+ node.addEventListener("transitioncancel", onFinishAnimation);
51
+ return () => {
52
+ node.removeEventListener("transitionend", onFinishAnimation);
53
+ node.addEventListener("transitioncancel", onFinishAnimation);
54
+ };
55
+ }, [sendExitComplete, isExiting]);
37
56
  const style = {
38
- style: {
39
- transition: `${keys} ${animation}`,
40
- ...getStyle()
41
- }
57
+ transition: `${keys} ${animation}`,
58
+ ...getStyle({
59
+ isEntering,
60
+ isExiting,
61
+ exitVariant: presence?.exitVariant,
62
+ enterVariant: presence?.enterVariant
63
+ })
42
64
  };
65
+ if (process.env.NODE_ENV === "development" && props["debug"]) {
66
+ console.log("CSS animation", style, { isEntering, isExiting });
67
+ }
43
68
  return useMemo(() => {
44
- return style;
69
+ return { style };
45
70
  }, [JSON.stringify(style)]);
46
71
  }
47
72
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/createAnimations.tsx"],
4
- "sourcesContent": ["import { AnimationDriver, Stack, Text } from '@tamagui/core'\nimport { useMemo, useRef } from 'react'\n\nexport function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {\n return {\n avoidClasses: false,\n View: Stack,\n Text: Text,\n animations,\n\n useAnimatedNumber(initial) {\n const val = useRef(initial)\n return {\n getInstance() {\n return val\n },\n getValue() {\n return val.current\n },\n setValue(next) {\n val.current = next\n },\n stop() {},\n }\n },\n\n useAnimatedNumberReaction(val, reaction) {\n // TODO use event listeners, would need access to the corresponsing node...\n },\n\n useAnimatedNumberStyle(val, getStyle) {\n return getStyle(val.getValue())\n },\n\n useAnimations: (props, { getStyle }) => {\n const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation\n const animation = animations[animationKey as any]\n if (!animation) {\n throw new Error(`no animation found: ${props.animation}`)\n }\n\n const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'\n\n const style = {\n style: {\n transition: `${keys} ${animation}`,\n ...getStyle(),\n },\n }\n\n return useMemo(() => {\n return style\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(style)])\n },\n }\n}\n"],
5
- "mappings": "AAAA;AACA;AAEO,0BAA4C,YAAmC;AACpF,SAAO;AAAA,IACL,cAAc;AAAA,IACd,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IAEA,kBAAkB,SAAS;AACzB,YAAM,MAAM,OAAO,OAAO;AAC1B,aAAO;AAAA,QACL,cAAc;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,WAAW;AACT,iBAAO,IAAI;AAAA,QACb;AAAA,QACA,SAAS,MAAM;AACb,cAAI,UAAU;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,0BAA0B,KAAK,UAAU;AAAA,IAEzC;AAAA,IAEA,uBAAuB,KAAK,UAAU;AACpC,aAAO,SAAS,IAAI,SAAS,CAAC;AAAA,IAChC;AAAA,IAEA,eAAe,CAAC,OAAO,EAAE,eAAe;AACtC,YAAM,eAAe,MAAM,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM;AACjF,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,uBAAuB,MAAM,WAAW;AAAA,MAC1D;AAEA,YAAM,OAAO,MAAM,cAAc,MAAM,YAAY,KAAK,GAAG,IAAI;AAE/D,YAAM,QAAQ;AAAA,QACZ,OAAO;AAAA,UACL,YAAY,GAAG,QAAQ;AAAA,UACvB,GAAG,SAAS;AAAA,QACd;AAAA,MACF;AAEA,aAAO,QAAQ,MAAM;AACnB,eAAO;AAAA,MAET,GAAG,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IAC5B;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { PresenceContext, usePresence } from '@tamagui/animate-presence'\nimport { AnimationDriver, Stack, Text, useIsomorphicLayoutEffect } from '@tamagui/core'\nimport { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react'\n\nexport function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {\n return {\n avoidClasses: false,\n View: Stack,\n Text: Text,\n animations,\n\n useAnimatedNumber(initial) {\n const val = useRef(initial)\n return {\n getInstance() {\n return val\n },\n getValue() {\n return val.current\n },\n setValue(next) {\n val.current = next\n },\n stop() {},\n }\n },\n\n useAnimatedNumberReaction(val, reaction) {\n // TODO use event listeners, would need access to the corresponsing node...\n },\n\n useAnimatedNumberStyle(val, getStyle) {\n return getStyle(val.getValue())\n },\n\n useAnimations: (props, { getStyle, state, hostRef }) => {\n const [isPresent, sendExitComplete] = usePresence()\n const presence = useContext(PresenceContext)\n const isExiting = isPresent === false\n const isEntering = !state.mounted\n const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation\n const animation = animations[animationKey as any]\n if (!animation) {\n return null\n }\n\n const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'\n\n useIsomorphicLayoutEffect(() => {\n if (!sendExitComplete || !isExiting || !hostRef.current) return\n const node = hostRef.current as HTMLElement\n const onFinishAnimation = () => {\n sendExitComplete?.()\n }\n node.addEventListener('transitionend', onFinishAnimation)\n node.addEventListener('transitioncancel', onFinishAnimation)\n return () => {\n node.removeEventListener('transitionend', onFinishAnimation)\n node.addEventListener('transitioncancel', onFinishAnimation)\n }\n }, [sendExitComplete, isExiting])\n\n const style = {\n transition: `${keys} ${animation}`,\n ...getStyle({\n isEntering,\n isExiting,\n exitVariant: presence?.exitVariant,\n enterVariant: presence?.enterVariant,\n }),\n }\n\n if (process.env.NODE_ENV === 'development' && props['debug']) {\n console.log('CSS animation', style, { isEntering, isExiting })\n }\n\n return useMemo(() => {\n return { style }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(style)])\n },\n }\n}\n"],
5
+ "mappings": "AAAA;AACA;AACA;AAEO,0BAA4C,YAAmC;AACpF,SAAO;AAAA,IACL,cAAc;AAAA,IACd,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IAEA,kBAAkB,SAAS;AACzB,YAAM,MAAM,OAAO,OAAO;AAC1B,aAAO;AAAA,QACL,cAAc;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,WAAW;AACT,iBAAO,IAAI;AAAA,QACb;AAAA,QACA,SAAS,MAAM;AACb,cAAI,UAAU;AAAA,QAChB;AAAA,QACA,OAAO;AAAA,QAAC;AAAA,MACV;AAAA,IACF;AAAA,IAEA,0BAA0B,KAAK,UAAU;AAAA,IAEzC;AAAA,IAEA,uBAAuB,KAAK,UAAU;AACpC,aAAO,SAAS,IAAI,SAAS,CAAC;AAAA,IAChC;AAAA,IAEA,eAAe,CAAC,OAAO,EAAE,UAAU,OAAO,cAAc;AACtD,YAAM,CAAC,WAAW,oBAAoB,YAAY;AAClD,YAAM,WAAW,WAAW,eAAe;AAC3C,YAAM,YAAY,cAAc;AAChC,YAAM,aAAa,CAAC,MAAM;AAC1B,YAAM,eAAe,MAAM,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,KAAK,MAAM;AACjF,YAAM,YAAY,WAAW;AAC7B,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,MACT;AAEA,YAAM,OAAO,MAAM,cAAc,MAAM,YAAY,KAAK,GAAG,IAAI;AAE/D,gCAA0B,MAAM;AAC9B,YAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,QAAQ;AAAS;AACzD,cAAM,OAAO,QAAQ;AACrB,cAAM,oBAAoB,MAAM;AAC9B,6BAAmB;AAAA,QACrB;AACA,aAAK,iBAAiB,iBAAiB,iBAAiB;AACxD,aAAK,iBAAiB,oBAAoB,iBAAiB;AAC3D,eAAO,MAAM;AACX,eAAK,oBAAoB,iBAAiB,iBAAiB;AAC3D,eAAK,iBAAiB,oBAAoB,iBAAiB;AAAA,QAC7D;AAAA,MACF,GAAG,CAAC,kBAAkB,SAAS,CAAC;AAEhC,YAAM,QAAQ;AAAA,QACZ,YAAY,GAAG,QAAQ;AAAA,QACvB,GAAG,SAAS;AAAA,UACV;AAAA,UACA;AAAA,UACA,aAAa,UAAU;AAAA,UACvB,cAAc,UAAU;AAAA,QAC1B,CAAC;AAAA,MACH;AAEA,UAAI,QAAQ,IAAI,aAAa,iBAAiB,MAAM,UAAU;AAC5D,gBAAQ,IAAI,iBAAiB,OAAO,EAAE,YAAY,UAAU,CAAC;AAAA,MAC/D;AAEA,aAAO,QAAQ,MAAM;AACnB,eAAO,EAAE,MAAM;AAAA,MAEjB,GAAG,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,IAC5B;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/animations-css",
3
- "version": "1.0.1-beta.148",
3
+ "version": "1.0.1-beta.151",
4
4
  "source": "src/index.ts",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -14,10 +14,11 @@
14
14
  "dist"
15
15
  ],
16
16
  "dependencies": {
17
- "@tamagui/core": "^1.0.1-beta.148"
17
+ "@tamagui/animate-presence": "^1.0.1-beta.151",
18
+ "@tamagui/core": "^1.0.1-beta.151"
18
19
  },
19
20
  "devDependencies": {
20
- "@tamagui/build": "^1.0.1-beta.148"
21
+ "@tamagui/build": "^1.0.1-beta.151"
21
22
  },
22
23
  "scripts": {
23
24
  "build": "tamagui-build",
@@ -1,5 +1,6 @@
1
- import { AnimationDriver, Stack, Text } from '@tamagui/core'
2
- import { useMemo, useRef } from 'react'
1
+ import { PresenceContext, usePresence } from '@tamagui/animate-presence'
2
+ import { AnimationDriver, Stack, Text, useIsomorphicLayoutEffect } from '@tamagui/core'
3
+ import { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
3
4
 
4
5
  export function createAnimations<A extends Object>(animations: A): AnimationDriver<A> {
5
6
  return {
@@ -32,24 +33,49 @@ export function createAnimations<A extends Object>(animations: A): AnimationDriv
32
33
  return getStyle(val.getValue())
33
34
  },
34
35
 
35
- useAnimations: (props, { getStyle }) => {
36
+ useAnimations: (props, { getStyle, state, hostRef }) => {
37
+ const [isPresent, sendExitComplete] = usePresence()
38
+ const presence = useContext(PresenceContext)
39
+ const isExiting = isPresent === false
40
+ const isEntering = !state.mounted
36
41
  const animationKey = Array.isArray(props.animation) ? props.animation[0] : props.animation
37
42
  const animation = animations[animationKey as any]
38
43
  if (!animation) {
39
- throw new Error(`no animation found: ${props.animation}`)
44
+ return null
40
45
  }
41
46
 
42
47
  const keys = props.animateOnly ? props.animateOnly.join(' ') : 'all'
43
48
 
49
+ useIsomorphicLayoutEffect(() => {
50
+ if (!sendExitComplete || !isExiting || !hostRef.current) return
51
+ const node = hostRef.current as HTMLElement
52
+ const onFinishAnimation = () => {
53
+ sendExitComplete?.()
54
+ }
55
+ node.addEventListener('transitionend', onFinishAnimation)
56
+ node.addEventListener('transitioncancel', onFinishAnimation)
57
+ return () => {
58
+ node.removeEventListener('transitionend', onFinishAnimation)
59
+ node.addEventListener('transitioncancel', onFinishAnimation)
60
+ }
61
+ }, [sendExitComplete, isExiting])
62
+
44
63
  const style = {
45
- style: {
46
- transition: `${keys} ${animation}`,
47
- ...getStyle(),
48
- },
64
+ transition: `${keys} ${animation}`,
65
+ ...getStyle({
66
+ isEntering,
67
+ isExiting,
68
+ exitVariant: presence?.exitVariant,
69
+ enterVariant: presence?.enterVariant,
70
+ }),
71
+ }
72
+
73
+ if (process.env.NODE_ENV === 'development' && props['debug']) {
74
+ console.log('CSS animation', style, { isEntering, isExiting })
49
75
  }
50
76
 
51
77
  return useMemo(() => {
52
- return style
78
+ return { style }
53
79
  // eslint-disable-next-line react-hooks/exhaustive-deps
54
80
  }, [JSON.stringify(style)])
55
81
  },