@impulse-ui-native/flyout 2.0.1 → 2.1.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ImpulseUI Native contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ interface FlyoutHandleProps {
5
+ placement: "top" | "bottom";
6
+ }
7
+ declare const FlyoutHandle: react.MemoExoticComponent<(props: FlyoutHandleProps) => react_jsx_runtime.JSX.Element>;
8
+
9
+ export { FlyoutHandle };
@@ -0,0 +1,35 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import Animated from "react-native-reanimated";
5
+ import { View } from "@impulse-ui-native/primitives";
6
+ import { useThemedStyles } from "@impulse-ui-native/theme";
7
+ const FlyoutHandle = memo(function FlyoutHandle2(props) {
8
+ const styles = useThemedStyles(themedStyles, props, [props.placement]);
9
+ return /* @__PURE__ */ jsx(Animated.View, { style: styles.flyoutHandleContainer, children: /* @__PURE__ */ jsx(View, { style: styles.flyoutHandle }) });
10
+ });
11
+ function themedStyles(theme, { placement }) {
12
+ const handleTokens = theme.components.flyout.handle;
13
+ return StyleSheet.create({
14
+ flyoutHandle: {
15
+ alignSelf: "center",
16
+ backgroundColor: handleTokens.backgroundColor,
17
+ width: handleTokens.width,
18
+ height: handleTokens.height,
19
+ borderRadius: handleTokens.borderRadius
20
+ },
21
+ flyoutHandleContainer: {
22
+ position: "absolute",
23
+ top: placement === "bottom" ? -handleTokens.containerHeight : void 0,
24
+ bottom: placement === "top" ? -handleTokens.containerHeight : void 0,
25
+ width: "100%",
26
+ justifyContent: "center",
27
+ alignItems: "center",
28
+ height: handleTokens.containerHeight
29
+ }
30
+ });
31
+ }
32
+ export {
33
+ FlyoutHandle
34
+ };
35
+ //# sourceMappingURL=flyout-handle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/flyout-handle.tsx"],"sourcesContent":["import { memo } from \"react\";\r\nimport { StyleSheet } from \"react-native\";\r\nimport Animated from \"react-native-reanimated\";\r\n\r\nimport { View } from \"@impulse-ui-native/primitives\";\r\nimport { AppTheme, useThemedStyles } from \"@impulse-ui-native/theme\";\r\n\r\ninterface FlyoutHandleProps {\r\n placement: \"top\" | \"bottom\";\r\n}\r\n\r\nexport const FlyoutHandle = memo(function FlyoutHandle(\r\n props: FlyoutHandleProps,\r\n) {\r\n const styles = useThemedStyles(themedStyles, props, [props.placement]);\r\n\r\n return (\r\n <Animated.View style={styles.flyoutHandleContainer}>\r\n <View style={styles.flyoutHandle} />\r\n </Animated.View>\r\n );\r\n});\r\n\r\nfunction themedStyles(\r\n theme: AppTheme,\r\n { placement }: { placement: \"top\" | \"bottom\" },\r\n) {\r\n const handleTokens = theme.components.flyout.handle;\r\n\r\n return StyleSheet.create({\r\n flyoutHandle: {\r\n alignSelf: \"center\",\r\n backgroundColor: handleTokens.backgroundColor,\r\n width: handleTokens.width,\r\n height: handleTokens.height,\r\n borderRadius: handleTokens.borderRadius,\r\n },\r\n flyoutHandleContainer: {\r\n position: \"absolute\",\r\n top: placement === \"bottom\" ? -handleTokens.containerHeight : undefined,\r\n bottom: placement === \"top\" ? -handleTokens.containerHeight : undefined,\r\n width: \"100%\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n height: handleTokens.containerHeight,\r\n },\r\n });\r\n}\r\n"],"mappings":"AAkBM;AAlBN,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAC3B,OAAO,cAAc;AAErB,SAAS,YAAY;AACrB,SAAmB,uBAAuB;AAMnC,MAAM,eAAe,KAAK,SAASA,cACxC,OACA;AACA,QAAM,SAAS,gBAAgB,cAAc,OAAO,CAAC,MAAM,SAAS,CAAC;AAErE,SACE,oBAAC,SAAS,MAAT,EAAc,OAAO,OAAO,uBAC3B,8BAAC,QAAK,OAAO,OAAO,cAAc,GACpC;AAEJ,CAAC;AAED,SAAS,aACP,OACA,EAAE,UAAU,GACZ;AACA,QAAM,eAAe,MAAM,WAAW,OAAO;AAE7C,SAAO,WAAW,OAAO;AAAA,IACvB,cAAc;AAAA,MACZ,WAAW;AAAA,MACX,iBAAiB,aAAa;AAAA,MAC9B,OAAO,aAAa;AAAA,MACpB,QAAQ,aAAa;AAAA,MACrB,cAAc,aAAa;AAAA,IAC7B;AAAA,IACA,uBAAuB;AAAA,MACrB,UAAU;AAAA,MACV,KAAK,cAAc,WAAW,CAAC,aAAa,kBAAkB;AAAA,MAC9D,QAAQ,cAAc,QAAQ,CAAC,aAAa,kBAAkB;AAAA,MAC9D,OAAO;AAAA,MACP,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,QAAQ,aAAa;AAAA,IACvB;AAAA,EACF,CAAC;AACH;","names":["FlyoutHandle"]}
@@ -0,0 +1,10 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+
5
+ interface FlyoutTitleProps {
6
+ title?: ReactNode | string;
7
+ }
8
+ declare const FlyoutTitle: react.MemoExoticComponent<(props: FlyoutTitleProps) => react_jsx_runtime.JSX.Element | null>;
9
+
10
+ export { FlyoutTitle };
@@ -0,0 +1,32 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { memo, useMemo } from "react";
3
+ import { StyleSheet } from "react-native";
4
+ import { Typography, View } from "@impulse-ui-native/primitives";
5
+ import { useThemedStyles } from "@impulse-ui-native/theme";
6
+ const FlyoutTitle = memo(function FlyoutTitle2(props) {
7
+ const styles = useThemedStyles(themedStyles);
8
+ const title = useMemo(() => {
9
+ if (typeof props.title === "string") {
10
+ return /* @__PURE__ */ jsx(Typography.Title3, { children: props.title });
11
+ }
12
+ return props.title;
13
+ }, [props.title]);
14
+ if (!props.title) {
15
+ return null;
16
+ }
17
+ return /* @__PURE__ */ jsx(View, { style: styles.container, children: title });
18
+ });
19
+ function themedStyles(theme) {
20
+ const titleTokens = theme.components.flyout.title;
21
+ return StyleSheet.create({
22
+ container: {
23
+ paddingVertical: titleTokens.paddingVertical,
24
+ paddingHorizontal: titleTokens.paddingHorizontal,
25
+ alignItems: "center"
26
+ }
27
+ });
28
+ }
29
+ export {
30
+ FlyoutTitle
31
+ };
32
+ //# sourceMappingURL=flyout-title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/flyout-title.tsx"],"sourcesContent":["import { memo, ReactNode, useMemo } from \"react\";\r\nimport { StyleSheet } from \"react-native\";\r\n\r\nimport { Typography, View } from \"@impulse-ui-native/primitives\";\r\nimport { AppTheme, useThemedStyles } from \"@impulse-ui-native/theme\";\r\n\r\ninterface FlyoutTitleProps {\r\n title?: ReactNode | string;\r\n}\r\n\r\nexport const FlyoutTitle = memo(function FlyoutTitle(props: FlyoutTitleProps) {\r\n const styles = useThemedStyles(themedStyles);\r\n\r\n const title = useMemo(() => {\r\n if (typeof props.title === \"string\") {\r\n return <Typography.Title3>{props.title}</Typography.Title3>;\r\n }\r\n\r\n return props.title;\r\n }, [props.title]);\r\n\r\n if (!props.title) {\r\n return null;\r\n }\r\n\r\n return <View style={styles.container}>{title}</View>;\r\n});\r\n\r\nfunction themedStyles(theme: AppTheme) {\r\n const titleTokens = theme.components.flyout.title;\r\n\r\n return StyleSheet.create({\r\n container: {\r\n paddingVertical: titleTokens.paddingVertical,\r\n paddingHorizontal: titleTokens.paddingHorizontal,\r\n alignItems: \"center\",\r\n },\r\n });\r\n}\r\n"],"mappings":"AAea;AAfb,SAAS,MAAiB,eAAe;AACzC,SAAS,kBAAkB;AAE3B,SAAS,YAAY,YAAY;AACjC,SAAmB,uBAAuB;AAMnC,MAAM,cAAc,KAAK,SAASA,aAAY,OAAyB;AAC5E,QAAM,SAAS,gBAAgB,YAAY;AAE3C,QAAM,QAAQ,QAAQ,MAAM;AAC1B,QAAI,OAAO,MAAM,UAAU,UAAU;AACnC,aAAO,oBAAC,WAAW,QAAX,EAAmB,gBAAM,OAAM;AAAA,IACzC;AAEA,WAAO,MAAM;AAAA,EACf,GAAG,CAAC,MAAM,KAAK,CAAC;AAEhB,MAAI,CAAC,MAAM,OAAO;AAChB,WAAO;AAAA,EACT;AAEA,SAAO,oBAAC,QAAK,OAAO,OAAO,WAAY,iBAAM;AAC/C,CAAC;AAED,SAAS,aAAa,OAAiB;AACrC,QAAM,cAAc,MAAM,WAAW,OAAO;AAE5C,SAAO,WAAW,OAAO;AAAA,IACvB,WAAW;AAAA,MACT,iBAAiB,YAAY;AAAA,MAC7B,mBAAmB,YAAY;AAAA,MAC/B,YAAY;AAAA,IACd;AAAA,EACF,CAAC;AACH;","names":["FlyoutTitle"]}
@@ -0,0 +1,9 @@
1
+ import * as react from 'react';
2
+ import { PropsWithChildren } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { FlyoutProps } from '../types/flyout.types.mjs';
5
+ import '@impulse-ui-native/types';
6
+
7
+ declare const Flyout: react.MemoExoticComponent<(props: PropsWithChildren<FlyoutProps>) => react_jsx_runtime.JSX.Element | null>;
8
+
9
+ export { Flyout };
@@ -0,0 +1,277 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ Fragment,
4
+ memo,
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useRef,
9
+ useState
10
+ } from "react";
11
+ import {
12
+ Pressable,
13
+ StyleSheet,
14
+ useWindowDimensions
15
+ } from "react-native";
16
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
17
+ import Animated, {
18
+ Easing,
19
+ useAnimatedStyle,
20
+ useSharedValue,
21
+ withSpring,
22
+ withTiming
23
+ } from "react-native-reanimated";
24
+ import {
25
+ SafeAreaView,
26
+ useSafeAreaInsets
27
+ } from "react-native-safe-area-context";
28
+ import { scheduleOnRN } from "react-native-worklets";
29
+ import { View } from "@impulse-ui-native/primitives";
30
+ import { useTheme } from "@impulse-ui-native/theme";
31
+ import { FlyoutHandleHeight } from "../constants";
32
+ import { FlyoutHandle } from "./flyout-handle";
33
+ import { FlyoutTitle } from "./flyout-title";
34
+ const EnterAnimationConfig = {
35
+ damping: 50,
36
+ stiffness: 300,
37
+ mass: 1,
38
+ overshootClamping: true
39
+ };
40
+ const ExitAnimationConfig = {
41
+ duration: 150,
42
+ easing: Easing.in(Easing.quad)
43
+ };
44
+ const Flyout = memo(function Flyout2(props) {
45
+ const {
46
+ id,
47
+ children,
48
+ placement = "bottom",
49
+ layer = 0,
50
+ title,
51
+ open,
52
+ topOffset,
53
+ bottomOffset,
54
+ onCloseFinished,
55
+ onClose,
56
+ onOpen,
57
+ onOpenFinished
58
+ } = props;
59
+ const theme = useTheme();
60
+ const insets = useSafeAreaInsets();
61
+ const windowDimensions = useWindowDimensions();
62
+ const flyoutTokens = theme.components.flyout;
63
+ const placementOffset = placement === "top" ? topOffset : bottomOffset;
64
+ const screenHeight = windowDimensions.height;
65
+ const maxHeight = screenHeight * flyoutTokens.maxHeightRatio;
66
+ const zIndex = flyoutTokens.zIndexBase + layer;
67
+ const [mounted, setMounted] = useState(open);
68
+ const [measuredHeight, setMeasuredHeight] = useState(null);
69
+ const [hasMeasured, setHasMeasured] = useState(false);
70
+ const [isTouchable, setIsTouchable] = useState(false);
71
+ const didOpen = useRef(false);
72
+ const isClosing = useRef(false);
73
+ const translateY = useSharedValue(
74
+ placement === "top" ? -screenHeight : screenHeight
75
+ );
76
+ const opacity = useSharedValue(0);
77
+ const onLayout = useCallback(
78
+ (event) => {
79
+ if (measuredHeight !== null) {
80
+ return;
81
+ }
82
+ const rawHeight = event.nativeEvent.layout.height;
83
+ const clampedHeight = rawHeight + insets[placement] + FlyoutHandleHeight;
84
+ translateY.value = placement === "top" ? -clampedHeight : clampedHeight;
85
+ setMeasuredHeight(clampedHeight);
86
+ setHasMeasured(true);
87
+ },
88
+ [insets, measuredHeight, placement, translateY]
89
+ );
90
+ const animateOut = useCallback(() => {
91
+ if (measuredHeight === null || isClosing.current) {
92
+ return;
93
+ }
94
+ isClosing.current = true;
95
+ setIsTouchable(false);
96
+ onClose?.(id);
97
+ const target = placement === "top" ? -measuredHeight : measuredHeight;
98
+ translateY.value = withTiming(target, ExitAnimationConfig, (finished) => {
99
+ if (finished) {
100
+ if (onCloseFinished) {
101
+ scheduleOnRN(onCloseFinished, id);
102
+ }
103
+ scheduleOnRN(setMounted, false);
104
+ scheduleOnRN(setMeasuredHeight, null);
105
+ scheduleOnRN(setHasMeasured, false);
106
+ }
107
+ });
108
+ opacity.value = withTiming(0, ExitAnimationConfig);
109
+ didOpen.current = false;
110
+ }, [
111
+ id,
112
+ measuredHeight,
113
+ onClose,
114
+ onCloseFinished,
115
+ opacity,
116
+ placement,
117
+ translateY
118
+ ]);
119
+ const dragGesture = useMemo(
120
+ () => Gesture.Pan().enabled(hasMeasured).onUpdate((event) => {
121
+ if (placement === "bottom") {
122
+ translateY.value = Math.max(0, event.translationY);
123
+ return;
124
+ }
125
+ translateY.value = Math.min(0, event.translationY);
126
+ }).onEnd((event) => {
127
+ if (measuredHeight === null) {
128
+ return;
129
+ }
130
+ let shouldClose = false;
131
+ if (placement === "bottom") {
132
+ shouldClose = event.translationY > measuredHeight * 0.5 || event.velocityY > 1500;
133
+ }
134
+ if (placement === "top") {
135
+ shouldClose = event.translationY < -measuredHeight * 0.5 || event.velocityY < -1500;
136
+ }
137
+ if (shouldClose) {
138
+ scheduleOnRN(animateOut);
139
+ return;
140
+ }
141
+ translateY.value = withSpring(0, EnterAnimationConfig);
142
+ opacity.value = withSpring(
143
+ flyoutTokens.overlayVisibleOpacity,
144
+ EnterAnimationConfig
145
+ );
146
+ }),
147
+ [
148
+ animateOut,
149
+ flyoutTokens.overlayVisibleOpacity,
150
+ hasMeasured,
151
+ measuredHeight,
152
+ opacity,
153
+ placement,
154
+ translateY
155
+ ]
156
+ );
157
+ const animatedStyle = useAnimatedStyle(() => {
158
+ const offset = placement === "bottom" ? -(placementOffset ?? 0) : placementOffset ?? 0;
159
+ return {
160
+ transform: [{ translateY: translateY.value + offset }],
161
+ zIndex
162
+ };
163
+ });
164
+ const overlayStyle = useAnimatedStyle(() => ({
165
+ opacity: opacity.value,
166
+ zIndex,
167
+ backgroundColor: flyoutTokens.overlayColor
168
+ }));
169
+ const edges = useMemo(() => {
170
+ return [placement, "left", "right"];
171
+ }, [placement]);
172
+ const styles = useMemo(() => {
173
+ return themedStyles(theme, placement, maxHeight);
174
+ }, [theme, placement, maxHeight]);
175
+ useEffect(() => {
176
+ if (!open) {
177
+ return;
178
+ }
179
+ setMounted(true);
180
+ setIsTouchable(false);
181
+ setMeasuredHeight(null);
182
+ setHasMeasured(false);
183
+ isClosing.current = false;
184
+ didOpen.current = false;
185
+ translateY.value = placement === "top" ? -screenHeight : screenHeight;
186
+ opacity.value = 0;
187
+ }, [open, opacity, placement, screenHeight, translateY]);
188
+ useEffect(() => {
189
+ if (!open || measuredHeight === null || !hasMeasured || didOpen.current) {
190
+ return;
191
+ }
192
+ setIsTouchable(true);
193
+ onOpen?.(id);
194
+ translateY.value = withSpring(0, EnterAnimationConfig, (finished) => {
195
+ if (finished && onOpenFinished) {
196
+ scheduleOnRN(onOpenFinished, id);
197
+ }
198
+ });
199
+ opacity.value = withSpring(
200
+ flyoutTokens.overlayVisibleOpacity,
201
+ EnterAnimationConfig
202
+ );
203
+ didOpen.current = true;
204
+ }, [
205
+ flyoutTokens.overlayVisibleOpacity,
206
+ hasMeasured,
207
+ id,
208
+ measuredHeight,
209
+ onOpen,
210
+ onOpenFinished,
211
+ opacity,
212
+ open,
213
+ translateY
214
+ ]);
215
+ useEffect(() => {
216
+ if (!open && didOpen.current) {
217
+ animateOut();
218
+ }
219
+ }, [animateOut, open]);
220
+ if (!mounted) {
221
+ return null;
222
+ }
223
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
224
+ /* @__PURE__ */ jsx(
225
+ Animated.View,
226
+ {
227
+ pointerEvents: isTouchable ? "auto" : "none",
228
+ style: [StyleSheet.absoluteFill, overlayStyle],
229
+ children: /* @__PURE__ */ jsx(Pressable, { onPress: animateOut, style: StyleSheet.absoluteFill })
230
+ }
231
+ ),
232
+ /* @__PURE__ */ jsx(GestureDetector, { gesture: dragGesture, children: /* @__PURE__ */ jsx(
233
+ Animated.View,
234
+ {
235
+ onLayout,
236
+ style: [
237
+ styles.container,
238
+ !hasMeasured && styles.hidden,
239
+ animatedStyle
240
+ ],
241
+ children: /* @__PURE__ */ jsxs(SafeAreaView, { edges, children: [
242
+ placement === "bottom" ? /* @__PURE__ */ jsx(FlyoutHandle, { placement }) : null,
243
+ title ? /* @__PURE__ */ jsx(FlyoutTitle, { title }) : null,
244
+ /* @__PURE__ */ jsx(View, { style: styles.content, children }),
245
+ placement === "top" ? /* @__PURE__ */ jsx(FlyoutHandle, { placement }) : null
246
+ ] })
247
+ }
248
+ ) })
249
+ ] });
250
+ });
251
+ function themedStyles(theme, placement, maxHeight) {
252
+ const flyoutTokens = theme.components.flyout;
253
+ return StyleSheet.create({
254
+ container: {
255
+ position: "absolute",
256
+ [placement]: 0,
257
+ left: 0,
258
+ right: 0,
259
+ backgroundColor: flyoutTokens.backgroundColor,
260
+ borderTopLeftRadius: placement === "bottom" ? flyoutTokens.borderRadius : 0,
261
+ borderTopRightRadius: placement === "bottom" ? flyoutTokens.borderRadius : 0,
262
+ borderBottomLeftRadius: placement === "top" ? flyoutTokens.borderRadius : 0,
263
+ borderBottomRightRadius: placement === "top" ? flyoutTokens.borderRadius : 0
264
+ },
265
+ content: {
266
+ paddingHorizontal: flyoutTokens.contentPaddingHorizontal,
267
+ maxHeight
268
+ },
269
+ hidden: {
270
+ opacity: flyoutTokens.hiddenOpacity
271
+ }
272
+ });
273
+ }
274
+ export {
275
+ Flyout
276
+ };
277
+ //# sourceMappingURL=flyout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/flyout.tsx"],"sourcesContent":["import {\r\n Fragment,\r\n memo,\r\n PropsWithChildren,\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n} from \"react\";\r\nimport {\r\n LayoutChangeEvent,\r\n Pressable,\r\n StyleSheet,\r\n useWindowDimensions,\r\n} from \"react-native\";\r\nimport { Gesture, GestureDetector } from \"react-native-gesture-handler\";\r\nimport Animated, {\r\n Easing,\r\n useAnimatedStyle,\r\n useSharedValue,\r\n withSpring,\r\n withTiming,\r\n} from \"react-native-reanimated\";\r\nimport {\r\n Edge,\r\n SafeAreaView,\r\n useSafeAreaInsets,\r\n} from \"react-native-safe-area-context\";\r\nimport { scheduleOnRN } from \"react-native-worklets\";\r\n\r\nimport { View } from \"@impulse-ui-native/primitives\";\r\nimport { AppTheme, useTheme } from \"@impulse-ui-native/theme\";\r\n\r\nimport { FlyoutHandleHeight } from \"../constants\";\r\nimport { FlyoutProps } from \"../types\";\r\nimport { FlyoutHandle } from \"./flyout-handle\";\r\nimport { FlyoutTitle } from \"./flyout-title\";\r\n\r\nconst EnterAnimationConfig = {\r\n damping: 50,\r\n stiffness: 300,\r\n mass: 1,\r\n overshootClamping: true,\r\n};\r\n\r\nconst ExitAnimationConfig = {\r\n duration: 150,\r\n easing: Easing.in(Easing.quad),\r\n};\r\n\r\nexport const Flyout = memo(function Flyout(\r\n props: PropsWithChildren<FlyoutProps>,\r\n) {\r\n const {\r\n id,\r\n children,\r\n placement = \"bottom\",\r\n layer = 0,\r\n title,\r\n open,\r\n topOffset,\r\n bottomOffset,\r\n onCloseFinished,\r\n onClose,\r\n onOpen,\r\n onOpenFinished,\r\n } = props;\r\n\r\n const theme = useTheme();\r\n const insets = useSafeAreaInsets();\r\n\r\n const windowDimensions = useWindowDimensions();\r\n\r\n const flyoutTokens = theme.components.flyout;\r\n\r\n const placementOffset = placement === \"top\" ? topOffset : bottomOffset;\r\n const screenHeight = windowDimensions.height;\r\n const maxHeight = screenHeight * flyoutTokens.maxHeightRatio;\r\n const zIndex = flyoutTokens.zIndexBase + layer;\r\n\r\n const [mounted, setMounted] = useState(open);\r\n const [measuredHeight, setMeasuredHeight] = useState<number | null>(null);\r\n const [hasMeasured, setHasMeasured] = useState(false);\r\n const [isTouchable, setIsTouchable] = useState(false);\r\n\r\n const didOpen = useRef(false);\r\n const isClosing = useRef(false);\r\n\r\n const translateY = useSharedValue(\r\n placement === \"top\" ? -screenHeight : screenHeight,\r\n );\r\n const opacity = useSharedValue(0);\r\n\r\n const onLayout = useCallback(\r\n (event: LayoutChangeEvent) => {\r\n if (measuredHeight !== null) {\r\n return;\r\n }\r\n\r\n const rawHeight = event.nativeEvent.layout.height;\r\n const clampedHeight = rawHeight + insets[placement] + FlyoutHandleHeight;\r\n\r\n translateY.value = placement === \"top\" ? -clampedHeight : clampedHeight;\r\n\r\n setMeasuredHeight(clampedHeight);\r\n setHasMeasured(true);\r\n },\r\n [insets, measuredHeight, placement, translateY],\r\n );\r\n\r\n const animateOut = useCallback(() => {\r\n if (measuredHeight === null || isClosing.current) {\r\n return;\r\n }\r\n\r\n isClosing.current = true;\r\n setIsTouchable(false);\r\n onClose?.(id);\r\n\r\n const target = placement === \"top\" ? -measuredHeight : measuredHeight;\r\n\r\n translateY.value = withTiming(target, ExitAnimationConfig, (finished) => {\r\n if (finished) {\r\n if (onCloseFinished) {\r\n scheduleOnRN(onCloseFinished, id);\r\n }\r\n\r\n scheduleOnRN(setMounted, false);\r\n scheduleOnRN(setMeasuredHeight, null);\r\n scheduleOnRN(setHasMeasured, false);\r\n }\r\n });\r\n\r\n opacity.value = withTiming(0, ExitAnimationConfig);\r\n didOpen.current = false;\r\n }, [\r\n id,\r\n measuredHeight,\r\n onClose,\r\n onCloseFinished,\r\n opacity,\r\n placement,\r\n translateY,\r\n ]);\r\n\r\n const dragGesture = useMemo(\r\n () =>\r\n Gesture.Pan()\r\n .enabled(hasMeasured)\r\n .onUpdate((event) => {\r\n if (placement === \"bottom\") {\r\n translateY.value = Math.max(0, event.translationY);\r\n return;\r\n }\r\n\r\n translateY.value = Math.min(0, event.translationY);\r\n })\r\n .onEnd((event) => {\r\n if (measuredHeight === null) {\r\n return;\r\n }\r\n\r\n let shouldClose = false;\r\n\r\n if (placement === \"bottom\") {\r\n shouldClose =\r\n event.translationY > measuredHeight * 0.5 ||\r\n event.velocityY > 1500;\r\n }\r\n\r\n if (placement === \"top\") {\r\n shouldClose =\r\n event.translationY < -measuredHeight * 0.5 ||\r\n event.velocityY < -1500;\r\n }\r\n\r\n if (shouldClose) {\r\n scheduleOnRN(animateOut);\r\n return;\r\n }\r\n\r\n translateY.value = withSpring(0, EnterAnimationConfig);\r\n opacity.value = withSpring(\r\n flyoutTokens.overlayVisibleOpacity,\r\n EnterAnimationConfig,\r\n );\r\n }),\r\n [\r\n animateOut,\r\n flyoutTokens.overlayVisibleOpacity,\r\n hasMeasured,\r\n measuredHeight,\r\n opacity,\r\n placement,\r\n translateY,\r\n ],\r\n );\r\n\r\n const animatedStyle = useAnimatedStyle(() => {\r\n const offset =\r\n placement === \"bottom\" ? -(placementOffset ?? 0) : (placementOffset ?? 0);\r\n\r\n return {\r\n transform: [{ translateY: translateY.value + offset }],\r\n zIndex,\r\n };\r\n });\r\n\r\n const overlayStyle = useAnimatedStyle(() => ({\r\n opacity: opacity.value,\r\n zIndex,\r\n backgroundColor: flyoutTokens.overlayColor,\r\n }));\r\n\r\n const edges = useMemo<Edge[]>(() => {\r\n return [placement, \"left\", \"right\"];\r\n }, [placement]);\r\n\r\n const styles = useMemo(() => {\r\n return themedStyles(theme, placement, maxHeight);\r\n }, [theme, placement, maxHeight]);\r\n\r\n useEffect(() => {\r\n if (!open) {\r\n return;\r\n }\r\n\r\n setMounted(true);\r\n setIsTouchable(false);\r\n setMeasuredHeight(null);\r\n setHasMeasured(false);\r\n\r\n isClosing.current = false;\r\n didOpen.current = false;\r\n\r\n translateY.value = placement === \"top\" ? -screenHeight : screenHeight;\r\n opacity.value = 0;\r\n }, [open, opacity, placement, screenHeight, translateY]);\r\n\r\n useEffect(() => {\r\n if (!open || measuredHeight === null || !hasMeasured || didOpen.current) {\r\n return;\r\n }\r\n\r\n setIsTouchable(true);\r\n onOpen?.(id);\r\n\r\n translateY.value = withSpring(0, EnterAnimationConfig, (finished) => {\r\n if (finished && onOpenFinished) {\r\n scheduleOnRN(onOpenFinished, id);\r\n }\r\n });\r\n\r\n opacity.value = withSpring(\r\n flyoutTokens.overlayVisibleOpacity,\r\n EnterAnimationConfig,\r\n );\r\n\r\n didOpen.current = true;\r\n }, [\r\n flyoutTokens.overlayVisibleOpacity,\r\n hasMeasured,\r\n id,\r\n measuredHeight,\r\n onOpen,\r\n onOpenFinished,\r\n opacity,\r\n open,\r\n translateY,\r\n ]);\r\n\r\n useEffect(() => {\r\n if (!open && didOpen.current) {\r\n animateOut();\r\n }\r\n }, [animateOut, open]);\r\n\r\n if (!mounted) {\r\n return null;\r\n }\r\n\r\n return (\r\n <Fragment>\r\n <Animated.View\r\n pointerEvents={isTouchable ? \"auto\" : \"none\"}\r\n style={[StyleSheet.absoluteFill, overlayStyle]}\r\n >\r\n <Pressable onPress={animateOut} style={StyleSheet.absoluteFill} />\r\n </Animated.View>\r\n\r\n <GestureDetector gesture={dragGesture}>\r\n <Animated.View\r\n onLayout={onLayout}\r\n style={[\r\n styles.container,\r\n !hasMeasured && styles.hidden,\r\n animatedStyle,\r\n ]}\r\n >\r\n <SafeAreaView edges={edges}>\r\n {placement === \"bottom\" ? (\r\n <FlyoutHandle placement={placement} />\r\n ) : null}\r\n\r\n {title ? <FlyoutTitle title={title} /> : null}\r\n\r\n <View style={styles.content}>{children}</View>\r\n\r\n {placement === \"top\" ? (\r\n <FlyoutHandle placement={placement} />\r\n ) : null}\r\n </SafeAreaView>\r\n </Animated.View>\r\n </GestureDetector>\r\n </Fragment>\r\n );\r\n});\r\n\r\nfunction themedStyles(\r\n theme: AppTheme,\r\n placement: \"top\" | \"bottom\",\r\n maxHeight: number,\r\n) {\r\n const flyoutTokens = theme.components.flyout;\r\n\r\n return StyleSheet.create({\r\n container: {\r\n position: \"absolute\",\r\n [placement]: 0,\r\n left: 0,\r\n right: 0,\r\n backgroundColor: flyoutTokens.backgroundColor,\r\n borderTopLeftRadius:\r\n placement === \"bottom\" ? flyoutTokens.borderRadius : 0,\r\n borderTopRightRadius:\r\n placement === \"bottom\" ? flyoutTokens.borderRadius : 0,\r\n borderBottomLeftRadius:\r\n placement === \"top\" ? flyoutTokens.borderRadius : 0,\r\n borderBottomRightRadius:\r\n placement === \"top\" ? flyoutTokens.borderRadius : 0,\r\n },\r\n content: {\r\n paddingHorizontal: flyoutTokens.contentPaddingHorizontal,\r\n maxHeight,\r\n },\r\n hidden: {\r\n opacity: flyoutTokens.hiddenOpacity,\r\n },\r\n });\r\n}\r\n"],"mappings":"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","names":["Flyout"]}
@@ -0,0 +1,5 @@
1
+ export { Flyout } from './flyout.mjs';
2
+ import 'react';
3
+ import 'react/jsx-runtime';
4
+ import '../types/flyout.types.mjs';
5
+ import '@impulse-ui-native/types';
@@ -0,0 +1,5 @@
1
+ import { Flyout } from "./flyout";
2
+ export {
3
+ Flyout
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/index.ts"],"sourcesContent":["export { Flyout } from \"./flyout\";\r\n"],"mappings":"AAAA,SAAS,cAAc;","names":[]}
@@ -0,0 +1,3 @@
1
+ declare const FlyoutHandleHeight = 32;
2
+
3
+ export { FlyoutHandleHeight };
@@ -0,0 +1,5 @@
1
+ const FlyoutHandleHeight = 32;
2
+ export {
3
+ FlyoutHandleHeight
4
+ };
5
+ //# sourceMappingURL=flyout-handle.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants/flyout-handle.constants.ts"],"sourcesContent":["export const FlyoutHandleHeight = 32;\r\n"],"mappings":"AAAO,MAAM,qBAAqB;","names":[]}
@@ -0,0 +1 @@
1
+ export { FlyoutHandleHeight } from './flyout-handle.constants.mjs';
@@ -0,0 +1,2 @@
1
+ export * from "./flyout-handle.constants";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants/index.ts"],"sourcesContent":["export * from \"./flyout-handle.constants\";\r\n"],"mappings":"AAAA,cAAc;","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,14 +1,5 @@
1
- import * as react from 'react';
2
- import { PropsWithChildren } from 'react';
3
- import { LayerComponentBaseProps } from '@impulse-ui-native/types';
4
-
5
- interface FlyoutProps extends LayerComponentBaseProps {
6
- open?: boolean;
7
- placement?: "top" | "bottom";
8
- topOffset?: number;
9
- bottomOffset?: number;
10
- }
11
-
12
- declare const Flyout: react.NamedExoticComponent<PropsWithChildren<FlyoutProps>>;
13
-
14
- export { Flyout, type FlyoutProps };
1
+ export { Flyout } from './components/flyout.mjs';
2
+ export { FlyoutProps } from './types/flyout.types.mjs';
3
+ import 'react';
4
+ import 'react/jsx-runtime';
5
+ import '@impulse-ui-native/types';
package/dist/index.js CHANGED
@@ -1,343 +1,3 @@
1
- // src/components/flyout.tsx
2
- import {
3
- Fragment,
4
- memo as memo3,
5
- useCallback,
6
- useEffect,
7
- useMemo as useMemo2,
8
- useRef,
9
- useState
10
- } from "react";
11
- import {
12
- Pressable,
13
- StyleSheet as StyleSheet3,
14
- useWindowDimensions
15
- } from "react-native";
16
- import { Gesture, GestureDetector } from "react-native-gesture-handler";
17
- import Animated2, {
18
- Easing,
19
- useAnimatedStyle,
20
- useSharedValue,
21
- withSpring,
22
- withTiming
23
- } from "react-native-reanimated";
24
- import {
25
- SafeAreaView,
26
- useSafeAreaInsets
27
- } from "react-native-safe-area-context";
28
- import { scheduleOnRN } from "react-native-worklets";
29
- import { View as View3 } from "@impulse-ui-native/primitives";
30
- import { useTheme } from "@impulse-ui-native/theme";
31
-
32
- // src/constants/flyout-handle.constants.ts
33
- var FlyoutHandleHeight = 32;
34
-
35
- // src/components/flyout-handle.tsx
36
- import { memo } from "react";
37
- import { StyleSheet } from "react-native";
38
- import Animated from "react-native-reanimated";
39
- import { View } from "@impulse-ui-native/primitives";
40
- import { useThemedStyles } from "@impulse-ui-native/theme";
41
- import { jsx } from "react/jsx-runtime";
42
- var FlyoutHandle = memo(function FlyoutHandle2(props) {
43
- const styles = useThemedStyles(themedStyles, props, [props.placement]);
44
- return /* @__PURE__ */ jsx(Animated.View, { style: styles.flyoutHandleContainer, children: /* @__PURE__ */ jsx(View, { style: styles.flyoutHandle }) });
45
- });
46
- function themedStyles(theme, { placement }) {
47
- const handleTokens = theme.components.flyout.handle;
48
- return StyleSheet.create({
49
- flyoutHandle: {
50
- alignSelf: "center",
51
- backgroundColor: handleTokens.backgroundColor,
52
- width: handleTokens.width,
53
- height: handleTokens.height,
54
- borderRadius: handleTokens.borderRadius
55
- },
56
- flyoutHandleContainer: {
57
- position: "absolute",
58
- top: placement === "bottom" ? -handleTokens.containerHeight : void 0,
59
- bottom: placement === "top" ? -handleTokens.containerHeight : void 0,
60
- width: "100%",
61
- justifyContent: "center",
62
- alignItems: "center",
63
- height: handleTokens.containerHeight
64
- }
65
- });
66
- }
67
-
68
- // src/components/flyout-title.tsx
69
- import { memo as memo2, useMemo } from "react";
70
- import { StyleSheet as StyleSheet2 } from "react-native";
71
- import { Typography, View as View2 } from "@impulse-ui-native/primitives";
72
- import { useThemedStyles as useThemedStyles2 } from "@impulse-ui-native/theme";
73
- import { jsx as jsx2 } from "react/jsx-runtime";
74
- var FlyoutTitle = memo2(function FlyoutTitle2(props) {
75
- const styles = useThemedStyles2(themedStyles2);
76
- const title = useMemo(() => {
77
- if (typeof props.title === "string") {
78
- return /* @__PURE__ */ jsx2(Typography.Title3, { children: props.title });
79
- }
80
- return props.title;
81
- }, [props.title]);
82
- if (!props.title) {
83
- return null;
84
- }
85
- return /* @__PURE__ */ jsx2(View2, { style: styles.container, children: title });
86
- });
87
- function themedStyles2(theme) {
88
- const titleTokens = theme.components.flyout.title;
89
- return StyleSheet2.create({
90
- container: {
91
- paddingVertical: titleTokens.paddingVertical,
92
- paddingHorizontal: titleTokens.paddingHorizontal,
93
- alignItems: "center"
94
- }
95
- });
96
- }
97
-
98
- // src/components/flyout.tsx
99
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
100
- var EnterAnimationConfig = {
101
- damping: 50,
102
- stiffness: 300,
103
- mass: 1,
104
- overshootClamping: true
105
- };
106
- var ExitAnimationConfig = {
107
- duration: 150,
108
- easing: Easing.in(Easing.quad)
109
- };
110
- var Flyout = memo3(function Flyout2(props) {
111
- const {
112
- id,
113
- children,
114
- placement = "bottom",
115
- layer = 0,
116
- title,
117
- open,
118
- topOffset,
119
- bottomOffset,
120
- onCloseFinished,
121
- onClose,
122
- onOpen,
123
- onOpenFinished
124
- } = props;
125
- const theme = useTheme();
126
- const insets = useSafeAreaInsets();
127
- const windowDimensions = useWindowDimensions();
128
- const flyoutTokens = theme.components.flyout;
129
- const placementOffset = placement === "top" ? topOffset : bottomOffset;
130
- const screenHeight = windowDimensions.height;
131
- const maxHeight = screenHeight * flyoutTokens.maxHeightRatio;
132
- const zIndex = flyoutTokens.zIndexBase + layer;
133
- const [mounted, setMounted] = useState(open);
134
- const [measuredHeight, setMeasuredHeight] = useState(null);
135
- const [hasMeasured, setHasMeasured] = useState(false);
136
- const [isTouchable, setIsTouchable] = useState(false);
137
- const didOpen = useRef(false);
138
- const isClosing = useRef(false);
139
- const translateY = useSharedValue(
140
- placement === "top" ? -screenHeight : screenHeight
141
- );
142
- const opacity = useSharedValue(0);
143
- const onLayout = useCallback(
144
- (event) => {
145
- if (measuredHeight !== null) {
146
- return;
147
- }
148
- const rawHeight = event.nativeEvent.layout.height;
149
- const clampedHeight = rawHeight + insets[placement] + FlyoutHandleHeight;
150
- translateY.value = placement === "top" ? -clampedHeight : clampedHeight;
151
- setMeasuredHeight(clampedHeight);
152
- setHasMeasured(true);
153
- },
154
- [insets, measuredHeight, placement, translateY]
155
- );
156
- const animateOut = useCallback(() => {
157
- if (measuredHeight === null || isClosing.current) {
158
- return;
159
- }
160
- isClosing.current = true;
161
- setIsTouchable(false);
162
- onClose?.(id);
163
- const target = placement === "top" ? -measuredHeight : measuredHeight;
164
- translateY.value = withTiming(target, ExitAnimationConfig, (finished) => {
165
- if (finished) {
166
- if (onCloseFinished) {
167
- scheduleOnRN(onCloseFinished, id);
168
- }
169
- scheduleOnRN(setMounted, false);
170
- scheduleOnRN(setMeasuredHeight, null);
171
- scheduleOnRN(setHasMeasured, false);
172
- }
173
- });
174
- opacity.value = withTiming(0, ExitAnimationConfig);
175
- didOpen.current = false;
176
- }, [
177
- id,
178
- measuredHeight,
179
- onClose,
180
- onCloseFinished,
181
- opacity,
182
- placement,
183
- translateY
184
- ]);
185
- const dragGesture = useMemo2(
186
- () => Gesture.Pan().enabled(hasMeasured).onUpdate((event) => {
187
- if (placement === "bottom") {
188
- translateY.value = Math.max(0, event.translationY);
189
- return;
190
- }
191
- translateY.value = Math.min(0, event.translationY);
192
- }).onEnd((event) => {
193
- if (measuredHeight === null) {
194
- return;
195
- }
196
- let shouldClose = false;
197
- if (placement === "bottom") {
198
- shouldClose = event.translationY > measuredHeight * 0.5 || event.velocityY > 1500;
199
- }
200
- if (placement === "top") {
201
- shouldClose = event.translationY < -measuredHeight * 0.5 || event.velocityY < -1500;
202
- }
203
- if (shouldClose) {
204
- scheduleOnRN(animateOut);
205
- return;
206
- }
207
- translateY.value = withSpring(0, EnterAnimationConfig);
208
- opacity.value = withSpring(
209
- flyoutTokens.overlayVisibleOpacity,
210
- EnterAnimationConfig
211
- );
212
- }),
213
- [
214
- animateOut,
215
- flyoutTokens.overlayVisibleOpacity,
216
- hasMeasured,
217
- measuredHeight,
218
- opacity,
219
- placement,
220
- translateY
221
- ]
222
- );
223
- const animatedStyle = useAnimatedStyle(() => {
224
- const offset = placement === "bottom" ? -(placementOffset ?? 0) : placementOffset ?? 0;
225
- return {
226
- transform: [{ translateY: translateY.value + offset }],
227
- zIndex
228
- };
229
- });
230
- const overlayStyle = useAnimatedStyle(() => ({
231
- opacity: opacity.value,
232
- zIndex,
233
- backgroundColor: flyoutTokens.overlayColor
234
- }));
235
- const edges = useMemo2(() => {
236
- return [placement, "left", "right"];
237
- }, [placement]);
238
- const styles = useMemo2(() => {
239
- return themedStyles3(theme, placement, maxHeight);
240
- }, [theme, placement, maxHeight]);
241
- useEffect(() => {
242
- if (!open) {
243
- return;
244
- }
245
- setMounted(true);
246
- setIsTouchable(false);
247
- setMeasuredHeight(null);
248
- setHasMeasured(false);
249
- isClosing.current = false;
250
- didOpen.current = false;
251
- translateY.value = placement === "top" ? -screenHeight : screenHeight;
252
- opacity.value = 0;
253
- }, [open, opacity, placement, screenHeight, translateY]);
254
- useEffect(() => {
255
- if (!open || measuredHeight === null || !hasMeasured || didOpen.current) {
256
- return;
257
- }
258
- setIsTouchable(true);
259
- onOpen?.(id);
260
- translateY.value = withSpring(0, EnterAnimationConfig, (finished) => {
261
- if (finished && onOpenFinished) {
262
- scheduleOnRN(onOpenFinished, id);
263
- }
264
- });
265
- opacity.value = withSpring(
266
- flyoutTokens.overlayVisibleOpacity,
267
- EnterAnimationConfig
268
- );
269
- didOpen.current = true;
270
- }, [
271
- flyoutTokens.overlayVisibleOpacity,
272
- hasMeasured,
273
- id,
274
- measuredHeight,
275
- onOpen,
276
- onOpenFinished,
277
- opacity,
278
- open,
279
- translateY
280
- ]);
281
- useEffect(() => {
282
- if (!open && didOpen.current) {
283
- animateOut();
284
- }
285
- }, [animateOut, open]);
286
- if (!mounted) {
287
- return null;
288
- }
289
- return /* @__PURE__ */ jsxs(Fragment, { children: [
290
- /* @__PURE__ */ jsx3(
291
- Animated2.View,
292
- {
293
- pointerEvents: isTouchable ? "auto" : "none",
294
- style: [StyleSheet3.absoluteFill, overlayStyle],
295
- children: /* @__PURE__ */ jsx3(Pressable, { onPress: animateOut, style: StyleSheet3.absoluteFill })
296
- }
297
- ),
298
- /* @__PURE__ */ jsx3(GestureDetector, { gesture: dragGesture, children: /* @__PURE__ */ jsx3(
299
- Animated2.View,
300
- {
301
- onLayout,
302
- style: [
303
- styles.container,
304
- !hasMeasured && styles.hidden,
305
- animatedStyle
306
- ],
307
- children: /* @__PURE__ */ jsxs(SafeAreaView, { edges, children: [
308
- placement === "bottom" ? /* @__PURE__ */ jsx3(FlyoutHandle, { placement }) : null,
309
- title ? /* @__PURE__ */ jsx3(FlyoutTitle, { title }) : null,
310
- /* @__PURE__ */ jsx3(View3, { style: styles.content, children }),
311
- placement === "top" ? /* @__PURE__ */ jsx3(FlyoutHandle, { placement }) : null
312
- ] })
313
- }
314
- ) })
315
- ] });
316
- });
317
- function themedStyles3(theme, placement, maxHeight) {
318
- const flyoutTokens = theme.components.flyout;
319
- return StyleSheet3.create({
320
- container: {
321
- position: "absolute",
322
- [placement]: 0,
323
- left: 0,
324
- right: 0,
325
- backgroundColor: flyoutTokens.backgroundColor,
326
- borderTopLeftRadius: placement === "bottom" ? flyoutTokens.borderRadius : 0,
327
- borderTopRightRadius: placement === "bottom" ? flyoutTokens.borderRadius : 0,
328
- borderBottomLeftRadius: placement === "top" ? flyoutTokens.borderRadius : 0,
329
- borderBottomRightRadius: placement === "top" ? flyoutTokens.borderRadius : 0
330
- },
331
- content: {
332
- paddingHorizontal: flyoutTokens.contentPaddingHorizontal,
333
- maxHeight
334
- },
335
- hidden: {
336
- opacity: flyoutTokens.hiddenOpacity
337
- }
338
- });
339
- }
340
- export {
341
- Flyout
342
- };
1
+ export * from "./components";
2
+ export * from "./types";
343
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/flyout.tsx","../src/constants/flyout-handle.constants.ts","../src/components/flyout-handle.tsx","../src/components/flyout-title.tsx"],"sourcesContent":["import {\r\n Fragment,\r\n memo,\r\n PropsWithChildren,\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n} from \"react\";\r\nimport {\r\n LayoutChangeEvent,\r\n Pressable,\r\n StyleSheet,\r\n useWindowDimensions,\r\n} from \"react-native\";\r\nimport { Gesture, GestureDetector } from \"react-native-gesture-handler\";\r\nimport Animated, {\r\n Easing,\r\n useAnimatedStyle,\r\n useSharedValue,\r\n withSpring,\r\n withTiming,\r\n} from \"react-native-reanimated\";\r\nimport {\r\n Edge,\r\n SafeAreaView,\r\n useSafeAreaInsets,\r\n} from \"react-native-safe-area-context\";\r\nimport { scheduleOnRN } from \"react-native-worklets\";\r\n\r\nimport { View } from \"@impulse-ui-native/primitives\";\r\nimport { AppTheme, useTheme } from \"@impulse-ui-native/theme\";\r\n\r\nimport { FlyoutHandleHeight } from \"../constants\";\r\nimport { FlyoutProps } from \"../types\";\r\nimport { FlyoutHandle } from \"./flyout-handle\";\r\nimport { FlyoutTitle } from \"./flyout-title\";\r\n\r\nconst EnterAnimationConfig = {\r\n damping: 50,\r\n stiffness: 300,\r\n mass: 1,\r\n overshootClamping: true,\r\n};\r\n\r\nconst ExitAnimationConfig = {\r\n duration: 150,\r\n easing: Easing.in(Easing.quad),\r\n};\r\n\r\nexport const Flyout = memo(function Flyout(\r\n props: PropsWithChildren<FlyoutProps>,\r\n) {\r\n const {\r\n id,\r\n children,\r\n placement = \"bottom\",\r\n layer = 0,\r\n title,\r\n open,\r\n topOffset,\r\n bottomOffset,\r\n onCloseFinished,\r\n onClose,\r\n onOpen,\r\n onOpenFinished,\r\n } = props;\r\n\r\n const theme = useTheme();\r\n const insets = useSafeAreaInsets();\r\n\r\n const windowDimensions = useWindowDimensions();\r\n\r\n const flyoutTokens = theme.components.flyout;\r\n\r\n const placementOffset = placement === \"top\" ? topOffset : bottomOffset;\r\n const screenHeight = windowDimensions.height;\r\n const maxHeight = screenHeight * flyoutTokens.maxHeightRatio;\r\n const zIndex = flyoutTokens.zIndexBase + layer;\r\n\r\n const [mounted, setMounted] = useState(open);\r\n const [measuredHeight, setMeasuredHeight] = useState<number | null>(null);\r\n const [hasMeasured, setHasMeasured] = useState(false);\r\n const [isTouchable, setIsTouchable] = useState(false);\r\n\r\n const didOpen = useRef(false);\r\n const isClosing = useRef(false);\r\n\r\n const translateY = useSharedValue(\r\n placement === \"top\" ? -screenHeight : screenHeight,\r\n );\r\n const opacity = useSharedValue(0);\r\n\r\n const onLayout = useCallback(\r\n (event: LayoutChangeEvent) => {\r\n if (measuredHeight !== null) {\r\n return;\r\n }\r\n\r\n const rawHeight = event.nativeEvent.layout.height;\r\n const clampedHeight = rawHeight + insets[placement] + FlyoutHandleHeight;\r\n\r\n translateY.value = placement === \"top\" ? -clampedHeight : clampedHeight;\r\n\r\n setMeasuredHeight(clampedHeight);\r\n setHasMeasured(true);\r\n },\r\n [insets, measuredHeight, placement, translateY],\r\n );\r\n\r\n const animateOut = useCallback(() => {\r\n if (measuredHeight === null || isClosing.current) {\r\n return;\r\n }\r\n\r\n isClosing.current = true;\r\n setIsTouchable(false);\r\n onClose?.(id);\r\n\r\n const target = placement === \"top\" ? -measuredHeight : measuredHeight;\r\n\r\n translateY.value = withTiming(target, ExitAnimationConfig, (finished) => {\r\n if (finished) {\r\n if (onCloseFinished) {\r\n scheduleOnRN(onCloseFinished, id);\r\n }\r\n\r\n scheduleOnRN(setMounted, false);\r\n scheduleOnRN(setMeasuredHeight, null);\r\n scheduleOnRN(setHasMeasured, false);\r\n }\r\n });\r\n\r\n opacity.value = withTiming(0, ExitAnimationConfig);\r\n didOpen.current = false;\r\n }, [\r\n id,\r\n measuredHeight,\r\n onClose,\r\n onCloseFinished,\r\n opacity,\r\n placement,\r\n translateY,\r\n ]);\r\n\r\n const dragGesture = useMemo(\r\n () =>\r\n Gesture.Pan()\r\n .enabled(hasMeasured)\r\n .onUpdate((event) => {\r\n if (placement === \"bottom\") {\r\n translateY.value = Math.max(0, event.translationY);\r\n return;\r\n }\r\n\r\n translateY.value = Math.min(0, event.translationY);\r\n })\r\n .onEnd((event) => {\r\n if (measuredHeight === null) {\r\n return;\r\n }\r\n\r\n let shouldClose = false;\r\n\r\n if (placement === \"bottom\") {\r\n shouldClose =\r\n event.translationY > measuredHeight * 0.5 ||\r\n event.velocityY > 1500;\r\n }\r\n\r\n if (placement === \"top\") {\r\n shouldClose =\r\n event.translationY < -measuredHeight * 0.5 ||\r\n event.velocityY < -1500;\r\n }\r\n\r\n if (shouldClose) {\r\n scheduleOnRN(animateOut);\r\n return;\r\n }\r\n\r\n translateY.value = withSpring(0, EnterAnimationConfig);\r\n opacity.value = withSpring(\r\n flyoutTokens.overlayVisibleOpacity,\r\n EnterAnimationConfig,\r\n );\r\n }),\r\n [\r\n animateOut,\r\n flyoutTokens.overlayVisibleOpacity,\r\n hasMeasured,\r\n measuredHeight,\r\n opacity,\r\n placement,\r\n translateY,\r\n ],\r\n );\r\n\r\n const animatedStyle = useAnimatedStyle(() => {\r\n const offset =\r\n placement === \"bottom\" ? -(placementOffset ?? 0) : (placementOffset ?? 0);\r\n\r\n return {\r\n transform: [{ translateY: translateY.value + offset }],\r\n zIndex,\r\n };\r\n });\r\n\r\n const overlayStyle = useAnimatedStyle(() => ({\r\n opacity: opacity.value,\r\n zIndex,\r\n backgroundColor: flyoutTokens.overlayColor,\r\n }));\r\n\r\n const edges = useMemo<Edge[]>(() => {\r\n return [placement, \"left\", \"right\"];\r\n }, [placement]);\r\n\r\n const styles = useMemo(() => {\r\n return themedStyles(theme, placement, maxHeight);\r\n }, [theme, placement, maxHeight]);\r\n\r\n useEffect(() => {\r\n if (!open) {\r\n return;\r\n }\r\n\r\n setMounted(true);\r\n setIsTouchable(false);\r\n setMeasuredHeight(null);\r\n setHasMeasured(false);\r\n\r\n isClosing.current = false;\r\n didOpen.current = false;\r\n\r\n translateY.value = placement === \"top\" ? -screenHeight : screenHeight;\r\n opacity.value = 0;\r\n }, [open, opacity, placement, screenHeight, translateY]);\r\n\r\n useEffect(() => {\r\n if (!open || measuredHeight === null || !hasMeasured || didOpen.current) {\r\n return;\r\n }\r\n\r\n setIsTouchable(true);\r\n onOpen?.(id);\r\n\r\n translateY.value = withSpring(0, EnterAnimationConfig, (finished) => {\r\n if (finished && onOpenFinished) {\r\n scheduleOnRN(onOpenFinished, id);\r\n }\r\n });\r\n\r\n opacity.value = withSpring(\r\n flyoutTokens.overlayVisibleOpacity,\r\n EnterAnimationConfig,\r\n );\r\n\r\n didOpen.current = true;\r\n }, [\r\n flyoutTokens.overlayVisibleOpacity,\r\n hasMeasured,\r\n id,\r\n measuredHeight,\r\n onOpen,\r\n onOpenFinished,\r\n opacity,\r\n open,\r\n translateY,\r\n ]);\r\n\r\n useEffect(() => {\r\n if (!open && didOpen.current) {\r\n animateOut();\r\n }\r\n }, [animateOut, open]);\r\n\r\n if (!mounted) {\r\n return null;\r\n }\r\n\r\n return (\r\n <Fragment>\r\n <Animated.View\r\n pointerEvents={isTouchable ? \"auto\" : \"none\"}\r\n style={[StyleSheet.absoluteFill, overlayStyle]}\r\n >\r\n <Pressable onPress={animateOut} style={StyleSheet.absoluteFill} />\r\n </Animated.View>\r\n\r\n <GestureDetector gesture={dragGesture}>\r\n <Animated.View\r\n onLayout={onLayout}\r\n style={[\r\n styles.container,\r\n !hasMeasured && styles.hidden,\r\n animatedStyle,\r\n ]}\r\n >\r\n <SafeAreaView edges={edges}>\r\n {placement === \"bottom\" ? (\r\n <FlyoutHandle placement={placement} />\r\n ) : null}\r\n\r\n {title ? <FlyoutTitle title={title} /> : null}\r\n\r\n <View style={styles.content}>{children}</View>\r\n\r\n {placement === \"top\" ? (\r\n <FlyoutHandle placement={placement} />\r\n ) : null}\r\n </SafeAreaView>\r\n </Animated.View>\r\n </GestureDetector>\r\n </Fragment>\r\n );\r\n});\r\n\r\nfunction themedStyles(\r\n theme: AppTheme,\r\n placement: \"top\" | \"bottom\",\r\n maxHeight: number,\r\n) {\r\n const flyoutTokens = theme.components.flyout;\r\n\r\n return StyleSheet.create({\r\n container: {\r\n position: \"absolute\",\r\n [placement]: 0,\r\n left: 0,\r\n right: 0,\r\n backgroundColor: flyoutTokens.backgroundColor,\r\n borderTopLeftRadius:\r\n placement === \"bottom\" ? flyoutTokens.borderRadius : 0,\r\n borderTopRightRadius:\r\n placement === \"bottom\" ? flyoutTokens.borderRadius : 0,\r\n borderBottomLeftRadius:\r\n placement === \"top\" ? flyoutTokens.borderRadius : 0,\r\n borderBottomRightRadius:\r\n placement === \"top\" ? flyoutTokens.borderRadius : 0,\r\n },\r\n content: {\r\n paddingHorizontal: flyoutTokens.contentPaddingHorizontal,\r\n maxHeight,\r\n },\r\n hidden: {\r\n opacity: flyoutTokens.hiddenOpacity,\r\n },\r\n });\r\n}\r\n","export const FlyoutHandleHeight = 32;\r\n","import { memo } from \"react\";\r\nimport { StyleSheet } from \"react-native\";\r\nimport Animated from \"react-native-reanimated\";\r\n\r\nimport { View } from \"@impulse-ui-native/primitives\";\r\nimport { AppTheme, useThemedStyles } from \"@impulse-ui-native/theme\";\r\n\r\ninterface FlyoutHandleProps {\r\n placement: \"top\" | \"bottom\";\r\n}\r\n\r\nexport const FlyoutHandle = memo(function FlyoutHandle(\r\n props: FlyoutHandleProps,\r\n) {\r\n const styles = useThemedStyles(themedStyles, props, [props.placement]);\r\n\r\n return (\r\n <Animated.View style={styles.flyoutHandleContainer}>\r\n <View style={styles.flyoutHandle} />\r\n </Animated.View>\r\n );\r\n});\r\n\r\nfunction themedStyles(\r\n theme: AppTheme,\r\n { placement }: { placement: \"top\" | \"bottom\" },\r\n) {\r\n const handleTokens = theme.components.flyout.handle;\r\n\r\n return StyleSheet.create({\r\n flyoutHandle: {\r\n alignSelf: \"center\",\r\n backgroundColor: handleTokens.backgroundColor,\r\n width: handleTokens.width,\r\n height: handleTokens.height,\r\n borderRadius: handleTokens.borderRadius,\r\n },\r\n flyoutHandleContainer: {\r\n position: \"absolute\",\r\n top: placement === \"bottom\" ? -handleTokens.containerHeight : undefined,\r\n bottom: placement === \"top\" ? -handleTokens.containerHeight : undefined,\r\n width: \"100%\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n height: handleTokens.containerHeight,\r\n },\r\n });\r\n}\r\n","import { memo, ReactNode, useMemo } from \"react\";\r\nimport { StyleSheet } from \"react-native\";\r\n\r\nimport { Typography, View } from \"@impulse-ui-native/primitives\";\r\nimport { AppTheme, useThemedStyles } from \"@impulse-ui-native/theme\";\r\n\r\ninterface FlyoutTitleProps {\r\n title?: ReactNode | string;\r\n}\r\n\r\nexport const FlyoutTitle = memo(function FlyoutTitle(props: FlyoutTitleProps) {\r\n const styles = useThemedStyles(themedStyles);\r\n\r\n const title = useMemo(() => {\r\n if (typeof props.title === \"string\") {\r\n return <Typography.Title3>{props.title}</Typography.Title3>;\r\n }\r\n\r\n return props.title;\r\n }, [props.title]);\r\n\r\n if (!props.title) {\r\n return null;\r\n }\r\n\r\n return <View style={styles.container}>{title}</View>;\r\n});\r\n\r\nfunction themedStyles(theme: AppTheme) {\r\n const titleTokens = theme.components.flyout.title;\r\n\r\n return StyleSheet.create({\r\n container: {\r\n paddingVertical: titleTokens.paddingVertical,\r\n paddingHorizontal: titleTokens.paddingHorizontal,\r\n alignItems: \"center\",\r\n },\r\n });\r\n}\r\n"],"mappings":"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","names":["memo","useMemo","StyleSheet","Animated","View","FlyoutHandle","memo","StyleSheet","View","useThemedStyles","jsx","FlyoutTitle","themedStyles","jsx","memo","Flyout","useMemo","themedStyles","Animated","StyleSheet","View"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./components\";\r\nexport * from \"./types\";\r\n"],"mappings":"AAAA,cAAc;AACd,cAAc;","names":[]}
@@ -0,0 +1,10 @@
1
+ import { LayerComponentBaseProps } from '@impulse-ui-native/types';
2
+
3
+ interface FlyoutProps extends LayerComponentBaseProps {
4
+ open?: boolean;
5
+ placement?: "top" | "bottom";
6
+ topOffset?: number;
7
+ bottomOffset?: number;
8
+ }
9
+
10
+ export type { FlyoutProps };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=flyout.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ export { FlyoutProps } from './flyout.types.mjs';
2
+ import '@impulse-ui-native/types';
@@ -0,0 +1,2 @@
1
+ export * from "./flyout.types";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["export * from \"./flyout.types\";\r\n"],"mappings":"AAAA,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@impulse-ui-native/flyout",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "source": "src/index.ts",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.mts",
@@ -17,26 +17,27 @@
17
17
  "README.md",
18
18
  "LICENSE"
19
19
  ],
20
+ "sideEffects": false,
20
21
  "dependencies": {
21
- "@impulse-ui-native/core": "2.0.1",
22
- "@impulse-ui-native/types": "2.0.1",
23
- "@impulse-ui-native/theme": "2.0.1",
24
- "@impulse-ui-native/primitives": "2.0.1"
22
+ "@impulse-ui-native/theme": "2.1.1",
23
+ "@impulse-ui-native/core": "2.1.1",
24
+ "@impulse-ui-native/types": "2.1.1",
25
+ "@impulse-ui-native/primitives": "2.1.1"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@types/react": "~19.2.14",
28
29
  "eslint": "^9.38.0",
29
30
  "prettier": "^3.6.2",
30
- "react": "19.2.0",
31
- "react-native": "0.83.6",
32
- "react-native-gesture-handler": "~2.30.1",
33
- "react-native-reanimated": "4.2.1",
34
- "react-native-safe-area-context": "~5.6.0",
35
- "react-native-worklets": "0.7.4",
36
- "typescript": "~5.9.2",
37
- "@impulse-ui-native/prettier-config": "1.0.0",
31
+ "react": "19.2.3",
32
+ "react-native": "0.86.2",
33
+ "react-native-gesture-handler": "~2.32.0",
34
+ "react-native-reanimated": "4.5.1",
35
+ "react-native-safe-area-context": "~5.7.0",
36
+ "react-native-worklets": "0.10.1",
37
+ "typescript": "~6.0.3",
38
38
  "@impulse-ui-native/eslint-config": "1.0.0",
39
- "@impulse-ui-native/tsconfig": "1.0.0"
39
+ "@impulse-ui-native/tsconfig": "1.0.0",
40
+ "@impulse-ui-native/prettier-config": "1.0.0"
40
41
  },
41
42
  "peerDependencies": {
42
43
  "react": ">=18",