feat: update inputnumber

pull/2502/head
tangjinzhou 2020-06-27 22:17:48 +08:00
parent a0c08a599a
commit 93bde3fb67
7 changed files with 174 additions and 141 deletions

@ -1 +1 @@
Subproject commit 6cde2f94ca4a1dbbb260a4436369e2084ff467ed Subproject commit d680625687a695a9b9a06a131e12f9611a480d5b

View File

@ -1,11 +1,11 @@
import { inject } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { initDefaultProps, getOptionProps, getListeners } from '../_util/props-util'; import { initDefaultProps, getOptionProps } from '../_util/props-util';
import classNames from 'classnames'; import classNames from '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 from '../vc-input-number/src'; import VcInputNumber from '../vc-input-number/src';
import { ConfigConsumerProps } from '../config-provider'; import { ConfigConsumerProps } from '../config-provider';
import Base from '../base';
export const InputNumberProps = { export const InputNumberProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
@ -29,56 +29,59 @@ export const InputNumberProps = {
const InputNumber = { const InputNumber = {
name: 'AInputNumber', name: 'AInputNumber',
model: { inheritAttrs: false,
prop: 'value',
event: 'change',
},
props: initDefaultProps(InputNumberProps, { props: initDefaultProps(InputNumberProps, {
step: 1, step: 1,
}), }),
inject: { setup() {
configProvider: { default: () => ConfigConsumerProps }, return {
configProvider: inject('configProvider', ConfigConsumerProps),
};
}, },
methods: { methods: {
saveInputNumber(inputNumberRef) {
this.inputNumberRef = inputNumberRef;
},
focus() { focus() {
this.$refs.inputNumberRef.focus(); this.inputNumberRef.focus();
}, },
blur() { blur() {
this.$refs.inputNumberRef.blur(); this.inputNumberRef.blur();
}, },
}, },
render() { render() {
const { prefixCls: customizePrefixCls, size, ...others } = getOptionProps(this); const { prefixCls: customizePrefixCls, size, class: className, ...others } = {
...getOptionProps(this),
...this.$attrs,
};
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('input-number', customizePrefixCls); const prefixCls = getPrefixCls('input-number', customizePrefixCls);
const inputNumberClass = classNames({ const inputNumberClass = classNames(
[`${prefixCls}-lg`]: size === 'large', {
[`${prefixCls}-sm`]: size === 'small', [`${prefixCls}-lg`]: size === 'large',
}); [`${prefixCls}-sm`]: size === 'small',
},
className,
);
const upIcon = <UpOutlined class={`${prefixCls}-handler-up-inner`} />; const upIcon = <UpOutlined class={`${prefixCls}-handler-up-inner`} />;
const downIcon = <DownOutlined class={`${prefixCls}-handler-down-inner`} />; const downIcon = <DownOutlined class={`${prefixCls}-handler-down-inner`} />;
const vcInputNumberprops = { const vcInputNumberprops = {
props: { prefixCls,
prefixCls, upHandler: upIcon,
upHandler: upIcon, downHandler: downIcon,
downHandler: downIcon, ...others,
...others,
},
class: inputNumberClass, class: inputNumberClass,
ref: 'inputNumberRef',
on: getListeners(this),
}; };
return <VcInputNumber {...vcInputNumberprops} />; return <VcInputNumber {...vcInputNumberprops} ref={this.saveInputNumber} />;
}, },
}; };
/* istanbul ignore next */ /* istanbul ignore next */
InputNumber.install = function(Vue) { InputNumber.install = function(app) {
Vue.use(Base); app.component(InputNumber.name, InputNumber);
Vue.component(InputNumber.name, InputNumber);
}; };
export default InputNumber; export default InputNumber;

View File

@ -1,9 +1,10 @@
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import Touchable from '../../vc-m-feedback'; import Touchable from '../../vc-m-feedback';
import { getListeners } from '../../_util/props-util'; import { getSlot } from '../../_util/props-util';
const InputHandler = { const InputHandler = {
name: 'InputHandler', name: 'InputHandler',
inheritAttrs: false,
props: { props: {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
@ -11,15 +12,12 @@ const InputHandler = {
render() { render() {
const { prefixCls, disabled } = this.$props; const { prefixCls, disabled } = this.$props;
const touchableProps = { const touchableProps = {
props: { disabled,
disabled, activeClassName: `${prefixCls}-handler-active`,
activeClassName: `${prefixCls}-handler-active`,
},
on: getListeners(this),
}; };
return ( return (
<Touchable {...touchableProps}> <Touchable {...touchableProps}>
<span>{this.$slots.default}</span> <span {...this.$attrs}>{getSlot(this)}</span>
</Touchable> </Touchable>
); );
}, },

View File

@ -1,7 +1,7 @@
// based on rc-input-number 4.5.5 // based on rc-input-number 4.5.5
import PropTypes from '../../_util/vue-types'; import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin'; import BaseMixin from '../../_util/BaseMixin';
import { initDefaultProps, hasProp, getOptionProps, getListeners } from '../../_util/props-util'; import { initDefaultProps, hasProp, getOptionProps } from '../../_util/props-util';
import classNames from 'classnames'; import classNames from 'classnames';
import KeyCode from '../../_util/KeyCode'; import KeyCode from '../../_util/KeyCode';
import InputHandler from './InputHandler'; import InputHandler from './InputHandler';
@ -76,15 +76,17 @@ const inputNumberProps = {
title: PropTypes.string, title: PropTypes.string,
name: PropTypes.string, name: PropTypes.string,
id: PropTypes.string, id: PropTypes.string,
type: PropTypes.string,
}; };
export default { export default {
name: 'VCInputNumber', name: 'VCInputNumber',
mixins: [BaseMixin], mixins: [BaseMixin],
model: { inheritAttrs: false,
prop: 'value', // model: {
event: 'change', // prop: 'value',
}, // event: 'change',
// },
props: initDefaultProps(inputNumberProps, { props: initDefaultProps(inputNumberProps, {
focusOnUpDown: true, focusOnUpDown: true,
useTouch: false, useTouch: false,
@ -158,6 +160,7 @@ export default {
typeof nextValue === 'number' && typeof nextValue === 'number' &&
nextValue > max nextValue > max
) { ) {
this.$emit('update:value', max);
this.$emit('change', max); this.$emit('change', max);
} }
if ( if (
@ -166,6 +169,7 @@ export default {
typeof nextValue === 'number' && typeof nextValue === 'number' &&
nextValue < min nextValue < min
) { ) {
this.$emit('update:value', min);
this.$emit('change', min); this.$emit('change', min);
} }
} }
@ -179,7 +183,7 @@ export default {
}, },
methods: { methods: {
updatedFunc() { updatedFunc() {
const inputElem = this.$refs.inputRef; const inputElem = this.inputRef;
// Restore cursor // Restore cursor
try { try {
// Firefox set the input cursor after it get focused. // Firefox set the input cursor after it get focused.
@ -270,7 +274,9 @@ export default {
} }
this.rawInput = this.parser(this.getValueFromEvent(e)); this.rawInput = this.parser(this.getValueFromEvent(e));
this.setState({ inputValue: this.rawInput }); this.setState({ inputValue: this.rawInput });
this.$emit('change', this.toNumber(this.rawInput)); // valid number or invalid string const num = this.toNumber(this.rawInput); // valid number or invalid string
this.$emit('update:value', num);
this.$emit('change', num);
}, },
onFocus(...args) { onFocus(...args) {
this.setState({ this.setState({
@ -285,12 +291,12 @@ export default {
}); });
const value = this.getCurrentValidValue(this.inputValue); const value = this.getCurrentValidValue(this.inputValue);
const newValue = this.setValue(value); const newValue = this.setValue(value);
if (this.$listeners.blur) { if (this.$attrs.onBlur) {
const originValue = this.$refs.inputRef.value; const originValue = this.inputRef.value;
const inputValue = this.getInputDisplayValue({ focused: false, sValue: newValue }); const inputValue = this.getInputDisplayValue({ focused: false, sValue: newValue });
this.$refs.inputRef.value = inputValue; this.inputRef.value = inputValue;
this.$emit('blur', ...args); this.$emit('blur', ...args);
this.$refs.inputRef.value = originValue; this.inputRef.value = originValue;
} }
}, },
getCurrentValidValue(value) { getCurrentValidValue(value) {
@ -366,6 +372,7 @@ export default {
); );
} }
if (changed) { if (changed) {
this.$emit('update:value', newValue);
this.$emit('change', newValue); this.$emit('change', newValue);
} }
return newValue; return newValue;
@ -431,7 +438,7 @@ export default {
recordCursorPosition() { recordCursorPosition() {
// Record position // Record position
try { try {
const inputElem = this.$refs.inputRef; const inputElem = this.inputRef;
this.cursorStart = inputElem.selectionStart; this.cursorStart = inputElem.selectionStart;
this.cursorEnd = inputElem.selectionEnd; this.cursorEnd = inputElem.selectionEnd;
this.currentValue = inputElem.value; this.currentValue = inputElem.value;
@ -444,17 +451,12 @@ export default {
} }
}, },
fixCaret(start, end) { fixCaret(start, end) {
if ( if (start === undefined || end === undefined || !this.inputRef || !this.inputRef.value) {
start === undefined ||
end === undefined ||
!this.$refs.inputRef ||
!this.$refs.inputRef.value
) {
return; return;
} }
try { try {
const inputElem = this.$refs.inputRef; const inputElem = this.inputRef;
const currentStart = inputElem.selectionStart; const currentStart = inputElem.selectionStart;
const currentEnd = inputElem.selectionEnd; const currentEnd = inputElem.selectionEnd;
@ -470,7 +472,7 @@ export default {
restoreByAfter(str) { restoreByAfter(str) {
if (str === undefined) return false; if (str === undefined) return false;
const fullStr = this.$refs.inputRef.value; const fullStr = this.inputRef.value;
const index = fullStr.lastIndexOf(str); const index = fullStr.lastIndexOf(str);
if (index === -1) return false; if (index === -1) return false;
@ -504,11 +506,11 @@ export default {
}); });
}, },
focus() { focus() {
this.$refs.inputRef.focus(); this.inputRef.focus();
this.recordCursorPosition(); this.recordCursorPosition();
}, },
blur() { blur() {
this.$refs.inputRef.blur(); this.inputRef.blur();
}, },
formatWrapper(num) { formatWrapper(num) {
// http://2ality.com/2012/03/signedzero.html // http://2ality.com/2012/03/signedzero.html
@ -621,8 +623,20 @@ export default {
handleInputClick() { handleInputClick() {
this.$emit('click'); this.$emit('click');
}, },
saveUp(node) {
this.upHandlerRef = node;
},
saveDown(node) {
this.downHandlerRef = node;
},
saveInput(node) {
this.inputRef = node;
},
}, },
render() { render() {
const props = { ...this.$props, ...this.$attrs };
const { const {
prefixCls, prefixCls,
disabled, disabled,
@ -631,8 +645,10 @@ export default {
autoComplete, autoComplete,
upHandler, upHandler,
downHandler, downHandler,
} = this.$props; class: className,
} = props;
const classes = classNames({ const classes = classNames({
[className]: className,
[prefixCls]: true, [prefixCls]: true,
[`${prefixCls}-disabled`]: disabled, [`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: this.focused, [`${prefixCls}-focused`]: this.focused,
@ -655,6 +671,16 @@ export default {
} }
} }
const dataOrAriaAttributeProps = {};
for (const key in props) {
if (
props.hasOwnProperty(key) &&
(key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role')
) {
dataOrAriaAttributeProps[key] = props[key];
}
}
const editable = !this.readOnly && !this.disabled; const editable = !this.readOnly && !this.disabled;
// focus state, show input value // focus state, show input value
@ -665,71 +691,61 @@ export default {
let downEvents; let downEvents;
if (useTouch) { if (useTouch) {
upEvents = { upEvents = {
touchstart: editable && !upDisabledClass ? this.up : noop, onTouchstart: editable && !upDisabledClass ? this.up : noop,
touchend: this.stop, onTouchend: this.stop,
}; };
downEvents = { downEvents = {
touchstart: editable && !downDisabledClass ? this.down : noop, onTouchstart: editable && !downDisabledClass ? this.down : noop,
touchend: this.stop, onTouchend: this.stop,
}; };
} else { } else {
upEvents = { upEvents = {
mousedown: editable && !upDisabledClass ? this.up : noop, onMousedown: editable && !upDisabledClass ? this.up : noop,
mouseup: this.stop, onMouseup: this.stop,
mouseleave: this.stop, onMouseleave: this.stop,
}; };
downEvents = { downEvents = {
mousedown: editable && !downDisabledClass ? this.down : noop, onMousedown: editable && !downDisabledClass ? this.down : noop,
mouseup: this.stop, onMouseup: this.stop,
mouseleave: this.stop, onMouseleave: this.stop,
}; };
} }
const isUpDisabled = !!upDisabledClass || disabled || readOnly; const isUpDisabled = !!upDisabledClass || disabled || readOnly;
const isDownDisabled = !!downDisabledClass || disabled || readOnly; const isDownDisabled = !!downDisabledClass || disabled || readOnly;
const {
mouseenter = noop,
mouseleave = noop,
mouseover = noop,
mouseout = noop,
} = getListeners(this);
const contentProps = {
on: { mouseenter, mouseleave, mouseover, mouseout },
class: classes,
attrs: { title: this.$props.title },
};
const upHandlerProps = { const upHandlerProps = {
props: { disabled: isUpDisabled,
disabled: isUpDisabled, prefixCls,
prefixCls, unselectable: 'unselectable',
}, role: 'button',
attrs: { 'aria-label': 'Increase Value',
unselectable: 'unselectable', 'aria-disabled': !!isUpDisabled,
role: 'button',
'aria-label': 'Increase Value',
'aria-disabled': !!isUpDisabled,
},
class: `${prefixCls}-handler ${prefixCls}-handler-up ${upDisabledClass}`, class: `${prefixCls}-handler ${prefixCls}-handler-up ${upDisabledClass}`,
on: upEvents, ...upEvents,
ref: 'up', ref: this.saveUp,
}; };
const downHandlerProps = { const downHandlerProps = {
props: { disabled: isDownDisabled,
disabled: isDownDisabled, prefixCls,
prefixCls, unselectable: 'unselectable',
}, role: 'button',
attrs: { 'aria-label': 'Decrease Value',
unselectable: 'unselectable', 'aria-disabled': !!isDownDisabled,
role: 'button',
'aria-label': 'Decrease Value',
'aria-disabled': !!isDownDisabled,
},
class: `${prefixCls}-handler ${prefixCls}-handler-down ${downDisabledClass}`, class: `${prefixCls}-handler ${prefixCls}-handler-down ${downDisabledClass}`,
on: downEvents, ...downEvents,
ref: 'down', ref: this.saveDown,
}; };
// ref for test
return ( return (
<div {...contentProps}> <div
class={classes}
style={props.style}
title={props.title}
onMouseenter={props.onMouseenter}
onMouseleave={props.onMouseleave}
onMouseover={props.onMouseover}
onMouseout={props.onMouseout}
>
<div class={`${prefixCls}-handler-wrap`}> <div class={`${prefixCls}-handler-wrap`}>
<InputHandler {...upHandlerProps}> <InputHandler {...upHandlerProps}>
{upHandler || ( {upHandler || (
@ -757,7 +773,7 @@ export default {
aria-valuemax={this.max} aria-valuemax={this.max}
aria-valuenow={sValue} aria-valuenow={sValue}
required={this.required} required={this.required}
type={this.type} type={props.type}
placeholder={this.placeholder} placeholder={this.placeholder}
onClick={this.handleInputClick} onClick={this.handleInputClick}
class={`${prefixCls}-input`} class={`${prefixCls}-input`}
@ -767,7 +783,7 @@ export default {
onBlur={this.onBlur} onBlur={this.onBlur}
onKeydown={editable ? this.onKeyDown : noop} onKeydown={editable ? this.onKeyDown : noop}
onKeyup={editable ? this.onKeyUp : noop} onKeyup={editable ? this.onKeyUp : noop}
maxLength={this.maxLength} maxlength={props.maxLength}
readOnly={this.readOnly} readOnly={this.readOnly}
disabled={this.disabled} disabled={this.disabled}
max={this.max} max={this.max}
@ -777,9 +793,10 @@ export default {
title={this.title} title={this.title}
id={this.id} id={this.id}
onInput={this.onChange} onInput={this.onChange}
ref="inputRef" ref={this.saveInput}
value={inputDisplayValue} value={inputDisplayValue}
pattern={this.pattern} pattern={this.pattern}
{...dataOrAriaAttributeProps}
/> />
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
import { initDefaultProps } from '../../_util/props-util'; import classNames from 'classnames';
import { initDefaultProps, getSlot } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode'; import { cloneElement } from '../../_util/vnode';
import warning from '../../_util/warning'; import warning from '../../_util/warning';
import BaseMixin from '../../_util/BaseMixin'; import BaseMixin from '../../_util/BaseMixin';
@ -7,10 +8,12 @@ import { ITouchProps } from './PropTypes';
export default { export default {
name: 'TouchFeedback', name: 'TouchFeedback',
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false,
props: initDefaultProps(ITouchProps, { props: initDefaultProps(ITouchProps, {
disabled: false, disabled: false,
}), }),
data() { data() {
this.child = null;
return { return {
active: false, active: false,
}; };
@ -26,8 +29,12 @@ export default {
}, },
methods: { methods: {
triggerEvent(type, isActive, ev) { triggerEvent(type, isActive, ev) {
// input-numberTouchableantd const eventType = `on${type}`;
this.$emit(type, ev); const { child } = this;
if (child.props[eventType]) {
child.props[eventType](ev);
}
if (isActive !== this.active) { if (isActive !== this.active) {
this.setState({ this.setState({
active: isActive, active: isActive,
@ -35,60 +42,66 @@ export default {
} }
}, },
onTouchStart(e) { onTouchStart(e) {
this.triggerEvent('touchstart', true, e); this.triggerEvent('Touchstart', true, e);
}, },
onTouchMove(e) { onTouchMove(e) {
this.triggerEvent('touchmove', false, e); this.triggerEvent('Touchmove', false, e);
}, },
onTouchEnd(e) { onTouchEnd(e) {
this.triggerEvent('touchend', false, e); this.triggerEvent('Touchend', false, e);
}, },
onTouchCancel(e) { onTouchCancel(e) {
this.triggerEvent('touchcancel', false, e); this.triggerEvent('Touchcancel', false, e);
}, },
onMouseDown(e) { onMouseDown(e) {
// pc simulate mobile // pc simulate mobile
this.triggerEvent('mousedown', true, e); this.triggerEvent('Mousedown', true, e);
}, },
onMouseUp(e) { onMouseUp(e) {
this.triggerEvent('mouseup', false, e); this.triggerEvent('Mouseup', false, e);
}, },
onMouseLeave(e) { onMouseLeave(e) {
this.triggerEvent('mouseleave', false, e); this.triggerEvent('Mouseleave', false, e);
}, },
}, },
render() { render() {
const { disabled, activeClassName = '', activeStyle = {} } = this.$props; const { disabled, activeClassName = '', activeStyle = {} } = this.$props;
const child = this.$slots.default; let child = getSlot(this);
if (child.length !== 1) { if (child.length !== 1) {
warning(false, 'm-feedback组件只能包含一个子元素'); warning(false, 'm-feedback组件只能包含一个子元素');
return null; return null;
} }
let childProps = { const events = disabled
on: disabled ? undefined
? {} : {
: { onTouchstart: this.onTouchStart,
touchstart: this.onTouchStart, onTouchmove: this.onTouchMove,
touchmove: this.onTouchMove, onTouchend: this.onTouchEnd,
touchend: this.onTouchEnd, onTouchcancel: this.onTouchCancel,
touchcancel: this.onTouchCancel, onMousedown: this.onMouseDown,
mousedown: this.onMouseDown, onMouseup: this.onMouseUp,
mouseup: this.onMouseUp, onMouseleave: this.onMouseLeave,
mouseleave: this.onMouseLeave, };
},
};
child = child[0];
this.child = child;
if (!disabled && this.active) { if (!disabled && this.active) {
childProps = { let { style, class: className } = child.props;
...childProps,
...{ if (activeStyle !== false) {
style: activeStyle, if (activeStyle) {
class: activeClassName, style = { ...style, ...activeStyle };
}, }
}; className = classNames(className, activeClassName);
}
return cloneElement(child, {
class: className,
style,
...events,
});
} }
return cloneElement(child, childProps); return cloneElement(child, events);
}, },
}; };

View File

@ -4,7 +4,7 @@
</div> </div>
</template> </template>
<script> <script>
import demo from '../antdv-demo/docs/input/demo/tooltip'; import demo from '../antdv-demo/docs/input-number/demo/index';
export default { export default {
components: { components: {

View File

@ -3,6 +3,7 @@ import { createApp } from 'vue';
import App from './App.vue'; import App from './App.vue';
import { import {
Input, Input,
InputNumber,
Rate, Rate,
Button, Button,
Upload, Upload,
@ -41,4 +42,5 @@ app
.use(Tooltip) .use(Tooltip)
.use(Col) .use(Col)
.use(Row) .use(Row)
.use(InputNumber)
.mount('#app'); .mount('#app');