diff --git a/Example/App.js b/Example/App.js index 387a63a1..d1436e82 100644 --- a/Example/App.js +++ b/Example/App.js @@ -60,7 +60,27 @@ export default class App extends Component { rightButtonBackgroundColor="#18c2ef" leftButtonBackgroundColor="#ff8080" /> + + Numeric Input Examples with four Buttons + { this.setState({ value1 }); console.log(this.state.value1); }} + onLimitReached={(isMin, msg) => console.log(isMin, msg)} + totalWidth={80} + totalHeight={30} + iconSize={10} + step={1} + minValue={0} + valueType="real" + fourButton + rounded + textColor="#B0228C" + iconStyle={{ color: "white" }} + rightButtonBackgroundColor="#18c2ef" + leftButtonBackgroundColor="#ff8080" + /> + { this.setState({ v1 }); console.log(v1) }} diff --git a/NumericInput/NumericInput.js b/NumericInput/NumericInput.js index 29255483..e4f74823 100644 --- a/NumericInput/NumericInput.js +++ b/NumericInput/NumericInput.js @@ -20,9 +20,14 @@ export default class NumericInput extends Component { } // this.props refers to the new props - componentDidUpdate() { + componentDidUpdate(prevProps) { const initSent = !(this.props.initValue !== 0 && !this.props.initValue); - + if (prevProps.value !== this.props.value) { + this.setState({ + value: this.props.value, + stringValue: this.props.value.toString(), + }); + } // compare the new value (props.initValue) with the existing/old one (this.state.value) if (this.props.initValue !== this.state.value && initSent) { this.setState({ @@ -36,6 +41,36 @@ export default class NumericInput extends Component { updateBaseResolution = (width, height) => { calcSize = create({ width, height }) } + startInc = () => { + this.inc(); + this.incInterval = setInterval(this.inc, 200); + } + stopInc = () => { + clearInterval(this.incInterval); + } + + startDec = () => { + this.dec(); + this.decInterval = setInterval(this.dec, 200); + } + stopDec = () => { + clearInterval(this.decInterval); + } + startInc10 = () => { + this.inc10(); + this.inc10Interval = setInterval(this.inc10, 200); + } + stopInc10 = () => { + clearInterval(this.inc10Interval); + } + + startDec10 = () => { + this.dec10(); + this.dec10Interval = setInterval(this.dec10, 200); + } + stopDec10 = () => { + clearInterval(this.dec10Interval); + } inc = () => { let value = this.props.value && (typeof this.props.value === 'number') ? this.props.value : this.state.value if (this.props.maxValue === null || (value + this.props.step < this.props.maxValue)) { @@ -51,6 +86,21 @@ export default class NumericInput extends Component { if (value !== this.props.value) this.props.onChange && this.props.onChange(Number(value)) } + inc10 = () => { + let value = this.props.value && (typeof this.props.value === 'number') ? this.props.value : this.state.value + if (this.props.maxValue === null || (value + this.props.step * 10 < this.props.maxValue)) { + value = (value + this.props.step * 10).toFixed(12) + value = this.props.valueType === 'real' ? parseFloat(value) : parseInt(value) + this.setState({ value, stringValue: value.toString() }) + } else if (this.props.maxValue !== null) { + this.props.onLimitReached(true, 'Reached Maximum Value!') + value = this.props.maxValue + this.setState({ value, stringValue: value.toString() }) + + } + if (value !== this.props.value) + this.props.onChange && this.props.onChange(Number(value)) + } dec = () => { let value = this.props.value && (typeof this.props.value === 'number') ? this.props.value : this.state.value if (this.props.minValue === null || (value - this.props.step > this.props.minValue)) { @@ -64,6 +114,19 @@ export default class NumericInput extends Component { this.props.onChange && this.props.onChange(Number(value)) this.setState({ value, stringValue: value.toString() }) } + dec10 = () => { + let value = this.props.value && (typeof this.props.value === 'number') ? this.props.value : this.state.value + if (this.props.minValue === null || (value - this.props.step * 10> this.props.minValue)) { + value = (value - this.props.step * 10).toFixed(12) + value = this.props.valueType === 'real' ? parseFloat(value) : parseInt(value) + } else if (this.props.minValue !== null) { + this.props.onLimitReached(false, 'Reached Minimum Value!') + value = this.props.minValue + } + if (value !== this.props.value) + this.props.onChange && this.props.onChange(Number(value)) + this.setState({ value, stringValue: value.toString() }) + } isLegalValue = (value, mReal, mInt) => value === '' || (((this.props.valueType === 'real' && mReal(value)) || (this.props.valueType !== 'real' && mInt(value))) && (this.props.maxValue === null || (parseFloat(value) <= this.props.maxValue)) && (this.props.minValue === null || (parseFloat(value) >= this.props.minValue))) realMatch = (value) => value && value.match(/-?\d+(\.(\d+)?)?/) && value.match(/-?\d+(\.(\d+)?)?/)[0] === value.match(/-?\d+(\.(\d+)?)?/).input @@ -156,36 +219,77 @@ export default class NumericInput extends Component { } render() { + const step = this.props.step const editable = this.props.editable + const fourButton = this.props.fourButton const sepratorWidth = (typeof this.props.separatorWidth === 'undefined') ? this.props.sepratorWidth : this.props.separatorWidth;//supporting old property name sepratorWidth const borderColor = this.props.borderColor const iconStyle = [style.icon, this.props.iconStyle] const totalWidth = this.props.totalWidth const totalHeight = this.props.totalHeight ? this.props.totalHeight : (totalWidth * 0.4) - const inputWidth = this.props.type === 'up-down' ? (totalWidth * 0.6) : (totalWidth * 0.4) - const borderRadiusTotal = totalHeight * 0.18 - const fontSize = totalHeight * 0.38 + const inputWidth = this.props.type === 'up-down' ? (totalWidth * 0.6) : (fourButton ? totalWidth * 0.3 : totalWidth * 0.4) + const borderRadiusTotal = totalHeight * 0.1 + const fontSize = totalHeight * 0.45 const textColor = this.props.textColor + const lableTextColor = this.props.lableTextColor const maxReached = this.state.value === this.props.maxValue const minReached = this.state.value === this.props.minValue const inputContainerStyle = this.props.type === 'up-down' ? [style.inputContainerUpDown, { width: totalWidth, height: totalHeight, borderColor: borderColor }, this.props.rounded ? { borderRadius: borderRadiusTotal } : {}, this.props.containerStyle] : - [style.inputContainerPlusMinus, { width: totalWidth, height: totalHeight, borderColor: borderColor }, this.props.rounded ? { borderRadius: borderRadiusTotal } : {}, this.props.containerStyle] + [style.inputContainerPlusMinus, { width: totalWidth + 24, height: totalHeight+10, borderColor: 'white' }, this.props.rounded ? { borderRadius: borderRadiusTotal } : {}, this.props.containerStyle] + const lableStyle = [style.lableTextStyle, { color: lableTextColor }] + const lable2Style = [style.lableTextStyle, { color: lableTextColor, fontSize: fontSize * 0.7}] const inputStyle = this.props.type === 'up-down' ? [style.inputUpDown, { width: inputWidth, height: totalHeight, fontSize: fontSize, color: textColor, borderRightWidth: 2, borderRightColor: borderColor }, this.props.inputStyle] : - [style.inputPlusMinus, { width: inputWidth, height: totalHeight, fontSize: fontSize, color: textColor, borderRightWidth: sepratorWidth, borderLeftWidth: sepratorWidth, borderLeftColor: borderColor, borderRightColor: borderColor }, this.props.inputStyle] + [style.inputPlusMinus, { width: inputWidth, height: totalHeight+10, fontSize: fontSize+2, color: textColor, borderRightWidth: sepratorWidth, borderLeftWidth: sepratorWidth, borderLeftColor: borderColor, borderRightColor: borderColor, borderColor:borderColor, borderWidth: 2, borderRadius: borderRadiusTotal }, this.props.inputStyle] const upDownStyle = [{ alignItems: 'center', width: totalWidth - inputWidth, backgroundColor: this.props.upDownButtonsBackgroundColor, borderRightWidth: 1, borderRightColor: borderColor }, this.props.rounded ? { borderTopRightRadius: borderRadiusTotal, borderBottomRightRadius: borderRadiusTotal } : {}] const rightButtonStyle = [ { - position: 'absolute', + // position: 'absolute', zIndex: -1, right: 0, - height: totalHeight - 2, + height: totalHeight + 1, + flexDirection: 'row', justifyContent: 'center', alignItems: 'center', borderWidth: 0, + borderBottomWidth: 0.5, backgroundColor: this.props.rightButtonBackgroundColor, - width: (totalWidth - inputWidth) / 2 + width: fourButton ? (totalWidth - inputWidth + 20) / 4 : (totalWidth - inputWidth + 20) / 2, + }, + this.props.rounded ? + { borderTopLeftRadius: borderRadiusTotal, borderBottomLeftRadius: borderRadiusTotal } + : {}] + const rightButton10Style = [ + { + // position: 'absolute', + zIndex: -1, + right: 0, + height: totalHeight - 7, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + borderWidth: 0, + borderBottomWidth: 0.5, + backgroundColor: this.props.rightButton10BackgroundColor, + width: fourButton ? (totalWidth - inputWidth + 20) / 4 : (totalWidth - inputWidth + 20) / 2, + }, + this.props.rounded ? + { borderTopLeftRadius: borderRadiusTotal, borderBottomLeftRadius: borderRadiusTotal } + : {}] + const leftButtonStyle = [ + { + // position: 'absolute', + zIndex: -1, + left: 0, + height: totalHeight + 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: this.props.leftButtonBackgroundColor, + width: fourButton ? (totalWidth - inputWidth + 20) / 4 : (totalWidth - inputWidth + 20) / 2, + borderBottomWidth: 0.5, + borderWidth: 0 }, this.props.rounded ? { @@ -193,32 +297,38 @@ export default class NumericInput extends Component { borderBottomRightRadius: borderRadiusTotal } : {}] - const leftButtonStyle = [ + const leftButton10Style = [ { - position: 'absolute', + // position: 'absolute', zIndex: -1, left: 0, - height: totalHeight - 2, + height: totalHeight - 7, + flexDirection: 'row', justifyContent: 'center', alignItems: 'center', - backgroundColor: this.props.leftButtonBackgroundColor, - width: (totalWidth - inputWidth) / 2, + color: this.props.textColor, + backgroundColor: this.props.leftButton10BackgroundColor, + borderBottomWidth: 0.5, + width: fourButton ? (totalWidth - inputWidth + 20) / 4 : (totalWidth - inputWidth + 20) / 2, borderWidth: 0 }, this.props.rounded ? - { borderTopLeftRadius: borderRadiusTotal, borderBottomLeftRadius: borderRadiusTotal } + { + borderTopRightRadius: borderRadiusTotal, + borderBottomRightRadius: borderRadiusTotal + } : {}] const inputWraperStyle = { alignSelf: 'center', - borderLeftColor: borderColor, - borderLeftWidth: sepratorWidth, - borderRightWidth: sepratorWidth, - borderRightColor: borderColor + // borderLeftColor: borderColor, + // borderLeftWidth: sepratorWidth, + // borderRightWidth: sepratorWidth, + // borderRightColor: borderColor } if (this.props.type === 'up-down') return ( - this.ref = ref} onBlur={this.onBlur} onFocus={this.onFocus} /> + this.ref = ref} onBlur={this.onBlur} onFocus={this.onFocus} /> + : null + } + - this.ref = ref} onBlur={this.onBlur} onFocus={this.onFocus} /> + this.ref = ref} onBlur={this.onBlur} onFocus={this.onFocus} /> - + {fourButton ? + + : null + } ) @@ -250,6 +376,9 @@ const style = StyleSheet.create({ backgroundColor: 'grey', height: calcSize(80), }, + lableTextStyle: { + fontFamily: 'BYekan', + }, inputContainerUpDown: { flexDirection: 'row', alignItems: 'center', @@ -291,8 +420,10 @@ NumericInput.propTypes = { sepratorWidth: PropTypes.number, type: PropTypes.oneOf(['up-down', 'plus-minus']), valueType: PropTypes.oneOf(['real', 'integer']), + fourButton: PropTypes.any, rounded: PropTypes.any, textColor: PropTypes.string, + lableTextColor: PropTypes.string, containerStyle: PropTypes.any, inputStyle: PropTypes.any, initValue: PropTypes.number, @@ -319,8 +450,10 @@ NumericInput.defaultProps = { totalWidth: calcSize(220), sepratorWidth: 1, type: 'plus-minus', + fourButton: false, rounded: false, - textColor: 'black', + textColor: '#B0228C', + lableTextColor: '#400010', containerStyle: {}, inputStyle: {}, initValue: null, @@ -330,8 +463,10 @@ NumericInput.defaultProps = { maxValue: null, step: 1, upDownButtonsBackgroundColor: 'white', - rightButtonBackgroundColor: 'white', - leftButtonBackgroundColor: 'white', + rightButtonBackgroundColor: '#99b3ff', + leftButtonBackgroundColor: '#ffb299', + rightButton10BackgroundColor: '#bfcfff', + leftButton10BackgroundColor: '#ffcfbf', editable: true, validateOnBlur: true, reachMaxIncIconStyle: {}, diff --git a/README.md b/README.md index 09390e11..bb0309e9 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ import NumericInput from 'react-native-numeric-input' step={1.5} valueType='real' rounded + fourButton textColor='#B0228C' iconStyle={{ color: 'white' }} rightButtonBackgroundColor='#EA3788' @@ -95,21 +96,24 @@ Name | Type | Defa **minValue** |`number` | none **maxValue** |`number` | none **step** |`number` | 1 -**valueType** |`'integer'` or `'real'` | `'integer'` +**valueType** |`'integer'` or `'real'` | `'integer'` **initValue** |`number` | null if not used will start at 0 **iconSize** |`number` | calcSize(30) **borderColor** |`string` | `'#d4d4d4'` **iconStyle** |`object` | none **totalWidth** |`number` | calcSize(220) -**separatorWidth** |`number` | 1 +**separatorWidth** |`number` | 1 **type** |`'plus-minus'` or `'up-down'` | `'plus-minus'` **rounded** |`boolean` | false +**fourButton** |`boolean` | false **textColor** |`string` | `'black'` **containerStyle** |`object` | none **inputStyle** |`object` | none **upDownButtonsBackgroundColor** |`string` | `'white'` **rightButtonBackgroundColor** |`string` | `'white'` **leftButtonBackgroundColor** |`string` | `'white'` +**rightButton10BackgroundColor** |`string` | `'white'` +**leftButton10BackgroundColor** |`string` | `'white'` **totalHeight** |`number` | none **onChange** |`function` | none - required prop **onLimitReached** |`function` | none (empty function) @@ -128,6 +132,7 @@ Name | Type | Defa * **totalWidth prop** - this prop is for the entire component width, and all other sizes are derived from it , unless given other size props * **initValue prop** - if using value prop, this is not needed and the initial value can be given by the value prop * **validateOnBlur** - added on version 1.3.2, if set to false the text input will validate while typing, not recommended, so just keep it true unless there is a good reason not to use the default functionallity +* **fourButton** - used to activate two new buttons to increment and decrement ×10 steps - **optional** * **reachMaxIncIconStyle** - added on version 1.4.0, used to set style to the increment button icon in case maxValue is reached - **optional** * **reachMaxDecIconStyle** - added on version 1.4.0, used to set style to the decrement button icon in case maxValue is reached - **optional** * **reachMinIncIconStyle** - added on version 1.4.0, used to set style to the increment button icon in case minValue is reached - **optional**