@iobroker/json-config 6.17.6 → 6.17.12

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,246 @@
1
+ import React, { Component } from 'react';
2
+ import { Grid, LinearProgress } from '@mui/material';
3
+
4
+ import { I18n } from '@iobroker/adapter-react-v5';
5
+ import type { ConfigItemCustom } from '#JC/types';
6
+ import type { ConfigGenericProps } from '#JC/JsonConfigComponent/ConfigGeneric';
7
+
8
+ const getOrLoadRemote = (
9
+ remote: string,
10
+ shareScope: string,
11
+ remoteFallbackUrl?: string,
12
+ ): Promise<{get: (module: string) => () => Promise<{ default: Record<string, React.Component> }>}> =>
13
+ new Promise((resolve, reject) => {
14
+ // check if remote exists on the global `window`object
15
+ if (!(window as any)[remote]) {
16
+ // search dom to see if remote tag exists, but might still be loading (async)
17
+ const existingRemote: HTMLScriptElement = document.querySelector(`script[data-webpack="${remote}"]`);
18
+ // when remote is loaded.
19
+ const onload = async () => {
20
+ // check if it was initialized
21
+ if ((window as any)[remote]) {
22
+ if (!(window as any)[remote].__initialized) {
23
+ // if share scope doesn't exist (like in webpack 4) then expect shareScope to be a manual object
24
+ // eslint-disable-next-line camelcase
25
+ // @ts-expect-error it is a trick and must be so
26
+ if (typeof __webpack_share_scopes__ === 'undefined') {
27
+ // use a default share scope object passed in manually
28
+ await (window as any)[remote].init(shareScope);
29
+ } else {
30
+ // otherwise, init share scope as usual
31
+ // eslint-disable-next-line
32
+ // @ts-expect-error it is a trick and must be so
33
+ await (window as any)[remote].init((__webpack_share_scopes__ as any)[shareScope]);
34
+ }
35
+ // mark remote as initialized
36
+ (window as any)[remote].__initialized = true;
37
+ }
38
+ } else {
39
+ console.error(`Cannot load ${remote}`);
40
+ reject(new Error(`Cannot load ${remote}`));
41
+ return;
42
+ }
43
+ // resolve promise so marking remote as loaded
44
+ resolve((window as any)[remote]);
45
+ };
46
+
47
+ if (existingRemote) {
48
+ // if existing remote but not loaded, hook into its onload and wait for it to be ready
49
+ existingRemote.onload = onload;
50
+ existingRemote.onerror = reject;
51
+ // check if remote fallback exists as param passed to function
52
+ // TODO: should scan public config for a matching key if no override exists
53
+ } else if (remoteFallbackUrl) {
54
+ // inject remote if a fallback exists and call the same onload function
55
+ const d = document;
56
+ const script = d.createElement('script');
57
+ script.type = 'text/javascript';
58
+ // mark as data-webpack so runtime can track it internally
59
+ script.setAttribute('data-webpack', `${remote}`);
60
+ script.async = true;
61
+ script.onerror = reject;
62
+ script.onload = onload;
63
+ script.src = remoteFallbackUrl;
64
+ d.getElementsByTagName('head')[0].appendChild(script);
65
+ } else {
66
+ // no remote and no fallback exist, reject
67
+ reject(new Error(`Cannot Find Remote ${remote} to inject`));
68
+ }
69
+ } else {
70
+ // remote already instantiated, resolve
71
+ resolve((window as any)[remote]);
72
+ }
73
+ });
74
+
75
+ function loadComponent(
76
+ remote: string,
77
+ sharedScope: string,
78
+ module: string,
79
+ url: string,
80
+ ): () => Promise<{ default: Record<string, React.Component> }> {
81
+ return async (): Promise<{ default: Record<string, React.Component> }> => {
82
+ const container = await getOrLoadRemote(remote, sharedScope, url);
83
+ const factory = await container.get(module);
84
+ return factory();
85
+ };
86
+ }
87
+
88
+ interface ConfigCustomProps extends ConfigGenericProps {
89
+ schema: ConfigItemCustom;
90
+ }
91
+
92
+ interface ConfigCustomState {
93
+ Component: Component | null;
94
+ error: string;
95
+ }
96
+
97
+ export default class ConfigCustom extends Component<ConfigCustomProps, ConfigCustomState> {
98
+ static runningLoads: Record<string, Promise<{ default: Record<string, React.Component> }>> = {};
99
+
100
+ constructor(props: ConfigCustomProps) {
101
+ super(props);
102
+ // schema.url - location of Widget
103
+ // schema.name - Component name
104
+ // schema.i18n - i18n
105
+
106
+ this.state = {
107
+ Component: null,
108
+ error: '',
109
+ };
110
+ }
111
+
112
+ // load component dynamically
113
+ async componentDidMount() {
114
+ if (!this.props.schema.url) {
115
+ console.error('URL is empty. Cannot load custom component!');
116
+ this.setState({ error: 'URL is empty. Cannot load custom component!' });
117
+ return;
118
+ }
119
+
120
+ let url;
121
+ /*
122
+ if (this.props.schema.url.startsWith('http:') || this.props.schema.url.startsWith('https:')) {
123
+ url = this.props.schema.url;
124
+ } else
125
+ */
126
+ if (this.props.schema.url.startsWith('./')) {
127
+ url = `${window.location.protocol}//${window.location.host}${this.props.schema.url.replace(/^\./, '')}`;
128
+ } else {
129
+ url = `${window.location.protocol}//${window.location.host}/adapter/${this.props.adapterName}/${this.props.schema.url}`;
130
+ }
131
+ const [uniqueName, fileToLoad, ...componentNameParts] = this.props.schema.name.split('/');
132
+ const componentName = componentNameParts.join('/');
133
+ if (!url) {
134
+ console.error('Cannot find URL for custom component! Please define "url" as "custom/customComponents.js" in the schema');
135
+ return;
136
+ }
137
+ if (!uniqueName || !fileToLoad || !componentName) {
138
+ console.error('Invalid format of "name"! Please define "name" as "ConfigCustomBackItUpSet/Components/AdapterExist" in the schema');
139
+ return;
140
+ }
141
+ let setPromise = ConfigCustom.runningLoads[`${url}!${fileToLoad}`];
142
+
143
+ if (!setPromise) {
144
+ let i18nPromise;
145
+ if (this.props.schema.i18n === true) {
146
+ // load i18n from files
147
+ const pos = url.lastIndexOf('/');
148
+ let i18nURL;
149
+ if (pos !== -1) {
150
+ i18nURL = url.substring(0, pos);
151
+ } else {
152
+ i18nURL = url;
153
+ }
154
+ const lang = I18n.getLanguage();
155
+ const file = `${i18nURL}/i18n/${lang}.json`;
156
+
157
+ i18nPromise = fetch(file)
158
+ .then(data => data.json())
159
+ .then(json => I18n.extendTranslations(json, lang))
160
+ .catch(error => {
161
+ if (lang !== 'en') {
162
+ // try to load English
163
+ fetch(`${i18nURL}/i18n/en.json`)
164
+ .then(data => data.json())
165
+ .then(json => I18n.extendTranslations(json, lang))
166
+ .catch(err => console.log(`Cannot load i18n "${file}": ${err}`));
167
+ return;
168
+ }
169
+ console.log(`Cannot load i18n "${file}": ${error}`);
170
+ });
171
+ } else if (this.props.schema.i18n && typeof this.props.schema.i18n === 'object') {
172
+ try {
173
+ I18n.extendTranslations(this.props.schema.i18n);
174
+ } catch (error) {
175
+ console.error(`Cannot import i18n: ${error}`);
176
+ }
177
+ }
178
+ try {
179
+ console.log(uniqueName, fileToLoad, componentName);
180
+ setPromise = loadComponent(uniqueName, 'default', `./${fileToLoad}`, url)();
181
+ if (i18nPromise) {
182
+ setPromise = Promise.all([setPromise, i18nPromise])
183
+ .then(result => result[0]);
184
+ }
185
+ // remember promise
186
+ ConfigCustom.runningLoads[`${url}!${fileToLoad}`] = setPromise;
187
+ } catch (error) {
188
+ this.setState({ error: `Cannot import from ${this.props.schema.url}: ${error}` });
189
+ }
190
+ }
191
+
192
+ try {
193
+ const component = (await setPromise).default;
194
+
195
+ if (!component?.[componentName]) {
196
+ const keys = Object.keys(component || {});
197
+ console.error('URL is empty. Cannot load custom component!');
198
+ this.setState({ error: `Component ${this.props.schema.name} not found in ${this.props.schema.url}. Found: ${keys.join(', ')}` });
199
+ } else {
200
+ this.setState({ Component: component[componentName] });
201
+ }
202
+ } catch (error) {
203
+ this.setState({ error: `Cannot import from ${this.props.schema.url}: ${error}` });
204
+ }
205
+ }
206
+
207
+ render() {
208
+ const CustomComponent: Component = this.state.Component;
209
+
210
+ // render temporary placeholder
211
+ if (!CustomComponent) {
212
+ if (this.state.error) {
213
+ return null;
214
+ }
215
+ const schema = this.props.schema || {} as ConfigItemCustom;
216
+
217
+ const item = <Grid
218
+ item
219
+ xs={schema.xs || undefined}
220
+ lg={schema.lg || undefined}
221
+ md={schema.md || undefined}
222
+ sm={schema.sm || undefined}
223
+ style={({
224
+ marginBottom: 0,
225
+ // marginRight: 8,
226
+ textAlign: 'left',
227
+ ...schema.style,
228
+ ...(this.props.themeType === 'dark' ? schema.darkStyle : {}),
229
+ })}
230
+ >
231
+ {this.state.error ? <div>{this.state.error}</div> : <LinearProgress />}
232
+ </Grid>;
233
+
234
+ if (schema.newLine) {
235
+ return <>
236
+ <div style={{ flexBasis: '100%', height: 0 }} />
237
+ {item}
238
+ </>;
239
+ }
240
+ return item;
241
+ }
242
+
243
+ // @ts-expect-error No idea how to solve it
244
+ return <CustomComponent {...this.props} />;
245
+ }
246
+ }
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+
3
+ import { DatePicker } from '@mui/x-date-pickers';
4
+
5
+ import type { ConfigItemDatePicker } from '#JC/types';
6
+ import ConfigGeneric, { type ConfigGenericProps } from './ConfigGeneric';
7
+
8
+ interface ConfigDatePickerProps extends ConfigGenericProps {
9
+ schema: ConfigItemDatePicker;
10
+ }
11
+
12
+ export default class ConfigDatePicker extends ConfigGeneric<ConfigDatePickerProps> {
13
+ componentDidMount() {
14
+ super.componentDidMount();
15
+ const value = ConfigGeneric.getValue(this.props.data, this.props.attr);
16
+ this.setState({ value });
17
+ }
18
+
19
+ renderItem(_error: unknown, disabled: boolean /* , defaultValue */): React.JSX.Element {
20
+ return <DatePicker
21
+ sx={theme => ({
22
+ width: '100%',
23
+ borderBottom: `1px solid ${theme.palette.text.primary}`,
24
+ '& fieldset': {
25
+ display: 'none',
26
+ },
27
+ '& input': {
28
+ padding: `${theme.spacing(1.5)} 0 4px 0`,
29
+ },
30
+ '& .MuiInputAdornment-root': {
31
+ marginLeft: 0,
32
+ marginTop: 1, // it is already in spaces
33
+ },
34
+ '& label': {
35
+ transform: 'translate(0px, -9px) scale(0.75)',
36
+ },
37
+ })}
38
+ format={this.props.systemConfig.dateFormat.toLowerCase().replace('mm', 'MM')}
39
+ disabled={!!disabled}
40
+ value={this.state.value as never}
41
+ onChange={value => {
42
+ this.setState({ value }, () =>
43
+ this.onChange(this.props.attr, this.state.value));
44
+ }}
45
+ label={this.getText(this.props.schema.label)}
46
+ />;
47
+ }
48
+ }
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+
3
+ import DeviceManager from '@iobroker/dm-gui-components';
4
+
5
+ import type { ConfigItemDeviceManager } from '#JC/types';
6
+ import ConfigGeneric, { type ConfigGenericProps, type ConfigGenericState } from './ConfigGeneric';
7
+
8
+ interface ConfigDeviceManagerProps extends ConfigGenericProps {
9
+ schema: ConfigItemDeviceManager;
10
+ }
11
+
12
+ class ConfigDeviceManager extends ConfigGeneric<ConfigDeviceManagerProps, ConfigGenericState> {
13
+ renderItem(): React.JSX.Element | null {
14
+ const schema = this.props.schema;
15
+
16
+ if (!schema) {
17
+ return null;
18
+ }
19
+
20
+ return <DeviceManager
21
+ uploadImagesToInstance={`${this.props.adapterName}.${this.props.instance}`}
22
+ title={this.getText(this.props.schema.label)}
23
+ socket={this.props.socket}
24
+ selectedInstance={`${this.props.adapterName}.${this.props.instance}`}
25
+ themeName={this.props.themeName}
26
+ themeType={this.props.themeType}
27
+ isFloatComma={this.props.isFloatComma}
28
+ dateFormat={this.props.dateFormat}
29
+ />;
30
+ }
31
+ }
32
+
33
+ export default ConfigDeviceManager;
@@ -0,0 +1,181 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@mui/styles';
4
+
5
+ import {
6
+ Button,
7
+ TextField,
8
+ IconButton,
9
+ } from '@mui/material';
10
+
11
+ import {
12
+ Article as IconText,
13
+ Code as IconCode,
14
+ PlayArrow as IconPlay,
15
+ Videocam as IconVideo,
16
+ } from '@mui/icons-material';
17
+
18
+ import { SelectFile as SelectFileDialog } from '@iobroker/adapter-react-v5';
19
+
20
+ import ConfigGeneric from './ConfigGeneric';
21
+ import ConfigFileSelector from './ConfigFileSelector';
22
+
23
+ const styles = () => ({
24
+ fullWidth: {
25
+ width: '100%',
26
+ },
27
+ fullWidthOneButton: {
28
+ width: 'calc(100% - 69px)',
29
+ marginRight: 4,
30
+ },
31
+ fullWidthIcon: {
32
+ width: 'calc(100% - 119px)',
33
+ marginRight: 4,
34
+ },
35
+ selectedImage: {
36
+ height: 40,
37
+ width: 40,
38
+ display: 'inline-block',
39
+ marginRight: 8,
40
+ },
41
+ });
42
+
43
+ const IMAGE_EXT = ['jpg', 'jpeg', 'svg', 'png', 'webp', 'gif', 'apng', 'avif', 'webp'];
44
+ const AUDIO_EXT = ['mp3', 'ogg', 'wav', 'aac'];
45
+ const VIDEO_EXT = ['avi', 'mp4', 'mov'];
46
+ const DOC_EXT = ['txt', 'log', 'html', 'htm'];
47
+ const JS_EXT = ['json', 'js', 'ts'];
48
+
49
+ class ConfigFile extends ConfigGeneric {
50
+ componentDidMount() {
51
+ super.componentDidMount();
52
+ const value = ConfigGeneric.getValue(this.props.data, this.props.attr);
53
+ this.imagePrefix = this.props.imagePrefix === undefined ? './files' : this.props.imagePrefix;
54
+ this.setState({ value: value ?? '' });
55
+ }
56
+
57
+ static getDerivedStateFromProps(props, state) {
58
+ const value = ConfigGeneric.getValue(props.data, props.attr);
59
+ if (value === null || value === undefined || value.toString().trim() !== (state.value || '').toString().trim()) {
60
+ return { value: value ?? '' };
61
+ }
62
+ return null;
63
+ }
64
+
65
+ loadFile() {
66
+ const pos = this.state.value.indexOf('/');
67
+ if (pos !== -1) {
68
+ const adapter = this.state.value.substring(0, pos);
69
+ const path = this.state.value.substring(pos + 1);
70
+ return this.props.socket.readFile(adapter, path, true);
71
+ }
72
+
73
+ return Promise.resolve(null);
74
+ }
75
+
76
+ play() {
77
+ this.loadFile()
78
+ .then(data => {
79
+ if (typeof AudioContext !== 'undefined' && data?.file) {
80
+ const context = new AudioContext();
81
+ const buf = ConfigFileSelector.base64ToArrayBuffer(data.file);
82
+ context.decodeAudioData(buf, buffer => {
83
+ const source = context.createBufferSource(); // creates a sound source
84
+ source.buffer = buffer; // tell the source which sounds to play
85
+ source.connect(context.destination); // connect the source to the context's destination (the speakers)
86
+ source.start(0);
87
+ }, err => window.alert(`Cannot play: ${err}`));
88
+ }
89
+ });
90
+ }
91
+
92
+ getIcon() {
93
+ const extension = this.state.value.split('.').pop().toLowerCase();
94
+ if (IMAGE_EXT.includes(extension)) {
95
+ return <div
96
+ className={this.props.classes.selectedImage}
97
+ style={{
98
+ backgroundImage: `url(${this.imagePrefix}/${this.state.value})`,
99
+ backgroundSize: 'contain',
100
+ backgroundRepeat: 'no-repeat',
101
+ }}
102
+ />;
103
+ } if (AUDIO_EXT.includes(extension)) {
104
+ return <IconButton style={{ color: '#00FF00' }} onClick={() => this.play()}><IconPlay /></IconButton>;
105
+ } if (DOC_EXT.includes(extension)) {
106
+ return <IconText />;
107
+ } if (VIDEO_EXT.includes(extension)) {
108
+ return <IconVideo />;
109
+ } if (JS_EXT.includes(extension)) {
110
+ return <IconCode />;
111
+ }
112
+ return null;
113
+ }
114
+
115
+ renderFileBrowser() {
116
+ if (!this.state.showFileBrowser) {
117
+ return null;
118
+ }
119
+ return <SelectFileDialog
120
+ imagePrefix={this.props.imagePrefix}
121
+ socket={this.props.socket}
122
+ selected={this.state.value}
123
+ onClose={() => this.setState({ showFileBrowser: false })}
124
+ onOk={value => {
125
+ this.setState({ value }, () =>
126
+ this.onChange(this.props.attr, this.props.schema.trim === false ? value : (value || '').trim()));
127
+ }}
128
+ selectOnlyFolders={this.props.schema.selectOnlyFolders}
129
+ allowUpload={this.props.schema.allowUpload}
130
+ allowDownload={this.props.schema.allowDownload}
131
+ allowCreateFolder={this.props.schema.allowCreateFolder}
132
+ allowView={this.props.schema.allowView}
133
+ showToolbar={this.props.schema.showToolbar}
134
+ limitPath={this.props.schema.limitPath}
135
+ />;
136
+ }
137
+
138
+ renderItem(error, disabled /* , defaultValue */) {
139
+ const icon = this.getIcon();
140
+
141
+ return <div className={this.props.classes.fullWidth}>
142
+ {icon}
143
+ <TextField
144
+ variant="standard"
145
+ className={icon ? this.props.classes.fullWidthIcon : this.props.classes.fullWidthOneButton}
146
+ value={this.state.value === null || this.state.value === undefined ? '' : this.state.value}
147
+ error={!!error}
148
+ disabled={!!disabled}
149
+ inputProps={{
150
+ maxLength: this.props.schema.maxLength || this.props.schema.max || undefined,
151
+ readOnly: !!this.props.schema.disableEdit,
152
+ }}
153
+ onChange={e => {
154
+ const value = e.target.value;
155
+ this.setState({ value }, () =>
156
+ this.onChange(this.props.attr, this.props.schema.trim === false ? value : (value || '').trim()));
157
+ }}
158
+ placeholder={this.getText(this.props.schema.placeholder)}
159
+ label={this.getText(this.props.schema.label)}
160
+ helperText={this.renderHelp(this.props.schema.help, this.props.schema.helpLink, this.props.schema.noTranslation)}
161
+ />
162
+ <Button variant="outlined" onClick={() => this.setState({ showFileBrowser: true })}>...</Button>
163
+ {this.renderFileBrowser()}
164
+ </div>;
165
+ }
166
+ }
167
+
168
+ ConfigFile.propTypes = {
169
+ socket: PropTypes.object.isRequired,
170
+ themeType: PropTypes.string,
171
+ themeName: PropTypes.string,
172
+ style: PropTypes.object,
173
+ className: PropTypes.string,
174
+ data: PropTypes.object.isRequired,
175
+ schema: PropTypes.object,
176
+ onError: PropTypes.func,
177
+ onChange: PropTypes.func,
178
+ imagePrefix: PropTypes.func,
179
+ };
180
+
181
+ export default withStyles(styles)(ConfigFile);