related-ui-components 2.6.6 → 2.6.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.
@@ -17,6 +17,7 @@ export interface PhoneInputProps extends Omit<CustomInputProps, "leftIcon" | "ri
17
17
  listTitleStyle?: StyleProp<TextStyle>;
18
18
  listTitle?: string;
19
19
  isBottomSheet?: boolean;
20
+ disableCountryPicker?: boolean;
20
21
  }
21
22
  declare const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps & React.RefAttributes<TextInput>>;
22
23
  export default PhoneInput;
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/Input/PhoneInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAChE,OAAO,EACL,SAAS,EAMT,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAoB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AASxD,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,eACf,SAAQ,IAAI,CACV,gBAAgB,EACd,UAAU,GACV,WAAW,GACX,kBAAkB,GAClB,cAAc,GACd,OAAO,GACP,sBAAsB,CACzB;IACD,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IAGtB,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAQD,QAAA,MAAM,UAAU,mFA0Hf,CAAC;AAkBF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"PhoneInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/Input/PhoneInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAChE,OAAO,EACL,SAAS,EAIT,SAAS,EACT,SAAS,EAEV,MAAM,cAAc,CAAC;AACtB,OAAoB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAQxD,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,eACf,SAAQ,IAAI,CACV,gBAAgB,EACd,UAAU,GACV,WAAW,GACX,kBAAkB,GAClB,cAAc,GACd,OAAO,GACP,sBAAsB,CACzB;IACD,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IAGtB,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAQD,QAAA,MAAM,UAAU,mFA6Hf,CAAC;AAkBF,eAAe,UAAU,CAAC"}
@@ -11,7 +11,6 @@ interface RangeSliderProps {
11
11
  min: number;
12
12
  max: number;
13
13
  }) => void;
14
- isRTL?: boolean;
15
14
  theme?: ThemeType;
16
15
  }
17
16
  declare const RangeSlider: React.FC<RangeSliderProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/RangeSlider/RangeSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAQrD,OAAO,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAoElD,UAAU,gBAAgB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA0H3C,CAAC;AAwDF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/RangeSlider/RangeSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,OAAO,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAMlD,UAAU,gBAAgB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAE9D,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAgI3C,CAAC;AAwCF,eAAe,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAWA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "related-ui-components",
3
- "version": "2.6.6",
3
+ "version": "2.6.8",
4
4
  "main": "./src/index.ts",
5
5
  "scripts": {
6
6
  "start": "expo start",
package/src/app.tsx CHANGED
@@ -1,5 +1,5 @@
1
- import React, { useState } from "react";
2
- import { SafeAreaView, StyleSheet, StatusBar, I18nManager } from "react-native";
1
+ import React, { useEffect, useState } from "react";
2
+ import { SafeAreaView, StyleSheet, StatusBar, I18nManager, DevSettings } from "react-native";
3
3
  import CarouselCardStack from "./components/CarouselCardStack/CarouselCardStack"; // Adjust path as needed
4
4
  import { GestureHandlerRootView } from "react-native-gesture-handler";
5
5
  import {
@@ -66,22 +66,21 @@ const App = () => {
66
66
  const newProgress = Math.floor(Math.random() * 101);
67
67
  setProgress(newProgress);
68
68
  };
69
- I18nManager.allowRTL(true);
70
- I18nManager.forceRTL(true);
69
+
71
70
  return (
72
71
  <>
73
- <SafeAreaProvider>
74
- <GestureHandlerRootView style={{ flex: 1 }}>
75
- <RelatedProvider theme={lightTheme}>
76
- <BottomSheetStackProvider>
77
- <StatusBar barStyle="light-content" />
78
- <SafeAreaView style={styles.appContainer}>
79
- {/* <CircularProgressBar
72
+ <SafeAreaProvider>
73
+ <GestureHandlerRootView style={{ flex: 1 }}>
74
+ <RelatedProvider theme={lightTheme}>
75
+ <BottomSheetStackProvider>
76
+ <StatusBar barStyle="light-content" />
77
+ <SafeAreaView style={styles.appContainer}>
78
+ {/* <CircularProgressBar
80
79
  progress={5}
81
80
  lineCap="round"
82
81
  textFont={""}
83
82
  /> */}
84
- {/* <DateRangePicker
83
+ {/* <DateRangePicker
85
84
  onDatesChange={(t) => {
86
85
  setDepartureDate(t.departure);
87
86
  setReturnDate(t.return);
@@ -91,7 +90,7 @@ const App = () => {
91
90
  returnDate={returnDate}
92
91
  // returnDisplayValue={returnDate}
93
92
  ></DateRangePicker> */}
94
- {/* <Filters
93
+ {/* <Filters
95
94
  bottomSheetBackgroundStyle={{
96
95
  borderTopRightRadius: 30,
97
96
  borderTopLeftRadius: 30,
@@ -115,20 +114,24 @@ const App = () => {
115
114
  applyButtonTextStyle={{ color: theme.secondary }}
116
115
  headerStyle={{ borderBottomWidth: 0 }}
117
116
  ></Filters> */}
118
- {/* <FlightSummary></FlightSummary> */}
119
- {/* <FlightForm></FlightForm> */}
120
- {/* <HotelSummary></HotelSummary> */}
121
- {/* <HotelForm></HotelForm> */}
122
- {/* <DateRangePicker onDatesChange={()=>{}} labelContainerStyle={{backgroundColor:"red"}}></DateRangePicker> */}
123
- {/* <CarRentalForm onSelectionChange={console.log}></CarRentalForm> */}
124
- {/* <CarouselCardStack data={DUMMY_DATA} /> */}
125
- {/* <CircularProgressBar variant="steps" totalSteps={4} currentStep={4} size={43} strokeWidth={2} progressColor="#9CA0A3"></CircularProgressBar> */}
126
- <RangeSlider initialMinValue={0} initialMaxValue={100} min={0} max={500} sliderWidth={100} onValueChange={console.log}></RangeSlider>
127
- {/* <PhoneInput value="" onChangeText={() => {}} inputContainerStyle={{height: 55}}></PhoneInput> */}
128
- </SafeAreaView>
129
- </BottomSheetStackProvider>
130
- </RelatedProvider>
131
- </GestureHandlerRootView>
117
+ {/* <FlightSummary></FlightSummary> */}
118
+ {/* <FlightForm></FlightForm> */}
119
+ {/* <HotelSummary></HotelSummary> */}
120
+ {/* <HotelForm></HotelForm> */}
121
+ {/* <DateRangePicker onDatesChange={()=>{}} labelContainerStyle={{backgroundColor:"red"}}></DateRangePicker> */}
122
+ {/* <CarRentalForm onSelectionChange={console.log}></CarRentalForm> */}
123
+ {/* <CarouselCardStack data={DUMMY_DATA} /> */}
124
+ {/* <CircularProgressBar variant="steps" totalSteps={4} currentStep={4} size={43} strokeWidth={2} progressColor="#9CA0A3"></CircularProgressBar> */}
125
+ {/* <RangeSlider initialMinValue={0} initialMaxValue={100} min={0} max={500} sliderWidth={100} onValueChange={console.log}></RangeSlider> */}
126
+ <PhoneInput
127
+ value=""
128
+ onChangeText={() => {}}
129
+ inputContainerStyle={{ height: 55 }}
130
+ ></PhoneInput>
131
+ </SafeAreaView>
132
+ </BottomSheetStackProvider>
133
+ </RelatedProvider>
134
+ </GestureHandlerRootView>
132
135
  </SafeAreaProvider>
133
136
  </>
134
137
  );
@@ -137,7 +140,7 @@ const App = () => {
137
140
  const styles = StyleSheet.create({
138
141
  appContainer: {
139
142
  flex: 1,
140
- backgroundColor:"grey",
143
+ backgroundColor: "grey",
141
144
  justifyContent: "center", // Center the carousel vertically if it's the main content
142
145
  },
143
146
  });
@@ -4,25 +4,33 @@ import {
4
4
  View,
5
5
  TouchableOpacity,
6
6
  StyleSheet,
7
- InteractionManager,
8
- ActivityIndicator,
9
7
  StyleProp,
10
8
  TextStyle,
9
+ Keyboard,
11
10
  } from "react-native";
12
- import { BottomSheetFlashList, BottomSheetFlatList } from "@gorhom/bottom-sheet";
11
+ import { BottomSheetFlatList } from "@gorhom/bottom-sheet";
13
12
  import { useTheme } from "../../theme";
14
13
  import { iso2ToFlagEmoji } from "../../utils/flags";
15
14
  import { Country } from "./PhoneInput";
15
+ import { allCountries } from "country-telephone-data";
16
+ import CustomInput from "./Input"; // Import the CustomInput component
16
17
 
17
18
  interface CountryPickerViewProps {
18
- countries: Country[];
19
+ countries?: Country[];
19
20
  onSelectCountry: (c: Country) => void;
20
21
  listTitle: string;
21
22
  listTitleStyle?: StyleProp<TextStyle>;
22
23
  countryNameStyle?: StyleProp<TextStyle>;
23
24
  countryCodeStyle?: StyleProp<TextStyle>;
25
+ searchPlaceholder?: string;
24
26
  }
25
27
 
28
+ const DEFAULT_COUNTRIES: Country[] = allCountries.map((c) => ({
29
+ code: `+${c.dialCode}`,
30
+ iso2: c.iso2,
31
+ name: c.name,
32
+ }));
33
+
26
34
  const CountryRow = React.memo(
27
35
  ({
28
36
  item,
@@ -35,7 +43,10 @@ const CountryRow = React.memo(
35
43
  nameStyle: StyleProp<TextStyle>;
36
44
  codeStyle: StyleProp<TextStyle>;
37
45
  }) => {
38
- const handlePress = () => onSelectCountry(item);
46
+ const handlePress = () => {
47
+ Keyboard.dismiss(); // Dismiss keyboard on selection
48
+ onSelectCountry(item);
49
+ };
39
50
 
40
51
  return (
41
52
  <TouchableOpacity style={styles.countryRow} onPress={handlePress}>
@@ -50,27 +61,43 @@ const CountryRow = React.memo(
50
61
  );
51
62
 
52
63
  const CountryPickerView = ({
53
- countries,
64
+ countries = DEFAULT_COUNTRIES,
54
65
  onSelectCountry,
55
66
  listTitle,
56
67
  listTitleStyle,
57
68
  countryNameStyle,
58
69
  countryCodeStyle,
70
+ searchPlaceholder = "Search...",
59
71
  }: CountryPickerViewProps) => {
60
72
  const { theme } = useTheme();
61
73
 
62
- // ADDED: Memoize styles to pass them stably to the memoized row component
74
+ const [searchQuery, setSearchQuery] = useState("");
75
+ const [filteredCountries, setFilteredCountries] = useState(countries);
76
+
77
+ useEffect(() => {
78
+ const lowercasedQuery = searchQuery.toLowerCase().trim();
79
+ if (lowercasedQuery === "") {
80
+ setFilteredCountries(countries);
81
+ } else {
82
+ const newFilteredData = countries.filter(
83
+ (country) =>
84
+ country.name.toLowerCase().includes(lowercasedQuery) ||
85
+ country.code.includes(lowercasedQuery)
86
+ );
87
+ setFilteredCountries(newFilteredData);
88
+ }
89
+ }, [searchQuery, countries]);
90
+
63
91
  const memoizedNameStyle = useMemo(
64
92
  () => [styles.countryName, { color: theme.text }, countryNameStyle],
65
93
  [theme.text, countryNameStyle]
66
94
  );
67
95
 
68
96
  const memoizedCodeStyle = useMemo(
69
- () => [styles.countryCode, { color: theme.helper }, countryCodeStyle], // Use a different theme color for contrast
97
+ () => [styles.countryCode, { color: theme.helper }, countryCodeStyle],
70
98
  [theme.helper, countryCodeStyle]
71
99
  );
72
100
 
73
- // CHANGED: renderCountry is now wrapped in useCallback
74
101
  const renderCountry = useCallback(
75
102
  ({ item }: { item: Country }) => (
76
103
  <CountryRow
@@ -88,14 +115,28 @@ const CountryPickerView = ({
88
115
  <Text style={[styles.popupTitle, { color: theme.text }, listTitleStyle]}>
89
116
  {listTitle}
90
117
  </Text>
118
+ <CustomInput
119
+ placeholder={searchPlaceholder}
120
+ value={searchQuery}
121
+ isBottomSheet
122
+ onChangeText={setSearchQuery}
123
+ containerStyle={styles.searchInputContainer}
124
+ />
91
125
  <BottomSheetFlatList
92
- data={countries}
93
- keyExtractor={(c: Country) => c.iso2}
126
+ data={filteredCountries}
127
+ keyExtractor={(c) => c.iso2}
94
128
  renderItem={renderCountry}
95
129
  contentContainerStyle={{ paddingBottom: 16 }}
96
- initialNumToRender={10}
130
+ initialNumToRender={15}
97
131
  maxToRenderPerBatch={10}
98
- // estimatedItemSize={56} // Provide a more accurate estimate
132
+ keyboardShouldPersistTaps="handled"
133
+ ListEmptyComponent={
134
+ <View style={styles.emptyListContainer}>
135
+ <Text style={[styles.emptyListText, { color: theme.helper }]}>
136
+ No countries found.
137
+ </Text>
138
+ </View>
139
+ }
99
140
  />
100
141
  </View>
101
142
  );
@@ -107,29 +148,27 @@ const styles = StyleSheet.create({
107
148
  width: "100%",
108
149
  paddingHorizontal: 16,
109
150
  },
110
- loaderContainer: {
111
- flex: 1,
112
- alignItems: "center",
113
- justifyContent: "center",
114
- },
115
151
  popupTitle: {
116
152
  fontSize: 18,
117
153
  fontWeight: "600",
118
154
  marginBottom: 12,
119
155
  paddingTop: 16,
120
156
  },
157
+ searchInputContainer: {
158
+ marginBottom: 12,
159
+ },
121
160
  countryRow: {
122
161
  flexDirection: "row",
123
162
  alignItems: "center",
124
- paddingVertical: 12, // Adjusted padding
125
- height: 56, // Set a fixed height
163
+ paddingVertical: 12,
164
+ // height: 56,
126
165
  },
127
166
  flag: {
128
167
  fontSize: 22,
129
168
  },
130
169
  countryText: {
131
170
  marginLeft: 12,
132
- flex: 1, // Allow text to take remaining space
171
+ flex: 1,
133
172
  },
134
173
  countryName: {
135
174
  fontSize: 16,
@@ -137,6 +176,13 @@ const styles = StyleSheet.create({
137
176
  countryCode: {
138
177
  fontSize: 14,
139
178
  },
179
+ emptyListContainer: {
180
+ paddingVertical: 40,
181
+ alignItems: "center",
182
+ },
183
+ emptyListText: {
184
+ fontSize: 16,
185
+ },
140
186
  });
141
187
 
142
188
  export default CountryPickerView;
@@ -2,12 +2,11 @@ import React, { forwardRef, useMemo, useCallback } from "react";
2
2
  import {
3
3
  TextInput,
4
4
  Text,
5
- View,
6
5
  TouchableOpacity,
7
6
  StyleSheet,
8
- TextInputProps,
9
7
  StyleProp,
10
8
  TextStyle,
9
+ I18nManager,
11
10
  } from "react-native";
12
11
  import CustomInput, { CustomInputProps } from "./Input";
13
12
  import { useTheme } from "../../theme";
@@ -15,8 +14,7 @@ import { Ionicons } from "@expo/vector-icons";
15
14
  import { useBottomSheetStack } from "../../contexts";
16
15
  import { allCountries } from "country-telephone-data";
17
16
  import { iso2ToFlagEmoji } from "../../utils/flags";
18
- import CountryPickerView from "./CountryPickerView";
19
- import { BottomSheetTextInput } from "@gorhom/bottom-sheet";
17
+ import CountryPickerView from "./CountryPickerView";
20
18
 
21
19
  export type Country = {
22
20
  code: string;
@@ -47,6 +45,8 @@ export interface PhoneInputProps
47
45
 
48
46
  listTitle?: string;
49
47
  isBottomSheet?: boolean;
48
+
49
+ disableCountryPicker?: boolean
50
50
  }
51
51
 
52
52
  const DEFAULT_COUNTRIES: Country[] = allCountries.map((c) => ({
@@ -77,6 +77,7 @@ const PhoneInput = forwardRef<TextInput, PhoneInputProps>(
77
77
  countryCodeStyle,
78
78
  countryNameStyle,
79
79
  isBottomSheet,
80
+ disableCountryPicker = false,
80
81
  ...rest
81
82
  },
82
83
  ref
@@ -130,10 +131,12 @@ const PhoneInput = forwardRef<TextInput, PhoneInputProps>(
130
131
  style={[
131
132
  styles.selector,
132
133
  {
133
- backgroundColor: theme.inputBackground,
134
- borderRightColor: theme.border,
134
+ backgroundColor: "transparent",
135
+ borderRightColor: I18nManager.isRTL ? undefined : theme.border,
136
+ borderLeftColor: I18nManager.isRTL ? theme.border : undefined,
135
137
  },
136
138
  ]}
139
+ disabled={disableCountryPicker}
137
140
  hitSlop={{ left: 30, top: 30, right: 30, bottom: 30 }}
138
141
  onPress={openCountryPicker}
139
142
  activeOpacity={0.8}
@@ -195,4 +198,4 @@ const styles = StyleSheet.create({
195
198
  },
196
199
  });
197
200
 
198
- export default PhoneInput;
201
+ export default PhoneInput;
@@ -1,79 +1,18 @@
1
1
  import React, { useCallback, useState } from "react";
2
- import { StyleSheet, View, Text, I18nManager } from "react-native";
2
+ import { I18nManager, StyleSheet, View } 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";
9
10
  import { ThemeType, useTheme } from "../../theme";
10
11
 
11
12
  const THUMB_SIZE = 28;
12
13
  const RAIL_HEIGHT = 6;
13
14
  const LABEL_HEIGHT = 26;
14
15
 
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
-
77
16
  interface RangeSliderProps {
78
17
  min: number;
79
18
  max: number;
@@ -82,8 +21,8 @@ interface RangeSliderProps {
82
21
  initialMinValue: number;
83
22
  initialMaxValue: number;
84
23
  onValueChange: (values: { min: number; max: number }) => void;
85
- isRTL?: boolean;
86
- theme?: ThemeType;
24
+
25
+ theme?: ThemeType
87
26
  }
88
27
 
89
28
  const RangeSlider: React.FC<RangeSliderProps> = ({
@@ -94,52 +33,47 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
94
33
  initialMinValue,
95
34
  initialMaxValue,
96
35
  onValueChange,
97
- theme,
36
+ theme
98
37
  }) => {
99
- const { theme: defaultTheme } = useTheme();
38
+ const { theme: defaultTheme} = useTheme();
39
+
100
40
  const currTheme = theme || defaultTheme;
101
- const isRTL = I18nManager.isRTL;
41
+
102
42
  const styles = getStyles(currTheme);
103
43
 
104
- const railWidth = sliderWidth - THUMB_SIZE;
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
+ );
105
49
 
106
50
  const valueToPosition = useCallback(
107
51
  (value: number) => {
108
52
  "worklet";
109
- const percentage = (value - min) / (max - min);
110
- return isRTL
111
- ? (1 - percentage) * railWidth
112
- : percentage * railWidth;
53
+ return ((value - min) / (max - min)) * sliderWidth;
113
54
  },
114
- [min, max, railWidth, isRTL]
55
+ [min, max, sliderWidth]
115
56
  );
116
57
 
117
58
  const positionToValue = useCallback(
118
59
  (position: number) => {
119
60
  "worklet";
120
- const percentage = position / railWidth;
121
- const rawValue = isRTL
122
- ? max - percentage * (max - min)
123
- : percentage * (max - min) + min;
61
+ const rawValue = (position / sliderWidth) * (max - min) + min;
124
62
  return Math.round(rawValue / step) * step;
125
63
  },
126
- [min, max, step, railWidth, isRTL]
64
+ [min, max, step, sliderWidth]
127
65
  );
128
66
 
129
- // --- Shared Values for Animation ---
130
67
  const leftPosition = useSharedValue(valueToPosition(initialMinValue));
131
68
  const rightPosition = useSharedValue(valueToPosition(initialMaxValue));
132
- const [leftLabel, setLeftLabel] = useState(initialMinValue.toLocaleString());
133
- const [rightLabel, setRightLabel] = useState(initialMaxValue.toLocaleString());
134
69
  const context = useSharedValue({ x: 0 });
135
70
  const activeThumb = useSharedValue<"left" | "right" | null>(null);
136
71
 
137
- // --- Gesture Handler ---
138
72
  const panGesture = Gesture.Pan()
139
73
  .onBegin((e) => {
140
74
  const distToLeft = Math.abs(e.x - leftPosition.value);
141
75
  const distToRight = Math.abs(e.x - rightPosition.value);
142
- if (distToLeft < distToRight) {
76
+ if (distToLeft <= distToRight) {
143
77
  activeThumb.value = "left";
144
78
  context.value = { x: leftPosition.value };
145
79
  } else {
@@ -152,11 +86,17 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
152
86
  const newPos = context.value.x + e.translationX;
153
87
 
154
88
  if (activeThumb.value === "left") {
155
- const clampedPos = Math.max(0, Math.min(newPos, rightPosition.value));
89
+ const clampedPos = Math.max(
90
+ 0,
91
+ Math.min(newPos, rightPosition.value - THUMB_SIZE)
92
+ );
156
93
  leftPosition.value = clampedPos;
157
94
  runOnJS(setLeftLabel)(positionToValue(clampedPos).toLocaleString());
158
95
  } else {
159
- const clampedPos = Math.min(railWidth, Math.max(newPos, leftPosition.value));
96
+ const clampedPos = Math.min(
97
+ sliderWidth,
98
+ Math.max(newPos, leftPosition.value + THUMB_SIZE)
99
+ );
160
100
  rightPosition.value = clampedPos;
161
101
  runOnJS(setRightLabel)(positionToValue(clampedPos).toLocaleString());
162
102
  }
@@ -169,18 +109,17 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
169
109
  activeThumb.value = null;
170
110
  });
171
111
 
172
- // --- Animated Styles ---
173
112
  const animatedLeftThumbStyle = useAnimatedStyle(() => ({
174
113
  transform: [{ translateX: leftPosition.value }],
175
114
  }));
176
115
 
177
116
  const animatedRightThumbStyle = useAnimatedStyle(() => ({
178
- transform: [{ translateX: rightPosition.value }],
117
+ transform: [{ translateX: rightPosition.value - THUMB_SIZE }],
179
118
  }));
180
119
 
181
120
  const animatedActiveRailStyle = useAnimatedStyle(() => ({
182
- left: leftPosition.value + THUMB_SIZE / 2,
183
- right: railWidth - rightPosition.value + THUMB_SIZE / 2,
121
+ left: leftPosition.value,
122
+ right: sliderWidth - rightPosition.value,
184
123
  }));
185
124
 
186
125
  return (
@@ -191,17 +130,23 @@ const RangeSlider: React.FC<RangeSliderProps> = ({
191
130
  leftPosition={leftPosition}
192
131
  rightPosition={rightPosition}
193
132
  sliderWidth={sliderWidth}
194
- isRTL={isRTL}
195
- theme={currTheme}
133
+ thumbSize={THUMB_SIZE}
196
134
  />
135
+
197
136
  <GestureDetector gesture={panGesture}>
198
137
  <View style={styles.railContainer}>
199
138
  <View style={styles.rail} />
200
139
  <Animated.View style={[styles.activeRail, animatedActiveRailStyle]} />
201
- <Animated.View style={[styles.thumbContainer, animatedLeftThumbStyle]}>
140
+
141
+ <Animated.View
142
+ style={[styles.thumbContainer, animatedLeftThumbStyle]}
143
+ >
202
144
  <View style={styles.thumb} />
203
145
  </Animated.View>
204
- <Animated.View style={[styles.thumbContainer, animatedRightThumbStyle]}>
146
+
147
+ <Animated.View
148
+ style={[styles.thumbContainer, animatedRightThumbStyle]}
149
+ >
205
150
  <View style={styles.thumb} />
206
151
  </Animated.View>
207
152
  </View>
@@ -214,7 +159,8 @@ const getStyles = (theme: ThemeType) =>
214
159
  StyleSheet.create({
215
160
  container: {
216
161
  height: LABEL_HEIGHT + THUMB_SIZE,
217
- justifyContent: "flex-end",
162
+ justifyContent: "center",
163
+ marginTop: LABEL_HEIGHT, // Add margin to prevent label cutoff
218
164
  },
219
165
  railContainer: {
220
166
  justifyContent: "center",
@@ -245,23 +191,6 @@ const getStyles = (theme: ThemeType) =>
245
191
  borderColor: theme.primary,
246
192
  borderWidth: 5,
247
193
  },
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
- },
265
194
  });
266
195
 
267
- export default RangeSlider;
196
+ export default RangeSlider;
package/src/index.ts CHANGED
@@ -1,11 +1,14 @@
1
- // import { registerRootComponent } from 'expo';
1
+ // import { registerRootComponent } from "expo";
2
2
  // import "react-native-reanimated";
3
3
 
4
-
5
4
  // import App from "./app";
5
+ // import { I18nManager } from "react-native";
6
+
7
+ // I18nManager.allowRTL(true);
8
+ // I18nManager.forceRTL(true);
6
9
 
7
10
  // registerRootComponent(App);
8
11
 
9
- export * from "./theme"
12
+ export * from "./theme";
10
13
  export * from "./components";
11
14
  export * from "./contexts";