1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
import React from "react";
// import native component
import {
SafeAreaView,
Dimensions,
StyleSheet,
View,
Text,
Image,
FlatList,
Platform,
StatusBar
} from "react-native";
//icon
import { Ionicons } from "@expo/vector-icons";
//Colors
import { COLORS } from "../../../constans";
//Search Item Component
import SearchItem from "./SearchItem";
import Animated, { EasingNode } from "react-native-reanimated";
import { TouchableOpacity, TextInput } from "react-native-gesture-handler";
const { Value } = Animated;
// Calculate window size
const { width, height } = Dimensions.get("window");
export class Header extends React.Component {
constructor(props) {
super(props);
//state
this.state = {
isFocused: false,
keyword: '',
productsFilter: ''
}
//---
this.textInput = React.createRef();
//---
//animated values
this._input_box_translate_x = new Value(width);
this._back_button_opacity = new Value(0);
this._content_translate_y = new Value(height);
this._content_opacity = new Value(0);
}
// Function Search
searchFilterFunction = ( searchText ) => {
const data = this.props.products.filter((product) =>
product.name.toLowerCase().includes(searchText.toLowerCase())
);
this.setState({ keyword: searchText, productsFilter: data });
};
_onFocus = () => {
// update state
this.setState({ isFocused: true });
// animation config
// input box
const input_box_translate_x_config = {
duration: 200,
toValue: 0,
easing: EasingNode.inOut(EasingNode.ease)
};
const back_button_opacity_config = {
duration: 200,
toValue: 1,
easing: EasingNode.inOut(EasingNode.ease)
};
// content
const content_translate_y_config = {
duration: 0,
toValue: 0,
easing: EasingNode.inOut(EasingNode.ease)
};
const content_opacity_config = {
duration: 200,
toValue: 1,
easing: EasingNode.inOut(EasingNode.ease)
};
// run animation
Animated.timing(this._input_box_translate_x, input_box_translate_x_config).start();
Animated.timing(this._back_button_opacity, back_button_opacity_config).start();
Animated.timing(this._content_translate_y, content_translate_y_config).start();
Animated.timing(this._content_opacity, content_opacity_config).start();
// force focus
// this.refs.textInput.focus();
this.textInput.current?.focus();
}
_onBlur = () => {
// update state
this.setState({ isFocused: false });
// animation config
// input box
const input_box_translate_x_config = {
duration: 50,
toValue: width,
easing: EasingNode.inOut(EasingNode.ease),
};
const back_button_opacity_config = {
duration: 50,
toValue: 0,
easing: EasingNode.inOut(EasingNode.ease),
};
// content
const content_translate_y_config = {
duration: 0,
toValue: height,
easing: EasingNode.inOut(EasingNode.ease),
};
const content_opacity_config = {
duration: 200,
toValue: 0,
easing: EasingNode.inOut(EasingNode.ease),
};
// run animation
Animated.timing(this._input_box_translate_x, input_box_translate_x_config).start();
Animated.timing(this._back_button_opacity, back_button_opacity_config).start();
Animated.timing(this._content_translate_y, content_translate_y_config).start();
Animated.timing(this._content_opacity, content_opacity_config).start();
// force blur
//this.refs.textInput.blur();
this.textInput.current.blur();
};
render() {
const scrollY = this.props.scrollPoint;
const headerPlatform = 50;
const clampedScrollY = scrollY.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
});
const _diff_clamp_scroll_y = Animated.diffClamp(
clampedScrollY,
0,
headerPlatform,
);
const _header_translate_y = Animated.interpolateNode(_diff_clamp_scroll_y, {
inputRange: [0, headerPlatform],
outputRange: [0, -headerPlatform],
extrapolate: 'clamp',
});
const _header_opacity = Animated.interpolateNode(_diff_clamp_scroll_y, {
inputRange: [0, headerPlatform],
outputRange: [1, 0],
extrapolate: 'clamp',
});
return (
<>
<SafeAreaView style={{ ...styles.header_safe_area, ...this.props.style }} >
<Animated.View
style={[
styles.header,
{
transform: [{translateY: _header_translate_y}],
opacity: _header_opacity,
},
]}
>
<View style={styles.header_inner}>
<TouchableOpacity onPress={() => console.log('toggleDrawer')/*this.props.navigation.toggleDrawer()*/}>
<Ionicons
name="ios-menu"
size={30}
color={COLORS.lighter_green}
/>
</TouchableOpacity>
<View>
<Image
source={require('../../../assets/images/logo.png')}
style={{
width: height < 668 ? 180 : 170,
resizeMode: 'contain'
}}
/>
</View>
<TouchableOpacity
activeOpacity={1}
underlayColor={'#ccd0d5'}
onPress={this._onFocus}
style={styles.search_icon_box}
>
<Ionicons name='ios-search' size={20} color={COLORS.black} />
</TouchableOpacity>
<Animated.View
style={[styles.input_box, {transform: [{ translateX: this._input_box_translate_x }] } ]}
>
<Animated.View style={{ opacity: this._back_button_opacity }}>
<TouchableOpacity
activeOpacity={1}
underlayColor={'#ccd0d5'}
onPress={this._onBlur}
style={styles.back_icon_box}
>
<Ionicons name="ios-arrow-back" size={25} color={COLORS.light_green} />
</TouchableOpacity>
</Animated.View>
<TextInput
//ref="textInput"
ref={this.textInput}
placeholder="Поиск товара"
clearButtonMode='always'
value={this.state.keyword}
onChangeText={(value) => this.searchFilterFunction(value)}
style={styles.input}
/>
</Animated.View>
</View>
</Animated.View>
</SafeAreaView>
<Animated.View
style={[
styles.content,
{ opacity: this._content_opacity, transform: [{ translateY: this._content_translate_y }] }
]}
>
<View style={styles.content_safe_area}>
{this.state.keyword === '' ? (
<View style={styles.image_placeholder_container}>
<Image
source={require('../../../assets/images/logo1.png')}
style={styles.image_placeholder}
/>
<Text style={styles.image_placeholder_text} >
Введите ключевое слово...
</Text>
</View>
) : (
<View
style={{
marginHorizontal: 20,
marginTop:
Platform.OS === 'android' ? 0 : height < 668 ? 0 : 60,
}}
>
{this.state.productsFilter.length === 0 ? (
<Text style={styles.image_placeholder_text}>Товары не найдены</Text>
) : (
<FlatList
data={this.state.productsFilter}
keyExtractor={(item) => item.id}
renderItem={ ({ item }) => {
return (
<SearchItem
item={item}
navigation={this.props.navigation}
/>
);
}}
/>
)}
</View>
)}
</View>
</Animated.View>
</>
)
}
}
const styles = StyleSheet.create({
header_safe_area: {
zIndex: 1000,
backgroundColor: COLORS.white,
paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0,
},
header: {
position: 'absolute',
backgroundColor: COLORS.white,
width,
height: 50,
top:
Platform.OS === 'android'
? StatusBar.currentHeight
: height > 736
? 40
: 20,
},
header_inner: {
flex: 1,
overflow: 'hidden',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 15,
},
search_icon_box: {
width: 35,
height: 35,
borderRadius: 35,
backgroundColor: COLORS.lighter_green,
borderWidth: 1,
borderColor: COLORS.white,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
input_box: {
height: 50,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
top: 0,
left: 0,
backgroundColor: COLORS.white,
width: width,
},
back_icon_box: {
width: 40,
height: 40,
borderRadius: 40,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
input: {
flex: 1,
height: 40,
backgroundColor: COLORS.light_grey,
borderRadius: 16,
paddingHorizontal: 16,
fontSize: 15,
marginHorizontal: 20,
},
content: {
width: width,
height: height,
position: 'absolute',
left: 0,
zIndex: 999,
},
content_safe_area: {
flex: 1,
paddingTop: Platform.OS === 'android' ? 80 : 40,
paddingBottom: 80,
backgroundColor: COLORS.white,
},
content_inner: {
backgroundColor: COLORS.white,
borderTopWidth: 1,
borderTopColor: COLORS.light_grey,
},
image_placeholder_container: {
flexDirection: 'column',
marginTop: 100,
},
image_placeholder: {
height: 80,
resizeMode: 'contain',
alignSelf: 'center',
},
image_placeholder_text: {
textAlign: 'center',
color: 'black',
marginTop: 5,
},
search_item: {
flexDirection: 'row',
height: 40,
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#e6e4eb',
marginLeft: 16,
},
item_icon: {
marginRight: 15,
}
});