Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 73 additions & 25 deletions src/search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import { searchDefaultProps } from './defaultProps';
import { ENTER_REG } from '../_common/js/common';
import useDefaultProps from '../hooks/useDefaultProps';
import { usePrefixClass } from '../hooks/useClass';
import Cell from '../cell/Cell';

export interface SearchProps extends TdSearchProps, StyledProps {}

const Search: FC<SearchProps> = (props) => {
const {
clearable,
clearTrigger,
action,
center,
disabled,
Expand All @@ -26,6 +28,7 @@ const Search: FC<SearchProps> = (props) => {
readonly,
shape,
value,
resultList,
onActionClick,
onBlur,
onChange,
Expand All @@ -36,6 +39,7 @@ const Search: FC<SearchProps> = (props) => {
const [focusState, setFocus] = useState(focus);
const inputRef = useRef(null);
const [searchValue, setSearchValue] = useDefault(value, '', onChange);
const [showResultList, setShowResultList] = useState(false);

const { classPrefix } = useConfig();
const searchClass = usePrefixClass('search');
Expand All @@ -53,6 +57,7 @@ const Search: FC<SearchProps> = (props) => {
};

const handleInput = (e: FormEvent<HTMLInputElement>) => {
setShowResultList(true);
if (e instanceof InputEvent) {
// 中文输入的时候inputType是insertCompositionText所以中文输入的时候禁止触发。
const checkInputType = e.inputType && e.inputType === 'insertCompositionText';
Expand All @@ -63,16 +68,18 @@ const Search: FC<SearchProps> = (props) => {
};

const handleClear = (e: MouseEvent<HTMLDivElement>) => {
setSearchValue('', { trigger: 'input-change' });
setSearchValue('', { trigger: 'clear', e });
setFocus(true);
onClear?.({ e });
};

const handleFocus = (e: FocusEvent<HTMLDivElement>) => {
setFocus(true);
onFocus?.({ value: searchValue, e });
};

const handleBlur = (e: FocusEvent<HTMLDivElement>) => {
setFocus(false);
onBlur?.({ value: searchValue, e });
};

Expand All @@ -88,6 +95,7 @@ const Search: FC<SearchProps> = (props) => {
// 如果按的是 enter 键, 13是 enter
if (ENTER_REG.test(e.code) || ENTER_REG.test(e.key)) {
e.preventDefault();
setShowResultList(false);
onSubmit?.({ value: searchValue, e });
}
};
Expand All @@ -100,7 +108,7 @@ const Search: FC<SearchProps> = (props) => {
};

const renderClear = () => {
if (clearable && searchValue) {
if (clearable && searchValue && (clearTrigger === 'always' || (clearTrigger === 'focus' && focusState))) {
return (
<div className={`${searchClass}__clear`} onClick={handleClear}>
<CloseCircleFilledIcon size="24" />
Expand All @@ -121,31 +129,71 @@ const Search: FC<SearchProps> = (props) => {
return null;
};

const highlightSearchValue = (item: string, value: string) => {
const parts = item.split(new RegExp(`(${value})`, 'gi'));
return parts.map((part, index) =>
part.toLowerCase() === value.toLowerCase() ? (
<span key={index} className={`${searchClass}__result-item--highLight`}>
{part}
</span>
) : (
part
),
);
};

const handleSelectOption = (item: string, e: MouseEvent<HTMLDivElement>) => {
setShowResultList(false);
setSearchValue(item, { trigger: 'option-click', e });
};

const renderResultList = () => {
if (!showResultList || !resultList || resultList.length === 0) {
return null;
}

return (
<div className={`${searchClass}__result-list`}>
{resultList.map((item, index) => (
<Cell
key={index}
className={`${searchClass}__result-item`}
onClick={(context) => handleSelectOption(item, context.e)}
title={highlightSearchValue(item, searchValue)}
/>
))}
</div>
);
};

return (
<div className={`${searchClass}`}>
<div className={`${boxClasses}`}>
{renderLeftIcon()}
<input
ref={inputRef}
value={searchValue}
type="search"
className={`${inputClasses}`}
style={
props.cursorColor ? ({ '--td-search-cursor-color': props.cursorColor } as React.CSSProperties) : undefined
}
autoFocus={focus}
placeholder={placeholder}
readOnly={readonly}
disabled={disabled}
onKeyDown={handleSearch}
onFocus={handleFocus}
onBlur={handleBlur}
onInput={handleInput}
onCompositionEnd={handleCompositionend}
/>
{renderClear()}
<div>
<div className={`${searchClass}`}>
<div className={`${boxClasses}`}>
{renderLeftIcon()}
<input
ref={inputRef}
value={searchValue}
type="search"
className={`${inputClasses}`}
style={
props.cursorColor ? ({ '--td-search-cursor-color': props.cursorColor } as React.CSSProperties) : undefined
}
autoFocus={focus}
placeholder={placeholder}
readOnly={readonly}
disabled={disabled}
onKeyDown={handleSearch}
onFocus={handleFocus}
onBlur={handleBlur}
onInput={handleInput}
onCompositionEnd={handleCompositionend}
/>
{renderClear()}
</div>
{renderAction()}
</div>
{renderAction()}
{renderResultList()}
</div>
);
};
Expand Down
57 changes: 27 additions & 30 deletions src/search/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,37 @@
import React, { useState } from 'react';
import React, { useState, useCallback } from 'react';
import { Search } from 'tdesign-mobile-react';

const list = [
'tdesign-vue',
'tdesign-react',
'tdesign-miniprogram',
'tdesign-angular',
'tdesign-mobile-vue',
'tdesign-mobile-react',
];

export default function Base() {
const [value, setValue] = useState<string>('');
const onChange = (val: string) => {
console.log('change: ', val);
const [resultList, setResultList] = useState<string[]>([]);

const onChange = useCallback((val: string, context: any) => {
console.log('onChange: ', val, context);
setValue(val);
};
const onBlur = () => {
console.log('blur');
};
const onClear = () => {
console.log('clear');
};
const onFocus = () => {
console.log('focus');
};
if (val) {
setResultList(list.filter((item) => item.toLowerCase().includes(val.toLowerCase())));
} else {
setResultList([]);
}
}, []);

const onSubmit = () => {
console.log('submit');
};
const onActionClick = () => {
console.log('action-click');
};
return (
<div className="search-example">
<Search
value={value}
placeholder="请输入关键字"
onActionClick={onActionClick}
onBlur={onBlur}
onChange={onChange}
onClear={onClear}
onFocus={onFocus}
onSubmit={onSubmit}
/>
<div>
<div className="search-example">
<Search placeholder="搜索预设文案" />
</div>
<div className="search-example">
<Search value={value} resultList={resultList} placeholder="输入tdesign,有预览效果" onChange={onChange} />
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions src/search/defaultProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { TdSearchProps } from './type';
export const searchDefaultProps: TdSearchProps = {
action: '',
center: false,
clearTrigger: 'always',
clearable: true,
cursorColor: '#0052d9',
disabled: false,
focus: false,
leftIcon: 'search',
placeholder: '',
readonly: undefined,
resultList: [],
shape: 'square',
};
4 changes: 3 additions & 1 deletion src/search/search.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ style | Object | - | CSS(Cascading Style Sheets),Typescript: `React.CSSPropert
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
clearTrigger | String | always | show clear icon, clicked to clear input value。options: always / focus | N
clearable | Boolean | true | \- | N
cursorColor | String | #0052d9 | `0.21.2` | N
disabled | Boolean | false | \- | N
Expand All @@ -19,13 +20,14 @@ leftIcon | TNode | 'search' | Typescript: `string \| TNode`。[see more ts defin
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 | undefined | \- | N
resultList | Array | [] | Typescript: `Array<string>` | 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
onChange | Function | | Typescript: `(value: string, context: { trigger: 'input-change' \| 'option-click' \| 'clear'; 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
Expand Down
4 changes: 3 additions & 1 deletion src/search/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ style | Object | - | 样式,TS 类型:`React.CSSProperties` | 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
clearTrigger | String | always | 清空图标触发方式,仅在输入框有值时有效。可选项:always / focus | N
clearable | Boolean | true | 是否启用清除控件 | N
cursorColor | String | #0052d9 | `0.21.2`。光标颜色 | N
disabled | Boolean | false | 是否禁用 | N
Expand All @@ -19,13 +20,14 @@ leftIcon | TNode | 'search' | 左侧图标。TS 类型:`string \| TNode`。[
placeholder | String | '' | 占位符 | N
prefixIcon | TElement | - | 已废弃。前置图标,默认为搜索图标。值为 `null` 时则不显示。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N
readonly | Boolean | undefined | 只读状态 | N
resultList | Array | [] | 预览结果列表。TS 类型:`Array<string>` | N
shape | String | 'square' | 搜索框形状。可选项:square/round | 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
onChange | Function | | TS 类型:`(value: string, context: { trigger: 'input-change' \| 'option-click' \| 'clear'; e?: InputEvent \| MouseEvent }) => void`<br/>搜索关键词发生变化时触发,可能场景有:搜索框内容发生变化、点击联想词 | N
onClear | Function | | TS 类型:`(context: { e: MouseEvent }) => 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
Expand Down
10 changes: 9 additions & 1 deletion src/search/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ export interface TdSearchProps {
* 只读状态
*/
readonly?: boolean;
/**
* 预览结果列表
* @default []
*/
resultList?: Array<string>;
/**
* 搜索框形状
* @default 'square'
Expand Down Expand Up @@ -91,7 +96,10 @@ export interface TdSearchProps {
*/
onChange?: (
value: string,
context: { trigger: 'input-change' | 'option-click'; e?: FormEvent<HTMLInputElement> | MouseEvent<HTMLDivElement> },
context: {
trigger: 'input-change' | 'option-click' | 'clear';
e?: FormEvent<HTMLInputElement> | MouseEvent<HTMLDivElement>;
},
) => void;
/**
* 点击清除时触发
Expand Down
Loading
Loading