related-ui-components 4.0.3 → 4.0.4
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/lib/module/contexts/UniversalModalProvider.js +218 -0
- package/lib/module/contexts/UniversalModalProvider.js.map +1 -0
- package/lib/module/contexts/index.js +1 -0
- package/lib/module/contexts/index.js.map +1 -1
- package/lib/typescript/src/contexts/UniversalModalProvider.d.ts +43 -0
- package/lib/typescript/src/contexts/UniversalModalProvider.d.ts.map +1 -0
- package/lib/typescript/src/contexts/index.d.ts +1 -0
- package/lib/typescript/src/contexts/index.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/contexts/UniversalModalProvider.tsx +293 -0
- package/src/contexts/index.ts +2 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Portal, PortalHost, PortalProvider } from "@gorhom/portal";
|
|
4
|
+
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { BackHandler, Platform, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
6
|
+
import { KeyboardAvoidingView } from "react-native-keyboard-controller";
|
|
7
|
+
import Animated, { Easing, runOnJS, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
|
8
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
9
|
+
|
|
10
|
+
// --- Types ---
|
|
11
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
// --- Context ---
|
|
13
|
+
|
|
14
|
+
const ModalContext = /*#__PURE__*/createContext(undefined);
|
|
15
|
+
export const DEFAULT_MODAL_HOST = "UniversalModalHost";
|
|
16
|
+
|
|
17
|
+
// --- Default Modal UI Component ---
|
|
18
|
+
const DefaultModalUI = ({
|
|
19
|
+
isVisible,
|
|
20
|
+
children,
|
|
21
|
+
options,
|
|
22
|
+
onClose
|
|
23
|
+
}) => {
|
|
24
|
+
const insets = useSafeAreaInsets();
|
|
25
|
+
const [isRendered, setIsRendered] = useState(isVisible);
|
|
26
|
+
const opacity = useSharedValue(0);
|
|
27
|
+
const {
|
|
28
|
+
closeOnBackdropPress = true,
|
|
29
|
+
closeOnBackButton = true,
|
|
30
|
+
animationConfig = {
|
|
31
|
+
duration: 300,
|
|
32
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
|
|
33
|
+
},
|
|
34
|
+
position = "center",
|
|
35
|
+
avoidKeyboard = true,
|
|
36
|
+
backdropStyle,
|
|
37
|
+
containerStyle
|
|
38
|
+
} = options;
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (isVisible) setIsRendered(true);
|
|
41
|
+
opacity.value = withTiming(isVisible ? 1 : 0, animationConfig, finished => {
|
|
42
|
+
if (finished && !isVisible) {
|
|
43
|
+
runOnJS(setIsRendered)(false);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}, [isVisible, animationConfig]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
|
|
49
|
+
if (isVisible && closeOnBackButton) {
|
|
50
|
+
onClose();
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
});
|
|
55
|
+
return () => backHandler.remove();
|
|
56
|
+
}, [isVisible, closeOnBackButton, onClose]);
|
|
57
|
+
const backdropAnimatedStyle = useAnimatedStyle(() => ({
|
|
58
|
+
opacity: opacity.value
|
|
59
|
+
}));
|
|
60
|
+
const contentAnimatedStyle = useAnimatedStyle(() => {
|
|
61
|
+
// Simple slide/fade effect based on position
|
|
62
|
+
let transform = [];
|
|
63
|
+
if (position === "bottom") {
|
|
64
|
+
transform.push({
|
|
65
|
+
translateY: (1 - opacity.value) * 100
|
|
66
|
+
});
|
|
67
|
+
} else if (position === "top") {
|
|
68
|
+
transform.push({
|
|
69
|
+
translateY: (1 - opacity.value) * -100
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
transform.push({
|
|
73
|
+
scale: 0.9 + opacity.value * 0.1
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
opacity: opacity.value,
|
|
78
|
+
transform
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
if (!isRendered) return null;
|
|
82
|
+
const justifyContent = position === "bottom" ? "flex-end" : position === "top" ? "flex-start" : "center";
|
|
83
|
+
const paddingBottom = position === "bottom" ? insets.bottom : 0;
|
|
84
|
+
const paddingTop = position === "top" ? insets.top : 0;
|
|
85
|
+
const ContentWrapper = avoidKeyboard ? KeyboardAvoidingView : View;
|
|
86
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
87
|
+
style: [StyleSheet.absoluteFill, styles.overlayWrapper, {
|
|
88
|
+
justifyContent
|
|
89
|
+
}],
|
|
90
|
+
children: [/*#__PURE__*/_jsx(Animated.View, {
|
|
91
|
+
style: [StyleSheet.absoluteFill, styles.backdrop, backdropStyle, backdropAnimatedStyle],
|
|
92
|
+
children: /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
93
|
+
style: StyleSheet.absoluteFill,
|
|
94
|
+
activeOpacity: 1,
|
|
95
|
+
onPress: closeOnBackdropPress ? onClose : undefined
|
|
96
|
+
})
|
|
97
|
+
}), /*#__PURE__*/_jsx(ContentWrapper, {
|
|
98
|
+
behavior: Platform.OS === "ios" ? "padding" : undefined,
|
|
99
|
+
style: [styles.keyboardView, {
|
|
100
|
+
paddingBottom,
|
|
101
|
+
paddingTop
|
|
102
|
+
}],
|
|
103
|
+
pointerEvents: "box-none",
|
|
104
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
105
|
+
style: [styles.baseContainer, containerStyle, contentAnimatedStyle],
|
|
106
|
+
children: children
|
|
107
|
+
})
|
|
108
|
+
})]
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// --- Provider ---
|
|
113
|
+
|
|
114
|
+
export const UniversalModalProvider = ({
|
|
115
|
+
children,
|
|
116
|
+
defaultOptions = {},
|
|
117
|
+
portalHostName = DEFAULT_MODAL_HOST,
|
|
118
|
+
CustomModalComponent
|
|
119
|
+
}) => {
|
|
120
|
+
const [state, setState] = useState({
|
|
121
|
+
isVisible: false,
|
|
122
|
+
content: null,
|
|
123
|
+
options: defaultOptions
|
|
124
|
+
});
|
|
125
|
+
const showModal = useCallback((content, options) => {
|
|
126
|
+
setState({
|
|
127
|
+
isVisible: true,
|
|
128
|
+
content,
|
|
129
|
+
options: {
|
|
130
|
+
...defaultOptions,
|
|
131
|
+
...options
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}, [defaultOptions]);
|
|
135
|
+
const hideModal = useCallback(() => {
|
|
136
|
+
setState(prev => ({
|
|
137
|
+
...prev,
|
|
138
|
+
isVisible: false
|
|
139
|
+
}));
|
|
140
|
+
// Clear content after animation normally finishes to avoid flicker
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
setState(prev => {
|
|
143
|
+
// Only clear if still hidden (prevent race condition if immediately reshown)
|
|
144
|
+
return !prev.isVisible ? {
|
|
145
|
+
...prev,
|
|
146
|
+
content: null
|
|
147
|
+
} : prev;
|
|
148
|
+
});
|
|
149
|
+
}, 350);
|
|
150
|
+
}, []);
|
|
151
|
+
const ModalUI = CustomModalComponent || DefaultModalUI;
|
|
152
|
+
const value = useMemo(() => ({
|
|
153
|
+
showModal,
|
|
154
|
+
hideModal,
|
|
155
|
+
isVisible: state.isVisible
|
|
156
|
+
}), [showModal, hideModal, state.isVisible]);
|
|
157
|
+
return /*#__PURE__*/_jsx(ModalContext.Provider, {
|
|
158
|
+
value: value,
|
|
159
|
+
children: /*#__PURE__*/_jsxs(PortalProvider, {
|
|
160
|
+
children: [children, /*#__PURE__*/_jsx(PortalHost, {
|
|
161
|
+
name: portalHostName
|
|
162
|
+
}), /*#__PURE__*/_jsx(Portal, {
|
|
163
|
+
hostName: portalHostName,
|
|
164
|
+
children: /*#__PURE__*/_jsx(ModalUI, {
|
|
165
|
+
isVisible: state.isVisible,
|
|
166
|
+
options: state.options,
|
|
167
|
+
onClose: hideModal,
|
|
168
|
+
children: state.content
|
|
169
|
+
})
|
|
170
|
+
})]
|
|
171
|
+
})
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// --- Hook ---
|
|
176
|
+
|
|
177
|
+
export const useModal = () => {
|
|
178
|
+
const context = useContext(ModalContext);
|
|
179
|
+
if (!context) {
|
|
180
|
+
throw new Error("useModal must be used within a UniversalModalProvider");
|
|
181
|
+
}
|
|
182
|
+
return context;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// --- Default Styles ---
|
|
186
|
+
|
|
187
|
+
const styles = StyleSheet.create({
|
|
188
|
+
overlayWrapper: {
|
|
189
|
+
zIndex: 1000,
|
|
190
|
+
elevation: 1000
|
|
191
|
+
},
|
|
192
|
+
backdrop: {
|
|
193
|
+
backgroundColor: "rgba(0, 0, 0, 0.6)"
|
|
194
|
+
},
|
|
195
|
+
keyboardView: {
|
|
196
|
+
flexGrow: 1,
|
|
197
|
+
justifyContent: "center",
|
|
198
|
+
alignItems: "center"
|
|
199
|
+
},
|
|
200
|
+
baseContainer: {
|
|
201
|
+
backgroundColor: "white",
|
|
202
|
+
// Default, allows override
|
|
203
|
+
borderRadius: 16,
|
|
204
|
+
padding: 20,
|
|
205
|
+
width: "90%",
|
|
206
|
+
maxWidth: 500,
|
|
207
|
+
alignItems: "center",
|
|
208
|
+
shadowColor: "#000",
|
|
209
|
+
shadowOffset: {
|
|
210
|
+
width: 0,
|
|
211
|
+
height: 2
|
|
212
|
+
},
|
|
213
|
+
shadowOpacity: 0.25,
|
|
214
|
+
shadowRadius: 3.84,
|
|
215
|
+
elevation: 5
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
//# sourceMappingURL=UniversalModalProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Portal","PortalHost","PortalProvider","React","createContext","useCallback","useContext","useEffect","useMemo","useState","BackHandler","Platform","StyleSheet","TouchableOpacity","View","KeyboardAvoidingView","Animated","Easing","runOnJS","useAnimatedStyle","useSharedValue","withTiming","useSafeAreaInsets","jsx","_jsx","jsxs","_jsxs","ModalContext","undefined","DEFAULT_MODAL_HOST","DefaultModalUI","isVisible","children","options","onClose","insets","isRendered","setIsRendered","opacity","closeOnBackdropPress","closeOnBackButton","animationConfig","duration","easing","bezier","position","avoidKeyboard","backdropStyle","containerStyle","value","finished","backHandler","addEventListener","remove","backdropAnimatedStyle","contentAnimatedStyle","transform","push","translateY","scale","justifyContent","paddingBottom","bottom","paddingTop","top","ContentWrapper","style","absoluteFill","styles","overlayWrapper","backdrop","activeOpacity","onPress","behavior","OS","keyboardView","pointerEvents","baseContainer","UniversalModalProvider","defaultOptions","portalHostName","CustomModalComponent","state","setState","content","showModal","hideModal","prev","setTimeout","ModalUI","Provider","name","hostName","useModal","context","Error","create","zIndex","elevation","backgroundColor","flexGrow","alignItems","borderRadius","padding","width","maxWidth","shadowColor","shadowOffset","height","shadowOpacity","shadowRadius"],"sourceRoot":"..\\..\\..\\src","sources":["contexts/UniversalModalProvider.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["contexts/index.ts"],"mappings":";;AAAA,cAAc,8BAA2B;AACzC,cAAc,+BAA4B","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["contexts/index.ts"],"mappings":";;AAAA,cAAc,8BAA2B;AACzC,cAAc,+BAA4B;AAC1C,cAAc,6BAA0B","ignoreList":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
3
|
+
import { WithTimingConfig } from "react-native-reanimated";
|
|
4
|
+
export interface ModalOptions {
|
|
5
|
+
/** Should the modal close when tapping the backdrop? Default: true */
|
|
6
|
+
closeOnBackdropPress?: boolean;
|
|
7
|
+
/** Should the hardware back button close the modal? Default: true */
|
|
8
|
+
closeOnBackButton?: boolean;
|
|
9
|
+
/** Animation configuration for entry/exit */
|
|
10
|
+
animationConfig?: WithTimingConfig;
|
|
11
|
+
/** Custom styles for the backdrop */
|
|
12
|
+
backdropStyle?: StyleProp<ViewStyle>;
|
|
13
|
+
/** Custom styles for the modal container (the white box) */
|
|
14
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
15
|
+
/** Position of the modal. Default: 'center' */
|
|
16
|
+
position?: "center" | "bottom" | "top";
|
|
17
|
+
/** If true, avoids keyboard view. Default: true */
|
|
18
|
+
avoidKeyboard?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface ModalContextType {
|
|
21
|
+
showModal: (content: ReactNode, options?: ModalOptions) => void;
|
|
22
|
+
hideModal: () => void;
|
|
23
|
+
isVisible: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface ModalProviderProps {
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
/** Global default options for all modals */
|
|
28
|
+
defaultOptions?: ModalOptions;
|
|
29
|
+
/** Optional: Provide a specific Portal Host name if nesting providers */
|
|
30
|
+
portalHostName?: string;
|
|
31
|
+
/** Optional: Override the internal Modal UI component completely */
|
|
32
|
+
CustomModalComponent?: React.FC<{
|
|
33
|
+
isVisible: boolean;
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
options: ModalOptions;
|
|
36
|
+
onClose: () => void;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export declare const DEFAULT_MODAL_HOST = "UniversalModalHost";
|
|
40
|
+
export declare const UniversalModalProvider: React.FC<ModalProviderProps>;
|
|
41
|
+
export declare const useModal: () => ModalContextType;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=UniversalModalProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UniversalModalProvider.d.ts","sourceRoot":"","sources":["../../../../src/contexts/UniversalModalProvider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAEZ,SAAS,EAMV,MAAM,OAAO,CAAC;AACf,OAAO,EAGL,SAAS,EAIT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAiB,EAQf,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAIjC,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,6CAA6C;IAC7C,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,qCAAqC;IACrC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,4DAA4D;IAC5D,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IACvC,mDAAmD;IACnD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,UAAU,gBAAgB;IACxB,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;IAChE,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,SAAS,CAAC;IACpB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,YAAY,CAAC;IAC9B,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,oBAAoB,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9B,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,EAAE,SAAS,CAAC;QACpB,OAAO,EAAE,YAAY,CAAC;QACtB,OAAO,EAAE,MAAM,IAAI,CAAC;KACrB,CAAC,CAAC;CACJ;AAMD,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAgHvD,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAiE/D,CAAC;AAIF,eAAO,MAAM,QAAQ,wBAMpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/contexts/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,4BAA4B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/contexts/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,0BAA0B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "related-ui-components",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"main": "./src/index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "expo start",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"react-native-reanimated": ">=3.0.0",
|
|
35
35
|
"react-native-safe-area-context": ">=5.4.0",
|
|
36
36
|
"react-native-svg": ">=15.11.2",
|
|
37
|
-
"react-native-toast-message": ">=2.3.3"
|
|
37
|
+
"react-native-toast-message": ">=2.3.3",
|
|
38
|
+
"react-native-keyboard-controller": "^1.18.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@babel/core": "^7.25.2",
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { Portal, PortalHost, PortalProvider } from "@gorhom/portal";
|
|
2
|
+
import React, {
|
|
3
|
+
createContext,
|
|
4
|
+
ReactNode,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
import {
|
|
12
|
+
BackHandler,
|
|
13
|
+
Platform,
|
|
14
|
+
StyleProp,
|
|
15
|
+
StyleSheet,
|
|
16
|
+
TouchableOpacity,
|
|
17
|
+
View,
|
|
18
|
+
ViewStyle,
|
|
19
|
+
} from "react-native";
|
|
20
|
+
import { KeyboardProvider as RNKeyboardProvider, KeyboardAvoidingView } from "react-native-keyboard-controller";
|
|
21
|
+
import Animated, {
|
|
22
|
+
Easing,
|
|
23
|
+
FadeIn,
|
|
24
|
+
FadeOut,
|
|
25
|
+
runOnJS,
|
|
26
|
+
useAnimatedStyle,
|
|
27
|
+
useSharedValue,
|
|
28
|
+
withTiming,
|
|
29
|
+
WithTimingConfig,
|
|
30
|
+
} from "react-native-reanimated";
|
|
31
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
32
|
+
|
|
33
|
+
// --- Types ---
|
|
34
|
+
export interface ModalOptions {
|
|
35
|
+
/** Should the modal close when tapping the backdrop? Default: true */
|
|
36
|
+
closeOnBackdropPress?: boolean;
|
|
37
|
+
/** Should the hardware back button close the modal? Default: true */
|
|
38
|
+
closeOnBackButton?: boolean;
|
|
39
|
+
/** Animation configuration for entry/exit */
|
|
40
|
+
animationConfig?: WithTimingConfig;
|
|
41
|
+
/** Custom styles for the backdrop */
|
|
42
|
+
backdropStyle?: StyleProp<ViewStyle>;
|
|
43
|
+
/** Custom styles for the modal container (the white box) */
|
|
44
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
45
|
+
/** Position of the modal. Default: 'center' */
|
|
46
|
+
position?: "center" | "bottom" | "top";
|
|
47
|
+
/** If true, avoids keyboard view. Default: true */
|
|
48
|
+
avoidKeyboard?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ModalContextType {
|
|
52
|
+
showModal: (content: ReactNode, options?: ModalOptions) => void;
|
|
53
|
+
hideModal: () => void;
|
|
54
|
+
isVisible: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ModalProviderProps {
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
/** Global default options for all modals */
|
|
60
|
+
defaultOptions?: ModalOptions;
|
|
61
|
+
/** Optional: Provide a specific Portal Host name if nesting providers */
|
|
62
|
+
portalHostName?: string;
|
|
63
|
+
/** Optional: Override the internal Modal UI component completely */
|
|
64
|
+
CustomModalComponent?: React.FC<{
|
|
65
|
+
isVisible: boolean;
|
|
66
|
+
children: ReactNode;
|
|
67
|
+
options: ModalOptions;
|
|
68
|
+
onClose: () => void;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- Context ---
|
|
73
|
+
|
|
74
|
+
const ModalContext = createContext<ModalContextType | undefined>(undefined);
|
|
75
|
+
|
|
76
|
+
export const DEFAULT_MODAL_HOST = "UniversalModalHost";
|
|
77
|
+
|
|
78
|
+
// --- Default Modal UI Component ---
|
|
79
|
+
const DefaultModalUI: React.FC<{
|
|
80
|
+
isVisible: boolean;
|
|
81
|
+
children: ReactNode;
|
|
82
|
+
options: ModalOptions;
|
|
83
|
+
onClose: () => void;
|
|
84
|
+
}> = ({ isVisible, children, options, onClose }) => {
|
|
85
|
+
const insets = useSafeAreaInsets();
|
|
86
|
+
const [isRendered, setIsRendered] = useState(isVisible);
|
|
87
|
+
const opacity = useSharedValue(0);
|
|
88
|
+
|
|
89
|
+
const {
|
|
90
|
+
closeOnBackdropPress = true,
|
|
91
|
+
closeOnBackButton = true,
|
|
92
|
+
animationConfig = { duration: 300, easing: Easing.bezier(0.25, 0.1, 0.25, 1) },
|
|
93
|
+
position = "center",
|
|
94
|
+
avoidKeyboard = true,
|
|
95
|
+
backdropStyle,
|
|
96
|
+
containerStyle,
|
|
97
|
+
} = options;
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (isVisible) setIsRendered(true);
|
|
101
|
+
|
|
102
|
+
opacity.value = withTiming(
|
|
103
|
+
isVisible ? 1 : 0,
|
|
104
|
+
animationConfig,
|
|
105
|
+
(finished) => {
|
|
106
|
+
if (finished && !isVisible) {
|
|
107
|
+
runOnJS(setIsRendered)(false);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}, [isVisible, animationConfig]);
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
|
|
115
|
+
if (isVisible && closeOnBackButton) {
|
|
116
|
+
onClose();
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
});
|
|
121
|
+
return () => backHandler.remove();
|
|
122
|
+
}, [isVisible, closeOnBackButton, onClose]);
|
|
123
|
+
|
|
124
|
+
const backdropAnimatedStyle = useAnimatedStyle(() => ({
|
|
125
|
+
opacity: opacity.value,
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
const contentAnimatedStyle = useAnimatedStyle(() => {
|
|
129
|
+
// Simple slide/fade effect based on position
|
|
130
|
+
let transform = [];
|
|
131
|
+
if (position === "bottom") {
|
|
132
|
+
transform.push({ translateY: (1 - opacity.value) * 100 });
|
|
133
|
+
} else if (position === "top") {
|
|
134
|
+
transform.push({ translateY: (1 - opacity.value) * -100 });
|
|
135
|
+
} else {
|
|
136
|
+
transform.push({ scale: 0.9 + opacity.value * 0.1 });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
opacity: opacity.value,
|
|
141
|
+
transform,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (!isRendered) return null;
|
|
146
|
+
|
|
147
|
+
const justifyContent =
|
|
148
|
+
position === "bottom" ? "flex-end" : position === "top" ? "flex-start" : "center";
|
|
149
|
+
|
|
150
|
+
const paddingBottom = position === "bottom" ? insets.bottom : 0;
|
|
151
|
+
const paddingTop = position === "top" ? insets.top : 0;
|
|
152
|
+
|
|
153
|
+
const ContentWrapper = avoidKeyboard ? KeyboardAvoidingView : View;
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<View style={[StyleSheet.absoluteFill, styles.overlayWrapper, { justifyContent }]}>
|
|
157
|
+
{/* Backdrop */}
|
|
158
|
+
<Animated.View style={[StyleSheet.absoluteFill, styles.backdrop, backdropStyle, backdropAnimatedStyle]}>
|
|
159
|
+
<TouchableOpacity
|
|
160
|
+
style={StyleSheet.absoluteFill}
|
|
161
|
+
activeOpacity={1}
|
|
162
|
+
onPress={closeOnBackdropPress ? onClose : undefined}
|
|
163
|
+
/>
|
|
164
|
+
</Animated.View>
|
|
165
|
+
|
|
166
|
+
{/* Content */}
|
|
167
|
+
<ContentWrapper
|
|
168
|
+
behavior={Platform.OS === "ios" ? "padding" : undefined}
|
|
169
|
+
style={[styles.keyboardView, { paddingBottom, paddingTop }]}
|
|
170
|
+
pointerEvents="box-none"
|
|
171
|
+
>
|
|
172
|
+
<Animated.View
|
|
173
|
+
style={[
|
|
174
|
+
styles.baseContainer,
|
|
175
|
+
containerStyle,
|
|
176
|
+
contentAnimatedStyle,
|
|
177
|
+
]}
|
|
178
|
+
>
|
|
179
|
+
{children}
|
|
180
|
+
</Animated.View>
|
|
181
|
+
</ContentWrapper>
|
|
182
|
+
</View>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// --- Provider ---
|
|
187
|
+
|
|
188
|
+
export const UniversalModalProvider: React.FC<ModalProviderProps> = ({
|
|
189
|
+
children,
|
|
190
|
+
defaultOptions = {},
|
|
191
|
+
portalHostName = DEFAULT_MODAL_HOST,
|
|
192
|
+
CustomModalComponent,
|
|
193
|
+
}) => {
|
|
194
|
+
const [state, setState] = useState<{
|
|
195
|
+
isVisible: boolean;
|
|
196
|
+
content: ReactNode | null;
|
|
197
|
+
options: ModalOptions;
|
|
198
|
+
}>({
|
|
199
|
+
isVisible: false,
|
|
200
|
+
content: null,
|
|
201
|
+
options: defaultOptions,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const showModal = useCallback(
|
|
205
|
+
(content: ReactNode, options?: ModalOptions) => {
|
|
206
|
+
setState({
|
|
207
|
+
isVisible: true,
|
|
208
|
+
content,
|
|
209
|
+
options: { ...defaultOptions, ...options },
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
[defaultOptions]
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const hideModal = useCallback(() => {
|
|
216
|
+
setState((prev) => ({ ...prev, isVisible: false }));
|
|
217
|
+
// Clear content after animation normally finishes to avoid flicker
|
|
218
|
+
setTimeout(() => {
|
|
219
|
+
setState((prev) => {
|
|
220
|
+
// Only clear if still hidden (prevent race condition if immediately reshown)
|
|
221
|
+
return !prev.isVisible ? { ...prev, content: null } : prev
|
|
222
|
+
});
|
|
223
|
+
}, 350);
|
|
224
|
+
}, []);
|
|
225
|
+
|
|
226
|
+
const ModalUI = CustomModalComponent || DefaultModalUI;
|
|
227
|
+
|
|
228
|
+
const value = useMemo(
|
|
229
|
+
() => ({ showModal, hideModal, isVisible: state.isVisible }),
|
|
230
|
+
[showModal, hideModal, state.isVisible]
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<ModalContext.Provider value={value}>
|
|
235
|
+
{/* Ensure PortalProvider exists if not wrapped at root */}
|
|
236
|
+
{/* Note: If you already have PortalProvider at root, this nested one might not be strictly necessary depending on usage,
|
|
237
|
+
but wrapping the host ensures the modal renders here. */}
|
|
238
|
+
<PortalProvider>
|
|
239
|
+
{children}
|
|
240
|
+
<PortalHost name={portalHostName} />
|
|
241
|
+
<Portal hostName={portalHostName}>
|
|
242
|
+
<ModalUI
|
|
243
|
+
isVisible={state.isVisible}
|
|
244
|
+
options={state.options}
|
|
245
|
+
onClose={hideModal}
|
|
246
|
+
>
|
|
247
|
+
{state.content}
|
|
248
|
+
</ModalUI>
|
|
249
|
+
</Portal>
|
|
250
|
+
</PortalProvider>
|
|
251
|
+
</ModalContext.Provider>
|
|
252
|
+
);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// --- Hook ---
|
|
256
|
+
|
|
257
|
+
export const useModal = () => {
|
|
258
|
+
const context = useContext(ModalContext);
|
|
259
|
+
if (!context) {
|
|
260
|
+
throw new Error("useModal must be used within a UniversalModalProvider");
|
|
261
|
+
}
|
|
262
|
+
return context;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// --- Default Styles ---
|
|
266
|
+
|
|
267
|
+
const styles = StyleSheet.create({
|
|
268
|
+
overlayWrapper: {
|
|
269
|
+
zIndex: 1000,
|
|
270
|
+
elevation: 1000,
|
|
271
|
+
},
|
|
272
|
+
backdrop: {
|
|
273
|
+
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
|
274
|
+
},
|
|
275
|
+
keyboardView: {
|
|
276
|
+
flexGrow: 1,
|
|
277
|
+
justifyContent: "center",
|
|
278
|
+
alignItems: "center",
|
|
279
|
+
},
|
|
280
|
+
baseContainer: {
|
|
281
|
+
backgroundColor: "white", // Default, allows override
|
|
282
|
+
borderRadius: 16,
|
|
283
|
+
padding: 20,
|
|
284
|
+
width: "90%",
|
|
285
|
+
maxWidth: 500,
|
|
286
|
+
alignItems: "center",
|
|
287
|
+
shadowColor: "#000",
|
|
288
|
+
shadowOffset: { width: 0, height: 2 },
|
|
289
|
+
shadowOpacity: 0.25,
|
|
290
|
+
shadowRadius: 3.84,
|
|
291
|
+
elevation: 5,
|
|
292
|
+
},
|
|
293
|
+
});
|
package/src/contexts/index.ts
CHANGED