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.
- package/lib/module/app.js +8 -11
- package/lib/module/app.js.map +1 -1
- package/lib/module/components/Input/CountryPickerView.js +60 -25
- package/lib/module/components/Input/CountryPickerView.js.map +1 -1
- package/lib/module/components/Input/PhoneInput.js +6 -3
- package/lib/module/components/Input/PhoneInput.js.map +1 -1
- package/lib/module/components/RangeSlider/RangeSlider.js +19 -98
- package/lib/module/components/RangeSlider/RangeSlider.js.map +1 -1
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/app.d.ts.map +1 -1
- package/lib/typescript/src/components/Input/CountryPickerView.d.ts +3 -2
- package/lib/typescript/src/components/Input/CountryPickerView.d.ts.map +1 -1
- package/lib/typescript/src/components/Input/PhoneInput.d.ts +1 -0
- package/lib/typescript/src/components/Input/PhoneInput.d.ts.map +1 -1
- package/lib/typescript/src/components/RangeSlider/RangeSlider.d.ts +0 -1
- package/lib/typescript/src/components/RangeSlider/RangeSlider.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/app.tsx +31 -28
- package/src/components/Input/CountryPickerView.tsx +67 -21
- package/src/components/Input/PhoneInput.tsx +10 -7
- package/src/components/RangeSlider/RangeSlider.tsx +42 -113
- package/src/index.ts +6 -3
|
@@ -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,
|
|
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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../../../../src/components/RangeSlider/RangeSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,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":"
|
|
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
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
|
-
|
|
70
|
-
I18nManager.forceRTL(true);
|
|
69
|
+
|
|
71
70
|
return (
|
|
72
71
|
<>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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 {
|
|
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
|
|
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 = () =>
|
|
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
|
-
|
|
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],
|
|
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={
|
|
93
|
-
keyExtractor={(c
|
|
126
|
+
data={filteredCountries}
|
|
127
|
+
keyExtractor={(c) => c.iso2}
|
|
94
128
|
renderItem={renderCountry}
|
|
95
129
|
contentContainerStyle={{ paddingBottom: 16 }}
|
|
96
|
-
initialNumToRender={
|
|
130
|
+
initialNumToRender={15}
|
|
97
131
|
maxToRenderPerBatch={10}
|
|
98
|
-
|
|
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,
|
|
125
|
-
height: 56,
|
|
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,
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
38
|
+
const { theme: defaultTheme} = useTheme();
|
|
39
|
+
|
|
100
40
|
const currTheme = theme || defaultTheme;
|
|
101
|
-
|
|
41
|
+
|
|
102
42
|
const styles = getStyles(currTheme);
|
|
103
43
|
|
|
104
|
-
|
|
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
|
-
|
|
110
|
-
return isRTL
|
|
111
|
-
? (1 - percentage) * railWidth
|
|
112
|
-
: percentage * railWidth;
|
|
53
|
+
return ((value - min) / (max - min)) * sliderWidth;
|
|
113
54
|
},
|
|
114
|
-
[min, max,
|
|
55
|
+
[min, max, sliderWidth]
|
|
115
56
|
);
|
|
116
57
|
|
|
117
58
|
const positionToValue = useCallback(
|
|
118
59
|
(position: number) => {
|
|
119
60
|
"worklet";
|
|
120
|
-
const
|
|
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,
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
183
|
-
right:
|
|
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
|
-
|
|
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
|
-
|
|
140
|
+
|
|
141
|
+
<Animated.View
|
|
142
|
+
style={[styles.thumbContainer, animatedLeftThumbStyle]}
|
|
143
|
+
>
|
|
202
144
|
<View style={styles.thumb} />
|
|
203
145
|
</Animated.View>
|
|
204
|
-
|
|
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: "
|
|
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
|
|
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";
|