@iobroker/json-config 6.17.6 → 6.17.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +21 -21
  2. package/build/JsonConfig.d.ts +1 -1
  3. package/build/JsonConfig.js +18 -19
  4. package/build/JsonConfig.js.map +1 -1
  5. package/build/JsonConfigComponent/ChipInput.d.ts +89 -11
  6. package/build/JsonConfigComponent/ChipInput.js +48 -137
  7. package/build/JsonConfigComponent/ChipInput.js.map +1 -1
  8. package/build/JsonConfigComponent/ConfigAccordion.d.ts +7 -14
  9. package/build/JsonConfigComponent/ConfigAccordion.js +10 -21
  10. package/build/JsonConfigComponent/ConfigAccordion.js.map +1 -1
  11. package/build/JsonConfigComponent/ConfigAlive.d.ts +5 -1
  12. package/build/JsonConfigComponent/ConfigAlive.js +0 -1
  13. package/build/JsonConfigComponent/ConfigAlive.js.map +1 -1
  14. package/build/JsonConfigComponent/ConfigAutocomplete.d.ts +5 -29
  15. package/build/JsonConfigComponent/ConfigAutocomplete.js +7 -5
  16. package/build/JsonConfigComponent/ConfigAutocomplete.js.map +1 -1
  17. package/build/JsonConfigComponent/ConfigAutocompleteSendTo.d.ts +5 -14
  18. package/build/JsonConfigComponent/ConfigAutocompleteSendTo.js +3 -3
  19. package/build/JsonConfigComponent/ConfigAutocompleteSendTo.js.map +1 -1
  20. package/build/JsonConfigComponent/ConfigCRON.d.ts +1 -1
  21. package/build/JsonConfigComponent/ConfigCertCollection.d.ts +7 -13
  22. package/build/JsonConfigComponent/ConfigCertCollection.js +8 -18
  23. package/build/JsonConfigComponent/ConfigCertCollection.js.map +1 -1
  24. package/build/JsonConfigComponent/ConfigCertificateSelect.d.ts +7 -13
  25. package/build/JsonConfigComponent/ConfigCertificateSelect.js +5 -18
  26. package/build/JsonConfigComponent/ConfigCertificateSelect.js.map +1 -1
  27. package/build/JsonConfigComponent/ConfigCertificates.d.ts +7 -13
  28. package/build/JsonConfigComponent/ConfigCertificates.js +7 -18
  29. package/build/JsonConfigComponent/ConfigCertificates.js.map +1 -1
  30. package/build/JsonConfigComponent/ConfigCheckbox.d.ts +3 -11
  31. package/build/JsonConfigComponent/ConfigCheckbox.js +2 -2
  32. package/build/JsonConfigComponent/ConfigCheckbox.js.map +1 -1
  33. package/build/JsonConfigComponent/ConfigChip.d.ts +1 -1
  34. package/build/JsonConfigComponent/ConfigColor.d.ts +13 -20
  35. package/build/JsonConfigComponent/ConfigColor.js +9 -23
  36. package/build/JsonConfigComponent/ConfigColor.js.map +1 -1
  37. package/build/JsonConfigComponent/ConfigCoordinates.d.ts +8 -13
  38. package/build/JsonConfigComponent/ConfigCoordinates.js +29 -39
  39. package/build/JsonConfigComponent/ConfigCoordinates.js.map +1 -1
  40. package/build/JsonConfigComponent/ConfigCustom.d.ts +4 -50
  41. package/build/JsonConfigComponent/ConfigCustom.js +0 -1
  42. package/build/JsonConfigComponent/ConfigCustom.js.map +1 -1
  43. package/build/JsonConfigComponent/ConfigDatePicker.d.ts +8 -3
  44. package/build/JsonConfigComponent/ConfigDatePicker.js +21 -13
  45. package/build/JsonConfigComponent/ConfigDatePicker.js.map +1 -1
  46. package/build/JsonConfigComponent/ConfigDeviceManager.d.ts +5 -1
  47. package/build/JsonConfigComponent/ConfigDeviceManager.js +1 -1
  48. package/build/JsonConfigComponent/ConfigDeviceManager.js.map +1 -1
  49. package/build/JsonConfigComponent/ConfigFile.d.ts +1 -1
  50. package/build/JsonConfigComponent/ConfigFunc.d.ts +1 -1
  51. package/build/JsonConfigComponent/ConfigGeneric.d.ts +46 -32
  52. package/build/JsonConfigComponent/ConfigGeneric.js +23 -12
  53. package/build/JsonConfigComponent/ConfigGeneric.js.map +1 -1
  54. package/build/JsonConfigComponent/ConfigIP.d.ts +1 -1
  55. package/build/JsonConfigComponent/ConfigImageSendTo.d.ts +1 -1
  56. package/build/JsonConfigComponent/ConfigInstanceSelect.d.ts +1 -1
  57. package/build/JsonConfigComponent/ConfigInterface.d.ts +1 -1
  58. package/build/JsonConfigComponent/ConfigJsonEditor.d.ts +1 -1
  59. package/build/JsonConfigComponent/ConfigLanguage.d.ts +3 -2
  60. package/build/JsonConfigComponent/ConfigLanguage.js +2 -2
  61. package/build/JsonConfigComponent/ConfigLanguage.js.map +1 -1
  62. package/build/JsonConfigComponent/ConfigNumber.d.ts +3 -13
  63. package/build/JsonConfigComponent/ConfigNumber.js +2 -2
  64. package/build/JsonConfigComponent/ConfigNumber.js.map +1 -1
  65. package/build/JsonConfigComponent/ConfigObjectId.d.ts +1 -1
  66. package/build/JsonConfigComponent/ConfigPanel.d.ts +7 -2
  67. package/build/JsonConfigComponent/ConfigPanel.js +5 -40
  68. package/build/JsonConfigComponent/ConfigPanel.js.map +1 -1
  69. package/build/JsonConfigComponent/ConfigPassword.d.ts +1 -1
  70. package/build/JsonConfigComponent/ConfigPort.d.ts +6 -1
  71. package/build/JsonConfigComponent/ConfigPort.js +2 -3
  72. package/build/JsonConfigComponent/ConfigPort.js.map +1 -1
  73. package/build/JsonConfigComponent/ConfigRoom.d.ts +1 -1
  74. package/build/JsonConfigComponent/ConfigSelect.d.ts +1 -1
  75. package/build/JsonConfigComponent/ConfigSelectSendTo.d.ts +7 -16
  76. package/build/JsonConfigComponent/ConfigSelectSendTo.js +15 -61
  77. package/build/JsonConfigComponent/ConfigSelectSendTo.js.map +1 -1
  78. package/build/JsonConfigComponent/ConfigSendto.d.ts +3 -20
  79. package/build/JsonConfigComponent/ConfigSendto.js +8 -8
  80. package/build/JsonConfigComponent/ConfigSendto.js.map +1 -1
  81. package/build/JsonConfigComponent/ConfigSetState.d.ts +1 -1
  82. package/build/JsonConfigComponent/ConfigSlider.d.ts +1 -1
  83. package/build/JsonConfigComponent/ConfigStaticDivider.d.ts +1 -1
  84. package/build/JsonConfigComponent/ConfigStaticHeader.d.ts +1 -1
  85. package/build/JsonConfigComponent/ConfigStaticImage.d.ts +1 -1
  86. package/build/JsonConfigComponent/ConfigStaticText.d.ts +1 -1
  87. package/build/JsonConfigComponent/ConfigTable.d.ts +4 -25
  88. package/build/JsonConfigComponent/ConfigTable.js +5 -4
  89. package/build/JsonConfigComponent/ConfigTable.js.map +1 -1
  90. package/build/JsonConfigComponent/ConfigTabs.d.ts +8 -33
  91. package/build/JsonConfigComponent/ConfigTabs.js +10 -43
  92. package/build/JsonConfigComponent/ConfigTabs.js.map +1 -1
  93. package/build/JsonConfigComponent/ConfigText.d.ts +7 -13
  94. package/build/JsonConfigComponent/ConfigText.js +13 -18
  95. package/build/JsonConfigComponent/ConfigText.js.map +1 -1
  96. package/build/JsonConfigComponent/ConfigTextSendTo.d.ts +7 -9
  97. package/build/JsonConfigComponent/ConfigTextSendTo.js +11 -21
  98. package/build/JsonConfigComponent/ConfigTextSendTo.js.map +1 -1
  99. package/build/JsonConfigComponent/ConfigTimePicker.d.ts +9 -3
  100. package/build/JsonConfigComponent/ConfigTimePicker.js +28 -18
  101. package/build/JsonConfigComponent/ConfigTimePicker.js.map +1 -1
  102. package/build/JsonConfigComponent/ConfigTopic.d.ts +1 -1
  103. package/build/JsonConfigComponent/ConfigUUID.d.ts +12 -13
  104. package/build/JsonConfigComponent/ConfigUUID.js +1 -6
  105. package/build/JsonConfigComponent/ConfigUUID.js.map +1 -1
  106. package/build/JsonConfigComponent/ConfigUser.d.ts +1 -1
  107. package/build/JsonConfigComponent/index.d.ts +66 -25
  108. package/build/JsonConfigComponent/index.js +48 -61
  109. package/build/JsonConfigComponent/index.js.map +1 -1
  110. package/build/index.d.ts +2 -2
  111. package/build/index.js +2 -2
  112. package/build/index.js.map +1 -1
  113. package/package.json +27 -26
  114. package/src/JsonConfig.tsx +710 -0
  115. package/src/JsonConfigComponent/ChipInput.tsx +752 -0
  116. package/src/JsonConfigComponent/ConfigAccordion.tsx +278 -0
  117. package/src/JsonConfigComponent/ConfigAlive.tsx +74 -0
  118. package/src/JsonConfigComponent/ConfigAutocomplete.tsx +108 -0
  119. package/src/JsonConfigComponent/ConfigAutocompleteSendTo.tsx +183 -0
  120. package/src/JsonConfigComponent/ConfigCRON.jsx +101 -0
  121. package/src/JsonConfigComponent/ConfigCertCollection.tsx +102 -0
  122. package/src/JsonConfigComponent/ConfigCertificateSelect.tsx +92 -0
  123. package/src/JsonConfigComponent/ConfigCertificates.tsx +202 -0
  124. package/src/JsonConfigComponent/ConfigCheckLicense.jsx +662 -0
  125. package/src/JsonConfigComponent/ConfigCheckbox.tsx +67 -0
  126. package/src/JsonConfigComponent/ConfigChip.jsx +81 -0
  127. package/src/JsonConfigComponent/ConfigColor.tsx +86 -0
  128. package/src/JsonConfigComponent/ConfigCoordinates.tsx +234 -0
  129. package/src/JsonConfigComponent/ConfigCustom.tsx +246 -0
  130. package/src/JsonConfigComponent/ConfigDatePicker.tsx +48 -0
  131. package/src/JsonConfigComponent/ConfigDeviceManager.tsx +33 -0
  132. package/src/JsonConfigComponent/ConfigFile.jsx +181 -0
  133. package/src/JsonConfigComponent/ConfigFileSelector.jsx +520 -0
  134. package/src/JsonConfigComponent/ConfigFunc.jsx +90 -0
  135. package/src/JsonConfigComponent/ConfigGeneric.tsx +1027 -0
  136. package/src/JsonConfigComponent/ConfigIP.jsx +96 -0
  137. package/src/JsonConfigComponent/ConfigImageSendTo.jsx +79 -0
  138. package/src/JsonConfigComponent/ConfigImageUpload.jsx +114 -0
  139. package/src/JsonConfigComponent/ConfigInstanceSelect.jsx +172 -0
  140. package/src/JsonConfigComponent/ConfigInterface.jsx +112 -0
  141. package/src/JsonConfigComponent/ConfigJsonEditor.jsx +103 -0
  142. package/src/JsonConfigComponent/ConfigLanguage.tsx +153 -0
  143. package/src/JsonConfigComponent/ConfigLicense.jsx +148 -0
  144. package/src/JsonConfigComponent/ConfigNumber.tsx +207 -0
  145. package/src/JsonConfigComponent/ConfigObjectId.jsx +113 -0
  146. package/src/JsonConfigComponent/ConfigPanel.tsx +360 -0
  147. package/src/JsonConfigComponent/ConfigPassword.jsx +160 -0
  148. package/src/JsonConfigComponent/ConfigPattern.jsx +50 -0
  149. package/src/JsonConfigComponent/ConfigPort.tsx +232 -0
  150. package/src/JsonConfigComponent/ConfigRoom.jsx +90 -0
  151. package/src/JsonConfigComponent/ConfigSelect.jsx +124 -0
  152. package/src/JsonConfigComponent/ConfigSelectSendTo.tsx +251 -0
  153. package/src/JsonConfigComponent/ConfigSendto.tsx +340 -0
  154. package/src/JsonConfigComponent/ConfigSetState.jsx +116 -0
  155. package/src/JsonConfigComponent/ConfigSlider.jsx +97 -0
  156. package/src/JsonConfigComponent/ConfigStaticDivider.jsx +51 -0
  157. package/src/JsonConfigComponent/ConfigStaticHeader.jsx +63 -0
  158. package/src/JsonConfigComponent/ConfigStaticImage.jsx +48 -0
  159. package/src/JsonConfigComponent/ConfigStaticText.jsx +72 -0
  160. package/src/JsonConfigComponent/ConfigTable.tsx +1040 -0
  161. package/src/JsonConfigComponent/ConfigTabs.tsx +150 -0
  162. package/src/JsonConfigComponent/ConfigText.tsx +188 -0
  163. package/src/JsonConfigComponent/ConfigTextSendTo.tsx +102 -0
  164. package/src/JsonConfigComponent/ConfigTimePicker.tsx +63 -0
  165. package/src/JsonConfigComponent/ConfigTopic.jsx +78 -0
  166. package/src/JsonConfigComponent/ConfigUUID.tsx +55 -0
  167. package/src/JsonConfigComponent/ConfigUser.jsx +104 -0
  168. package/src/JsonConfigComponent/index.tsx +435 -0
  169. package/src/JsonConfigComponent/wrapper/Components/CustomModal.jsx +145 -0
  170. package/src/JsonConfigComponent/wrapper/Components/Editor.jsx +65 -0
  171. package/src/Utils.jsx +1683 -0
  172. package/src/index.tsx +14 -0
  173. package/src/types.d.ts +372 -0
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@mui/styles';
4
+
5
+ import {
6
+ InputLabel,
7
+ TextField,
8
+ MenuItem,
9
+ FormHelperText,
10
+ FormControl,
11
+ Select,
12
+ } from '@mui/material';
13
+
14
+ import { Icon, Utils, I18n } from '@iobroker/adapter-react-v5';
15
+
16
+ import ConfigGeneric from './ConfigGeneric';
17
+
18
+ const styles = () => ({
19
+ fullWidth: {
20
+ width: '100%',
21
+ },
22
+ icon: {
23
+ width: 16,
24
+ height: 16,
25
+ marginRight: 8,
26
+ },
27
+ });
28
+
29
+ class ConfigUser extends ConfigGeneric {
30
+ componentDidMount() {
31
+ super.componentDidMount();
32
+ this.props.socket.getUsers()
33
+ .then(users => {
34
+ const _users = {};
35
+ const lang = I18n.getLanguage();
36
+
37
+ if (this.props.schema.short) {
38
+ users.forEach(user => _users[user._id] = {
39
+ color: user.common?.color,
40
+ icon: user.common?.icon,
41
+ name: Utils.getObjectNameFromObj(user, lang),
42
+ });
43
+ } else {
44
+ users.forEach(user => _users[user._id.replace(/^system\.user\./, '')] = {
45
+ color: user.common?.color,
46
+ icon: user.common?.icon,
47
+ name: Utils.getObjectNameFromObj(user, lang),
48
+ });
49
+ }
50
+
51
+ this.setState({ users: _users });
52
+ });
53
+ }
54
+
55
+ renderItem(error, disabled /* , defaultValue */) {
56
+ const value = ConfigGeneric.getValue(this.props.data, this.props.attr);
57
+
58
+ return <FormControl variant="standard" className={this.props.classes.fullWidth}>
59
+ {this.state.users && this.props.schema.label ? <InputLabel>{this.getText(this.props.schema.label)}</InputLabel> : null}
60
+ {!this.state.users ?
61
+ <TextField
62
+ variant="standard"
63
+ error={!!error}
64
+ disabled={!!disabled}
65
+ value={value}
66
+ onChange={e => this.onChange(this.props.attr, e.target.value)}
67
+ label={this.getText(this.props.schema.label)}
68
+ />
69
+ :
70
+ <Select
71
+ variant="standard"
72
+ error={!!error}
73
+ disabled={!!disabled}
74
+ value={value}
75
+ renderValue={val => <span>
76
+ {this.state.users && this.state.users[val]?.icon ? <Icon src={this.state.users && this.state.users[val]?.icon} className={this.props.classes.icon} /> : null}
77
+ {(this.state.users && this.state.users[val]?.name) || val || ''}
78
+ </span>}
79
+ style={{ color: (this.state.users && this.state.users[value]?.color) || undefined, backgroundColor: Utils.getInvertedColor(this.state.users && this.state.users[value]?.color, this.props.themeType) }}
80
+ onChange={e => this.onChange(this.props.attr, e.target.value)}
81
+ >
82
+ {this.state.users && Object.keys(this.state.users).map(id => <MenuItem style={{ color: this.state.users[id].color || undefined, backgroundColor: Utils.getInvertedColor(this.state.users[id].color, this.props.themeType) }} key={id} value={id}>
83
+ {this.state.users[id].icon ? <Icon src={this.state.users[id].icon} className={this.props.classes.icon} /> : null}
84
+ {this.state.users[id].name}
85
+ </MenuItem>)}
86
+ </Select>}
87
+ {this.props.schema.help ? <FormHelperText>{this.renderHelp(this.props.schema.help, this.props.schema.helpLink, this.props.schema.noTranslation)}</FormHelperText> : null}
88
+ </FormControl>;
89
+ }
90
+ }
91
+
92
+ ConfigUser.propTypes = {
93
+ socket: PropTypes.object.isRequired,
94
+ themeType: PropTypes.string,
95
+ themeName: PropTypes.string,
96
+ style: PropTypes.object,
97
+ className: PropTypes.string,
98
+ data: PropTypes.object.isRequired,
99
+ schema: PropTypes.object,
100
+ onError: PropTypes.func,
101
+ onChange: PropTypes.func,
102
+ };
103
+
104
+ export default withStyles(styles)(ConfigUser);
@@ -0,0 +1,435 @@
1
+ import React, { Component } from 'react';
2
+ import { withStyles } from '@mui/styles';
3
+
4
+ import { LinearProgress } from '@mui/material';
5
+
6
+ import {type AdminConnection, I18n, Utils} from '@iobroker/adapter-react-v5';
7
+ import type { ThemeName, ThemeType } from '@iobroker/adapter-react-v5/types';
8
+
9
+ import type { ConfigItemPanel, ConfigItemTabs } from '#JC/types';
10
+ import ConfigTabs from './ConfigTabs';
11
+ import ConfigPanel from './ConfigPanel';
12
+
13
+ const styles: Record<string, any> = {
14
+ root: {
15
+ width: '100%',
16
+ height: '100%',
17
+ },
18
+ };
19
+
20
+ interface JsonConfigComponentProps {
21
+ socket: AdminConnection;
22
+ themeName: ThemeName;
23
+ themeType: ThemeType;
24
+ adapterName: string;
25
+ instance: number;
26
+ isFloatComma: boolean;
27
+ dateFormat: string;
28
+ imagePrefix?: string;
29
+ schema: ConfigItemTabs | ConfigItemPanel;
30
+ common?: Record<string, any>;
31
+ data: Record<string, any>;
32
+ updateData?: number;
33
+ onError: (error: boolean) => void;
34
+ onChange?: (data: Record<string, any>, changed: boolean, saveConfig: boolean) => void;
35
+ custom?: boolean;
36
+ onValueChange?: (attr: string, value: any, saveConfig: boolean) => void;
37
+ embedded?: boolean;
38
+ multiEdit?: boolean;
39
+ instanceObj?: ioBroker.InstanceObject;
40
+ customObj?: ioBroker.Object;
41
+ customs?: Record<string, Component>;
42
+ classes: Record<string, string>;
43
+ className?: string;
44
+ }
45
+
46
+ interface JsonConfigComponentState {
47
+ originalData: string;
48
+ changed: boolean;
49
+ errors: Record<string, string>;
50
+ systemConfig: ioBroker.SystemConfigCommon | null;
51
+ updateData?: number;
52
+ alive: boolean;
53
+ commandRunning: boolean;
54
+ schema: ConfigItemTabs | ConfigItemPanel;
55
+ data: Record<string, any> | null;
56
+ }
57
+
58
+ export class JsonConfigComponentClass extends Component<JsonConfigComponentProps, JsonConfigComponentState> {
59
+ private readonly forceUpdateHandlers: Record<string, (data: any) => void>;
60
+
61
+ private errorTimeout: ReturnType<typeof setTimeout> | null = null;
62
+
63
+ private errorCached: Record<string, string> | null = null;
64
+
65
+ constructor(props: JsonConfigComponentProps) {
66
+ super(props);
67
+
68
+ this.state = {
69
+ originalData: JSON.stringify(this.props.data),
70
+ // eslint-disable-next-line react/no-unused-state
71
+ changed: false,
72
+ errors: {},
73
+ updateData: this.props.updateData || 0,
74
+ systemConfig: null,
75
+ alive: false,
76
+ commandRunning: false,
77
+ schema: JSON.parse(JSON.stringify(this.props.schema)),
78
+ data: null,
79
+ };
80
+
81
+ this.forceUpdateHandlers = {};
82
+
83
+ this.buildDependencies(this.state.schema);
84
+
85
+ this.readData();
86
+ }
87
+
88
+ static getDerivedStateFromProps(props: JsonConfigComponentProps, state: JsonConfigComponentState) {
89
+ if (props.updateData !== state.updateData) {
90
+ return {
91
+ updateData: props.updateData,
92
+ originalData: JSON.stringify(props.data),
93
+ schema: JSON.parse(JSON.stringify(props.schema)),
94
+ };
95
+ }
96
+ return null;
97
+ }
98
+
99
+ static async loadI18n(socket: AdminConnection, i18n: boolean | string | Record<string, Record<ioBroker.Languages, string>>, adapterName: string) {
100
+ if (i18n === true || (i18n && typeof i18n === 'string')) {
101
+ const lang = I18n.getLanguage();
102
+ const path = typeof i18n === 'string' ? i18n : 'i18n';
103
+ let exists = await socket.fileExists(`${adapterName}.admin`, `${path}/${lang}.json`);
104
+ let fileName;
105
+ if (exists) {
106
+ fileName = `${path}/${lang}.json`;
107
+ } else {
108
+ exists = await socket.fileExists(`${adapterName}.admin`, `${path}/${lang}/translations.json`);
109
+ if (exists) {
110
+ fileName = `${path}/${lang}/translations.json`;
111
+ } else if (lang !== 'en') {
112
+ // fallback to english
113
+ exists = await socket.fileExists(`${adapterName}.admin`, `${path}/en.json`);
114
+ if (exists) {
115
+ fileName = `${path}/en.json`;
116
+ } else {
117
+ exists = await socket.fileExists(`${adapterName}.admin`, `${path}/en/translations.json`);
118
+ if (exists) {
119
+ fileName = `${path}/en/translations.json`;
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ if (fileName) {
126
+ const jsonFile = await socket.readFile(`${adapterName}.admin`, fileName);
127
+ let jsonStr: string;
128
+ if (jsonFile.file !== undefined) {
129
+ jsonStr = jsonFile.file;
130
+ } else {
131
+ // @ts-expect-error deprecated
132
+ jsonStr = jsonFile;
133
+ }
134
+
135
+ try {
136
+ const json = JSON.parse(jsonStr);
137
+ // apply file to I18n
138
+ I18n.extendTranslations(json, lang);
139
+ } catch (e) {
140
+ console.error(`Cannot parse language file "${adapterName}.admin/${fileName}: ${e}`);
141
+ return '';
142
+ }
143
+ return fileName;
144
+ }
145
+ console.warn(`Cannot find i18n for ${adapterName} / ${fileName}`);
146
+ return '';
147
+ } if (i18n && typeof i18n === 'object') {
148
+ I18n.extendTranslations(i18n);
149
+ return '';
150
+ }
151
+ return '';
152
+ }
153
+
154
+ onCommandRunning = (commandRunning: boolean) => this.setState({ commandRunning });
155
+
156
+ async readSettings() : Promise<void> {
157
+ if ((this.props.custom || this.props.common) && this.props.data) {
158
+ return;
159
+ }
160
+ const obj = await this.props.socket.getObject(`system.adapter.${this.props.adapterName}.${this.props.instance}`);
161
+ // eslint-disable-next-line react/no-unused-state
162
+ this.setState({ data: this.props.data || obj.native });
163
+ }
164
+
165
+ readData() {
166
+ this.readSettings()
167
+ .then(() => this.props.socket.getCompactSystemConfig())
168
+ .then(systemConfig =>
169
+ this.props.socket.getState(`system.adapter.${this.props.adapterName}.${this.props.instance}.alive`)
170
+ .then(state => {
171
+ if (this.props.custom) {
172
+ this.setState({ systemConfig: systemConfig.common, alive: !!(state && state.val) });
173
+ } else {
174
+ this.setState({ systemConfig: systemConfig.common, alive: !!(state && state.val) }, () =>
175
+ this.props.socket.subscribeState(`system.adapter.${this.props.adapterName}.${this.props.instance}.alive`, this.onAlive));
176
+ }
177
+ }));
178
+ }
179
+
180
+ onAlive = (id: string, state?: ioBroker.State | null) => {
181
+ if (!!(state?.val) !== this.state.alive) {
182
+ this.setState({ alive: !!state?.val });
183
+ }
184
+ };
185
+
186
+ onChange = (attrOrData: string | Record<string, any>, value: any, cb?: () => void, saveConfig?: boolean) => {
187
+ if (this.props.onValueChange) {
188
+ this.props.onValueChange(attrOrData as string, value, saveConfig);
189
+ cb && cb();
190
+ } else if (attrOrData && this.props.onChange) {
191
+ const newState: Partial<JsonConfigComponentState> = { data: attrOrData as Record<string, any> };
192
+
193
+ newState.changed = JSON.stringify(attrOrData) !== this.state.originalData;
194
+
195
+ this.setState(newState as JsonConfigComponentState, () => {
196
+ this.props.onChange(attrOrData as Record<string, any>, newState.changed, saveConfig);
197
+ cb && cb();
198
+ });
199
+ } else if (saveConfig) {
200
+ this.props.onChange(null, null, saveConfig);
201
+ }
202
+ };
203
+
204
+ onError = (attr: string, error?: string) => {
205
+ this.errorCached = this.errorCached || JSON.parse(JSON.stringify(this.state.errors));
206
+ const errors = this.errorCached;
207
+ if (error) {
208
+ errors[attr] = error;
209
+ } else {
210
+ delete errors[attr];
211
+ }
212
+
213
+ this.errorTimeout && clearTimeout(this.errorTimeout);
214
+ if (JSON.stringify(errors) !== JSON.stringify(this.state.errors)) {
215
+ this.errorTimeout = setTimeout(() =>
216
+ this.setState({ errors: this.errorCached }, () => {
217
+ this.errorTimeout = null;
218
+ this.errorCached = null;
219
+ this.props.onError(!!Object.keys(this.state.errors).length);
220
+ }), 50);
221
+ } else {
222
+ this.errorCached = null;
223
+ }
224
+ };
225
+
226
+ flatten(schema: Record<string, any>, _list?: Record<string, any>): Record<string, any> {
227
+ _list = _list || {};
228
+ if (schema.items) {
229
+ Object.keys(schema.items).forEach(attr => {
230
+ _list[attr] = schema.items[attr];
231
+ this.flatten(schema.items[attr], _list);
232
+ });
233
+ }
234
+
235
+ return _list;
236
+ }
237
+
238
+ buildDependencies(schema: ConfigItemTabs | ConfigItemPanel) {
239
+ const attrs = this.flatten(schema as Record<string, any>);
240
+ Object.keys(attrs).forEach(attr => {
241
+ if (attrs[attr].confirm?.alsoDependsOn) {
242
+ attrs[attr].confirm?.alsoDependsOn.forEach((dep: string) => {
243
+ if (!attrs[dep]) {
244
+ console.error(`[JsonConfigComponent] Attribute ${dep} does not exist!`);
245
+ if (dep.startsWith('data.')) {
246
+ console.warn(`[JsonConfigComponent] please use "${dep.replace(/^data\./, '')}" instead of "${dep}"`);
247
+ }
248
+ } else {
249
+ attrs[dep].confirmDependsOn = attrs[dep].confirmDependsOn || [];
250
+
251
+ const depObj = { ...attrs[attr], attr };
252
+ if (depObj.confirm) {
253
+ depObj.confirm.cancel = 'Undo';
254
+ }
255
+
256
+ attrs[dep].confirmDependsOn.push(depObj);
257
+ }
258
+ });
259
+ }
260
+
261
+ if (attrs[attr].onChange?.alsoDependsOn) {
262
+ attrs[attr].onChange?.alsoDependsOn.forEach((dep: string) => {
263
+ if (!attrs[dep]) {
264
+ console.error(`[JsonConfigComponent] Attribute ${dep} does not exist!`);
265
+ if (dep.startsWith('data.')) {
266
+ console.warn(`[JsonConfigComponent] please use "${dep.replace(/^data\./, '')}" instead of "${dep}"`);
267
+ }
268
+ } else {
269
+ attrs[dep].onChangeDependsOn = attrs[dep].onChangeDependsOn || [];
270
+
271
+ const depObj = { ...attrs[attr], attr };
272
+
273
+ attrs[dep].onChangeDependsOn.push(depObj);
274
+ }
275
+ });
276
+ }
277
+
278
+ if (attrs[attr].hidden?.alsoDependsOn) {
279
+ attrs[attr].hidden?.alsoDependsOn.forEach((dep: string) => {
280
+ if (!attrs[dep]) {
281
+ console.error(`[JsonConfigComponent] Attribute ${dep} does not exist!`);
282
+ if (dep.startsWith('data.')) {
283
+ console.warn(`[JsonConfigComponent] please use "${dep.replace(/^data\./, '')}" instead of "${dep}"`);
284
+ }
285
+ } else {
286
+ attrs[dep].hiddenDependsOn = attrs[dep].hiddenDependsOn || [];
287
+
288
+ const depObj = { ...attrs[attr], attr };
289
+
290
+ attrs[dep].hiddenDependsOn.push(depObj);
291
+ }
292
+ });
293
+ }
294
+
295
+ if (attrs[attr].label?.alsoDependsOn) {
296
+ attrs[attr].label?.alsoDependsOn.forEach((dep: string) => {
297
+ if (!attrs[dep]) {
298
+ console.error(`[JsonConfigComponent] Attribute ${dep} does not exist!`);
299
+ if (dep.startsWith('data.')) {
300
+ console.warn(`[JsonConfigComponent] please use "${dep.replace(/^data\./, '')}" instead of "${dep}"`);
301
+ }
302
+ } else {
303
+ attrs[dep].labelDependsOn = attrs[dep].labelDependsOn || [];
304
+
305
+ const depObj = { ...attrs[attr], attr };
306
+
307
+ attrs[dep].labelDependsOn.push(depObj);
308
+ }
309
+ });
310
+ }
311
+
312
+ if (attrs[attr].help?.alsoDependsOn) {
313
+ attrs[attr].help?.alsoDependsOn.forEach((dep: string) => {
314
+ if (!attrs[dep]) {
315
+ console.error(`[JsonConfigComponent] Attribute ${dep} does not exist!`);
316
+ if (dep.startsWith('data.')) {
317
+ console.warn(`[JsonConfigComponent] please use "${dep.replace(/^data\./, '')}" instead of "${dep}"`);
318
+ }
319
+ } else {
320
+ attrs[dep].helpDependsOn = attrs[dep].helpDependsOn || [];
321
+
322
+ const depObj = { ...attrs[attr], attr };
323
+
324
+ attrs[dep].helpDependsOn.push(depObj);
325
+ }
326
+ });
327
+ }
328
+ });
329
+ }
330
+
331
+ renderItem(item: ConfigItemTabs | ConfigItemPanel) {
332
+ if (item.type === 'tabs') {
333
+ return <ConfigTabs
334
+ onCommandRunning={this.onCommandRunning}
335
+ commandRunning={this.state.commandRunning}
336
+ socket={this.props.socket}
337
+ adapterName={this.props.adapterName}
338
+ instance={this.props.instance}
339
+ common={this.props.common}
340
+ alive={this.state.alive}
341
+ themeType={this.props.themeType}
342
+ themeName={this.props.themeName}
343
+ data={this.props.data}
344
+ originalData={JSON.parse(this.state.originalData)}
345
+ schema={item as ConfigItemTabs}
346
+ systemConfig={this.state.systemConfig}
347
+ customs={this.props.customs}
348
+ dateFormat={this.props.dateFormat}
349
+ isFloatComma={this.props.isFloatComma}
350
+ multiEdit={this.props.multiEdit}
351
+ imagePrefix={this.props.imagePrefix}
352
+ custom={this.props.custom}
353
+ customObj={this.props.customObj}
354
+ instanceObj={this.props.instanceObj}
355
+ changeLanguage={this.changeLanguage}
356
+ forceUpdate={this.forceAttrUpdate}
357
+ registerOnForceUpdate={this.registerOnForceUpdate}
358
+ onChange={this.onChange}
359
+ changed={this.state.changed}
360
+ onError={(attr, error) => this.onError(attr, error)}
361
+ />;
362
+ }
363
+ if (item.type === 'panel' ||
364
+ // @ts-expect-error type could be empty
365
+ !item.type
366
+ ) {
367
+ return <ConfigPanel
368
+ index={1000}
369
+ isParentTab={!this.props.embedded}
370
+ changed={this.state.changed}
371
+ onCommandRunning={this.onCommandRunning}
372
+ commandRunning={this.state.commandRunning}
373
+ socket={this.props.socket}
374
+ adapterName={this.props.adapterName}
375
+ instance={this.props.instance}
376
+ common={this.props.common}
377
+ alive={this.state.alive}
378
+ themeType={this.props.themeType}
379
+ themeName={this.props.themeName}
380
+ data={this.props.data}
381
+ originalData={JSON.parse(this.state.originalData)}
382
+ schema={item as ConfigItemPanel}
383
+ systemConfig={this.state.systemConfig}
384
+ customs={this.props.customs}
385
+ dateFormat={this.props.dateFormat}
386
+ isFloatComma={this.props.isFloatComma}
387
+ multiEdit={this.props.multiEdit}
388
+ imagePrefix={this.props.imagePrefix}
389
+ custom={this.props.custom}
390
+ customObj={this.props.customObj}
391
+ instanceObj={this.props.instanceObj}
392
+ changeLanguage={this.changeLanguage}
393
+ forceUpdate={this.forceAttrUpdate}
394
+ registerOnForceUpdate={this.registerOnForceUpdate}
395
+ onChange={this.onChange}
396
+ onError={(attr, error) => this.onError(attr, error)}
397
+ />;
398
+ }
399
+
400
+ return null;
401
+ }
402
+
403
+ changeLanguage = () => {
404
+ this.forceUpdate();
405
+ };
406
+
407
+ forceAttrUpdate = (attr: string | string[], data: any) => {
408
+ if (Array.isArray(attr)) {
409
+ attr.forEach(a =>
410
+ this.forceUpdateHandlers[a] && this.forceUpdateHandlers[a](data));
411
+ } else if (this.forceUpdateHandlers[attr]) {
412
+ this.forceUpdateHandlers[attr](data);
413
+ }
414
+ };
415
+
416
+ registerOnForceUpdate = (attr: string, cb: (data: any) => void) => {
417
+ if (cb) {
418
+ this.forceUpdateHandlers[attr] = cb;
419
+ } else if (this.forceUpdateHandlers[attr]) {
420
+ delete this.forceUpdateHandlers[attr];
421
+ }
422
+ };
423
+
424
+ render() {
425
+ if (!this.state.systemConfig) {
426
+ return <LinearProgress />;
427
+ }
428
+
429
+ return <div className={Utils.clsx(!this.props.embedded && this.props.classes.root, this.props.className)} style={this.state.schema.style}>
430
+ {this.renderItem(this.state.schema)}
431
+ </div>;
432
+ }
433
+ }
434
+
435
+ export default withStyles(styles)(JsonConfigComponentClass);
@@ -0,0 +1,145 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@mui/styles';
4
+
5
+ import {
6
+ Dialog, DialogActions, DialogContent,
7
+ DialogTitle, IconButton, TextField, Button,
8
+ } from '@mui/material';
9
+
10
+ import {
11
+ Check as CheckIcon,
12
+ Close as CloseIcon,
13
+ Language as LanguageIcon,
14
+ } from '@mui/icons-material';
15
+
16
+ import { Utils, I18n } from '@iobroker/adapter-react-v5';
17
+
18
+ const styles = theme => ({
19
+ modalDialog: {
20
+ minWidth: 400,
21
+ maxWidth: 800,
22
+ },
23
+ overflowHidden: {
24
+ display: 'flex',
25
+ overflow: 'hidden',
26
+ },
27
+ titleIcon: {
28
+ marginRight: 5,
29
+ },
30
+ content: {
31
+ fontSize: 16,
32
+ },
33
+ languageButton: {
34
+ position: 'absolute',
35
+ right: theme.spacing(1),
36
+ top: theme.spacing(1),
37
+ },
38
+ languageButtonActive: {
39
+ color: theme.palette.primary.main,
40
+ },
41
+ });
42
+
43
+ const CustomModal = ({
44
+ toggleTranslation, noTranslation, title, fullWidth, help, maxWidth, progress, icon, applyDisabled, applyButton, classes, onClose, children, titleButtonApply, titleButtonClose, onApply, textInput, defaultValue, overflowHidden,
45
+ }) => {
46
+ const [value, setValue] = useState(defaultValue);
47
+ useEffect(() => {
48
+ setValue(defaultValue);
49
+ }, [defaultValue]);
50
+
51
+ let Icon = null;
52
+
53
+ if (icon) {
54
+ Icon = icon;
55
+ }
56
+
57
+ return <Dialog
58
+ open={!0}
59
+ maxWidth={maxWidth || 'md'}
60
+ fullWidth={!!fullWidth}
61
+ disableEscapeKeyDown={false}
62
+ onClose={onClose}
63
+ classes={{ paper: classes.modalDialog /* paper: classes.background */ }}
64
+ >
65
+ {title && <DialogTitle>
66
+ {icon ? <Icon className={classes.titleIcon} /> : null}
67
+ {title}
68
+ {I18n.getLanguage() !== 'en' && toggleTranslation ? <IconButton
69
+ size="large"
70
+ className={Utils.clsx(classes.languageButton, noTranslation && classes.languageButtonActive)}
71
+ onClick={() => toggleTranslation()}
72
+ title={I18n.t('Disable/Enable translation')}
73
+ >
74
+ <LanguageIcon />
75
+ </IconButton> : null}
76
+ </DialogTitle>}
77
+ <DialogContent className={Utils.clsx(overflowHidden ? classes.overflowHidden : null, classes.content)} style={{ paddingTop: 8 }}>
78
+ {textInput && <TextField
79
+ // className={className}
80
+ autoComplete="off"
81
+ fullWidth
82
+ autoFocus
83
+ variant="outlined"
84
+ size="medium"
85
+ // rows={10}
86
+ multiline
87
+ value={value}
88
+ onChange={e => setValue(e.target.value)}
89
+ // customValue
90
+ />}
91
+ {children}
92
+ {help ? <div>{help}</div> : null}
93
+ </DialogContent>
94
+ <DialogActions>
95
+ {applyButton && <Button
96
+ startIcon={<CheckIcon />}
97
+ disabled={progress || (applyDisabled && defaultValue === value)}
98
+ onClick={() => onApply(textInput ? value : '')}
99
+ variant="contained"
100
+ color="primary"
101
+ >
102
+ {I18n.t(titleButtonApply)}
103
+ </Button>}
104
+ <Button
105
+ color="grey"
106
+ onClick={onClose}
107
+ disabled={progress}
108
+ variant="contained"
109
+ startIcon={<CloseIcon />}
110
+ >
111
+ {I18n.t(titleButtonClose)}
112
+ </Button>
113
+ </DialogActions>
114
+ </Dialog>;
115
+ };
116
+
117
+ CustomModal.defaultProps = {
118
+ onApply: () => { },
119
+ onClose: () => { },
120
+ applyButton: true,
121
+ applyDisabled: false,
122
+ titleButtonClose: 'Cancel',
123
+ titleButtonApply: 'Ok',
124
+ overflowHidden: false,
125
+ help: '',
126
+ };
127
+
128
+ CustomModal.propTypes = {
129
+ icon: PropTypes.object,
130
+ onClose: PropTypes.func,
131
+ children: PropTypes.any,
132
+ titleButtonClose: PropTypes.string,
133
+ titleButtonApply: PropTypes.string,
134
+ onApply: PropTypes.func,
135
+ fullWidth: PropTypes.bool,
136
+ maxWidth: PropTypes.string,
137
+ applyButton: PropTypes.bool,
138
+ applyDisabled: PropTypes.bool,
139
+ overflowHidden: PropTypes.bool,
140
+ help: PropTypes.string,
141
+ noTranslation: PropTypes.bool,
142
+ toggleTranslation: PropTypes.func,
143
+ };
144
+
145
+ export default withStyles(styles)(CustomModal);