related-ui-components 4.2.6 → 4.2.8
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/components/Filters/PointsRangeSelector.js +58 -72
- package/lib/module/components/Filters/PointsRangeSelector.js.map +1 -1
- package/lib/module/contexts/UniversalModalProvider.js +31 -41
- package/lib/module/contexts/UniversalModalProvider.js.map +1 -1
- package/lib/typescript/src/components/Filters/PointsRangeSelector.d.ts.map +1 -1
- package/lib/typescript/src/contexts/UniversalModalProvider.d.ts +1 -11
- package/lib/typescript/src/contexts/UniversalModalProvider.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Filters/PointsRangeSelector.tsx +90 -74
- package/src/contexts/UniversalModalProvider.tsx +40 -49
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, {
|
|
3
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { View, StyleSheet, Dimensions, I18nManager } from "react-native";
|
|
5
5
|
import MultiSlider from "@ptomasroos/react-native-multi-slider";
|
|
6
6
|
import CustomInput from "../Input/Input.js";
|
|
7
7
|
import { useTheme } from "../../theme/index.js";
|
|
8
|
-
|
|
9
|
-
// Get initial screen width (we'll refine this with onLayout)
|
|
10
8
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
9
|
const {
|
|
12
10
|
width: SCREEN_WIDTH
|
|
13
11
|
} = Dimensions.get("window");
|
|
14
|
-
const HORIZONTAL_PADDING = 40;
|
|
15
|
-
|
|
12
|
+
const HORIZONTAL_PADDING = 40;
|
|
13
|
+
const sanitizeNumericInput = text => {
|
|
14
|
+
const sanitized = text.replace(/[^0-9.]/g, "");
|
|
15
|
+
const parts = sanitized.split(".");
|
|
16
|
+
if (parts.length <= 2) {
|
|
17
|
+
return sanitized;
|
|
18
|
+
}
|
|
19
|
+
return `${parts[0]}.${parts.slice(1).join("")}`;
|
|
20
|
+
};
|
|
16
21
|
const PointsRangeSelector = ({
|
|
17
22
|
minLimit = 0,
|
|
18
23
|
maxLimit = 1000,
|
|
@@ -21,7 +26,6 @@ const PointsRangeSelector = ({
|
|
|
21
26
|
resetKey,
|
|
22
27
|
isRTL = I18nManager.isRTL,
|
|
23
28
|
onChange,
|
|
24
|
-
// Style props
|
|
25
29
|
containerStyle,
|
|
26
30
|
inputsContainerStyle,
|
|
27
31
|
customInputContainerStyle,
|
|
@@ -44,14 +48,21 @@ const PointsRangeSelector = ({
|
|
|
44
48
|
} = useTheme();
|
|
45
49
|
isRTL = rtl || isRTL;
|
|
46
50
|
const styles = createStyles(theme, isRTL);
|
|
51
|
+
const sliderStep = multiSliderProps.step ?? 10;
|
|
47
52
|
const [values, setValues] = useState([initialMin, initialMax]);
|
|
53
|
+
const valuesRef = useRef([initialMin, initialMax]);
|
|
48
54
|
const [minInputValue, setMinInputValue] = useState(initialMin.toString());
|
|
49
55
|
const [maxInputValue, setMaxInputValue] = useState(initialMax.toString());
|
|
50
|
-
|
|
51
|
-
// Track container width for responsive sizing
|
|
52
56
|
const [containerWidth, setContainerWidth] = useState(SCREEN_WIDTH - HORIZONTAL_PADDING);
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
const setRange = useCallback((nextValues, syncInputs = true) => {
|
|
58
|
+
valuesRef.current = nextValues;
|
|
59
|
+
setValues(nextValues);
|
|
60
|
+
if (syncInputs) {
|
|
61
|
+
setMinInputValue(nextValues[0].toString());
|
|
62
|
+
setMaxInputValue(nextValues[1].toString());
|
|
63
|
+
}
|
|
64
|
+
onChange?.(nextValues[0], nextValues[1]);
|
|
65
|
+
}, [onChange]);
|
|
55
66
|
const onContainerLayout = useCallback(event => {
|
|
56
67
|
const {
|
|
57
68
|
width
|
|
@@ -59,53 +70,38 @@ const PointsRangeSelector = ({
|
|
|
59
70
|
setContainerWidth(width);
|
|
60
71
|
}, []);
|
|
61
72
|
useEffect(() => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (text && !isNaN(Number(text))) {
|
|
94
|
-
const newValue = Number(text);
|
|
95
|
-
if (newValue > maxLimit || newValue <= values[0]) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const newValues = [values[0], newValue];
|
|
99
|
-
setValues(newValues);
|
|
100
|
-
if (onChange) {
|
|
101
|
-
onChange(newValues[0], newValues[1]);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// Custom marker component
|
|
73
|
+
setRange([initialMin, initialMax]);
|
|
74
|
+
}, [resetKey, initialMin, initialMax, setRange]);
|
|
75
|
+
const handleValuesChange = useCallback(newValues => {
|
|
76
|
+
setRange([newValues[0], newValues[1]]);
|
|
77
|
+
}, [setRange]);
|
|
78
|
+
const handleMinInputChange = useCallback(text => {
|
|
79
|
+
const sanitized = sanitizeNumericInput(text);
|
|
80
|
+
setMinInputValue(sanitized);
|
|
81
|
+
const newMin = Number(sanitized);
|
|
82
|
+
if (!sanitized || Number.isNaN(newMin)) return;
|
|
83
|
+
const [, currentMax] = valuesRef.current;
|
|
84
|
+
if (newMin < minLimit || newMin >= currentMax) return;
|
|
85
|
+
setRange([newMin, currentMax], false);
|
|
86
|
+
}, [minLimit, setRange]);
|
|
87
|
+
const handleMaxInputChange = useCallback(text => {
|
|
88
|
+
const sanitized = sanitizeNumericInput(text);
|
|
89
|
+
setMaxInputValue(sanitized);
|
|
90
|
+
const newMax = Number(sanitized);
|
|
91
|
+
if (!sanitized || Number.isNaN(newMax)) return;
|
|
92
|
+
const [currentMin] = valuesRef.current;
|
|
93
|
+
if (newMax > maxLimit || newMax <= currentMin) return;
|
|
94
|
+
setRange([currentMin, newMax], false);
|
|
95
|
+
}, [maxLimit, setRange]);
|
|
96
|
+
const handleMinInputBlur = useCallback(() => {
|
|
97
|
+
const [currentMin] = valuesRef.current;
|
|
98
|
+
setMinInputValue(currentMin.toString());
|
|
99
|
+
}, []);
|
|
100
|
+
const handleMaxInputBlur = useCallback(() => {
|
|
101
|
+
const [, currentMax] = valuesRef.current;
|
|
102
|
+
setMaxInputValue(currentMax.toString());
|
|
103
|
+
}, []);
|
|
107
104
|
const CustomMarker = ({
|
|
108
|
-
currentValue,
|
|
109
105
|
enabled
|
|
110
106
|
}) => /*#__PURE__*/_jsx(View, {
|
|
111
107
|
style: [styles.marker, enabled ? styles.markerEnabled : styles.markerDisabled, markerStyle, enabled ? markerEnabledStyle : markerDisabledStyle],
|
|
@@ -119,12 +115,11 @@ const PointsRangeSelector = ({
|
|
|
119
115
|
style: [styles.markerDot, markerDotStyle]
|
|
120
116
|
})
|
|
121
117
|
});
|
|
122
|
-
|
|
123
|
-
// Prepare inputs in the correct order based on RTL setting
|
|
124
118
|
const renderInputs = () => {
|
|
125
119
|
const minInput = /*#__PURE__*/_jsx(CustomInput, {
|
|
126
120
|
value: minInputValue,
|
|
127
121
|
onChangeText: handleMinInputChange,
|
|
122
|
+
onBlur: handleMinInputBlur,
|
|
128
123
|
keyboardType: "numeric",
|
|
129
124
|
placeholder: "Min",
|
|
130
125
|
containerStyle: [styles.customInputContainer, customInputContainerStyle],
|
|
@@ -135,6 +130,7 @@ const PointsRangeSelector = ({
|
|
|
135
130
|
const maxInput = /*#__PURE__*/_jsx(CustomInput, {
|
|
136
131
|
value: maxInputValue,
|
|
137
132
|
onChangeText: handleMaxInputChange,
|
|
133
|
+
onBlur: handleMaxInputBlur,
|
|
138
134
|
keyboardType: "numeric",
|
|
139
135
|
placeholder: "Max",
|
|
140
136
|
containerStyle: [styles.customInputContainer, customInputContainerStyle],
|
|
@@ -142,8 +138,6 @@ const PointsRangeSelector = ({
|
|
|
142
138
|
textAlign: isRTL ? "right" : "left",
|
|
143
139
|
...inputProps
|
|
144
140
|
});
|
|
145
|
-
|
|
146
|
-
// If RTL, we swap the order of the inputs
|
|
147
141
|
return isRTL ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
148
142
|
children: [maxInput, minInput]
|
|
149
143
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
@@ -162,7 +156,7 @@ const PointsRangeSelector = ({
|
|
|
162
156
|
values: values,
|
|
163
157
|
min: minLimit,
|
|
164
158
|
max: maxLimit,
|
|
165
|
-
step:
|
|
159
|
+
step: sliderStep,
|
|
166
160
|
allowOverlap: false,
|
|
167
161
|
snapped: true,
|
|
168
162
|
onValuesChange: handleValuesChange,
|
|
@@ -170,11 +164,8 @@ const PointsRangeSelector = ({
|
|
|
170
164
|
unselectedStyle: [styles.unselectedTrack, unselectedTrackStyle],
|
|
171
165
|
containerStyle: [styles.sliderContainerStyle, multiSliderContainerStyle],
|
|
172
166
|
trackStyle: [styles.track, trackStyle],
|
|
173
|
-
sliderLength: containerWidth
|
|
174
|
-
,
|
|
175
|
-
customMarker: CustomMarker
|
|
176
|
-
// For RTL support with MultiSlider
|
|
177
|
-
,
|
|
167
|
+
sliderLength: containerWidth,
|
|
168
|
+
customMarker: CustomMarker,
|
|
178
169
|
enabledsOne: true,
|
|
179
170
|
enabledTwo: true,
|
|
180
171
|
isRTL: isRTL,
|
|
@@ -184,9 +175,7 @@ const PointsRangeSelector = ({
|
|
|
184
175
|
});
|
|
185
176
|
};
|
|
186
177
|
const createStyles = (theme, isRTL) => StyleSheet.create({
|
|
187
|
-
container: {
|
|
188
|
-
// Default container styling
|
|
189
|
-
},
|
|
178
|
+
container: {},
|
|
190
179
|
inputsContainer: {
|
|
191
180
|
flexDirection: "row",
|
|
192
181
|
justifyContent: "space-between",
|
|
@@ -203,8 +192,6 @@ const createStyles = (theme, isRTL) => StyleSheet.create({
|
|
|
203
192
|
marginVertical: 10,
|
|
204
193
|
alignItems: "center",
|
|
205
194
|
width: "100%"
|
|
206
|
-
// Apply any RTL specific styling if needed
|
|
207
|
-
// transform: isRTL ? [{ scaleX: -1 }] : [],
|
|
208
195
|
},
|
|
209
196
|
sliderContainerStyle: {
|
|
210
197
|
height: 30,
|
|
@@ -236,7 +223,6 @@ const createStyles = (theme, isRTL) => StyleSheet.create({
|
|
|
236
223
|
elevation: 3,
|
|
237
224
|
alignItems: "center",
|
|
238
225
|
justifyContent: "center",
|
|
239
|
-
// Flip the marker back when parent is flipped for RTL
|
|
240
226
|
transform: isRTL ? [{
|
|
241
227
|
scaleX: -1
|
|
242
228
|
}] : []
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","
|
|
1
|
+
{"version":3,"names":["React","useCallback","useEffect","useRef","useState","View","StyleSheet","Dimensions","I18nManager","MultiSlider","CustomInput","useTheme","jsx","_jsx","Fragment","_Fragment","jsxs","_jsxs","width","SCREEN_WIDTH","get","HORIZONTAL_PADDING","sanitizeNumericInput","text","sanitized","replace","parts","split","length","slice","join","PointsRangeSelector","minLimit","maxLimit","initialMin","initialMax","resetKey","isRTL","onChange","containerStyle","inputsContainerStyle","customInputContainerStyle","customInputFieldStyle","sliderContainerStyle","multiSliderContainerStyle","trackStyle","selectedTrackStyle","unselectedTrackStyle","markerStyle","markerEnabledStyle","markerDisabledStyle","markerDotStyle","inputProps","multiSliderProps","theme","rtl","styles","createStyles","sliderStep","step","values","setValues","valuesRef","minInputValue","setMinInputValue","toString","maxInputValue","setMaxInputValue","containerWidth","setContainerWidth","setRange","nextValues","syncInputs","current","onContainerLayout","event","nativeEvent","layout","handleValuesChange","newValues","handleMinInputChange","newMin","Number","isNaN","currentMax","handleMaxInputChange","newMax","currentMin","handleMinInputBlur","handleMaxInputBlur","CustomMarker","enabled","style","marker","markerEnabled","markerDisabled","hitSlop","top","right","bottom","left","children","markerDot","renderInputs","minInput","value","onChangeText","onBlur","keyboardType","placeholder","customInputContainer","inputContainerStyle","customInputField","textAlign","maxInput","container","onLayout","inputsContainer","sliderContainer","min","max","allowOverlap","snapped","onValuesChange","selectedStyle","selectedTrack","unselectedStyle","unselectedTrack","track","sliderLength","customMarker","enabledsOne","enabledTwo","create","flexDirection","justifyContent","marginBottom","minHeight","marginVertical","alignItems","height","borderRadius","backgroundColor","primary","border","surface","borderWidth","shadowColor","shadowOffset","shadowOpacity","shadowRadius","elevation","transform","scaleX","borderColor","disabled"],"sourceRoot":"..\\..\\..\\..\\src","sources":["components/Filters/PointsRangeSelector.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { BackHandler, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
5
5
|
import { KeyboardAvoidingView } from "react-native-keyboard-controller";
|
|
6
6
|
import Animated, { Easing, runOnJS, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
|
7
7
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
8
|
-
|
|
9
|
-
// --- Types ---
|
|
10
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
-
// --- Context ---
|
|
12
|
-
|
|
13
9
|
const ModalContext = /*#__PURE__*/createContext(undefined);
|
|
14
10
|
export const DEFAULT_MODAL_HOST = "UniversalModalHost";
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const DEFAULT_ANIMATION_CONFIG = {
|
|
12
|
+
duration: 300,
|
|
13
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
|
|
14
|
+
};
|
|
17
15
|
const DefaultModalUI = ({
|
|
18
16
|
isVisible,
|
|
19
17
|
children,
|
|
@@ -23,19 +21,12 @@ const DefaultModalUI = ({
|
|
|
23
21
|
}) => {
|
|
24
22
|
const insets = useSafeAreaInsets();
|
|
25
23
|
const [isRendered, setIsRendered] = useState(isVisible);
|
|
24
|
+
const hasRenderedVisibleModalRef = useRef(false);
|
|
26
25
|
const opacity = useSharedValue(0);
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
if (!isVisible && !isRendered) {
|
|
29
|
-
onDismiss();
|
|
30
|
-
}
|
|
31
|
-
}, [isVisible, isRendered, onDismiss]);
|
|
32
26
|
const {
|
|
33
27
|
closeOnBackdropPress = true,
|
|
34
28
|
closeOnBackButton = true,
|
|
35
|
-
animationConfig =
|
|
36
|
-
duration: 300,
|
|
37
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1)
|
|
38
|
-
},
|
|
29
|
+
animationConfig = DEFAULT_ANIMATION_CONFIG,
|
|
39
30
|
position = "center",
|
|
40
31
|
animateFrom = "center",
|
|
41
32
|
avoidKeyboard = true,
|
|
@@ -44,13 +35,22 @@ const DefaultModalUI = ({
|
|
|
44
35
|
closeIconComponent
|
|
45
36
|
} = options;
|
|
46
37
|
useEffect(() => {
|
|
47
|
-
if (isVisible)
|
|
38
|
+
if (isVisible) {
|
|
39
|
+
hasRenderedVisibleModalRef.current = true;
|
|
40
|
+
setIsRendered(true);
|
|
41
|
+
}
|
|
48
42
|
opacity.value = withTiming(isVisible ? 1 : 0, animationConfig, finished => {
|
|
49
43
|
if (finished && !isVisible) {
|
|
50
44
|
runOnJS(setIsRendered)(false);
|
|
51
45
|
}
|
|
52
46
|
});
|
|
53
47
|
}, [isVisible, animationConfig, opacity]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (hasRenderedVisibleModalRef.current && !isVisible && !isRendered) {
|
|
50
|
+
hasRenderedVisibleModalRef.current = false;
|
|
51
|
+
onDismiss();
|
|
52
|
+
}
|
|
53
|
+
}, [isVisible, isRendered, onDismiss]);
|
|
54
54
|
useEffect(() => {
|
|
55
55
|
const backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
|
|
56
56
|
if (isVisible && closeOnBackButton) {
|
|
@@ -68,20 +68,13 @@ const DefaultModalUI = ({
|
|
|
68
68
|
opacity: opacity.value
|
|
69
69
|
}));
|
|
70
70
|
const contentAnimatedStyle = useAnimatedStyle(() => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
translateY: (1 - opacity.value) * -100
|
|
79
|
-
});
|
|
80
|
-
} else {
|
|
81
|
-
transform.push({
|
|
82
|
-
scale: 0.9 + opacity.value * 0.1
|
|
83
|
-
});
|
|
84
|
-
}
|
|
71
|
+
const transform = animateFrom === "bottom" ? [{
|
|
72
|
+
translateY: (1 - opacity.value) * 100
|
|
73
|
+
}] : animateFrom === "top" ? [{
|
|
74
|
+
translateY: (1 - opacity.value) * -100
|
|
75
|
+
}] : [{
|
|
76
|
+
scale: 0.9 + opacity.value * 0.1
|
|
77
|
+
}];
|
|
85
78
|
return {
|
|
86
79
|
opacity: opacity.value,
|
|
87
80
|
transform
|
|
@@ -135,11 +128,9 @@ const DefaultModalUI = ({
|
|
|
135
128
|
})]
|
|
136
129
|
});
|
|
137
130
|
};
|
|
138
|
-
|
|
139
|
-
// --- Provider ---
|
|
140
131
|
export const UniversalModalProvider = ({
|
|
141
132
|
children,
|
|
142
|
-
|
|
133
|
+
defaultOptions,
|
|
143
134
|
CustomModalComponent
|
|
144
135
|
}) => {
|
|
145
136
|
const [state, setState] = useState({
|
|
@@ -147,16 +138,18 @@ export const UniversalModalProvider = ({
|
|
|
147
138
|
content: null,
|
|
148
139
|
options: {}
|
|
149
140
|
});
|
|
150
|
-
const dismissCallbackRef =
|
|
141
|
+
const dismissCallbackRef = useRef(undefined);
|
|
151
142
|
const showModal = useCallback((content, options) => {
|
|
143
|
+
dismissCallbackRef.current = undefined;
|
|
152
144
|
setState({
|
|
153
145
|
isVisible: true,
|
|
154
146
|
content,
|
|
155
147
|
options: {
|
|
148
|
+
...defaultOptions,
|
|
156
149
|
...options
|
|
157
150
|
}
|
|
158
151
|
});
|
|
159
|
-
}, []);
|
|
152
|
+
}, [defaultOptions]);
|
|
160
153
|
const hideModal = useCallback(options => {
|
|
161
154
|
dismissCallbackRef.current = options?.onDismiss;
|
|
162
155
|
setState(prev => ({
|
|
@@ -166,7 +159,7 @@ export const UniversalModalProvider = ({
|
|
|
166
159
|
}, []);
|
|
167
160
|
const handleDismiss = useCallback(() => {
|
|
168
161
|
setState(prev => {
|
|
169
|
-
if (prev.isVisible) return prev;
|
|
162
|
+
if (prev.isVisible || prev.content === null) return prev;
|
|
170
163
|
return {
|
|
171
164
|
...prev,
|
|
172
165
|
content: null
|
|
@@ -200,10 +193,7 @@ export const useModal = () => {
|
|
|
200
193
|
return context;
|
|
201
194
|
};
|
|
202
195
|
const styles = StyleSheet.create({
|
|
203
|
-
overlayWrapper: {
|
|
204
|
-
// zIndex: 1000,
|
|
205
|
-
// elevation: 1000,
|
|
206
|
-
},
|
|
196
|
+
overlayWrapper: {},
|
|
207
197
|
backdrop: {
|
|
208
198
|
backgroundColor: "rgba(0, 0, 0, 0.6)"
|
|
209
199
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","createContext","useCallback","useContext","useEffect","useMemo","useState","BackHandler","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","onDismiss","insets","isRendered","setIsRendered","opacity","closeOnBackdropPress","closeOnBackButton","animationConfig","
|
|
1
|
+
{"version":3,"names":["React","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","BackHandler","StyleSheet","TouchableOpacity","View","KeyboardAvoidingView","Animated","Easing","runOnJS","useAnimatedStyle","useSharedValue","withTiming","useSafeAreaInsets","jsx","_jsx","jsxs","_jsxs","ModalContext","undefined","DEFAULT_MODAL_HOST","DEFAULT_ANIMATION_CONFIG","duration","easing","bezier","DefaultModalUI","isVisible","children","options","onClose","onDismiss","insets","isRendered","setIsRendered","hasRenderedVisibleModalRef","opacity","closeOnBackdropPress","closeOnBackButton","animationConfig","position","animateFrom","avoidKeyboard","backdropStyle","containerStyle","closeIconComponent","current","value","finished","backHandler","addEventListener","remove","backdropAnimatedStyle","closeIconAnimatedStyle","contentAnimatedStyle","transform","translateY","scale","justifyContent","paddingBottom","bottom","paddingTop","top","ContentWrapper","style","absoluteFill","styles","overlayWrapper","backdrop","activeOpacity","onPress","closeIconContainer","pointerEvents","hitSlop","left","right","behavior","keyboardVerticalOffset","keyboardView","baseContainer","UniversalModalProvider","defaultOptions","CustomModalComponent","state","setState","content","dismissCallbackRef","showModal","hideModal","prev","handleDismiss","callback","ModalUI","Provider","useModal","context","Error","create","backgroundColor","alignItems","width","zIndex"],"sourceRoot":"..\\..\\..\\src","sources":["contexts/UniversalModalProvider.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PointsRangeSelector.d.ts","sourceRoot":"","sources":["../../../../../src/components/Filters/PointsRangeSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"PointsRangeSelector.d.ts","sourceRoot":"","sources":["../../../../../src/components/Filters/PointsRangeSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,EAKL,SAAS,EAEV,MAAM,cAAc,CAAC;AAQtB,UAAU,wBAAwB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9C,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,oBAAoB,CAAC,EAAE,SAAS,CAAC;IACjC,yBAAyB,CAAC,EAAE,SAAS,CAAC;IACtC,qBAAqB,CAAC,EAAE,SAAS,CAAC;IAClC,oBAAoB,CAAC,EAAE,SAAS,CAAC;IACjC,yBAAyB,CAAC,EAAE,SAAS,CAAC;IACtC,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,kBAAkB,CAAC,EAAE,SAAS,CAAC;IAC/B,oBAAoB,CAAC,EAAE,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,kBAAkB,CAAC,EAAE,SAAS,CAAC;IAC/B,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAChC,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,gBAAgB,CAAC,EAAE,GAAG,CAAC;CACxB;AAaD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAsN3D,CAAC;AAoEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -2,22 +2,14 @@ import React, { ReactNode } from "react";
|
|
|
2
2
|
import { StyleProp, ViewStyle } from "react-native";
|
|
3
3
|
import { WithTimingConfig } from "react-native-reanimated";
|
|
4
4
|
export interface ModalOptions {
|
|
5
|
-
/** Should the modal close when tapping the backdrop? Default: true */
|
|
6
5
|
closeOnBackdropPress?: boolean;
|
|
7
|
-
/** Should the hardware back button close the modal? Default: true */
|
|
8
6
|
closeOnBackButton?: boolean;
|
|
9
|
-
/** Animation configuration for entry/exit */
|
|
10
7
|
animationConfig?: WithTimingConfig;
|
|
11
|
-
/** Custom styles for the backdrop */
|
|
12
8
|
backdropStyle?: StyleProp<ViewStyle>;
|
|
13
|
-
/** Custom styles for the modal container (the white box) */
|
|
14
9
|
containerStyle?: StyleProp<ViewStyle>;
|
|
15
|
-
/** Position of the modal. Default: 'center' */
|
|
16
10
|
position?: "center" | "bottom" | "top";
|
|
17
11
|
animateFrom?: "center" | "bottom" | "top";
|
|
18
|
-
/** If true, avoids keyboard view. Default: true */
|
|
19
12
|
avoidKeyboard?: boolean;
|
|
20
|
-
/** Optional component to render as a close icon at the top right or left of the screen */
|
|
21
13
|
closeIconComponent?: ReactNode;
|
|
22
14
|
}
|
|
23
15
|
interface HideModalOptions {
|
|
@@ -29,16 +21,14 @@ interface ModalContextType {
|
|
|
29
21
|
}
|
|
30
22
|
interface ModalProviderProps {
|
|
31
23
|
children: ReactNode;
|
|
32
|
-
/** Global default options for all modals */
|
|
33
24
|
defaultOptions?: ModalOptions;
|
|
34
|
-
/** Optional: Provide a specific Portal Host name if nesting providers */
|
|
35
25
|
portalHostName?: string;
|
|
36
|
-
/** Optional: Override the internal Modal UI component completely */
|
|
37
26
|
CustomModalComponent?: React.FC<{
|
|
38
27
|
isVisible: boolean;
|
|
39
28
|
children: ReactNode;
|
|
40
29
|
options: ModalOptions;
|
|
41
30
|
onClose: () => void;
|
|
31
|
+
onDismiss: () => void;
|
|
42
32
|
}>;
|
|
43
33
|
}
|
|
44
34
|
export declare const DEFAULT_MODAL_HOST = "UniversalModalHost";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UniversalModalProvider.d.ts","sourceRoot":"","sources":["../../../../src/contexts/UniversalModalProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAEZ,SAAS,
|
|
1
|
+
{"version":3,"file":"UniversalModalProvider.d.ts","sourceRoot":"","sources":["../../../../src/contexts/UniversalModalProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAEZ,SAAS,EAOV,MAAM,OAAO,CAAC;AACf,OAAO,EAEL,SAAS,EAIT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAiB,EAMf,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,WAAW,YAAY;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IACvC,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,SAAS,CAAC;CAChC;AAED,UAAU,gBAAgB;IACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,UAAU,gBAAgB;IACxB,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;IAChE,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACjD;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,SAAS,CAAC;IACpB,cAAc,CAAC,EAAE,YAAY,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,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;QACpB,SAAS,EAAE,MAAM,IAAI,CAAC;KACvB,CAAC,CAAC;CACJ;AAID,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAuKvD,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkE/D,CAAC;AAEF,eAAO,MAAM,QAAQ,wBAQpB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
|
-
Text,
|
|
5
4
|
StyleSheet,
|
|
6
5
|
Dimensions,
|
|
7
6
|
LayoutChangeEvent,
|
|
8
7
|
ViewStyle,
|
|
9
|
-
TextStyle,
|
|
10
8
|
I18nManager,
|
|
11
9
|
} from "react-native";
|
|
12
10
|
import MultiSlider from "@ptomasroos/react-native-multi-slider";
|
|
13
11
|
import CustomInput from "../Input/Input";
|
|
14
12
|
import { ThemeType, useTheme } from "../../theme";
|
|
15
13
|
|
|
16
|
-
// Get initial screen width (we'll refine this with onLayout)
|
|
17
14
|
const { width: SCREEN_WIDTH } = Dimensions.get("window");
|
|
18
|
-
const HORIZONTAL_PADDING = 40;
|
|
15
|
+
const HORIZONTAL_PADDING = 40;
|
|
19
16
|
|
|
20
17
|
interface PointsRangeSelectorProps {
|
|
21
18
|
minLimit?: number;
|
|
@@ -26,7 +23,6 @@ interface PointsRangeSelectorProps {
|
|
|
26
23
|
isRTL?: boolean;
|
|
27
24
|
onChange?: (min: number, max: number) => void;
|
|
28
25
|
|
|
29
|
-
// Style props
|
|
30
26
|
containerStyle?: ViewStyle;
|
|
31
27
|
inputsContainerStyle?: ViewStyle;
|
|
32
28
|
customInputContainerStyle?: ViewStyle;
|
|
@@ -44,6 +40,17 @@ interface PointsRangeSelectorProps {
|
|
|
44
40
|
multiSliderProps?: any;
|
|
45
41
|
}
|
|
46
42
|
|
|
43
|
+
const sanitizeNumericInput = (text: string) => {
|
|
44
|
+
const sanitized = text.replace(/[^0-9.]/g, "");
|
|
45
|
+
const parts = sanitized.split(".");
|
|
46
|
+
|
|
47
|
+
if (parts.length <= 2) {
|
|
48
|
+
return sanitized;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return `${parts[0]}.${parts.slice(1).join("")}`;
|
|
52
|
+
};
|
|
53
|
+
|
|
47
54
|
const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
48
55
|
minLimit = 0,
|
|
49
56
|
maxLimit = 1000,
|
|
@@ -53,7 +60,6 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
53
60
|
isRTL = I18nManager.isRTL,
|
|
54
61
|
onChange,
|
|
55
62
|
|
|
56
|
-
// Style props
|
|
57
63
|
containerStyle,
|
|
58
64
|
inputsContainerStyle,
|
|
59
65
|
customInputContainerStyle,
|
|
@@ -70,83 +76,99 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
70
76
|
inputProps = {},
|
|
71
77
|
multiSliderProps = {},
|
|
72
78
|
}) => {
|
|
73
|
-
const {theme, isRTL: rtl} = useTheme();
|
|
79
|
+
const { theme, isRTL: rtl } = useTheme();
|
|
74
80
|
isRTL = rtl || isRTL;
|
|
81
|
+
|
|
75
82
|
const styles = createStyles(theme, isRTL);
|
|
83
|
+
const sliderStep = multiSliderProps.step ?? 10;
|
|
84
|
+
|
|
85
|
+
const [values, setValues] = useState<[number, number]>([
|
|
86
|
+
initialMin,
|
|
87
|
+
initialMax,
|
|
88
|
+
]);
|
|
89
|
+
const valuesRef = useRef<[number, number]>([initialMin, initialMax]);
|
|
76
90
|
|
|
77
|
-
const [values, setValues] = useState([initialMin, initialMax]);
|
|
78
91
|
const [minInputValue, setMinInputValue] = useState(initialMin.toString());
|
|
79
92
|
const [maxInputValue, setMaxInputValue] = useState(initialMax.toString());
|
|
80
93
|
|
|
81
|
-
// Track container width for responsive sizing
|
|
82
94
|
const [containerWidth, setContainerWidth] = useState(
|
|
83
|
-
SCREEN_WIDTH - HORIZONTAL_PADDING
|
|
95
|
+
SCREEN_WIDTH - HORIZONTAL_PADDING,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const setRange = useCallback(
|
|
99
|
+
(nextValues: [number, number], syncInputs = true) => {
|
|
100
|
+
valuesRef.current = nextValues;
|
|
101
|
+
setValues(nextValues);
|
|
102
|
+
|
|
103
|
+
if (syncInputs) {
|
|
104
|
+
setMinInputValue(nextValues[0].toString());
|
|
105
|
+
setMaxInputValue(nextValues[1].toString());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
onChange?.(nextValues[0], nextValues[1]);
|
|
109
|
+
},
|
|
110
|
+
[onChange],
|
|
84
111
|
);
|
|
85
112
|
|
|
86
|
-
// Handle container layout to get precise width
|
|
87
113
|
const onContainerLayout = useCallback((event: LayoutChangeEvent) => {
|
|
88
114
|
const { width } = event.nativeEvent.layout;
|
|
89
115
|
setContainerWidth(width);
|
|
90
116
|
}, []);
|
|
91
117
|
|
|
92
118
|
useEffect(() => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
setMaxInputValue(initialMax.toString());
|
|
96
|
-
|
|
97
|
-
if (onChange) {
|
|
98
|
-
onChange(initialMin, initialMax);
|
|
99
|
-
}
|
|
100
|
-
}, [resetKey, initialMin, initialMax]);
|
|
101
|
-
|
|
102
|
-
const handleValuesChange = (newValues: number[]) => {
|
|
103
|
-
setValues(newValues);
|
|
104
|
-
setMinInputValue(newValues[0].toString());
|
|
105
|
-
setMaxInputValue(newValues[1].toString());
|
|
106
|
-
|
|
107
|
-
if (onChange) {
|
|
108
|
-
onChange(newValues[0], newValues[1]);
|
|
109
|
-
}
|
|
110
|
-
};
|
|
119
|
+
setRange([initialMin, initialMax]);
|
|
120
|
+
}, [resetKey, initialMin, initialMax, setRange]);
|
|
111
121
|
|
|
112
|
-
const
|
|
113
|
-
|
|
122
|
+
const handleValuesChange = useCallback(
|
|
123
|
+
(newValues: number[]) => {
|
|
124
|
+
setRange([newValues[0], newValues[1]]);
|
|
125
|
+
},
|
|
126
|
+
[setRange],
|
|
127
|
+
);
|
|
114
128
|
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
const handleMinInputChange = useCallback(
|
|
130
|
+
(text: string) => {
|
|
131
|
+
const sanitized = sanitizeNumericInput(text);
|
|
132
|
+
setMinInputValue(sanitized);
|
|
117
133
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
134
|
+
const newMin = Number(sanitized);
|
|
135
|
+
if (!sanitized || Number.isNaN(newMin)) return;
|
|
121
136
|
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
if (onChange) {
|
|
125
|
-
onChange(newValues[0], newValues[1]);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
};
|
|
137
|
+
const [, currentMax] = valuesRef.current;
|
|
138
|
+
if (newMin < minLimit || newMin >= currentMax) return;
|
|
129
139
|
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
setRange([newMin, currentMax], false);
|
|
141
|
+
},
|
|
142
|
+
[minLimit, setRange],
|
|
143
|
+
);
|
|
132
144
|
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
const handleMaxInputChange = useCallback(
|
|
146
|
+
(text: string) => {
|
|
147
|
+
const sanitized = sanitizeNumericInput(text);
|
|
148
|
+
setMaxInputValue(sanitized);
|
|
135
149
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
150
|
+
const newMax = Number(sanitized);
|
|
151
|
+
if (!sanitized || Number.isNaN(newMax)) return;
|
|
139
152
|
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
if (onChange) {
|
|
143
|
-
onChange(newValues[0], newValues[1]);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
};
|
|
153
|
+
const [currentMin] = valuesRef.current;
|
|
154
|
+
if (newMax > maxLimit || newMax <= currentMin) return;
|
|
147
155
|
|
|
148
|
-
|
|
149
|
-
|
|
156
|
+
setRange([currentMin, newMax], false);
|
|
157
|
+
},
|
|
158
|
+
[maxLimit, setRange],
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const handleMinInputBlur = useCallback(() => {
|
|
162
|
+
const [currentMin] = valuesRef.current;
|
|
163
|
+
setMinInputValue(currentMin.toString());
|
|
164
|
+
}, []);
|
|
165
|
+
|
|
166
|
+
const handleMaxInputBlur = useCallback(() => {
|
|
167
|
+
const [, currentMax] = valuesRef.current;
|
|
168
|
+
setMaxInputValue(currentMax.toString());
|
|
169
|
+
}, []);
|
|
170
|
+
|
|
171
|
+
const CustomMarker = ({ enabled }: any) => (
|
|
150
172
|
<View
|
|
151
173
|
style={[
|
|
152
174
|
styles.marker,
|
|
@@ -160,12 +182,12 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
160
182
|
</View>
|
|
161
183
|
);
|
|
162
184
|
|
|
163
|
-
// Prepare inputs in the correct order based on RTL setting
|
|
164
185
|
const renderInputs = () => {
|
|
165
186
|
const minInput = (
|
|
166
187
|
<CustomInput
|
|
167
188
|
value={minInputValue}
|
|
168
189
|
onChangeText={handleMinInputChange}
|
|
190
|
+
onBlur={handleMinInputBlur}
|
|
169
191
|
keyboardType="numeric"
|
|
170
192
|
placeholder="Min"
|
|
171
193
|
containerStyle={[
|
|
@@ -182,6 +204,7 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
182
204
|
<CustomInput
|
|
183
205
|
value={maxInputValue}
|
|
184
206
|
onChangeText={handleMaxInputChange}
|
|
207
|
+
onBlur={handleMaxInputBlur}
|
|
185
208
|
keyboardType="numeric"
|
|
186
209
|
placeholder="Max"
|
|
187
210
|
containerStyle={[
|
|
@@ -194,7 +217,6 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
194
217
|
/>
|
|
195
218
|
);
|
|
196
219
|
|
|
197
|
-
// If RTL, we swap the order of the inputs
|
|
198
220
|
return isRTL ? (
|
|
199
221
|
<>
|
|
200
222
|
{maxInput}
|
|
@@ -222,7 +244,7 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
222
244
|
values={values}
|
|
223
245
|
min={minLimit}
|
|
224
246
|
max={maxLimit}
|
|
225
|
-
step={
|
|
247
|
+
step={sliderStep}
|
|
226
248
|
allowOverlap={false}
|
|
227
249
|
snapped
|
|
228
250
|
onValuesChange={handleValuesChange}
|
|
@@ -233,11 +255,10 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
233
255
|
multiSliderContainerStyle,
|
|
234
256
|
]}
|
|
235
257
|
trackStyle={[styles.track, trackStyle]}
|
|
236
|
-
sliderLength={containerWidth}
|
|
258
|
+
sliderLength={containerWidth}
|
|
237
259
|
customMarker={CustomMarker}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
enabledTwo={true}
|
|
260
|
+
enabledsOne
|
|
261
|
+
enabledTwo
|
|
241
262
|
isRTL={isRTL}
|
|
242
263
|
{...multiSliderProps}
|
|
243
264
|
/>
|
|
@@ -248,9 +269,7 @@ const PointsRangeSelector: React.FC<PointsRangeSelectorProps> = ({
|
|
|
248
269
|
|
|
249
270
|
const createStyles = (theme: ThemeType, isRTL: boolean) =>
|
|
250
271
|
StyleSheet.create({
|
|
251
|
-
container: {
|
|
252
|
-
// Default container styling
|
|
253
|
-
},
|
|
272
|
+
container: {},
|
|
254
273
|
inputsContainer: {
|
|
255
274
|
flexDirection: "row",
|
|
256
275
|
justifyContent: "space-between",
|
|
@@ -267,8 +286,6 @@ const createStyles = (theme: ThemeType, isRTL: boolean) =>
|
|
|
267
286
|
marginVertical: 10,
|
|
268
287
|
alignItems: "center",
|
|
269
288
|
width: "100%",
|
|
270
|
-
// Apply any RTL specific styling if needed
|
|
271
|
-
// transform: isRTL ? [{ scaleX: -1 }] : [],
|
|
272
289
|
},
|
|
273
290
|
sliderContainerStyle: {
|
|
274
291
|
height: 30,
|
|
@@ -300,7 +317,6 @@ const createStyles = (theme: ThemeType, isRTL: boolean) =>
|
|
|
300
317
|
elevation: 3,
|
|
301
318
|
alignItems: "center",
|
|
302
319
|
justifyContent: "center",
|
|
303
|
-
// Flip the marker back when parent is flipped for RTL
|
|
304
320
|
transform: isRTL ? [{ scaleX: -1 }] : [],
|
|
305
321
|
},
|
|
306
322
|
markerEnabled: {
|
|
@@ -317,4 +333,4 @@ const createStyles = (theme: ThemeType, isRTL: boolean) =>
|
|
|
317
333
|
},
|
|
318
334
|
});
|
|
319
335
|
|
|
320
|
-
export default PointsRangeSelector;
|
|
336
|
+
export default PointsRangeSelector;
|
|
@@ -5,6 +5,7 @@ import React, {
|
|
|
5
5
|
useContext,
|
|
6
6
|
useEffect,
|
|
7
7
|
useMemo,
|
|
8
|
+
useRef,
|
|
8
9
|
useState,
|
|
9
10
|
} from "react";
|
|
10
11
|
import {
|
|
@@ -26,24 +27,15 @@ import Animated, {
|
|
|
26
27
|
} from "react-native-reanimated";
|
|
27
28
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
28
29
|
|
|
29
|
-
// --- Types ---
|
|
30
30
|
export interface ModalOptions {
|
|
31
|
-
/** Should the modal close when tapping the backdrop? Default: true */
|
|
32
31
|
closeOnBackdropPress?: boolean;
|
|
33
|
-
/** Should the hardware back button close the modal? Default: true */
|
|
34
32
|
closeOnBackButton?: boolean;
|
|
35
|
-
/** Animation configuration for entry/exit */
|
|
36
33
|
animationConfig?: WithTimingConfig;
|
|
37
|
-
/** Custom styles for the backdrop */
|
|
38
34
|
backdropStyle?: StyleProp<ViewStyle>;
|
|
39
|
-
/** Custom styles for the modal container (the white box) */
|
|
40
35
|
containerStyle?: StyleProp<ViewStyle>;
|
|
41
|
-
/** Position of the modal. Default: 'center' */
|
|
42
36
|
position?: "center" | "bottom" | "top";
|
|
43
37
|
animateFrom?: "center" | "bottom" | "top";
|
|
44
|
-
/** If true, avoids keyboard view. Default: true */
|
|
45
38
|
avoidKeyboard?: boolean;
|
|
46
|
-
/** Optional component to render as a close icon at the top right or left of the screen */
|
|
47
39
|
closeIconComponent?: ReactNode;
|
|
48
40
|
}
|
|
49
41
|
|
|
@@ -58,26 +50,26 @@ interface ModalContextType {
|
|
|
58
50
|
|
|
59
51
|
interface ModalProviderProps {
|
|
60
52
|
children: ReactNode;
|
|
61
|
-
/** Global default options for all modals */
|
|
62
53
|
defaultOptions?: ModalOptions;
|
|
63
|
-
/** Optional: Provide a specific Portal Host name if nesting providers */
|
|
64
54
|
portalHostName?: string;
|
|
65
|
-
/** Optional: Override the internal Modal UI component completely */
|
|
66
55
|
CustomModalComponent?: React.FC<{
|
|
67
56
|
isVisible: boolean;
|
|
68
57
|
children: ReactNode;
|
|
69
58
|
options: ModalOptions;
|
|
70
59
|
onClose: () => void;
|
|
60
|
+
onDismiss: () => void;
|
|
71
61
|
}>;
|
|
72
62
|
}
|
|
73
63
|
|
|
74
|
-
// --- Context ---
|
|
75
|
-
|
|
76
64
|
const ModalContext = createContext<ModalContextType | undefined>(undefined);
|
|
77
65
|
|
|
78
66
|
export const DEFAULT_MODAL_HOST = "UniversalModalHost";
|
|
79
67
|
|
|
80
|
-
|
|
68
|
+
const DEFAULT_ANIMATION_CONFIG: WithTimingConfig = {
|
|
69
|
+
duration: 300,
|
|
70
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
71
|
+
};
|
|
72
|
+
|
|
81
73
|
const DefaultModalUI: React.FC<{
|
|
82
74
|
isVisible: boolean;
|
|
83
75
|
children: ReactNode;
|
|
@@ -87,21 +79,13 @@ const DefaultModalUI: React.FC<{
|
|
|
87
79
|
}> = ({ isVisible, children, options, onClose, onDismiss }) => {
|
|
88
80
|
const insets = useSafeAreaInsets();
|
|
89
81
|
const [isRendered, setIsRendered] = useState(isVisible);
|
|
82
|
+
const hasRenderedVisibleModalRef = useRef(false);
|
|
90
83
|
const opacity = useSharedValue(0);
|
|
91
84
|
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
if (!isVisible && !isRendered) {
|
|
94
|
-
onDismiss();
|
|
95
|
-
}
|
|
96
|
-
}, [isVisible, isRendered, onDismiss]);
|
|
97
|
-
|
|
98
85
|
const {
|
|
99
86
|
closeOnBackdropPress = true,
|
|
100
87
|
closeOnBackButton = true,
|
|
101
|
-
animationConfig =
|
|
102
|
-
duration: 300,
|
|
103
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
104
|
-
},
|
|
88
|
+
animationConfig = DEFAULT_ANIMATION_CONFIG,
|
|
105
89
|
position = "center",
|
|
106
90
|
animateFrom = "center",
|
|
107
91
|
avoidKeyboard = true,
|
|
@@ -111,7 +95,10 @@ const DefaultModalUI: React.FC<{
|
|
|
111
95
|
} = options;
|
|
112
96
|
|
|
113
97
|
useEffect(() => {
|
|
114
|
-
if (isVisible)
|
|
98
|
+
if (isVisible) {
|
|
99
|
+
hasRenderedVisibleModalRef.current = true;
|
|
100
|
+
setIsRendered(true);
|
|
101
|
+
}
|
|
115
102
|
|
|
116
103
|
opacity.value = withTiming(
|
|
117
104
|
isVisible ? 1 : 0,
|
|
@@ -124,6 +111,13 @@ const DefaultModalUI: React.FC<{
|
|
|
124
111
|
);
|
|
125
112
|
}, [isVisible, animationConfig, opacity]);
|
|
126
113
|
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (hasRenderedVisibleModalRef.current && !isVisible && !isRendered) {
|
|
116
|
+
hasRenderedVisibleModalRef.current = false;
|
|
117
|
+
onDismiss();
|
|
118
|
+
}
|
|
119
|
+
}, [isVisible, isRendered, onDismiss]);
|
|
120
|
+
|
|
127
121
|
useEffect(() => {
|
|
128
122
|
const backHandler = BackHandler.addEventListener(
|
|
129
123
|
"hardwareBackPress",
|
|
@@ -132,9 +126,11 @@ const DefaultModalUI: React.FC<{
|
|
|
132
126
|
onClose();
|
|
133
127
|
return true;
|
|
134
128
|
}
|
|
129
|
+
|
|
135
130
|
return false;
|
|
136
131
|
},
|
|
137
132
|
);
|
|
133
|
+
|
|
138
134
|
return () => backHandler.remove();
|
|
139
135
|
}, [isVisible, closeOnBackButton, onClose]);
|
|
140
136
|
|
|
@@ -147,14 +143,12 @@ const DefaultModalUI: React.FC<{
|
|
|
147
143
|
}));
|
|
148
144
|
|
|
149
145
|
const contentAnimatedStyle = useAnimatedStyle(() => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
transform.push({ scale: 0.9 + opacity.value * 0.1 });
|
|
157
|
-
}
|
|
146
|
+
const transform =
|
|
147
|
+
animateFrom === "bottom"
|
|
148
|
+
? [{ translateY: (1 - opacity.value) * 100 }]
|
|
149
|
+
: animateFrom === "top"
|
|
150
|
+
? [{ translateY: (1 - opacity.value) * -100 }]
|
|
151
|
+
: [{ scale: 0.9 + opacity.value * 0.1 }];
|
|
158
152
|
|
|
159
153
|
return {
|
|
160
154
|
opacity: opacity.value,
|
|
@@ -173,7 +167,6 @@ const DefaultModalUI: React.FC<{
|
|
|
173
167
|
|
|
174
168
|
const paddingBottom = position === "bottom" ? insets.bottom : 0;
|
|
175
169
|
const paddingTop = position === "top" ? insets.top : 0;
|
|
176
|
-
|
|
177
170
|
const ContentWrapper = avoidKeyboard ? KeyboardAvoidingView : View;
|
|
178
171
|
|
|
179
172
|
return (
|
|
@@ -184,7 +177,6 @@ const DefaultModalUI: React.FC<{
|
|
|
184
177
|
{ justifyContent },
|
|
185
178
|
]}
|
|
186
179
|
>
|
|
187
|
-
{/* Backdrop */}
|
|
188
180
|
<Animated.View
|
|
189
181
|
style={[
|
|
190
182
|
StyleSheet.absoluteFill,
|
|
@@ -219,9 +211,8 @@ const DefaultModalUI: React.FC<{
|
|
|
219
211
|
</Animated.View>
|
|
220
212
|
)}
|
|
221
213
|
|
|
222
|
-
{/* Content */}
|
|
223
214
|
<ContentWrapper
|
|
224
|
-
behavior=
|
|
215
|
+
behavior="padding"
|
|
225
216
|
keyboardVerticalOffset={16}
|
|
226
217
|
style={[
|
|
227
218
|
styles.keyboardView,
|
|
@@ -239,10 +230,9 @@ const DefaultModalUI: React.FC<{
|
|
|
239
230
|
);
|
|
240
231
|
};
|
|
241
232
|
|
|
242
|
-
// --- Provider ---
|
|
243
233
|
export const UniversalModalProvider: React.FC<ModalProviderProps> = ({
|
|
244
234
|
children,
|
|
245
|
-
|
|
235
|
+
defaultOptions,
|
|
246
236
|
CustomModalComponent,
|
|
247
237
|
}) => {
|
|
248
238
|
const [state, setState] = useState<{
|
|
@@ -255,17 +245,19 @@ export const UniversalModalProvider: React.FC<ModalProviderProps> = ({
|
|
|
255
245
|
options: {},
|
|
256
246
|
});
|
|
257
247
|
|
|
258
|
-
const dismissCallbackRef =
|
|
248
|
+
const dismissCallbackRef = useRef<(() => void) | undefined>(undefined);
|
|
259
249
|
|
|
260
250
|
const showModal = useCallback(
|
|
261
251
|
(content: ReactNode, options?: ModalOptions) => {
|
|
252
|
+
dismissCallbackRef.current = undefined;
|
|
253
|
+
|
|
262
254
|
setState({
|
|
263
255
|
isVisible: true,
|
|
264
256
|
content,
|
|
265
|
-
options: { ...options },
|
|
257
|
+
options: { ...defaultOptions, ...options },
|
|
266
258
|
});
|
|
267
259
|
},
|
|
268
|
-
[],
|
|
260
|
+
[defaultOptions],
|
|
269
261
|
);
|
|
270
262
|
|
|
271
263
|
const hideModal = useCallback((options?: HideModalOptions) => {
|
|
@@ -275,7 +267,7 @@ export const UniversalModalProvider: React.FC<ModalProviderProps> = ({
|
|
|
275
267
|
|
|
276
268
|
const handleDismiss = useCallback(() => {
|
|
277
269
|
setState((prev) => {
|
|
278
|
-
if (prev.isVisible) return prev;
|
|
270
|
+
if (prev.isVisible || prev.content === null) return prev;
|
|
279
271
|
return { ...prev, content: null };
|
|
280
272
|
});
|
|
281
273
|
|
|
@@ -308,17 +300,16 @@ export const UniversalModalProvider: React.FC<ModalProviderProps> = ({
|
|
|
308
300
|
|
|
309
301
|
export const useModal = () => {
|
|
310
302
|
const context = useContext(ModalContext);
|
|
303
|
+
|
|
311
304
|
if (!context) {
|
|
312
305
|
throw new Error("useModal must be used within a UniversalModalProvider");
|
|
313
306
|
}
|
|
307
|
+
|
|
314
308
|
return context;
|
|
315
309
|
};
|
|
316
310
|
|
|
317
311
|
const styles = StyleSheet.create({
|
|
318
|
-
overlayWrapper: {
|
|
319
|
-
// zIndex: 1000,
|
|
320
|
-
// elevation: 1000,
|
|
321
|
-
},
|
|
312
|
+
overlayWrapper: {},
|
|
322
313
|
backdrop: {
|
|
323
314
|
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
|
324
315
|
},
|
|
@@ -334,4 +325,4 @@ const styles = StyleSheet.create({
|
|
|
334
325
|
right: 16,
|
|
335
326
|
zIndex: 2000,
|
|
336
327
|
},
|
|
337
|
-
});
|
|
328
|
+
});
|