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 />
<borderlessVue />
<prefixVue />
<statusVue />
<iconVue />
</demo-sort>
</template>
<script lang="ts">
@ -23,6 +25,8 @@ import borderlessVue from './borderless.vue';
import keyboardVue from './keyboard.vue';
import outOfRangeVue from './out-of-range.vue';
import prefixVue from './prefix.vue';
import statusVue from './status.vue';
import iconVue from './icon.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
import { defineComponent } from 'vue';
@ -31,6 +35,8 @@ export default defineComponent({
CN,
US,
components: {
iconVue,
statusVue,
prefixVue,
Basic,
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 | - | |
| prefix | The prefix icon for the Input | slot | - | 3.0 |
| 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 | |
| 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 | | |
| upIcon | custom up icon | slot | `<UpOutlined />` | 3.3.0 |
| downIcon | custom up down | slot | `<DownOutlined />` | 3.3.0 |
### events

View File

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

View File

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

View File

@ -8,7 +8,7 @@
&-affix-wrapper {
.input();
// or number handler will cover form status
position: static;
position: relative;
display: inline-flex;
width: 90px;
padding: 0;
@ -49,14 +49,33 @@
visibility: hidden;
content: '\a0';
}
.@{ant-prefix}-input-number-handler-wrap {
z-index: 2;
}
}
&-prefix {
&-prefix,
&-suffix {
display: flex;
flex: none;
align-items: center;
pointer-events: none;
}
&-prefix {
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 {

View File

@ -2,6 +2,7 @@
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './affix';
@import './status';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
@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);
}
}