Skip to content

Commit 176bbfd

Browse files
ChenlingasMxjaywcjlove
authored andcommitted
fix:SearchBar组件
1 parent 6649bc2 commit 176bbfd

File tree

6 files changed

+268
-0
lines changed

6 files changed

+268
-0
lines changed

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
module.exports = {
22
root: true,
33
extends: '@react-native-community',
4+
rulers: {
5+
'eslintreact-hooks/exhaustive-deps': 0
6+
}
47
};

components/SearchBar/index.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
### 模糊搜素组件
2+
3+
```
4+
<SearchBar
5+
labelInValue
6+
options={[
7+
{label:'上海',value:1},
8+
{label:'南京',value:2}
9+
]}
10+
onFocu={()=>{}}
11+
onChange={val=>console.log('val',val)}
12+
/>
13+
```
14+
### props
15+
16+
| 参数 | 说明 | 类型 | 默认值 |
17+
| -------------------- | ------------ | ------- | ------- |
18+
| `labelInValue` |是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 string 变为 { key: string, label: ReactNode } 的格式 | Boolean | `false` |
19+
| `options` | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }[] | [] |
20+
| `onChange` | 时间变化 | void | - |
21+
| `onFocus` | 获得焦点时回调 | void | () => { } |
22+
| `onChangeText` | 搜索框文字变化 | void | - |
23+
| `disabled` | 是否禁用 | boolean | false |
24+
| `placeholder` | 搜索框默认文本 | string | `输入搜索...` |
25+
| `loading` | 加载中状态 | boolean | false |
26+
| `extra` | 图标 | dom | extra : <Icon xml={down} size={18} /> |

components/SearchBar/index.tsx

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import React, { useState, useEffect } from 'react';
2+
import {
3+
View,
4+
Text,
5+
TextInput,
6+
SafeAreaView,
7+
StyleSheet,
8+
TouchableWithoutFeedback,
9+
ActivityIndicator,
10+
Pressable,
11+
} from 'react-native';
12+
import MaskLayer from '../MaskLayer';
13+
import List from '../List'
14+
import { down } from './svg';
15+
import Icon from '../Icon';
16+
17+
interface SearchBarProps {
18+
onChangeText?: (value: string) => void;
19+
options?: Array<OptionsState>;
20+
onChange?: (value: string) => void;
21+
onFocus?: (e: any | string) => void;
22+
labelInValue?: Boolean;
23+
disabled?: Boolean;
24+
value?: String;
25+
loading?: Boolean;
26+
placeholder?: String;
27+
extra?: JSX.Element;
28+
}
29+
30+
interface OptionsState {
31+
label: string;
32+
value: string | number;
33+
}
34+
35+
// 搜索组件
36+
export default function SearchBar({
37+
onChangeText,
38+
options = [],
39+
onChange,
40+
labelInValue,
41+
disabled,
42+
value,
43+
onFocus,
44+
loading,
45+
placeholder,
46+
extra,
47+
}: SearchBarProps) {
48+
const onHandleChangeText = (val: string) => {
49+
onChangeText && onChangeText(val);
50+
}
51+
const [curValue, setCurValue] = useState<any>(value);
52+
const [visible, setVisivble] = useState(false);
53+
useEffect(() => {
54+
setCurValue(value);
55+
}, [value]);
56+
57+
useEffect(() => {
58+
visible && onFocus;
59+
}, [visible]);
60+
61+
const showSearchBar = () => {
62+
if (disabled) {
63+
return;
64+
}
65+
setVisivble(true);
66+
};
67+
const textValue = labelInValue ? curValue && curValue.label : curValue;
68+
return !visible ? (
69+
<Pressable onPress={showSearchBar}>
70+
<View style={[styles.content]}>
71+
<Text style={styles.contentTitle}>
72+
{textValue ? textValue : placeholder}
73+
</Text>
74+
{React.isValidElement(extra) ? extra : <Icon xml={down} size={18} />}
75+
</View>
76+
</Pressable>
77+
) : (
78+
<MaskLayer visible={visible}>
79+
<SafeAreaView style={styles.container}>
80+
<View style={styles.inputBox}>
81+
<View style={styles.leftBox}>
82+
<View style={styles.icon}>
83+
<Icon name="search" color="#ccc" size={20} />
84+
</View>
85+
<TextInput
86+
placeholderTextColor="#000"
87+
onFocus={onFocus && onFocus}
88+
style={styles.input}
89+
placeholder="输入搜索..."
90+
onChangeText={onHandleChangeText}
91+
/>
92+
</View>
93+
94+
<TouchableWithoutFeedback
95+
onPress={() => {
96+
setVisivble(false);
97+
}}>
98+
<View style={styles.cancel}>
99+
<Text>取消</Text>
100+
</View>
101+
</TouchableWithoutFeedback>
102+
</View>
103+
{loading ? (
104+
<ActivityIndicator
105+
color="#0A0258"
106+
size="large"
107+
style={styles.loading}
108+
/>
109+
) : (
110+
<List style={styles.list}>
111+
{options.map(itm => (
112+
<List.Item
113+
key={itm.value}
114+
onPress={() => {
115+
const selectValue: any | {
116+
key: string;
117+
label: string;
118+
} = labelInValue
119+
? { key: itm.value, label: itm.label }
120+
: itm.value;
121+
onChange && onChange(selectValue);
122+
setCurValue(selectValue);
123+
setVisivble(false);
124+
}}>
125+
<Text style={{ fontSize: 16 }}>{itm.label}</Text>
126+
</List.Item>
127+
))}
128+
</List>
129+
)}
130+
</SafeAreaView>
131+
</MaskLayer>
132+
);
133+
}
134+
135+
const styles = StyleSheet.create({
136+
container: {
137+
flex: 1,
138+
backgroundColor: '#F5F5F5',
139+
},
140+
contentTitle: {
141+
fontSize: 16,
142+
color: 'black',
143+
},
144+
inputBox: {
145+
paddingLeft: 10,
146+
paddingRight: 10,
147+
marginTop: 5,
148+
flexDirection: 'row',
149+
alignItems: 'center',
150+
height: 40,
151+
},
152+
leftBox: {
153+
flex: 1,
154+
flexDirection: 'row',
155+
backgroundColor: '#fff',
156+
alignItems: 'center',
157+
paddingTop: 8,
158+
paddingBottom: 8,
159+
},
160+
icon: {
161+
backgroundColor: '#fff',
162+
paddingLeft: 10,
163+
justifyContent: 'center',
164+
},
165+
input: {
166+
backgroundColor: '#fff',
167+
fontSize: 16,
168+
flex: 1,
169+
paddingLeft: 10,
170+
color: '#7C7D7E',
171+
paddingBottom: 0,
172+
paddingTop: 0,
173+
},
174+
cancel: {
175+
color: '#7C7D7E',
176+
paddingLeft: 10,
177+
justifyContent: 'center',
178+
},
179+
list: {
180+
marginLeft: 10,
181+
marginTop: 10,
182+
marginRight: 10,
183+
},
184+
loading: {
185+
position: 'absolute',
186+
top: '20%',
187+
left: '45%',
188+
},
189+
content: {
190+
flexDirection: 'row',
191+
height: 35,
192+
alignItems: 'center',
193+
justifyContent: 'space-between',
194+
paddingRight: 5,
195+
backgroundColor:"#fff",
196+
paddingHorizontal:16
197+
},
198+
disabled: {
199+
backgroundColor: '#f5f5f5',
200+
},
201+
});

components/SearchBar/svg.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const down = `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="8" viewBox="0 0 13 8">
2+
<polyline fill="none" stroke="#C7C7CC" stroke-width="2" points="365 203 370.45 208.45 365 213.9" transform="rotate(90 289.725 -74.55)"/>
3+
</svg>`;

src/routes.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,12 @@ export const stackPageData: Routes[] = [
241241
description: '布局控件。',
242242
},
243243
},
244+
{
245+
name: 'SearchBar',
246+
component: require('./routes/SearchBar').default,
247+
params: {
248+
title: 'SearchBar 模糊搜索select',
249+
description: '模糊搜索select。',
250+
},
251+
},
244252
];

src/routes/SearchBar/index.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { useState } from 'react';
2+
import { SafeAreaView } from 'react-native';
3+
import SearchBar from '../../../components/SearchBar'
4+
import Layout from '../../Layout';
5+
const SearchBarDemo = (props:any) => {
6+
7+
const { Header } = Layout;
8+
const {route} = props;
9+
const description = route.params.description;
10+
const title = route.params.title;
11+
12+
const [data, setData] = useState([
13+
{ label: '上海', value: 1 },
14+
{ label: '南京', value: 2 }
15+
])
16+
return (
17+
<SafeAreaView style={{ flex: 1 }}>
18+
<Header title={title} description={description} />
19+
<SearchBar
20+
labelInValue
21+
options={data}
22+
onChange={val => console.log('val', val)}
23+
/>
24+
</SafeAreaView>
25+
);
26+
};
27+
export default SearchBarDemo;

0 commit comments

Comments
 (0)