add vc-m-feedback
parent
0794519184
commit
3578f9774e
|
@ -0,0 +1,115 @@
|
||||||
|
@inputNumberPrefixCls: rc-input-number;
|
||||||
|
|
||||||
|
.@{inputNumberPrefixCls} {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 26px;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 26px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
border: 1px solid #D9D9D9;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&-handler {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 12px;
|
||||||
|
height: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
display:block;
|
||||||
|
touch-action: none;
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-handler-up-inner, &-handler-down-inner {
|
||||||
|
color: #666666;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #23c0fa;
|
||||||
|
|
||||||
|
.@{inputNumberPrefixCls}-handler-up, .@{inputNumberPrefixCls}-handler-wrap {
|
||||||
|
border-color: #23c0fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-disabled:hover {
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
|
||||||
|
.@{inputNumberPrefixCls}-handler-up, .@{inputNumberPrefixCls}-handler-wrap {
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-input-wrap {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
outline: 0;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
line-height: 26px;
|
||||||
|
height: 26px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
color: #666666;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-handler-wrap {
|
||||||
|
float: right;
|
||||||
|
border-left: 1px solid #D9D9D9;
|
||||||
|
width: 20px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-handler-up {
|
||||||
|
border-bottom: 1px solid #D9D9D9;
|
||||||
|
padding-top: 1px;
|
||||||
|
&-inner {
|
||||||
|
&:after {
|
||||||
|
content: '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-handler-down {
|
||||||
|
&-inner {
|
||||||
|
&:after {
|
||||||
|
content: '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.handler-disabled() {
|
||||||
|
opacity: 0.72;
|
||||||
|
&:hover {
|
||||||
|
color: #999;
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-handler-down-disabled, &-handler-up-disabled {
|
||||||
|
.handler-disabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
&-disabled {
|
||||||
|
.@{inputNumberPrefixCls}-input {
|
||||||
|
opacity: 0.72;
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
}
|
||||||
|
.@{inputNumberPrefixCls}-handler {
|
||||||
|
.handler-disabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Touchable from 'rmc-feedback';
|
||||||
|
|
||||||
|
class InputHandler extends Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
prefixCls, disabled, onTouchStart, onTouchEnd,
|
||||||
|
onMouseDown, onMouseUp, onMouseLeave, ...otherProps,
|
||||||
|
} = this.props;
|
||||||
|
return (
|
||||||
|
<Touchable
|
||||||
|
disabled={disabled}
|
||||||
|
onTouchStart={onTouchStart}
|
||||||
|
onTouchEnd={onTouchEnd}
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
|
onMouseUp={onMouseUp}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
activeClassName={`${prefixCls}-handler-active`}
|
||||||
|
>
|
||||||
|
<span {...otherProps} />
|
||||||
|
</Touchable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputHandler.propTypes = {
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onTouchStart: PropTypes.func,
|
||||||
|
onTouchEnd: PropTypes.func,
|
||||||
|
onMouseDown: PropTypes.func,
|
||||||
|
onMouseUp: PropTypes.func,
|
||||||
|
onMouseLeave: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputHandler;
|
|
@ -0,0 +1,610 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import isNegativeZero from 'is-negative-zero';
|
||||||
|
import InputHandler from './InputHandler';
|
||||||
|
|
||||||
|
function noop() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function preventDefault(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultParser(input) {
|
||||||
|
return input.replace(/[^\w\.-]+/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When click and hold on a button - the speed of auto changin the value.
|
||||||
|
*/
|
||||||
|
const SPEED = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When click and hold on a button - the delay before auto changin the value.
|
||||||
|
*/
|
||||||
|
const DELAY = 600;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max Safe Integer -- on IE this is not available, so manually set the number in that case.
|
||||||
|
* The reason this is used, instead of Infinity is because numbers above the MSI are unstable
|
||||||
|
*/
|
||||||
|
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;
|
||||||
|
|
||||||
|
export default class InputNumber extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
value: PropTypes.oneOfType([
|
||||||
|
PropTypes.number,
|
||||||
|
PropTypes.string,
|
||||||
|
]),
|
||||||
|
defaultValue: PropTypes.oneOfType([
|
||||||
|
PropTypes.number,
|
||||||
|
PropTypes.string,
|
||||||
|
]),
|
||||||
|
focusOnUpDown: PropTypes.bool,
|
||||||
|
autoFocus: PropTypes.bool,
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
onKeyDown: PropTypes.func,
|
||||||
|
onKeyUp: PropTypes.func,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
tabIndex: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onFocus: PropTypes.func,
|
||||||
|
onBlur: PropTypes.func,
|
||||||
|
readOnly: PropTypes.bool,
|
||||||
|
max: PropTypes.number,
|
||||||
|
min: PropTypes.number,
|
||||||
|
step: PropTypes.oneOfType([
|
||||||
|
PropTypes.number,
|
||||||
|
PropTypes.string,
|
||||||
|
]),
|
||||||
|
upHandler: PropTypes.node,
|
||||||
|
downHandler: PropTypes.node,
|
||||||
|
useTouch: PropTypes.bool,
|
||||||
|
formatter: PropTypes.func,
|
||||||
|
parser: PropTypes.func,
|
||||||
|
onMouseEnter: PropTypes.func,
|
||||||
|
onMouseLeave: PropTypes.func,
|
||||||
|
onMouseOver: PropTypes.func,
|
||||||
|
onMouseOut: PropTypes.func,
|
||||||
|
precision: PropTypes.number,
|
||||||
|
required: PropTypes.bool,
|
||||||
|
pattern: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
focusOnUpDown: true,
|
||||||
|
useTouch: false,
|
||||||
|
prefixCls: 'rc-input-number',
|
||||||
|
min: -MAX_SAFE_INTEGER,
|
||||||
|
step: 1,
|
||||||
|
style: {},
|
||||||
|
onChange: noop,
|
||||||
|
onKeyDown: noop,
|
||||||
|
onFocus: noop,
|
||||||
|
onBlur: noop,
|
||||||
|
parser: defaultParser,
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
let value;
|
||||||
|
if ('value' in props) {
|
||||||
|
value = props.value;
|
||||||
|
} else {
|
||||||
|
value = props.defaultValue;
|
||||||
|
}
|
||||||
|
value = this.toNumber(value);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
inputValue: this.toPrecisionAsStep(value),
|
||||||
|
value,
|
||||||
|
focused: props.autoFocus,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.componentDidUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if ('value' in nextProps) {
|
||||||
|
const value = this.state.focused
|
||||||
|
? nextProps.value : this.getValidValue(nextProps.value, nextProps.min, nextProps.max);
|
||||||
|
this.setState({
|
||||||
|
value,
|
||||||
|
inputValue: this.inputting ? value : this.toPrecisionAsStep(value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUpdate() {
|
||||||
|
try {
|
||||||
|
this.start = this.input.selectionStart;
|
||||||
|
this.end = this.input.selectionEnd;
|
||||||
|
} catch (e) {
|
||||||
|
// Fix error in Chrome:
|
||||||
|
// Failed to read the 'selectionStart' property from 'HTMLInputElement'
|
||||||
|
// http://stackoverflow.com/q/21177489/3040605
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
// pressingUpOrDown is true means that someone just click up or down button
|
||||||
|
// https://github.com/ant-design/ant-design/issues/9204
|
||||||
|
if (!this.pressingUpOrDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.props.focusOnUpDown && this.state.focused) {
|
||||||
|
const selectionRange = this.input.setSelectionRange;
|
||||||
|
if (selectionRange &&
|
||||||
|
typeof selectionRange === 'function' &&
|
||||||
|
this.start !== undefined &&
|
||||||
|
this.end !== undefined) {
|
||||||
|
this.input.setSelectionRange(this.start, this.end);
|
||||||
|
} else {
|
||||||
|
this.focus();
|
||||||
|
}
|
||||||
|
this.pressingUpOrDown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown = (e, ...args) => {
|
||||||
|
if (e.keyCode === 38) {
|
||||||
|
const ratio = this.getRatio(e);
|
||||||
|
this.up(e, ratio);
|
||||||
|
this.stop();
|
||||||
|
} else if (e.keyCode === 40) {
|
||||||
|
const ratio = this.getRatio(e);
|
||||||
|
this.down(e, ratio);
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
const { onKeyDown } = this.props;
|
||||||
|
if (onKeyDown) {
|
||||||
|
onKeyDown(e, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyUp = (e, ...args) => {
|
||||||
|
this.stop();
|
||||||
|
const { onKeyUp } = this.props;
|
||||||
|
if (onKeyUp) {
|
||||||
|
onKeyUp(e, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange = (e) => {
|
||||||
|
if (this.state.focused) {
|
||||||
|
this.inputting = true;
|
||||||
|
}
|
||||||
|
const input = this.props.parser(this.getValueFromEvent(e));
|
||||||
|
this.setState({ inputValue: input });
|
||||||
|
this.props.onChange(this.toNumberWhenUserInput(input)); // valid number or invalid string
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus = (...args) => {
|
||||||
|
this.setState({
|
||||||
|
focused: true,
|
||||||
|
});
|
||||||
|
this.props.onFocus(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur = (e, ...args) => {
|
||||||
|
this.inputting = false;
|
||||||
|
this.setState({
|
||||||
|
focused: false,
|
||||||
|
});
|
||||||
|
const value = this.getCurrentValidValue(this.state.inputValue);
|
||||||
|
e.persist(); // fix https://github.com/react-component/input-number/issues/51
|
||||||
|
this.setValue(value, () => {
|
||||||
|
this.props.onBlur(e, ...args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentValidValue(value) {
|
||||||
|
let val = value;
|
||||||
|
if (val === '') {
|
||||||
|
val = '';
|
||||||
|
} else if (!this.isNotCompleteNumber(val)) {
|
||||||
|
val = this.getValidValue(val);
|
||||||
|
} else {
|
||||||
|
val = this.state.value;
|
||||||
|
}
|
||||||
|
return this.toNumber(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRatio(e) {
|
||||||
|
let ratio = 1;
|
||||||
|
if (e.metaKey || e.ctrlKey) {
|
||||||
|
ratio = 0.1;
|
||||||
|
} else if (e.shiftKey) {
|
||||||
|
ratio = 10;
|
||||||
|
}
|
||||||
|
return ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValueFromEvent(e) {
|
||||||
|
// optimize for chinese input expierence
|
||||||
|
// https://github.com/ant-design/ant-design/issues/8196
|
||||||
|
return e.target.value.trim().replace(/。/g, '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
getValidValue(value, min = this.props.min, max = this.props.max) {
|
||||||
|
let val = parseFloat(value, 10);
|
||||||
|
// https://github.com/ant-design/ant-design/issues/7358
|
||||||
|
if (isNaN(val)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (val < min) {
|
||||||
|
val = min;
|
||||||
|
}
|
||||||
|
if (val > max) {
|
||||||
|
val = max;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(v, callback) {
|
||||||
|
// trigger onChange
|
||||||
|
const newValue = this.isNotCompleteNumber(parseFloat(v, 10)) ? undefined : parseFloat(v, 10);
|
||||||
|
const changed = newValue !== this.state.value ||
|
||||||
|
`${newValue}` !== `${this.state.inputValue}`; // https://github.com/ant-design/ant-design/issues/7363
|
||||||
|
if (!('value' in this.props)) {
|
||||||
|
this.setState({
|
||||||
|
value: newValue,
|
||||||
|
inputValue: this.toPrecisionAsStep(v),
|
||||||
|
}, callback);
|
||||||
|
} else {
|
||||||
|
// always set input value same as value
|
||||||
|
this.setState({
|
||||||
|
inputValue: this.toPrecisionAsStep(this.state.value),
|
||||||
|
}, callback);
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
this.props.onChange(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrecision(value) {
|
||||||
|
if ('precision' in this.props) {
|
||||||
|
return this.props.precision;
|
||||||
|
}
|
||||||
|
const valueString = value.toString();
|
||||||
|
if (valueString.indexOf('e-') >= 0) {
|
||||||
|
return parseInt(valueString.slice(valueString.indexOf('e-') + 2), 10);
|
||||||
|
}
|
||||||
|
let precision = 0;
|
||||||
|
if (valueString.indexOf('.') >= 0) {
|
||||||
|
precision = valueString.length - valueString.indexOf('.') - 1;
|
||||||
|
}
|
||||||
|
return precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
// step={1.0} value={1.51}
|
||||||
|
// press +
|
||||||
|
// then value should be 2.51, rather than 2.5
|
||||||
|
// if this.props.precision is undefined
|
||||||
|
// https://github.com/react-component/input-number/issues/39
|
||||||
|
getMaxPrecision(currentValue, ratio = 1) {
|
||||||
|
if ('precision' in this.props) {
|
||||||
|
return this.props.precision;
|
||||||
|
}
|
||||||
|
const { step } = this.props;
|
||||||
|
const ratioPrecision = this.getPrecision(ratio);
|
||||||
|
const stepPrecision = this.getPrecision(step);
|
||||||
|
const currentValuePrecision = this.getPrecision(currentValue);
|
||||||
|
if (!currentValue) {
|
||||||
|
return ratioPrecision + stepPrecision;
|
||||||
|
}
|
||||||
|
return Math.max(currentValuePrecision, ratioPrecision + stepPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrecisionFactor(currentValue, ratio = 1) {
|
||||||
|
const precision = this.getMaxPrecision(currentValue, ratio);
|
||||||
|
return Math.pow(10, precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
blur() {
|
||||||
|
this.input.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
formatWrapper(num) {
|
||||||
|
// http://2ality.com/2012/03/signedzero.html
|
||||||
|
// https://github.com/ant-design/ant-design/issues/9439
|
||||||
|
if (isNegativeZero(num)) {
|
||||||
|
return '-0';
|
||||||
|
}
|
||||||
|
if (this.props.formatter) {
|
||||||
|
return this.props.formatter(num);
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
toPrecisionAsStep(num) {
|
||||||
|
if (this.isNotCompleteNumber(num) || num === '') {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
const precision = Math.abs(this.getMaxPrecision(num));
|
||||||
|
if (precision === 0) {
|
||||||
|
return num.toString();
|
||||||
|
}
|
||||||
|
if (!isNaN(precision)) {
|
||||||
|
return Number(num).toFixed(precision);
|
||||||
|
}
|
||||||
|
return num.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// '1.' '1x' 'xx' '' => are not complete numbers
|
||||||
|
isNotCompleteNumber(num) {
|
||||||
|
return (
|
||||||
|
isNaN(num) ||
|
||||||
|
num === '' ||
|
||||||
|
num === null ||
|
||||||
|
(num && num.toString().indexOf('.') === num.toString().length - 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toNumber(num) {
|
||||||
|
if (this.isNotCompleteNumber(num)) {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
if ('precision' in this.props) {
|
||||||
|
return Number(Number(num).toFixed(this.props.precision));
|
||||||
|
}
|
||||||
|
return Number(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
// '1.0' '1.00' => may be a inputing number
|
||||||
|
toNumberWhenUserInput(num) {
|
||||||
|
// num.length > 16 => prevent input large number will became Infinity
|
||||||
|
if ((/\.\d*0$/.test(num) || num.length > 16) && this.state.focused) {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
return this.toNumber(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
upStep(val, rat) {
|
||||||
|
const { step, min } = this.props;
|
||||||
|
const precisionFactor = this.getPrecisionFactor(val, rat);
|
||||||
|
const precision = Math.abs(this.getMaxPrecision(val, rat));
|
||||||
|
let result;
|
||||||
|
if (typeof val === 'number') {
|
||||||
|
result =
|
||||||
|
((precisionFactor * val + precisionFactor * step * rat) /
|
||||||
|
precisionFactor).toFixed(precision);
|
||||||
|
} else {
|
||||||
|
result = min === -Infinity ? step : min;
|
||||||
|
}
|
||||||
|
return this.toNumber(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
downStep(val, rat) {
|
||||||
|
const { step, min } = this.props;
|
||||||
|
const precisionFactor = this.getPrecisionFactor(val, rat);
|
||||||
|
const precision = Math.abs(this.getMaxPrecision(val, rat));
|
||||||
|
let result;
|
||||||
|
if (typeof val === 'number') {
|
||||||
|
result =
|
||||||
|
((precisionFactor * val - precisionFactor * step * rat) /
|
||||||
|
precisionFactor).toFixed(precision);
|
||||||
|
} else {
|
||||||
|
result = min === -Infinity ? -step : min;
|
||||||
|
}
|
||||||
|
return this.toNumber(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
step(type, e, ratio = 1, recursive) {
|
||||||
|
this.stop();
|
||||||
|
if (e) {
|
||||||
|
e.persist();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
const props = this.props;
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value = this.getCurrentValidValue(this.state.inputValue) || 0;
|
||||||
|
if (this.isNotCompleteNumber(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let val = this[`${type}Step`](value, ratio);
|
||||||
|
const outOfRange = val > props.max || val < props.min;
|
||||||
|
if (val > props.max) {
|
||||||
|
val = props.max;
|
||||||
|
} else if (val < props.min) {
|
||||||
|
val = props.min;
|
||||||
|
}
|
||||||
|
this.setValue(val);
|
||||||
|
this.setState({
|
||||||
|
focused: true,
|
||||||
|
});
|
||||||
|
if (outOfRange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.autoStepTimer = setTimeout(() => {
|
||||||
|
this[type](e, ratio, true);
|
||||||
|
}, recursive ? SPEED : DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop = () => {
|
||||||
|
if (this.autoStepTimer) {
|
||||||
|
clearTimeout(this.autoStepTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
down = (e, ratio, recursive) => {
|
||||||
|
this.pressingUpOrDown = true;
|
||||||
|
this.step('down', e, ratio, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
up = (e, ratio, recursive) => {
|
||||||
|
this.pressingUpOrDown = true;
|
||||||
|
this.step('up', e, ratio, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveInput = (node) => {
|
||||||
|
this.input = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const props = { ...this.props };
|
||||||
|
const { prefixCls, disabled, readOnly, useTouch } = props;
|
||||||
|
const classes = classNames({
|
||||||
|
[prefixCls]: true,
|
||||||
|
[props.className]: !!props.className,
|
||||||
|
[`${prefixCls}-disabled`]: disabled,
|
||||||
|
[`${prefixCls}-focused`]: this.state.focused,
|
||||||
|
});
|
||||||
|
let upDisabledClass = '';
|
||||||
|
let downDisabledClass = '';
|
||||||
|
const { value } = this.state;
|
||||||
|
if (value || value === 0) {
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
const val = Number(value);
|
||||||
|
if (val >= props.max) {
|
||||||
|
upDisabledClass = `${prefixCls}-handler-up-disabled`;
|
||||||
|
}
|
||||||
|
if (val <= props.min) {
|
||||||
|
downDisabledClass = `${prefixCls}-handler-down-disabled`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
upDisabledClass = `${prefixCls}-handler-up-disabled`;
|
||||||
|
downDisabledClass = `${prefixCls}-handler-down-disabled`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editable = !props.readOnly && !props.disabled;
|
||||||
|
|
||||||
|
// focus state, show input value
|
||||||
|
// unfocus state, show valid value
|
||||||
|
let inputDisplayValue;
|
||||||
|
if (this.state.focused) {
|
||||||
|
inputDisplayValue = this.state.inputValue;
|
||||||
|
} else {
|
||||||
|
inputDisplayValue = this.toPrecisionAsStep(this.state.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputDisplayValue === undefined || inputDisplayValue === null) {
|
||||||
|
inputDisplayValue = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let upEvents;
|
||||||
|
let downEvents;
|
||||||
|
if (useTouch) {
|
||||||
|
upEvents = {
|
||||||
|
onTouchStart: (editable && !upDisabledClass) ? this.up : noop,
|
||||||
|
onTouchEnd: this.stop,
|
||||||
|
};
|
||||||
|
downEvents = {
|
||||||
|
onTouchStart: (editable && !downDisabledClass) ? this.down : noop,
|
||||||
|
onTouchEnd: this.stop,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
upEvents = {
|
||||||
|
onMouseDown: (editable && !upDisabledClass) ? this.up : noop,
|
||||||
|
onMouseUp: this.stop,
|
||||||
|
onMouseLeave: this.stop,
|
||||||
|
};
|
||||||
|
downEvents = {
|
||||||
|
onMouseDown: (editable && !downDisabledClass) ? this.down : noop,
|
||||||
|
onMouseUp: this.stop,
|
||||||
|
onMouseLeave: this.stop,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const inputDisplayValueFormat = this.formatWrapper(inputDisplayValue);
|
||||||
|
const isUpDisabled = !!upDisabledClass || disabled || readOnly;
|
||||||
|
const isDownDisabled = !!downDisabledClass || disabled || readOnly;
|
||||||
|
// ref for test
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classes}
|
||||||
|
style={props.style}
|
||||||
|
onMouseEnter={props.onMouseEnter}
|
||||||
|
onMouseLeave={props.onMouseLeave}
|
||||||
|
onMouseOver={props.onMouseOver}
|
||||||
|
onMouseOut={props.onMouseOut}
|
||||||
|
>
|
||||||
|
<div className={`${prefixCls}-handler-wrap`}>
|
||||||
|
<InputHandler
|
||||||
|
ref="up"
|
||||||
|
disabled={isUpDisabled}
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
unselectable="unselectable"
|
||||||
|
{...upEvents}
|
||||||
|
role="button"
|
||||||
|
aria-label="Increase Value"
|
||||||
|
aria-disabled={!!isUpDisabled}
|
||||||
|
className={`${prefixCls}-handler ${prefixCls}-handler-up ${upDisabledClass}`}
|
||||||
|
>
|
||||||
|
{this.props.upHandler || <span
|
||||||
|
unselectable="unselectable"
|
||||||
|
className={`${prefixCls}-handler-up-inner`}
|
||||||
|
onClick={preventDefault}
|
||||||
|
/>}
|
||||||
|
</InputHandler>
|
||||||
|
<InputHandler
|
||||||
|
ref="down"
|
||||||
|
disabled={isDownDisabled}
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
unselectable="unselectable"
|
||||||
|
{...downEvents}
|
||||||
|
role="button"
|
||||||
|
aria-label="Decrease Value"
|
||||||
|
aria-disabled={!!isDownDisabled}
|
||||||
|
className={`${prefixCls}-handler ${prefixCls}-handler-down ${downDisabledClass}`}
|
||||||
|
>
|
||||||
|
{this.props.downHandler || <span
|
||||||
|
unselectable="unselectable"
|
||||||
|
className={`${prefixCls}-handler-down-inner`}
|
||||||
|
onClick={preventDefault}
|
||||||
|
/>}
|
||||||
|
</InputHandler>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`${prefixCls}-input-wrap`}
|
||||||
|
role="spinbutton"
|
||||||
|
aria-valuemin={props.min}
|
||||||
|
aria-valuemax={props.max}
|
||||||
|
aria-valuenow={value}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
required={props.required}
|
||||||
|
type={props.type}
|
||||||
|
placeholder={props.placeholder}
|
||||||
|
onClick={props.onClick}
|
||||||
|
className={`${prefixCls}-input`}
|
||||||
|
tabIndex={props.tabIndex}
|
||||||
|
autoComplete="off"
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
|
onKeyDown={editable ? this.onKeyDown : noop}
|
||||||
|
onKeyUp={editable ? this.onKeyUp : noop}
|
||||||
|
autoFocus={props.autoFocus}
|
||||||
|
maxLength={props.maxLength}
|
||||||
|
readOnly={props.readOnly}
|
||||||
|
disabled={props.disabled}
|
||||||
|
max={props.max}
|
||||||
|
min={props.min}
|
||||||
|
step={props.step}
|
||||||
|
name={props.name}
|
||||||
|
id={props.id}
|
||||||
|
onChange={this.onChange}
|
||||||
|
ref={this.saveInput}
|
||||||
|
value={inputDisplayValueFormat}
|
||||||
|
pattern={props.pattern}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import TouchFeedback from '../index'
|
||||||
|
import './simple.less'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<TouchFeedback activeClassName='active' activeStyle={{ color: 'red' }}>
|
||||||
|
<div class='normal' style={{
|
||||||
|
backgroundColor: 'yellow',
|
||||||
|
}}
|
||||||
|
onClick={() => console.log('click div')}>click to active</div>
|
||||||
|
</TouchFeedback>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.normal {
|
||||||
|
color: '#000'
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
import TouchFeedback from './src/TouchFeedback'
|
||||||
|
export default TouchFeedback
|
|
@ -0,0 +1,13 @@
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
|
||||||
|
export const ITouchProps = {
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
activeClassName: PropTypes.string,
|
||||||
|
activeStyle: PropTypes.any,
|
||||||
|
// onTouchStart: PropTypes.func,
|
||||||
|
// onTouchEnd: PropTypes.func,
|
||||||
|
// onTouchCancel: PropTypes.func,
|
||||||
|
// onMouseDown: PropTypes.func,
|
||||||
|
// onMouseUp: PropTypes.func,
|
||||||
|
// onMouseLeave: PropTypes.func,
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { initDefaultProps } from '../../_util/props-util'
|
||||||
|
import { cloneElement } from '../../_util/vnode'
|
||||||
|
import warning from '../../_util/warning'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import { ITouchProps } from './PropTypes'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TouchFeedback',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: initDefaultProps(ITouchProps, {
|
||||||
|
disabled: false,
|
||||||
|
}),
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
active: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.disabled && this.active) {
|
||||||
|
this.setState({
|
||||||
|
active: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
triggerEvent (type, isActive, ev) {
|
||||||
|
// const eventType = `on${type}`
|
||||||
|
// if (this.props[eventType]) {
|
||||||
|
// this.props[eventType](ev)
|
||||||
|
// }
|
||||||
|
this.$emit(type, ev)
|
||||||
|
if (isActive !== this.active) {
|
||||||
|
this.setState({
|
||||||
|
active: isActive,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTouchStart (e) {
|
||||||
|
this.triggerEvent('touchstart', true, e)
|
||||||
|
},
|
||||||
|
onTouchMove (e) {
|
||||||
|
this.triggerEvent('touchmove', false, e)
|
||||||
|
},
|
||||||
|
onTouchEnd (e) {
|
||||||
|
this.triggerEvent('touchend', false, e)
|
||||||
|
},
|
||||||
|
onTouchCancel (e) {
|
||||||
|
this.triggerEvent('touchcancel', false, e)
|
||||||
|
},
|
||||||
|
onMouseDown (e) {
|
||||||
|
// todo
|
||||||
|
// pc simulate mobile
|
||||||
|
// if (this.props.onTouchStart) {
|
||||||
|
this.triggerEvent('touchstart', true, e)
|
||||||
|
// }
|
||||||
|
this.triggerEvent('mousedown', true, e)
|
||||||
|
},
|
||||||
|
onMouseUp (e) {
|
||||||
|
// if (this.props.onTouchEnd) {
|
||||||
|
this.triggerEvent('touchend', false, e)
|
||||||
|
// }
|
||||||
|
this.triggerEvent('mouseup', false, e)
|
||||||
|
},
|
||||||
|
onMouseLeave (e) {
|
||||||
|
this.triggerEvent('mouseleave', false, e)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const { disabled, activeClassName = '', activeStyle = {}} = this.$props
|
||||||
|
|
||||||
|
const child = this.$slots.default
|
||||||
|
if (child.length !== 1) {
|
||||||
|
warning(false, '只能包含一个子元素')
|
||||||
|
}
|
||||||
|
let childProps = {
|
||||||
|
on: disabled ? {} : {
|
||||||
|
touchstart: this.onTouchStart,
|
||||||
|
touchmove: this.onTouchMove,
|
||||||
|
touchend: this.onTouchEnd,
|
||||||
|
touchcancel: this.onTouchCancel,
|
||||||
|
mousedown: this.onMouseDown,
|
||||||
|
mouseup: this.onMouseUp,
|
||||||
|
mouseleave: this.onMouseLeave,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disabled && this.active) {
|
||||||
|
childProps = { ...childProps, ...{
|
||||||
|
style: activeStyle,
|
||||||
|
class: activeClassName,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloneElement(child, childProps)
|
||||||
|
},
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ const AsyncComp = () => {
|
||||||
const hashs = window.location.hash.split('/')
|
const hashs = window.location.hash.split('/')
|
||||||
const d = hashs[hashs.length - 1]
|
const d = hashs[hashs.length - 1]
|
||||||
return {
|
return {
|
||||||
component: import(`../components/grid/demo/${d}`),
|
component: import(`../components/vc-m-feedback/demo/${d}`),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default [
|
export default [
|
||||||
|
|
Loading…
Reference in New Issue