@procaaso/alphinity-ui-components 1.1.3 → 1.3.0
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/README.md +11 -0
- package/dist/index.cjs +123 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -100
- package/dist/index.d.ts +167 -100
- package/dist/index.js +126 -119
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -477,11 +477,12 @@ var ControlPanel = ({
|
|
|
477
477
|
secondSetpointValue,
|
|
478
478
|
onSecondSetpointChange,
|
|
479
479
|
modeConfigs = {},
|
|
480
|
-
defaultUnit
|
|
480
|
+
defaultUnit,
|
|
481
481
|
defaultLabel = "Setpoint",
|
|
482
|
-
defaultMin
|
|
483
|
-
defaultMax
|
|
484
|
-
defaultPrecision
|
|
482
|
+
defaultMin,
|
|
483
|
+
defaultMax,
|
|
484
|
+
defaultPrecision,
|
|
485
|
+
scaling,
|
|
485
486
|
status = "normal",
|
|
486
487
|
collapsed = false,
|
|
487
488
|
onCollapseChange,
|
|
@@ -503,11 +504,11 @@ var ControlPanel = ({
|
|
|
503
504
|
onCollapseChange?.(!newExpanded);
|
|
504
505
|
};
|
|
505
506
|
const currentModeConfig = modeConfigs[selectedMode] || {};
|
|
506
|
-
const currentUnit = currentModeConfig.unit || defaultUnit;
|
|
507
|
+
const currentUnit = currentModeConfig.unit || defaultUnit || scaling?.units || "Units";
|
|
507
508
|
const currentLabel = currentModeConfig.label || defaultLabel;
|
|
508
|
-
const currentMin = currentModeConfig.min ?? defaultMin;
|
|
509
|
-
const currentMax = currentModeConfig.max ?? defaultMax;
|
|
510
|
-
const currentPrecision = currentModeConfig.precision ?? defaultPrecision;
|
|
509
|
+
const currentMin = currentModeConfig.min ?? defaultMin ?? scaling?.euLow ?? 0;
|
|
510
|
+
const currentMax = currentModeConfig.max ?? defaultMax ?? scaling?.euHigh ?? 100;
|
|
511
|
+
const currentPrecision = currentModeConfig.precision ?? defaultPrecision ?? scaling?.decimals ?? 2;
|
|
511
512
|
const hasSecondSetpoint = currentModeConfig.hasSecondSetpoint || false;
|
|
512
513
|
const baseClass = "ui-control-panel";
|
|
513
514
|
const statusClass = status ? `ui-control-panel--${status}` : "";
|
|
@@ -936,6 +937,8 @@ function DeviceControlPanel({
|
|
|
936
937
|
onParameterChange,
|
|
937
938
|
onStart,
|
|
938
939
|
onStop,
|
|
940
|
+
startPending = false,
|
|
941
|
+
stopPending = false,
|
|
939
942
|
onCustomAction,
|
|
940
943
|
onOpenConfig,
|
|
941
944
|
showConfigButton = true,
|
|
@@ -1682,11 +1685,10 @@ function DeviceControlPanel({
|
|
|
1682
1685
|
"button",
|
|
1683
1686
|
{
|
|
1684
1687
|
onClick: onStart,
|
|
1685
|
-
disabled: binding.state.isRunning,
|
|
1686
1688
|
style: {
|
|
1687
1689
|
flex: 1,
|
|
1688
1690
|
height: touchSize,
|
|
1689
|
-
backgroundColor:
|
|
1691
|
+
backgroundColor: "#10b981",
|
|
1690
1692
|
color: "#ffffff",
|
|
1691
1693
|
border: "none",
|
|
1692
1694
|
borderRadius: "6px",
|
|
@@ -1694,21 +1696,20 @@ function DeviceControlPanel({
|
|
|
1694
1696
|
fontWeight: 700,
|
|
1695
1697
|
fontFamily: "Arial, sans-serif",
|
|
1696
1698
|
letterSpacing: "0.5px",
|
|
1697
|
-
cursor:
|
|
1698
|
-
opacity:
|
|
1699
|
+
cursor: "pointer",
|
|
1700
|
+
opacity: 1
|
|
1699
1701
|
},
|
|
1700
|
-
children: "START"
|
|
1702
|
+
children: startPending ? "STARTING\u2026" : "START"
|
|
1701
1703
|
}
|
|
1702
1704
|
),
|
|
1703
1705
|
activeCapabilities?.canStop && /* @__PURE__ */ jsx8(
|
|
1704
1706
|
"button",
|
|
1705
1707
|
{
|
|
1706
1708
|
onClick: onStop,
|
|
1707
|
-
disabled: !binding.state.isRunning,
|
|
1708
1709
|
style: {
|
|
1709
1710
|
flex: 1,
|
|
1710
1711
|
height: touchSize,
|
|
1711
|
-
backgroundColor:
|
|
1712
|
+
backgroundColor: "#ef4444",
|
|
1712
1713
|
color: "#ffffff",
|
|
1713
1714
|
border: "none",
|
|
1714
1715
|
borderRadius: "6px",
|
|
@@ -1716,10 +1717,10 @@ function DeviceControlPanel({
|
|
|
1716
1717
|
fontWeight: 700,
|
|
1717
1718
|
fontFamily: "Arial, sans-serif",
|
|
1718
1719
|
letterSpacing: "0.5px",
|
|
1719
|
-
cursor:
|
|
1720
|
-
opacity:
|
|
1720
|
+
cursor: "pointer",
|
|
1721
|
+
opacity: 1
|
|
1721
1722
|
},
|
|
1722
|
-
children: "STOP"
|
|
1723
|
+
children: stopPending ? "STOPPING\u2026" : "STOP"
|
|
1723
1724
|
}
|
|
1724
1725
|
)
|
|
1725
1726
|
] }),
|
|
@@ -4354,12 +4355,13 @@ function DataOverlay({
|
|
|
4354
4355
|
scale = 1,
|
|
4355
4356
|
statusColorMap
|
|
4356
4357
|
}) {
|
|
4357
|
-
const { value, display, history } = telemetry;
|
|
4358
|
+
const { value, display, history, scaling } = telemetry;
|
|
4358
4359
|
const showValue = display?.showValue ?? true;
|
|
4359
4360
|
const showStatus = display?.showStatus ?? true;
|
|
4360
4361
|
const showSparkline = display?.showSparkline ?? false;
|
|
4361
4362
|
const label = display?.label;
|
|
4362
|
-
const precision = display?.precision;
|
|
4363
|
+
const precision = scaling?.decimals ?? display?.precision;
|
|
4364
|
+
const unit = scaling?.units ?? value.unit;
|
|
4363
4365
|
const sparklineColor = display?.sparklineColor;
|
|
4364
4366
|
const textColor = display?.textColor;
|
|
4365
4367
|
const backgroundColor = display?.backgroundColor;
|
|
@@ -4383,7 +4385,7 @@ function DataOverlay({
|
|
|
4383
4385
|
const colors = backgroundColor && backgroundColor !== "status" ? { bg: backgroundColor, text: textColor || "#ffffff", border: backgroundColor } : statusColors;
|
|
4384
4386
|
const finalTextColor = textColor || colors.text;
|
|
4385
4387
|
const formattedValue = formatValue(value.value);
|
|
4386
|
-
const displayText =
|
|
4388
|
+
const displayText = unit ? `${formattedValue} ${unit}` : formattedValue;
|
|
4387
4389
|
const sparklinePath = (() => {
|
|
4388
4390
|
if (!showSparkline || !history || history.length < 2) return "";
|
|
4389
4391
|
const width = 70 * scale;
|
|
@@ -4391,8 +4393,9 @@ function DataOverlay({
|
|
|
4391
4393
|
const padding = 5 * scale;
|
|
4392
4394
|
const effectiveWidth = width - padding * 2;
|
|
4393
4395
|
const effectiveHeight = height - padding * 2;
|
|
4394
|
-
const
|
|
4395
|
-
const
|
|
4396
|
+
const hasEuRange = scaling?.euLow !== void 0 && scaling?.euHigh !== void 0 && scaling.euHigh !== scaling.euLow;
|
|
4397
|
+
const min = hasEuRange ? scaling.euLow : Math.min(...history);
|
|
4398
|
+
const max = hasEuRange ? scaling.euHigh : Math.max(...history);
|
|
4396
4399
|
const range = max - min || 1;
|
|
4397
4400
|
const points = history.map((val, i) => {
|
|
4398
4401
|
const xPos = i / (history.length - 1) * effectiveWidth + padding;
|
|
@@ -7553,8 +7556,12 @@ function createControlBinding(nodeId, deviceConfig, actions) {
|
|
|
7553
7556
|
}
|
|
7554
7557
|
|
|
7555
7558
|
// src/hooks/useDeviceControls.ts
|
|
7556
|
-
import { useState as useState15, useCallback as useCallback8 } from "react";
|
|
7557
|
-
function
|
|
7559
|
+
import { useState as useState15, useCallback as useCallback8, useRef as useRef6 } from "react";
|
|
7560
|
+
function commandKey(nodeId, kind, suffix) {
|
|
7561
|
+
return suffix ? `${nodeId}:${kind}:${suffix}` : `${nodeId}:${kind}`;
|
|
7562
|
+
}
|
|
7563
|
+
function useDeviceControls(initialBindings, options) {
|
|
7564
|
+
const { commandTimeoutMs } = options ?? {};
|
|
7558
7565
|
const [controls, setControls] = useState15(() => {
|
|
7559
7566
|
const map = /* @__PURE__ */ new Map();
|
|
7560
7567
|
if (initialBindings) {
|
|
@@ -7564,6 +7571,11 @@ function useDeviceControls(initialBindings) {
|
|
|
7564
7571
|
}
|
|
7565
7572
|
return map;
|
|
7566
7573
|
});
|
|
7574
|
+
const pendingRef = useRef6(/* @__PURE__ */ new Map());
|
|
7575
|
+
const [pendingCommands, setPendingCommands] = useState15(() => /* @__PURE__ */ new Set());
|
|
7576
|
+
const syncPending = useCallback8(() => {
|
|
7577
|
+
setPendingCommands(new Set(pendingRef.current.keys()));
|
|
7578
|
+
}, []);
|
|
7567
7579
|
const updateControl = useCallback8((nodeId, updates) => {
|
|
7568
7580
|
setControls((prev) => {
|
|
7569
7581
|
const newMap = new Map(prev);
|
|
@@ -7653,121 +7665,112 @@ function useDeviceControls(initialBindings) {
|
|
|
7653
7665
|
},
|
|
7654
7666
|
[controls]
|
|
7655
7667
|
);
|
|
7656
|
-
const
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
updateDeviceState(nodeId, {
|
|
7669
|
-
currentMode: binding.state.currentMode,
|
|
7670
|
-
lastCommandResult: {
|
|
7671
|
-
success: false,
|
|
7672
|
-
message: error instanceof Error ? error.message : "Mode change failed"
|
|
7673
|
-
}
|
|
7674
|
-
});
|
|
7668
|
+
const isCommandPending = useCallback8(
|
|
7669
|
+
(nodeId, kind) => {
|
|
7670
|
+
if (kind) {
|
|
7671
|
+
const prefix2 = `${nodeId}:${kind}`;
|
|
7672
|
+
for (const key of pendingCommands) {
|
|
7673
|
+
if (key === prefix2 || key.startsWith(`${prefix2}:`)) return true;
|
|
7674
|
+
}
|
|
7675
|
+
return false;
|
|
7676
|
+
}
|
|
7677
|
+
const prefix = `${nodeId}:`;
|
|
7678
|
+
for (const key of pendingCommands) {
|
|
7679
|
+
if (key.startsWith(prefix)) return true;
|
|
7675
7680
|
}
|
|
7681
|
+
return false;
|
|
7676
7682
|
},
|
|
7677
|
-
[
|
|
7683
|
+
[pendingCommands]
|
|
7678
7684
|
);
|
|
7679
|
-
const
|
|
7680
|
-
|
|
7681
|
-
const
|
|
7682
|
-
if (
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
await binding.actions.onParameterChange?.(parameterId, value);
|
|
7686
|
-
updateDeviceState(nodeId, {
|
|
7687
|
-
lastCommandTime: Date.now(),
|
|
7688
|
-
lastCommandResult: { success: true, message: `Parameter ${parameterId} updated` }
|
|
7689
|
-
});
|
|
7690
|
-
} catch (error) {
|
|
7691
|
-
updateDeviceState(nodeId, {
|
|
7692
|
-
lastCommandResult: {
|
|
7693
|
-
success: false,
|
|
7694
|
-
message: error instanceof Error ? error.message : "Parameter change failed"
|
|
7695
|
-
}
|
|
7696
|
-
});
|
|
7697
|
-
}
|
|
7685
|
+
const markPending = useCallback8(
|
|
7686
|
+
(key, delta) => {
|
|
7687
|
+
const next = (pendingRef.current.get(key) ?? 0) + delta;
|
|
7688
|
+
if (next > 0) pendingRef.current.set(key, next);
|
|
7689
|
+
else pendingRef.current.delete(key);
|
|
7690
|
+
syncPending();
|
|
7698
7691
|
},
|
|
7699
|
-
[
|
|
7692
|
+
[syncPending]
|
|
7700
7693
|
);
|
|
7701
|
-
const
|
|
7702
|
-
async (nodeId) => {
|
|
7694
|
+
const runCommand = useCallback8(
|
|
7695
|
+
async (nodeId, key, invoke, messages) => {
|
|
7703
7696
|
const binding = controls.get(nodeId);
|
|
7704
7697
|
if (!binding) return;
|
|
7705
|
-
|
|
7698
|
+
markPending(key, 1);
|
|
7699
|
+
let timer;
|
|
7706
7700
|
try {
|
|
7707
|
-
|
|
7701
|
+
const action = Promise.resolve(invoke(binding));
|
|
7702
|
+
if (commandTimeoutMs && commandTimeoutMs > 0) {
|
|
7703
|
+
await Promise.race([
|
|
7704
|
+
action,
|
|
7705
|
+
new Promise((_, reject) => {
|
|
7706
|
+
timer = setTimeout(
|
|
7707
|
+
() => reject(new Error(`${messages.success.replace(/ sent$/, "")} timed out (unconfirmed)`)),
|
|
7708
|
+
commandTimeoutMs
|
|
7709
|
+
);
|
|
7710
|
+
})
|
|
7711
|
+
]);
|
|
7712
|
+
} else {
|
|
7713
|
+
await action;
|
|
7714
|
+
}
|
|
7708
7715
|
updateDeviceState(nodeId, {
|
|
7709
|
-
status: "normal",
|
|
7710
7716
|
lastCommandTime: Date.now(),
|
|
7711
|
-
lastCommandResult: { success: true, message:
|
|
7717
|
+
lastCommandResult: { success: true, message: messages.success }
|
|
7712
7718
|
});
|
|
7713
7719
|
} catch (error) {
|
|
7714
7720
|
updateDeviceState(nodeId, {
|
|
7715
|
-
|
|
7716
|
-
status: "fault",
|
|
7721
|
+
lastCommandTime: Date.now(),
|
|
7717
7722
|
lastCommandResult: {
|
|
7718
7723
|
success: false,
|
|
7719
|
-
message: error instanceof Error ? error.message :
|
|
7724
|
+
message: error instanceof Error ? error.message : messages.failure
|
|
7720
7725
|
}
|
|
7721
7726
|
});
|
|
7727
|
+
} finally {
|
|
7728
|
+
if (timer) clearTimeout(timer);
|
|
7729
|
+
markPending(key, -1);
|
|
7722
7730
|
}
|
|
7723
7731
|
},
|
|
7724
|
-
[controls, updateDeviceState]
|
|
7732
|
+
[controls, updateDeviceState, markPending, commandTimeoutMs]
|
|
7733
|
+
);
|
|
7734
|
+
const sendModeChange = useCallback8(
|
|
7735
|
+
(nodeId, mode) => runCommand(
|
|
7736
|
+
nodeId,
|
|
7737
|
+
commandKey(nodeId, "mode"),
|
|
7738
|
+
(binding) => binding.actions.onModeChange?.(mode),
|
|
7739
|
+
{ success: `Mode ${mode} command sent`, failure: "Mode change failed" }
|
|
7740
|
+
),
|
|
7741
|
+
[runCommand]
|
|
7742
|
+
);
|
|
7743
|
+
const sendParameterChange = useCallback8(
|
|
7744
|
+
(nodeId, parameterId, value) => runCommand(
|
|
7745
|
+
nodeId,
|
|
7746
|
+
commandKey(nodeId, "parameter", parameterId),
|
|
7747
|
+
(binding) => binding.actions.onParameterChange?.(parameterId, value),
|
|
7748
|
+
{ success: `Parameter ${parameterId} command sent`, failure: "Parameter change failed" }
|
|
7749
|
+
),
|
|
7750
|
+
[runCommand]
|
|
7751
|
+
);
|
|
7752
|
+
const sendStartCommand = useCallback8(
|
|
7753
|
+
(nodeId) => runCommand(nodeId, commandKey(nodeId, "start"), (binding) => binding.actions.onStart?.(), {
|
|
7754
|
+
success: "Start command sent",
|
|
7755
|
+
failure: "Start command failed"
|
|
7756
|
+
}),
|
|
7757
|
+
[runCommand]
|
|
7725
7758
|
);
|
|
7726
7759
|
const sendStopCommand = useCallback8(
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
await binding.actions.onStop?.();
|
|
7733
|
-
updateDeviceState(nodeId, {
|
|
7734
|
-
status: "off",
|
|
7735
|
-
lastCommandTime: Date.now(),
|
|
7736
|
-
lastCommandResult: { success: true, message: "Device stopped" }
|
|
7737
|
-
});
|
|
7738
|
-
} catch (error) {
|
|
7739
|
-
updateDeviceState(nodeId, {
|
|
7740
|
-
isRunning: true,
|
|
7741
|
-
status: "fault",
|
|
7742
|
-
lastCommandResult: {
|
|
7743
|
-
success: false,
|
|
7744
|
-
message: error instanceof Error ? error.message : "Stop failed"
|
|
7745
|
-
}
|
|
7746
|
-
});
|
|
7747
|
-
}
|
|
7748
|
-
},
|
|
7749
|
-
[controls, updateDeviceState]
|
|
7760
|
+
(nodeId) => runCommand(nodeId, commandKey(nodeId, "stop"), (binding) => binding.actions.onStop?.(), {
|
|
7761
|
+
success: "Stop command sent",
|
|
7762
|
+
failure: "Stop command failed"
|
|
7763
|
+
}),
|
|
7764
|
+
[runCommand]
|
|
7750
7765
|
);
|
|
7751
7766
|
const sendCustomAction = useCallback8(
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
lastCommandResult: { success: true, message: `Action ${actionId} executed` }
|
|
7760
|
-
});
|
|
7761
|
-
} catch (error) {
|
|
7762
|
-
updateDeviceState(nodeId, {
|
|
7763
|
-
lastCommandResult: {
|
|
7764
|
-
success: false,
|
|
7765
|
-
message: error instanceof Error ? error.message : "Action failed"
|
|
7766
|
-
}
|
|
7767
|
-
});
|
|
7768
|
-
}
|
|
7769
|
-
},
|
|
7770
|
-
[controls, updateDeviceState]
|
|
7767
|
+
(nodeId, actionId) => runCommand(
|
|
7768
|
+
nodeId,
|
|
7769
|
+
commandKey(nodeId, "action", actionId),
|
|
7770
|
+
(binding) => binding.actions.onCustomAction?.(actionId),
|
|
7771
|
+
{ success: `Action ${actionId} command sent`, failure: "Action failed" }
|
|
7772
|
+
),
|
|
7773
|
+
[runCommand]
|
|
7771
7774
|
);
|
|
7772
7775
|
return {
|
|
7773
7776
|
controls,
|
|
@@ -7778,6 +7781,10 @@ function useDeviceControls(initialBindings) {
|
|
|
7778
7781
|
setControlBinding,
|
|
7779
7782
|
removeControlBinding,
|
|
7780
7783
|
getControlBinding,
|
|
7784
|
+
// In-flight command visibility (interaction state, not device state).
|
|
7785
|
+
// `isCommandPending` is the stable query surface; the raw key set is
|
|
7786
|
+
// intentionally not exported (its key format is an implementation detail).
|
|
7787
|
+
isCommandPending,
|
|
7781
7788
|
// Command helpers
|
|
7782
7789
|
sendModeChange,
|
|
7783
7790
|
sendParameterChange,
|
|
@@ -8066,7 +8073,7 @@ function useDeviceConfigs(initialConfigs) {
|
|
|
8066
8073
|
}
|
|
8067
8074
|
|
|
8068
8075
|
// src/components/DeviceConfigPanel/DeviceConfigPanel.tsx
|
|
8069
|
-
import { useState as useState17, useEffect as useEffect12, useMemo, useRef as
|
|
8076
|
+
import { useState as useState17, useEffect as useEffect12, useMemo, useRef as useRef7 } from "react";
|
|
8070
8077
|
import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
8071
8078
|
function DeviceConfigPanel({
|
|
8072
8079
|
binding,
|
|
@@ -8108,7 +8115,7 @@ function DeviceConfigPanel({
|
|
|
8108
8115
|
});
|
|
8109
8116
|
const [showToast, setShowToast] = useState17(true);
|
|
8110
8117
|
const [bindingId, setBindingId] = useState17(binding?.nodeId || null);
|
|
8111
|
-
const lastResultTimestampRef =
|
|
8118
|
+
const lastResultTimestampRef = useRef7(0);
|
|
8112
8119
|
if (binding && binding.nodeId !== bindingId) {
|
|
8113
8120
|
const values = {};
|
|
8114
8121
|
binding.parameters.forEach((param) => {
|