@iobroker/dm-gui-components 0.0.2 → 0.0.4
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.
- package/DeviceCard.d.ts +36 -4
- package/DeviceCard.js +271 -142
- package/DeviceList.d.ts +3 -2
- package/DeviceList.js +8 -8
- package/README.md +4 -1
- package/package.json +1 -1
package/DeviceCard.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
2
|
import { Connection } from '@iobroker/adapter-react-v5';
|
|
3
|
-
import { DeviceInfo } from '@iobroker/dm-utils';
|
|
3
|
+
import { DeviceDetails, DeviceInfo } from '@iobroker/dm-utils';
|
|
4
4
|
import { ActionBase, ControlBase, ControlState } from '@iobroker/dm-utils/build/types/base';
|
|
5
5
|
interface DeviceCardProps {
|
|
6
6
|
title?: string;
|
|
@@ -12,9 +12,41 @@ interface DeviceCardProps {
|
|
|
12
12
|
deviceHandler: (deviceId: string, action: ActionBase<'api'>, refresh: () => void) => () => void;
|
|
13
13
|
controlHandler: (deviceId: string, control: ControlBase, state: ControlState) => () => Promise<ioBroker.State | null>;
|
|
14
14
|
controlStateHandler: (deviceId: string, control: ControlBase) => () => Promise<ioBroker.State | null>;
|
|
15
|
+
smallCards?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface DeviceCardState {
|
|
18
|
+
open: boolean;
|
|
19
|
+
details: DeviceDetails | null;
|
|
20
|
+
data: Record<string, any>;
|
|
21
|
+
icon: string | undefined;
|
|
22
|
+
showControlDialog: boolean;
|
|
15
23
|
}
|
|
16
24
|
/**
|
|
17
25
|
* Device Card Component
|
|
18
26
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
declare class DeviceCard extends Component<DeviceCardProps, DeviceCardState> {
|
|
28
|
+
constructor(props: DeviceCardProps);
|
|
29
|
+
fetchIcon(): Promise<void>;
|
|
30
|
+
componentDidMount(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Load the device details
|
|
33
|
+
*/
|
|
34
|
+
loadDetails(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Refresh the device details
|
|
37
|
+
*/
|
|
38
|
+
refresh: () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Copy the device ID to the clipboard
|
|
41
|
+
* @returns {void}
|
|
42
|
+
*/
|
|
43
|
+
copyToClipboard: () => Promise<void>;
|
|
44
|
+
renderDialog(): React.JSX.Element;
|
|
45
|
+
renderControlDialog(): React.JSX.Element;
|
|
46
|
+
renderControls(): React.JSX.Element;
|
|
47
|
+
renderActions(): React.JSX.Element[];
|
|
48
|
+
renderSmall(): React.JSX.Element;
|
|
49
|
+
renderBig(): React.JSX.Element;
|
|
50
|
+
render(): React.JSX.Element;
|
|
51
|
+
}
|
|
52
|
+
export default DeviceCard;
|
package/DeviceCard.js
CHANGED
|
@@ -47,162 +47,291 @@ function getText(text) {
|
|
|
47
47
|
/**
|
|
48
48
|
* Device Card Component
|
|
49
49
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
50
|
+
class DeviceCard extends react_1.Component {
|
|
51
|
+
constructor(props) {
|
|
52
|
+
super(props);
|
|
53
|
+
/**
|
|
54
|
+
* Refresh the device details
|
|
55
|
+
*/
|
|
56
|
+
this.refresh = () => {
|
|
57
|
+
this.setState({ details: null });
|
|
58
|
+
this.loadDetails().catch(console.error);
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Copy the device ID to the clipboard
|
|
62
|
+
* @returns {void}
|
|
63
|
+
*/
|
|
64
|
+
this.copyToClipboard = async () => {
|
|
65
|
+
const textToCopy = this.props.device.id;
|
|
66
|
+
adapter_react_v5_1.Utils.copyToClipboard(textToCopy);
|
|
67
|
+
alert(`${(0, Utils_1.getTranslation)('copied')} ${textToCopy} ${(0, Utils_1.getTranslation)('toClipboard')}!`);
|
|
68
|
+
};
|
|
69
|
+
this.state = {
|
|
70
|
+
open: false,
|
|
71
|
+
details: null,
|
|
72
|
+
data: {},
|
|
73
|
+
icon: props.device.icon,
|
|
74
|
+
showControlDialog: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async fetchIcon() {
|
|
78
|
+
if (!this.props.device.icon) {
|
|
79
|
+
// try to load the icon from file storage
|
|
80
|
+
const fileName = `${this.props.device.manufacturer ? `${this.props.device.manufacturer}_` : ''}${this.props.device.model ? this.props.device.model : this.props.device.id}`;
|
|
81
|
+
try {
|
|
82
|
+
const file = await this.props.socket.readFile(this.props.instanceId.replace('system.adapter.', ''), `${fileName}.webp`, true);
|
|
83
|
+
this.setState({ icon: `data:image/${file.mimeType},${file}` });
|
|
84
|
+
// const response = await fetch(url);
|
|
85
|
+
// if (response.ok) {
|
|
86
|
+
// const blob = await response.blob();
|
|
87
|
+
// const reader = new FileReader();
|
|
88
|
+
// reader.onloadend = () => {
|
|
89
|
+
// setIcon(reader.result);
|
|
90
|
+
// };
|
|
91
|
+
// reader.readAsDataURL(blob);
|
|
92
|
+
// } else {
|
|
93
|
+
// throw new Error('Response not ok');
|
|
94
|
+
// }
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
this.state.icon && this.setState({ icon: '' });
|
|
85
98
|
}
|
|
86
99
|
}
|
|
87
|
-
|
|
100
|
+
}
|
|
101
|
+
componentDidMount() {
|
|
102
|
+
this.fetchIcon()
|
|
88
103
|
.catch(e => console.error(e));
|
|
89
|
-
}
|
|
104
|
+
}
|
|
90
105
|
/**
|
|
91
106
|
* Load the device details
|
|
92
107
|
*/
|
|
93
|
-
|
|
94
|
-
console.log(`Loading device details for ${device.id}... from ${instanceId}`);
|
|
95
|
-
const
|
|
96
|
-
console.log(`Got device details for ${device.id}:`,
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
setDetails(null);
|
|
104
|
-
loadDetails().catch(console.error);
|
|
105
|
-
};
|
|
106
|
-
/**
|
|
107
|
-
* Open the modal
|
|
108
|
-
*/
|
|
109
|
-
const openModal = () => {
|
|
110
|
-
if (!open) {
|
|
111
|
-
loadDetails().catch(console.error);
|
|
112
|
-
setOpen(true);
|
|
108
|
+
async loadDetails() {
|
|
109
|
+
console.log(`Loading device details for ${this.props.device.id}... from ${this.props.instanceId}`);
|
|
110
|
+
const details = await this.props.socket.sendTo(this.props.instanceId, 'dm:deviceDetails', this.props.device.id);
|
|
111
|
+
console.log(`Got device details for ${this.props.device.id}:`, details);
|
|
112
|
+
this.setState({ details, data: (details === null || details === void 0 ? void 0 : details.data) || {} });
|
|
113
|
+
}
|
|
114
|
+
;
|
|
115
|
+
renderDialog() {
|
|
116
|
+
if (!this.state.open || !this.state.details) {
|
|
117
|
+
return null;
|
|
113
118
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const handleImageClick = async (imageData) => imageData && setIcon(imageData);
|
|
120
|
-
/**
|
|
121
|
-
* Copy the device ID to the clipboard
|
|
122
|
-
* @returns {void}
|
|
123
|
-
*/
|
|
124
|
-
const copyToClipboard = async () => {
|
|
125
|
-
const textToCopy = device.id;
|
|
126
|
-
adapter_react_v5_1.Utils.copyToClipboard(textToCopy);
|
|
127
|
-
alert(`${(0, Utils_1.getTranslation)('copied')} ${textToCopy} ${(0, Utils_1.getTranslation)('toClipboard')}!`);
|
|
128
|
-
};
|
|
129
|
-
react_1.default.useEffect(() => setData((details === null || details === void 0 ? void 0 : details.data) || {}), [details]);
|
|
130
|
-
const renderedDialog = open && details ? react_1.default.createElement(material_1.Dialog, { open: !0, maxWidth: "md", onClose: handleClose },
|
|
131
|
-
react_1.default.createElement(material_1.DialogContent, null,
|
|
132
|
-
react_1.default.createElement(JsonConfig_1.default, { instanceId: instanceId, socket: socket, schema: details.schema, data: data, onChange: setData })),
|
|
133
|
-
react_1.default.createElement(material_1.DialogActions, null,
|
|
134
|
-
react_1.default.createElement(material_1.Button, { variant: "contained", color: "primary", onClick: handleClose, autoFocus: true }, (0, Utils_1.getTranslation)('closeButtonText')))) : null;
|
|
135
|
-
let renderedControls;
|
|
136
|
-
let controlDialog;
|
|
137
|
-
const firstControl = (_a = device.controls) === null || _a === void 0 ? void 0 : _a[0];
|
|
138
|
-
if (((_b = device.controls) === null || _b === void 0 ? void 0 : _b.length) === 1 && firstControl && ((firstControl.type === 'icon' || firstControl.type === 'switch') && !firstControl.label)) {
|
|
139
|
-
// control can be placed in button icon
|
|
140
|
-
renderedControls = react_1.default.createElement(DeviceControl_1.default, { control: firstControl, colors: colors, socket: socket, deviceId: device.id, controlHandler: controlHandler, controlStateHandler: controlStateHandler });
|
|
119
|
+
return react_1.default.createElement(material_1.Dialog, { open: !0, maxWidth: "md", onClose: () => this.setState({ open: false }) },
|
|
120
|
+
react_1.default.createElement(material_1.DialogContent, null,
|
|
121
|
+
react_1.default.createElement(JsonConfig_1.default, { instanceId: this.props.instanceId, socket: this.props.socket, schema: this.state.details.schema, data: this.state.data, onChange: (data) => this.setState({ data }) })),
|
|
122
|
+
react_1.default.createElement(material_1.DialogActions, null,
|
|
123
|
+
react_1.default.createElement(material_1.Button, { variant: "contained", color: "primary", onClick: () => this.setState({ open: false }), autoFocus: true }, (0, Utils_1.getTranslation)('closeButtonText'))));
|
|
141
124
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
125
|
+
renderControlDialog() {
|
|
126
|
+
var _a;
|
|
127
|
+
if (!this.state.showControlDialog) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const colors = { primary: '#111', secondary: '#888' };
|
|
131
|
+
return react_1.default.createElement(material_1.Dialog, { open: !0, onClose: () => this.setState({ showControlDialog: false }) },
|
|
132
|
+
react_1.default.createElement(material_1.DialogTitle, null,
|
|
133
|
+
this.props.title,
|
|
134
|
+
react_1.default.createElement(material_1.IconButton, { style: {
|
|
135
|
+
position: 'absolute',
|
|
136
|
+
top: 5,
|
|
137
|
+
right: 5,
|
|
138
|
+
zIndex: 10,
|
|
139
|
+
}, onClick: () => this.setState({ showControlDialog: false }) },
|
|
140
|
+
react_1.default.createElement(icons_material_1.Close, null))),
|
|
141
|
+
react_1.default.createElement(material_1.DialogContent, null, (_a = this.props.device.controls) === null || _a === void 0 ? void 0 : _a.map(control => react_1.default.createElement(DeviceControl_1.default, { key: control.id, control: control, socket: this.props.socket, colors: colors, deviceId: this.props.device.id, controlHandler: this.props.controlHandler, controlStateHandler: this.props.controlStateHandler }))));
|
|
142
|
+
}
|
|
143
|
+
renderControls() {
|
|
144
|
+
var _a, _b, _c;
|
|
145
|
+
const colors = { primary: '#111', secondary: '#888' };
|
|
146
|
+
const firstControl = (_a = this.props.device.controls) === null || _a === void 0 ? void 0 : _a[0];
|
|
147
|
+
if (((_b = this.props.device.controls) === null || _b === void 0 ? void 0 : _b.length) === 1 && firstControl && ((firstControl.type === 'icon' || firstControl.type === 'switch') && !firstControl.label)) {
|
|
148
|
+
// control can be placed in button icon
|
|
149
|
+
return react_1.default.createElement(DeviceControl_1.default, { control: firstControl, colors: colors, socket: this.props.socket, deviceId: this.props.device.id, controlHandler: this.props.controlHandler, controlStateHandler: this.props.controlStateHandler });
|
|
150
|
+
}
|
|
151
|
+
if ((_c = this.props.device.controls) === null || _c === void 0 ? void 0 : _c.length) {
|
|
152
|
+
// place button and show controls dialog
|
|
153
|
+
return react_1.default.createElement(material_1.Fab, { size: "small", onClick: () => this.setState({ showControlDialog: true }) },
|
|
154
|
+
react_1.default.createElement(icons_material_1.VideogameAsset, null));
|
|
158
155
|
}
|
|
156
|
+
return null;
|
|
159
157
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
158
|
+
renderActions() {
|
|
159
|
+
var _a;
|
|
160
|
+
return ((_a = this.props.device.actions) === null || _a === void 0 ? void 0 : _a.length) ? this.props.device.actions.map(a => react_1.default.createElement(DeviceActionButton_1.default, { key: a.id, deviceId: this.props.device.id, action: a, deviceHandler: this.props.deviceHandler, refresh: this.refresh })) : null;
|
|
161
|
+
}
|
|
162
|
+
renderSmall() {
|
|
163
|
+
const hasDetails = this.props.device.hasDetails;
|
|
164
|
+
const status = !this.props.device.status ? [] : Array.isArray(this.props.device.status) ? this.props.device.status : [this.props.device.status];
|
|
165
|
+
return react_1.default.createElement(material_1.Card, { sx: {
|
|
166
|
+
maxWidth: 345,
|
|
167
|
+
minWidth: 200,
|
|
168
|
+
} },
|
|
169
|
+
react_1.default.createElement(material_1.CardHeader, { sx: theme => ({
|
|
170
|
+
backgroundColor: this.props.device.color || theme.palette.secondary.main,
|
|
171
|
+
color: this.props.device.color ? adapter_react_v5_1.Utils.invertColor(this.props.device.color, true) : theme.palette.secondary.contrastText,
|
|
172
|
+
maxWidth: 345,
|
|
173
|
+
}), avatar: react_1.default.createElement("div", null,
|
|
174
|
+
this.props.uploadImagesToInstance ? react_1.default.createElement(DeviceImageUpload_1.default, { uploadImagesToInstance: this.props.uploadImagesToInstance, deviceId: this.props.device.id, manufacturer: getText(this.props.device.manufacturer), model: getText(this.props.device.model), onImageSelect: async (imageData) => imageData && this.setState({ icon: imageData }), socket: this.props.socket }) : null,
|
|
175
|
+
this.state.icon ? react_1.default.createElement(adapter_react_v5_1.Icon, { src: this.state.icon }) : react_1.default.createElement(NoImageIcon, null)), action: hasDetails ? react_1.default.createElement(material_1.IconButton, { "aria-label": "settings", onClick: () => {
|
|
176
|
+
if (!this.state.open) {
|
|
177
|
+
this.loadDetails().catch(console.error);
|
|
178
|
+
this.setState({ open: true });
|
|
179
|
+
}
|
|
180
|
+
} },
|
|
181
|
+
react_1.default.createElement(icons_material_1.MoreVert, null)) : null, title: this.props.title, subheader: this.props.device.manufacturer ? react_1.default.createElement("span", null,
|
|
182
|
+
react_1.default.createElement("b", { style: { marginRight: 4 } },
|
|
183
|
+
(0, Utils_1.getTranslation)('manufacturer'),
|
|
184
|
+
":"),
|
|
185
|
+
getText(this.props.device.manufacturer)) : null }),
|
|
186
|
+
react_1.default.createElement(material_1.CardContent, { style: { position: 'relative' } },
|
|
187
|
+
(status === null || status === void 0 ? void 0 : status.length) ? react_1.default.createElement("div", { style: {
|
|
188
|
+
display: 'flex',
|
|
189
|
+
position: 'absolute',
|
|
190
|
+
top: -11,
|
|
191
|
+
background: '#88888880',
|
|
192
|
+
padding: '0 8px',
|
|
193
|
+
borderRadius: 5,
|
|
194
|
+
width: 'calc(100% - 46px)',
|
|
195
|
+
} }, status.map((s, i) => react_1.default.createElement(DeviceStatus_1.default, { key: i, status: s }))) : null,
|
|
196
|
+
react_1.default.createElement("div", null,
|
|
197
|
+
react_1.default.createElement(material_1.Typography, { variant: "body1" },
|
|
198
|
+
react_1.default.createElement("div", { onClick: this.copyToClipboard },
|
|
199
|
+
react_1.default.createElement("b", null, "ID:"),
|
|
200
|
+
react_1.default.createElement("span", { style: { marginLeft: 4 } }, this.props.device.id.replace(/.*\.\d\./, ''))),
|
|
201
|
+
this.props.device.manufacturer ? react_1.default.createElement("div", null,
|
|
202
|
+
react_1.default.createElement("b", { style: { marginRight: 4 } },
|
|
203
|
+
(0, Utils_1.getTranslation)('manufacturer'),
|
|
204
|
+
":"),
|
|
205
|
+
getText(this.props.device.manufacturer)) : null,
|
|
206
|
+
this.props.device.model ? react_1.default.createElement("div", null,
|
|
207
|
+
react_1.default.createElement("b", { style: { marginRight: 4 } },
|
|
208
|
+
(0, Utils_1.getTranslation)('model'),
|
|
209
|
+
":"),
|
|
210
|
+
getText(this.props.device.model)) : null))),
|
|
211
|
+
react_1.default.createElement(material_1.CardActions, { disableSpacing: true },
|
|
212
|
+
this.renderActions(),
|
|
213
|
+
react_1.default.createElement("div", { style: { flexGrow: 1 } }),
|
|
214
|
+
this.renderControls()),
|
|
215
|
+
this.renderDialog(),
|
|
216
|
+
this.renderControlDialog());
|
|
217
|
+
}
|
|
218
|
+
renderBig() {
|
|
219
|
+
var _a;
|
|
220
|
+
const cardStyle = {
|
|
221
|
+
// backgroundColor: '#fafafa',
|
|
222
|
+
width: 300,
|
|
223
|
+
minHeight: 280,
|
|
224
|
+
margin: 10,
|
|
225
|
+
overflow: 'hidden',
|
|
226
|
+
};
|
|
227
|
+
/** @type {CSSProperties} */
|
|
228
|
+
const headerStyle = {
|
|
229
|
+
display: 'flex',
|
|
230
|
+
position: 'relative',
|
|
231
|
+
justifyContent: 'space-between',
|
|
232
|
+
minHeight: 60,
|
|
233
|
+
color: '#000',
|
|
234
|
+
padding: '0 10px 0 10px',
|
|
235
|
+
backgroundColor: '#77c7ff8c',
|
|
236
|
+
borderRadius: '4px 4px 0 0',
|
|
237
|
+
};
|
|
238
|
+
/** @type {CSSProperties} */
|
|
239
|
+
const imgAreaStyle = {
|
|
240
|
+
height: 45,
|
|
241
|
+
width: 45,
|
|
242
|
+
margin: 'auto',
|
|
243
|
+
justifyContent: 'center',
|
|
244
|
+
display: 'grid',
|
|
245
|
+
};
|
|
246
|
+
/** @type {CSSProperties} */
|
|
247
|
+
const imgStyle = {
|
|
248
|
+
zIndex: 2,
|
|
249
|
+
maxWidth: '100%',
|
|
250
|
+
maxHeight: '100%',
|
|
251
|
+
};
|
|
252
|
+
/** @type {CSSProperties} */
|
|
253
|
+
const titleStyle = {
|
|
254
|
+
color: '#333',
|
|
255
|
+
width: '100%',
|
|
256
|
+
fontSize: 16,
|
|
257
|
+
fontWeight: 'bold',
|
|
258
|
+
paddingTop: 16,
|
|
259
|
+
paddingLeft: 8,
|
|
260
|
+
whiteSpace: 'nowrap',
|
|
261
|
+
overflow: 'hidden',
|
|
262
|
+
textOverflow: 'ellipsis',
|
|
263
|
+
};
|
|
264
|
+
/** @type {CSSProperties} */
|
|
265
|
+
const detailsButtonStyle = {
|
|
266
|
+
right: 20,
|
|
267
|
+
bottom: -20,
|
|
268
|
+
position: 'absolute',
|
|
269
|
+
};
|
|
270
|
+
/** @type {CSSProperties} */
|
|
271
|
+
const bodyStyle = {
|
|
272
|
+
height: 'calc(100% - 116px)',
|
|
273
|
+
};
|
|
274
|
+
/** @type {CSSProperties} */
|
|
275
|
+
const deviceInfoStyle = {
|
|
276
|
+
padding: '20px 16px 0 16px',
|
|
277
|
+
height: 133,
|
|
278
|
+
};
|
|
279
|
+
/** @type {CSSProperties} */
|
|
280
|
+
const statusStyle = {
|
|
281
|
+
padding: '15px 15px 0 15px',
|
|
282
|
+
height: 41,
|
|
283
|
+
};
|
|
284
|
+
const status = !this.props.device.status ? [] : Array.isArray(this.props.device.status) ? this.props.device.status : [this.props.device.status];
|
|
285
|
+
return react_1.default.createElement(material_1.Paper, { style: cardStyle, key: this.props.id },
|
|
286
|
+
react_1.default.createElement("div", { style: headerStyle },
|
|
287
|
+
react_1.default.createElement("div", { style: imgAreaStyle },
|
|
288
|
+
this.props.uploadImagesToInstance ? react_1.default.createElement(DeviceImageUpload_1.default, { uploadImagesToInstance: this.props.uploadImagesToInstance, deviceId: this.props.device.id, manufacturer: getText(this.props.device.manufacturer), model: getText(this.props.device.model), onImageSelect: async (imageData) => imageData && this.setState({ icon: imageData }), socket: this.props.socket }) : null,
|
|
289
|
+
react_1.default.createElement(adapter_react_v5_1.Icon, { src: this.state.icon, style: imgStyle })),
|
|
290
|
+
react_1.default.createElement("div", { style: titleStyle }, this.props.title),
|
|
291
|
+
this.props.device.hasDetails ? react_1.default.createElement(material_1.Fab, { size: "small", style: detailsButtonStyle, onClick: () => {
|
|
292
|
+
if (!this.state.open) {
|
|
293
|
+
this.loadDetails().catch(console.error);
|
|
294
|
+
this.setState({ open: true });
|
|
295
|
+
}
|
|
296
|
+
}, color: "primary" },
|
|
297
|
+
react_1.default.createElement(icons_material_1.MoreVert, null)) : null),
|
|
298
|
+
react_1.default.createElement("div", { style: statusStyle }, status.map((s, i) => react_1.default.createElement(DeviceStatus_1.default, { key: i, status: s }))),
|
|
299
|
+
react_1.default.createElement("div", { style: bodyStyle },
|
|
300
|
+
react_1.default.createElement(material_1.Typography, { variant: "body1", style: deviceInfoStyle },
|
|
301
|
+
react_1.default.createElement("div", { onClick: this.copyToClipboard },
|
|
189
302
|
react_1.default.createElement("b", null, "ID:"),
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
303
|
+
" ",
|
|
304
|
+
this.props.device.id.replace(/.*\.\d\./, '')),
|
|
305
|
+
this.props.device.manufacturer ? react_1.default.createElement("div", null,
|
|
306
|
+
react_1.default.createElement("b", null,
|
|
193
307
|
(0, Utils_1.getTranslation)('manufacturer'),
|
|
194
308
|
":"),
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
309
|
+
" ",
|
|
310
|
+
getText(this.props.device.manufacturer)) : null,
|
|
311
|
+
this.props.device.model ? react_1.default.createElement("div", null,
|
|
312
|
+
react_1.default.createElement("b", null,
|
|
198
313
|
(0, Utils_1.getTranslation)('model'),
|
|
199
314
|
":"),
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
315
|
+
" ",
|
|
316
|
+
getText(this.props.device.model)) : null),
|
|
317
|
+
!!((_a = this.props.device.actions) === null || _a === void 0 ? void 0 : _a.length) && react_1.default.createElement("div", { style: {
|
|
318
|
+
flex: 1,
|
|
319
|
+
position: 'relative',
|
|
320
|
+
display: 'flex',
|
|
321
|
+
gap: 8,
|
|
322
|
+
paddingBottom: 5,
|
|
323
|
+
height: 34,
|
|
324
|
+
paddingLeft: 10,
|
|
325
|
+
paddingRight: 10,
|
|
326
|
+
} }, this.renderActions())),
|
|
327
|
+
this.renderDialog(),
|
|
328
|
+
this.renderControlDialog());
|
|
329
|
+
}
|
|
330
|
+
render() {
|
|
331
|
+
if (this.props.smallCards) {
|
|
332
|
+
return this.renderSmall();
|
|
333
|
+
}
|
|
334
|
+
return this.renderBig();
|
|
335
|
+
}
|
|
207
336
|
}
|
|
208
337
|
exports.default = DeviceCard;
|
package/DeviceList.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ import Communication, { CommunicationProps, CommunicationState } from './Communi
|
|
|
4
4
|
interface DeviceListProps extends CommunicationProps {
|
|
5
5
|
uploadImagesToInstance?: string;
|
|
6
6
|
filter?: string;
|
|
7
|
-
|
|
7
|
+
embedded?: boolean;
|
|
8
8
|
title?: string;
|
|
9
9
|
style: React.CSSProperties;
|
|
10
|
+
smallCards: boolean;
|
|
10
11
|
}
|
|
11
12
|
interface DeviceListState extends CommunicationState {
|
|
12
13
|
devices: DeviceInfo[];
|
|
@@ -33,7 +34,7 @@ export default class DeviceList extends Communication<DeviceListProps, DeviceLis
|
|
|
33
34
|
private lastPropsFilter;
|
|
34
35
|
private lastInstance;
|
|
35
36
|
private filterTimeout;
|
|
36
|
-
private language;
|
|
37
|
+
private readonly language;
|
|
37
38
|
constructor(props: DeviceListProps);
|
|
38
39
|
componentDidMount(): Promise<void>;
|
|
39
40
|
componentWillUnmount(): void;
|
package/DeviceList.js
CHANGED
|
@@ -67,7 +67,7 @@ class DeviceList extends Communication_1.default {
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
// @ts-ignore
|
|
70
|
-
Object.
|
|
70
|
+
Object.assign(this.state, {
|
|
71
71
|
devices: [],
|
|
72
72
|
filteredDevices: [],
|
|
73
73
|
filter: '',
|
|
@@ -81,7 +81,7 @@ class DeviceList extends Communication_1.default {
|
|
|
81
81
|
this.language = adapter_react_v5_1.I18n.getLanguage();
|
|
82
82
|
}
|
|
83
83
|
async componentDidMount() {
|
|
84
|
-
if (!this.props.
|
|
84
|
+
if (!this.props.embedded) {
|
|
85
85
|
try {
|
|
86
86
|
// check if instance is alive
|
|
87
87
|
const alive = await this.props.socket.getState(`system.adapter.${this.props.selectedInstance}.alive`);
|
|
@@ -106,7 +106,7 @@ class DeviceList extends Communication_1.default {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
componentWillUnmount() {
|
|
109
|
-
if (!this.props.
|
|
109
|
+
if (!this.props.embedded) {
|
|
110
110
|
this.props.socket.unsubscribeState(`system.adapter.${this.props.selectedInstance}.alive`, this.aliveHandler);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -129,7 +129,7 @@ class DeviceList extends Communication_1.default {
|
|
|
129
129
|
return text;
|
|
130
130
|
}
|
|
131
131
|
applyFilter() {
|
|
132
|
-
const filter = this.props.
|
|
132
|
+
const filter = this.props.embedded ? this.props.filter : this.state.filter;
|
|
133
133
|
// filter devices name
|
|
134
134
|
if (filter) {
|
|
135
135
|
const filteredDevices = this.state.devices.filter(device => this.getText(device.name).toLowerCase().includes(filter.toLowerCase()));
|
|
@@ -154,16 +154,16 @@ class DeviceList extends Communication_1.default {
|
|
|
154
154
|
const emptyStyle = {
|
|
155
155
|
padding: 25,
|
|
156
156
|
};
|
|
157
|
-
if (this.props.
|
|
157
|
+
if (this.props.embedded && this.lastPropsFilter !== this.props.filter) {
|
|
158
158
|
this.lastPropsFilter = this.props.filter;
|
|
159
159
|
setTimeout(() => this.applyFilter(), 50);
|
|
160
160
|
}
|
|
161
|
-
if (this.props.
|
|
161
|
+
if (this.props.embedded && this.lastInstance !== this.props.selectedInstance) {
|
|
162
162
|
this.lastInstance = this.props.selectedInstance;
|
|
163
163
|
setTimeout(() => this.loadData().catch(console.error), 50);
|
|
164
164
|
}
|
|
165
165
|
let list;
|
|
166
|
-
if (!this.props.
|
|
166
|
+
if (!this.props.embedded && !this.state.alive) {
|
|
167
167
|
list = react_1.default.createElement("div", { style: emptyStyle },
|
|
168
168
|
react_1.default.createElement("span", null, (0, Utils_1.getTranslation)('instanceNotAlive')));
|
|
169
169
|
}
|
|
@@ -178,7 +178,7 @@ class DeviceList extends Communication_1.default {
|
|
|
178
178
|
else {
|
|
179
179
|
list = this.state.filteredDevices.map(device => react_1.default.createElement(DeviceCard_1.default, { key: device.id, id: device.id, title: this.getText(device.name), device: device, instanceId: this.props.selectedInstance, uploadImagesToInstance: this.props.uploadImagesToInstance, deviceHandler: this.deviceHandler, controlHandler: this.controlHandler, controlStateHandler: this.controlStateHandler, socket: this.props.socket }));
|
|
180
180
|
}
|
|
181
|
-
if (this.props.
|
|
181
|
+
if (this.props.embedded) {
|
|
182
182
|
return list;
|
|
183
183
|
}
|
|
184
184
|
return react_1.default.createElement("div", { style: { width: '100%', height: '100%', overflow: 'hidden' } },
|
package/README.md
CHANGED