Skip to content

Commit

Permalink
feat(search): 组件对齐 mobile-vue (#476)
Browse files Browse the repository at this point in the history
* feat(search): update search

update search style to v2, alignment vue mobile

feat #473

* feat(search): update search demo

* fix(search): fix event type, default value and simplify function

* fix: remove suffixIcon

* fix: fix cr

---------

Co-authored-by: anlyyao <anly_yaw@163.com>
  • Loading branch information
slatejack and anlyyao authored Aug 21, 2024
1 parent ee54e86 commit f92c1ee
Show file tree
Hide file tree
Showing 21 changed files with 730 additions and 228 deletions.
2 changes: 1 addition & 1 deletion site/mobile/mobile.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default {
{
title: 'Search 搜索框',
name: 'search',
component: () => import('tdesign-mobile-react/search/_example/index.jsx'),
component: () => import('tdesign-mobile-react/search/_example/index.tsx'),
},
{
title: 'Badge 徽标',
Expand Down
3 changes: 1 addition & 2 deletions src/popup/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from 'react';
import Button from '../../button';
import Popup from '..';
import { Popup, Button } from 'tdesign-mobile-react';

const placementList = [
{ value: 'top', text: '顶部弹出' },
Expand Down
6 changes: 3 additions & 3 deletions src/popup/_example/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import { Popup, Button } from 'tdesign-mobile-react';
import TDemoBlock from '../../../site/mobile/components/DemoBlock';
import TDemoHeader from '../../../site/mobile/components/DemoHeader';
import './style/index.less';
import WithTitle from './with-title';
import CustomClose from './custom-close';
import Button from '../../button';
import Popup from '..';

import './style/index.less';

function PlacementBottom() {
const [visible, setVisible] = useState(false);
Expand Down
221 changes: 117 additions & 104 deletions src/search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,137 +1,150 @@
import React, { FC, useState, useRef } from 'react';
import { CloseCircleFilledIcon } from 'tdesign-icons-react';
import isFunction from 'lodash/isFunction';
import { Button } from '../button';
import React, { useState, useRef } from 'react';
import type { FC, FormEvent, CompositionEvent, MouseEvent, KeyboardEvent, FocusEvent } from 'react';
import { CloseCircleFilledIcon, SearchIcon } from 'tdesign-icons-react';
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';
import { usePrefixClass } from '../hooks/useClass';

export interface SearchProps extends TdSearchProps, StyledProps {}

const Search: FC<SearchProps> = (props) => {
const {
className = '',
style = {},
action = '',
clearable,
action,
center,
disabled,
focus,
label,
leftIcon,
placeholder,
rightIcon,
shape = 'square',
value = '',
readonly,
shape,
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 searchClass = usePrefixClass('search');

const inputRef = useRef(null);
const [focusState, setFocus] = useState(focus);
const boxClasses = classNames(`${searchClass}__input-box`, `${searchClass}__input-box--${shape}`, {
[`${classPrefix}-is-focused`]: focusState,
});
const inputClasses = classNames(`${classPrefix}-input__keyword`, {
[`${searchClass}--center`]: center,
});

function handleBlur(e: React.FocusEvent<HTMLInputElement, Element>) {
setFocus(false);
inputRef.current.blur();
const { value } = e.currentTarget;
isFunction(onBlur) && onBlur(value, { e });
}

function handleClear(e: React.MouseEvent<SVGSVGElement, MouseEvent>) {
isFunction(onClear) && onClear({ e });
isFunction(onChange) && onChange('');
}

function handleAction(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
isFunction(onActionClick) && onActionClick({ e });
}

function handleChange(e: React.ChangeEvent<HTMLInputElement> | React.CompositionEvent<HTMLInputElement>) {
const { value } = e.currentTarget;
isFunction(onChange) && onChange(value, { e });
}

function handleFocus(e: React.FocusEvent<HTMLInputElement, Element>) {
const { value } = e.currentTarget;
isFunction(onFocus) && onFocus(value, { e });
}

function handleSubmit(e: React.FocusEvent<HTMLInputElement, Element>) {
const { value } = e.currentTarget;
isFunction(onSubmit) && onSubmit(value, { e });
}

function handleClick() {
inputRef.current.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;
}

inputValueChangeHandle(e);
};

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

const shapeStyle = { borderRadius: shape === 'square' ? null : '50px' };
const handleFocus = (e: FocusEvent<HTMLDivElement>) => {
onFocus?.({ value: searchValue, e });
};

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 handleBlur = (e: FocusEvent<HTMLDivElement>) => {
onBlur?.({ value: searchValue, e });
};

const handleCompositionend = (e: CompositionEvent) => {
inputValueChangeHandle(e as CompositionEvent<HTMLInputElement>);
};

const handleAction = (e: MouseEvent) => {
onActionClick?.({ e });
};

const handleSearch = (e: KeyboardEvent<HTMLDivElement>) => {
// 如果按的是 enter 键, 13是 enter
if (ENTER_REG.test(e.code) || ENTER_REG.test(e.key)) {
e.preventDefault();
onSubmit?.({ value: searchValue, e });
}
};

const renderLeftIcon = () => {
if (leftIcon === 'search') {
return <SearchIcon size="24px" />;
}
return parseTNode(leftIcon);
};

const renderClear = () => {
if (clearable && searchValue) {
return (
<div className={`${searchClass}__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={`${searchClass}__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={`${searchClass}`}>
<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;
13 changes: 13 additions & 0 deletions src/search/_example/action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { Search } from 'tdesign-mobile-react';

export default function Action() {
const onChange = (val: string) => {
console.log('change: ', val);
};
return (
<div className="search-example">
<Search placeholder="搜索预设文案" center focus action="取消" onChange={onChange} />
</div>
);
}
19 changes: 0 additions & 19 deletions src/search/_example/base.jsx

This file was deleted.

40 changes: 40 additions & 0 deletions src/search/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useState } from 'react';
import { Search } from 'tdesign-mobile-react';

export default function Base() {
const [value, setValue] = useState<string>('');
const onChange = (val: string) => {
console.log('change: ', val);
setValue(val);
};
const onBlur = () => {
console.log('blur');
};
const onClear = () => {
console.log('clear');
};
const onFocus = () => {
console.log('focus');
};

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>
);
}
20 changes: 0 additions & 20 deletions src/search/_example/index.jsx

This file was deleted.

Loading

0 comments on commit f92c1ee

Please sign in to comment.