@impulse-ui-native/toast 6.3.1 → 7.0.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
@@ -78,7 +78,7 @@ import { XIcon } from "@impulse-ui-native/icon/icons/x";
78
78
  <Toast.Description>Choose your own content and actions.</Toast.Description>
79
79
  </Toast.Content>
80
80
  <Toast.Close>
81
- <Icon icon={XIcon} size={24} />
81
+ <Icon icon={XIcon} size={18} />
82
82
  </Toast.Close>
83
83
  </Toast.Root>;
84
84
  ```
@@ -105,8 +105,12 @@ Toasts fade and translate by the theme edge offset. Top toasts sit 8 px below th
105
105
 
106
106
  ## Theme
107
107
 
108
- Toast uses `theme.components.toast` for surfaces, geometry, typography, actions and semantic icon colors. Override those tokens through ThemeProvider's components prop, including a factory that derives custom values from the active light/dark theme. No additional toast provider or host is needed.
108
+ Toast uses `theme.components.toast` for surfaces, geometry, typography, actions and semantic icon colors. Override those tokens through ThemeProvider's components prop, including a factory that derives custom values from the active light/dark theme. The default close icon uses closeIconSize (18) and closeColor (text.tertiary), matching Modal and Popover. Semantic icons retain their independent iconSize (24) and feedback colors. No additional toast provider or host is needed.
109
109
 
110
110
  ## Verification
111
111
 
112
112
  Use the Toast stories in the on-device Storybook host to verify rendering, touch interaction, timer pause/resume, and lifecycle cancellation on iOS and Android.
113
+
114
+ Toast uses the shared useOverlayLifecycle hook from the overlay package.
115
+ onStatusChange(id, status) reports opening, open, closing, and closed.
116
+ The duration timer starts when status reaches open.
@@ -69,7 +69,7 @@ const ToastRoot = memo(function ToastRoot2(props) {
69
69
  animatedDepth.value = withTiming(stackDepth, { duration: StackDuration });
70
70
  return () => cancelAnimation(animatedDepth);
71
71
  }, [animatedDepth, stackDepth]);
72
- if (lifecycle.finished) {
72
+ if (!lifecycle.mounted) {
73
73
  return null;
74
74
  }
75
75
  return /* @__PURE__ */ jsx(ToastContext.Provider, { value: context, children: /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/toast-root.tsx"],"sourcesContent":["import { memo, useEffect, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\nimport Animated, {\n cancelAnimation,\n useAnimatedStyle,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\n\nimport type { AppTheme } from \"@impulse-ui-native/theme\";\nimport { OverlayOrder, useOverlayLayer } from \"@impulse-ui-native/overlay\";\nimport { useComponentsTokens, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ToastRootProps } from \"../types\";\nimport { DefaultDuration, StackDuration } from \"../constants/toast.constants\";\nimport { ToastContext } from \"../contexts/toast.context\";\nimport { useToastLifecycle } from \"../hooks/use-toast-lifecycle.hook\";\n\nexport const ToastRoot = memo(function ToastRoot(props: ToastRootProps) {\n const {\n id,\n children,\n placement = \"bottom\",\n tone = \"success\",\n duration = DefaultDuration,\n layer = 0,\n } = props;\n\n const insets = useSafeAreaInsets();\n\n const tokens = useComponentsTokens().toast;\n\n const lifecycle = useToastLifecycle(props, duration);\n const toastLayer = useOverlayLayer(id, undefined, OverlayOrder.NewestFirst);\n const styles = useThemedStyles(themedStyles);\n const { progress } = lifecycle;\n const edgeOffset = tokens.edgeOffset;\n const stackDepth = Math.min(\n Math.max(0, toastLayer),\n Math.max(0, tokens.stackLimit - 1),\n );\n const animatedDepth = useSharedValue(stackDepth);\n const animatedStyle = useAnimatedStyle(() => ({\n opacity: progress.value,\n transform: [\n {\n translateY:\n (1 - progress.value) *\n (placement === \"top\" ? -edgeOffset : edgeOffset) +\n animatedDepth.value * tokens.gap,\n },\n { scale: Math.max(0, 1 - animatedDepth.value * tokens.stackScaleStep) },\n ],\n }));\n\n const context = useMemo(\n () => ({\n tone,\n interactive: lifecycle.interactive,\n close: lifecycle.close,\n }),\n [tone, lifecycle.interactive, lifecycle.close],\n );\n const containerStyle = useMemo(\n () => [\n styles.container,\n {\n left: insets.left + tokens.edgeOffset,\n right: insets.right + tokens.edgeOffset,\n [placement]:\n insets[placement] +\n (placement === \"top\" ? tokens.topOffset : tokens.edgeOffset),\n transformOrigin: \"center top\",\n zIndex: tokens.zIndexBase + layer,\n },\n animatedStyle,\n ],\n [styles.container, insets, tokens, placement, layer, animatedStyle],\n );\n\n useEffect(() => {\n animatedDepth.value = withTiming(stackDepth, { duration: StackDuration });\n\n return () => cancelAnimation(animatedDepth);\n }, [animatedDepth, stackDepth]);\n\n if (lifecycle.finished) {\n return null;\n }\n\n return (\n <ToastContext.Provider value={context}>\n <Animated.View\n pointerEvents={lifecycle.interactive ? \"auto\" : \"none\"}\n style={containerStyle}\n >\n {children}\n </Animated.View>\n </ToastContext.Provider>\n );\n});\n\nfunction themedStyles(theme: AppTheme) {\n const tokens = theme.components.toast;\n\n return StyleSheet.create({\n container: {\n position: \"absolute\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: tokens.gap,\n padding: tokens.padding,\n borderRadius: tokens.borderRadius,\n borderWidth: tokens.borderWidth,\n borderColor: tokens.borderColor,\n backgroundColor: tokens.backgroundColor,\n },\n });\n}\n"],"mappings":"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","names":["ToastRoot"]}
1
+ {"version":3,"sources":["../../src/components/toast-root.tsx"],"sourcesContent":["import { memo, useEffect, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\nimport Animated, {\n cancelAnimation,\n useAnimatedStyle,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\n\nimport type { AppTheme } from \"@impulse-ui-native/theme\";\nimport { OverlayOrder, useOverlayLayer } from \"@impulse-ui-native/overlay\";\nimport { useComponentsTokens, useThemedStyles } from \"@impulse-ui-native/theme\";\n\nimport type { ToastRootProps } from \"../types\";\nimport { DefaultDuration, StackDuration } from \"../constants/toast.constants\";\nimport { ToastContext } from \"../contexts/toast.context\";\nimport { useToastLifecycle } from \"../hooks/use-toast-lifecycle.hook\";\n\nexport const ToastRoot = memo(function ToastRoot(props: ToastRootProps) {\n const {\n id,\n children,\n placement = \"bottom\",\n tone = \"success\",\n duration = DefaultDuration,\n layer = 0,\n } = props;\n\n const insets = useSafeAreaInsets();\n\n const tokens = useComponentsTokens().toast;\n\n const lifecycle = useToastLifecycle(props, duration);\n const toastLayer = useOverlayLayer(id, undefined, OverlayOrder.NewestFirst);\n const styles = useThemedStyles(themedStyles);\n const { progress } = lifecycle;\n const edgeOffset = tokens.edgeOffset;\n const stackDepth = Math.min(\n Math.max(0, toastLayer),\n Math.max(0, tokens.stackLimit - 1),\n );\n const animatedDepth = useSharedValue(stackDepth);\n const animatedStyle = useAnimatedStyle(() => ({\n opacity: progress.value,\n transform: [\n {\n translateY:\n (1 - progress.value) *\n (placement === \"top\" ? -edgeOffset : edgeOffset) +\n animatedDepth.value * tokens.gap,\n },\n { scale: Math.max(0, 1 - animatedDepth.value * tokens.stackScaleStep) },\n ],\n }));\n\n const context = useMemo(\n () => ({\n tone,\n interactive: lifecycle.interactive,\n close: lifecycle.close,\n }),\n [tone, lifecycle.interactive, lifecycle.close],\n );\n const containerStyle = useMemo(\n () => [\n styles.container,\n {\n left: insets.left + tokens.edgeOffset,\n right: insets.right + tokens.edgeOffset,\n [placement]:\n insets[placement] +\n (placement === \"top\" ? tokens.topOffset : tokens.edgeOffset),\n transformOrigin: \"center top\",\n zIndex: tokens.zIndexBase + layer,\n },\n animatedStyle,\n ],\n [styles.container, insets, tokens, placement, layer, animatedStyle],\n );\n\n useEffect(() => {\n animatedDepth.value = withTiming(stackDepth, { duration: StackDuration });\n\n return () => cancelAnimation(animatedDepth);\n }, [animatedDepth, stackDepth]);\n\n if (!lifecycle.mounted) {\n return null;\n }\n\n return (\n <ToastContext.Provider value={context}>\n <Animated.View\n pointerEvents={lifecycle.interactive ? \"auto\" : \"none\"}\n style={containerStyle}\n >\n {children}\n </Animated.View>\n </ToastContext.Provider>\n );\n});\n\nfunction themedStyles(theme: AppTheme) {\n const tokens = theme.components.toast;\n\n return StyleSheet.create({\n container: {\n position: \"absolute\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: tokens.gap,\n padding: tokens.padding,\n borderRadius: tokens.borderRadius,\n borderWidth: tokens.borderWidth,\n borderColor: tokens.borderColor,\n backgroundColor: tokens.backgroundColor,\n },\n });\n}\n"],"mappings":"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","names":["ToastRoot"]}
@@ -36,8 +36,8 @@ const ToastComponent = memo(function ToastComponent2({
36
36
  Icon,
37
37
  {
38
38
  icon: XIcon,
39
- size: tokens.iconSize,
40
- color: tokens.descriptionColor
39
+ size: tokens.closeIconSize,
40
+ color: tokens.closeColor
41
41
  }
42
42
  ) }) : null
43
43
  ] });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/toast.tsx"],"sourcesContent":["import { memo } from \"react\";\n\nimport { Icon } from \"@impulse-ui-native/icon\";\nimport { XIcon } from \"@impulse-ui-native/icon/icons/x\";\nimport { useComponentsTokens } from \"@impulse-ui-native/theme\";\n\nimport type { ToastProps } from \"../types\";\nimport { ToastIcons } from \"../constants/toast-icons.constants\";\nimport { Toast } from \"./toast-parts\";\n\nexport const ToastComponent = memo(function ToastComponent({\n children,\n title,\n description,\n action,\n actionProps,\n tone = \"success\",\n hideIcon = false,\n hideClose = false,\n ...props\n}: ToastProps) {\n const tokens = useComponentsTokens().toast;\n\n return (\n <Toast.Root {...props} tone={tone}>\n {!hideIcon ? (\n <Toast.Icon>\n <Icon\n icon={ToastIcons[tone]}\n size={tokens.iconSize}\n color={tokens.tones[tone].value}\n />\n </Toast.Icon>\n ) : null}\n <Toast.Content>\n {title ? <Toast.Title>{title}</Toast.Title> : null}\n {description !== null && description !== undefined ? (\n <Toast.Description>{description}</Toast.Description>\n ) : null}\n {children}\n </Toast.Content>\n {action !== null && action !== undefined ? (\n <Toast.Action {...actionProps}>{action}</Toast.Action>\n ) : null}\n {!hideClose ? (\n <Toast.Close>\n <Icon\n icon={XIcon}\n size={tokens.iconSize}\n color={tokens.descriptionColor}\n />\n </Toast.Close>\n ) : null}\n </Toast.Root>\n );\n});\n"],"mappings":"AA2BU,cAOJ,YAPI;AA3BV,SAAS,YAAY;AAErB,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,2BAA2B;AAGpC,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AAEf,MAAM,iBAAiB,KAAK,SAASA,gBAAe;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,GAAG;AACL,GAAe;AACb,QAAM,SAAS,oBAAoB,EAAE;AAErC,SACE,qBAAC,MAAM,MAAN,EAAY,GAAG,OAAO,MACpB;AAAA,KAAC,WACA,oBAAC,MAAM,MAAN,EACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW,IAAI;AAAA,QACrB,MAAM,OAAO;AAAA,QACb,OAAO,OAAO,MAAM,IAAI,EAAE;AAAA;AAAA,IAC5B,GACF,IACE;AAAA,IACJ,qBAAC,MAAM,SAAN,EACE;AAAA,cAAQ,oBAAC,MAAM,OAAN,EAAa,iBAAM,IAAiB;AAAA,MAC7C,gBAAgB,QAAQ,gBAAgB,SACvC,oBAAC,MAAM,aAAN,EAAmB,uBAAY,IAC9B;AAAA,MACH;AAAA,OACH;AAAA,IACC,WAAW,QAAQ,WAAW,SAC7B,oBAAC,MAAM,QAAN,EAAc,GAAG,aAAc,kBAAO,IACrC;AAAA,IACH,CAAC,YACA,oBAAC,MAAM,OAAN,EACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA;AAAA,IAChB,GACF,IACE;AAAA,KACN;AAEJ,CAAC;","names":["ToastComponent"]}
1
+ {"version":3,"sources":["../../src/components/toast.tsx"],"sourcesContent":["import { memo } from \"react\";\n\nimport { Icon } from \"@impulse-ui-native/icon\";\nimport { XIcon } from \"@impulse-ui-native/icon/icons/x\";\nimport { useComponentsTokens } from \"@impulse-ui-native/theme\";\n\nimport type { ToastProps } from \"../types\";\nimport { ToastIcons } from \"../constants/toast-icons.constants\";\nimport { Toast } from \"./toast-parts\";\n\nexport const ToastComponent = memo(function ToastComponent({\n children,\n title,\n description,\n action,\n actionProps,\n tone = \"success\",\n hideIcon = false,\n hideClose = false,\n ...props\n}: ToastProps) {\n const tokens = useComponentsTokens().toast;\n\n return (\n <Toast.Root {...props} tone={tone}>\n {!hideIcon ? (\n <Toast.Icon>\n <Icon\n icon={ToastIcons[tone]}\n size={tokens.iconSize}\n color={tokens.tones[tone].value}\n />\n </Toast.Icon>\n ) : null}\n <Toast.Content>\n {title ? <Toast.Title>{title}</Toast.Title> : null}\n {description !== null && description !== undefined ? (\n <Toast.Description>{description}</Toast.Description>\n ) : null}\n {children}\n </Toast.Content>\n {action !== null && action !== undefined ? (\n <Toast.Action {...actionProps}>{action}</Toast.Action>\n ) : null}\n {!hideClose ? (\n <Toast.Close>\n <Icon\n icon={XIcon}\n size={tokens.closeIconSize}\n color={tokens.closeColor}\n />\n </Toast.Close>\n ) : null}\n </Toast.Root>\n );\n});\n"],"mappings":"AA2BU,cAOJ,YAPI;AA3BV,SAAS,YAAY;AAErB,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,2BAA2B;AAGpC,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AAEf,MAAM,iBAAiB,KAAK,SAASA,gBAAe;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,GAAG;AACL,GAAe;AACb,QAAM,SAAS,oBAAoB,EAAE;AAErC,SACE,qBAAC,MAAM,MAAN,EAAY,GAAG,OAAO,MACpB;AAAA,KAAC,WACA,oBAAC,MAAM,MAAN,EACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW,IAAI;AAAA,QACrB,MAAM,OAAO;AAAA,QACb,OAAO,OAAO,MAAM,IAAI,EAAE;AAAA;AAAA,IAC5B,GACF,IACE;AAAA,IACJ,qBAAC,MAAM,SAAN,EACE;AAAA,cAAQ,oBAAC,MAAM,OAAN,EAAa,iBAAM,IAAiB;AAAA,MAC7C,gBAAgB,QAAQ,gBAAgB,SACvC,oBAAC,MAAM,aAAN,EAAmB,uBAAY,IAC9B;AAAA,MACH;AAAA,OACH;AAAA,IACC,WAAW,QAAQ,WAAW,SAC7B,oBAAC,MAAM,QAAN,EAAc,GAAG,aAAc,kBAAO,IACrC;AAAA,IACH,CAAC,YACA,oBAAC,MAAM,OAAN,EACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA;AAAA,IAChB,GACF,IACE;AAAA,KACN;AAEJ,CAAC;","names":["ToastComponent"]}
@@ -1,15 +1,17 @@
1
+ import * as _impulse_ui_native_overlay from '@impulse-ui-native/overlay';
1
2
  import * as react_native_reanimated from 'react-native-reanimated';
2
3
  import { ToastRootProps } from '../types/toast.types.mjs';
3
4
  import 'react';
4
- import '@impulse-ui-native/overlay';
5
5
  import '@impulse-ui-native/primitives';
6
6
  import '@impulse-ui-native/theme';
7
7
 
8
8
  declare function useToastLifecycle(props: ToastRootProps, duration: number): {
9
9
  progress: react_native_reanimated.SharedValue<number>;
10
10
  close: () => void;
11
- finished: boolean;
11
+ status: _impulse_ui_native_overlay.OverlayLifecycleStatus;
12
+ mounted: boolean;
12
13
  interactive: boolean;
14
+ completeTransition: _impulse_ui_native_overlay.OverlayTransitionCompletion;
13
15
  };
14
16
 
15
17
  export { useToastLifecycle };
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState } from "react";
1
+ import { useEffect, useMemo } from "react";
2
2
  import {
3
3
  cancelAnimation,
4
4
  useSharedValue,
@@ -6,126 +6,55 @@ import {
6
6
  } from "react-native-reanimated";
7
7
  import { scheduleOnRN } from "react-native-worklets";
8
8
  import { useEventCallback, useTimer } from "@impulse-ui-native/core";
9
- import { useOverlayContext } from "@impulse-ui-native/overlay";
9
+ import {
10
+ useOverlayContext,
11
+ useOverlayLifecycle
12
+ } from "@impulse-ui-native/overlay";
10
13
  import { EnterDuration, ExitDuration } from "../constants/toast.constants";
11
14
  function useToastLifecycle(props, duration) {
12
- const {
13
- id,
14
- open = true,
15
- onOpen,
16
- onOpenFinished,
17
- onClose,
18
- onCloseFinished
19
- } = props;
15
+ const { id, open = true } = props;
20
16
  const { store } = useOverlayContext();
21
- const [entered, setEntered] = useState(false);
22
- const [finished, setFinished] = useState(false);
23
- const opened = useRef(false);
24
- const closing = useRef(false);
25
- const settled = useRef(false);
26
- const transitionId = useRef(0);
27
17
  const progress = useSharedValue(0);
28
- const close = useEventCallback(() => store.close(id));
29
- const notifyOpen = useEventCallback(() => onOpen?.(id));
30
- const notifyEntered = useEventCallback(() => onOpenFinished?.(id));
31
- const notifyClose = useEventCallback(() => onClose?.(id));
32
- const finishClose = useEventCallback(() => {
33
- if (settled.current) {
34
- return;
18
+ const enter = useEventCallback(
19
+ (transitionId, complete) => {
20
+ progress.set(
21
+ withTiming(1, { duration: EnterDuration }, (finished) => {
22
+ if (finished) {
23
+ scheduleOnRN(complete, transitionId);
24
+ }
25
+ })
26
+ );
35
27
  }
36
- settled.current = true;
37
- setFinished(true);
38
- onCloseFinished?.(id);
39
- });
40
- const finishTransition = useEventCallback(
41
- (completedTransitionId, entering) => {
42
- if (transitionId.current !== completedTransitionId || settled.current) {
43
- return;
44
- }
45
- if (entering) {
46
- setEntered(true);
47
- notifyEntered();
48
- } else {
49
- finishClose();
50
- }
28
+ );
29
+ const exit = useEventCallback(
30
+ (transitionId, complete) => {
31
+ progress.set(
32
+ withTiming(0, { duration: ExitDuration }, (finished) => {
33
+ if (finished) {
34
+ scheduleOnRN(complete, transitionId);
35
+ }
36
+ })
37
+ );
51
38
  }
52
39
  );
40
+ const options = useMemo(
41
+ () => ({ onEnter: enter, onExit: exit }),
42
+ [enter, exit]
43
+ );
44
+ const lifecycleProps = useMemo(() => ({ ...props, open }), [props, open]);
45
+ const lifecycle = useOverlayLifecycle(lifecycleProps, options);
46
+ const close = useEventCallback(() => store.close(id));
53
47
  useTimer({
54
- enabled: entered && open && !finished,
48
+ enabled: lifecycle.status === "open" && open,
55
49
  duration,
56
50
  callback: close,
57
51
  pauseOnBackground: true
58
52
  });
59
- useEffect(() => {
60
- if (finished) {
61
- return;
62
- }
63
- const currentTransitionId = ++transitionId.current;
64
- if (!open) {
65
- const firstClose = !closing.current;
66
- closing.current = true;
67
- if (!opened.current) {
68
- try {
69
- if (firstClose) {
70
- notifyClose();
71
- }
72
- } finally {
73
- finishClose();
74
- }
75
- return;
76
- }
77
- try {
78
- if (firstClose) {
79
- notifyClose();
80
- }
81
- } finally {
82
- progress.value = withTiming(
83
- 0,
84
- { duration: ExitDuration },
85
- (completed) => {
86
- if (completed) {
87
- scheduleOnRN(finishTransition, currentTransitionId, false);
88
- }
89
- }
90
- );
91
- }
92
- return () => {
93
- transitionId.current += 1;
94
- cancelAnimation(progress);
95
- };
96
- }
97
- const firstOpen = !opened.current;
98
- opened.current = true;
99
- try {
100
- if (firstOpen) {
101
- notifyOpen();
102
- }
103
- } finally {
104
- progress.value = withTiming(
105
- 1,
106
- { duration: EnterDuration },
107
- (completed) => {
108
- if (completed) {
109
- scheduleOnRN(finishTransition, currentTransitionId, true);
110
- }
111
- }
112
- );
113
- }
114
- return () => {
115
- transitionId.current += 1;
116
- cancelAnimation(progress);
117
- };
118
- }, [
119
- finishClose,
120
- finishTransition,
121
- finished,
122
- notifyClose,
123
- notifyEntered,
124
- notifyOpen,
125
- open,
126
- progress
127
- ]);
128
- return { progress, close, finished, interactive: open && !finished };
53
+ useEffect(() => () => cancelAnimation(progress), [progress]);
54
+ return useMemo(
55
+ () => ({ ...lifecycle, progress, close }),
56
+ [lifecycle, progress, close]
57
+ );
129
58
  }
130
59
  export {
131
60
  useToastLifecycle
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/use-toast-lifecycle.hook.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n cancelAnimation,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\nimport { scheduleOnRN } from \"react-native-worklets\";\n\nimport { useEventCallback, useTimer } from \"@impulse-ui-native/core\";\nimport { useOverlayContext } from \"@impulse-ui-native/overlay\";\n\nimport type { ToastRootProps } from \"../types\";\nimport { EnterDuration, ExitDuration } from \"../constants/toast.constants\";\n\nexport function useToastLifecycle(props: ToastRootProps, duration: number) {\n const {\n id,\n open = true,\n onOpen,\n onOpenFinished,\n onClose,\n onCloseFinished,\n } = props;\n\n const { store } = useOverlayContext();\n\n const [entered, setEntered] = useState(false);\n const [finished, setFinished] = useState(false);\n\n const opened = useRef(false);\n const closing = useRef(false);\n const settled = useRef(false);\n const transitionId = useRef(0);\n\n const progress = useSharedValue(0);\n\n const close = useEventCallback(() => store.close(id));\n\n const notifyOpen = useEventCallback(() => onOpen?.(id));\n\n const notifyEntered = useEventCallback(() => onOpenFinished?.(id));\n\n const notifyClose = useEventCallback(() => onClose?.(id));\n\n const finishClose = useEventCallback(() => {\n if (settled.current) {\n return;\n }\n\n settled.current = true;\n\n setFinished(true);\n\n onCloseFinished?.(id);\n });\n\n const finishTransition = useEventCallback(\n (completedTransitionId: number, entering: boolean) => {\n if (transitionId.current !== completedTransitionId || settled.current) {\n return;\n }\n\n if (entering) {\n setEntered(true);\n\n notifyEntered();\n } else {\n finishClose();\n }\n },\n );\n\n useTimer({\n enabled: entered && open && !finished,\n duration,\n callback: close,\n pauseOnBackground: true,\n });\n\n useEffect(() => {\n if (finished) {\n return;\n }\n\n const currentTransitionId = ++transitionId.current;\n\n if (!open) {\n const firstClose = !closing.current;\n\n closing.current = true;\n\n if (!opened.current) {\n try {\n if (firstClose) {\n notifyClose();\n }\n } finally {\n finishClose();\n }\n\n return;\n }\n\n try {\n if (firstClose) {\n notifyClose();\n }\n } finally {\n progress.value = withTiming(\n 0,\n { duration: ExitDuration },\n (completed) => {\n if (completed) {\n scheduleOnRN(finishTransition, currentTransitionId, false);\n }\n },\n );\n }\n\n return () => {\n transitionId.current += 1;\n\n cancelAnimation(progress);\n };\n }\n\n const firstOpen = !opened.current;\n\n opened.current = true;\n\n try {\n if (firstOpen) {\n notifyOpen();\n }\n } finally {\n progress.value = withTiming(\n 1,\n { duration: EnterDuration },\n (completed) => {\n if (completed) {\n scheduleOnRN(finishTransition, currentTransitionId, true);\n }\n },\n );\n }\n\n return () => {\n transitionId.current += 1;\n\n cancelAnimation(progress);\n };\n }, [\n finishClose,\n finishTransition,\n finished,\n notifyClose,\n notifyEntered,\n notifyOpen,\n open,\n progress,\n ]);\n\n return { progress, close, finished, interactive: open && !finished };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/hooks/use-toast-lifecycle.hook.ts"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport {\n cancelAnimation,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\nimport { scheduleOnRN } from \"react-native-worklets\";\n\nimport type { OverlayTransitionHandler } from \"@impulse-ui-native/overlay\";\nimport { useEventCallback, useTimer } from \"@impulse-ui-native/core\";\nimport {\n useOverlayContext,\n useOverlayLifecycle,\n} from \"@impulse-ui-native/overlay\";\n\nimport type { ToastRootProps } from \"../types\";\nimport { EnterDuration, ExitDuration } from \"../constants/toast.constants\";\n\nexport function useToastLifecycle(props: ToastRootProps, duration: number) {\n const { id, open = true } = props;\n const { store } = useOverlayContext();\n\n const progress = useSharedValue(0);\n\n const enter = useEventCallback<OverlayTransitionHandler>(\n (transitionId, complete) => {\n progress.set(\n withTiming(1, { duration: EnterDuration }, (finished) => {\n if (finished) {\n scheduleOnRN(complete, transitionId);\n }\n }),\n );\n },\n );\n const exit = useEventCallback<OverlayTransitionHandler>(\n (transitionId, complete) => {\n progress.set(\n withTiming(0, { duration: ExitDuration }, (finished) => {\n if (finished) {\n scheduleOnRN(complete, transitionId);\n }\n }),\n );\n },\n );\n\n const options = useMemo(\n () => ({ onEnter: enter, onExit: exit }),\n [enter, exit],\n );\n const lifecycleProps = useMemo(() => ({ ...props, open }), [props, open]);\n const lifecycle = useOverlayLifecycle(lifecycleProps, options);\n const close = useEventCallback(() => store.close(id));\n\n useTimer({\n enabled: lifecycle.status === \"open\" && open,\n duration,\n callback: close,\n pauseOnBackground: true,\n });\n\n useEffect(() => () => cancelAnimation(progress), [progress]);\n\n return useMemo(\n () => ({ ...lifecycle, progress, close }),\n [lifecycle, progress, close],\n );\n}\n"],"mappings":"AAAA,SAAS,WAAW,eAAe;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAG7B,SAAS,kBAAkB,gBAAgB;AAC3C;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGP,SAAS,eAAe,oBAAoB;AAErC,SAAS,kBAAkB,OAAuB,UAAkB;AACzE,QAAM,EAAE,IAAI,OAAO,KAAK,IAAI;AAC5B,QAAM,EAAE,MAAM,IAAI,kBAAkB;AAEpC,QAAM,WAAW,eAAe,CAAC;AAEjC,QAAM,QAAQ;AAAA,IACZ,CAAC,cAAc,aAAa;AAC1B,eAAS;AAAA,QACP,WAAW,GAAG,EAAE,UAAU,cAAc,GAAG,CAAC,aAAa;AACvD,cAAI,UAAU;AACZ,yBAAa,UAAU,YAAY;AAAA,UACrC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO;AAAA,IACX,CAAC,cAAc,aAAa;AAC1B,eAAS;AAAA,QACP,WAAW,GAAG,EAAE,UAAU,aAAa,GAAG,CAAC,aAAa;AACtD,cAAI,UAAU;AACZ,yBAAa,UAAU,YAAY;AAAA,UACrC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU;AAAA,IACd,OAAO,EAAE,SAAS,OAAO,QAAQ,KAAK;AAAA,IACtC,CAAC,OAAO,IAAI;AAAA,EACd;AACA,QAAM,iBAAiB,QAAQ,OAAO,EAAE,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC;AACxE,QAAM,YAAY,oBAAoB,gBAAgB,OAAO;AAC7D,QAAM,QAAQ,iBAAiB,MAAM,MAAM,MAAM,EAAE,CAAC;AAEpD,WAAS;AAAA,IACP,SAAS,UAAU,WAAW,UAAU;AAAA,IACxC;AAAA,IACA,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB,CAAC;AAED,YAAU,MAAM,MAAM,gBAAgB,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAE3D,SAAO;AAAA,IACL,OAAO,EAAE,GAAG,WAAW,UAAU,MAAM;AAAA,IACvC,CAAC,WAAW,UAAU,KAAK;AAAA,EAC7B;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@impulse-ui-native/toast",
3
- "version": "6.3.1",
3
+ "version": "7.0.0",
4
4
  "description": "Compound toast notifications for ImpulseUI Native.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -27,11 +27,11 @@
27
27
  ],
28
28
  "sideEffects": false,
29
29
  "dependencies": {
30
- "@impulse-ui-native/theme": "6.3.1",
31
- "@impulse-ui-native/overlay": "6.3.1",
32
- "@impulse-ui-native/primitives": "6.3.1",
33
- "@impulse-ui-native/core": "6.3.1",
34
- "@impulse-ui-native/icon": "6.3.1"
30
+ "@impulse-ui-native/primitives": "7.0.0",
31
+ "@impulse-ui-native/theme": "7.0.0",
32
+ "@impulse-ui-native/core": "7.0.0",
33
+ "@impulse-ui-native/overlay": "7.0.0",
34
+ "@impulse-ui-native/icon": "7.0.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/react": "~19.2.14",
@@ -44,8 +44,8 @@
44
44
  "react-native-safe-area-context": "~5.7.0",
45
45
  "typescript": "~6.0.3",
46
46
  "react-native-svg": "15.15.4",
47
- "@impulse-ui-native/prettier-config": "1.0.0",
48
47
  "@impulse-ui-native/eslint-config": "1.0.0",
48
+ "@impulse-ui-native/prettier-config": "1.0.0",
49
49
  "@impulse-ui-native/tsconfig": "1.0.0"
50
50
  },
51
51
  "peerDependencies": {