Skip to content

Commit 0afbacc

Browse files
authored
Merge pull request mxdi9i7#31 from mxdi9i7/peterz/search
Search component
2 parents d080634 + e28192d commit 0afbacc

File tree

11 files changed

+356
-8
lines changed

11 files changed

+356
-8
lines changed

src/components/Field/index.scss

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ $baseClass: 'vant-field';
7474
line-height: inherit;
7575
border: 0;
7676
resize: none;
77+
padding: 0;
7778

7879
&::placeholder {
7980
color: $placeholder;

src/components/Field/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ const Field = ({
181181
<div className={`${baseClass}__field`}>
182182
{leftIcon && (
183183
<Icon
184+
color='#323233'
184185
click={handleClickLeftIcon}
185186
name={leftIcon}
186187
size={ICON_SIZE}

src/components/Icons/index.stories.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const AllIcons = () => (
4141
);
4242

4343
export const IconColor = () => (
44-
<div className='container'>
44+
<div className='icons container'>
4545
<Icon name='chat-o' color='#1976D2' />
4646
<Icon name='add' color='#BBDEFB' />
4747
<Icon name='add-square' color='#4CAF50' />
@@ -50,7 +50,7 @@ export const IconColor = () => (
5050
);
5151

5252
export const IconSize = () => (
53-
<div className='container' size='18px'>
53+
<div className='container icons' size='18px'>
5454
<Icon name='chat-o' size='28px' />
5555
<Icon name='add' size='38px' />
5656
<Icon name='add-square' size='48px' />
@@ -59,22 +59,22 @@ export const IconSize = () => (
5959
);
6060

6161
export const IconDotsAndBadges = () => (
62-
<div className='container'>
62+
<div className='container icons'>
6363
<Icon name='chat-o' dot />
6464
<Icon name='add' badge='1' />
6565
<Icon name='add-square' badge='12' />
6666
<Icon name='after-sale' badge='99+' />
6767
</div>
6868
);
6969
export const IconTags = () => (
70-
<div className='container'>
70+
<div className='container icons'>
7171
<Icon name='chat-o' tag='i' />
7272
<Icon name='add' tag='span' />
7373
</div>
7474
);
7575

7676
export const IconAction = () => (
77-
<div className='container'>
77+
<div className='container icons'>
7878
<Icon name='chat-o' click={(e) => window.alert(e.target)} />
7979
</div>
8080
);

src/components/Search/index.scss

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
@import '../../styles/colors.scss';
2+
@import '../../styles/spacing.scss';
3+
@import '../../styles/variables.scss';
4+
@import '../../styles/typography.scss';
5+
6+
$baseClass: 'vant-search';
7+
8+
.#{$baseClass} {
9+
background-color: $default;
10+
width: 100%;
11+
display: flex;
12+
padding: 10px 12px;
13+
position: relative;
14+
align-items: center;
15+
16+
&:first-of-type(i) {
17+
position: absolute;
18+
left: 8px;
19+
}
20+
21+
&__round {
22+
.vant-field {
23+
border-radius: 999px;
24+
}
25+
}
26+
27+
&__showAction {
28+
padding-right: 0;
29+
}
30+
31+
&__leftIcon {
32+
.vant-field__error {
33+
padding-left: 24px;
34+
}
35+
}
36+
37+
.vant-field {
38+
background-color: $grey-background;
39+
padding: 0 0 0 8px;
40+
41+
.vant-field__input {
42+
padding: 5px 8px 5px 0;
43+
44+
.vant-icon__container {
45+
padding: 0;
46+
}
47+
48+
input {
49+
width: 100%;
50+
background-color: $grey-background;
51+
border: none;
52+
border-radius: 2px;
53+
padding-left: $space-md;
54+
}
55+
}
56+
}
57+
58+
&__action {
59+
padding: 0 $space-md;
60+
cursor: pointer;
61+
62+
button {
63+
min-height: 34px;
64+
}
65+
66+
.#{$baseClass}__cancel {
67+
@include search-action;
68+
cursor: pointer;
69+
background-color: $default;
70+
border: 0;
71+
padding: 0;
72+
}
73+
}
74+
75+
&__disabled {
76+
input {
77+
cursor: not-allowed;
78+
}
79+
}
80+
}
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React, { useState } from 'react';
2+
import Search from '.';
3+
import '../../styles/stories.scss';
4+
import Button from '../Button';
5+
6+
export default {
7+
title: 'Search',
8+
component: Search
9+
};
10+
11+
export const BasicUsage = () => (
12+
<div className='container column grey'>
13+
<Search />
14+
<Search shape='round' />
15+
</div>
16+
);
17+
18+
export const CustomLabel = () => (
19+
<div className='container column grey'>
20+
<Search
21+
placeholder='Label width of 65px'
22+
label='Address'
23+
labelWidth='65px'
24+
/>
25+
<Search label='Address' labelAlign='center' />
26+
<Search label='Address' labelAlign='right' />
27+
</div>
28+
);
29+
30+
export const BackgroundColor = () => (
31+
<div className='container grey'>
32+
<Search background='rgb(79, 192, 141)' />
33+
</div>
34+
);
35+
36+
export const MaxLength = () => (
37+
<div className='container grey'>
38+
<Search maxLength={5} />
39+
</div>
40+
);
41+
42+
export const PlaceholderAutoFocus = () => (
43+
<div className='container grey column'>
44+
<Search placeholder='This is a placeholder' />
45+
<Search placeholder='This is a auto focus' autofocus />
46+
</div>
47+
);
48+
49+
export const SearchActions = () => {
50+
const handleClick = (e) => {
51+
e.preventDefault();
52+
alert('Action clicked');
53+
};
54+
const [value, setValue] = useState('');
55+
const [focus, setFocus] = useState(false);
56+
return (
57+
<div className='container column grey'>
58+
<h1>Value: {value}</h1>
59+
<Search cancel={handleClick} showAction />
60+
<Search cancel={handleClick} showAction actionText='Clear' />
61+
<Search
62+
showAction
63+
action={
64+
<Button
65+
click={handleClick}
66+
text='Action'
67+
type='primary'
68+
size='small'
69+
block
70+
/>
71+
}
72+
/>
73+
<Search
74+
placeholder='Search Action, press Enter to search'
75+
search={() => alert('Searched')}
76+
/>
77+
78+
<Search
79+
rightIcon={focus ? 'success' : 'cross'}
80+
clearable={false}
81+
placeholder='Focus blur and input actions'
82+
input={(e) => setValue(e.target.value)}
83+
focus={() => setFocus(true)}
84+
blur={() => setFocus(false)}
85+
/>
86+
</div>
87+
);
88+
};
89+
90+
export const DisabledReadonlyError = () => (
91+
<div className='container column grey'>
92+
<Search disabled />
93+
<Search readonly />
94+
<Search error errorMessage='Something is up' />
95+
</div>
96+
);
97+
98+
export const AlignmentAndIcon = () => (
99+
<div className='container column grey'>
100+
<Search inputAlign='center' placeholder='This is a placeholder' />
101+
<Search inputAlign='right' placeholder='This is a placeholder' />
102+
<Search leftIcon='smile-o' />
103+
<Search leftIcon='' rightIcon='search' clearable={false} />
104+
</div>
105+
);

src/components/Search/index.tsx

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React, { useState } from 'react';
2+
3+
import classnames from '../../utils/classNames';
4+
5+
import './index.scss';
6+
import { IProps } from './types';
7+
import Field from '../Field';
8+
9+
const baseClass = 'vant-search';
10+
11+
const Search = ({
12+
label,
13+
shape = 'square',
14+
background,
15+
maxLength,
16+
placeholder,
17+
clearable = true,
18+
autofocus,
19+
showAction,
20+
disabled,
21+
readonly,
22+
error,
23+
inputAlign = 'left',
24+
leftIcon = 'search',
25+
rightIcon,
26+
actionText = 'Cancel',
27+
search,
28+
input,
29+
focus,
30+
blur,
31+
clear,
32+
cancel,
33+
action,
34+
errorMessage,
35+
labelAlign,
36+
labelWidth
37+
}: IProps) => {
38+
const [value, setValue] = useState('');
39+
40+
const handleSearch = (e) => {
41+
e.preventDefault();
42+
if (search) search(e);
43+
};
44+
const handleActionClick = (e) => {
45+
e.preventDefault();
46+
if (cancel) cancel(e);
47+
};
48+
49+
const handleInput = (e) => {
50+
if (input) input(e);
51+
setValue(e.target.value);
52+
};
53+
54+
const handleFocus = (e) => {
55+
if (focus) focus(e);
56+
};
57+
58+
const handleBlur = (e) => {
59+
if (blur) blur(e);
60+
};
61+
62+
const handleClear = (e) => {
63+
e.preventDefault();
64+
if (clear) {
65+
clear(e);
66+
}
67+
setValue('');
68+
};
69+
70+
const searchProps = {
71+
className: classnames(baseClass, [
72+
{ label },
73+
{ [shape]: shape },
74+
{ disabled },
75+
{ showAction },
76+
{ leftIcon }
77+
]),
78+
style: {},
79+
onSubmit: handleSearch
80+
};
81+
82+
if (background)
83+
Object.assign(searchProps, { style: { backgroundColor: background } });
84+
85+
return (
86+
<form {...searchProps}>
87+
<Field
88+
value={value}
89+
label={label}
90+
labelAlign={labelAlign}
91+
labelWidth={labelWidth}
92+
input={handleInput}
93+
blur={handleBlur}
94+
focus={handleFocus}
95+
clear={handleClear}
96+
clearable={clearable}
97+
leftIcon={leftIcon}
98+
maxLength={maxLength}
99+
placeholder={placeholder}
100+
autofocus={autofocus}
101+
readonly={readonly}
102+
disabled={disabled}
103+
error={error}
104+
errorMessage={errorMessage}
105+
inputAlign={inputAlign}
106+
rightIcon={rightIcon}
107+
/>
108+
{showAction && (
109+
<div className={`${baseClass}__action`}>
110+
{action || (
111+
<button
112+
onClick={handleActionClick}
113+
className={`${baseClass}__cancel`}
114+
>
115+
{actionText}
116+
</button>
117+
)}
118+
</div>
119+
)}
120+
</form>
121+
);
122+
};
123+
124+
export default Search;

0 commit comments

Comments
 (0)