@impulse-ui-native/toast 6.3.0 → 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 +6 -2
- package/dist/components/toast-root.js +1 -1
- package/dist/components/toast-root.js.map +1 -1
- package/dist/components/toast.js +2 -2
- package/dist/components/toast.js.map +1 -1
- package/dist/hooks/use-toast-lifecycle.hook.d.mts +4 -2
- package/dist/hooks/use-toast-lifecycle.hook.js +38 -109
- package/dist/hooks/use-toast-lifecycle.hook.js.map +1 -1
- package/package.json +6 -6
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={
|
|
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.
|
|
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.
|
|
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"]}
|
package/dist/components/toast.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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:
|
|
48
|
+
enabled: lifecycle.status === "open" && open,
|
|
55
49
|
duration,
|
|
56
50
|
callback: close,
|
|
57
51
|
pauseOnBackground: true
|
|
58
52
|
});
|
|
59
|
-
useEffect(() =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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,
|
|
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": "
|
|
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/
|
|
31
|
-
"@impulse-ui-native/
|
|
32
|
-
"@impulse-ui-native/
|
|
33
|
-
"@impulse-ui-native/
|
|
34
|
-
"@impulse-ui-native/icon": "
|
|
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",
|