@adcops/autocore-react 3.0.32 → 3.0.34
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/dist/assets/BlocklyLogo.js +9 -1
- package/dist/assets/Distance.js +5 -1
- package/dist/assets/JogLong.js +3 -1
- package/dist/assets/JogMedium.js +3 -1
- package/dist/assets/JogShort.js +3 -1
- package/dist/assets/PythonLogo.js +21 -1
- package/dist/assets/Rotation3D.js +3 -1
- package/dist/assets/RotationCcw.js +10 -1
- package/dist/assets/RotationCcwA.js +16 -1
- package/dist/assets/RotationCcwB.js +16 -1
- package/dist/assets/RotationCcwC.js +16 -1
- package/dist/assets/RotationCw.js +10 -1
- package/dist/assets/RotationCwA.js +16 -1
- package/dist/assets/RotationCwB.js +16 -1
- package/dist/assets/RotationCwC.js +16 -1
- package/dist/assets/Run.js +3 -1
- package/dist/assets/Speed.js +5 -1
- package/dist/assets/SpeedFast.js +3 -1
- package/dist/assets/SpeedMedium.js +3 -1
- package/dist/assets/SpeedNone.js +3 -1
- package/dist/assets/SpeedSlow.js +3 -1
- package/dist/assets/Walk.js +3 -1
- package/dist/assets/index.js +22 -1
- package/dist/components/BlocklyEditor.js +508 -1
- package/dist/components/CodeEditor.js +108 -1
- package/dist/components/FileList.js +218 -1
- package/dist/components/FileSelect.js +63 -1
- package/dist/components/FitText.js +13 -1
- package/dist/components/Indicator.js +113 -1
- package/dist/components/IndicatorButton.js +187 -1
- package/dist/components/IndicatorRect.js +93 -1
- package/dist/components/JogPanel.js +295 -1
- package/dist/components/Lamp.js +161 -1
- package/dist/components/Osk.js +125 -1
- package/dist/components/OskDialog.js +128 -1
- package/dist/components/ProgressBarWithValue.js +18 -1
- package/dist/components/TextInput.js +139 -1
- package/dist/components/ToggleGroup.js +204 -1
- package/dist/components/ValueDisplay.js +160 -1
- package/dist/components/ValueIndicator.js +38 -1
- package/dist/components/ValueInput.js +158 -1
- package/dist/core/ActionMode.js +18 -1
- package/dist/core/EventEmitterContext.js +252 -1
- package/dist/core/IndicatorButtonState.js +9 -1
- package/dist/core/IndicatorColor.js +36 -1
- package/dist/core/MaskPatterns.js +80 -1
- package/dist/core/NumerableTypes.js +10 -1
- package/dist/core/PositionContext.js +24 -1
- package/dist/core/UniqueId.js +32 -1
- package/dist/core/ValueSimulator.js +233 -1
- package/dist/core/hoc.js +44 -1
- package/dist/hooks/adsHooks.js +253 -1
- package/dist/hooks/commandHooks.d.ts +1 -1
- package/dist/hooks/commandHooks.js +252 -1
- package/dist/hooks/index.js +10 -1
- package/dist/hooks/useScaledValue.js +86 -1
- package/dist/hub/CommandMessage.js +9 -1
- package/dist/hub/HubBase.js +310 -1
- package/dist/hub/HubSimulate.js +46 -1
- package/dist/hub/HubTauri.js +130 -1
- package/dist/hub/HubWebSocket.js +240 -1
- package/dist/hub/index.js +57 -1
- package/package.json +3 -3
- package/src/hooks/commandHooks.tsx +11 -8
|
@@ -1 +1,218 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
4
|
+
* Created Date: 2024-04-24 16:01:53
|
|
5
|
+
* -----
|
|
6
|
+
* Last Modified: 2025-01-20 19:15:50
|
|
7
|
+
* -----
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
/** @file FileList
|
|
11
|
+
* FileList allows a user to view the contents of a DataStore in the autocore-server.
|
|
12
|
+
* Files can be downloaded, deleted and optionally uploaded (if enabled).
|
|
13
|
+
*
|
|
14
|
+
* The FileList requires the autocore-server to be the backend, as files are transferred
|
|
15
|
+
* using specific commands via websockets.
|
|
16
|
+
*/
|
|
17
|
+
import React, { useState, useContext, useEffect } from 'react';
|
|
18
|
+
import { DataTable } from 'primereact/datatable';
|
|
19
|
+
import { Column } from 'primereact/column';
|
|
20
|
+
import { Toolbar } from 'primereact/toolbar';
|
|
21
|
+
import { Button } from 'primereact/button';
|
|
22
|
+
import { confirmPopup } from 'primereact/confirmpopup';
|
|
23
|
+
import { FileUpload } from 'primereact/fileupload';
|
|
24
|
+
import { EventEmitterContext } from '../core/EventEmitterContext';
|
|
25
|
+
/**
|
|
26
|
+
* `FileList` is a React functional component that displays a list of files retrieved from a specified domain
|
|
27
|
+
* in an autocore-server.
|
|
28
|
+
* It allows users to download and delete files. The component also supports file uploads, if enabled.
|
|
29
|
+
*
|
|
30
|
+
* The component uses the `EventEmitterContext` to make API calls to a backend to list, download, and delete files.
|
|
31
|
+
* It dynamically handles file operations based on the `domain` prop which determines the API endpoints for these actions.
|
|
32
|
+
*
|
|
33
|
+
* Requires
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <ConfirmPopup />
|
|
36
|
+
* ```
|
|
37
|
+
* somewhere in a top-level page.
|
|
38
|
+
*
|
|
39
|
+
* Props:
|
|
40
|
+
* - `domain` (string): The domain name assigned to the DATASTORE servelet containing the data.
|
|
41
|
+
* Default: "DATASTORE"
|
|
42
|
+
* - `enableUpload` (boolean): If true, enables an upload button allowing files to be uploaded to the datastore.
|
|
43
|
+
* Default: false
|
|
44
|
+
*
|
|
45
|
+
* Example Usage:
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <FileList domain="MyDomain" enableUpload={true} />
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @param {FileListProps} props The properties passed to the component.
|
|
51
|
+
*/
|
|
52
|
+
export const FileList = ({ domain = "DATASTORE", enableUpload = false, subdir, filter = ".json", onSuccess, onError }) => {
|
|
53
|
+
const [uploadKey, setUploadKey] = useState(0);
|
|
54
|
+
const { invoke } = useContext(EventEmitterContext);
|
|
55
|
+
const [files, setFiles] = useState();
|
|
56
|
+
const makeTargetName = (s) => {
|
|
57
|
+
if (s !== null) {
|
|
58
|
+
if (subdir !== undefined) {
|
|
59
|
+
return `${subdir}/${s}`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Retrieve a list of files from an autocore-server DataStoreServelet.
|
|
71
|
+
*/
|
|
72
|
+
const listFiles = async () => {
|
|
73
|
+
try {
|
|
74
|
+
const args = subdir !== undefined ? { "subdir": subdir } : {};
|
|
75
|
+
let res = await invoke(domain, "list_files", args);
|
|
76
|
+
let items = [];
|
|
77
|
+
for (let i = 0; i < res.data.length; ++i) {
|
|
78
|
+
const item = res.data[i];
|
|
79
|
+
items.push({
|
|
80
|
+
id: i + 1,
|
|
81
|
+
name: item
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
setFiles(items);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (onError)
|
|
88
|
+
onError(`Failed to upload file list: ${error}`);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Handles when the download button is clicked on a list item.
|
|
93
|
+
* @param file The file item selected in the DataTable
|
|
94
|
+
*/
|
|
95
|
+
const handleDownload = async (file) => {
|
|
96
|
+
let target = makeTargetName(file.name);
|
|
97
|
+
try {
|
|
98
|
+
await invoke(domain, "download_file", { file_name: target });
|
|
99
|
+
if (onSuccess)
|
|
100
|
+
onSuccess(`Downloaded file: ${file.name}`);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (onError)
|
|
104
|
+
onError(`Failed downloading file: ${error}`);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Sends the command to the autocore-server domain to delete the file.
|
|
109
|
+
* @param file_name Name of the file to delete.
|
|
110
|
+
*/
|
|
111
|
+
const handleDelete = async (file_name) => {
|
|
112
|
+
let target = makeTargetName(file_name);
|
|
113
|
+
try {
|
|
114
|
+
await invoke(domain, "delete_file", { file_name: target });
|
|
115
|
+
if (onSuccess)
|
|
116
|
+
onSuccess(`Deleted file: ${file_name}`);
|
|
117
|
+
listFiles(); // Refresh the file list after successful upload
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (onError)
|
|
121
|
+
onError(`Failed to delete file: ${error}`);
|
|
122
|
+
}
|
|
123
|
+
listFiles();
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Handles the upload of files selected through the PrimeReact FileUpload component.
|
|
127
|
+
* This function is triggered when a user selects files for upload and it processes each file asynchronously.
|
|
128
|
+
*
|
|
129
|
+
* The function reads the selected file as an ArrayBuffer, converts it to a Base64-encoded string, and then
|
|
130
|
+
* sends it to the server using a custom function `invoke` which interacts with the server via API calls.
|
|
131
|
+
* Upon successful upload, a success message is dispatched to the application's notification system. If the
|
|
132
|
+
* upload fails, an error message is similarly dispatched.
|
|
133
|
+
*
|
|
134
|
+
* Usage of this function requires that it be attached to a FileUpload component's event handler in the React component.
|
|
135
|
+
*
|
|
136
|
+
* @param {FileUploadSelectEvent} event - The event object provided by the FileUpload component, containing the files selected by the user.
|
|
137
|
+
*
|
|
138
|
+
* The `FileUploadSelectEvent` type should include:
|
|
139
|
+
* - `files`: An array of `File` objects that the user has selected for upload.
|
|
140
|
+
*
|
|
141
|
+
* This function utilizes the `FileReader` API to read the contents of the file. It checks if the read result
|
|
142
|
+
* is an instance of `ArrayBuffer` before proceeding to convert it to a Base64 string. Errors during file reading
|
|
143
|
+
* or uploading are caught and appropriate actions are taken (e.g., logging the error, dispatching error notifications).
|
|
144
|
+
*
|
|
145
|
+
* Note:
|
|
146
|
+
* - Ensure that the `invoke` function is properly implemented to handle the API request for file uploading.
|
|
147
|
+
* - Adjust the maximum file size and the types of files accepted by the FileUpload component according to your application's requirements.
|
|
148
|
+
* - This handler assumes a single file handling scenario. If multiple file uploads are needed, modifications to the iteration over `event.files` may be required.
|
|
149
|
+
*/
|
|
150
|
+
const handleUpload = async (event) => {
|
|
151
|
+
const files = event.files;
|
|
152
|
+
const file = files[0]; // Assuming single file upload
|
|
153
|
+
let target = makeTargetName(file.name);
|
|
154
|
+
const reader = new FileReader();
|
|
155
|
+
reader.onload = async (e) => {
|
|
156
|
+
// Convert array buffer to base64
|
|
157
|
+
const arrayBuffer = e.target?.result;
|
|
158
|
+
const base64String = arrayBufferToBase64(arrayBuffer);
|
|
159
|
+
try {
|
|
160
|
+
await invoke(domain, "write_file", { file_name: target, value: base64String, options: { "base64": true } });
|
|
161
|
+
if (onSuccess)
|
|
162
|
+
onSuccess(`Uploaded file ${file.name}`);
|
|
163
|
+
// Refresh the file list after successful upload
|
|
164
|
+
listFiles();
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (onError)
|
|
168
|
+
onError(`Failed to upload file: ${error}`);
|
|
169
|
+
}
|
|
170
|
+
// Reset upload state of button so it show the file upload dialog again.
|
|
171
|
+
resetUpload();
|
|
172
|
+
};
|
|
173
|
+
reader.onerror = (error) => {
|
|
174
|
+
if (onError)
|
|
175
|
+
onError(`Error reading file: ${error}`);
|
|
176
|
+
};
|
|
177
|
+
reader.readAsArrayBuffer(file);
|
|
178
|
+
};
|
|
179
|
+
function arrayBufferToBase64(buffer) {
|
|
180
|
+
let binary = '';
|
|
181
|
+
let bytes = new Uint8Array(buffer);
|
|
182
|
+
let len = bytes.byteLength;
|
|
183
|
+
for (let i = 0; i < len; i++) {
|
|
184
|
+
binary += String.fromCharCode(bytes[i]);
|
|
185
|
+
}
|
|
186
|
+
return window.btoa(binary);
|
|
187
|
+
}
|
|
188
|
+
const resetUpload = () => {
|
|
189
|
+
setUploadKey(prevKey => prevKey + 1); // Increment key to force re-render
|
|
190
|
+
};
|
|
191
|
+
const title = `File Listing [/${subdir !== undefined ? subdir : ''}]`;
|
|
192
|
+
const toolbarStartContents = (_jsx(React.Fragment, { children: _jsx("span", { style: { fontWeight: 600 }, children: title }) }));
|
|
193
|
+
const toolbarEndContents = (_jsxs(React.Fragment, { children: [enableUpload && (_jsx(FileUpload, { customUpload: true, auto: true, uploadHandler: handleUpload, accept: filter, maxFileSize: 25000, mode: "basic", chooseLabel: "", chooseOptions: {
|
|
194
|
+
icon: 'pi pi-upload',
|
|
195
|
+
className: 'p-button-icon-only p-button-text p-button-rounded p-mr-2'
|
|
196
|
+
} }, uploadKey)), _jsx(Button, { icon: "pi pi-refresh", onClick: () => { listFiles(); }, className: "p-button-rounded p-mr-2", "aria-label": "Refresh", size: "small", rounded: true, text: true })] }));
|
|
197
|
+
/**
|
|
198
|
+
* Confirm that the user really wants to delete.
|
|
199
|
+
* @param event
|
|
200
|
+
*/
|
|
201
|
+
const confirmDelete = (file, event) => {
|
|
202
|
+
confirmPopup({
|
|
203
|
+
target: event.currentTarget,
|
|
204
|
+
message: `Are you want to delete file ${file.name}?\nWARNING: This cannot be undone.`,
|
|
205
|
+
icon: 'pi pi-info-circle',
|
|
206
|
+
defaultFocus: 'reject',
|
|
207
|
+
acceptClassName: 'p-button-danger',
|
|
208
|
+
accept: () => handleDelete(file.name)
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
listFiles();
|
|
213
|
+
return () => {
|
|
214
|
+
};
|
|
215
|
+
}, [domain, enableUpload]);
|
|
216
|
+
return (_jsxs("div", { children: [_jsx(Toolbar, { start: toolbarStartContents, end: toolbarEndContents, style: { padding: "1mm" } }), _jsxs(DataTable, { value: files, children: [_jsx(Column, { field: "name", header: "Name" }), _jsx(Column, { body: (rowData) => (_jsxs(_Fragment, { children: [_jsx(Button, { icon: "pi pi-download", onClick: () => handleDownload(rowData), className: "p-button-rounded p-button-success p-mr-2", style: { marginRight: "2mm" }, size: "small" }), _jsx(Button, { icon: "pi pi-trash", onClick: (e) => confirmDelete(rowData, e), className: "p-button-rounded p-button-danger", size: "small" })] })), header: "Actions" })] })] }));
|
|
217
|
+
};
|
|
218
|
+
export default FileList;
|
|
@@ -1 +1,63 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
4
|
+
* Created Date: 2024-04-28 16:06:59
|
|
5
|
+
* -----
|
|
6
|
+
* Last Modified: 2024-06-12 14:40:20
|
|
7
|
+
* -----
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
import { useState, useContext, useEffect } from 'react';
|
|
11
|
+
import { DataTable } from 'primereact/datatable';
|
|
12
|
+
import { Column } from 'primereact/column';
|
|
13
|
+
import { Button } from 'primereact/button';
|
|
14
|
+
import { EventEmitterContext } from "../core/EventEmitterContext";
|
|
15
|
+
export const FileSelect = ({ domain = "DATASTORE", subdir, filter, onFileSelected, onAccept, onCancel }) => {
|
|
16
|
+
const { invoke } = useContext(EventEmitterContext);
|
|
17
|
+
const [files, setFiles] = useState([]);
|
|
18
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
19
|
+
const listFiles = async () => {
|
|
20
|
+
try {
|
|
21
|
+
let args;
|
|
22
|
+
if (filter !== undefined && filter !== null) {
|
|
23
|
+
args = subdir ? { "subdir": subdir, options: { "filter": filter } } : { options: { "filter": filter } };
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
args = subdir ? { "subdir": subdir } : {};
|
|
27
|
+
}
|
|
28
|
+
const res = await invoke(domain, "list_files", args);
|
|
29
|
+
const items = res.data.map((item, index) => ({
|
|
30
|
+
id: index + 1,
|
|
31
|
+
name: item
|
|
32
|
+
}));
|
|
33
|
+
setFiles(items);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error("Failed to list files:", error);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const handleSelect = (file) => {
|
|
40
|
+
setSelectedFile(file);
|
|
41
|
+
if (onFileSelected)
|
|
42
|
+
onFileSelected(file.name);
|
|
43
|
+
};
|
|
44
|
+
const makeReturnFileName = () => {
|
|
45
|
+
if (selectedFile !== null) {
|
|
46
|
+
if (subdir !== undefined) {
|
|
47
|
+
return `${subdir}/${selectedFile.name}`;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return selectedFile.name;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
listFiles();
|
|
59
|
+
}, [domain, subdir]);
|
|
60
|
+
return (_jsxs("div", { children: [_jsxs(DataTable, { value: files, selectionMode: "single", selection: selectedFile, onSelectionChange: e => handleSelect(e.value), children: [_jsx(Column, { field: "name", header: "File Name" }), _jsx(Column, { body: (rowData) => (_jsx(Button, { label: selectedFile?.name === rowData.name ? "Selected" : "Select", icon: selectedFile?.name === rowData.name ? "pi pi-check-circle" : "pi pi-circle-off", onClick: () => handleSelect(rowData), className: `p-button-rounded ${selectedFile?.name === rowData.name ? "p-button-success" : "p-button-outlined"}`, "aria-label": "Select", size: "small" })), header: "Select" })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'flex-end', marginTop: '10px' }, children: [_jsx(Button, { label: "Cancel", icon: "pi pi-times", className: "p-button-text", onClick: onCancel }), _jsx(Button, { label: "Accept", icon: "pi pi-check", className: "p-button", onClick: () => { if (onAccept !== undefined && selectedFile !== null)
|
|
61
|
+
onAccept(makeReturnFileName()); }, disabled: !selectedFile })] })] }));
|
|
62
|
+
};
|
|
63
|
+
export default FileSelect;
|
|
@@ -1 +1,13 @@
|
|
|
1
|
-
import{jsx as _jsx}from"react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import useFitText from 'use-fit-text';
|
|
3
|
+
/**
|
|
4
|
+
* FitText dynamically adjusts the font size of its children to fit the parent container.
|
|
5
|
+
*
|
|
6
|
+
* @param {FitTextProps} props The props for the FitText component.
|
|
7
|
+
* @returns A div element that resizes its child text to fit.
|
|
8
|
+
*/
|
|
9
|
+
export const FitText = ({ children }) => {
|
|
10
|
+
const { fontSize, ref } = useFitText();
|
|
11
|
+
return (_jsx("div", { ref: ref, style: { width: '100%', height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }, children: _jsx("p", { style: { fontSize }, children: children }) }));
|
|
12
|
+
};
|
|
13
|
+
export default FitText;
|
|
@@ -1 +1,113 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2024 Automated Design Corp. All Rights Reserved.
|
|
4
|
+
* Created Date: 2024-01-16 14:39:41
|
|
5
|
+
* -----
|
|
6
|
+
* Last Modified: 2024-03-12 21:53:48
|
|
7
|
+
* -----
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
import { Component } from 'react';
|
|
11
|
+
//import clsx from 'clsx';
|
|
12
|
+
import { EventEmitterContext } from '../core/EventEmitterContext';
|
|
13
|
+
//import { IPositionContext } from '../core/PositionContext';
|
|
14
|
+
import { IndicatorColor } from "../core/IndicatorColor";
|
|
15
|
+
export { IndicatorColor };
|
|
16
|
+
export class Indicator extends Component {
|
|
17
|
+
constructor(props) {
|
|
18
|
+
super(props);
|
|
19
|
+
Object.defineProperty(this, "unsubscribeTopicId", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: null
|
|
24
|
+
});
|
|
25
|
+
this.state = {
|
|
26
|
+
subscribedValue: props.value,
|
|
27
|
+
fontSize: "100%"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
componentDidMount() {
|
|
31
|
+
const { topic } = this.props;
|
|
32
|
+
if (topic && this.unsubscribeTopicId === null) {
|
|
33
|
+
const { subscribe } = this.context;
|
|
34
|
+
this.unsubscribeTopicId = subscribe(topic, (value) => {
|
|
35
|
+
this.setState({ subscribedValue: value });
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
componentDidUpdate(prevProps) {
|
|
40
|
+
if (prevProps.value !== this.props.value) {
|
|
41
|
+
this.setState({ subscribedValue: this.props.value });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
componentWillUnmount() {
|
|
45
|
+
if (this.unsubscribeTopicId !== null) {
|
|
46
|
+
const { unsubscribe } = this.context;
|
|
47
|
+
unsubscribe(this.unsubscribeTopicId);
|
|
48
|
+
this.unsubscribeTopicId = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
render() {
|
|
52
|
+
const { label, value, onColor, offColor, invalidColor } = this.props;
|
|
53
|
+
const { subscribedValue } = this.state;
|
|
54
|
+
//
|
|
55
|
+
// Load the value from the proper source.
|
|
56
|
+
// If the `value` property is undefined, that takes precedence.
|
|
57
|
+
//
|
|
58
|
+
let displayValue = subscribedValue;
|
|
59
|
+
if (value !== undefined) {
|
|
60
|
+
displayValue = value;
|
|
61
|
+
}
|
|
62
|
+
let color;
|
|
63
|
+
switch (displayValue) {
|
|
64
|
+
case true:
|
|
65
|
+
color = onColor;
|
|
66
|
+
break;
|
|
67
|
+
case false:
|
|
68
|
+
color = offColor;
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
color = invalidColor;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
// const style: React.CSSProperties = {
|
|
75
|
+
// position: useAbsolutePositioning ? 'absolute' : 'relative',
|
|
76
|
+
// display: useAbsolutePositioning ? "" : "inline-block",
|
|
77
|
+
// verticalAlign: 'middle',
|
|
78
|
+
// top: useAbsolutePositioning && y ? `${yOffset + adjScale * y}px` : undefined,
|
|
79
|
+
// left: useAbsolutePositioning && x ? `${xOffset + adjScale * x}px` : undefined,
|
|
80
|
+
// width: adjWidth ? `${adjWidth * adjScale}px` : undefined,
|
|
81
|
+
// height: adjHeight ? `${adjHeight * adjScale}px` : undefined,
|
|
82
|
+
// lineHeight: adjHeight ? `${adjHeight * adjScale}px` : undefined,
|
|
83
|
+
// backgroundColor: color,
|
|
84
|
+
// borderRadius: "20px"
|
|
85
|
+
// };
|
|
86
|
+
const groupStyle = {
|
|
87
|
+
alignItems: "center !important"
|
|
88
|
+
};
|
|
89
|
+
const lampDivStyle = {
|
|
90
|
+
backgroundColor: color,
|
|
91
|
+
width: "22px"
|
|
92
|
+
};
|
|
93
|
+
return (_jsxs("div", { className: "p-inputgroup flex-1", style: groupStyle, children: [_jsx("span", { className: "p-inputgroup-addon", style: lampDivStyle, children: "\u00A0" }), _jsx("span", { className: "p-inputgroup-addon", children: label })] }));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
Object.defineProperty(Indicator, "contextType", {
|
|
97
|
+
enumerable: true,
|
|
98
|
+
configurable: true,
|
|
99
|
+
writable: true,
|
|
100
|
+
value: EventEmitterContext
|
|
101
|
+
});
|
|
102
|
+
// Define default properties
|
|
103
|
+
Object.defineProperty(Indicator, "defaultProps", {
|
|
104
|
+
enumerable: true,
|
|
105
|
+
configurable: true,
|
|
106
|
+
writable: true,
|
|
107
|
+
value: {
|
|
108
|
+
onColor: "green",
|
|
109
|
+
offColor: "gray",
|
|
110
|
+
invalidColor: "black"
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
export default Indicator;
|
|
@@ -1 +1,187 @@
|
|
|
1
|
-
import{jsx as _jsx}from"react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2024 Automated Design Corp. All Rights Reserved.
|
|
4
|
+
* Created Date: 2024-01-21 10:33:58
|
|
5
|
+
* -----
|
|
6
|
+
* Last Modified: 2024-03-12 20:55:17
|
|
7
|
+
* Modified By: ADC
|
|
8
|
+
* -----
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
import { Component } from 'react';
|
|
12
|
+
import { Button } from 'primereact/button';
|
|
13
|
+
import { EventEmitterContext } from '../core/EventEmitterContext';
|
|
14
|
+
import { IndicatorColor } from "../core/IndicatorColor";
|
|
15
|
+
export { IndicatorColor };
|
|
16
|
+
import { ActionMode } from '../core/ActionMode';
|
|
17
|
+
export { ActionMode };
|
|
18
|
+
export class IndicatorButton extends Component {
|
|
19
|
+
constructor(props) {
|
|
20
|
+
super(props);
|
|
21
|
+
/**
|
|
22
|
+
* Handles updates for the main topic.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(this, "handleTopicUpdate", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: (value) => {
|
|
29
|
+
this.setState({ currentValue: value });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Handles updates for the disable topic.
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(this, "handleDisableTopicUpdate", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: (value) => {
|
|
40
|
+
this.setState({ isDisabled: value });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Handles updates for the invisible topic.
|
|
45
|
+
*/
|
|
46
|
+
Object.defineProperty(this, "handleInvisibleTopicUpdate", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: (value) => {
|
|
51
|
+
this.setState({ isInvisible: value });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
this.state = {
|
|
55
|
+
currentValue: undefined,
|
|
56
|
+
isDisabled: false,
|
|
57
|
+
isInvisible: false,
|
|
58
|
+
isPressed: false
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
componentDidMount() {
|
|
62
|
+
this.setupSubscriptions();
|
|
63
|
+
}
|
|
64
|
+
componentDidUpdate(prevProps) {
|
|
65
|
+
prevProps;
|
|
66
|
+
// Logic to handle updates in props, if necessary
|
|
67
|
+
}
|
|
68
|
+
componentWillUnmount() {
|
|
69
|
+
// Unsubscribe logic if needed
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Sets up subscriptions based on provided topics.
|
|
73
|
+
*/
|
|
74
|
+
setupSubscriptions() {
|
|
75
|
+
const { topic, disableTopic, invisibleTopic } = this.props;
|
|
76
|
+
const { subscribe } = this.context;
|
|
77
|
+
if (topic) {
|
|
78
|
+
// Subscribe to the main topic
|
|
79
|
+
subscribe(topic, this.handleTopicUpdate);
|
|
80
|
+
}
|
|
81
|
+
if (disableTopic) {
|
|
82
|
+
// Subscribe to the disable topic
|
|
83
|
+
subscribe(disableTopic, this.handleDisableTopicUpdate);
|
|
84
|
+
}
|
|
85
|
+
if (invisibleTopic) {
|
|
86
|
+
// Subscribe to the invisible topic
|
|
87
|
+
subscribe(invisibleTopic, this.handleInvisibleTopicUpdate);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Handle the button being pressed down. Used for TAP and PRESSED action modes.
|
|
92
|
+
*/
|
|
93
|
+
handleOnPressed() {
|
|
94
|
+
const { isPressed } = this.state;
|
|
95
|
+
const { actionMode } = this.props;
|
|
96
|
+
if (!isPressed) {
|
|
97
|
+
this.setState({ isPressed: true });
|
|
98
|
+
if (actionMode === ActionMode.Tap || actionMode === ActionMode.Pressed) {
|
|
99
|
+
this.dispatchCommand(true);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Handle the button being released. Used for TAP and RELEASED action modes.
|
|
105
|
+
*/
|
|
106
|
+
handleOnReleased() {
|
|
107
|
+
const { isPressed } = this.state;
|
|
108
|
+
const { actionMode } = this.props;
|
|
109
|
+
if (isPressed) {
|
|
110
|
+
this.setState({ isPressed: false });
|
|
111
|
+
if (actionMode === ActionMode.Tap) {
|
|
112
|
+
this.dispatchCommand(false);
|
|
113
|
+
}
|
|
114
|
+
else if (actionMode === ActionMode.Released) {
|
|
115
|
+
this.dispatchCommand(true);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Dispatch a value to any listeners.
|
|
121
|
+
* @param value The value to dispatch as part of the payload.
|
|
122
|
+
*/
|
|
123
|
+
dispatchCommand(value) {
|
|
124
|
+
const { command, commandTopic, commandArgs } = this.props;
|
|
125
|
+
const { dispatch } = this.context;
|
|
126
|
+
if (command !== undefined && command !== null && command.length > 0) {
|
|
127
|
+
const payload = {
|
|
128
|
+
topic: commandTopic,
|
|
129
|
+
value: value,
|
|
130
|
+
...commandArgs
|
|
131
|
+
};
|
|
132
|
+
dispatch({
|
|
133
|
+
topic: command,
|
|
134
|
+
payload: payload
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
render() {
|
|
139
|
+
const { currentValue, isDisabled, isInvisible } = this.state;
|
|
140
|
+
const { className, severity, label, raised, value, onColor, offColor, options, topic, command, commandArgs, disableTopic, invisibleTopic, inputMode, invert, onIcon, offIcon, icon, ...restProps } = this.props;
|
|
141
|
+
//
|
|
142
|
+
// Load the value from the proper source.
|
|
143
|
+
// If the `value` property is undefined, that takes precedence.
|
|
144
|
+
//
|
|
145
|
+
let displayValue = currentValue;
|
|
146
|
+
if (value !== undefined) {
|
|
147
|
+
displayValue = value;
|
|
148
|
+
}
|
|
149
|
+
// Determine the label and color based on the current state
|
|
150
|
+
let btnLabel = undefined;
|
|
151
|
+
if (options !== undefined && options !== null) {
|
|
152
|
+
btnLabel = displayValue ? options[1] : options[0];
|
|
153
|
+
if (btnLabel === undefined || btnLabel === null || btnLabel.length === 0)
|
|
154
|
+
btnLabel = label;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
btnLabel = label;
|
|
158
|
+
}
|
|
159
|
+
let btnIcon = displayValue ? onIcon : offIcon;
|
|
160
|
+
if (btnIcon === undefined || btnIcon === null || btnIcon.length === 0)
|
|
161
|
+
btnIcon = icon;
|
|
162
|
+
let color = displayValue ? onColor : offColor;
|
|
163
|
+
// Handle special cases like undefined state or specific input modes
|
|
164
|
+
if (displayValue === undefined) {
|
|
165
|
+
color = IndicatorColor.IndicatorInvalid;
|
|
166
|
+
}
|
|
167
|
+
return (_jsx(Button, { ...restProps, label: btnLabel, icon: `pi ${btnIcon}`, className: className, style: { color: "white", backgroundColor: color, display: isInvisible ? 'none' : 'block' }, disabled: isDisabled, onMouseDown: () => this.handleOnPressed(), onTouchStart: () => this.handleOnPressed(), onMouseUp: () => this.handleOnReleased(), onTouchEnd: () => this.handleOnReleased(), value: disableTopic, raised: true }));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
Object.defineProperty(IndicatorButton, "contextType", {
|
|
171
|
+
enumerable: true,
|
|
172
|
+
configurable: true,
|
|
173
|
+
writable: true,
|
|
174
|
+
value: EventEmitterContext
|
|
175
|
+
});
|
|
176
|
+
// Define default props
|
|
177
|
+
Object.defineProperty(IndicatorButton, "defaultProps", {
|
|
178
|
+
enumerable: true,
|
|
179
|
+
configurable: true,
|
|
180
|
+
writable: true,
|
|
181
|
+
value: {
|
|
182
|
+
onColor: IndicatorColor.IndicatorGreen,
|
|
183
|
+
offColor: "gray", // Default offColor
|
|
184
|
+
// ... other default values for props ...
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
export default IndicatorButton;
|