From 807cfebf76506979f8049f58163ff5644d9b8de8 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 6 Feb 2019 19:25:01 -0800 Subject: [PATCH] Number input and max release size limit increased Fixed: Number input changing value while typing New: Maximum size limit has been doubled Closes #2921 --- frontend/src/Components/Form/NumberInput.js | 80 ++++++++++++----- frontend/src/Components/Form/TextInput.js | 3 + .../Quality/Definition/QualityDefinition.js | 87 +++++++++++++++---- .../Definition/QualityDefinitionConnector.js | 10 +-- .../Quality/Definition/QualityDefinitions.js | 14 ++- .../Definition/QualityDefinitionsConnector.js | 6 +- frontend/src/Utilities/Number/roundNumber.js | 5 ++ 7 files changed, 154 insertions(+), 51 deletions(-) create mode 100644 frontend/src/Utilities/Number/roundNumber.js diff --git a/frontend/src/Components/Form/NumberInput.js b/frontend/src/Components/Form/NumberInput.js index 20b6fd0a1..c03871a44 100644 --- a/frontend/src/Components/Form/NumberInput.js +++ b/frontend/src/Components/Form/NumberInput.js @@ -2,44 +2,86 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import TextInput from './TextInput'; +function parseValue(props, value) { + const { + isFloat, + min, + max + } = props; + + if (value == null) { + return min; + } + + let newValue = isFloat ? parseFloat(value) : parseInt(value); + + if (min != null && newValue != null && newValue < min) { + newValue = min; + } else if (max != null && newValue != null && newValue > max) { + newValue = max; + } + + return newValue; +} + class NumberInput extends Component { // - // Listeners + // Lifecycle - onChange = ({ name, value }) => { - let newValue = null; + constructor(props, context) { + super(props, context); + + this.state = { + value: props.value.toString(), + isFocused: false + }; + } - if (value) { - newValue = this.props.isFloat ? parseFloat(value) : parseInt(value); + componentDidUpdate(prevProps, prevState) { + if (this.props.value !== prevProps.value && !this.state.isFocused) { + this.setState({ value: this.props.value.toString() }); } + } + + // + // Listeners + + onChange = ({ name, value }) => { + this.setState({ value }); this.props.onChange({ name, - value: newValue + value: parseValue(this.props, value) }); + + } + + onFocus = () => { + this.setState({ isFocused: true }); } onBlur = () => { const { name, - value, - min, - max, onChange } = this.props; - let newValue = value; + const { value } = this.state; + const parsedValue = parseValue(this.props, value); - if (min != null && newValue != null && newValue < min) { - newValue = min; - } else if (max != null && newValue != null && newValue > max) { - newValue = max; + if (parsedValue.toString() === value) { + this.setState({ isFocused: false }); + } else { + this.setState({ + value: parsedValue.toString(), + isFocused: false + }); } onChange({ name, - value: newValue + value: parseValue(this.props, this.state.value) }); } @@ -47,18 +89,16 @@ class NumberInput extends Component { // Render render() { - const { - value, - ...otherProps - } = this.props; + const value = this.state.value; return ( ); } diff --git a/frontend/src/Components/Form/TextInput.js b/frontend/src/Components/Form/TextInput.js index 92c0f4baf..9feefa616 100644 --- a/frontend/src/Components/Form/TextInput.js +++ b/frontend/src/Components/Form/TextInput.js @@ -127,6 +127,7 @@ class TextInput extends Component { hasError, hasWarning, hasButton, + step, onBlur } = this.props; @@ -146,6 +147,7 @@ class TextInput extends Component { )} name={name} value={value} + step={step} onChange={this.onChange} onFocus={this.onFocus} onBlur={onBlur} @@ -168,6 +170,7 @@ TextInput.propTypes = { hasError: PropTypes.bool, hasWarning: PropTypes.bool, hasButton: PropTypes.bool, + step: PropTypes.number, onChange: PropTypes.func.isRequired, onFocus: PropTypes.func, onBlur: PropTypes.func, diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.js b/frontend/src/Settings/Quality/Definition/QualityDefinition.js index 2ff558037..ea5075334 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinition.js +++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.js @@ -2,28 +2,38 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import ReactSlider from 'react-slider'; import formatBytes from 'Utilities/Number/formatBytes'; +import roundNumber from 'Utilities/Number/roundNumber'; import { kinds } from 'Helpers/Props'; import Label from 'Components/Label'; import NumberInput from 'Components/Form/NumberInput'; import TextInput from 'Components/Form/TextInput'; import styles from './QualityDefinition.css'; +const MIN = 0; +const MAX = 400; + const slider = { - min: 0, - max: 200, + min: MIN, + max: roundNumber(Math.pow(MAX, 1 / 1.1)), step: 0.1 }; -function getValue(value) { - if (value < slider.min) { - return slider.min; +function getValue(inputValue) { + if (inputValue < MIN) { + return MIN; } - if (value > slider.max) { - return slider.max; + if (inputValue > MAX) { + return MAX; } - return value; + return roundNumber(inputValue); +} + +function getSliderValue(value, defaultValue) { + const sliderValue = value ? Math.pow(value, 1 / 1.1) : defaultValue; + + return roundNumber(sliderValue); } class QualityDefinition extends Component { @@ -35,6 +45,11 @@ class QualityDefinition extends Component { super(props, context); this._forceUpdateTimeout = null; + + this.state = { + sliderMinSize: getSliderValue(props.minSize, slider.min), + sliderMaxSize: getSliderValue(props.maxSize, slider.max) + }; } componentDidMount() { @@ -54,15 +69,37 @@ class QualityDefinition extends Component { // // Listeners - onSizeChange = ([minSize, maxSize]) => { - maxSize = maxSize === slider.max ? null : maxSize; + onSliderChange = ([sliderMinSize, sliderMaxSize]) => { + this.setState({ + sliderMinSize, + sliderMaxSize + }); + + this.props.onSizeChange({ + minSize: roundNumber(Math.pow(sliderMinSize, 1.1)), + maxSize: sliderMaxSize === slider.max ? null : roundNumber(Math.pow(sliderMaxSize, 1.1)) + }); + } + + onAfterSliderChange = () => { + const { + minSize, + maxSize + } = this.props; - this.props.onSizeChange({ minSize, maxSize }); + this.setState({ + sliderMiSize: getSliderValue(minSize, slider.min), + sliderMaxSize: getSliderValue(maxSize, slider.max) + }); } onMinSizeChange = ({ value }) => { const minSize = getValue(value); + this.setState({ + sliderMinSize: getSliderValue(minSize, slider.min) + }); + this.props.onSizeChange({ minSize, maxSize: this.props.maxSize @@ -70,7 +107,11 @@ class QualityDefinition extends Component { } onMaxSizeChange = ({ value }) => { - const maxSize = value === slider.max ? null : getValue(value); + const maxSize = value === MAX ? null : getValue(value); + + this.setState({ + sliderMaxSize: getSliderValue(maxSize, slider.max) + }); this.props.onSizeChange({ minSize: this.props.minSize, @@ -92,6 +133,11 @@ class QualityDefinition extends Component { onTitleChange } = this.props; + const { + sliderMinSize, + sliderMaxSize + } = this.state; + const minBytes = minSize * 1024 * 1024; const minThirty = formatBytes(minBytes * 30, 2); const minSixty = formatBytes(minBytes * 60, 2); @@ -120,13 +166,14 @@ class QualityDefinition extends Component { max={slider.max} step={slider.step} minDistance={10} - value={[minSize || slider.min, maxSize || slider.max]} + value={[sliderMinSize, sliderMaxSize]} withBars={true} snapDragDisabled={true} className={styles.slider} barClassName={styles.bar} handleClassName={styles.handle} - onChange={this.onSizeChange} + onChange={this.onSliderChange} + onAfterChange={this.onAfterSliderChange} />
@@ -151,9 +198,10 @@ class QualityDefinition extends Component { @@ -165,9 +213,10 @@ class QualityDefinition extends Component { diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js b/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js index 9404dfd9f..a76c9440f 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js +++ b/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js @@ -5,12 +5,6 @@ import { setQualityDefinitionValue } from 'Store/Actions/settingsActions'; import { clearPendingChanges } from 'Store/Actions/baseActions'; import QualityDefinition from './QualityDefinition'; -function mapStateToProps(state) { - return { - advancedSettings: state.settings.advancedSettings - }; -} - const mapDispatchToProps = { setQualityDefinitionValue, clearPendingChanges @@ -40,7 +34,7 @@ class QualityDefinitionConnector extends Component { this.props.setQualityDefinitionValue({ id, name: 'minSize', value: minSize }); } - if (minSize !== currentMaxSize) { + if (maxSize !== currentMaxSize) { this.props.setQualityDefinitionValue({ id, name: 'maxSize', value: maxSize }); } } @@ -67,4 +61,4 @@ QualityDefinitionConnector.propTypes = { clearPendingChanges: PropTypes.func.isRequired }; -export default connect(mapStateToProps, mapDispatchToProps)(QualityDefinitionConnector); +export default connect(null, mapDispatchToProps)(QualityDefinitionConnector); diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinitions.js b/frontend/src/Settings/Quality/Definition/QualityDefinitions.js index 18db844f8..f8050a7e7 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinitions.js +++ b/frontend/src/Settings/Quality/Definition/QualityDefinitions.js @@ -13,6 +13,7 @@ class QualityDefinitions extends Component { render() { const { items, + advancedSettings, ...otherProps } = this.props; @@ -26,7 +27,14 @@ class QualityDefinitions extends Component {
Quality
Title
Size Limit
-
Megabytes Per Minute
+ + { + advancedSettings ? +
+ Megabytes Per Minute +
: + null + }
@@ -36,6 +44,7 @@ class QualityDefinitions extends Component { ); }) @@ -57,7 +66,8 @@ QualityDefinitions.propTypes = { isFetching: PropTypes.bool.isRequired, error: PropTypes.object, defaultProfile: PropTypes.object, - items: PropTypes.arrayOf(PropTypes.object).isRequired + items: PropTypes.arrayOf(PropTypes.object).isRequired, + advancedSettings: PropTypes.bool.isRequired }; export default QualityDefinitions; diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinitionsConnector.js b/frontend/src/Settings/Quality/Definition/QualityDefinitionsConnector.js index 9a3e0a90c..c2f830afd 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinitionsConnector.js +++ b/frontend/src/Settings/Quality/Definition/QualityDefinitionsConnector.js @@ -9,7 +9,8 @@ import QualityDefinitions from './QualityDefinitions'; function createMapStateToProps() { return createSelector( (state) => state.settings.qualityDefinitions, - (qualityDefinitions) => { + (state) => state.settings.advancedSettings, + (qualityDefinitions, advancedSettings) => { const items = qualityDefinitions.items.map((item) => { const pendingChanges = qualityDefinitions.pendingChanges[item.id] || {}; @@ -19,7 +20,8 @@ function createMapStateToProps() { return { ...qualityDefinitions, items, - hasPendingChanges: !_.isEmpty(qualityDefinitions.pendingChanges) + hasPendingChanges: !_.isEmpty(qualityDefinitions.pendingChanges), + advancedSettings }; } ); diff --git a/frontend/src/Utilities/Number/roundNumber.js b/frontend/src/Utilities/Number/roundNumber.js new file mode 100644 index 000000000..e1a19018f --- /dev/null +++ b/frontend/src/Utilities/Number/roundNumber.js @@ -0,0 +1,5 @@ +export default function roundNumber(input, decimalPlaces = 1) { + const multiplier = Math.pow(10, decimalPlaces); + + return Math.round(input * multiplier) / multiplier; +}