From 11da853af2d06af289e633a82de005c9fd94e153 Mon Sep 17 00:00:00 2001 From: Max Hauser Date: Sat, 2 Sep 2023 12:49:41 +0200 Subject: [PATCH] setting a number value now renders a real number input (#2091) - with min/max/step support - closes #2083 - closes #1099 --- README.md | 1 + .../components/Object/ObjectBrowserValue.jsx | 58 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a4465130d..9d51edc24 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ The icons may not be reused in other projects without the proper flaticon licens ## Changelog ### **WORK IN PROGRESS** * (foxriver76) fixed problem with discovery dialog +* (foxriver76) object browser now validates setting state of type number ### 6.9.2 (2023-09-01) * (foxriver76) show info, if server time differs from client time diff --git a/src/src/components/Object/ObjectBrowserValue.jsx b/src/src/components/Object/ObjectBrowserValue.jsx index dc00b4804..ae9f41030 100644 --- a/src/src/components/Object/ObjectBrowserValue.jsx +++ b/src/src/components/Object/ObjectBrowserValue.jsx @@ -180,6 +180,8 @@ class ObjectBrowserValue extends Component { chartEnabled: (window._localStorage || window.localStorage).getItem('App.chartSetValue') !== 'false', fullScreen: (window._localStorage || window.localStorage).getItem('App.fullScreen') === 'true', targetValue: value, + /** IF input is invalid, set value button is disabled */ + valid: true, }; this.ack = false; @@ -188,7 +190,7 @@ class ObjectBrowserValue extends Component { this.inputRef = React.createRef(); - this.chartFrom = Date.now() - 3600000 * 2; + this.chartFrom = Date.now() - 3_600_000 * 2; } componentDidMount() { @@ -203,10 +205,21 @@ class ObjectBrowserValue extends Component { } setTimeout(() => { - if (this.inputRef && this.inputRef.current) { + if (this.inputRef?.current) { const el = this.inputRef.current; const value = el.value || ''; + const origType = el.type; + + // type number cannot be selected, so we perform a short workaround + if (el.type === 'number') { + el.type = 'text'; + } + el.setSelectionRange(0, value.length); + + if (origType === 'number') { + el.type = origType; + } } }, 200); } @@ -250,6 +263,33 @@ class ObjectBrowserValue extends Component { }); } + /** @typedef {{ value: unknown, common: ioBroker.StateCommon }} NumberValidationOptions */ + + /** + * Check if a number value is valid according to the objects common properties + * @param {NumberValidationOptions} options value and common information + * + * @returns {boolean} + */ + isNumberValid(options) { + const { common } = options; + let { value } = options; + + if (typeof value !== 'number') { + value = Number(value); + } + + if (typeof common.min === 'number' && value < common.min) { + return false; + } + + if (typeof common.max === 'number' && value > common.max) { + return false; + } + + return true; + } + /** * Render time picker component for date type * @returns {React.JSX.Element} @@ -489,7 +529,7 @@ class ObjectBrowserValue extends Component { this.checkJsonError(); } - this.setState({ type: e.target.value }); + this.setState({ type: e.target.value, valid: e.target.value === 'number' ? this.isNumberValid({ value: this.state.targetValue, common: this.props.object.common }) : true }); }} > String @@ -534,14 +574,19 @@ class ObjectBrowserValue extends Component { variant="standard" classes={{ root: this.props.classes.textInput }} autoFocus + error={!this.state.valid} + type="number" + inputProps={{ step: this.props.object.common.step, min: this.props.object.common.min, max: this.props.object.common.max }} inputRef={this.inputRef} helperText={this.props.t( 'Press ENTER to write the value, when focused', )} - value={parseFloat(this.state.targetValue) || 0} + value={this.state.targetValue.toString() || 0} label={this.props.t('Value')} - onKeyUp={e => e.keyCode === 13 && this.onUpdate(e)} - onChange={e => this.setState({ targetValue: e.target.value })} + onKeyUp={e => e.keyCode === 13 && this.state.valid && this.onUpdate(e)} + onChange={e => { + this.setState({ targetValue: Number(e.target.value), valid: this.isNumberValid({ value: e.target.value, common: this.props.object.common }) }); + }} /> : (this.state.type === 'json' ? this.renderJsonEditor() : (this.state.type === 'states' ? @@ -630,6 +675,7 @@ class ObjectBrowserValue extends Component { {!this.props.expertMode ?
: null}