related-ui-components 2.6.4 → 2.6.6

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.
Files changed (38) hide show
  1. package/lib/module/app.js +3 -1
  2. package/lib/module/app.js.map +1 -1
  3. package/lib/module/components/Input/CountryPickerView.js +2 -1
  4. package/lib/module/components/Input/CountryPickerView.js.map +1 -1
  5. package/lib/module/components/Input/Input.js +11 -8
  6. package/lib/module/components/Input/Input.js.map +1 -1
  7. package/lib/module/components/Input/PhoneInput.js +3 -5
  8. package/lib/module/components/Input/PhoneInput.js.map +1 -1
  9. package/lib/module/components/Input/index.js +2 -0
  10. package/lib/module/components/Input/index.js.map +1 -1
  11. package/lib/module/components/RangeSlider/RangeSlider.js +98 -19
  12. package/lib/module/components/RangeSlider/RangeSlider.js.map +1 -1
  13. package/lib/module/contexts/BottomSheetStackContext.js.map +1 -1
  14. package/lib/module/contexts/BottomSheetStackProvider.js +48 -59
  15. package/lib/module/contexts/BottomSheetStackProvider.js.map +1 -1
  16. package/lib/typescript/src/app.d.ts.map +1 -1
  17. package/lib/typescript/src/components/Input/CountryPickerView.d.ts +2 -2
  18. package/lib/typescript/src/components/Input/CountryPickerView.d.ts.map +1 -1
  19. package/lib/typescript/src/components/Input/Input.d.ts +1 -0
  20. package/lib/typescript/src/components/Input/Input.d.ts.map +1 -1
  21. package/lib/typescript/src/components/Input/PhoneInput.d.ts.map +1 -1
  22. package/lib/typescript/src/components/Input/index.d.ts +2 -0
  23. package/lib/typescript/src/components/Input/index.d.ts.map +1 -1
  24. package/lib/typescript/src/components/RangeSlider/RangeSlider.d.ts +1 -0
  25. package/lib/typescript/src/components/RangeSlider/RangeSlider.d.ts.map +1 -1
  26. package/lib/typescript/src/contexts/BottomSheetStackContext.d.ts +0 -1
  27. package/lib/typescript/src/contexts/BottomSheetStackContext.d.ts.map +1 -1
  28. package/lib/typescript/src/contexts/BottomSheetStackProvider.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/app.tsx +3 -1
  31. package/src/components/Input/CountryPickerView.tsx +4 -2
  32. package/src/components/Input/Input.tsx +12 -7
  33. package/src/components/Input/PhoneInput.tsx +3 -5
  34. package/src/components/Input/index.ts +6 -3
  35. package/src/components/RangeSlider/RangeSlider.tsx +113 -42
  36. package/src/contexts/BottomSheetStackContext.tsx +0 -1
  37. package/src/contexts/BottomSheetStackProvider.tsx +46 -66
  38. package/src/index.ts +1 -1
@@ -1,18 +1,79 @@
1
1
  import React, { useCallback, useState } from "react";
2
- import { StyleSheet, View } from "react-native";
2
+ import { StyleSheet, View, Text, I18nManager } from "react-native";
3
3
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
4
4
  import Animated, {
5
5
  runOnJS,
6
6
  useAnimatedStyle,
7
7
  useSharedValue,
8
8
  } from "react-native-reanimated";
9
- import { SliderLabels } from "./SliderLabel";
10
9
  import { ThemeType, useTheme } from "../../theme";
11
10
 
12
11
  const THUMB_SIZE = 28;
13
12
  const RAIL_HEIGHT = 6;
14
13
  const LABEL_HEIGHT = 26;
15
14
 
15
+
16
+ /**
17
+ * Renders the floating labels above the thumbs.
18
+ */
19
+ const SliderLabels = ({
20
+ leftValue,
21
+ rightValue,
22
+ leftPosition,
23
+ rightPosition,
24
+ sliderWidth,
25
+ isRTL,
26
+ theme,
27
+ }: {
28
+ leftValue: string;
29
+ rightValue: string;
30
+ leftPosition: Animated.SharedValue<number>;
31
+ rightPosition: Animated.SharedValue<number>;
32
+ sliderWidth: number;
33
+ isRTL: boolean;
34
+ theme: ThemeType;
35
+ }) => {
36
+ const styles = getStyles(theme);
37
+
38
+ const animatedLeftLabelStyle = useAnimatedStyle(() => {
39
+ const labelWidth = leftValue.length * 8;
40
+ const centeredPosition =
41
+ leftPosition.value + THUMB_SIZE / 2 - labelWidth / 2;
42
+ const clampedPosition = Math.max(
43
+ 0,
44
+ Math.min(centeredPosition, sliderWidth - labelWidth)
45
+ );
46
+ return { transform: [{ translateX: clampedPosition }] };
47
+ });
48
+
49
+ const animatedRightLabelStyle = useAnimatedStyle(() => {
50
+ const labelWidth = rightValue.length * 8;
51
+ const centeredPosition =
52
+ rightPosition.value + THUMB_SIZE / 2 - labelWidth / 2;
53
+ const clampedPosition = Math.max(
54
+ 0,
55
+ Math.min(centeredPosition, sliderWidth - labelWidth)
56
+ );
57
+ return { transform: [{ translateX: clampedPosition }] };
58
+ });
59
+
60
+ const minLabel = isRTL ? rightValue : leftValue;
61
+ const maxLabel = isRTL ? leftValue : rightValue;
62
+ const minStyle = isRTL ? animatedRightLabelStyle : animatedLeftLabelStyle;
63
+ const maxStyle = isRTL ? animatedLeftLabelStyle : animatedRightLabelStyle;
64
+
65
+ return (
66
+ <View style={styles.labelRow}>
67
+ <Animated.View style={[styles.labelContainer, minStyle]}>
68
+ <Text style={styles.labelText}>{minLabel}</Text>
69
+ </Animated.View>
70
+ <Animated.View style={[styles.labelContainer, maxStyle]}>
71
+ <Text style={styles.labelText}>{maxLabel}</Text>
72
+ </Animated.View>
73
+ </View>
74
+ );
75
+ };
76
+
16
77
  interface RangeSliderProps {
17
78
  min: number;
18
79
  max: number;
@@ -21,8 +82,8 @@ interface RangeSliderProps {
21
82
  initialMinValue: number;
22
83
  initialMaxValue: number;
23
84
  onValueChange: (values: { min: number; max: number }) => void;
24
-
25
- theme?: ThemeType
85
+ isRTL?: boolean;
86
+ theme?: ThemeType;
26
87
  }
27
88
 
28
89
  const RangeSlider: React.FC<RangeSliderProps> = ({
@@ -33,47 +94,52 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
33
94
  initialMinValue,
34
95
  initialMaxValue,
35
96
  onValueChange,
36
- theme
97
+ theme,
37
98
  }) => {
38
- const { theme: defaultTheme} = useTheme();
39
-
99
+ const { theme: defaultTheme } = useTheme();
40
100
  const currTheme = theme || defaultTheme;
41
-
101
+ const isRTL = I18nManager.isRTL;
42
102
  const styles = getStyles(currTheme);
43
103
 
44
- // State for label text values, passed down to the SliderLabels component
45
- const [leftLabel, setLeftLabel] = useState(initialMinValue.toLocaleString());
46
- const [rightLabel, setRightLabel] = useState(
47
- initialMaxValue.toLocaleString()
48
- );
104
+ const railWidth = sliderWidth - THUMB_SIZE;
49
105
 
50
106
  const valueToPosition = useCallback(
51
107
  (value: number) => {
52
108
  "worklet";
53
- return ((value - min) / (max - min)) * sliderWidth;
109
+ const percentage = (value - min) / (max - min);
110
+ return isRTL
111
+ ? (1 - percentage) * railWidth
112
+ : percentage * railWidth;
54
113
  },
55
- [min, max, sliderWidth]
114
+ [min, max, railWidth, isRTL]
56
115
  );
57
116
 
58
117
  const positionToValue = useCallback(
59
118
  (position: number) => {
60
119
  "worklet";
61
- const rawValue = (position / sliderWidth) * (max - min) + min;
120
+ const percentage = position / railWidth;
121
+ const rawValue = isRTL
122
+ ? max - percentage * (max - min)
123
+ : percentage * (max - min) + min;
62
124
  return Math.round(rawValue / step) * step;
63
125
  },
64
- [min, max, step, sliderWidth]
126
+ [min, max, step, railWidth, isRTL]
65
127
  );
66
128
 
129
+ // --- Shared Values for Animation ---
67
130
  const leftPosition = useSharedValue(valueToPosition(initialMinValue));
68
131
  const rightPosition = useSharedValue(valueToPosition(initialMaxValue));
132
+ const [leftLabel, setLeftLabel] = useState(initialMinValue.toLocaleString());
133
+ const [rightLabel, setRightLabel] = useState(initialMaxValue.toLocaleString());
69
134
  const context = useSharedValue({ x: 0 });
70
135
  const activeThumb = useSharedValue<"left" | "right" | null>(null);
71
136
 
137
+ // --- Gesture Handler ---
72
138
  const panGesture = Gesture.Pan()
73
139
  .onBegin((e) => {
74
140
  const distToLeft = Math.abs(e.x - leftPosition.value);
75
141
  const distToRight = Math.abs(e.x - rightPosition.value);
76
- if (distToLeft <= distToRight) {
142
+ if (distToLeft < distToRight) {
77
143
  activeThumb.value = "left";
78
144
  context.value = { x: leftPosition.value };
79
145
  } else {
@@ -86,17 +152,11 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
86
152
  const newPos = context.value.x + e.translationX;
87
153
 
88
154
  if (activeThumb.value === "left") {
89
- const clampedPos = Math.max(
90
- 0,
91
- Math.min(newPos, rightPosition.value - THUMB_SIZE)
92
- );
155
+ const clampedPos = Math.max(0, Math.min(newPos, rightPosition.value));
93
156
  leftPosition.value = clampedPos;
94
157
  runOnJS(setLeftLabel)(positionToValue(clampedPos).toLocaleString());
95
158
  } else {
96
- const clampedPos = Math.min(
97
- sliderWidth,
98
- Math.max(newPos, leftPosition.value + THUMB_SIZE)
99
- );
159
+ const clampedPos = Math.min(railWidth, Math.max(newPos, leftPosition.value));
100
160
  rightPosition.value = clampedPos;
101
161
  runOnJS(setRightLabel)(positionToValue(clampedPos).toLocaleString());
102
162
  }
@@ -109,17 +169,18 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
109
169
  activeThumb.value = null;
110
170
  });
111
171
 
172
+ // --- Animated Styles ---
112
173
  const animatedLeftThumbStyle = useAnimatedStyle(() => ({
113
174
  transform: [{ translateX: leftPosition.value }],
114
175
  }));
115
176
 
116
177
  const animatedRightThumbStyle = useAnimatedStyle(() => ({
117
- transform: [{ translateX: rightPosition.value - THUMB_SIZE }],
178
+ transform: [{ translateX: rightPosition.value }],
118
179
  }));
119
180
 
120
181
  const animatedActiveRailStyle = useAnimatedStyle(() => ({
121
- left: leftPosition.value,
122
- right: sliderWidth - rightPosition.value,
182
+ left: leftPosition.value + THUMB_SIZE / 2,
183
+ right: railWidth - rightPosition.value + THUMB_SIZE / 2,
123
184
  }));
124
185
 
125
186
  return (
@@ -130,23 +191,17 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
130
191
  leftPosition={leftPosition}
131
192
  rightPosition={rightPosition}
132
193
  sliderWidth={sliderWidth}
133
- thumbSize={THUMB_SIZE}
194
+ isRTL={isRTL}
195
+ theme={currTheme}
134
196
  />
135
-
136
197
  <GestureDetector gesture={panGesture}>
137
198
  <View style={styles.railContainer}>
138
199
  <View style={styles.rail} />
139
200
  <Animated.View style={[styles.activeRail, animatedActiveRailStyle]} />
140
-
141
- <Animated.View
142
- style={[styles.thumbContainer, animatedLeftThumbStyle]}
143
- >
201
+ <Animated.View style={[styles.thumbContainer, animatedLeftThumbStyle]}>
144
202
  <View style={styles.thumb} />
145
203
  </Animated.View>
146
-
147
- <Animated.View
148
- style={[styles.thumbContainer, animatedRightThumbStyle]}
149
- >
204
+ <Animated.View style={[styles.thumbContainer, animatedRightThumbStyle]}>
150
205
  <View style={styles.thumb} />
151
206
  </Animated.View>
152
207
  </View>
@@ -159,8 +214,7 @@ const getStyles = (theme: ThemeType) =>
159
214
  StyleSheet.create({
160
215
  container: {
161
216
  height: LABEL_HEIGHT + THUMB_SIZE,
162
- justifyContent: "center",
163
- marginTop: LABEL_HEIGHT, // Add margin to prevent label cutoff
217
+ justifyContent: "flex-end",
164
218
  },
165
219
  railContainer: {
166
220
  justifyContent: "center",
@@ -191,6 +245,23 @@ const getStyles = (theme: ThemeType) =>
191
245
  borderColor: theme.primary,
192
246
  borderWidth: 5,
193
247
  },
248
+ labelRow: {
249
+ position: "absolute",
250
+ top: 0,
251
+ width: "100%",
252
+ },
253
+ labelContainer: {
254
+ position: "absolute",
255
+ backgroundColor: theme.primary,
256
+ borderRadius: 4,
257
+ paddingVertical: 4,
258
+ paddingHorizontal: 8,
259
+ height: LABEL_HEIGHT,
260
+ },
261
+ labelText: {
262
+ color: theme.onPrimary,
263
+ fontSize: 12,
264
+ },
194
265
  });
195
266
 
196
- export default RangeSlider;
267
+ export default RangeSlider;
@@ -6,7 +6,6 @@ export interface BottomSheetStackItem {
6
6
  snapPoints?: string[] | number[];
7
7
  initialSnapIndex?: number;
8
8
  dynamicSizing?: boolean;
9
- isModal?: boolean;
10
9
  bottomSheetProps?: Partial<Omit<BottomSheetProps, "children">>;
11
10
  }
12
11
 
@@ -1,8 +1,6 @@
1
1
  import BottomSheet, {
2
2
  BottomSheetBackdrop,
3
3
  BottomSheetBackdropProps,
4
- BottomSheetModal,
5
- BottomSheetModalProvider,
6
4
  } from "@gorhom/bottom-sheet";
7
5
  import React, {
8
6
  ReactNode,
@@ -12,7 +10,7 @@ import React, {
12
10
  useRef,
13
11
  useState,
14
12
  } from "react";
15
- import { BackHandler, StyleSheet, View } from "react-native";
13
+ import { BackHandler, StyleSheet } from "react-native";
16
14
  import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
17
15
  import { useSafeAreaInsets } from "react-native-safe-area-context";
18
16
  import {
@@ -24,10 +22,7 @@ export const BottomSheetStackProvider: React.FC<{ children: ReactNode }> = ({
24
22
  children,
25
23
  }) => {
26
24
  const [stack, setStack] = useState<BottomSheetStackItem[]>([]);
27
-
28
25
  const sheetRef = useRef<BottomSheet>(null);
29
- const modalRef = useRef<BottomSheetModal>(null);
30
-
31
26
  const { top } = useSafeAreaInsets();
32
27
 
33
28
  const push = useCallback((item: BottomSheetStackItem) => {
@@ -36,7 +31,6 @@ export const BottomSheetStackProvider: React.FC<{ children: ReactNode }> = ({
36
31
 
37
32
  const clear = useCallback(() => {
38
33
  sheetRef.current?.close();
39
- modalRef.current?.dismiss();
40
34
  setStack([]);
41
35
  }, []);
42
36
 
@@ -58,30 +52,26 @@ export const BottomSheetStackProvider: React.FC<{ children: ReactNode }> = ({
58
52
 
59
53
  useEffect(() => {
60
54
  if (isOpen && currentItem) {
61
- if (currentItem.isModal) {
62
- sheetRef.current?.close();
63
- modalRef.current?.present();
64
- } else {
65
- modalRef.current?.dismiss();
66
- sheetRef.current?.snapToIndex(currentItem.initialSnapIndex ?? 0);
67
- }
55
+ sheetRef.current?.snapToIndex(currentItem.initialSnapIndex ?? 0);
68
56
  }
69
57
 
70
58
  const backHandlerSubscription = BackHandler.addEventListener(
71
59
  "hardwareBackPress",
72
60
  () => {
73
- if (!isOpen) return false;
74
61
  if (canGoBack) {
75
62
  pop();
76
- } else {
77
- clear();
63
+ return true;
78
64
  }
79
- return true;
65
+ if (!canGoBack && stack.length === 1) {
66
+ sheetRef.current?.close();
67
+ return true;
68
+ }
69
+ return false;
80
70
  }
81
71
  );
82
72
 
83
73
  return () => backHandlerSubscription.remove();
84
- }, [isOpen, canGoBack, currentItem, pop, clear]);
74
+ }, [isOpen, canGoBack, currentItem, pop, stack]);
85
75
 
86
76
  const AnimatedView = Animated.View;
87
77
  const enteringAnimation = FadeIn.duration(200);
@@ -92,6 +82,7 @@ export const BottomSheetStackProvider: React.FC<{ children: ReactNode }> = ({
92
82
  <BottomSheetBackdrop
93
83
  {...props}
94
84
  disappearsOnIndex={-1}
85
+ style={[props.style, { backgroundColor: "rgba(0,0,0,0,1)" }]}
95
86
  appearsOnIndex={0}
96
87
  pressBehavior={"close"}
97
88
  />
@@ -99,61 +90,50 @@ export const BottomSheetStackProvider: React.FC<{ children: ReactNode }> = ({
99
90
  []
100
91
  );
101
92
 
102
- const currentSheetContent = currentItem ? (
103
- <AnimatedView
104
- key={stack.length}
105
- entering={enteringAnimation}
106
- exiting={exitingAnimation}
107
- style={styles.animatedView}
108
- >
109
- {currentItem.component}
110
- </AnimatedView>
111
- ) : null;
112
-
113
93
  return (
114
- <BottomSheetModalProvider>
115
- <BottomSheetStackContext.Provider
116
- value={{ push, pop, replace, clear, canGoBack }}
94
+ <BottomSheetStackContext.Provider
95
+ value={{ push, pop, replace, clear, canGoBack }}
96
+ >
97
+ {children}
98
+ <BottomSheet
99
+ {...currentItem?.bottomSheetProps}
100
+ ref={sheetRef}
101
+ index={-1}
102
+ snapPoints={currentItem?.snapPoints || ["100%"]}
103
+ enableDynamicSizing={currentItem?.dynamicSizing ?? false}
104
+ enablePanDownToClose
105
+ android_keyboardInputMode="adjustResize"
106
+ enableBlurKeyboardOnGesture
107
+ keyboardBlurBehavior="restore"
108
+ keyboardBehavior="fillParent"
109
+ onClose={clear}
110
+ backdropComponent={renderBackdrop}
111
+ topInset={top}
112
+ handleIndicatorStyle={{ backgroundColor: "#CCCCCC" }}
113
+ style={styles.sheetContainer}
117
114
  >
118
- {children}
119
-
120
- <BottomSheet
121
- {...currentItem?.bottomSheetProps}
122
- ref={sheetRef}
123
- index={-1}
124
- snapPoints={currentItem?.isModal ? ["1%"] : (currentItem?.snapPoints || ["100%"])}
125
- enablePanDownToClose
126
- onClose={clear}
127
- backdropComponent={renderBackdrop}
128
- topInset={top}
129
- handleIndicatorStyle={{ backgroundColor: "#CCCCCC" }}
130
- style={styles.sheetContainer}
131
- >
132
- {!currentItem?.isModal ? currentSheetContent : <View />}
133
- </BottomSheet>
134
-
135
- <BottomSheetModal
136
- {...currentItem?.bottomSheetProps}
137
- ref={modalRef}
138
- index={0}
139
- snapPoints={currentItem?.isModal ? (currentItem?.snapPoints || ["100%"]) : ["1%"]}
140
- onDismiss={clear}
141
- topInset={top}
142
- handleIndicatorStyle={{ backgroundColor: "#CCCCCC" }}
143
- style={styles.sheetContainer}
144
- >
145
- {currentItem?.isModal ? currentSheetContent : <View />}
146
- </BottomSheetModal>
147
- </BottomSheetStackContext.Provider>
148
- </BottomSheetModalProvider>
115
+ {currentItem && (
116
+ <AnimatedView
117
+ key={stack.length}
118
+ entering={enteringAnimation}
119
+ exiting={exitingAnimation}
120
+ style={styles.animatedView}
121
+ >
122
+ {currentItem.component}
123
+ </AnimatedView>
124
+ )}
125
+ </BottomSheet>
126
+ </BottomSheetStackContext.Provider>
149
127
  );
150
128
  };
151
129
 
152
130
  const styles = StyleSheet.create({
153
131
  sheetContainer: {
154
- // Styles for the sheet container can be added here
132
+ // backgroundColor: "#FFFFFF",
133
+ // borderTopLeftRadius: 20,
134
+ // borderTopRightRadius: 20,
155
135
  },
156
136
  animatedView: {
157
137
  flex: 1,
158
138
  },
159
- });
139
+ });
package/src/index.ts CHANGED
@@ -8,4 +8,4 @@
8
8
 
9
9
  export * from "./theme"
10
10
  export * from "./components";
11
- export * from "./contexts";
11
+ export * from "./contexts";