Skip to content

Commit

Permalink
Migrated basic input value widget to react
Browse files Browse the repository at this point in the history
  • Loading branch information
GermanBluefox committed Jul 10, 2024
1 parent 0938952 commit d750180
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ npm run start
### **WORK IN PROGRESS**
* (bluefox) Removed incompatible package for styles
* (bluefox) All widgets must be updated
* (bluefox) The basic input value widget was migrated to ReactJS

### 2.9.64 (2024-05-23)
* (bluefox) Added possibility to clear a text field by button
Expand Down
6 changes: 3 additions & 3 deletions packages/iobroker.vis-2/src/public/widgets/basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@
if (data.autoSet) {
$this.keyup(function(e) {
var timer = $this.data('timer');
if (timer) clearTimeout(timer);
timer && clearTimeout(timer);
$this.data('timer', setTimeout(function () {
$this.trigger('change');
}), 1000);
Expand Down Expand Up @@ -2520,7 +2520,7 @@
</div>
</script>

<script id="tplValueInput"
<!--script id="tplValueInput"
type="text/ejs"
class="vis-tpl"
data-vis-prev='<img src="widgets/basic/img/Prev_ValueInput.png"></img>'
Expand All @@ -2543,7 +2543,7 @@
<%== this.data.attr('html_append') %>
</div>
</div>
</script>
</script-->

<script id="tplValueBoolSelect"
type="text/ejs"
Expand Down
253 changes: 253 additions & 0 deletions packages/iobroker.vis-2/src/src/Vis/Widgets/Basic/BasicValueInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/**
* ioBroker.vis
* https://github.com/ioBroker/ioBroker.vis
*
* Copyright (c) 2022-2023 Denis Haev https://github.com/GermanBluefox,
* Creative Common Attribution-NonCommercial (CC BY-NC)
*
* http://creativecommons.org/licenses/by-nc/4.0/
*
* Short content:
* Licensees may copy, distribute, display and perform the work and make derivative works based on it only if they give the author or licensor the credits in the manner specified by these.
* Licensees may copy, distribute, display, and perform the work and make derivative works based on it only for noncommercial purposes.
* (Free for non-commercial use).
*/

import React from 'react';

import { IconButton, TextField } from '@mui/material';

import { KeyboardReturn as EnterIcon } from '@mui/icons-material';

// eslint-disable-next-line import/no-cycle
import type { RxRenderWidgetProps } from '@iobroker/types-vis-2';
import VisRxWidget, { type VisRxWidgetState } from '../../visRxWidget';

// eslint-disable-next-line no-use-before-define
type RxData = {
oid: string;
html_prepend?: string;
html_append?: string;
numeric?: boolean;
min?: number;
max?: number;
autoSet?: boolean;
readOnly?: boolean;
autoFocus?: boolean;
withEnter?: boolean;
noStyle?: boolean;
autoSetDelay?: number;
variant?: 'standard' | 'outlined' | 'filled';
};

interface BasicValueInputState extends VisRxWidgetState {
value: string;
sentValue: boolean;
}

class BasicValueInput extends VisRxWidget<RxData, BasicValueInputState> {
private setTimer: ReturnType<typeof setTimeout> | null = null;

static getWidgetInfo() {
return {
id: 'tplValueInput',
visSet: 'basic',
visName: 'Input val',
visWidgetLabel: 'vis-2-widgets-basic-input_value', // Label of widget
visPrev: 'widgets/basic/img/Prev_ValueInput.png',
visAttrs: [{
name: 'common',
fields: [
{ name: 'oid', type: 'id' },
{ name: 'html_prepend', type: 'html' },
{ name: 'html_append', type: 'html' },
{ name: 'numeric', type: 'checkbox' },
{ name: 'min', type: 'number', hidden: '!data.numeric' },
{ name: 'max', type: 'number', hidden: '!data.numeric' },
{ name: 'autoSet', type: 'checkbox' },
{
name: 'autoSetDelay',
label: 'vis-2-widgets-basic-autoSetDelay',
type: 'number',
hidden: '!data.autoSet',
default: 1000,
},
{ name: 'readOnly', type: 'checkbox' },
{ name: 'autoFocus', type: 'checkbox' },
{ name: 'noStyle', type: 'checkbox', label: 'vis-2-widgets-basic-noStyle' },
{ name: 'withEnter', type: 'checkbox', hidden: '!!data.noStyle' },
{
name: 'variant',
type: 'select',
hidden: '!!data.noStyle',
noTranslation: true,
options: [
{ value: 'standard', label: 'standard' },
{ value: 'outlined', label: 'outlined' },
{ value: 'standard', label: 'standard' },
],
default: 'standard',
},
],
}],
visDefaultStyle: {
width: 150,
height: 70,
},
} as const;
}

componentDidMount() {
super.componentDidMount();

const oid = this.state.rxData.oid && this.state.rxData.oid !== 'nothing_selected' && this.state.rxData.oid.includes('"') ? '' : this.state.rxData.oid || '';
let value: string;
if (this.state.rxData.oid) {
if (this.state.rxData.oid.includes('"')) {
value = this.state.rxData.oid.substring(1, this.state.rxData.oid.length - 1);
} else if (oid) {
value = this.state.values[`${oid}.val`] !== null && this.state.values[`${oid}.val`] !== undefined ? this.state.values[`${oid}.val`].toString() : '';
} else {
value = '';
}
} else {
value = '';
}

this.setState({ value, sentValue: true });
}

onStateUpdated(id: string, state: ioBroker.State | null) {
if (id === this.state.rxData.oid && state && state.val !== null && state.val !== undefined) {
const valStr = state.val.toString();
if (this.state.value.toString() !== valStr) {
this.setState({ value: valStr });
}
}
}

// eslint-disable-next-line class-methods-use-this
getWidgetInfo() {
return BasicValueInput.getWidgetInfo();
}

renderWidgetBody(props: RxRenderWidgetProps) {
super.renderWidgetBody(props);

// set default width and height
if (props.style.width === undefined) {
props.style.width = 100;
}
if (props.style.height === undefined) {
props.style.height = 25;
}
const oid = this.state.rxData.oid && this.state.rxData.oid !== 'nothing_selected' && this.state.rxData.oid.includes('"') ? '' : this.state.rxData.oid || '';

let value;
if (this.state.rxData.oid) {
if (this.state.rxData.oid.includes('"')) {
value = this.state.rxData.oid.substring(1, this.state.rxData.oid.length - 1);
} else if (oid) {
value = this.state.value;
if (value === undefined || value === null) {
value = '';
}
} else {
value = '';
}
} else {
value = '';
}

let content;
if (this.state.rxData.noStyle) {
content = <input
type={this.state.rxData.numeric ? 'number' : 'text'}
style={{
flexGrow: 1,
height: 'calc(100% - 6px)',
backgroundColor: this.props.context.themeType === 'dark' ? '#333' : '#fff',
color: this.props.context.themeType === 'dark' ? '#FFF' : '#000',
}}
readOnly={this.state.rxData.readOnly || this.props.editMode}
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus={this.state.rxData.autoFocus}
value={value}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
this.setState({ value: e.target.value, sentValue: false }, () => {
if (this.state.rxData.autoSet) {
this.setTimer && clearTimeout(this.setTimer);
this.setTimer = setTimeout(() => {
this.setTimer = null;
this.setState({ sentValue: true }, () =>
this.props.context.setValue(oid, this.state.value));
}, this.state.rxData.autoSetDelay || 1000);
}
})}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && oid) {
this.setState({ sentValue: true }, () =>
this.props.context.setValue(oid, this.state.value));
}
}}
/>;
} else {
content = <TextField
type={this.state.rxData.numeric ? 'number' : 'text'}
style={{ flexGrow: 1, height: '100%' }}
sx={{
'& .MuiInputBase-root': {
height: '100%',
},
}}
variant={this.state.rxData.variant}
InputLabelProps={{ shrink: true }}
InputProps={{
readOnly: this.state.rxData.readOnly || this.props.editMode,
endAdornment: !this.props.editMode && this.state.rxData.withEnter && oid && !this.state.sentValue ? <IconButton
size="small"
onClick={() => this.setState({ sentValue: true }, () =>
this.props.context.setValue(oid, this.state.value))}
>
<EnterIcon />
</IconButton> : null,
}}
label={this.state.rxData.html_prepend}
helperText={this.state.rxData.html_append}
value={value}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ value: e.target.value, sentValue: false }, () => {
if (this.state.rxData.autoSet && oid) {
this.setTimer && clearTimeout(this.setTimer);
this.setTimer = setTimeout(() => {
this.setTimer = null;
this.setState({ sentValue: true }, () =>
this.props.context.setValue(oid, this.state.value));
}, this.state.rxData.autoSetDelay || 1000);
}
});
}}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && oid) {
this.setState({ sentValue: true }, () =>
this.props.context.setValue(oid, this.state.value));
}
}}
/>;
}

return <div className="vis-widget-body" style={{ display: 'flex', alignItems: 'center', gap: 3 }}>
{this.state.rxData.noStyle ? <span
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: this.state.rxData.html_prepend ?? '' }}
/> : null}
{content}
{this.state.rxData.noStyle ? <span
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: this.state.rxData.html_append ?? '' }}
/> : null}
</div>;
}
}

export default BasicValueInput;
2 changes: 2 additions & 0 deletions packages/iobroker.vis-2/src/src/Vis/Widgets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import BasicBulb from './Basic/BasicBulb';
import BasicRedNumber from './Basic/BasicRedNumber';
import BasicHtml from './Basic/BasicHtml';
import BasicValueString from './Basic/BasicValueString';
import BasicValueInput from './Basic/BasicValueInput';
import BasicViewInWidget from './Basic/BasicViewInWidget';
import BasicViewInWidget8 from './Basic/BasicViewInWidget8';
import BasicGroup from './Basic/BasicGroup';
Expand Down Expand Up @@ -60,6 +61,7 @@ const WIDGETS = [
BasicFilterDropdown,
BasicLink,
BasicValueString,
BasicValueInput,
BasicViewInWidget,
BasicViewInWidget8,
BasicGroup,
Expand Down
2 changes: 2 additions & 0 deletions packages/iobroker.vis-2/src/src/Vis/visRxWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ class VisRxWidget<TRxData extends Record<string, any>, TState extends Partial<Vi
}

static t(key: string, ...args: string[]) {
// it is a very strange construction,
// but "this" at this place takes the spout class (what is required) and not the instance
if (this.getI18nPrefix) {
return I18n.t(`${this.getI18nPrefix()}${key}`, ...args);
}
Expand Down
7 changes: 5 additions & 2 deletions packages/iobroker.vis-2/src/src/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "Folgende Ansichten werden geändert",
"Show this attribute by all views": "Dieses Attribut in allen Ansichten anzeigen",
"Menu width": "Menübreite",
"Do not ask again": "Frag nicht nochmal"
}
"Do not ask again": "Frag nicht nochmal",
"vis-2-widgets-basic-input_value": "Eingegebener Wert",
"vis-2-widgets-basic-autoSetDelay": "Verzögerung für automatisches Schreiben",
"vis-2-widgets-basic-noStyle": "Kein Style"
}
5 changes: 4 additions & 1 deletion packages/iobroker.vis-2/src/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "Following views will be changed",
"Show this attribute by all views": "Show this attribute by all views",
"Menu width": "Menu width",
"Do not ask again": "Do not ask again"
"Do not ask again": "Do not ask again",
"vis-2-widgets-basic-input_value": "Input value",
"vis-2-widgets-basic-autoSetDelay": "Delay for auto-write",
"vis-2-widgets-basic-noStyle": "No style"
}
5 changes: 4 additions & 1 deletion packages/iobroker.vis-2/src/src/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "Se cambiarán las siguientes vistas",
"Show this attribute by all views": "Mostrar este atributo en todas las vistas.",
"Menu width": "Ancho del menú",
"Do not ask again": "No preguntes de nuevo"
"Do not ask again": "No preguntes de nuevo",
"vis-2-widgets-basic-input_value": "Valor de entrada",
"vis-2-widgets-basic-autoSetDelay": "Retraso para la escritura automática",
"vis-2-widgets-basic-noStyle": "Sin estilo"
}
5 changes: 4 additions & 1 deletion packages/iobroker.vis-2/src/src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "Les vues suivantes seront modifiées",
"Show this attribute by all views": "Afficher cet attribut dans toutes les vues",
"Menu width": "Largeur du menu",
"Do not ask again": "Ne demande plus"
"Do not ask again": "Ne demande plus",
"vis-2-widgets-basic-input_value": "Valeur d'entrée",
"vis-2-widgets-basic-autoSetDelay": "Délai d'écriture automatique",
"vis-2-widgets-basic-noStyle": "Aucun style"
}
5 changes: 4 additions & 1 deletion packages/iobroker.vis-2/src/src/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "Le visualizzazioni seguenti verranno modificate",
"Show this attribute by all views": "Mostra questo attributo per tutte le visualizzazioni",
"Menu width": "Larghezza del menù",
"Do not ask again": "Non chiedere di nuovo"
"Do not ask again": "Non chiedere di nuovo",
"vis-2-widgets-basic-input_value": "Valore immesso",
"vis-2-widgets-basic-autoSetDelay": "Ritardo per la scrittura automatica",
"vis-2-widgets-basic-noStyle": "Nessuno stile"
}
5 changes: 4 additions & 1 deletion packages/iobroker.vis-2/src/src/i18n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "De volgende weergaven zullen worden gewijzigd",
"Show this attribute by all views": "Toon dit attribuut bij alle weergaven",
"Menu width": "Menubreedte",
"Do not ask again": "Niet meer vragen"
"Do not ask again": "Niet meer vragen",
"vis-2-widgets-basic-input_value": "Invoerwaarde",
"vis-2-widgets-basic-autoSetDelay": "Vertraging voor automatisch schrijven",
"vis-2-widgets-basic-noStyle": "Geen stijl"
}
5 changes: 4 additions & 1 deletion packages/iobroker.vis-2/src/src/i18n/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -741,5 +741,8 @@
"Following views will be changed": "Następujące widoki ulegną zmianie",
"Show this attribute by all views": "Pokaż ten atrybut we wszystkich widokach",
"Menu width": "Szerokość menu",
"Do not ask again": "Nie pytaj ponownie"
"Do not ask again": "Nie pytaj ponownie",
"vis-2-widgets-basic-input_value": "Wartość wejściowa",
"vis-2-widgets-basic-autoSetDelay": "Opóźnienie automatycznego zapisu",
"vis-2-widgets-basic-noStyle": "Brak stylu"
}
Loading

0 comments on commit d750180

Please sign in to comment.