mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 11:08:45 +08:00
feat: Input
add NumericInput demo & update searchInput demo (#3861)
* Input add NumericInput demo update searchInput * Input - add Input.Search - update NumericInput * snap update
This commit is contained in:
parent
91b2242f44
commit
73c776e73d
@ -57,6 +57,7 @@ export interface InputProps {
|
||||
|
||||
export default class Input extends Component<InputProps, any> {
|
||||
static Group: any;
|
||||
static Search: any;
|
||||
static defaultProps = {
|
||||
disabled: false,
|
||||
prefixCls: 'ant-input',
|
||||
|
83
components/input/Search.tsx
Normal file
83
components/input/Search.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Input from './Input';
|
||||
import Icon from '../icon';
|
||||
import splitObject from '../_util/splitObject';
|
||||
import omit from 'omit.js';
|
||||
|
||||
export interface SearchProps {
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
defaultValue?: any;
|
||||
value?: any;
|
||||
onChange?: React.FormEventHandler<any>;
|
||||
onSearch?: React.FormEventHandler<any>;
|
||||
}
|
||||
|
||||
export default class Search extends React.Component<SearchProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-input-search',
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
let value;
|
||||
if ('value' in props) {
|
||||
value = props.value;
|
||||
} else if ('defaultValue' in props) {
|
||||
value = props.defaultValue;
|
||||
} else {
|
||||
value = '';
|
||||
}
|
||||
this.state = {
|
||||
value,
|
||||
focus: false,
|
||||
};
|
||||
}
|
||||
onChange = (e) => {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value: e.target.value });
|
||||
}
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange(e);
|
||||
}
|
||||
}
|
||||
onSearch = () => {
|
||||
if (this.state.focus && this.props.onSearch) {
|
||||
this.props.onSearch(this.state.value);
|
||||
} else if (!this.state.focus) {
|
||||
this.setState({ focus: true });
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const [{ className, placeholder, prefixCls }, others] = splitObject(
|
||||
this.props, ['className', 'placeholder', 'prefixCls']
|
||||
);
|
||||
// Fix https://fb.me/react-unknown-prop
|
||||
const otherProps = omit(others, [
|
||||
'defaultValue',
|
||||
'value',
|
||||
'onChange',
|
||||
'onSearch',
|
||||
]);
|
||||
const wrapperCls = classNames({
|
||||
[`${prefixCls}-wrapper`]: true,
|
||||
[`${prefixCls}-wrapper-focus`]: this.state.focus,
|
||||
[className]: !!className,
|
||||
});
|
||||
return (
|
||||
<div className={wrapperCls} {...otherProps}>
|
||||
<Input
|
||||
className={prefixCls}
|
||||
placeholder={placeholder}
|
||||
value={this.state.value}
|
||||
onChange={this.onChange}
|
||||
onPressEnter={this.onSearch}
|
||||
/>
|
||||
<Icon className={`${prefixCls}-icon`} onClick={this.onSearch} type="search" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -14,60 +14,10 @@ title:
|
||||
Example of creating a search box by grouping a standard input with a search button.
|
||||
|
||||
````jsx
|
||||
import { Input, Button } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
const InputGroup = Input.Group;
|
||||
|
||||
const SearchInput = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
value: '',
|
||||
focus: false,
|
||||
};
|
||||
},
|
||||
handleInputChange(e) {
|
||||
this.setState({
|
||||
value: e.target.value,
|
||||
});
|
||||
},
|
||||
handleFocusBlur(e) {
|
||||
this.setState({
|
||||
focus: e.target === document.activeElement,
|
||||
});
|
||||
},
|
||||
handleSearch() {
|
||||
if (this.props.onSearch) {
|
||||
this.props.onSearch(this.state.value);
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const { style, size, placeholder } = this.props;
|
||||
const btnCls = classNames({
|
||||
'ant-search-btn': true,
|
||||
'ant-search-btn-noempty': !!this.state.value.trim(),
|
||||
});
|
||||
const searchCls = classNames({
|
||||
'ant-search-input': true,
|
||||
'ant-search-input-focus': this.state.focus,
|
||||
});
|
||||
return (
|
||||
<div className="ant-search-input-wrapper" style={style}>
|
||||
<InputGroup className={searchCls}>
|
||||
<Input placeholder={placeholder} value={this.state.value} onChange={this.handleInputChange}
|
||||
onFocus={this.handleFocusBlur} onBlur={this.handleFocusBlur} onPressEnter={this.handleSearch}
|
||||
/>
|
||||
<div className="ant-input-group-wrap">
|
||||
<Button icon="search" className={btnCls} size={size} onClick={this.handleSearch} />
|
||||
</div>
|
||||
</InputGroup>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
import { Input } from 'antd';
|
||||
const InputSearch = Input.Search;
|
||||
|
||||
ReactDOM.render(
|
||||
<SearchInput placeholder="input search text"
|
||||
onSearch={value => console.log(value)} style={{ width: 200 }}
|
||||
/>
|
||||
<InputSearch placeholder="input search text" onSearch={value => console.log(value)} />
|
||||
, mountNode);
|
||||
````
|
||||
|
118
components/input/demo/tooltip.md
Normal file
118
components/input/demo/tooltip.md
Normal file
@ -0,0 +1,118 @@
|
||||
---
|
||||
order: 7
|
||||
title:
|
||||
zh-CN: 数值输入框
|
||||
en-US: Numeric Input
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
结合 [Tooltip](/components/tooltip) 组件,实现一个数值输入框,方便内容超长时的全量展现。
|
||||
|
||||
## en-US
|
||||
|
||||
You can use the Input in conjunction with [Tooltip](/components/tooltip) component to create a Numeric Input, which can provide a good experience for extra-long content display.
|
||||
|
||||
````jsx
|
||||
import { Input, Tooltip } from 'antd';
|
||||
|
||||
function formatNumber(value) {
|
||||
value += '';
|
||||
const list = value.split('.');
|
||||
const prefix = list[0].charAt(0) === '-' ? '-' : '';
|
||||
let num = prefix ? list[0].slice(1) : list[0];
|
||||
let result = '';
|
||||
while (num.length > 3) {
|
||||
result = `,${num.slice(-3)}${result}`;
|
||||
num = num.slice(0, num.length - 3);
|
||||
}
|
||||
if (num) {
|
||||
result = num + result;
|
||||
}
|
||||
return `${prefix}${result}${list[1] ? `.${list[1]}` : ''}`;
|
||||
}
|
||||
|
||||
class NumericInput extends React.Component {
|
||||
onChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const reg = /^-?(0|[1-9][0-9]*)(\.[0-9]*)?$/;
|
||||
if ((!isNaN(value) && reg.test(value)) || value === '' || value === '-') {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
// '.' at the end or only '-' in the input box.
|
||||
onBlur = () => {
|
||||
const { value } = this.props;
|
||||
if (value.charAt(value.length - 1) === '.' || value === '-') {
|
||||
this.props.onChange({ value: value.slice(0, -1) });
|
||||
}
|
||||
if (this.props.onBlur) {
|
||||
this.props.onBlur();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { value } = this.props;
|
||||
const title = (value ?
|
||||
(<span className="numeric-input-title">
|
||||
{value !== '-' ? formatNumber(value) : '-'}
|
||||
</span>) : '');
|
||||
return (
|
||||
<div>
|
||||
<Tooltip
|
||||
trigger={['focus']}
|
||||
title={title}
|
||||
placement="topLeft"
|
||||
overlayClassName="numeric-input"
|
||||
>
|
||||
<Input
|
||||
{...this.props}
|
||||
onChange={this.onChange}
|
||||
onBlur={this.onBlur}
|
||||
placeholder="input a number"
|
||||
maxLength="25"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NumericInputDemo extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { value: '' };
|
||||
}
|
||||
onChange = (value) => {
|
||||
this.setState({ value });
|
||||
}
|
||||
render() {
|
||||
const { value } = this.state;
|
||||
return (
|
||||
<div className="numeric-input-demo">
|
||||
<NumericInput value={value} onChange={this.onChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<NumericInputDemo />, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
/* to prevent the arrow overflow the popup container,
|
||||
or the height is not enough when content is empty */
|
||||
.numeric-input .ant-tooltip-inner {
|
||||
min-width: 32px;
|
||||
min-height: 37px;
|
||||
}
|
||||
|
||||
.numeric-input .numeric-input-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.numeric-input-demo {
|
||||
width: 120px;
|
||||
}
|
||||
````
|
@ -32,6 +32,15 @@ Keyboard and mouse can be used for providing or changing data.
|
||||
> When `Input` is used in a `Form.Item` context, if the `Form.Item` has the `id` and `options` props defined
|
||||
then `value`, `defaultValue`, and `id` props are automatically set.
|
||||
|
||||
#### Input.Search
|
||||
|
||||
| Property | Description | Type | Available Values | Default |
|
||||
|-----------|------------------------------------------|------------|-------|--------|
|
||||
| defaultValue | The initial value. | any | | |
|
||||
| value | The content value. | any | | |
|
||||
| onChange | The callback function that is triggered when you change the value. | function(e) | | |
|
||||
| onSearch | The callback function that is triggered when you click on the search-icon or press Enter key. | function | | |
|
||||
|
||||
#### Input.Group
|
||||
|
||||
| Property | Description | Type | Available Values | Default |
|
||||
|
@ -1,5 +1,7 @@
|
||||
import Input from './Input';
|
||||
import Group from './Group';
|
||||
import Search from './Search';
|
||||
|
||||
Input.Group = Group;
|
||||
Input.Search = Search;
|
||||
export default Input;
|
||||
|
@ -31,6 +31,15 @@ title: Input
|
||||
|
||||
> 如果 `Input` 在 `Form.Item` 内,并且 `Form.Item` 设置了 `id` 和 `options` 属性,则 `value` `defaultValue` 和 `id` 属性会被自动设置。
|
||||
|
||||
#### Input.Search
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|-----------|------------------------------------------|------------|-------|--------|
|
||||
| defaultValue | 初始默认值 | any | | |
|
||||
| value | value 值 | any | | |
|
||||
| onChange | 值改变的回调 | function(e) | | |
|
||||
| onSearch | 点击搜索或按下回车键时的回调 | function | | |
|
||||
|
||||
#### Input.Group
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|
@ -3,44 +3,42 @@
|
||||
@import "../../button/style/mixin";
|
||||
@import "./mixin";
|
||||
|
||||
.@{ant-prefix}-search-input-wrapper {
|
||||
.@{ant-prefix}-input-search-wrapper {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
min-width: 24px;
|
||||
height: @input-height-base;
|
||||
|
||||
.@{ant-prefix}-input-search {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
transition: all .3s ease;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-search-icon {
|
||||
position: absolute;
|
||||
line-height: @input-height-base;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
transition: all .3s ease;
|
||||
|
||||
&:hover {
|
||||
color: @input-hover-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-search-input {
|
||||
&.@{ant-prefix}-input-group .@{ant-prefix}-input:first-child,
|
||||
&.@{ant-prefix}-input-group .@{ant-prefix}-select:first-child {
|
||||
border-radius: @border-radius-base;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
.@{ant-prefix}-input-search-wrapper-focus {
|
||||
.@{ant-prefix}-input-search {
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.@{ant-prefix}-input-group .@{ant-prefix}-input:first-child {
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-search-btn {
|
||||
.btn-default;
|
||||
border-radius: 0 @border-radius-base - 1 @border-radius-base - 1 0;
|
||||
left: -1px;
|
||||
position: relative;
|
||||
border-width: 0 0 0 1px;
|
||||
z-index: 2;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
&:hover {
|
||||
border-color: @border-color-base;
|
||||
}
|
||||
}
|
||||
&&-focus .@{ant-prefix}-search-btn-noempty,
|
||||
&:hover .@{ant-prefix}-search-btn-noempty {
|
||||
.btn-primary;
|
||||
}
|
||||
.@{ant-prefix}-select-combobox {
|
||||
.@{ant-prefix}-select-selection__rendered {
|
||||
margin-right: 29px;
|
||||
}
|
||||
.@{ant-prefix}-input-search-icon {
|
||||
left: 100%;
|
||||
margin-left: -22px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
@ -169,28 +169,17 @@ exports[`test renders ./components/input/demo/group.md correctly 1`] = `
|
||||
|
||||
exports[`test renders ./components/input/demo/search-input.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-search-input-wrapper"
|
||||
style="width:200px;">
|
||||
class="ant-input-search-wrapper">
|
||||
<span
|
||||
class="ant-input-group ant-search-input">
|
||||
<span
|
||||
class="ant-input-wrapper">
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="input search text"
|
||||
type="text"
|
||||
value="" />
|
||||
</span>
|
||||
<div
|
||||
class="ant-input-group-wrap">
|
||||
<button
|
||||
class="ant-btn ant-btn-icon-only ant-search-btn"
|
||||
type="button">
|
||||
<i
|
||||
class="anticon anticon-search " />
|
||||
</button>
|
||||
</div>
|
||||
class="ant-input-wrapper">
|
||||
<input
|
||||
class="ant-input ant-input-search"
|
||||
placeholder="input search text"
|
||||
type="text"
|
||||
value="" />
|
||||
</span>
|
||||
<i
|
||||
class="anticon anticon-search ant-input-search-icon" />
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -230,3 +219,20 @@ exports[`test renders ./components/input/demo/textarea.md correctly 1`] = `
|
||||
type="textarea" />
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`test renders ./components/input/demo/tooltip.md correctly 1`] = `
|
||||
<div
|
||||
class="numeric-input-demo">
|
||||
<div>
|
||||
<span
|
||||
class="ant-input-wrapper">
|
||||
<input
|
||||
class="ant-input"
|
||||
maxlength="25"
|
||||
placeholder="input a number"
|
||||
type="text"
|
||||
value="" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
Loading…
Reference in New Issue
Block a user