react-numeric-input
Advanced tools
| /* global describe, it */ | ||
| import expect from 'expect' | ||
| import NumericInput from '../src/NumericInput.jsx' | ||
| import React from 'react' | ||
| import TestUtils from 'react-addons-test-utils' | ||
| describe('NumericInput', function() { | ||
| it('passes focus events to "onFocus" prop', (done) => { | ||
| let onFocusCalls = 0, widget | ||
| function onFocus(event) { | ||
| onFocusCalls += 1 | ||
| expect(this).toEqual(null) | ||
| expect(typeof event).toEqual("object") | ||
| } | ||
| widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onFocus={ onFocus } /> | ||
| ) | ||
| // Rendering must not bring the focus to the input | ||
| expect(onFocusCalls).toEqual(0) | ||
| // Trigger a focus on the input and assert that | ||
| // the onFocus callback receives it | ||
| TestUtils.Simulate.focus(widget.refs.input) | ||
| setTimeout(() => { | ||
| expect(onFocusCalls).toEqual(1) | ||
| // Now blur and then click on the up button. That should return | ||
| // the focus back to the input and call the onFocus callback again | ||
| TestUtils.Simulate.blur(widget.refs.input) | ||
| setTimeout(() => { | ||
| TestUtils.Simulate.mouseDown(widget.refs.input.nextElementSibling) | ||
| setTimeout(() => { | ||
| expect(onFocusCalls).toEqual(2) | ||
| done() | ||
| }, 50) | ||
| }, 50) | ||
| }, 50) | ||
| }); | ||
| it('passes blur events to "onBlur" prop', (done) => { | ||
| let onBlurCalls = 0 | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onBlur={ function(event) { | ||
| onBlurCalls += 1 | ||
| expect(this).toEqual(null) | ||
| expect(typeof event).toEqual("object") | ||
| }} /> | ||
| ) | ||
| // Rendering must not trigger any blur on the input | ||
| expect(onBlurCalls).toEqual(0) | ||
| // Start by focusing the input | ||
| TestUtils.Simulate.focus(widget.refs.input) | ||
| // Test again to see if after focus the input didn't blur somehow | ||
| setTimeout(() => { | ||
| expect(onBlurCalls).toEqual(0) | ||
| // Trigger a blur on the input and assert that | ||
| // the onBlur callback receives it | ||
| TestUtils.Simulate.blur(widget.refs.input) | ||
| setTimeout(() => { | ||
| expect(onBlurCalls).toEqual(1) | ||
| // Hit the up button. This should bring the focus back to the | ||
| // input | ||
| TestUtils.Simulate.mouseDown(widget.refs.input.nextElementSibling) | ||
| setTimeout(() => { | ||
| // Now blur it again and see if it counts | ||
| TestUtils.Simulate.blur(widget.refs.input) | ||
| setTimeout(() => { | ||
| expect(onBlurCalls).toEqual(2) | ||
| done() | ||
| }, 50) | ||
| }, 50) | ||
| }, 50) | ||
| }, 50) | ||
| }); | ||
| it('respects the "autoFocus" prop', (done) => { | ||
| let onFocusCalls = 0 | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput autoFocus onFocus={ () => onFocusCalls += 1 } /> | ||
| ) | ||
| // window.blur() | ||
| // window.focus() | ||
| // Rendering must bring the focus to the input | ||
| setTimeout(() => { | ||
| if (document.activeElement === widget.refs.input) { | ||
| expect(onFocusCalls).toEqual(1) | ||
| done() | ||
| } | ||
| else { | ||
| console.log("Unable to test autoFocus") | ||
| done(/*new Error("Unable to autoFocus")*/) | ||
| } | ||
| }, 1000) | ||
| }) | ||
| }); |
| /* global describe, it */ | ||
| import expect from 'expect' | ||
| import NumericInput from '../src/NumericInput.jsx' | ||
| import React from 'react' | ||
| import TestUtils from 'react-addons-test-utils' | ||
| const KEYCODE_UP = 38; | ||
| const KEYCODE_DOWN = 40; | ||
| describe('NumericInput', function() { | ||
| it('calls onChange when user types in something valid', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| // Press "5" | ||
| widget.refs.input.value = "5" | ||
| TestUtils.Simulate.change(widget.refs.input) | ||
| // Rendering must bring the focus to the input | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(5) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('does not call onChange when user types in something invalid', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| // Press "r" | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { keyCode: 114 }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(0) | ||
| expect(lastChange).toEqual(undefined) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the up arrow key', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { keyCode: KEYCODE_UP }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the Ctrl+up', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } precision={2} /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { | ||
| keyCode: KEYCODE_UP, | ||
| ctrlKey: true | ||
| }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(0.1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the Command+up', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } precision={2} /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { | ||
| keyCode: KEYCODE_UP, | ||
| metaKey: true | ||
| }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(0.1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the Shift+up', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } precision={2} /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { | ||
| keyCode : KEYCODE_UP, | ||
| shiftKey: true | ||
| }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(10) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the down arrow key', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { keyCode: KEYCODE_DOWN }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(-1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the Ctrl+down', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } precision={2} /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { | ||
| keyCode: KEYCODE_DOWN, | ||
| ctrlKey: true | ||
| }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(-0.1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the Command+down', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } precision={2} /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { | ||
| keyCode: KEYCODE_DOWN, | ||
| metaKey: true | ||
| }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(-0.1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user the Shift+down', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } precision={2} /> | ||
| ) | ||
| TestUtils.Simulate.keyDown(widget.refs.input, { | ||
| keyCode : KEYCODE_DOWN, | ||
| shiftKey: true | ||
| }) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(-10) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user hits the up button', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| // Press "5" | ||
| TestUtils.Simulate.mouseDown(widget.refs.input.nextElementSibling) | ||
| // Rendering must bring the focus to the input | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange when user hits the down button', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| // Press "5" | ||
| TestUtils.Simulate.mouseDown(widget.refs.input.nextElementSibling.nextElementSibling) | ||
| // Rendering must bring the focus to the input | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(1) | ||
| expect(lastChange).toEqual(-1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('calls onChange due to auto-increase', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| TestUtils.Simulate.mouseDown(widget.refs.input.nextElementSibling) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(2) | ||
| expect(lastChange).toEqual(2) | ||
| done() | ||
| }, NumericInput.DELAY + Math.round(NumericInput.SPEED/2)) | ||
| }) | ||
| it('calls onChange due to auto-decrease', (done) => { | ||
| let onChangeCalls = 0 | ||
| let lastChange | ||
| function onChange(x) { | ||
| onChangeCalls += 1 | ||
| lastChange = x | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onChange={ onChange } /> | ||
| ) | ||
| TestUtils.Simulate.mouseDown(widget.refs.input.nextElementSibling.nextElementSibling) | ||
| setTimeout(() => { | ||
| expect(onChangeCalls).toEqual(2) | ||
| expect(lastChange).toEqual(-2) | ||
| done() | ||
| }, NumericInput.DELAY + Math.round(NumericInput.SPEED/2)) | ||
| }) | ||
| }) |
| /* global React, describe, it */ | ||
| import expect from 'expect' | ||
| import NumericInput from '../src/NumericInput.jsx' | ||
| import React from 'react' | ||
| import TestUtils from 'react-addons-test-utils' | ||
| function testProp(cfg) { | ||
| return Promise.all(cfg.map.map(entry => { | ||
| return new Promise(resolve => { | ||
| let props = { [cfg.propName] : entry.in } | ||
| let app = TestUtils.renderIntoDocument(<NumericInput {...props} />) | ||
| let result = cfg.getValue(app) | ||
| expect(result).toEqual( | ||
| entry.out, | ||
| `If the "${cfg.propName}" is set to ${entry.in} the result | ||
| should be "${entry.out}" but got "${result}"` | ||
| ) | ||
| resolve() | ||
| }) | ||
| })) | ||
| } | ||
| function testInputProp(cfg) { | ||
| cfg.getValue = cfg.getValue || (widget => widget.refs.input[cfg.propName]) | ||
| return testProp(cfg).then(() => { | ||
| let app = TestUtils.renderIntoDocument(<NumericInput />) | ||
| let attrName = cfg.attrName || cfg.propName.toLowerCase() | ||
| if (attrName == "pattern") { | ||
| return new Promise(resolve => { | ||
| expect(app.refs.input.outerHTML.search( | ||
| new RegExp('\\bpattern="\\.\\*"') | ||
| )).toNotEqual( | ||
| -1, | ||
| `If the "pattern" is not set the corresponding | ||
| attribute should be set to ".*". The outerHTML was | ||
| ${app.refs.input.outerHTML}.` | ||
| ) | ||
| resolve() | ||
| }) | ||
| } | ||
| return new Promise(resolve => { | ||
| expect(app.refs.input.outerHTML.search( | ||
| new RegExp("\\b" + attrName + "=", "i") | ||
| )).toEqual( | ||
| -1, | ||
| `If the "${cfg.propName}" is not set the corresponding | ||
| attribute should not be set. The outerHTML was | ||
| ${app.refs.input.outerHTML}.` | ||
| ) | ||
| resolve() | ||
| }) | ||
| }) | ||
| } | ||
| function testAllInputProps(cfgArr) { | ||
| return Promise.all(cfgArr.map(testInputProp)) | ||
| } | ||
| describe('NumericInput', () => { | ||
| it('handles properly all the props', done => { | ||
| // These are all attached on the input | ||
| testAllInputProps([ | ||
| { | ||
| propName: "name", | ||
| map: [ | ||
| { in: "" , out: "" }, | ||
| { in: "a" , out: "a" }, | ||
| { in: "b" , out: "b" }, | ||
| { in: null , out: "" }, | ||
| { in: undefined, out: "" }, | ||
| { in: 123 , out: "123" } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "disabled", | ||
| map: [ | ||
| { in: null , out: false }, | ||
| { in: undefined, out: false }, | ||
| { in: true , out: true }, | ||
| { in: false , out: false } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "required", | ||
| map: [ | ||
| { in: null , out: false }, | ||
| { in: undefined, out: false }, | ||
| { in: true , out: true }, | ||
| { in: false , out: false } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "maxLength", | ||
| map: [ | ||
| // { in: null , out: 524288 }, | ||
| // { in: undefined, out: 524288 }, | ||
| // { in: 0 , out: 524288 }, | ||
| { in: 1 , out: 1 }, | ||
| { in: 2 , out: 2 } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "className", | ||
| attrName: "class", | ||
| map: [ | ||
| { in: "" , out: "" }, | ||
| { in: "a" , out: "a" }, | ||
| { in: "b" , out: "b" }, | ||
| { in: null , out: "" }, | ||
| { in: undefined, out: "" } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "readOnly", | ||
| map: [ | ||
| { in: null , out: false }, | ||
| { in: undefined, out: false }, | ||
| { in: true , out: true }, | ||
| { in: false , out: false } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "noValidate", | ||
| map: [ | ||
| { in: "novalidate", out: true }, | ||
| { in: "whatever-string", out: true }, | ||
| { in: null , out: false }, | ||
| { in: undefined, out: false }, | ||
| { in: true , out: true }, | ||
| { in: false , out: false } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "pattern", | ||
| map: [ | ||
| { in: "" , out: ".*" }, | ||
| { in: "a" , out: "a" }, | ||
| { in: "b" , out: "b" }, | ||
| { in: null , out: ".*" }, | ||
| { in: undefined, out: ".*" } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "title", | ||
| map: [ | ||
| { in: "" , out: "" }, | ||
| { in: "a" , out: "a" }, | ||
| { in: "b" , out: "b" }, | ||
| { in: null , out: "" }, | ||
| { in: undefined, out: "" }, | ||
| { in: 123 , out: "123" } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "size", | ||
| map: [ | ||
| // { in: null , out: null }, | ||
| // { in: undefined, out: null }, | ||
| // { in: 0 , out: 0 }, | ||
| { in: 1 , out: 1 }, | ||
| { in: 2 , out: 2 } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "value", | ||
| map: [ | ||
| { in: "" , out: "" }, | ||
| { in: "a" , out: "0" }, | ||
| { in: "b" , out: "0" }, | ||
| { in: null , out: "" }, | ||
| { in: undefined, out: "" }, | ||
| { in: 123 , out: "123" } | ||
| ] | ||
| }, | ||
| { | ||
| propName: "defaultValue", | ||
| // attrName: "value", | ||
| getValue : (widget) => widget.refs.input.value, | ||
| map: [ | ||
| { in: "" , out: "" }, | ||
| { in: "a" , out: "0" }, | ||
| { in: "b" , out: "0" }, | ||
| { in: null , out: "" }, | ||
| { in: undefined, out: "" }, | ||
| { in: 123 , out: "123" } | ||
| ] | ||
| }//, | ||
| // { | ||
| // propName: "step", | ||
| // map: [ | ||
| // { in: null , out: null }, | ||
| // { in: undefined, out: null }, | ||
| // { in: 0 , out: 0 }, | ||
| // { in: 1 , out: 1 }, | ||
| // { in: 2 , out: 2 } | ||
| // ] | ||
| // }, | ||
| // { | ||
| // propName: "min", | ||
| // map: [ | ||
| // { in: null , out: 524288 }, | ||
| // { in: undefined, out: 524288 }, | ||
| // { in: 0 , out: 524288 }, | ||
| // { in: 1 , out: 1 }, | ||
| // { in: 2 , out: 2 } | ||
| // ] | ||
| // }, | ||
| // { | ||
| // propName: "max", | ||
| // map: [ | ||
| // { in: null , out: 524288 }, | ||
| // { in: undefined, out: 524288 }, | ||
| // { in: 0 , out: 524288 }, | ||
| // { in: 1 , out: 1 }, | ||
| // { in: 2 , out: 2 } | ||
| // ] | ||
| // }, | ||
| // { | ||
| // propName: "precision", | ||
| // map: [ | ||
| // { in: null , out: 524288 }, | ||
| // { in: undefined, out: 524288 }, | ||
| // { in: 0 , out: 524288 }, | ||
| // { in: 1 , out: 1 }, | ||
| // { in: 2 , out: 2 } | ||
| // ] | ||
| // } | ||
| ]) | ||
| // TODO: Test props which are not passed to the input | ||
| .then(() => {}) | ||
| .then(() => done()) | ||
| .catch(done) | ||
| }) | ||
| }) |
| /* global describe, it */ | ||
| import expect from 'expect' | ||
| import NumericInput from '../src/NumericInput.jsx' | ||
| import React from 'react' | ||
| import TestUtils from 'react-addons-test-utils' | ||
| const INVALID_CLASS = "has-error" | ||
| class Wrapper extends React.Component | ||
| { | ||
| constructor(...args) | ||
| { | ||
| super(...args) | ||
| this.state = this.props | ||
| } | ||
| render() | ||
| { | ||
| return ( | ||
| <NumericInput { ...this.state } ref="NumericInput" /> | ||
| ) | ||
| } | ||
| } | ||
| describe('NumericInput', function() { | ||
| it('respects the noValidate prop', function(done) { | ||
| let onInvalidCalled = false | ||
| function testInvalidEvent() { | ||
| onInvalidCalled = true | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput noValidate required onInvalid={ testInvalidEvent } /> | ||
| ) | ||
| window.setTimeout(function() { | ||
| expect( | ||
| onInvalidCalled, | ||
| "Must not call the onInvalid callback" | ||
| ).toEqual(false) | ||
| expect( | ||
| widget.refs.wrapper.className.indexOf(INVALID_CLASS), | ||
| "Must not have the '" + INVALID_CLASS + "' class" | ||
| ).toEqual(-1) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('renders as invalid if it is both empty and required', (done) => { | ||
| let onInvalidCalled = false | ||
| function testInvalidEvent() { | ||
| onInvalidCalled = true | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput required onInvalid={ testInvalidEvent }/> | ||
| ) | ||
| setTimeout(() => { | ||
| expect(widget.refs.input.required).toEqual(true, "Must be required") | ||
| expect(widget.refs.input.validity.valid).toEqual(false, "Must not be valid") | ||
| expect(widget.refs.input.value).toEqual("", "Must have empty value") | ||
| expect(onInvalidCalled).toEqual( | ||
| true, | ||
| "Must call the onInvalid callback" | ||
| ) | ||
| expect( | ||
| widget.refs.wrapper.className.indexOf(INVALID_CLASS), | ||
| "Must have the '" + INVALID_CLASS + "' class" | ||
| ).toNotEqual(-1) | ||
| done() | ||
| }, 500) | ||
| }) | ||
| it('renders as invalid if the value length exceeds maxLength', (done) => { | ||
| let onInvalidCalled = false | ||
| function testInvalidEvent() { | ||
| onInvalidCalled = true | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput maxLength={2} value={1234} onInvalid={ testInvalidEvent } /> | ||
| ) | ||
| setTimeout(() => { | ||
| expect(widget.refs.input.value).toEqual('1234') | ||
| expect(widget.refs.wrapper.className.indexOf(INVALID_CLASS)).toNotEqual(-1) | ||
| expect(onInvalidCalled).toEqual( | ||
| true, | ||
| "Must trigger 'invalid' if the initial value is longer than the maxLength" | ||
| ) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('renders as invalid if the value does not match a pattern', (done) => { | ||
| let onInvalidCalled = false | ||
| function testInvalidEvent() { | ||
| onInvalidCalled = true | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput pattern="\\d+" value={12.34} precision={2} onInvalid={ testInvalidEvent } /> | ||
| ) | ||
| setTimeout(() => { | ||
| expect(widget.refs.wrapper.className.indexOf(INVALID_CLASS)).toNotEqual(-1) | ||
| expect(onInvalidCalled).toEqual(true) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('does not render as invalid if the value does match a pattern', (done) => { | ||
| let onInvalidCalled = false | ||
| function testInvalidEvent() { | ||
| onInvalidCalled = true | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput pattern="12.34" value={12.34} precision={2} onInvalid={ testInvalidEvent } /> | ||
| ) | ||
| setTimeout(() => { | ||
| expect(widget.refs.wrapper.className.indexOf(INVALID_CLASS)).toEqual(-1) | ||
| expect(onInvalidCalled).toEqual(false) | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('handles setCustomValidity()', (done) => { | ||
| let onInvalidCalled = false | ||
| let validationError = null | ||
| function testInvalidEvent(msg) { | ||
| onInvalidCalled = true | ||
| validationError = msg | ||
| } | ||
| let widget = TestUtils.renderIntoDocument( | ||
| <NumericInput pattern="\\d+" value={12.34} precision={2} onInvalid={ testInvalidEvent } /> | ||
| ) | ||
| widget.refs.input.value = "abc" | ||
| widget.refs.input.setCustomValidity("This is a test") | ||
| TestUtils.Simulate.change(widget.refs.input) | ||
| setTimeout(() => { | ||
| expect(widget.refs.wrapper.className.indexOf(INVALID_CLASS)).toNotEqual(-1) | ||
| expect(onInvalidCalled).toEqual(true) | ||
| expect(validationError).toEqual("This is a test") | ||
| done() | ||
| }, 50) | ||
| }) | ||
| it('does not call onValid multiple times in sequence', (done) => { | ||
| let _called = 0 | ||
| let widget = TestUtils.renderIntoDocument(<Wrapper onValid={ () => _called += 1 }/>) | ||
| expect(_called).toEqual(1, "Must call the onValid callback when the initial render produces valid value") | ||
| widget.setState({ value: 5 }) | ||
| expect(widget.refs.NumericInput.refs.input.value).toEqual('5') | ||
| expect(_called).toEqual(1, "Must not call the onValid callback after setting valid value") | ||
| widget.setState({ maxLength: 5 }) | ||
| expect(widget.refs.NumericInput.refs.input.maxLength).toEqual(5) | ||
| expect(_called).toEqual(1, "Must not call the onValid callback after setting big enough maxLength") | ||
| widget.setState({ required: true }) | ||
| expect(widget.refs.NumericInput.refs.input.required).toEqual(true) | ||
| expect(_called).toEqual(1, "Must not call the onValid callback after setting required to true while the value is not empty") | ||
| widget.setState({ value: 6 }) | ||
| expect(widget.refs.NumericInput.refs.input.value).toEqual('6') | ||
| expect(_called).toEqual(1, "Must not call the onValid callback after transition to another valid value") | ||
| done() | ||
| }) | ||
| it('does not call onInvalid multiple times in sequence') | ||
| }) |
| /* global $, hljs, NumericInput, React */ | ||
| export default class Demo extends React.Component | ||
| { | ||
| constructor(...args) { | ||
| super(...args) | ||
| // var that = this; | ||
| this.state = { | ||
| inputProps : { | ||
| name : { value: "whatever" , on: false }, | ||
| className : { value: "form-control", on: true }, | ||
| value : { value: 50, on: true }, | ||
| min : { value: 0, on: true }, | ||
| max : { value: 100, on: true }, | ||
| precision : { value: 0, on: true }, | ||
| size : { value: 5, on: true }, | ||
| maxLength : { value: 2, on: false }, | ||
| disabled : { value: true, on: false }, | ||
| readOnly : { value: true, on: false }, | ||
| mobile : { value: true, on: false }, | ||
| required : { value: true, on: false }, | ||
| noValidate: { value: true, on: false }, | ||
| pattern : { value: "[0-9].[0-9][0-9]", on: false }, | ||
| title : { value: "The title attr", on: false } | ||
| } | ||
| } | ||
| } | ||
| componentDidUpdate() { | ||
| hljs.highlightBlock(this.refs.code) | ||
| } | ||
| toggleProp(propName) { | ||
| this.state.inputProps[propName].on = !this.state.inputProps[propName].on | ||
| this.setState(this.state) | ||
| } | ||
| setProp(propName, event) { | ||
| let val = event.target ? event.target.value : event | ||
| this.state.inputProps[propName].value = val | ||
| this.setState(this.state) | ||
| } | ||
| onChange(x) { | ||
| this.state.inputProps.value.value = x | ||
| if (this.state.inputProps.value.on) { | ||
| this.setState(this.state); | ||
| } | ||
| } | ||
| onInvalid(message) { | ||
| // console.log("Invalid", message) | ||
| $(this.refs.errorMessage).text(message || "Unknown error") | ||
| } | ||
| onValid() { | ||
| // console.log("Valid") | ||
| $(this.refs.errorMessage).empty() | ||
| } | ||
| renderCode() { | ||
| let out = '<NumericInput ' | ||
| let hasProps = false | ||
| for (let propName in this.state.inputProps) { | ||
| if (this.state.inputProps[propName].on && !this.state.inputProps[propName].hidden) { | ||
| let val = this.state.inputProps[propName].value | ||
| out += `\n\t${propName}` | ||
| if (val !== true) { | ||
| out += '=' + ( | ||
| typeof val == 'string' ? `"${val}" ` : `{ ${val} } ` | ||
| ) | ||
| } | ||
| hasProps = true | ||
| } | ||
| } | ||
| if (hasProps) { | ||
| out += '\n' | ||
| } | ||
| out += '/>' | ||
| return <div className="code js" ref="code" style={{ minHeight: 379 }}>{ out }</div> | ||
| } | ||
| renderPropEditors(config) { | ||
| return config.map((props, propName) => { | ||
| let editor = null | ||
| let { type, name, ...rest } = props | ||
| if (type == 'text') { | ||
| editor = ( | ||
| <input | ||
| type="text" | ||
| className="form-control input-sm" | ||
| value={ this.state.inputProps[name].value } | ||
| onChange={ this.setProp.bind(this, name) } | ||
| /> | ||
| ) | ||
| } | ||
| else if (type == "number") { | ||
| editor = ( | ||
| <NumericInput | ||
| className="form-control input-sm" | ||
| value={ this.state.inputProps[name].value } | ||
| onChange={ this.setProp.bind(this, name) } | ||
| { ...rest } | ||
| /> | ||
| ) | ||
| } | ||
| return ( | ||
| <tr key={ propName }> | ||
| <td className="unselectable"> | ||
| <label style={{ display: "block" }}> | ||
| <input | ||
| type="checkbox" | ||
| checked={ this.state.inputProps[name].on } | ||
| onChange={ this.toggleProp.bind(this, name) } | ||
| /> | ||
| { name } | ||
| </label> | ||
| </td> | ||
| <td> | ||
| { editor } | ||
| </td> | ||
| </tr> | ||
| ) | ||
| }) | ||
| } | ||
| render() { | ||
| let inputProps = {} | ||
| for (let propName in this.state.inputProps) { | ||
| if (this.state.inputProps[propName].on) { | ||
| inputProps[propName] = this.state.inputProps[propName].value | ||
| } | ||
| // else { | ||
| // inputProps[propName] = null | ||
| // } | ||
| } | ||
| return ( | ||
| <div className="row"> | ||
| <div className="col-xs-6"> | ||
| <div className="panel panel-default"> | ||
| <div className="panel-heading">Props</div> | ||
| <table className="table table-striped table-condensed"> | ||
| <colgroup> | ||
| <col width={169}/> | ||
| <col/> | ||
| </colgroup> | ||
| <thead> | ||
| <tr> | ||
| <th>name</th> | ||
| <th>value</th> | ||
| </tr> | ||
| </thead> | ||
| </table> | ||
| <div style={{ | ||
| overflow : 'auto', | ||
| maxHeight: 452 | ||
| }}> | ||
| <table className="table table-striped table-condensed"> | ||
| <colgroup> | ||
| <col width={169}/> | ||
| <col/> | ||
| </colgroup> | ||
| <tbody> | ||
| {this.renderPropEditors([ | ||
| { name: "name" , type: "text" }, | ||
| { name: "className" , type: "text" }, | ||
| { name: "value" , type: "text" }, | ||
| { name: "min" , type: "number" }, | ||
| { name: "max" , type: "number" }, | ||
| { name: "precision" , type: "number", min: 0, max: 20 }, | ||
| { name: "size" , type: "number", min: 0, max: 60 }, | ||
| { name: "maxLength" , type: "number", min: 0, max: 20 }, | ||
| { name: "disabled" , type: "bool" }, | ||
| { name: "readOnly" , type: "bool" }, | ||
| { name: "mobile" , type: "bool" }, | ||
| { name: "pattern" , type: "text" }, | ||
| { name: "title" , type: "text" }, | ||
| { name: "required" , type: "bool" }, | ||
| { name: "noValidate", type: "bool" } | ||
| ])} | ||
| {/* | ||
| parse function parseFloat | ||
| format function none | ||
| style object none | ||
| */} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div className="col-xs-6"> | ||
| <div className="panel panel-primary"> | ||
| <div className="panel-heading">Preview</div> | ||
| <div className="panel-body"> | ||
| <div ref="example"> | ||
| <NumericInput { ...inputProps } | ||
| onChange={ this.onChange.bind(this) } | ||
| onInvalid={ this.onInvalid.bind(this) } | ||
| onValid={ this.onValid.bind(this) } | ||
| /> | ||
| <div className="help-block"> | ||
| <span ref="errorMessage" className="text-danger"/> | ||
| </div> | ||
| </div> | ||
| <hr/> | ||
| { this.renderCode() } | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } | ||
| } |
@@ -1,6 +0,8 @@ | ||
| /* global describe, it, ReactDOM, React */ | ||
| import expect from 'expect'; | ||
| import NumericInput from '../src/NumericInput.jsx'; | ||
| /* global describe, it */ | ||
| import expect from 'expect' | ||
| import NumericInput from '../src/NumericInput.jsx' | ||
| import React from 'react' | ||
| import ReactDOM from 'react-dom' | ||
| import TestUtils from 'react-addons-test-utils' | ||
| const TestUtils = React.addons.TestUtils; | ||
| const KEYCODE_UP = 38; | ||
@@ -37,11 +39,11 @@ const KEYCODE_DOWN = 40; | ||
| // Test the step | ||
| // Test the step | ||
| TestUtils.Simulate.keyDown(inputNode, { keyCode: KEYCODE_UP }); | ||
| expect(inputNode.value).toEqual('5.20'); | ||
| // Test the max | ||
| // Test the max | ||
| TestUtils.Simulate.keyDown(inputNode, { keyCode: KEYCODE_UP }); | ||
| expect(inputNode.value).toEqual('5.30'); | ||
| // Test the min | ||
| // Test the min | ||
| TestUtils.Simulate.keyDown(inputNode, { keyCode: KEYCODE_DOWN }); | ||
@@ -53,2 +55,20 @@ expect(inputNode.value).toEqual('5.10'); | ||
| it('accepts value of 0', () => { | ||
| var widget = TestUtils.renderIntoDocument(<NumericInput value={0}/>), | ||
| inputNode = widget.refs.input; | ||
| expect(inputNode.value).toEqual('0'); | ||
| }); | ||
| it('accepts value of "0"', () => { | ||
| var widget = TestUtils.renderIntoDocument(<NumericInput value="0"/>), | ||
| inputNode = widget.refs.input; | ||
| expect(inputNode.value).toEqual('0'); | ||
| }); | ||
| it('accepts value of ""', () => { | ||
| var widget = TestUtils.renderIntoDocument(<NumericInput value=""/>), | ||
| inputNode = widget.refs.input; | ||
| expect(inputNode.value).toEqual(''); | ||
| }); | ||
| it('can auto-increase', (done) => { | ||
@@ -341,3 +361,75 @@ this.timeout | ||
| expect(btnDownNode.style.top).toEqual('50%'); | ||
| }) | ||
| }); | ||
| it("calls it's onChange callback properly", () => { | ||
| var value = null, stringValue = ""; | ||
| function onChange(valueAsNumber, valueAsString) { | ||
| value = valueAsNumber | ||
| stringValue = valueAsString | ||
| } | ||
| function format(val) { | ||
| return val * 100 + 'x'; | ||
| } | ||
| var widget = TestUtils.renderIntoDocument( | ||
| <NumericInput value={0} onChange={onChange} format={format} /> | ||
| ), | ||
| widgetNode = ReactDOM.findDOMNode(widget), | ||
| btnUpNode = widgetNode.firstChild.nextElementSibling, | ||
| inputNode = widget.refs.input; | ||
| expect(inputNode.value).toEqual('0x'); | ||
| expect(value).toEqual(null); | ||
| TestUtils.Simulate.mouseDown(btnUpNode); | ||
| expect(inputNode.value).toEqual('100x'); | ||
| expect(stringValue).toEqual('100x'); | ||
| expect(value).toEqual(1); | ||
| }); | ||
| it("calls it's onFocus and onBlur callbacks", () => { | ||
| var hasFocus = null; | ||
| function onFocus() { | ||
| hasFocus = true; | ||
| } | ||
| function onBlur() { | ||
| hasFocus = false; | ||
| } | ||
| var widget = TestUtils.renderIntoDocument( | ||
| <NumericInput onFocus={onFocus} onBlur={onBlur} /> | ||
| ), | ||
| inputNode = widget.refs.input; | ||
| expect(hasFocus).toEqual(null); | ||
| TestUtils.Simulate.focus(inputNode); | ||
| expect(hasFocus).toEqual(true); | ||
| TestUtils.Simulate.blur(inputNode); | ||
| expect(hasFocus).toEqual(false); | ||
| }); | ||
| it("calls it's onKeyDown callbacks and makest the event cancelable", () => { | ||
| var hits = 0, widget, inputNode; | ||
| function onKeyDown(e) { | ||
| expect(e.target).toEqual(inputNode); | ||
| if (hits > 0) { | ||
| e.preventDefault() | ||
| } | ||
| hits++; | ||
| } | ||
| widget = TestUtils.renderIntoDocument( | ||
| <NumericInput value={0} onKeyDown={onKeyDown} /> | ||
| ); | ||
| inputNode = widget.refs.input; | ||
| expect(hits).toEqual(0); | ||
| expect(inputNode.value).toEqual('0'); | ||
| TestUtils.Simulate.keyDown(inputNode, { keyCode: KEYCODE_UP }); | ||
| expect(hits).toEqual(1); | ||
| expect(inputNode.value).toEqual('1'); | ||
| TestUtils.Simulate.keyDown(inputNode, { keyCode: KEYCODE_UP }); | ||
| expect(hits).toEqual(2); | ||
| expect(inputNode.value).toEqual('1'); | ||
| }); | ||
| }); |
+372
-101
@@ -57,3 +57,3 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
| 'use strict'; | ||
| "use strict"; | ||
@@ -87,2 +87,31 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
| /** | ||
| * Just a simple helper to provide support for older IEs. This is not exactly a | ||
| * polyfill for classList.add but it does what we need with minimal efford. | ||
| * Works with single className only! | ||
| */ | ||
| function addClass(element, className) { | ||
| if (element.classList) { | ||
| return element.classList.add(className); | ||
| } | ||
| if (!element.className.search(new RegExp("\\b" + className + "\\b"))) { | ||
| element.className = " " + className; | ||
| } | ||
| } | ||
| /** | ||
| * Just a simple helper to provide support for older IEs. This is not exactly a | ||
| * polyfill for classList.remove but it does what we need with minimal efford. | ||
| * Works with single className only! | ||
| */ | ||
| function removeClass(element, className) { | ||
| if (element.className) { | ||
| if (element.classList) { | ||
| return element.classList.remove(className); | ||
| } | ||
| element.className = element.className.replace(new RegExp("\\b" + className + "\\b", "g"), ""); | ||
| } | ||
| } | ||
| var NumericInput = exports.NumericInput = function (_React$Component) { | ||
@@ -112,15 +141,15 @@ _inherits(NumericInput, _React$Component); | ||
| _this._timer = null; | ||
| _this._valid = undefined; | ||
| _this.state = { | ||
| step: props.step, | ||
| min: props.min, | ||
| max: props.max, | ||
| style: {}, | ||
| value: 'value' in props ? _this._parse(String(props.value || '')) : null | ||
| selectionStart: null, | ||
| selectionEnd: null, | ||
| value: "value" in props ? props.value : props.defaultValue, | ||
| btnDownHover: false, | ||
| btnDownActive: false, | ||
| btnUpHover: false, | ||
| btnUpActive: false, | ||
| inputFocus: false | ||
| }; | ||
| for (var x in NumericInput.style) { | ||
| _this.state.style[x] = Object.assign({}, NumericInput.style[x], props.style[x] || {}); | ||
| } | ||
| _this.stop = _this.stop.bind(_this); | ||
@@ -131,3 +160,9 @@ return _this; | ||
| /** | ||
| * This is used to clear the timer if any | ||
| * Special care is taken for the "value" prop: | ||
| * - If not provided - set it to null | ||
| * - If the prop is a number - use it as is | ||
| * - Otherwise: | ||
| * 1. Convert it to string (falsy values become "") | ||
| * 2. Then trim it. | ||
| * 3. Then parse it to number (delegating to this.props.parse if any) | ||
| */ | ||
@@ -139,2 +174,4 @@ | ||
| */ | ||
| //, | ||
| // noValidate: false | ||
@@ -148,3 +185,58 @@ /** | ||
| _createClass(NumericInput, [{ | ||
| key: 'componentWillUnmount', | ||
| key: "componentWillReceiveProps", | ||
| value: function componentWillReceiveProps(props) { | ||
| var _value = String(props.value || props.value === 0 ? props.value : '').replace(/^\s*|\s*$/, ""); | ||
| this.setState({ | ||
| value: "value" in props && _value !== '' ? this._parse(_value) : null | ||
| }); | ||
| } | ||
| /** | ||
| * After the component has been rendered into the DOM, do whatever is | ||
| * needed to "reconnect" it to the outer world, i.e. restore selection, | ||
| * call some of the callbacks, validate etc. | ||
| */ | ||
| }, { | ||
| key: "componentDidUpdate", | ||
| value: function componentDidUpdate(prevProps, prevState) { | ||
| // Call the onChange if needed. This is placed here because there are | ||
| // many reasons for changing the value and this is the common place | ||
| // that can capture them all | ||
| if (prevState.value != this.state.value) { | ||
| this._invokeEventCallback("onChange", this.state.value, this.refs.input.value); | ||
| } | ||
| // Notify about the focus | ||
| if (this.state.inputFocus && !prevState.inputFocus) { | ||
| this.refs.input.focus(); | ||
| // Restore selectionStart (if any) | ||
| if (this.state.selectionStart || this.state.selectionStart === 0) { | ||
| this.refs.input.selectionStart = this.state.selectionStart; | ||
| } | ||
| // Restore selectionEnd (if any) | ||
| if (this.state.selectionEnd || this.state.selectionEnd === 0) { | ||
| this.refs.input.selectionEnd = this.state.selectionEnd; | ||
| } | ||
| } | ||
| // This is a special case! If the component has the "autoFocus" prop | ||
| // and the browser did focus it we have pass that to the onFocus | ||
| if (!this.state.inputFocus && document.activeElement === this.refs.input) { | ||
| this.state.inputFocus = true; | ||
| } | ||
| this.checkValidity(); | ||
| } | ||
| /** | ||
| * This is used to clear the timer if any | ||
| */ | ||
| }, { | ||
| key: "componentWillUnmount", | ||
| value: function componentWillUnmount() { | ||
@@ -159,3 +251,3 @@ this.stop(); | ||
| }, { | ||
| key: 'componentDidMount', | ||
| key: "componentDidMount", | ||
| value: function componentDidMount() { | ||
@@ -173,5 +265,77 @@ var _this2 = this; | ||
| }; | ||
| this.checkValidity(); | ||
| } | ||
| /** | ||
| * Unless noValidate is set to true, the component will check the | ||
| * existing validation state (if any) and will toggle the "has-error" | ||
| * CSS class on the wrapper | ||
| */ | ||
| }, { | ||
| key: "checkValidity", | ||
| value: function checkValidity() { | ||
| var valid = undefined, | ||
| validationError = ""; | ||
| var supportsValidation = !!this.refs.input.checkValidity; | ||
| // noValidate | ||
| var noValidate = !!(this.props.noValidate && this.props.noValidate != "false"); | ||
| this.refs.input.noValidate = noValidate; | ||
| // If "noValidate" is set or "checkValidity" is not supported then | ||
| // consider the element valid. Otherwise consider it invalid and | ||
| // make some additional checks below | ||
| valid = noValidate || !supportsValidation; | ||
| if (valid) { | ||
| validationError = ""; | ||
| } else { | ||
| // In some browsers once a pattern is set it connot be removed. The | ||
| // browser sets it to "" instead which results in validation | ||
| // failures... | ||
| if (this.refs.input.pattern === "") { | ||
| this.refs.input.pattern = this.props.required ? ".+" : ".*"; | ||
| } | ||
| // Now check validity | ||
| if (supportsValidation) { | ||
| this.refs.input.checkValidity(); | ||
| valid = this.refs.input.validity.valid; | ||
| if (!valid) { | ||
| validationError = this.refs.input.validationMessage; | ||
| } | ||
| } | ||
| // Some brousers might fail to validate maxLength | ||
| if (valid && supportsValidation && this.props.maxLength) { | ||
| if (this.refs.input.value.length > this.props.maxLength) { | ||
| validationError = "This value is too long"; | ||
| } | ||
| } | ||
| } | ||
| validationError = validationError || (valid ? "" : this.refs.input.validationMessage || "Unknown Error"); | ||
| var validStateChanged = this._valid !== validationError; | ||
| this._valid = validationError; | ||
| if (validationError) { | ||
| addClass(this.refs.wrapper, "has-error"); | ||
| if (validStateChanged) { | ||
| this._invokeEventCallback("onInvalid", validationError, this.state.value, this.refs.input.value); | ||
| } | ||
| } else { | ||
| removeClass(this.refs.wrapper, "has-error"); | ||
| if (validStateChanged) { | ||
| this._invokeEventCallback("onValid", this.state.value, this.refs.input.value); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Used internally to parse the argument x to it's numeric representation. | ||
@@ -185,3 +349,3 @@ * If the argument cannot be converted to finite number returns 0; If a | ||
| }, { | ||
| key: '_toNumber', | ||
| key: "_toNumber", | ||
| value: function _toNumber(x) { | ||
@@ -208,3 +372,3 @@ var n = parseFloat(x); | ||
| }, { | ||
| key: '_parse', | ||
| key: "_parse", | ||
| value: function _parse(x) { | ||
@@ -224,3 +388,3 @@ if (typeof this.props.parse == 'function') { | ||
| }, { | ||
| key: '_format', | ||
| key: "_format", | ||
| value: function _format(n) { | ||
@@ -242,8 +406,8 @@ var _n = this._toNumber(n).toFixed(this.props.precision); | ||
| }, { | ||
| key: '_step', | ||
| value: function _step(n) { | ||
| var _n = this._toNumber((this.state.value || 0) + this.state.step * n); | ||
| key: "_step", | ||
| value: function _step(n, callback) { | ||
| var _n = this._toNumber((this.state.value || 0) + this.props.step * n); | ||
| if (_n !== this.state.value) { | ||
| this.setState({ value: _n }); | ||
| this.setState({ value: _n }, callback); | ||
| } | ||
@@ -259,3 +423,3 @@ } | ||
| }, { | ||
| key: '_onChange', | ||
| key: "_onChange", | ||
| value: function _onChange(e) { | ||
@@ -268,17 +432,53 @@ this.setState({ | ||
| /** | ||
| * This binds the Up/Down arrow keys | ||
| * This binds the Up/Down arrow key listeners | ||
| */ | ||
| }, { | ||
| key: '_onKeyDown', | ||
| value: function _onKeyDown(e) { | ||
| if (e.keyCode === KEYCODE_UP) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? 0.1 : e.shiftKey ? 10 : 1); | ||
| } else if (e.keyCode === KEYCODE_DOWN) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? -0.1 : e.shiftKey ? -10 : -1); | ||
| key: "_onKeyDown", | ||
| value: function _onKeyDown() { | ||
| for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
| args[_key] = arguments[_key]; | ||
| } | ||
| this._invokeEventCallback.apply(this, ["onKeyDown"].concat(args)); | ||
| var e = args[0]; | ||
| if (!e.isDefaultPrevented()) { | ||
| if (e.keyCode === KEYCODE_UP) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? 0.1 : e.shiftKey ? 10 : 1); | ||
| } else if (e.keyCode === KEYCODE_DOWN) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? -0.1 : e.shiftKey ? -10 : -1); | ||
| } | ||
| } | ||
| } | ||
| }, { | ||
| key: "_onSelectionChange", | ||
| value: function _onSelectionChange(e) { | ||
| var _this3 = this; | ||
| this.setState({ | ||
| selectionStart: this.refs.input.selectionStart, | ||
| selectionEnd: this.refs.input.selectionEnd | ||
| }, function () { | ||
| switch (e.type) { | ||
| case "input": | ||
| if (_this3.props.onInput) { | ||
| _this3.props.onInput.call(_this3.refs.input, e); | ||
| } | ||
| break; | ||
| case "select": | ||
| if (_this3.props.onSelect) { | ||
| _this3.props.onSelect.call(_this3.refs.input, e); | ||
| } | ||
| break; | ||
| case "selectstart": | ||
| if (_this3.props.onSelectStart) { | ||
| _this3.props.onSelectStart.call(_this3.refs.input, e); | ||
| } | ||
| break; | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
@@ -289,3 +489,3 @@ * Stops the widget from auto-changing by clearing the timer (if any) | ||
| }, { | ||
| key: 'stop', | ||
| key: "stop", | ||
| value: function stop() { | ||
@@ -307,11 +507,11 @@ if (this._timer) { | ||
| }, { | ||
| key: 'increase', | ||
| value: function increase(_recursive) { | ||
| var _this3 = this; | ||
| key: "increase", | ||
| value: function increase(_recursive, callback) { | ||
| var _this4 = this; | ||
| this.stop(); | ||
| this._step(1); | ||
| this._step(1, callback); | ||
| if (isNaN(this.state.value) || this.state.value < this.props.max) { | ||
| this._timer = setTimeout(function () { | ||
| _this3.increase(true); | ||
| _this4.increase(true); | ||
| }, _recursive ? NumericInput.SPEED : NumericInput.DELAY); | ||
@@ -331,11 +531,11 @@ } | ||
| }, { | ||
| key: 'decrease', | ||
| value: function decrease(_recursive) { | ||
| var _this4 = this; | ||
| key: "decrease", | ||
| value: function decrease(_recursive, callback) { | ||
| var _this5 = this; | ||
| this.stop(); | ||
| this._step(-1); | ||
| this._step(-1, callback); | ||
| if (isNaN(this.state.value) || this.state.value > this.props.min) { | ||
| this._timer = setTimeout(function () { | ||
| _this4.decrease(true); | ||
| _this5.decrease(true); | ||
| }, _recursive ? NumericInput.SPEED : NumericInput.DELAY); | ||
@@ -352,15 +552,9 @@ } | ||
| }, { | ||
| key: 'onMouseDown', | ||
| value: function onMouseDown(dir, e) { | ||
| var _this5 = this; | ||
| e.preventDefault(); | ||
| key: "onMouseDown", | ||
| value: function onMouseDown(dir, callback) { | ||
| if (dir == 'down') { | ||
| this.decrease(); | ||
| this.decrease(false, callback); | ||
| } else if (dir == 'up') { | ||
| this.increase(); | ||
| this.increase(false, callback); | ||
| } | ||
| setTimeout(function () { | ||
| _this5.refs.input.focus(); | ||
| }); | ||
| } | ||
@@ -376,3 +570,3 @@ | ||
| }, { | ||
| key: 'onTouchStart', | ||
| key: "onTouchStart", | ||
| value: function onTouchStart(dir, e) { | ||
@@ -386,3 +580,16 @@ e.preventDefault(); | ||
| } | ||
| }, { | ||
| key: "_invokeEventCallback", | ||
| value: function _invokeEventCallback(callbackName) { | ||
| if (typeof this.props[callbackName] == "function") { | ||
| var _props$callbackName; | ||
| for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | ||
| args[_key2 - 1] = arguments[_key2]; | ||
| } | ||
| (_props$callbackName = this.props[callbackName]).call.apply(_props$callbackName, [null].concat(args)); | ||
| } | ||
| } | ||
| /** | ||
@@ -394,3 +601,3 @@ * Renders an input wrapped in relative span and up/down buttons | ||
| }, { | ||
| key: 'render', | ||
| key: "render", | ||
| value: function render() { | ||
@@ -401,15 +608,24 @@ var _this6 = this; | ||
| var state = this.state; | ||
| var css = {}; | ||
| // Build the styles | ||
| for (var x in NumericInput.style) { | ||
| css[x] = Object.assign({}, NumericInput.style[x], props.style ? props.style[x] || {} : {}); | ||
| } | ||
| var _props = this.props; | ||
| var | ||
| // These are ignored in rendering | ||
| step = props.step; | ||
| var min = props.min; | ||
| var max = props.max; | ||
| var precision = props.precision; | ||
| var parse = props.parse; | ||
| var format = props.format; | ||
| var value = props.value; | ||
| var type = props.type; | ||
| var style = props.style; | ||
| step = _props.step; | ||
| var min = _props.min; | ||
| var max = _props.max; | ||
| var precision = _props.precision; | ||
| var parse = _props.parse; | ||
| var format = _props.format; | ||
| var value = _props.value; | ||
| var type = _props.type; | ||
| var style = _props.style; | ||
| var defaultValue = _props.defaultValue; | ||
| var rest = _objectWithoutProperties(props, ['step', 'min', 'max', 'precision', 'parse', 'format', 'value', 'type', 'style']); | ||
| var rest = _objectWithoutProperties(_props, ["step", "min", "max", "precision", "parse", "format", "value", "type", "style", "defaultValue"]); | ||
@@ -426,4 +642,5 @@ var hasFormControl = props.className && /\bform-control\b/.test(props.className); | ||
| wrap: { | ||
| style: Object.assign({}, NumericInput.style.wrap, props.style.wrap), | ||
| className: 'react-numeric-input' | ||
| style: css.wrap, | ||
| className: 'react-numeric-input', | ||
| ref: 'wrapper' | ||
| }, | ||
@@ -433,15 +650,18 @@ input: _extends({ | ||
| type: 'text', | ||
| style: Object.assign({}, state.style.input, !hasFormControl ? state.style['input:not(.form-control)'] : {}, state.inputFocus ? state.style['input:focus'] : {}), | ||
| value: state.value || state.value === 0 ? this._format(state.value) : '' | ||
| style: Object.assign({}, css.input, !hasFormControl ? css['input:not(.form-control)'] : {}, state.inputFocus ? css['input:focus'] : {}) | ||
| }, rest), | ||
| btnUp: { | ||
| style: Object.assign({}, state.style.btn, state.style.btnUp, props.disabled ? state.style['btn:disabled'] : state.btnUpActive ? state.style['btn:active'] : state.btnUpHover ? state.style['btn:hover'] : {}) | ||
| style: Object.assign({}, css.btn, css.btnUp, props.disabled ? css['btn:disabled'] : state.btnUpActive ? css['btn:active'] : state.btnUpHover ? css['btn:hover'] : {}) | ||
| }, | ||
| btnDown: { | ||
| style: Object.assign({}, state.style.btn, state.style.btnDown, props.disabled ? state.style['btn:disabled'] : state.btnDownActive ? state.style['btn:active'] : state.btnDownHover ? state.style['btn:hover'] : {}) | ||
| style: Object.assign({}, css.btn, css.btnDown, props.disabled ? css['btn:disabled'] : state.btnDownActive ? css['btn:active'] : state.btnDownHover ? css['btn:hover'] : {}) | ||
| } | ||
| }; | ||
| if (state.value || state.value === 0) { | ||
| attrs.input.value = this._format(state.value); | ||
| } | ||
| if (hasFormControl) { | ||
| Object.assign(attrs.wrap.style, state.style['wrap.hasFormControl']); | ||
| Object.assign(attrs.wrap.style, css['wrap.hasFormControl']); | ||
| } | ||
@@ -451,5 +671,5 @@ | ||
| if (mobile) { | ||
| Object.assign(attrs.input.style, state.style['input.mobile']); | ||
| Object.assign(attrs.btnUp.style, state.style['btnUp.mobile']); | ||
| Object.assign(attrs.btnDown.style, state.style['btnDown.mobile']); | ||
| Object.assign(attrs.input.style, css['input.mobile']); | ||
| Object.assign(attrs.btnUp.style, css['btnUp.mobile']); | ||
| Object.assign(attrs.btnDown.style, css['btnDown.mobile']); | ||
| } | ||
@@ -485,8 +705,16 @@ | ||
| }, | ||
| onMouseDown: function onMouseDown(e) { | ||
| onMouseDown: function onMouseDown() { | ||
| for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
| args[_key3] = arguments[_key3]; | ||
| } | ||
| args[0].preventDefault(); | ||
| _this6.setState({ | ||
| btnUpHover: true, | ||
| btnUpActive: true | ||
| btnUpActive: true, | ||
| inputFocus: true | ||
| }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onFocus"].concat(args)); | ||
| }); | ||
| _this6.onMouseDown('up', e); | ||
| _this6.onMouseDown('up'); | ||
| } | ||
@@ -516,8 +744,16 @@ }); | ||
| }, | ||
| onMouseDown: function onMouseDown(e) { | ||
| onMouseDown: function onMouseDown() { | ||
| for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { | ||
| args[_key4] = arguments[_key4]; | ||
| } | ||
| args[0].preventDefault(); | ||
| _this6.setState({ | ||
| btnDownHover: true, | ||
| btnDownActive: true | ||
| btnDownActive: true, | ||
| inputFocus: true | ||
| }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onFocus"].concat(args)); | ||
| }); | ||
| _this6.onMouseDown('down', e); | ||
| _this6.onMouseDown('down'); | ||
| } | ||
@@ -529,9 +765,26 @@ }); | ||
| onKeyDown: this._onKeyDown.bind(this), | ||
| onInput: this._onSelectionChange.bind(this), | ||
| onSelect: this._onSelectionChange.bind(this), | ||
| onSelectStart: this._onSelectionChange.bind(this), | ||
| onFocus: function onFocus() { | ||
| _this6.setState({ inputFocus: true }); | ||
| for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { | ||
| args[_key5] = arguments[_key5]; | ||
| } | ||
| _this6.setState({ inputFocus: true }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onFocus"].concat(args)); | ||
| }); | ||
| }, | ||
| onBlur: function onBlur() { | ||
| _this6.setState({ inputFocus: false }); | ||
| for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { | ||
| args[_key6] = arguments[_key6]; | ||
| } | ||
| _this6.setState({ inputFocus: false }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onBlur"].concat(args)); | ||
| }); | ||
| } | ||
| }); | ||
| } else { | ||
| Object.assign(attrs.input.style, css['input:disabled']); | ||
| } | ||
@@ -541,15 +794,15 @@ | ||
| return _react2.default.createElement( | ||
| 'span', | ||
| "span", | ||
| attrs.wrap, | ||
| _react2.default.createElement('input', attrs.input), | ||
| _react2.default.createElement("input", attrs.input), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnUp, | ||
| _react2.default.createElement('i', { style: state.style.minus }), | ||
| _react2.default.createElement('i', { style: state.style.plus }) | ||
| _react2.default.createElement("i", { style: css.minus }), | ||
| _react2.default.createElement("i", { style: css.plus }) | ||
| ), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnDown, | ||
| _react2.default.createElement('i', { style: state.style.minus }) | ||
| _react2.default.createElement("i", { style: css.minus }) | ||
| ) | ||
@@ -560,14 +813,14 @@ ); | ||
| return _react2.default.createElement( | ||
| 'span', | ||
| "span", | ||
| attrs.wrap, | ||
| _react2.default.createElement('input', attrs.input), | ||
| _react2.default.createElement("input", attrs.input), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnUp, | ||
| _react2.default.createElement('i', { style: state.style.arrowUp }) | ||
| _react2.default.createElement("i", { style: css.arrowUp }) | ||
| ), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnDown, | ||
| _react2.default.createElement('i', { style: state.style.arrowDown }) | ||
| _react2.default.createElement("i", { style: css.arrowDown }) | ||
| ) | ||
@@ -586,2 +839,3 @@ ); | ||
| precision: PropTypes.number, | ||
| maxLength: PropTypes.number, | ||
| parse: PropTypes.func, | ||
@@ -592,6 +846,19 @@ format: PropTypes.func, | ||
| readOnly: PropTypes.bool, | ||
| required: PropTypes.bool, | ||
| noValidate: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), | ||
| style: PropTypes.object, | ||
| type: PropTypes.string, | ||
| pattern: PropTypes.string, | ||
| onFocus: PropTypes.func, | ||
| onBlur: PropTypes.func, | ||
| onKeyDown: PropTypes.func, | ||
| onChange: PropTypes.func, | ||
| onInvalid: PropTypes.func, | ||
| onValid: PropTypes.func, | ||
| onInput: PropTypes.func, | ||
| onSelect: PropTypes.func, | ||
| onSelectStart: PropTypes.func, | ||
| size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
| value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
| defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
| mobile: function mobile(props, propName) { | ||
@@ -605,3 +872,3 @@ var prop = props[propName]; | ||
| NumericInput.defaultProps = { | ||
| value: 0, | ||
| // value : '', | ||
| step: 1, | ||
@@ -613,6 +880,5 @@ min: Number.MIN_SAFE_INTEGER || -9007199254740991, | ||
| format: null, | ||
| className: '', | ||
| // className : '', | ||
| mobile: 'auto', | ||
| style: {} | ||
| }; | ||
| style: {} }; | ||
| NumericInput.style = { | ||
@@ -689,3 +955,3 @@ | ||
| background: 'rgba(0,0,0,.1)', | ||
| boxShadow: '-1px -1px 3px rgba(0,0,0,.1) inset, 1px 1px 3px rgba(255,255,255,.7) inset' | ||
| boxShadow: "-1px -1px 3px rgba(0,0,0,.1) inset,\n 1px 1px 3px rgba(255,255,255,.7) inset" | ||
| }, | ||
@@ -732,3 +998,3 @@ | ||
| background: 'rgba(0,0,0,.3)', | ||
| boxShadow: '0 1px 3px rgba(0,0,0,.2) inset, -1px -1px 4px rgba(255,255,255,.5) inset' | ||
| boxShadow: "0 1px 3px rgba(0,0,0,.2) inset,\n -1px -1px 4px rgba(255,255,255,.5) inset" | ||
| }, | ||
@@ -764,3 +1030,8 @@ | ||
| 'input:focus': {} | ||
| 'input:focus': {}, | ||
| 'input:disabled': { | ||
| color: 'rgba(0, 0, 0, 0.3)', | ||
| textShadow: '0 1px 0 rgba(255, 255, 255, 0.8)' | ||
| } | ||
| }; | ||
@@ -767,0 +1038,0 @@ NumericInput.SPEED = 50; |
@@ -1,1 +0,1 @@ | ||
| !function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e(require("React"));else if("function"==typeof define&&define.amd)define(["React"],e);else{var n=e("object"==typeof exports?require("React"):t.React);for(var o in n)("object"==typeof exports?exports:t)[o]=n[o]}}(this,function(t){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={exports:{},id:o,loaded:!1};return t[o].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function r(t,e){var n={};for(var o in t)e.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(t,o)&&(n[o]=t[o]);return n}function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var u=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t},p=function(){function t(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(e,n,o){return n&&t(e.prototype,n),o&&t(e,o),e}}();Object.defineProperty(e,"__esModule",{value:!0}),e.NumericInput=void 0;var l=n(1),c=o(l),b=c["default"].PropTypes,f=38,d=40,h=e.NumericInput=function(t){function e(t){s(this,e);var n=i(this,Object.getPrototypeOf(e).call(this,t));n._timer=null,n.state={step:t.step,min:t.min,max:t.max,style:{},value:"value"in t?n._parse(String(t.value||"")):null};for(var o in e.style)n.state.style[o]=Object.assign({},e.style[o],t.style[o]||{});return n.stop=n.stop.bind(n),n}return a(e,t),p(e,[{key:"componentWillUnmount",value:function(){this.stop()}},{key:"componentDidMount",value:function(){var t=this;this.refs.input.getValueAsNumber=function(){return t.state.value||0},this.refs.input.setValue=function(e){t.setState({value:t._parse(e)})}}},{key:"_toNumber",value:function(t){var e=parseFloat(t),n=Math.pow(10,this.props.precision);return(isNaN(e)||!isFinite(e))&&(e=0),e=Math.min(Math.max(e,this.props.min),this.props.max),e=Math.round(e*n)/n}},{key:"_parse",value:function(t){return"function"==typeof this.props.parse?parseFloat(this.props.parse(t)):parseFloat(t)}},{key:"_format",value:function(t){var e=this._toNumber(t).toFixed(this.props.precision);return this.props.format?this.props.format(e):e}},{key:"_step",value:function(t){var e=this._toNumber((this.state.value||0)+this.state.step*t);e!==this.state.value&&this.setState({value:e})}},{key:"_onChange",value:function(t){this.setState({value:this._parse(t.target.value)})}},{key:"_onKeyDown",value:function(t){t.keyCode===f?(t.preventDefault(),this._step(t.ctrlKey||t.metaKey?.1:t.shiftKey?10:1)):t.keyCode===d&&(t.preventDefault(),this._step(t.ctrlKey||t.metaKey?-.1:t.shiftKey?-10:-1))}},{key:"stop",value:function(){this._timer&&window.clearTimeout(this._timer)}},{key:"increase",value:function(t){var n=this;this.stop(),this._step(1),(isNaN(this.state.value)||this.state.value<this.props.max)&&(this._timer=setTimeout(function(){n.increase(!0)},t?e.SPEED:e.DELAY))}},{key:"decrease",value:function(t){var n=this;this.stop(),this._step(-1),(isNaN(this.state.value)||this.state.value>this.props.min)&&(this._timer=setTimeout(function(){n.decrease(!0)},t?e.SPEED:e.DELAY))}},{key:"onMouseDown",value:function(t,e){var n=this;e.preventDefault(),"down"==t?this.decrease():"up"==t&&this.increase(),setTimeout(function(){n.refs.input.focus()})}},{key:"onTouchStart",value:function(t,e){e.preventDefault(),"down"==t?this.decrease():"up"==t&&this.increase()}},{key:"render",value:function(){var t=this,n=this.props,o=this.state,s=(n.step,n.min,n.max,n.precision,n.parse,n.format,n.value,n.type,n.style,r(n,["step","min","max","precision","parse","format","value","type","style"])),i=n.className&&/\bform-control\b/.test(n.className),a="auto"==n.mobile?"ontouchstart"in document:n.mobile;"function"==typeof a&&(a=a.call(this)),a=!!a;var p={wrap:{style:Object.assign({},e.style.wrap,n.style.wrap),className:"react-numeric-input"},input:u({ref:"input",type:"text",style:Object.assign({},o.style.input,i?{}:o.style["input:not(.form-control)"],o.inputFocus?o.style["input:focus"]:{}),value:o.value||0===o.value?this._format(o.value):""},s),btnUp:{style:Object.assign({},o.style.btn,o.style.btnUp,n.disabled?o.style["btn:disabled"]:o.btnUpActive?o.style["btn:active"]:o.btnUpHover?o.style["btn:hover"]:{})},btnDown:{style:Object.assign({},o.style.btn,o.style.btnDown,n.disabled?o.style["btn:disabled"]:o.btnDownActive?o.style["btn:active"]:o.btnDownHover?o.style["btn:hover"]:{})}};return i&&Object.assign(p.wrap.style,o.style["wrap.hasFormControl"]),a&&(Object.assign(p.input.style,o.style["input.mobile"]),Object.assign(p.btnUp.style,o.style["btnUp.mobile"]),Object.assign(p.btnDown.style,o.style["btnDown.mobile"])),n.disabled||(Object.assign(p.wrap,{onMouseUp:this.stop,onMouseLeave:this.stop}),Object.assign(p.btnUp,{onTouchStart:this.onTouchStart.bind(this,"up"),onTouchEnd:this.stop,onMouseEnter:function(){t.setState({btnUpHover:!0})},onMouseLeave:function(){t.stop(),t.setState({btnUpHover:!1,btnUpActive:!1})},onMouseUp:function(){t.setState({btnUpHover:!0,btnUpActive:!1})},onMouseDown:function(e){t.setState({btnUpHover:!0,btnUpActive:!0}),t.onMouseDown("up",e)}}),Object.assign(p.btnDown,{onTouchStart:this.onTouchStart.bind(this,"down"),onTouchEnd:this.stop,onMouseEnter:function(){t.setState({btnDownHover:!0})},onMouseLeave:function(){t.stop(),t.setState({btnDownHover:!1,btnDownActive:!1})},onMouseUp:function(){t.setState({btnDownHover:!0,btnDownActive:!1})},onMouseDown:function(e){t.setState({btnDownHover:!0,btnDownActive:!0}),t.onMouseDown("down",e)}}),Object.assign(p.input,{onChange:this._onChange.bind(this),onKeyDown:this._onKeyDown.bind(this),onFocus:function(){t.setState({inputFocus:!0})},onBlur:function(){t.setState({inputFocus:!1})}})),a?c["default"].createElement("span",p.wrap,c["default"].createElement("input",p.input),c["default"].createElement("b",p.btnUp,c["default"].createElement("i",{style:o.style.minus}),c["default"].createElement("i",{style:o.style.plus})),c["default"].createElement("b",p.btnDown,c["default"].createElement("i",{style:o.style.minus}))):c["default"].createElement("span",p.wrap,c["default"].createElement("input",p.input),c["default"].createElement("b",p.btnUp,c["default"].createElement("i",{style:o.style.arrowUp})),c["default"].createElement("b",p.btnDown,c["default"].createElement("i",{style:o.style.arrowDown})))}}]),e}(c["default"].Component);h.propTypes={step:b.number,min:b.number,max:b.number,precision:b.number,parse:b.func,format:b.func,className:b.string,disabled:b.bool,readOnly:b.bool,style:b.object,type:b.string,size:b.oneOfType([b.number,b.string]),value:b.oneOfType([b.number,b.string]),mobile:function(t,e){var n=t[e];return n!==!0&&n!==!1&&"auto"!==n&&"function"!=typeof n?new Error('The "mobile" prop must be true, false, "auto" or a function'):void 0}},h.defaultProps={value:0,step:1,min:Number.MIN_SAFE_INTEGER||-9007199254740991,max:Number.MAX_SAFE_INTEGER||9007199254740991,precision:0,parse:null,format:null,className:"",mobile:"auto",style:{}},h.style={wrap:{position:"relative",display:"inline-block"},"wrap.hasFormControl":{display:"block"},arrowUp:{position:"absolute",top:"50%",left:"50%",width:0,height:0,borderWidth:"0 0.6ex 0.6ex 0.6ex",borderColor:"transparent transparent rgba(0, 0, 0, 0.7)",borderStyle:"solid",margin:"-0.3ex 0 0 -0.56ex"},arrowDown:{position:"absolute",top:"50%",left:"50%",width:0,height:0,borderWidth:"0.6ex 0.6ex 0 0.6ex",borderColor:"rgba(0, 0, 0, 0.7) transparent transparent",borderStyle:"solid",margin:"-0.3ex 0 0 -0.56ex"},plus:{position:"absolute",top:"50%",left:"50%",width:2,height:10,background:"rgba(0,0,0,.7)",margin:"-5px 0 0 -1px"},minus:{position:"absolute",top:"50%",left:"50%",width:10,height:2,background:"rgba(0,0,0,.7)",margin:"-1px 0 0 -5px"},btn:{position:"absolute",right:2,width:"2.26ex",borderColor:"rgba(0,0,0,.1)",borderStyle:"solid",textAlign:"center",cursor:"default",transition:"all 0.1s",background:"rgba(0,0,0,.1)",boxShadow:"-1px -1px 3px rgba(0,0,0,.1) inset, 1px 1px 3px rgba(255,255,255,.7) inset"},btnUp:{top:2,bottom:"50%",borderRadius:"2px 2px 0 0",borderWidth:"1px 1px 0 1px"},"btnUp.mobile":{width:"3.3ex",bottom:2,boxShadow:"none",borderRadius:2,borderWidth:1},btnDown:{top:"50%",bottom:2,borderRadius:"0 0 2px 2px",borderWidth:"0 1px 1px 1px"},"btnDown.mobile":{width:"3.3ex",bottom:2,left:2,top:2,right:"auto",boxShadow:"none",borderRadius:2,borderWidth:1},"btn:hover":{background:"rgba(0,0,0,.2)"},"btn:active":{background:"rgba(0,0,0,.3)",boxShadow:"0 1px 3px rgba(0,0,0,.2) inset, -1px -1px 4px rgba(255,255,255,.5) inset"},"btn:disabled":{opacity:.5,boxShadow:"none",cursor:"not-allowed"},input:{paddingRight:"3ex",boxSizing:"border-box"},"input:not(.form-control)":{border:"1px solid #ccc",borderRadius:2,paddingLeft:4,display:"block",WebkitAppearance:"none",lineHeight:"normal"},"input.mobile":{paddingLeft:" 3.4ex",paddingRight:"3.4ex",textAlign:"center"},"input:focus":{}},h.SPEED=50,h.DELAY=500,e["default"]=h},function(e,n){e.exports=t}])}); | ||
| !function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e(require("React"));else if("function"==typeof define&&define.amd)define(["React"],e);else{var n=e("object"==typeof exports?require("React"):t.React);for(var o in n)("object"==typeof exports?exports:t)[o]=n[o]}}(this,function(t){return function(t){function e(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return t[o].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){var n={};for(var o in t)e.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(t,o)&&(n[o]=t[o]);return n}function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function r(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function u(t,e){return t.classList?t.classList.add(e):void(t.className.search(new RegExp("\\b"+e+"\\b"))||(t.className=" "+e))}function l(t,e){if(t.className){if(t.classList)return t.classList.remove(e);t.className=t.className.replace(new RegExp("\\b"+e+"\\b","g"),"")}}var p=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t},c=function(){function t(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(e,n,o){return n&&t(e.prototype,n),o&&t(e,o),e}}();Object.defineProperty(e,"__esModule",{value:!0}),e.NumericInput=void 0;var b=n(1),h=o(b),f=h["default"].PropTypes,d=38,v=40,m=e.NumericInput=function(t){function e(t){a(this,e);var n=s(this,Object.getPrototypeOf(e).call(this,t));return n._timer=null,n._valid=void 0,n.state={selectionStart:null,selectionEnd:null,value:"value"in t?t.value:t.defaultValue,btnDownHover:!1,btnDownActive:!1,btnUpHover:!1,btnUpActive:!1,inputFocus:!1},n.stop=n.stop.bind(n),n}return r(e,t),c(e,[{key:"componentWillReceiveProps",value:function(t){var e=String(t.value||0===t.value?t.value:"").replace(/^\s*|\s*$/,"");this.setState({value:"value"in t&&""!==e?this._parse(e):null})}},{key:"componentDidUpdate",value:function(t,e){e.value!=this.state.value&&this._invokeEventCallback("onChange",this.state.value,this.refs.input.value),this.state.inputFocus&&!e.inputFocus&&(this.refs.input.focus(),(this.state.selectionStart||0===this.state.selectionStart)&&(this.refs.input.selectionStart=this.state.selectionStart),(this.state.selectionEnd||0===this.state.selectionEnd)&&(this.refs.input.selectionEnd=this.state.selectionEnd)),this.state.inputFocus||document.activeElement!==this.refs.input||(this.state.inputFocus=!0),this.checkValidity()}},{key:"componentWillUnmount",value:function(){this.stop()}},{key:"componentDidMount",value:function(){var t=this;this.refs.input.getValueAsNumber=function(){return t.state.value||0},this.refs.input.setValue=function(e){t.setState({value:t._parse(e)})},this.checkValidity()}},{key:"checkValidity",value:function(){var t=void 0,e="",n=!!this.refs.input.checkValidity,o=!(!this.props.noValidate||"false"==this.props.noValidate);this.refs.input.noValidate=o,t=o||!n,t?e="":(""===this.refs.input.pattern&&(this.refs.input.pattern=this.props.required?".+":".*"),n&&(this.refs.input.checkValidity(),t=this.refs.input.validity.valid,t||(e=this.refs.input.validationMessage)),t&&n&&this.props.maxLength&&this.refs.input.value.length>this.props.maxLength&&(e="This value is too long")),e=e||(t?"":this.refs.input.validationMessage||"Unknown Error");var i=this._valid!==e;this._valid=e,e?(u(this.refs.wrapper,"has-error"),i&&this._invokeEventCallback("onInvalid",e,this.state.value,this.refs.input.value)):(l(this.refs.wrapper,"has-error"),i&&this._invokeEventCallback("onValid",this.state.value,this.refs.input.value))}},{key:"_toNumber",value:function(t){var e=parseFloat(t),n=Math.pow(10,this.props.precision);return(isNaN(e)||!isFinite(e))&&(e=0),e=Math.min(Math.max(e,this.props.min),this.props.max),e=Math.round(e*n)/n}},{key:"_parse",value:function(t){return"function"==typeof this.props.parse?parseFloat(this.props.parse(t)):parseFloat(t)}},{key:"_format",value:function(t){var e=this._toNumber(t).toFixed(this.props.precision);return this.props.format?this.props.format(e):e}},{key:"_step",value:function(t,e){var n=this._toNumber((this.state.value||0)+this.props.step*t);n!==this.state.value&&this.setState({value:n},e)}},{key:"_onChange",value:function(t){this.setState({value:this._parse(t.target.value)})}},{key:"_onKeyDown",value:function(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];this._invokeEventCallback.apply(this,["onKeyDown"].concat(e));var o=e[0];o.isDefaultPrevented()||(o.keyCode===d?(o.preventDefault(),this._step(o.ctrlKey||o.metaKey?.1:o.shiftKey?10:1)):o.keyCode===v&&(o.preventDefault(),this._step(o.ctrlKey||o.metaKey?-.1:o.shiftKey?-10:-1)))}},{key:"_onSelectionChange",value:function(t){var e=this;this.setState({selectionStart:this.refs.input.selectionStart,selectionEnd:this.refs.input.selectionEnd},function(){switch(t.type){case"input":e.props.onInput&&e.props.onInput.call(e.refs.input,t);break;case"select":e.props.onSelect&&e.props.onSelect.call(e.refs.input,t);break;case"selectstart":e.props.onSelectStart&&e.props.onSelectStart.call(e.refs.input,t)}})}},{key:"stop",value:function(){this._timer&&window.clearTimeout(this._timer)}},{key:"increase",value:function(t,n){var o=this;this.stop(),this._step(1,n),(isNaN(this.state.value)||this.state.value<this.props.max)&&(this._timer=setTimeout(function(){o.increase(!0)},t?e.SPEED:e.DELAY))}},{key:"decrease",value:function(t,n){var o=this;this.stop(),this._step(-1,n),(isNaN(this.state.value)||this.state.value>this.props.min)&&(this._timer=setTimeout(function(){o.decrease(!0)},t?e.SPEED:e.DELAY))}},{key:"onMouseDown",value:function(t,e){"down"==t?this.decrease(!1,e):"up"==t&&this.increase(!1,e)}},{key:"onTouchStart",value:function(t,e){e.preventDefault(),"down"==t?this.decrease():"up"==t&&this.increase()}},{key:"_invokeEventCallback",value:function(t){if("function"==typeof this.props[t]){for(var e,n=arguments.length,o=Array(n>1?n-1:0),i=1;n>i;i++)o[i-1]=arguments[i];(e=this.props[t]).call.apply(e,[null].concat(o))}}},{key:"render",value:function(){var t=this,n=this.props,o=this.state,a={};for(var s in e.style)a[s]=Object.assign({},e.style[s],n.style?n.style[s]||{}:{});var r=this.props,u=(r.step,r.min,r.max,r.precision,r.parse,r.format,r.value,r.type,r.style,r.defaultValue,i(r,["step","min","max","precision","parse","format","value","type","style","defaultValue"])),l=n.className&&/\bform-control\b/.test(n.className),c="auto"==n.mobile?"ontouchstart"in document:n.mobile;"function"==typeof c&&(c=c.call(this)),c=!!c;var b={wrap:{style:a.wrap,className:"react-numeric-input",ref:"wrapper"},input:p({ref:"input",type:"text",style:Object.assign({},a.input,l?{}:a["input:not(.form-control)"],o.inputFocus?a["input:focus"]:{})},u),btnUp:{style:Object.assign({},a.btn,a.btnUp,n.disabled?a["btn:disabled"]:o.btnUpActive?a["btn:active"]:o.btnUpHover?a["btn:hover"]:{})},btnDown:{style:Object.assign({},a.btn,a.btnDown,n.disabled?a["btn:disabled"]:o.btnDownActive?a["btn:active"]:o.btnDownHover?a["btn:hover"]:{})}};return(o.value||0===o.value)&&(b.input.value=this._format(o.value)),l&&Object.assign(b.wrap.style,a["wrap.hasFormControl"]),c&&(Object.assign(b.input.style,a["input.mobile"]),Object.assign(b.btnUp.style,a["btnUp.mobile"]),Object.assign(b.btnDown.style,a["btnDown.mobile"])),n.disabled?Object.assign(b.input.style,a["input:disabled"]):(Object.assign(b.wrap,{onMouseUp:this.stop,onMouseLeave:this.stop}),Object.assign(b.btnUp,{onTouchStart:this.onTouchStart.bind(this,"up"),onTouchEnd:this.stop,onMouseEnter:function(){t.setState({btnUpHover:!0})},onMouseLeave:function(){t.stop(),t.setState({btnUpHover:!1,btnUpActive:!1})},onMouseUp:function(){t.setState({btnUpHover:!0,btnUpActive:!1})},onMouseDown:function(){for(var e=arguments.length,n=Array(e),o=0;e>o;o++)n[o]=arguments[o];n[0].preventDefault(),t.setState({btnUpHover:!0,btnUpActive:!0,inputFocus:!0},function(){t._invokeEventCallback.apply(t,["onFocus"].concat(n))}),t.onMouseDown("up")}}),Object.assign(b.btnDown,{onTouchStart:this.onTouchStart.bind(this,"down"),onTouchEnd:this.stop,onMouseEnter:function(){t.setState({btnDownHover:!0})},onMouseLeave:function(){t.stop(),t.setState({btnDownHover:!1,btnDownActive:!1})},onMouseUp:function(){t.setState({btnDownHover:!0,btnDownActive:!1})},onMouseDown:function(){for(var e=arguments.length,n=Array(e),o=0;e>o;o++)n[o]=arguments[o];n[0].preventDefault(),t.setState({btnDownHover:!0,btnDownActive:!0,inputFocus:!0},function(){t._invokeEventCallback.apply(t,["onFocus"].concat(n))}),t.onMouseDown("down")}}),Object.assign(b.input,{onChange:this._onChange.bind(this),onKeyDown:this._onKeyDown.bind(this),onInput:this._onSelectionChange.bind(this),onSelect:this._onSelectionChange.bind(this),onSelectStart:this._onSelectionChange.bind(this),onFocus:function(){for(var e=arguments.length,n=Array(e),o=0;e>o;o++)n[o]=arguments[o];t.setState({inputFocus:!0},function(){t._invokeEventCallback.apply(t,["onFocus"].concat(n))})},onBlur:function(){for(var e=arguments.length,n=Array(e),o=0;e>o;o++)n[o]=arguments[o];t.setState({inputFocus:!1},function(){t._invokeEventCallback.apply(t,["onBlur"].concat(n))})}})),c?h["default"].createElement("span",b.wrap,h["default"].createElement("input",b.input),h["default"].createElement("b",b.btnUp,h["default"].createElement("i",{style:a.minus}),h["default"].createElement("i",{style:a.plus})),h["default"].createElement("b",b.btnDown,h["default"].createElement("i",{style:a.minus}))):h["default"].createElement("span",b.wrap,h["default"].createElement("input",b.input),h["default"].createElement("b",b.btnUp,h["default"].createElement("i",{style:a.arrowUp})),h["default"].createElement("b",b.btnDown,h["default"].createElement("i",{style:a.arrowDown})))}}]),e}(h["default"].Component);m.propTypes={step:f.number,min:f.number,max:f.number,precision:f.number,maxLength:f.number,parse:f.func,format:f.func,className:f.string,disabled:f.bool,readOnly:f.bool,required:f.bool,noValidate:f.oneOfType([f.bool,f.string]),style:f.object,type:f.string,pattern:f.string,onFocus:f.func,onBlur:f.func,onKeyDown:f.func,onChange:f.func,onInvalid:f.func,onValid:f.func,onInput:f.func,onSelect:f.func,onSelectStart:f.func,size:f.oneOfType([f.number,f.string]),value:f.oneOfType([f.number,f.string]),defaultValue:f.oneOfType([f.number,f.string]),mobile:function(t,e){var n=t[e];return n!==!0&&n!==!1&&"auto"!==n&&"function"!=typeof n?new Error('The "mobile" prop must be true, false, "auto" or a function'):void 0}},m.defaultProps={step:1,min:Number.MIN_SAFE_INTEGER||-9007199254740991,max:Number.MAX_SAFE_INTEGER||9007199254740991,precision:0,parse:null,format:null,mobile:"auto",style:{}},m.style={wrap:{position:"relative",display:"inline-block"},"wrap.hasFormControl":{display:"block"},arrowUp:{position:"absolute",top:"50%",left:"50%",width:0,height:0,borderWidth:"0 0.6ex 0.6ex 0.6ex",borderColor:"transparent transparent rgba(0, 0, 0, 0.7)",borderStyle:"solid",margin:"-0.3ex 0 0 -0.56ex"},arrowDown:{position:"absolute",top:"50%",left:"50%",width:0,height:0,borderWidth:"0.6ex 0.6ex 0 0.6ex",borderColor:"rgba(0, 0, 0, 0.7) transparent transparent",borderStyle:"solid",margin:"-0.3ex 0 0 -0.56ex"},plus:{position:"absolute",top:"50%",left:"50%",width:2,height:10,background:"rgba(0,0,0,.7)",margin:"-5px 0 0 -1px"},minus:{position:"absolute",top:"50%",left:"50%",width:10,height:2,background:"rgba(0,0,0,.7)",margin:"-1px 0 0 -5px"},btn:{position:"absolute",right:2,width:"2.26ex",borderColor:"rgba(0,0,0,.1)",borderStyle:"solid",textAlign:"center",cursor:"default",transition:"all 0.1s",background:"rgba(0,0,0,.1)",boxShadow:"-1px -1px 3px rgba(0,0,0,.1) inset,\n 1px 1px 3px rgba(255,255,255,.7) inset"},btnUp:{top:2,bottom:"50%",borderRadius:"2px 2px 0 0",borderWidth:"1px 1px 0 1px"},"btnUp.mobile":{width:"3.3ex",bottom:2,boxShadow:"none",borderRadius:2,borderWidth:1},btnDown:{top:"50%",bottom:2,borderRadius:"0 0 2px 2px",borderWidth:"0 1px 1px 1px"},"btnDown.mobile":{width:"3.3ex",bottom:2,left:2,top:2,right:"auto",boxShadow:"none",borderRadius:2,borderWidth:1},"btn:hover":{background:"rgba(0,0,0,.2)"},"btn:active":{background:"rgba(0,0,0,.3)",boxShadow:"0 1px 3px rgba(0,0,0,.2) inset,\n -1px -1px 4px rgba(255,255,255,.5) inset"},"btn:disabled":{opacity:.5,boxShadow:"none",cursor:"not-allowed"},input:{paddingRight:"3ex",boxSizing:"border-box"},"input:not(.form-control)":{border:"1px solid #ccc",borderRadius:2,paddingLeft:4,display:"block",WebkitAppearance:"none",lineHeight:"normal"},"input.mobile":{paddingLeft:" 3.4ex",paddingRight:"3.4ex",textAlign:"center"},"input:focus":{},"input:disabled":{color:"rgba(0, 0, 0, 0.3)",textShadow:"0 1px 0 rgba(255, 255, 255, 0.8)"}},m.SPEED=50,m.DELAY=500,e["default"]=m},function(e,n){e.exports=t}])}); |
+309
-2
@@ -67,2 +67,6 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
| var _Demo = __webpack_require__(3); | ||
| var _Demo2 = _interopRequireDefault(_Demo); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -72,3 +76,2 @@ | ||
| /* global $, hljs, NumericInput */ | ||
| $(function () { | ||
@@ -83,2 +86,4 @@ $('script.jsx').each(function (i, s) { | ||
| _reactDom2.default.render(_react2.default.createElement(_Demo2.default, null), $('.demo')[0]); | ||
| hljs.configure({ useBR: false }); | ||
@@ -89,3 +94,3 @@ | ||
| }); | ||
| }); | ||
| }); /* global $, hljs, NumericInput */ | ||
@@ -104,2 +109,304 @@ /***/ }, | ||
| /***/ }, | ||
| /* 3 */ | ||
| /***/ function(module, exports) { | ||
| "use strict"; | ||
| var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
| var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } | ||
| function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
| function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
| function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
| var /* global $, hljs, NumericInput, React */ | ||
| Demo = function (_React$Component) { | ||
| _inherits(Demo, _React$Component); | ||
| function Demo() { | ||
| var _Object$getPrototypeO; | ||
| _classCallCheck(this, Demo); | ||
| for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
| args[_key] = arguments[_key]; | ||
| } | ||
| // var that = this; | ||
| var _this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Demo)).call.apply(_Object$getPrototypeO, [this].concat(args))); | ||
| _this.state = { | ||
| inputProps: { | ||
| name: { value: "whatever", on: false }, | ||
| className: { value: "form-control", on: true }, | ||
| value: { value: 50, on: true }, | ||
| min: { value: 0, on: true }, | ||
| max: { value: 100, on: true }, | ||
| precision: { value: 0, on: true }, | ||
| size: { value: 5, on: true }, | ||
| maxLength: { value: 2, on: false }, | ||
| disabled: { value: true, on: false }, | ||
| readOnly: { value: true, on: false }, | ||
| mobile: { value: true, on: false }, | ||
| required: { value: true, on: false }, | ||
| noValidate: { value: true, on: false }, | ||
| pattern: { value: "[0-9].[0-9][0-9]", on: false }, | ||
| title: { value: "The title attr", on: false } | ||
| } | ||
| }; | ||
| return _this; | ||
| } | ||
| _createClass(Demo, [{ | ||
| key: "componentDidUpdate", | ||
| value: function componentDidUpdate() { | ||
| hljs.highlightBlock(this.refs.code); | ||
| } | ||
| }, { | ||
| key: "toggleProp", | ||
| value: function toggleProp(propName) { | ||
| this.state.inputProps[propName].on = !this.state.inputProps[propName].on; | ||
| this.setState(this.state); | ||
| } | ||
| }, { | ||
| key: "setProp", | ||
| value: function setProp(propName, event) { | ||
| var val = event.target ? event.target.value : event; | ||
| this.state.inputProps[propName].value = val; | ||
| this.setState(this.state); | ||
| } | ||
| }, { | ||
| key: "onChange", | ||
| value: function onChange(x) { | ||
| this.state.inputProps.value.value = x; | ||
| if (this.state.inputProps.value.on) { | ||
| this.setState(this.state); | ||
| } | ||
| } | ||
| }, { | ||
| key: "onInvalid", | ||
| value: function onInvalid(message) { | ||
| // console.log("Invalid", message) | ||
| $(this.refs.errorMessage).text(message || "Unknown error"); | ||
| } | ||
| }, { | ||
| key: "onValid", | ||
| value: function onValid() { | ||
| // console.log("Valid") | ||
| $(this.refs.errorMessage).empty(); | ||
| } | ||
| }, { | ||
| key: "renderCode", | ||
| value: function renderCode() { | ||
| var out = '<NumericInput '; | ||
| var hasProps = false; | ||
| for (var propName in this.state.inputProps) { | ||
| if (this.state.inputProps[propName].on && !this.state.inputProps[propName].hidden) { | ||
| var val = this.state.inputProps[propName].value; | ||
| out += "\n\t" + propName; | ||
| if (val !== true) { | ||
| out += '=' + (typeof val == 'string' ? "\"" + val + "\" " : "{ " + val + " } "); | ||
| } | ||
| hasProps = true; | ||
| } | ||
| } | ||
| if (hasProps) { | ||
| out += '\n'; | ||
| } | ||
| out += '/>'; | ||
| return React.createElement( | ||
| "div", | ||
| { className: "code js", ref: "code", style: { minHeight: 379 } }, | ||
| out | ||
| ); | ||
| } | ||
| }, { | ||
| key: "renderPropEditors", | ||
| value: function renderPropEditors(config) { | ||
| var _this2 = this; | ||
| return config.map(function (props, propName) { | ||
| var editor = null; | ||
| var type = props.type; | ||
| var name = props.name; | ||
| var rest = _objectWithoutProperties(props, ["type", "name"]); | ||
| if (type == 'text') { | ||
| editor = React.createElement("input", { | ||
| type: "text", | ||
| className: "form-control input-sm", | ||
| value: _this2.state.inputProps[name].value, | ||
| onChange: _this2.setProp.bind(_this2, name) | ||
| }); | ||
| } else if (type == "number") { | ||
| editor = React.createElement(NumericInput, _extends({ | ||
| className: "form-control input-sm", | ||
| value: _this2.state.inputProps[name].value, | ||
| onChange: _this2.setProp.bind(_this2, name) | ||
| }, rest)); | ||
| } | ||
| return React.createElement( | ||
| "tr", | ||
| { key: propName }, | ||
| React.createElement( | ||
| "td", | ||
| { className: "unselectable" }, | ||
| React.createElement( | ||
| "label", | ||
| { style: { display: "block" } }, | ||
| React.createElement("input", { | ||
| type: "checkbox", | ||
| checked: _this2.state.inputProps[name].on, | ||
| onChange: _this2.toggleProp.bind(_this2, name) | ||
| }), | ||
| " ", | ||
| name | ||
| ) | ||
| ), | ||
| React.createElement( | ||
| "td", | ||
| null, | ||
| editor | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| }, { | ||
| key: "render", | ||
| value: function render() { | ||
| var inputProps = {}; | ||
| for (var propName in this.state.inputProps) { | ||
| if (this.state.inputProps[propName].on) { | ||
| inputProps[propName] = this.state.inputProps[propName].value; | ||
| } | ||
| // else { | ||
| // inputProps[propName] = null | ||
| // } | ||
| } | ||
| return React.createElement( | ||
| "div", | ||
| { className: "row" }, | ||
| React.createElement( | ||
| "div", | ||
| { className: "col-xs-6" }, | ||
| React.createElement( | ||
| "div", | ||
| { className: "panel panel-default" }, | ||
| React.createElement( | ||
| "div", | ||
| { className: "panel-heading" }, | ||
| "Props" | ||
| ), | ||
| React.createElement( | ||
| "table", | ||
| { className: "table table-striped table-condensed" }, | ||
| React.createElement( | ||
| "colgroup", | ||
| null, | ||
| React.createElement("col", { width: 169 }), | ||
| React.createElement("col", null) | ||
| ), | ||
| React.createElement( | ||
| "thead", | ||
| null, | ||
| React.createElement( | ||
| "tr", | ||
| null, | ||
| React.createElement( | ||
| "th", | ||
| null, | ||
| "name" | ||
| ), | ||
| React.createElement( | ||
| "th", | ||
| null, | ||
| "value" | ||
| ) | ||
| ) | ||
| ) | ||
| ), | ||
| React.createElement( | ||
| "div", | ||
| { style: { | ||
| overflow: 'auto', | ||
| maxHeight: 452 | ||
| } }, | ||
| React.createElement( | ||
| "table", | ||
| { className: "table table-striped table-condensed" }, | ||
| React.createElement( | ||
| "colgroup", | ||
| null, | ||
| React.createElement("col", { width: 169 }), | ||
| React.createElement("col", null) | ||
| ), | ||
| React.createElement( | ||
| "tbody", | ||
| null, | ||
| this.renderPropEditors([{ name: "name", type: "text" }, { name: "className", type: "text" }, { name: "value", type: "text" }, { name: "min", type: "number" }, { name: "max", type: "number" }, { name: "precision", type: "number", min: 0, max: 20 }, { name: "size", type: "number", min: 0, max: 60 }, { name: "maxLength", type: "number", min: 0, max: 20 }, { name: "disabled", type: "bool" }, { name: "readOnly", type: "bool" }, { name: "mobile", type: "bool" }, { name: "pattern", type: "text" }, { name: "title", type: "text" }, { name: "required", type: "bool" }, { name: "noValidate", type: "bool" }]) | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| ), | ||
| React.createElement( | ||
| "div", | ||
| { className: "col-xs-6" }, | ||
| React.createElement( | ||
| "div", | ||
| { className: "panel panel-primary" }, | ||
| React.createElement( | ||
| "div", | ||
| { className: "panel-heading" }, | ||
| "Preview" | ||
| ), | ||
| React.createElement( | ||
| "div", | ||
| { className: "panel-body" }, | ||
| React.createElement( | ||
| "div", | ||
| { ref: "example" }, | ||
| React.createElement(NumericInput, _extends({}, inputProps, { | ||
| onChange: this.onChange.bind(this), | ||
| onInvalid: this.onInvalid.bind(this), | ||
| onValid: this.onValid.bind(this) | ||
| })), | ||
| React.createElement( | ||
| "div", | ||
| { className: "help-block" }, | ||
| React.createElement("span", { ref: "errorMessage", className: "text-danger" }) | ||
| ) | ||
| ), | ||
| React.createElement("hr", null), | ||
| this.renderCode() | ||
| ) | ||
| ) | ||
| ) | ||
| ); | ||
| } | ||
| }]); | ||
| return Demo; | ||
| }(React.Component); | ||
| exports.default = Demo; | ||
| /***/ } | ||
@@ -106,0 +413,0 @@ /******/ ]) |
| /* global $, hljs, NumericInput */ | ||
| import React from "react" | ||
| import React from "react" | ||
| import ReactDOM from "react-dom" | ||
| import Demo from "./Demo" | ||
| // import NumericInput from '../index.js'; | ||
@@ -19,2 +20,4 @@ | ||
| ReactDOM.render(<Demo/>, $('.demo')[0]); | ||
| hljs.configure({ useBR : false }); | ||
@@ -21,0 +24,0 @@ |
+31
-13
@@ -13,3 +13,3 @@ <!DOCTYPE html> | ||
| font-family: Menlo, monospace; | ||
| font-size : 1.3rem !important; | ||
| font-size : 1.4rem !important; | ||
| white-space: pre-wrap; | ||
@@ -26,2 +26,11 @@ } | ||
| } | ||
| .demo .table > tbody > tr > th, | ||
| .demo .table > tbody > tr > td { | ||
| vertical-align: middle; | ||
| } | ||
| .unselectable { | ||
| -webkit-user-select: none; | ||
| -moz-user-select: none; | ||
| user-select: none; | ||
| } | ||
| </style> | ||
@@ -40,3 +49,3 @@ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> | ||
| <p> | ||
| This will behave exactly like <code><input type="number"></code>. | ||
| This will behave exactly like <code><input type="number"></code>. | ||
| It will create an empty numeric input that starts changing from zero. | ||
@@ -47,3 +56,3 @@ The difference is that this works on any browser and does have the same | ||
| <blockquote> | ||
| <div class="code jsx"><NumericInput/></div> | ||
| <div class="code jsx"><NumericInput/></div> | ||
| <script class="jsx" type="text/plain">{}</script> | ||
@@ -61,3 +70,3 @@ </blockquote> | ||
| <blockquote> | ||
| <div class="code jsx"><NumericInput className="form-control"/></div> | ||
| <div class="code jsx"><NumericInput className="form-control"/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -76,3 +85,3 @@ <script class="jsx" type="text/plain">{"className": "form-control"}</script> | ||
| <blockquote> | ||
| <div class="code jsx"><NumericInput min={0} max={100} value={50}/></div> | ||
| <div class="code jsx"><NumericInput min={0} max={100} value={50}/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -93,3 +102,3 @@ <script class="jsx" type="text/plain"> | ||
| <blockquote> | ||
| <div class="code jsx"><NumericInput step={0.1} precision={2} value={50.3}/></div> | ||
| <div class="code jsx"><NumericInput step={0.1} precision={2} value={50.3}/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -115,3 +124,3 @@ <script class="jsx" type="text/plain"> | ||
| }</div> | ||
| <div class="code jsx"><NumericInput precision={2} value={50.3} step={0.1} format={myFormat}/></div> | ||
| <div class="code jsx"><NumericInput precision={2} value={50.3} step={0.1} format={myFormat}/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -136,3 +145,3 @@ <script class="jsx" type="text/plain"> | ||
| <blockquote> | ||
| <div class="code jsx"><NumericInput disabled value={23.45}/></div> | ||
| <div class="code jsx"><NumericInput disabled value={23.45}/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -149,3 +158,3 @@ <script class="jsx" type="text/plain"> | ||
| <blockquote> | ||
| <div class="code jsx"><NumericInput readOnly value={23.45}/></div> | ||
| <div class="code jsx"><NumericInput readOnly value={23.45}/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -200,3 +209,3 @@ <script class="jsx" type="text/plain"> | ||
| } | ||
| }}/></div> | ||
| }}/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -266,3 +275,3 @@ <script class="jsx" type="text/plain"> | ||
| spellcheck="false" | ||
| tabindex="5"/></div> | ||
| tabindex="5"/></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -303,3 +312,3 @@ <script class="jsx" type="text/plain"> | ||
| <blockquote> | ||
| <div class="code xml"><NumericInput mobile /></div> | ||
| <div class="code xml"><NumericInput mobile /></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -310,3 +319,3 @@ <script class="jsx" type="text/plain">{ mobile: true }</script> | ||
| <br/> | ||
| <div class="code xml"><NumericInput mobile className="form-control" /></div> | ||
| <div class="code xml"><NumericInput mobile className="form-control" /></div> | ||
| <div class="col-sm-5 col-md-4 col-lg-3"> | ||
@@ -337,2 +346,11 @@ <script class="jsx" type="text/plain">{ mobile: true, className: "form-control" }</script> | ||
| </fieldset> | ||
| <fieldset> | ||
| <legend class="text-success">Interactive Demo</legend> | ||
| <div class="row"> | ||
| <div class="col-xs-12 demo"></div> | ||
| </div> | ||
| </fieldset> | ||
| <br/> | ||
| <br/> | ||
| </div> | ||
@@ -339,0 +357,0 @@ <script src="examples.js"></script> |
+312
-99
@@ -48,3 +48,3 @@ module.exports = | ||
| 'use strict'; | ||
| "use strict"; | ||
@@ -78,2 +78,21 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
| function addClass(element, className) { | ||
| if (element.classList) { | ||
| return element.classList.add(className); | ||
| } | ||
| if (!element.className.search(new RegExp("\\b" + className + "\\b"))) { | ||
| element.className = " " + className; | ||
| } | ||
| } | ||
| function removeClass(element, className) { | ||
| if (element.className) { | ||
| if (element.classList) { | ||
| return element.classList.remove(className); | ||
| } | ||
| element.className = element.className.replace(new RegExp("\\b" + className + "\\b", "g"), ""); | ||
| } | ||
| } | ||
| var NumericInput = exports.NumericInput = function (_React$Component) { | ||
@@ -88,15 +107,15 @@ _inherits(NumericInput, _React$Component); | ||
| _this._timer = null; | ||
| _this._valid = undefined; | ||
| _this.state = { | ||
| step: props.step, | ||
| min: props.min, | ||
| max: props.max, | ||
| style: {}, | ||
| value: 'value' in props ? _this._parse(String(props.value || '')) : null | ||
| selectionStart: null, | ||
| selectionEnd: null, | ||
| value: "value" in props ? props.value : props.defaultValue, | ||
| btnDownHover: false, | ||
| btnDownActive: false, | ||
| btnUpHover: false, | ||
| btnUpActive: false, | ||
| inputFocus: false | ||
| }; | ||
| for (var x in NumericInput.style) { | ||
| _this.state.style[x] = Object.assign({}, NumericInput.style[x], props.style[x] || {}); | ||
| } | ||
| _this.stop = _this.stop.bind(_this); | ||
@@ -107,3 +126,37 @@ return _this; | ||
| _createClass(NumericInput, [{ | ||
| key: 'componentWillUnmount', | ||
| key: "componentWillReceiveProps", | ||
| value: function componentWillReceiveProps(props) { | ||
| var _value = String(props.value || props.value === 0 ? props.value : '').replace(/^\s*|\s*$/, ""); | ||
| this.setState({ | ||
| value: "value" in props && _value !== '' ? this._parse(_value) : null | ||
| }); | ||
| } | ||
| }, { | ||
| key: "componentDidUpdate", | ||
| value: function componentDidUpdate(prevProps, prevState) { | ||
| if (prevState.value != this.state.value) { | ||
| this._invokeEventCallback("onChange", this.state.value, this.refs.input.value); | ||
| } | ||
| if (this.state.inputFocus && !prevState.inputFocus) { | ||
| this.refs.input.focus(); | ||
| if (this.state.selectionStart || this.state.selectionStart === 0) { | ||
| this.refs.input.selectionStart = this.state.selectionStart; | ||
| } | ||
| if (this.state.selectionEnd || this.state.selectionEnd === 0) { | ||
| this.refs.input.selectionEnd = this.state.selectionEnd; | ||
| } | ||
| } | ||
| if (!this.state.inputFocus && document.activeElement === this.refs.input) { | ||
| this.state.inputFocus = true; | ||
| } | ||
| this.checkValidity(); | ||
| } | ||
| }, { | ||
| key: "componentWillUnmount", | ||
| value: function componentWillUnmount() { | ||
@@ -113,3 +166,3 @@ this.stop(); | ||
| }, { | ||
| key: 'componentDidMount', | ||
| key: "componentDidMount", | ||
| value: function componentDidMount() { | ||
@@ -127,5 +180,60 @@ var _this2 = this; | ||
| }; | ||
| this.checkValidity(); | ||
| } | ||
| }, { | ||
| key: '_toNumber', | ||
| key: "checkValidity", | ||
| value: function checkValidity() { | ||
| var valid = undefined, | ||
| validationError = ""; | ||
| var supportsValidation = !!this.refs.input.checkValidity; | ||
| var noValidate = !!(this.props.noValidate && this.props.noValidate != "false"); | ||
| this.refs.input.noValidate = noValidate; | ||
| valid = noValidate || !supportsValidation; | ||
| if (valid) { | ||
| validationError = ""; | ||
| } else { | ||
| if (this.refs.input.pattern === "") { | ||
| this.refs.input.pattern = this.props.required ? ".+" : ".*"; | ||
| } | ||
| if (supportsValidation) { | ||
| this.refs.input.checkValidity(); | ||
| valid = this.refs.input.validity.valid; | ||
| if (!valid) { | ||
| validationError = this.refs.input.validationMessage; | ||
| } | ||
| } | ||
| if (valid && supportsValidation && this.props.maxLength) { | ||
| if (this.refs.input.value.length > this.props.maxLength) { | ||
| validationError = "This value is too long"; | ||
| } | ||
| } | ||
| } | ||
| validationError = validationError || (valid ? "" : this.refs.input.validationMessage || "Unknown Error"); | ||
| var validStateChanged = this._valid !== validationError; | ||
| this._valid = validationError; | ||
| if (validationError) { | ||
| addClass(this.refs.wrapper, "has-error"); | ||
| if (validStateChanged) { | ||
| this._invokeEventCallback("onInvalid", validationError, this.state.value, this.refs.input.value); | ||
| } | ||
| } else { | ||
| removeClass(this.refs.wrapper, "has-error"); | ||
| if (validStateChanged) { | ||
| this._invokeEventCallback("onValid", this.state.value, this.refs.input.value); | ||
| } | ||
| } | ||
| } | ||
| }, { | ||
| key: "_toNumber", | ||
| value: function _toNumber(x) { | ||
@@ -144,3 +252,3 @@ var n = parseFloat(x); | ||
| }, { | ||
| key: '_parse', | ||
| key: "_parse", | ||
| value: function _parse(x) { | ||
@@ -153,3 +261,3 @@ if (typeof this.props.parse == 'function') { | ||
| }, { | ||
| key: '_format', | ||
| key: "_format", | ||
| value: function _format(n) { | ||
@@ -165,12 +273,12 @@ var _n = this._toNumber(n).toFixed(this.props.precision); | ||
| }, { | ||
| key: '_step', | ||
| value: function _step(n) { | ||
| var _n = this._toNumber((this.state.value || 0) + this.state.step * n); | ||
| key: "_step", | ||
| value: function _step(n, callback) { | ||
| var _n = this._toNumber((this.state.value || 0) + this.props.step * n); | ||
| if (_n !== this.state.value) { | ||
| this.setState({ value: _n }); | ||
| this.setState({ value: _n }, callback); | ||
| } | ||
| } | ||
| }, { | ||
| key: '_onChange', | ||
| key: "_onChange", | ||
| value: function _onChange(e) { | ||
@@ -182,14 +290,50 @@ this.setState({ | ||
| }, { | ||
| key: '_onKeyDown', | ||
| value: function _onKeyDown(e) { | ||
| if (e.keyCode === KEYCODE_UP) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? 0.1 : e.shiftKey ? 10 : 1); | ||
| } else if (e.keyCode === KEYCODE_DOWN) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? -0.1 : e.shiftKey ? -10 : -1); | ||
| key: "_onKeyDown", | ||
| value: function _onKeyDown() { | ||
| for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
| args[_key] = arguments[_key]; | ||
| } | ||
| this._invokeEventCallback.apply(this, ["onKeyDown"].concat(args)); | ||
| var e = args[0]; | ||
| if (!e.isDefaultPrevented()) { | ||
| if (e.keyCode === KEYCODE_UP) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? 0.1 : e.shiftKey ? 10 : 1); | ||
| } else if (e.keyCode === KEYCODE_DOWN) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? -0.1 : e.shiftKey ? -10 : -1); | ||
| } | ||
| } | ||
| } | ||
| }, { | ||
| key: 'stop', | ||
| key: "_onSelectionChange", | ||
| value: function _onSelectionChange(e) { | ||
| var _this3 = this; | ||
| this.setState({ | ||
| selectionStart: this.refs.input.selectionStart, | ||
| selectionEnd: this.refs.input.selectionEnd | ||
| }, function () { | ||
| switch (e.type) { | ||
| case "input": | ||
| if (_this3.props.onInput) { | ||
| _this3.props.onInput.call(_this3.refs.input, e); | ||
| } | ||
| break; | ||
| case "select": | ||
| if (_this3.props.onSelect) { | ||
| _this3.props.onSelect.call(_this3.refs.input, e); | ||
| } | ||
| break; | ||
| case "selectstart": | ||
| if (_this3.props.onSelectStart) { | ||
| _this3.props.onSelectStart.call(_this3.refs.input, e); | ||
| } | ||
| break; | ||
| } | ||
| }); | ||
| } | ||
| }, { | ||
| key: "stop", | ||
| value: function stop() { | ||
@@ -201,11 +345,11 @@ if (this._timer) { | ||
| }, { | ||
| key: 'increase', | ||
| value: function increase(_recursive) { | ||
| var _this3 = this; | ||
| key: "increase", | ||
| value: function increase(_recursive, callback) { | ||
| var _this4 = this; | ||
| this.stop(); | ||
| this._step(1); | ||
| this._step(1, callback); | ||
| if (isNaN(this.state.value) || this.state.value < this.props.max) { | ||
| this._timer = setTimeout(function () { | ||
| _this3.increase(true); | ||
| _this4.increase(true); | ||
| }, _recursive ? NumericInput.SPEED : NumericInput.DELAY); | ||
@@ -215,11 +359,11 @@ } | ||
| }, { | ||
| key: 'decrease', | ||
| value: function decrease(_recursive) { | ||
| var _this4 = this; | ||
| key: "decrease", | ||
| value: function decrease(_recursive, callback) { | ||
| var _this5 = this; | ||
| this.stop(); | ||
| this._step(-1); | ||
| this._step(-1, callback); | ||
| if (isNaN(this.state.value) || this.state.value > this.props.min) { | ||
| this._timer = setTimeout(function () { | ||
| _this4.decrease(true); | ||
| _this5.decrease(true); | ||
| }, _recursive ? NumericInput.SPEED : NumericInput.DELAY); | ||
@@ -229,18 +373,12 @@ } | ||
| }, { | ||
| key: 'onMouseDown', | ||
| value: function onMouseDown(dir, e) { | ||
| var _this5 = this; | ||
| e.preventDefault(); | ||
| key: "onMouseDown", | ||
| value: function onMouseDown(dir, callback) { | ||
| if (dir == 'down') { | ||
| this.decrease(); | ||
| this.decrease(false, callback); | ||
| } else if (dir == 'up') { | ||
| this.increase(); | ||
| this.increase(false, callback); | ||
| } | ||
| setTimeout(function () { | ||
| _this5.refs.input.focus(); | ||
| }); | ||
| } | ||
| }, { | ||
| key: 'onTouchStart', | ||
| key: "onTouchStart", | ||
| value: function onTouchStart(dir, e) { | ||
@@ -255,3 +393,16 @@ e.preventDefault(); | ||
| }, { | ||
| key: 'render', | ||
| key: "_invokeEventCallback", | ||
| value: function _invokeEventCallback(callbackName) { | ||
| if (typeof this.props[callbackName] == "function") { | ||
| var _props$callbackName; | ||
| for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | ||
| args[_key2 - 1] = arguments[_key2]; | ||
| } | ||
| (_props$callbackName = this.props[callbackName]).call.apply(_props$callbackName, [null].concat(args)); | ||
| } | ||
| } | ||
| }, { | ||
| key: "render", | ||
| value: function render() { | ||
@@ -262,14 +413,22 @@ var _this6 = this; | ||
| var state = this.state; | ||
| var step = props.step; | ||
| var min = props.min; | ||
| var max = props.max; | ||
| var precision = props.precision; | ||
| var parse = props.parse; | ||
| var format = props.format; | ||
| var value = props.value; | ||
| var type = props.type; | ||
| var style = props.style; | ||
| var css = {}; | ||
| var rest = _objectWithoutProperties(props, ['step', 'min', 'max', 'precision', 'parse', 'format', 'value', 'type', 'style']); | ||
| for (var x in NumericInput.style) { | ||
| css[x] = Object.assign({}, NumericInput.style[x], props.style ? props.style[x] || {} : {}); | ||
| } | ||
| var _props = this.props; | ||
| var step = _props.step; | ||
| var min = _props.min; | ||
| var max = _props.max; | ||
| var precision = _props.precision; | ||
| var parse = _props.parse; | ||
| var format = _props.format; | ||
| var value = _props.value; | ||
| var type = _props.type; | ||
| var style = _props.style; | ||
| var defaultValue = _props.defaultValue; | ||
| var rest = _objectWithoutProperties(_props, ["step", "min", "max", "precision", "parse", "format", "value", "type", "style", "defaultValue"]); | ||
| var hasFormControl = props.className && /\bform-control\b/.test(props.className); | ||
@@ -285,4 +444,5 @@ | ||
| wrap: { | ||
| style: Object.assign({}, NumericInput.style.wrap, props.style.wrap), | ||
| className: 'react-numeric-input' | ||
| style: css.wrap, | ||
| className: 'react-numeric-input', | ||
| ref: 'wrapper' | ||
| }, | ||
@@ -292,21 +452,24 @@ input: _extends({ | ||
| type: 'text', | ||
| style: Object.assign({}, state.style.input, !hasFormControl ? state.style['input:not(.form-control)'] : {}, state.inputFocus ? state.style['input:focus'] : {}), | ||
| value: state.value || state.value === 0 ? this._format(state.value) : '' | ||
| style: Object.assign({}, css.input, !hasFormControl ? css['input:not(.form-control)'] : {}, state.inputFocus ? css['input:focus'] : {}) | ||
| }, rest), | ||
| btnUp: { | ||
| style: Object.assign({}, state.style.btn, state.style.btnUp, props.disabled ? state.style['btn:disabled'] : state.btnUpActive ? state.style['btn:active'] : state.btnUpHover ? state.style['btn:hover'] : {}) | ||
| style: Object.assign({}, css.btn, css.btnUp, props.disabled ? css['btn:disabled'] : state.btnUpActive ? css['btn:active'] : state.btnUpHover ? css['btn:hover'] : {}) | ||
| }, | ||
| btnDown: { | ||
| style: Object.assign({}, state.style.btn, state.style.btnDown, props.disabled ? state.style['btn:disabled'] : state.btnDownActive ? state.style['btn:active'] : state.btnDownHover ? state.style['btn:hover'] : {}) | ||
| style: Object.assign({}, css.btn, css.btnDown, props.disabled ? css['btn:disabled'] : state.btnDownActive ? css['btn:active'] : state.btnDownHover ? css['btn:hover'] : {}) | ||
| } | ||
| }; | ||
| if (state.value || state.value === 0) { | ||
| attrs.input.value = this._format(state.value); | ||
| } | ||
| if (hasFormControl) { | ||
| Object.assign(attrs.wrap.style, state.style['wrap.hasFormControl']); | ||
| Object.assign(attrs.wrap.style, css['wrap.hasFormControl']); | ||
| } | ||
| if (mobile) { | ||
| Object.assign(attrs.input.style, state.style['input.mobile']); | ||
| Object.assign(attrs.btnUp.style, state.style['btnUp.mobile']); | ||
| Object.assign(attrs.btnDown.style, state.style['btnDown.mobile']); | ||
| Object.assign(attrs.input.style, css['input.mobile']); | ||
| Object.assign(attrs.btnUp.style, css['btnUp.mobile']); | ||
| Object.assign(attrs.btnDown.style, css['btnDown.mobile']); | ||
| } | ||
@@ -341,8 +504,16 @@ | ||
| }, | ||
| onMouseDown: function onMouseDown(e) { | ||
| onMouseDown: function onMouseDown() { | ||
| for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
| args[_key3] = arguments[_key3]; | ||
| } | ||
| args[0].preventDefault(); | ||
| _this6.setState({ | ||
| btnUpHover: true, | ||
| btnUpActive: true | ||
| btnUpActive: true, | ||
| inputFocus: true | ||
| }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onFocus"].concat(args)); | ||
| }); | ||
| _this6.onMouseDown('up', e); | ||
| _this6.onMouseDown('up'); | ||
| } | ||
@@ -372,8 +543,16 @@ }); | ||
| }, | ||
| onMouseDown: function onMouseDown(e) { | ||
| onMouseDown: function onMouseDown() { | ||
| for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { | ||
| args[_key4] = arguments[_key4]; | ||
| } | ||
| args[0].preventDefault(); | ||
| _this6.setState({ | ||
| btnDownHover: true, | ||
| btnDownActive: true | ||
| btnDownActive: true, | ||
| inputFocus: true | ||
| }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onFocus"].concat(args)); | ||
| }); | ||
| _this6.onMouseDown('down', e); | ||
| _this6.onMouseDown('down'); | ||
| } | ||
@@ -385,9 +564,26 @@ }); | ||
| onKeyDown: this._onKeyDown.bind(this), | ||
| onInput: this._onSelectionChange.bind(this), | ||
| onSelect: this._onSelectionChange.bind(this), | ||
| onSelectStart: this._onSelectionChange.bind(this), | ||
| onFocus: function onFocus() { | ||
| _this6.setState({ inputFocus: true }); | ||
| for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { | ||
| args[_key5] = arguments[_key5]; | ||
| } | ||
| _this6.setState({ inputFocus: true }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onFocus"].concat(args)); | ||
| }); | ||
| }, | ||
| onBlur: function onBlur() { | ||
| _this6.setState({ inputFocus: false }); | ||
| for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { | ||
| args[_key6] = arguments[_key6]; | ||
| } | ||
| _this6.setState({ inputFocus: false }, function () { | ||
| _this6._invokeEventCallback.apply(_this6, ["onBlur"].concat(args)); | ||
| }); | ||
| } | ||
| }); | ||
| } else { | ||
| Object.assign(attrs.input.style, css['input:disabled']); | ||
| } | ||
@@ -397,15 +593,15 @@ | ||
| return _react2.default.createElement( | ||
| 'span', | ||
| "span", | ||
| attrs.wrap, | ||
| _react2.default.createElement('input', attrs.input), | ||
| _react2.default.createElement("input", attrs.input), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnUp, | ||
| _react2.default.createElement('i', { style: state.style.minus }), | ||
| _react2.default.createElement('i', { style: state.style.plus }) | ||
| _react2.default.createElement("i", { style: css.minus }), | ||
| _react2.default.createElement("i", { style: css.plus }) | ||
| ), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnDown, | ||
| _react2.default.createElement('i', { style: state.style.minus }) | ||
| _react2.default.createElement("i", { style: css.minus }) | ||
| ) | ||
@@ -416,14 +612,14 @@ ); | ||
| return _react2.default.createElement( | ||
| 'span', | ||
| "span", | ||
| attrs.wrap, | ||
| _react2.default.createElement('input', attrs.input), | ||
| _react2.default.createElement("input", attrs.input), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnUp, | ||
| _react2.default.createElement('i', { style: state.style.arrowUp }) | ||
| _react2.default.createElement("i", { style: css.arrowUp }) | ||
| ), | ||
| _react2.default.createElement( | ||
| 'b', | ||
| "b", | ||
| attrs.btnDown, | ||
| _react2.default.createElement('i', { style: state.style.arrowDown }) | ||
| _react2.default.createElement("i", { style: css.arrowDown }) | ||
| ) | ||
@@ -442,2 +638,3 @@ ); | ||
| precision: PropTypes.number, | ||
| maxLength: PropTypes.number, | ||
| parse: PropTypes.func, | ||
@@ -448,6 +645,19 @@ format: PropTypes.func, | ||
| readOnly: PropTypes.bool, | ||
| required: PropTypes.bool, | ||
| noValidate: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), | ||
| style: PropTypes.object, | ||
| type: PropTypes.string, | ||
| pattern: PropTypes.string, | ||
| onFocus: PropTypes.func, | ||
| onBlur: PropTypes.func, | ||
| onKeyDown: PropTypes.func, | ||
| onChange: PropTypes.func, | ||
| onInvalid: PropTypes.func, | ||
| onValid: PropTypes.func, | ||
| onInput: PropTypes.func, | ||
| onSelect: PropTypes.func, | ||
| onSelectStart: PropTypes.func, | ||
| size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
| value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
| defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||
| mobile: function mobile(props, propName) { | ||
@@ -461,3 +671,2 @@ var prop = props[propName]; | ||
| NumericInput.defaultProps = { | ||
| value: 0, | ||
| step: 1, | ||
@@ -469,6 +678,5 @@ min: Number.MIN_SAFE_INTEGER || -9007199254740991, | ||
| format: null, | ||
| className: '', | ||
| mobile: 'auto', | ||
| style: {} | ||
| }; | ||
| style: {} }; | ||
| NumericInput.style = { | ||
@@ -538,3 +746,3 @@ wrap: { | ||
| background: 'rgba(0,0,0,.1)', | ||
| boxShadow: '-1px -1px 3px rgba(0,0,0,.1) inset, 1px 1px 3px rgba(255,255,255,.7) inset' | ||
| boxShadow: "-1px -1px 3px rgba(0,0,0,.1) inset,\n 1px 1px 3px rgba(255,255,255,.7) inset" | ||
| }, | ||
@@ -581,3 +789,3 @@ | ||
| background: 'rgba(0,0,0,.3)', | ||
| boxShadow: '0 1px 3px rgba(0,0,0,.2) inset, -1px -1px 4px rgba(255,255,255,.5) inset' | ||
| boxShadow: "0 1px 3px rgba(0,0,0,.2) inset,\n -1px -1px 4px rgba(255,255,255,.5) inset" | ||
| }, | ||
@@ -611,3 +819,8 @@ | ||
| 'input:focus': {} | ||
| 'input:focus': {}, | ||
| 'input:disabled': { | ||
| color: 'rgba(0, 0, 0, 0.3)', | ||
| textShadow: '0 1px 0 rgba(255, 255, 255, 0.8)' | ||
| } | ||
| }; | ||
@@ -614,0 +827,0 @@ NumericInput.SPEED = 50; |
+2
-9
@@ -11,3 +11,3 @@ /* global process */ | ||
| 'Firefox', | ||
| // 'Opera', | ||
| 'Opera', | ||
| 'Safari' | ||
@@ -21,5 +21,2 @@ ], | ||
| 'https://raw.githubusercontent.com/es-shims/es5-shim/master/es5-shim.js', | ||
| 'https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-with-addons.js', | ||
| 'https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js', | ||
| './__tests__/tests.webpack.js' | ||
@@ -30,3 +27,3 @@ ], | ||
| }, | ||
| reporters: [ 'dots' ], | ||
| reporters: [ 'progress' ], | ||
| webpack: { | ||
@@ -48,6 +45,2 @@ devtool: 'inline-source-map', | ||
| ] | ||
| }, | ||
| externals : { | ||
| 'react' : 'React', | ||
| 'react-dom' : 'ReactDOM' | ||
| } | ||
@@ -54,0 +47,0 @@ }, |
+1
-1
| { | ||
| "name": "react-numeric-input", | ||
| "version": "2.0.1", | ||
| "version": "2.0.3", | ||
| "description": "Number input component that can replace the native number input which is not yet very well supported and where it is, it does not have the same appearance across the browsers. Additionally this component offers more flexible options and can be used for any values (differently formatted representations of the internal numeric value).", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
+10
-2
@@ -11,3 +11,3 @@ # <img align="right" src="http://vlad-ignatov.github.io/react-numeric-input/examples/v2.0.0/screenshot.png" width="123"/>React Numeric Input | ||
| [Live demo](http://vlad-ignatov.github.io/react-numeric-input/examples/v2.0.0/index.html) | ||
| [Live demo](http://vlad-ignatov.github.io/react-numeric-input/examples/v2.0.3/index.html) | ||
@@ -62,3 +62,3 @@ ## Installation | ||
| ## Props | ||
| Option | Type | Default | ||
| Name | Type | Default | ||
| -------------|-------------------------------------|:-------: | ||
@@ -83,2 +83,10 @@ **value** |`number` or `string` | `""` which converts to 0 | ||
| ## Event Callbacks | ||
| You can pass callback props like `onClick`, `onMouseOver` etc. and they will be | ||
| attached to the input element and React will call them with `null` scope and the corresponding event. However, there are few special cases to be aware of: | ||
| * `onChange` - Called with `valueAsNumber` and `valueAsString`. The `valueAsNumber` represents the internal numeric value while `valueAsString` is the same as the input value and might be completely different from the numeric one if custom formatting is used. | ||
| * `onInvalid` - Will be called with `errorMessage`, `valueAsNumber` and `valueAsString`. | ||
| * `onValid` - There is no corresponding event in browsers. It will be called when the component transitions from invalid to valid state with the same arguments as onChange: `valueAsNumber` and `valueAsString`. | ||
| ## Styling | ||
@@ -85,0 +93,0 @@ The component uses inline styles which you can customize. The `style` prop is not added |
+363
-101
@@ -7,22 +7,71 @@ import React from "react"; | ||
| /** | ||
| * Just a simple helper to provide support for older IEs. This is not exactly a | ||
| * polyfill for classList.add but it does what we need with minimal efford. | ||
| * Works with single className only! | ||
| */ | ||
| function addClass(element, className) { | ||
| if (element.classList) { | ||
| return element.classList.add(className) | ||
| } | ||
| if (!element.className.search(new RegExp("\\b" + className + "\\b"))) { | ||
| element.className = " " + className | ||
| } | ||
| } | ||
| /** | ||
| * Just a simple helper to provide support for older IEs. This is not exactly a | ||
| * polyfill for classList.remove but it does what we need with minimal efford. | ||
| * Works with single className only! | ||
| */ | ||
| function removeClass(element, className) { | ||
| if (element.className) { | ||
| if (element.classList) { | ||
| return element.classList.remove(className) | ||
| } | ||
| element.className = element.className.replace( | ||
| new RegExp("\\b" + className + "\\b", "g"), | ||
| "" | ||
| ) | ||
| } | ||
| } | ||
| export class NumericInput extends React.Component | ||
| { | ||
| static propTypes = { | ||
| step : PropTypes.number, | ||
| min : PropTypes.number, | ||
| max : PropTypes.number, | ||
| precision : PropTypes.number, | ||
| parse : PropTypes.func, | ||
| format : PropTypes.func, | ||
| className : PropTypes.string, | ||
| disabled : PropTypes.bool, | ||
| readOnly : PropTypes.bool, | ||
| style : PropTypes.object, | ||
| type : PropTypes.string, | ||
| size : PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), | ||
| value : PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), | ||
| step : PropTypes.number, | ||
| min : PropTypes.number, | ||
| max : PropTypes.number, | ||
| precision : PropTypes.number, | ||
| maxLength : PropTypes.number, | ||
| parse : PropTypes.func, | ||
| format : PropTypes.func, | ||
| className : PropTypes.string, | ||
| disabled : PropTypes.bool, | ||
| readOnly : PropTypes.bool, | ||
| required : PropTypes.bool, | ||
| noValidate : PropTypes.oneOfType([ PropTypes.bool, PropTypes.string ]), | ||
| style : PropTypes.object, | ||
| type : PropTypes.string, | ||
| pattern : PropTypes.string, | ||
| onFocus : PropTypes.func, | ||
| onBlur : PropTypes.func, | ||
| onKeyDown : PropTypes.func, | ||
| onChange : PropTypes.func, | ||
| onInvalid : PropTypes.func, | ||
| onValid : PropTypes.func, | ||
| onInput : PropTypes.func, | ||
| onSelect : PropTypes.func, | ||
| onSelectStart: PropTypes.func, | ||
| size : PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), | ||
| value : PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), | ||
| defaultValue : PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), | ||
| mobile(props, propName) { | ||
| let prop = props[propName] | ||
| if (prop !== true && prop !== false && prop !== 'auto' && typeof prop != 'function') { | ||
| return new Error('The "mobile" prop must be true, false, "auto" or a function'); | ||
| if (prop !== true && prop !== false && prop !== 'auto' && | ||
| typeof prop != 'function') { | ||
| return new Error( | ||
| 'The "mobile" prop must be true, false, "auto" or a function' | ||
| ); | ||
| } | ||
@@ -37,3 +86,3 @@ } | ||
| static defaultProps = { | ||
| value : 0, | ||
| // value : '', | ||
| step : 1, | ||
@@ -45,5 +94,6 @@ min : Number.MIN_SAFE_INTEGER || -9007199254740991, | ||
| format : null, | ||
| className : '', | ||
| // className : '', | ||
| mobile : 'auto', | ||
| style : {} | ||
| style : {}//, | ||
| // noValidate: false | ||
| }; | ||
@@ -127,3 +177,4 @@ | ||
| background : 'rgba(0,0,0,.1)', | ||
| boxShadow : '-1px -1px 3px rgba(0,0,0,.1) inset, 1px 1px 3px rgba(255,255,255,.7) inset' | ||
| boxShadow : `-1px -1px 3px rgba(0,0,0,.1) inset, | ||
| 1px 1px 3px rgba(255,255,255,.7) inset` | ||
| }, | ||
@@ -170,3 +221,4 @@ | ||
| background: 'rgba(0,0,0,.3)', | ||
| boxShadow : '0 1px 3px rgba(0,0,0,.2) inset, -1px -1px 4px rgba(255,255,255,.5) inset' | ||
| boxShadow : `0 1px 3px rgba(0,0,0,.2) inset, | ||
| -1px -1px 4px rgba(255,255,255,.5) inset` | ||
| }, | ||
@@ -202,3 +254,8 @@ | ||
| 'input:focus': {} | ||
| 'input:focus': {}, | ||
| 'input:disabled': { | ||
| color : 'rgba(0, 0, 0, 0.3)', | ||
| textShadow: '0 1px 0 rgba(255, 255, 255, 0.8)' | ||
| } | ||
| }; | ||
@@ -227,19 +284,13 @@ | ||
| this._timer = null; | ||
| this._valid = undefined; | ||
| this.state = { | ||
| step : props.step, | ||
| min : props.min, | ||
| max : props.max, | ||
| style: {}, | ||
| value: 'value' in props ? | ||
| this._parse(String(props.value || '')) : | ||
| null | ||
| }; | ||
| for (let x in NumericInput.style) { | ||
| this.state.style[x] = Object.assign( | ||
| {}, | ||
| NumericInput.style[x], | ||
| props.style[x] || {} | ||
| ); | ||
| selectionStart: null, | ||
| selectionEnd : null, | ||
| value : "value" in props ? props.value : props.defaultValue, | ||
| btnDownHover : false, | ||
| btnDownActive : false, | ||
| btnUpHover : false, | ||
| btnUpActive : false, | ||
| inputFocus : false | ||
| } | ||
@@ -251,2 +302,61 @@ | ||
| /** | ||
| * Special care is taken for the "value" prop: | ||
| * - If not provided - set it to null | ||
| * - If the prop is a number - use it as is | ||
| * - Otherwise: | ||
| * 1. Convert it to string (falsy values become "") | ||
| * 2. Then trim it. | ||
| * 3. Then parse it to number (delegating to this.props.parse if any) | ||
| */ | ||
| componentWillReceiveProps(props) | ||
| { | ||
| let _value = String( | ||
| props.value || props.value === 0 ? props.value : '' | ||
| ).replace(/^\s*|\s*$/, "") | ||
| this.setState({ | ||
| value: "value" in props && _value !== '' ? this._parse(_value) : null | ||
| }) | ||
| } | ||
| /** | ||
| * After the component has been rendered into the DOM, do whatever is | ||
| * needed to "reconnect" it to the outer world, i.e. restore selection, | ||
| * call some of the callbacks, validate etc. | ||
| */ | ||
| componentDidUpdate(prevProps, prevState) | ||
| { | ||
| // Call the onChange if needed. This is placed here because there are | ||
| // many reasons for changing the value and this is the common place | ||
| // that can capture them all | ||
| if (prevState.value != this.state.value) { | ||
| this._invokeEventCallback("onChange", this.state.value, this.refs.input.value) | ||
| } | ||
| // Notify about the focus | ||
| if (this.state.inputFocus && !prevState.inputFocus) { | ||
| this.refs.input.focus() | ||
| // Restore selectionStart (if any) | ||
| if (this.state.selectionStart || this.state.selectionStart === 0) { | ||
| this.refs.input.selectionStart = this.state.selectionStart | ||
| } | ||
| // Restore selectionEnd (if any) | ||
| if (this.state.selectionEnd || this.state.selectionEnd === 0) { | ||
| this.refs.input.selectionEnd = this.state.selectionEnd | ||
| } | ||
| } | ||
| // This is a special case! If the component has the "autoFocus" prop | ||
| // and the browser did focus it we have pass that to the onFocus | ||
| if (!this.state.inputFocus && document.activeElement === this.refs.input) { | ||
| this.state.inputFocus = true | ||
| } | ||
| this.checkValidity() | ||
| } | ||
| /** | ||
| * This is used to clear the timer if any | ||
@@ -264,3 +374,3 @@ */ | ||
| { | ||
| this.refs.input.getValueAsNumber = () => this.state.value || 0; | ||
| this.refs.input.getValueAsNumber = () => this.state.value || 0 | ||
@@ -270,7 +380,90 @@ this.refs.input.setValue = (value) => { | ||
| value: this._parse(value) | ||
| }); | ||
| }; | ||
| }) | ||
| } | ||
| this.checkValidity() | ||
| } | ||
| /** | ||
| * Unless noValidate is set to true, the component will check the | ||
| * existing validation state (if any) and will toggle the "has-error" | ||
| * CSS class on the wrapper | ||
| */ | ||
| checkValidity() { | ||
| let valid, validationError = "" | ||
| let supportsValidation = !!this.refs.input.checkValidity | ||
| // noValidate | ||
| let noValidate = !!( | ||
| this.props.noValidate && this.props.noValidate != "false" | ||
| ) | ||
| this.refs.input.noValidate = noValidate | ||
| // If "noValidate" is set or "checkValidity" is not supported then | ||
| // consider the element valid. Otherwise consider it invalid and | ||
| // make some additional checks below | ||
| valid = noValidate || !supportsValidation | ||
| if (valid) { | ||
| validationError = "" | ||
| } | ||
| else { | ||
| // In some browsers once a pattern is set it connot be removed. The | ||
| // browser sets it to "" instead which results in validation | ||
| // failures... | ||
| if (this.refs.input.pattern === "") { | ||
| this.refs.input.pattern = this.props.required ? ".+" : ".*" | ||
| } | ||
| // Now check validity | ||
| if (supportsValidation) { | ||
| this.refs.input.checkValidity() | ||
| valid = this.refs.input.validity.valid | ||
| if (!valid) { | ||
| validationError = this.refs.input.validationMessage | ||
| } | ||
| } | ||
| // Some brousers might fail to validate maxLength | ||
| if (valid && supportsValidation && this.props.maxLength) { | ||
| if (this.refs.input.value.length > this.props.maxLength) { | ||
| validationError = "This value is too long" | ||
| } | ||
| } | ||
| } | ||
| validationError = validationError || ( | ||
| valid ? "" : this.refs.input.validationMessage || "Unknown Error" | ||
| ) | ||
| let validStateChanged = this._valid !== validationError | ||
| this._valid = validationError | ||
| if (validationError) { | ||
| addClass(this.refs.wrapper, "has-error") | ||
| if (validStateChanged) { | ||
| this._invokeEventCallback( | ||
| "onInvalid", | ||
| validationError, | ||
| this.state.value, | ||
| this.refs.input.value | ||
| ) | ||
| } | ||
| } | ||
| else { | ||
| removeClass(this.refs.wrapper, "has-error") | ||
| if (validStateChanged) { | ||
| this._invokeEventCallback( | ||
| "onValid", | ||
| this.state.value, | ||
| this.refs.input.value | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Used internally to parse the argument x to it's numeric representation. | ||
@@ -330,10 +523,10 @@ * If the argument cannot be converted to finite number returns 0; If a | ||
| */ | ||
| _step(n: number): boolean | ||
| _step(n: number, callback): boolean | ||
| { | ||
| let _n = this._toNumber( | ||
| (this.state.value || 0) + this.state.step * n | ||
| (this.state.value || 0) + this.props.step * n | ||
| ); | ||
| if (_n !== this.state.value) { | ||
| this.setState({ value: _n }); | ||
| this.setState({ value: _n }, callback); | ||
| } | ||
@@ -351,20 +544,50 @@ } | ||
| value: this._parse(e.target.value) | ||
| }); | ||
| }) | ||
| } | ||
| /** | ||
| * This binds the Up/Down arrow keys | ||
| * This binds the Up/Down arrow key listeners | ||
| */ | ||
| _onKeyDown(e: KeyboardEvent): void | ||
| _onKeyDown(...args): void | ||
| { | ||
| if (e.keyCode === KEYCODE_UP) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? 0.1 : e.shiftKey ? 10 : 1); | ||
| this._invokeEventCallback("onKeyDown", ...args) | ||
| let e = args[0] | ||
| if (!e.isDefaultPrevented()) { | ||
| if (e.keyCode === KEYCODE_UP) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? 0.1 : e.shiftKey ? 10 : 1); | ||
| } | ||
| else if (e.keyCode === KEYCODE_DOWN) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? -0.1 : e.shiftKey ? -10 : -1); | ||
| } | ||
| } | ||
| else if (e.keyCode === KEYCODE_DOWN) { | ||
| e.preventDefault(); | ||
| this._step(e.ctrlKey || e.metaKey ? -0.1 : e.shiftKey ? -10 : -1); | ||
| } | ||
| } | ||
| _onSelectionChange(e): void | ||
| { | ||
| this.setState({ | ||
| selectionStart: this.refs.input.selectionStart, | ||
| selectionEnd: this.refs.input.selectionEnd | ||
| }, () => { | ||
| switch (e.type) { | ||
| case "input": | ||
| if (this.props.onInput) { | ||
| this.props.onInput.call(this.refs.input, e) | ||
| } | ||
| break; | ||
| case "select": | ||
| if (this.props.onSelect) { | ||
| this.props.onSelect.call(this.refs.input, e) | ||
| } | ||
| break; | ||
| case "selectstart": | ||
| if (this.props.onSelectStart) { | ||
| this.props.onSelectStart.call(this.refs.input, e) | ||
| } | ||
| break; | ||
| } | ||
| }) | ||
| } | ||
| /** | ||
@@ -388,6 +611,6 @@ * Stops the widget from auto-changing by clearing the timer (if any) | ||
| */ | ||
| increase(_recursive: boolean): void | ||
| increase(_recursive: boolean, callback): void | ||
| { | ||
| this.stop(); | ||
| this._step(1); | ||
| this._step(1, callback); | ||
| if (isNaN(this.state.value) || this.state.value < this.props.max) { | ||
@@ -408,6 +631,6 @@ this._timer = setTimeout(() => { | ||
| */ | ||
| decrease(_recursive: boolean): void | ||
| decrease(_recursive: boolean, callback): void | ||
| { | ||
| this.stop(); | ||
| this._step(-1); | ||
| this._step(-1, callback); | ||
| if (isNaN(this.state.value) || this.state.value > this.props.min) { | ||
@@ -425,12 +648,10 @@ this._timer = setTimeout(() => { | ||
| */ | ||
| onMouseDown(dir, e) | ||
| onMouseDown(dir, callback) | ||
| { | ||
| e.preventDefault(); | ||
| if (dir == 'down') { | ||
| this.decrease(); | ||
| this.decrease(false, callback); | ||
| } | ||
| else if (dir == 'up') { | ||
| this.increase(); | ||
| this.increase(false, callback); | ||
| } | ||
| setTimeout(() => { this.refs.input.focus(); }); | ||
| } | ||
@@ -455,2 +676,9 @@ | ||
| _invokeEventCallback(callbackName, ...args) | ||
| { | ||
| if (typeof this.props[callbackName] == "function") { | ||
| this.props[callbackName].call(null, ...args); | ||
| } | ||
| } | ||
| /** | ||
@@ -464,11 +692,25 @@ * Renders an input wrapped in relative span and up/down buttons | ||
| let state = this.state | ||
| let css = {} | ||
| // Build the styles | ||
| for (let x in NumericInput.style) { | ||
| css[x] = Object.assign( | ||
| {}, | ||
| NumericInput.style[x], | ||
| props.style ? props.style[x] || {} : {} | ||
| ); | ||
| } | ||
| let { | ||
| // These are ignored in rendering | ||
| step, min, max, precision, parse, format, value, type, style, | ||
| step, min, max, precision, parse, format, | ||
| value, type, style, defaultValue, | ||
| // The rest are passed to the input | ||
| ...rest | ||
| } = props | ||
| } = this.props; | ||
| let hasFormControl = props.className && (/\bform-control\b/).test(props.className) | ||
| let hasFormControl = props.className && (/\bform-control\b/).test( | ||
| props.className | ||
| ) | ||
@@ -485,4 +727,5 @@ let mobile = props.mobile == 'auto' ? | ||
| wrap : { | ||
| style : Object.assign({}, NumericInput.style.wrap, props.style.wrap), | ||
| className: 'react-numeric-input' | ||
| style : css.wrap, | ||
| className: 'react-numeric-input', | ||
| ref : 'wrapper' | ||
| }, | ||
@@ -494,11 +737,8 @@ input : { | ||
| {}, | ||
| state.style.input, | ||
| css.input, | ||
| !hasFormControl ? | ||
| state.style['input:not(.form-control)'] : | ||
| css['input:not(.form-control)'] : | ||
| {}, | ||
| state.inputFocus ? state.style['input:focus'] : {} | ||
| state.inputFocus ? css['input:focus'] : {} | ||
| ), | ||
| value: state.value || state.value === 0 ? | ||
| this._format(state.value) : | ||
| '', | ||
| ...rest | ||
@@ -509,10 +749,10 @@ }, | ||
| {}, | ||
| state.style.btn, | ||
| state.style.btnUp, | ||
| css.btn, | ||
| css.btnUp, | ||
| props.disabled ? | ||
| state.style['btn:disabled'] : | ||
| css['btn:disabled'] : | ||
| state.btnUpActive ? | ||
| state.style['btn:active'] : | ||
| css['btn:active'] : | ||
| state.btnUpHover ? | ||
| state.style['btn:hover'] : | ||
| css['btn:hover'] : | ||
| {} | ||
@@ -524,10 +764,10 @@ ) | ||
| {}, | ||
| state.style.btn, | ||
| state.style.btnDown, | ||
| css.btn, | ||
| css.btnDown, | ||
| props.disabled ? | ||
| state.style['btn:disabled'] : | ||
| css['btn:disabled'] : | ||
| state.btnDownActive ? | ||
| state.style['btn:active'] : | ||
| css['btn:active'] : | ||
| state.btnDownHover ? | ||
| state.style['btn:hover'] : | ||
| css['btn:hover'] : | ||
| {} | ||
@@ -538,4 +778,8 @@ ) | ||
| if (state.value || state.value === 0) { | ||
| attrs.input.value = this._format(state.value) | ||
| } | ||
| if (hasFormControl) { | ||
| Object.assign(attrs.wrap.style, state.style['wrap.hasFormControl']) | ||
| Object.assign(attrs.wrap.style, css['wrap.hasFormControl']) | ||
| } | ||
@@ -545,5 +789,5 @@ | ||
| if (mobile) { | ||
| Object.assign(attrs.input .style, state.style['input.mobile' ]) | ||
| Object.assign(attrs.btnUp .style, state.style['btnUp.mobile' ]) | ||
| Object.assign(attrs.btnDown.style, state.style['btnDown.mobile']) | ||
| Object.assign(attrs.input .style, css['input.mobile' ]) | ||
| Object.assign(attrs.btnUp .style, css['btnUp.mobile' ]) | ||
| Object.assign(attrs.btnDown.style, css['btnDown.mobile']) | ||
| } | ||
@@ -579,8 +823,12 @@ | ||
| }, | ||
| onMouseDown: (e) => { | ||
| onMouseDown: (...args) => { | ||
| args[0].preventDefault(); | ||
| this.setState({ | ||
| btnUpHover : true, | ||
| btnUpActive : true | ||
| btnUpActive : true, | ||
| inputFocus : true | ||
| }, () => { | ||
| this._invokeEventCallback("onFocus", ...args) | ||
| }); | ||
| this.onMouseDown('up', e); | ||
| this.onMouseDown('up'); | ||
| } | ||
@@ -610,8 +858,12 @@ }); | ||
| }, | ||
| onMouseDown: (e) => { | ||
| onMouseDown: (...args) => { | ||
| args[0].preventDefault(); | ||
| this.setState({ | ||
| btnDownHover : true, | ||
| btnDownActive : true | ||
| btnDownActive : true, | ||
| inputFocus : true | ||
| }, () => { | ||
| this._invokeEventCallback("onFocus", ...args) | ||
| }); | ||
| this.onMouseDown('down', e); | ||
| this.onMouseDown('down'); | ||
| } | ||
@@ -623,10 +875,20 @@ }); | ||
| onKeyDown: this._onKeyDown.bind(this), | ||
| onFocus : () => { | ||
| this.setState({ inputFocus: true }); | ||
| onInput: this._onSelectionChange.bind(this), | ||
| onSelect: this._onSelectionChange.bind(this), | ||
| onSelectStart: this._onSelectionChange.bind(this), | ||
| onFocus: (...args) => { | ||
| this.setState({ inputFocus: true }, () => { | ||
| this._invokeEventCallback("onFocus", ...args) | ||
| }); | ||
| }, | ||
| onBlur : () => { | ||
| this.setState({ inputFocus: false }); | ||
| onBlur: (...args) => { | ||
| this.setState({ inputFocus: false }, () => { | ||
| this._invokeEventCallback("onBlur", ...args) | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| else { | ||
| Object.assign(attrs.input.style, css['input:disabled']) | ||
| } | ||
@@ -638,7 +900,7 @@ if (mobile) { | ||
| <b {...attrs.btnUp}> | ||
| <i style={state.style.minus}/> | ||
| <i style={state.style.plus}/> | ||
| <i style={css.minus}/> | ||
| <i style={css.plus}/> | ||
| </b> | ||
| <b {...attrs.btnDown}> | ||
| <i style={state.style.minus}/> | ||
| <i style={css.minus}/> | ||
| </b> | ||
@@ -653,6 +915,6 @@ </span> | ||
| <b {...attrs.btnUp}> | ||
| <i style={state.style.arrowUp}/> | ||
| <i style={css.arrowUp}/> | ||
| </b> | ||
| <b {...attrs.btnDown}> | ||
| <i style={state.style.arrowDown}/> | ||
| <i style={css.arrowDown}/> | ||
| </b> | ||
@@ -659,0 +921,0 @@ </span> |
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
225505
62.51%28
21.74%4232
85.21%151
5.59%3
50%