diff --git a/components/index.js b/components/index.js index e3650b104..92e252232 100644 --- a/components/index.js +++ b/components/index.js @@ -146,3 +146,5 @@ export { default as Progress } from './progress' import Timeline from './timeline' const TimelineItem = Timeline.Item export { Timeline, TimelineItem } + +export { default as InputNumber } from './input-number' diff --git a/components/input-number/demo/basic.md b/components/input-number/demo/basic.md new file mode 100644 index 000000000..3e92744b0 --- /dev/null +++ b/components/input-number/demo/basic.md @@ -0,0 +1,35 @@ +<cn> +#### 基本 +数字输入框。 +</cn> + +<us> +#### Basic +Numeric-only input box. +</us> + +```html +<template> + <div> + <a-input-number :min="1" :max="10" v-model="value" @change="onChange" /> + 当前值:{{value}} + </div> +</template> +<script> + export default { + data() { + return { + value: 3 + } + }, + methods: { + onChange(value) { + console.log('changed', value); + }, + }, + } +</script> +``` + + + diff --git a/components/input-number/demo/digit.md b/components/input-number/demo/digit.md new file mode 100644 index 000000000..8a501cca8 --- /dev/null +++ b/components/input-number/demo/digit.md @@ -0,0 +1,28 @@ +<cn> +#### 小数 +和原生的数字输入框一样,value 的精度由 step 的小数位数决定。 +</cn> + +<us> +#### Decimals +A numeric-only input box whose values can be increased or decreased using a decimal step. The number of decimals (also known as precision) is determined by the step prop. +</us> + +```html +<template> + <a-input-number :min="0" :max="10" :step="0.1" @change="onChange" /> +</template> +<script> + export default { + methods: { + onChange(value) { + console.log('changed', value); + }, + }, + } +</script> +``` + + + + diff --git a/components/input-number/demo/disabled.md b/components/input-number/demo/disabled.md new file mode 100644 index 000000000..00ddd303c --- /dev/null +++ b/components/input-number/demo/disabled.md @@ -0,0 +1,37 @@ +<cn> +#### 不可用 +点击按钮切换可用状态。 +</cn> + +<us> +#### Disabled +Click the button to toggle between available and disabled states. +</us> + +```html +<template> + <div> + <a-input-number :min="1" :max="10" :disabled="disabled" :defaultValue="3" /> + <div style="marginTop:20px"> + <a-button @click="toggle" type="primary">Toggle disabled</a-button> + </div> + </div> +</template> +<script> + export default { + data () { + return { + disabled: true, + } + }, + methods: { + toggle() { + this.disabled = !this.disabled + } + }, + } +</script> +``` + + + diff --git a/components/input-number/demo/formatter.md b/components/input-number/demo/formatter.md new file mode 100644 index 000000000..a4f7a2736 --- /dev/null +++ b/components/input-number/demo/formatter.md @@ -0,0 +1,42 @@ +<cn> +#### 格式化展示 +通过 `formatter` 格式化数字,以展示具有具体含义的数据,往往需要配合 `parser` 一起使用。 +</cn> + +<us> +#### Formatter +Display value within it's situation with `formatter`, and we usually use `parser` at the same time. +</us> + +```html +<template> + <div> + <a-input-number + :defaultValue="1000" + :formatter="value => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')" + :parser="value => value.replace(/\$\s?|(,*)/g, '')" + @change="onChange" + /> + <a-input-number + :defaultValue="100" + :min="0" + :max="100" + :formatter="value => `${value}%`" + :parser="value => value.replace('%', '')" + @change="onChange" + /> + </div> +</template> +<script> + export default { + methods: { + onChange(value) { + console.log('changed', value); + }, + }, + } +</script> +``` + + + diff --git a/components/input-number/demo/index.vue b/components/input-number/demo/index.vue new file mode 100644 index 000000000..8f5ff5519 --- /dev/null +++ b/components/input-number/demo/index.vue @@ -0,0 +1,53 @@ +<script> +import Basic from './basic.md' +import Disabled from './disabled.md' +import Digit from './digit.md' +import Formatter from './formatter.md' +import Size from './size.md' +import CN from '../index.zh-CN.md' +import US from '../index.en-US.md' + +const md = { + cn: `# 数字输入框 + 通过鼠标或键盘,输入范围内的数值。 + ## 何时使用 + 当需要获取标准数值时。 + ## 代码演示`, + us: `# Data Entry + Enter a number within certain range with the mouse or keyboard. + ## When To Use + When a numeric value needs to be provided. + ## Examples + `, +} +export default { + category: 'Components', + subtitle: '数字输入框', + type: 'Data Entry', + title: 'InputNumber', + render () { + return ( + <div> + <md cn={md.cn} us={md.us}/> + <br/> + <Basic /> + <br/> + <Size /> + <br /> + <Disabled /> + <br/> + <Digit /> + <br/> + <Formatter /> + <br/> + <api> + <template slot='cn'> + <CN/> + </template> + <US/> + </api> + </div> + ) + }, +} +</script> diff --git a/components/input-number/demo/size.md b/components/input-number/demo/size.md new file mode 100644 index 000000000..5e66d5f76 --- /dev/null +++ b/components/input-number/demo/size.md @@ -0,0 +1,36 @@ +<cn> +#### 三种大小 +三种大小的数字输入框,当 size 分别为 `large` 和 `small` 时,输入框高度为 `40px` 和 `24px` ,默认高度为 `32px`。 +</cn> + +<us> +#### Sizes +There are three sizes available to a numeric input box. By default, the size is `32px`. The two additional sizes are `large` and `small` which means `40px` and `24px`, respectively. +</us> + +```html +<template> + <div> + <a-input-number size="large" :min="1" :max="100000" :defaultValue="3" @change="onChange" /> + <a-input-number :min="1" :max="100000" :defaultValue="3" @change="onChange" /> + <a-input-number size="small" :min="1" :max="100000" :defaultValue="3" @change="onChange" /> + </div> +</template> +<script> + export default { + methods: { + onChange(value) { + console.log('changed', value); + }, + }, + } +</script> +<style scoped> + .ant-input-number { + margin-right: 10px; + } +</style> +``` + + + diff --git a/components/input-number/index.en-US.md b/components/input-number/index.en-US.md new file mode 100644 index 000000000..5d0e71229 --- /dev/null +++ b/components/input-number/index.en-US.md @@ -0,0 +1,28 @@ +## API + +| property | description | type | default | +| -------- | ----------- | ---- | ------- | +| autoFocus | get focus when component mounted | boolean | false | +| defaultValue | initial value | number | | +| disabled | disable the input | boolean | false | +| formatter | Specifies the format of the value presented | function(value: number \| string): string | - | +| max | max vale | number | Infinity | +| min | min value | number | -Infinity | +| parser | Specifies the value extracted from formatter | function( string): number | - | +| precision | precision of input value | number | - | +| size | width of input box | string | - | +| step | The number to which the current value is increased or decreased. It can be an integer or decimal. | number\|string | 1 | +| value(v-model) | current value | number | | + + +### events +| Events Name | Description | Arguments | +| --- | --- | --- | +| change | The callback triggered when the value is changed. | function(value: number \| string) | | + +## Methods + +| Name | Description | +| ---- | ----------- | +| blur() | remove focus | +| focus() | get focus | diff --git a/components/input-number/index.jsx b/components/input-number/index.jsx new file mode 100644 index 000000000..902ea2b77 --- /dev/null +++ b/components/input-number/index.jsx @@ -0,0 +1,62 @@ +import PropTypes from '../_util/vue-types' +import { initDefaultProps, getOptionProps } from '../_util/props-util' +import classNames from 'classnames' +import VcInputNumber from '../vc-input-number/src' + +export const InputNumberProps = { + prefixCls: PropTypes.string, + min: PropTypes.number, + max: PropTypes.number, + value: PropTypes.number, + step: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), + defaultValue: PropTypes.number, + tabIndex: PropTypes.number, + disabled: PropTypes.bool, + size: PropTypes.oneOf(['large', 'small', 'default']), + formatter: PropTypes.func, + parser: PropTypes.func, + placeholder: PropTypes.string, + name: PropTypes.string, + id: PropTypes.string, + precision: PropTypes.number, +} + +export default { + name: 'InputNumber', + model: { + prop: 'value', + event: 'change', + }, + props: initDefaultProps(InputNumberProps, { + prefixCls: 'ant-input-number', + step: 1, + }), + methods: { + focus () { + this.$refs.inputNumberRef.focus() + }, + blur () { + this.$refs.inputNumberRef.blur() + }, + }, + + render () { + const { size, ...others } = getOptionProps(this) + const inputNumberClass = classNames({ + [`${this.prefixCls}-lg`]: size === 'large', + [`${this.prefixCls}-sm`]: size === 'small', + }) + const vcInputNumberprops = { + props: { + ...others, + }, + class: inputNumberClass, + ref: 'inputNumberRef', + on: this.$listeners, + } + return <VcInputNumber {...vcInputNumberprops} /> + }, +} diff --git a/components/input-number/index.zh-CN.md b/components/input-number/index.zh-CN.md new file mode 100644 index 000000000..25c5949cb --- /dev/null +++ b/components/input-number/index.zh-CN.md @@ -0,0 +1,29 @@ +## API + +属性如下 + +| 成员 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| autoFocus | 自动获取焦点 | boolean | false | +| defaultValue | 初始值 | number | | +| disabled | 禁用 | boolean | false | +| formatter | 指定输入框展示值的格式 | function(value: number \| string): string | - | +| max | 最大值 | number | Infinity | +| min | 最小值 | number | -Infinity | +| parser | 指定从 formatter 里转换回数字的方式,和 formatter 搭配使用 | function( string): number | - | +| precision | 数值精度 | number | - | +| size | 输入框大小 | string | 无 | +| step | 每次改变步数,可以为小数 | number\|string | 1 | +| value(v-model) | 当前值 | number | | + +### 事件 +| 事件名称 | 说明 | 回调参数 | +| --- | --- | --- | +| change | 变化回调 | Function(value: number \| string) | | + +## 方法 + +| 名称 | 描述 | +| --- | --- | +| blur() | 移除焦点 | +| focus() | 获取焦点 | diff --git a/components/input-number/style/index.jsx b/components/input-number/style/index.jsx new file mode 100644 index 000000000..cf31ed80f --- /dev/null +++ b/components/input-number/style/index.jsx @@ -0,0 +1,2 @@ +import '../../style/index.less' +import './index.less' diff --git a/components/input-number/style/index.less b/components/input-number/style/index.less new file mode 100644 index 000000000..f865df881 --- /dev/null +++ b/components/input-number/style/index.less @@ -0,0 +1,171 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../input/style/mixin"; + +@input-number-prefix-cls: ~"@{ant-prefix}-input-number"; + +.@{input-number-prefix-cls} { + .reset-component; + .input; + margin: 0; + padding: 0; + display: inline-block; + border: @border-width-base @border-style-base @border-color-base; + border-radius: @border-radius-base; + width: 90px; + + &-handler { + text-align: center; + line-height: 0; + height: 50%; + overflow: hidden; + color: @text-color-secondary; + position: relative; + transition: all 0.1s linear; + display: block; + width: 100%; + font-weight: bold; + &:active { + background: #f4f4f4; + } + &:hover &-up-inner, + &:hover &-down-inner { + color: @primary-5; + } + } + + &-handler-up-inner, + &-handler-down-inner { + .iconfont-mixin(); + line-height: 12px; + user-select: none; + position: absolute; + width: 12px; + height: 12px; + transition: all 0.1s linear; + .iconfont-size-under-12px(7px); + right: 4px; + color: @text-color-secondary; + } + + &:hover { + .hover(); + } + + &-focused { + .active(); + } + + &-disabled { + .disabled(); + .@{input-number-prefix-cls}-input { + cursor: not-allowed; + background-color: @disabled-bg; + } + .@{input-number-prefix-cls}-handler-wrap { + display: none; + } + } + + &-input { + width: 100%; + text-align: left; + outline: 0; + -moz-appearance: textfield; + height: @input-height-base - 2px; + transition: all 0.3s linear; + color: @input-color; + background-color: @input-bg; + border: 0; + border-radius: @border-radius-base; + padding: 0 @control-padding-horizontal - 1px; + display: block; + .placeholder(); + + &[disabled] { + .disabled(); + } + } + + &-lg { + padding: 0; + font-size: @font-size-lg; + + input { + height: @input-height-lg - 2px; + } + } + + &-sm { + padding: 0; + + input { + height: @input-height-sm - 2px; + padding: 0 @control-padding-horizontal-sm - 1px; + } + } + + &-handler-wrap { + border-left: @border-width-base @border-style-base @border-color-base; + width: 22px; + height: 100%; + background: @component-background; + position: absolute; + top: 0; + right: 0; + opacity: 0; + border-radius: 0 @border-radius-base @border-radius-base 0; + transition: opacity 0.24s linear 0.1s; + z-index: 2; // https://github.com/ant-design/ant-design/issues/6289 + } + + &-handler-wrap:hover &-handler { + height: 40%; + } + + &:hover &-handler-wrap { + opacity: 1; + } + + &-handler-up { + cursor: pointer; + &-inner { + top: 50%; + margin-top: -6px; + &:before { + text-align: center; + content: "\e61e"; + } + } + &:hover { + height: 60% !important; + } + } + + &-handler-down { + border-top: @border-width-base @border-style-base @border-color-base; + top: -1px; + cursor: pointer; + &-inner { + top: 50%; + margin-top: -6px; + &:before { + text-align: center; + content: "\e61d"; + } + } + &:hover { + height: 60% !important; + } + } + + &-handler-up-disabled, + &-handler-down-disabled { + cursor: not-allowed; + } + + &-handler-up-disabled:hover &-handler-up-inner, + &-handler-down-disabled:hover &-handler-down-inner { + color: @disabled-color; + } +} diff --git a/components/slider/demo/index.vue b/components/slider/demo/index.vue index 7d65ffaff..a04488de3 100644 --- a/components/slider/demo/index.vue +++ b/components/slider/demo/index.vue @@ -1,6 +1,6 @@ <script> import Basic from './basic.md' -// import InputNumber from './input-number.md' +import InputNumber from './input-number.md' import IconSlider from './icon-slider.md' import TipFormatter from './tip-formatter.md' import Event from './event.md' @@ -34,6 +34,8 @@ export default { <br/> <Basic /> <br /> + <InputNumber /> + <br /> <IconSlider /> <br /> <TipFormatter /> diff --git a/components/slider/demo/input-number.md b/components/slider/demo/input-number.md index 3a8e51c45..5522f72c5 100644 --- a/components/slider/demo/input-number.md +++ b/components/slider/demo/input-number.md @@ -15,28 +15,28 @@ Synchronize with [InptNumber](#/us/components/input-number/) component. <a-col :span="12"> <a-slider :min="1" :max="20" v-model="inputValue1" /> </a-col> - <!-- <a-col :span="4"> - <a-inputNumber + <a-col :span="4"> + <a-input-number :min="1" :max="20" style="marginLeft: 16px" v-model="inputValue1" /> - </a-ol> --> + </a-col> </a-row> <a-row> <a-col :span="12"> <a-slider :min="0" :max="1" v-model="inputValue" :step="0.01" /> </a-col> - <!-- <a-col :span="4"> - <a-inputNumber + <a-col :span="4"> + <a-input-number :min="0" :max="1" :step="0.01" style="marginLeft: 16px" v-model="inputValue" /> - </a-col> --> + </a-col> </a-row> </div> </template> diff --git a/components/style.js b/components/style.js index 5e3caafad..ffd2dec1b 100644 --- a/components/style.js +++ b/components/style.js @@ -38,3 +38,4 @@ import './slider/style' import './table/style' import './progress/style' import './timeline/style' +import './input-number/style' diff --git a/components/timeline/demo/index.vue b/components/timeline/demo/index.vue index 494692085..9a04e0517 100644 --- a/components/timeline/demo/index.vue +++ b/components/timeline/demo/index.vue @@ -17,7 +17,7 @@ const md = { ## 代码演示`, us: `# Data Display Vertical display timeline. - + ## When To Use - When a series of information needs to be ordered by time (ascend or descend). - When you need a timeline to make a visual connection. ## Examples diff --git a/examples/demo.js b/examples/demo.js index afd09b0e3..c032ecb56 100644 --- a/examples/demo.js +++ b/examples/demo.js @@ -39,3 +39,4 @@ export { default as slider } from 'antd/slider/demo/index.vue' export { default as progress } from 'antd/progress/demo/index.vue' export { default as timeline } from 'antd/timeline/demo/index.vue' export { default as table } from 'antd/table/demo/index.vue' +export { default as inputNumber } from 'antd/input-number/demo/index.vue' diff --git a/examples/routes.js b/examples/routes.js index 3d6bac0a1..0d3dafd6f 100644 --- a/examples/routes.js +++ b/examples/routes.js @@ -3,7 +3,7 @@ const AsyncComp = () => { const hashs = window.location.hash.split('/') const d = hashs[hashs.length - 1] return { - component: import(`../components/vc-input-number/demo/${d}`), + component: import(`../components/input-number/demo/${d}`), } } export default [