feat: inputnumber add status & upIcon & downIcon

pull/5820/head
tangjinzhou 2022-05-19 11:03:26 +08:00
parent 093fa555ba
commit 6e41fbd01f
9 changed files with 195 additions and 21 deletions

View File

@ -0,0 +1,47 @@
<docs>
---
order: 99
title:
zh-CN: 图标按钮
en-US: Icon
---
## zh-CN
使用 `upIcon` `downIcon` 插槽自定义图标
## en-US
use `upIcon` `downIcon` custom icon
</docs>
<template>
<div>
<a-input-number id="inputNumber" v-model:value="value" :min="1" :max="10">
<template #upIcon>
<ArrowUpOutlined />
</template>
<template #downIcon>
<ArrowDownOutlined />
</template>
</a-input-number>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons-vue';
export default defineComponent({
components: {
ArrowUpOutlined,
ArrowDownOutlined,
},
setup() {
const value = ref<number>(3);
return {
value,
};
},
});
</script>

View File

@ -10,6 +10,8 @@
<outOfRangeVue /> <outOfRangeVue />
<borderlessVue /> <borderlessVue />
<prefixVue /> <prefixVue />
<statusVue />
<iconVue />
</demo-sort> </demo-sort>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -23,6 +25,8 @@ import borderlessVue from './borderless.vue';
import keyboardVue from './keyboard.vue'; import keyboardVue from './keyboard.vue';
import outOfRangeVue from './out-of-range.vue'; import outOfRangeVue from './out-of-range.vue';
import prefixVue from './prefix.vue'; import prefixVue from './prefix.vue';
import statusVue from './status.vue';
import iconVue from './icon.vue';
import CN from '../index.zh-CN.md'; import CN from '../index.zh-CN.md';
import US from '../index.en-US.md'; import US from '../index.en-US.md';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
@ -31,6 +35,8 @@ export default defineComponent({
CN, CN,
US, US,
components: { components: {
iconVue,
statusVue,
prefixVue, prefixVue,
Basic, Basic,
Disabled, Disabled,

View File

@ -0,0 +1,43 @@
<docs>
---
order: 19
version: 3.3.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` InputNumber 添加状态可选 `error` 或者 `warning`
## en-US
Add status to InputNumber with `status`, which could be `error` or `warning`.
</docs>
<template>
<a-space direction="vertical" style="width: 100%">
<a-input-number status="error" style="width: 100%" />
<a-input-number status="warning" style="width: 100%" />
<a-input-number status="error" style="width: 100%">
<template #prefix><ClockCircleOutlined /></template>
</a-input-number>
<a-input-number status="warning" style="width: 100%">
<template #prefix><ClockCircleOutlined /></template>
</a-input-number>
</a-space>
</template>
<script lang="ts">
import { ClockCircleOutlined } from '@ant-design/icons-vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
ClockCircleOutlined,
},
setup() {
return {};
},
});
</script>

View File

@ -31,9 +31,12 @@ When a numeric value needs to be provided.
| precision | precision of input value | number | - | | | precision | precision of input value | number | - | |
| prefix | The prefix icon for the Input | slot | - | 3.0 | | prefix | The prefix icon for the Input | slot | - | 3.0 |
| size | height of input box | string | - | | | size | height of input box | string | - | |
| status | Set validation status | 'error' \| 'warning' | - | 3.3.0 |
| step | The number to which the current value is increased or decreased. It can be an integer or decimal. | number\|string | 1 | | | step | The number to which the current value is increased or decreased. It can be an integer or decimal. | number\|string | 1 | |
| stringMode | Set value as string to support high precision decimals. Will return string value by `change` | boolean | false | 3.0 | | stringMode | Set value as string to support high precision decimals. Will return string value by `change` | boolean | false | 3.0 |
| value(v-model) | current value | number | | | | value(v-model) | current value | number | | |
| upIcon | custom up icon | slot | `<UpOutlined />` | 3.3.0 |
| downIcon | custom up down | slot | `<DownOutlined />` | 3.3.0 |
### events ### events

View File

@ -1,16 +1,22 @@
import type { PropType, ExtractPropTypes, HTMLAttributes, App } from 'vue'; import type { PropType, ExtractPropTypes, HTMLAttributes, App } from 'vue';
import { watch, defineComponent, nextTick, onMounted, ref } from 'vue'; import { watch, defineComponent, nextTick, onMounted, ref, computed } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import UpOutlined from '@ant-design/icons-vue/UpOutlined'; import UpOutlined from '@ant-design/icons-vue/UpOutlined';
import DownOutlined from '@ant-design/icons-vue/DownOutlined'; import DownOutlined from '@ant-design/icons-vue/DownOutlined';
import VcInputNumber, { inputNumberProps as baseInputNumberProps } from './src/InputNumber'; import VcInputNumber, { inputNumberProps as baseInputNumberProps } from './src/InputNumber';
import type { SizeType } from '../config-provider'; import type { SizeType } from '../config-provider';
import { useInjectFormItemContext } from '../form/FormItemContext'; import {
FormItemInputContext,
NoFormStatus,
useInjectFormItemContext,
} from '../form/FormItemContext';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import omit from '../_util/omit'; import omit from '../_util/omit';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import isValidValue from '../_util/isValidValue'; import isValidValue from '../_util/isValidValue';
import type { InputStatus } from '../_util/statusUtils';
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
const baseProps = baseInputNumberProps(); const baseProps = baseInputNumberProps();
export const inputNumberProps = () => ({ export const inputNumberProps = () => ({
...baseProps, ...baseProps,
@ -25,6 +31,7 @@ export const inputNumberProps = () => ({
prefix: PropTypes.any, prefix: PropTypes.any,
'onUpdate:value': baseProps.onChange, 'onUpdate:value': baseProps.onChange,
valueModifiers: Object, valueModifiers: Object,
status: String as PropType<InputStatus>,
}); });
export type InputNumberProps = Partial<ExtractPropTypes<ReturnType<typeof inputNumberProps>>>; export type InputNumberProps = Partial<ExtractPropTypes<ReturnType<typeof inputNumberProps>>>;
@ -37,6 +44,8 @@ const InputNumber = defineComponent({
slots: ['addonBefore', 'addonAfter', 'prefix'], slots: ['addonBefore', 'addonAfter', 'prefix'],
setup(props, { emit, expose, attrs, slots }) { setup(props, { emit, expose, attrs, slots }) {
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject();
const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status));
const { prefixCls, size, direction } = useConfigInject('input-number', props); const { prefixCls, size, direction } = useConfigInject('input-number', props);
const mergedValue = ref(props.value === undefined ? props.defaultValue : props.value); const mergedValue = ref(props.value === undefined ? props.defaultValue : props.value);
const focused = ref(false); const focused = ref(false);
@ -84,6 +93,7 @@ const InputNumber = defineComponent({
}); });
}); });
return () => { return () => {
const { hasFeedback, isFormItemInput, feedbackIcon } = formItemInputContext;
const { const {
class: className, class: className,
bordered, bordered,
@ -106,7 +116,9 @@ const InputNumber = defineComponent({
[`${preCls}-rtl`]: direction.value === 'rtl', [`${preCls}-rtl`]: direction.value === 'rtl',
[`${preCls}-readonly`]: readonly, [`${preCls}-readonly`]: readonly,
[`${preCls}-borderless`]: !bordered, [`${preCls}-borderless`]: !bordered,
[`${preCls}-in-form-item`]: isFormItemInput,
}, },
getStatusClassNames(preCls, mergedStatus.value),
className, className,
); );
@ -123,14 +135,22 @@ const InputNumber = defineComponent({
onBlur={handleBlur} onBlur={handleBlur}
onFocus={handleFocus} onFocus={handleFocus}
v-slots={{ v-slots={{
upHandler: () => <UpOutlined class={`${preCls}-handler-up-inner`} />, upHandler: slots.upIcon
downHandler: () => <DownOutlined class={`${preCls}-handler-down-inner`} />, ? () => <span class={`${preCls}-handler-up-inner`}>{slots.upIcon()}</span>
: () => <UpOutlined class={`${preCls}-handler-up-inner`} />,
downHandler: slots.downIcon
? () => <span class={`${preCls}-handler-down-inner`}>{slots.downIcon()}</span>
: () => <DownOutlined class={`${preCls}-handler-down-inner`} />,
}} }}
/> />
); );
const hasAddon = isValidValue(addonBefore) || isValidValue(addonAfter); const hasAddon = isValidValue(addonBefore) || isValidValue(addonAfter);
if (isValidValue(prefix)) { const hasPrefix = isValidValue(prefix);
const affixWrapperCls = classNames(`${preCls}-affix-wrapper`, { if (hasPrefix || hasFeedback) {
const affixWrapperCls = classNames(
`${preCls}-affix-wrapper`,
getStatusClassNames(`${preCls}-affix-wrapper`, mergedStatus.value, hasFeedback),
{
[`${preCls}-affix-wrapper-focused`]: focused.value, [`${preCls}-affix-wrapper-focused`]: focused.value,
[`${preCls}-affix-wrapper-disabled`]: props.disabled, [`${preCls}-affix-wrapper-disabled`]: props.disabled,
[`${preCls}-affix-wrapper-sm`]: size.value === 'small', [`${preCls}-affix-wrapper-sm`]: size.value === 'small',
@ -140,15 +160,17 @@ const InputNumber = defineComponent({
[`${preCls}-affix-wrapper-borderless`]: !bordered, [`${preCls}-affix-wrapper-borderless`]: !bordered,
// className will go to addon wrapper // className will go to addon wrapper
[`${className}`]: !hasAddon && className, [`${className}`]: !hasAddon && className,
}); },
);
element = ( element = (
<div <div
class={affixWrapperCls} class={affixWrapperCls}
style={style} style={style}
onMouseup={() => inputNumberRef.value!.focus()} onMouseup={() => inputNumberRef.value!.focus()}
> >
<span class={`${preCls}-prefix`}>{prefix}</span> {hasPrefix && <span class={`${preCls}-prefix`}>{prefix}</span>}
{element} {element}
{hasFeedback && <span class={`${preCls}-suffix`}>{feedbackIcon}</span>}
</div> </div>
); );
} }
@ -172,14 +194,15 @@ const InputNumber = defineComponent({
[`${preCls}-group-wrapper-lg`]: mergeSize === 'large', [`${preCls}-group-wrapper-lg`]: mergeSize === 'large',
[`${preCls}-group-wrapper-rtl`]: direction.value === 'rtl', [`${preCls}-group-wrapper-rtl`]: direction.value === 'rtl',
}, },
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus.value, hasFeedback),
className, className,
); );
element = ( element = (
<div class={mergedGroupClassName} style={style}> <div class={mergedGroupClassName} style={style}>
<div class={mergedWrapperClassName}> <div class={mergedWrapperClassName}>
{addonBeforeNode} {addonBeforeNode && <NoFormStatus>{addonBeforeNode}</NoFormStatus>}
{element} {element}
{addonAfterNode} {addonAfterNode && <NoFormStatus>{addonAfterNode}</NoFormStatus>}
</div> </div>
</div> </div>
); );

View File

@ -34,8 +34,11 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg
| precision | 数值精度 | number | - | | | precision | 数值精度 | number | - | |
| prefix | 带有前缀图标的 input | slot | - | 3.0 | | prefix | 带有前缀图标的 input | slot | - | 3.0 |
| size | 输入框大小 | string | 无 | | | size | 输入框大小 | string | 无 | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 |
| step | 每次改变步数,可以为小数 | number\|string | 1 | | | step | 每次改变步数,可以为小数 | number\|string | 1 | |
| stringMode | 字符值模式,开启后支持高精度小数。同时 `change` 事件将返回 string 类型 | boolean | false | 3.0 | | stringMode | 字符值模式,开启后支持高精度小数。同时 `change` 事件将返回 string 类型 | boolean | false | 3.0 |
| upIcon | 自定义上箭头图标 | slot | `<UpOutlined />` | 3.3.0 |
| downIcon | 自定义下箭头图标 | slot | `<DownOutlined />` | 3.3.0 |
| value(v-model) | 当前值 | number | | | | value(v-model) | 当前值 | number | | |
### 事件 ### 事件

View File

@ -8,7 +8,7 @@
&-affix-wrapper { &-affix-wrapper {
.input(); .input();
// or number handler will cover form status // or number handler will cover form status
position: static; position: relative;
display: inline-flex; display: inline-flex;
width: 90px; width: 90px;
padding: 0; padding: 0;
@ -49,14 +49,33 @@
visibility: hidden; visibility: hidden;
content: '\a0'; content: '\a0';
} }
.@{ant-prefix}-input-number-handler-wrap {
z-index: 2;
}
} }
&-prefix { &-prefix,
&-suffix {
display: flex; display: flex;
flex: none; flex: none;
align-items: center; align-items: center;
pointer-events: none;
}
&-prefix {
margin-inline-end: @input-affix-margin; margin-inline-end: @input-affix-margin;
} }
&-suffix {
position: absolute;
top: 0;
right: 0;
z-index: 1;
height: 100%;
margin-right: @input-padding-horizontal-base;
margin-left: @input-affix-margin;
}
} }
.@{ant-prefix}-input-number-group-wrapper .@{ant-prefix}-input-number-affix-wrapper { .@{ant-prefix}-input-number-group-wrapper .@{ant-prefix}-input-number-affix-wrapper {

View File

@ -2,6 +2,7 @@
@import '../../style/mixins/index'; @import '../../style/mixins/index';
@import '../../input/style/mixin'; @import '../../input/style/mixin';
@import './affix'; @import './affix';
@import './status';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number'; @input-number-prefix-cls: ~'@{ant-prefix}-input-number';
@form-item-prefix-cls: ~'@{ant-prefix}-form-item'; @form-item-prefix-cls: ~'@{ant-prefix}-form-item';

View File

@ -0,0 +1,29 @@
@import '../../input/style/mixin';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
@input-number-wrapper-cls: @input-number-prefix-cls, ~'@{input-number-prefix-cls}-affix-wrapper';
each(@input-number-wrapper-cls, {
.@{value} {
&-status-error {
.status-color(@value, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
.status-color-common(@input-number-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline)
}
&-status-warning {
.status-color(@value, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
.status-color-common(@input-number-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline)
}
}
});
.@{input-number-prefix-cls}-group-wrapper {
&-status-error {
.group-status-color(@input-number-prefix-cls, @error-color, @error-color);
}
&-status-warning {
.group-status-color(@input-number-prefix-cls, @warning-color, @warning-color);
}
}