forked from Tencent/tdesign-mobile-react
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update search style to v2, alignment vue mobile feat Tencent#473
- Loading branch information
Showing
6 changed files
with
203 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,137 +1,148 @@ | ||
import React, { FC, useState, useRef } from 'react'; | ||
import { CloseCircleFilledIcon } from 'tdesign-icons-react'; | ||
import React, { useState, useRef } from 'react'; | ||
import type { FC, FormEvent, CompositionEvent, MouseEvent, KeyboardEvent, FocusEvent } from 'react'; | ||
import { CloseCircleFilledIcon, SearchIcon } from 'tdesign-icons-react'; | ||
import isFunction from 'lodash/isFunction'; | ||
import { Button } from '../button'; | ||
import classNames from 'classnames'; | ||
import useDefault from '../_util/useDefault'; | ||
import parseTNode from '../_util/parseTNode'; | ||
import useConfig from '../_util/useConfig'; | ||
import type { TdSearchProps } from './type'; | ||
import type { StyledProps } from '../common'; | ||
import { searchDefaultProps } from './defaultProps' | ||
import { searchDefaultProps } from './defaultProps'; | ||
import { ENTER_REG } from '../_common/js/common'; | ||
import useDefaultProps from '../hooks/useDefaultProps'; | ||
|
||
export interface SearchProps extends TdSearchProps, StyledProps {} | ||
|
||
const Search: FC<SearchProps> = (props) => { | ||
const { | ||
className = '', | ||
style = {}, | ||
clearable, | ||
action = '', | ||
center, | ||
disabled, | ||
focus, | ||
label, | ||
leftIcon, | ||
placeholder, | ||
rightIcon, | ||
readonly, | ||
shape = 'square', | ||
value = '', | ||
onActionClick, | ||
onBlur, | ||
onChange, | ||
onClear, | ||
onFocus, | ||
onSubmit, | ||
} = props; | ||
} = useDefaultProps(props, searchDefaultProps); | ||
const [focusState, setFocus] = useState(focus); | ||
const inputRef = useRef(null); | ||
const [searchValue, setSearchValue] = useDefault(value, '', onChange); | ||
|
||
const { classPrefix } = useConfig(); | ||
const rootClassName = `${classPrefix}-search`; | ||
const boxClasses = classNames(`${rootClassName}__input-box`, `${rootClassName}__input-box--${shape}`, { | ||
[`${classPrefix}-is-focused`]: focusState, | ||
}); | ||
const inputClasses = classNames(`${classPrefix}-input__keyword`, { | ||
[`${rootClassName}--center`]: center, | ||
}); | ||
|
||
const inputRef = useRef(null); | ||
const [focusState, setFocus] = useState(focus); | ||
const inputValueChangeHandle = (e: FormEvent<HTMLInputElement>) => { | ||
const { value } = e.target as HTMLInputElement; | ||
setSearchValue(value, { trigger: 'input-change', e }); | ||
}; | ||
|
||
const handleInput = (e: FormEvent<HTMLInputElement>) => { | ||
if (e instanceof InputEvent) { | ||
// 中文输入的时候inputType是insertCompositionText所以中文输入的时候禁止触发。 | ||
const checkInputType = e.inputType && e.inputType === 'insertCompositionText'; | ||
if (e.isComposing || checkInputType) return; | ||
} | ||
|
||
function handleBlur(e: React.FocusEvent<HTMLInputElement, Element>) { | ||
setFocus(false); | ||
inputRef.current.blur(); | ||
const { value } = e.currentTarget; | ||
isFunction(onBlur) && onBlur(value, { e }); | ||
} | ||
inputValueChangeHandle(e); | ||
}; | ||
|
||
function handleClear(e: React.MouseEvent<SVGSVGElement, MouseEvent>) { | ||
const handleClear = (e: MouseEvent) => { | ||
setSearchValue('', { trigger: 'input-change' }); | ||
setFocus(true); | ||
isFunction(onClear) && onClear({ e }); | ||
isFunction(onChange) && onChange(''); | ||
} | ||
}; | ||
|
||
function handleAction(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) { | ||
isFunction(onActionClick) && onActionClick({ e }); | ||
} | ||
const handleFocus = (e: FocusEvent) => { | ||
isFunction(onFocus) && onFocus({ value: searchValue, e }); | ||
}; | ||
|
||
function handleChange(e: React.ChangeEvent<HTMLInputElement> | React.CompositionEvent<HTMLInputElement>) { | ||
const { value } = e.currentTarget; | ||
isFunction(onChange) && onChange(value, { e }); | ||
} | ||
const handleBlur = (e: FocusEvent) => { | ||
isFunction(onBlur) && onBlur({ value: searchValue, e }); | ||
}; | ||
|
||
function handleFocus(e: React.FocusEvent<HTMLInputElement, Element>) { | ||
const { value } = e.currentTarget; | ||
isFunction(onFocus) && onFocus(value, { e }); | ||
} | ||
const handleCompositionend = (e: CompositionEvent) => { | ||
inputValueChangeHandle(e as CompositionEvent<HTMLInputElement>); | ||
}; | ||
|
||
function handleSubmit(e: React.FocusEvent<HTMLInputElement, Element>) { | ||
const { value } = e.currentTarget; | ||
isFunction(onSubmit) && onSubmit(value, { e }); | ||
} | ||
const handleAction = (e: MouseEvent) => { | ||
isFunction(onActionClick) && onActionClick({ e }); | ||
}; | ||
|
||
function handleClick() { | ||
inputRef.current.focus(); | ||
setFocus(true); | ||
} | ||
const handleSearch = (e: KeyboardEvent) => { | ||
// 如果按的是 enter 键, 13是 enter | ||
if (ENTER_REG.test(e.code) || ENTER_REG.test(e.key)) { | ||
e.preventDefault(); | ||
props.onSubmit?.({ value: searchValue, e: e as KeyboardEvent<HTMLDivElement> }); | ||
} | ||
}; | ||
|
||
const shapeStyle = { borderRadius: shape === 'square' ? null : '50px' }; | ||
const renderLeftIcon = () => { | ||
if (leftIcon === 'search') { | ||
return <SearchIcon size="24px" />; | ||
} | ||
return parseTNode(leftIcon); | ||
}; | ||
|
||
return ( | ||
<div | ||
className={`${classPrefix}-search ${focusState ? `${classPrefix}-is-focused` : ''} ${className}`} | ||
style={{ ...style }} | ||
> | ||
{label && ( | ||
<div | ||
className={`${classPrefix}-search__label-text`} | ||
style={{ | ||
marginLeft: '0px', | ||
paddingRight: '8px', | ||
color: 'rgba(0,0,0,0.9)', | ||
whiteSpace: 'nowrap', | ||
}} | ||
> | ||
{label} | ||
const renderClear = () => { | ||
if (clearable && searchValue) { | ||
return ( | ||
<div className={`${rootClassName}__clear`} onClick={handleClear}> | ||
<CloseCircleFilledIcon size="24" /> | ||
</div> | ||
)} | ||
<div className={`${classPrefix}-search__form`} style={{ ...shapeStyle }}> | ||
<div className={`${classPrefix}-search__box`}> | ||
<div className={`${classPrefix}-search__icon-search`}>{leftIcon}</div> | ||
<input | ||
style={{ textAlign: center ? 'center' : null }} | ||
ref={inputRef} | ||
type="text" | ||
autoFocus={focusState} | ||
disabled={disabled} | ||
value={value} | ||
placeholder={placeholder} | ||
className={`${classPrefix}-search__input`} | ||
onBlur={handleBlur} | ||
onChange={handleChange} | ||
onFocus={handleFocus} | ||
onSubmit={handleSubmit} | ||
/> | ||
<div className={`${classPrefix}-search__icon-close`}> | ||
{value.length > 0 && <CloseCircleFilledIcon onClick={handleClear} />} | ||
{rightIcon} | ||
</div> | ||
); | ||
} | ||
return null; | ||
}; | ||
|
||
const renderAction = () => { | ||
if (action && searchValue) { | ||
return ( | ||
<div className={`${rootClassName}__search-action`} onClick={handleAction}> | ||
{parseTNode(action)} | ||
</div> | ||
<label className={`${classPrefix}-search__label`} style={{ ...shapeStyle }} onClick={handleClick}> | ||
<div className={`${classPrefix}-search__label-icon-search`}>{leftIcon}</div> | ||
<span className={`${classPrefix}-search__label-text`}>{placeholder}</span> | ||
</label> | ||
); | ||
} | ||
return null; | ||
}; | ||
|
||
return ( | ||
<div className={`${rootClassName}`}> | ||
<div className={`${boxClasses}`}> | ||
{renderLeftIcon()} | ||
<input | ||
ref={inputRef} | ||
value={searchValue} | ||
type="search" | ||
className={`${inputClasses}`} | ||
autoFocus={focus} | ||
placeholder={placeholder} | ||
readOnly={readonly} | ||
disabled={disabled} | ||
onKeyPress={handleSearch} | ||
onFocus={handleFocus} | ||
onBlur={handleBlur} | ||
onInput={handleInput} | ||
onCompositionEnd={handleCompositionend} | ||
/> | ||
{renderClear()} | ||
</div> | ||
{focusState && ( | ||
<Button | ||
className={`${classPrefix}-search__cancel-button`} | ||
variant="text" | ||
theme="primary" | ||
onClick={handleAction} | ||
> | ||
{action} | ||
</Button> | ||
)} | ||
{renderAction()} | ||
</div> | ||
); | ||
}; | ||
|
||
Search.defaultProps = searchDefaultProps; | ||
|
||
export default Search; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
:: BASE_DOC :: | ||
|
||
## API | ||
|
||
### Search Props | ||
|
||
name | type | default | description | required | ||
-- | -- | -- | -- | -- | ||
className | String | - | className of component | N | ||
style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N | ||
action | TNode | '' | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
autocompleteOptions | Array | - | autocomplete words list。Typescript:`Array<AutocompleteOption>` `type AutocompleteOption = string \| { label: string \| TNode; group?: boolean }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/search/type.ts) | N | ||
center | Boolean | false | \- | N | ||
clearable | Boolean | true | \- | N | ||
disabled | Boolean | false | \- | N | ||
focus | Boolean | false | \- | N | ||
leftIcon | TNode | 'search' | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
placeholder | String | '' | \- | N | ||
prefixIcon | TElement | - | `deprecated`。Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
readonly | Boolean | false | \- | N | ||
shape | String | 'square' | options: square/round | N | ||
suffixIcon | TElement | - | `deprecated`。Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
value | String | - | \- | N | ||
defaultValue | String | - | uncontrolled property | N | ||
onActionClick | Function | | Typescript:`({}) => void`<br/> | N | ||
onBlur | Function | | Typescript:`(context: { value: string; e: FocusEvent }) => void`<br/> | N | ||
onChange | Function | | Typescript:`(value: string, context: { trigger: 'input-change' \| 'option-click'; e?: InputEvent \| MouseEvent }) => void`<br/> | N | ||
onClear | Function | | Typescript:`(context: { e: MouseEvent }) => void`<br/> | N | ||
onFocus | Function | | Typescript:`(context: { value: string; e: FocusEvent }) => void`<br/> | N | ||
onSearch | Function | | Typescript:`(context?: { value: string; trigger: 'submit' \| 'option-click' \| 'clear'; e?: InputEvent \| MouseEvent }) => void`<br/> | N | ||
onSubmit | Function | | Typescript:`(context: { value: string; e: KeyboardEvent }) => void`<br/> | N |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,31 @@ | ||
:: BASE_DOC :: | ||
|
||
## API | ||
|
||
### Search Props | ||
|
||
名称 | 类型 | 默认值 | 说明 | 必传 | ||
名称 | 类型 | 默认值 | 描述 | 必传 | ||
-- | -- | -- | -- | -- | ||
className | String | - | 类名 | N | ||
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N | ||
action | TNode | '' | 自定义右侧操作按钮文字。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
action | TNode | '' | 自定义右侧操作按钮文字。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
autocompleteOptions | Array | - | 【讨论中】联想词列表,如果不存在或长度为 0 则不显示联想框。可以使用函数 `label` 自定义联想词为任意内容;也可使用插槽 `option` 定义联想词内容,插槽参数为 `{ option: AutocompleteOption; index: number }`。如果 `group` 值为 `true` 则表示当前项为分组标题。TS 类型:`Array<AutocompleteOption>` `type AutocompleteOption = string \| { label: string \| TNode; group?: boolean }`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/search/type.ts) | N | ||
center | Boolean | false | 是否居中 | N | ||
clearable | Boolean | true | 是否启用清除控件 | N | ||
disabled | Boolean | false | 是否禁用 | N | ||
focus | Boolean | false | 是否聚焦 | N | ||
label | String | '' | 左侧文本 | N | ||
leftIcon | TElement | - | 左侧图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
leftIcon | TNode | 'search' | 左侧图标。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
placeholder | String | '' | 占位符 | N | ||
rightIcon | TElement | - | 右侧图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
prefixIcon | TElement | - | 已废弃。前置图标,默认为搜索图标。值为 `null` 时则不显示。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
readonly | Boolean | false | 只读状态 | N | ||
shape | String | 'square' | 搜索框形状。可选项:square/round | N | ||
value | String | '' | 值 | N | ||
defaultValue | String | '' | 值。非受控属性 | N | ||
onActionClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>点击右侧操作按钮文字时触发时触发 | N | ||
onBlur | Function | | TS 类型:`(value: string, context: { e: FocusEvent }) => void`<br/>失去焦点时触发 | N | ||
onChange | Function | | TS 类型:`(value: string, context?: { e?: InputEvent | MouseEvent }) => void`<br/>值发生变化时触发 | N | ||
suffixIcon | TElement | - | 已废弃。后置图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N | ||
value | String | - | 值 | N | ||
defaultValue | String | - | 值。非受控属性 | N | ||
onActionClick | Function | | TS 类型:`({}) => void`<br/>点击右侧操作按钮文字时触发 | N | ||
onBlur | Function | | TS 类型:`(context: { value: string; e: FocusEvent }) => void`<br/>失去焦点时触发 | N | ||
onChange | Function | | TS 类型:`(value: string, context: { trigger: 'input-change' \| 'option-click'; e?: InputEvent \| MouseEvent }) => void`<br/>搜索关键词发生变化时触发,可能场景有:搜索框内容发生变化、点击联想词 | N | ||
onClear | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>点击清除时触发 | N | ||
onFocus | Function | | TS 类型:`(value: string, context: { e: FocusEvent }) => void`<br/>获得焦点时触发 | N | ||
onSubmit | Function | | TS 类型:`(value: string, context: { e: KeyboardEvent }) => void`<br/>提交时触发 | N | ||
onFocus | Function | | TS 类型:`(context: { value: string; e: FocusEvent }) => void`<br/>获得焦点时触发 | N | ||
onSearch | Function | | TS 类型:`(context?: { value: string; trigger: 'submit' \| 'option-click' \| 'clear'; e?: InputEvent \| MouseEvent }) => void`<br/>【讨论中】搜索触发,包含:手机键盘提交健、联想关键词点击、清空按钮点击等 | N | ||
onSubmit | Function | | TS 类型:`(context: { value: string; e: KeyboardEvent }) => void`<br/>提交时触发,如:手机键盘提交按钮点击 | N |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
import '../../_common/style/mobile/components/search/_index.less'; | ||
import '../../_common/style/mobile/components/search/v2/_index.less'; |
Oops, something went wrong.