diff --git a/src/components/Field/index.scss b/src/components/Field/index.scss index 3517b4f5b..f7597e5d4 100644 --- a/src/components/Field/index.scss +++ b/src/components/Field/index.scss @@ -74,6 +74,7 @@ $baseClass: 'vant-field'; line-height: inherit; border: 0; resize: none; + padding: 0; &::placeholder { color: $placeholder; diff --git a/src/components/Field/index.tsx b/src/components/Field/index.tsx index c2e0580fb..13ea06e1b 100644 --- a/src/components/Field/index.tsx +++ b/src/components/Field/index.tsx @@ -181,6 +181,7 @@ const Field = ({
{leftIcon && ( ( ); export const IconColor = () => ( -
+
@@ -50,7 +50,7 @@ export const IconColor = () => ( ); export const IconSize = () => ( -
+
@@ -59,7 +59,7 @@ export const IconSize = () => ( ); export const IconDotsAndBadges = () => ( -
+
@@ -67,14 +67,14 @@ export const IconDotsAndBadges = () => (
); export const IconTags = () => ( -
+
); export const IconAction = () => ( -
+
window.alert(e.target)} />
); diff --git a/src/components/Search/index.scss b/src/components/Search/index.scss new file mode 100644 index 000000000..51c39b686 --- /dev/null +++ b/src/components/Search/index.scss @@ -0,0 +1,80 @@ +@import '../../styles/colors.scss'; +@import '../../styles/spacing.scss'; +@import '../../styles/variables.scss'; +@import '../../styles/typography.scss'; + +$baseClass: 'vant-search'; + +.#{$baseClass} { + background-color: $default; + width: 100%; + display: flex; + padding: 10px 12px; + position: relative; + align-items: center; + + &:first-of-type(i) { + position: absolute; + left: 8px; + } + + &__round { + .vant-field { + border-radius: 999px; + } + } + + &__showAction { + padding-right: 0; + } + + &__leftIcon { + .vant-field__error { + padding-left: 24px; + } + } + + .vant-field { + background-color: $grey-background; + padding: 0 0 0 8px; + + .vant-field__input { + padding: 5px 8px 5px 0; + + .vant-icon__container { + padding: 0; + } + + input { + width: 100%; + background-color: $grey-background; + border: none; + border-radius: 2px; + padding-left: $space-md; + } + } + } + + &__action { + padding: 0 $space-md; + cursor: pointer; + + button { + min-height: 34px; + } + + .#{$baseClass}__cancel { + @include search-action; + cursor: pointer; + background-color: $default; + border: 0; + padding: 0; + } + } + + &__disabled { + input { + cursor: not-allowed; + } + } +} diff --git a/src/components/Search/index.stories.tsx b/src/components/Search/index.stories.tsx new file mode 100644 index 000000000..98e58dcfc --- /dev/null +++ b/src/components/Search/index.stories.tsx @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; +import Search from '.'; +import '../../styles/stories.scss'; +import Button from '../Button'; + +export default { + title: 'Search', + component: Search +}; + +export const BasicUsage = () => ( +
+ + +
+); + +export const CustomLabel = () => ( +
+ + + +
+); + +export const BackgroundColor = () => ( +
+ +
+); + +export const MaxLength = () => ( +
+ +
+); + +export const PlaceholderAutoFocus = () => ( +
+ + +
+); + +export const SearchActions = () => { + const handleClick = (e) => { + e.preventDefault(); + alert('Action clicked'); + }; + const [value, setValue] = useState(''); + const [focus, setFocus] = useState(false); + return ( +
+

Value: {value}

+ + + + } + /> + alert('Searched')} + /> + + setValue(e.target.value)} + focus={() => setFocus(true)} + blur={() => setFocus(false)} + /> +
+ ); +}; + +export const DisabledReadonlyError = () => ( +
+ + + +
+); + +export const AlignmentAndIcon = () => ( +
+ + + + +
+); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx new file mode 100644 index 000000000..4caef6d7b --- /dev/null +++ b/src/components/Search/index.tsx @@ -0,0 +1,124 @@ +import React, { useState } from 'react'; + +import classnames from '../../utils/classNames'; + +import './index.scss'; +import { IProps } from './types'; +import Field from '../Field'; + +const baseClass = 'vant-search'; + +const Search = ({ + label, + shape = 'square', + background, + maxLength, + placeholder, + clearable = true, + autofocus, + showAction, + disabled, + readonly, + error, + inputAlign = 'left', + leftIcon = 'search', + rightIcon, + actionText = 'Cancel', + search, + input, + focus, + blur, + clear, + cancel, + action, + errorMessage, + labelAlign, + labelWidth +}: IProps) => { + const [value, setValue] = useState(''); + + const handleSearch = (e) => { + e.preventDefault(); + if (search) search(e); + }; + const handleActionClick = (e) => { + e.preventDefault(); + if (cancel) cancel(e); + }; + + const handleInput = (e) => { + if (input) input(e); + setValue(e.target.value); + }; + + const handleFocus = (e) => { + if (focus) focus(e); + }; + + const handleBlur = (e) => { + if (blur) blur(e); + }; + + const handleClear = (e) => { + e.preventDefault(); + if (clear) { + clear(e); + } + setValue(''); + }; + + const searchProps = { + className: classnames(baseClass, [ + { label }, + { [shape]: shape }, + { disabled }, + { showAction }, + { leftIcon } + ]), + style: {}, + onSubmit: handleSearch + }; + + if (background) + Object.assign(searchProps, { style: { backgroundColor: background } }); + + return ( +
+ + {showAction && ( +
+ {action || ( + + )} +
+ )} + + ); +}; + +export default Search; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index f9f3501eb..00792a8ed 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -1,3 +1,30 @@ +import { ReactElement } from 'react'; +import { TAlignment } from '../Field/types'; + export interface IProps { label?: string; + labelWidth?: string; + labelAlign?: TAlignment; + shape?: 'round' | 'square'; + background?: string; + maxLength?: number; + placeholder?: string; + errorMessage?: string; + clearable?: boolean; + autofocus?: boolean; + showAction?: boolean; + disabled?: boolean; + readonly?: boolean; + error?: boolean; + inputAlign?: TAlignment; + leftIcon?: string; + rightIcon?: string; + actionText?: string; + search?: Function; + input?: Function; + focus?: Function; + blur?: Function; + clear?: Function; + cancel?: Function; + action?: ReactElement; } diff --git a/src/index.tsx b/src/index.tsx index 3748492a6..0c43765c5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,19 +3,22 @@ import Icon from './components/Icons'; import Tag from './components/Tag'; import Navbar from './components/Navbar'; import Field from './components/Field'; +import Search from './components/Search'; export { default as Button } from './components/Button'; export { default as Icon } from './components/Icons'; export { default as Tag } from './components/Tag'; export { default as Navbar } from './components/Navbar'; export { default as Field } from './components/Field'; +export { default as Search } from './components/Search'; const Vant = { Button, Icon, Tag, Navbar, - Field + Field, + Search }; export default Vant; diff --git a/src/styles/colors.scss b/src/styles/colors.scss index 4492ffa39..37acfc48c 100644 --- a/src/styles/colors.scss +++ b/src/styles/colors.scss @@ -10,6 +10,7 @@ $grey: #969799; $dark-text: #323233; $light-text: #fff; $grey-text: #ebedf0; +$grey-background: #f7f8fa; $placeholder: #c8c9cc; $word-limit: #656566; diff --git a/src/styles/stories.scss b/src/styles/stories.scss index 20fad8484..b0f843e78 100644 --- a/src/styles/stories.scss +++ b/src/styles/stories.scss @@ -14,7 +14,7 @@ body { align-items: center; &.grey { - background-color: #f7f8fa; + background-color: $grey-background; } &.button { @@ -51,7 +51,7 @@ body { padding: 15px; display: flex; flex-direction: column; - background-color: #f7f8fa; + background-color: $grey-background; h1 { font-size: 22px; diff --git a/src/styles/typography.scss b/src/styles/typography.scss index c80daee6f..27733cb1b 100644 --- a/src/styles/typography.scss +++ b/src/styles/typography.scss @@ -31,3 +31,9 @@ color: $dark-text; font-weight: 300; } + +@mixin search-action { + font-size: 14px; + line-height: 34px; + font-weight: 300; +}