@procaaso/alphinity-ui-components 1.15.0 → 1.17.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/dist/index.d.cts CHANGED
@@ -510,8 +510,23 @@ interface CustomAction {
510
510
  label: string;
511
511
  /** Optional leading icon / emoji. */
512
512
  icon?: string;
513
- /** Visual variant (reserved for future styling). */
514
- variant?: 'primary' | 'secondary' | 'alarm' | 'warning';
513
+ /**
514
+ * Visual variant. The ramp variants give the button a colour that shows its
515
+ * function:
516
+ * - `start`: green, the same green as START (for example START RAMP).
517
+ * - `hold`: amber (for example HOLD).
518
+ * - `cancel`: slate grey. Red stays only on STOP, so an operator cannot
519
+ * confuse "cancel the ramp" with "stop the device".
520
+ * - `complete`: a status, not a command. A pale green fill, a solid green
521
+ * border, and a check mark. The operator cannot press it, and it never
522
+ * fires onCustomAction.
523
+ * When `disabled` is true, `start` / `hold` / `cancel` use the same hatched,
524
+ * dashed-border look as a disabled START.
525
+ *
526
+ * `primary` / `secondary` / `alarm` / `warning` are reserved and render as a
527
+ * plain button.
528
+ */
529
+ variant?: 'primary' | 'secondary' | 'alarm' | 'warning' | 'start' | 'hold' | 'cancel' | 'complete';
515
530
  /**
516
531
  * When true, the button renders disabled and does NOT fire onCustomAction.
517
532
  * Use for actions that are only conditionally available (e.g. a Tare that is
@@ -698,6 +713,15 @@ interface DeviceControlBinding {
698
713
  style?: React.CSSProperties;
699
714
  /** Group parameters by category */
700
715
  groupParameters?: boolean;
716
+ /**
717
+ * Layout of the custom action buttons.
718
+ * - 'wrap' (default): each action has a 120px minimum width and the
719
+ * actions wrap onto more rows when the panel is narrow.
720
+ * - 'row': all actions share ONE row with no minimum width and a small
721
+ * side padding, so several actions fit in a narrow faceplate. A long
722
+ * label can wrap onto two lines inside its button.
723
+ */
724
+ customActionsLayout?: 'wrap' | 'row';
701
725
  };
702
726
  /** Styling overrides */
703
727
  theme?: {
package/dist/index.d.ts CHANGED
@@ -510,8 +510,23 @@ interface CustomAction {
510
510
  label: string;
511
511
  /** Optional leading icon / emoji. */
512
512
  icon?: string;
513
- /** Visual variant (reserved for future styling). */
514
- variant?: 'primary' | 'secondary' | 'alarm' | 'warning';
513
+ /**
514
+ * Visual variant. The ramp variants give the button a colour that shows its
515
+ * function:
516
+ * - `start`: green, the same green as START (for example START RAMP).
517
+ * - `hold`: amber (for example HOLD).
518
+ * - `cancel`: slate grey. Red stays only on STOP, so an operator cannot
519
+ * confuse "cancel the ramp" with "stop the device".
520
+ * - `complete`: a status, not a command. A pale green fill, a solid green
521
+ * border, and a check mark. The operator cannot press it, and it never
522
+ * fires onCustomAction.
523
+ * When `disabled` is true, `start` / `hold` / `cancel` use the same hatched,
524
+ * dashed-border look as a disabled START.
525
+ *
526
+ * `primary` / `secondary` / `alarm` / `warning` are reserved and render as a
527
+ * plain button.
528
+ */
529
+ variant?: 'primary' | 'secondary' | 'alarm' | 'warning' | 'start' | 'hold' | 'cancel' | 'complete';
515
530
  /**
516
531
  * When true, the button renders disabled and does NOT fire onCustomAction.
517
532
  * Use for actions that are only conditionally available (e.g. a Tare that is
@@ -698,6 +713,15 @@ interface DeviceControlBinding {
698
713
  style?: React.CSSProperties;
699
714
  /** Group parameters by category */
700
715
  groupParameters?: boolean;
716
+ /**
717
+ * Layout of the custom action buttons.
718
+ * - 'wrap' (default): each action has a 120px minimum width and the
719
+ * actions wrap onto more rows when the panel is narrow.
720
+ * - 'row': all actions share ONE row with no minimum width and a small
721
+ * side padding, so several actions fit in a narrow faceplate. A long
722
+ * label can wrap onto two lines inside its button.
723
+ */
724
+ customActionsLayout?: 'wrap' | 'row';
701
725
  };
702
726
  /** Styling overrides */
703
727
  theme?: {
package/dist/index.js CHANGED
@@ -1143,6 +1143,20 @@ var DISABLED_BUTTON_BG = "#e5e7eb";
1143
1143
  var DISABLED_BUTTON_TEXT = "#4b5563";
1144
1144
  var DISABLED_BUTTON_BORDER = "#6b7280";
1145
1145
  var DISABLED_BUTTON_HATCH = "repeating-linear-gradient(135deg, rgba(0,0,0,0.10) 0px, rgba(0,0,0,0.10) 4px, transparent 4px, transparent 10px)";
1146
+ var START_BUTTON_BG = "#10b981";
1147
+ var STOP_BUTTON_BG = "#ef4444";
1148
+ var COMMAND_VARIANT_STYLES = {
1149
+ start: { backgroundColor: START_BUTTON_BG, color: "#ffffff" },
1150
+ hold: { backgroundColor: "#f59e0b", color: "#1f1300" },
1151
+ cancel: { backgroundColor: "#64748b", color: "#ffffff" }
1152
+ };
1153
+ var COMPLETE_VARIANT_STYLE = {
1154
+ backgroundColor: "#d1fae5",
1155
+ color: "#065f46",
1156
+ border: `2px solid ${START_BUTTON_BG}`,
1157
+ cursor: "default",
1158
+ opacity: 1
1159
+ };
1146
1160
  function DeviceControlPanelImpl({
1147
1161
  binding,
1148
1162
  onClose,
@@ -1224,6 +1238,7 @@ function DeviceControlPanelImpl({
1224
1238
  const statusColor = statusEntry.bg;
1225
1239
  const isUnknownStatus = normalizeDeviceStatus(binding.state.status) === "unknown";
1226
1240
  const startStopRowShown = binding.display?.showStartStop !== false && (activeCapabilities?.canStart === true || activeCapabilities?.canStop === true);
1241
+ const customActionsInRow = binding.display?.customActionsLayout === "row";
1227
1242
  const startEnabledNow = activeCapabilities?.canStart === true && activeCapabilities?.startEnabled !== false && !startPending;
1228
1243
  const stopEnabledNow = activeCapabilities?.canStop === true && activeCapabilities?.stopEnabled !== false;
1229
1244
  const startStopFontPx = Math.max(18, Math.round(18 * fontScale));
@@ -2041,7 +2056,7 @@ function DeviceControlPanelImpl({
2041
2056
  },
2042
2057
  disabled: !startEnabledNow,
2043
2058
  "aria-busy": startPending || void 0,
2044
- style: startStopButtonStyle(startEnabledNow, "#10b981"),
2059
+ style: startStopButtonStyle(startEnabledNow, START_BUTTON_BG),
2045
2060
  children: startPending ? "STARTING\u2026" : "START"
2046
2061
  }
2047
2062
  ),
@@ -2056,48 +2071,86 @@ function DeviceControlPanelImpl({
2056
2071
  },
2057
2072
  disabled: !stopEnabledNow,
2058
2073
  "aria-busy": stopPending || void 0,
2059
- style: startStopButtonStyle(stopEnabledNow, "#ef4444"),
2074
+ style: startStopButtonStyle(stopEnabledNow, STOP_BUTTON_BG),
2060
2075
  children: stopPending ? "STOPPING\u2026" : "STOP"
2061
2076
  }
2062
2077
  )
2063
2078
  ]
2064
2079
  }
2065
2080
  ),
2066
- activeCapabilities?.customActions && activeCapabilities.customActions.length > 0 && /* @__PURE__ */ jsx9("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: activeCapabilities.customActions.map((action) => {
2067
- const actionDisabled = action.disabled === true;
2068
- return /* @__PURE__ */ jsxs8(
2069
- "button",
2070
- {
2071
- onClick: () => {
2072
- if (!actionDisabled) onCustomAction?.(action.id);
2073
- },
2074
- disabled: actionDisabled,
2075
- title: actionDisabled ? action.disabledReason : void 0,
2076
- style: {
2077
- flex: 1,
2078
- minWidth: "120px",
2079
- height: touchSize,
2080
- backgroundColor: surfaceColor,
2081
- color: textColor,
2082
- border: `1px solid ${borderColor}`,
2083
- borderRadius: "6px",
2084
- fontSize: "11px",
2085
- fontWeight: 600,
2086
- fontFamily: "monospace",
2087
- letterSpacing: "0.5px",
2088
- textTransform: "uppercase",
2089
- cursor: actionDisabled ? "not-allowed" : "pointer",
2090
- opacity: actionDisabled ? 0.4 : 1,
2091
- transition: "all 0.15s"
2092
- },
2093
- children: [
2094
- action.icon && /* @__PURE__ */ jsx9("span", { style: { marginRight: "6px" }, children: action.icon }),
2095
- action.label
2096
- ]
2081
+ activeCapabilities?.customActions && activeCapabilities.customActions.length > 0 && /* @__PURE__ */ jsx9(
2082
+ "div",
2083
+ {
2084
+ style: {
2085
+ display: "flex",
2086
+ flexWrap: customActionsInRow ? "nowrap" : "wrap",
2087
+ gap: "8px"
2097
2088
  },
2098
- action.id
2099
- );
2100
- }) })
2089
+ children: activeCapabilities.customActions.map((action) => {
2090
+ const isComplete = action.variant === "complete";
2091
+ const actionDisabled = action.disabled === true || isComplete;
2092
+ const commandStyle = action.variant === "start" || action.variant === "hold" || action.variant === "cancel" ? COMMAND_VARIANT_STYLES[action.variant] : void 0;
2093
+ let variantStyle;
2094
+ if (isComplete) {
2095
+ variantStyle = COMPLETE_VARIANT_STYLE;
2096
+ } else if (commandStyle && actionDisabled) {
2097
+ variantStyle = {
2098
+ backgroundColor: DISABLED_BUTTON_BG,
2099
+ backgroundImage: DISABLED_BUTTON_HATCH,
2100
+ color: DISABLED_BUTTON_TEXT,
2101
+ border: `2px dashed ${DISABLED_BUTTON_BORDER}`,
2102
+ cursor: "not-allowed",
2103
+ opacity: 1
2104
+ };
2105
+ } else if (commandStyle) {
2106
+ variantStyle = {
2107
+ ...commandStyle,
2108
+ border: "2px solid transparent",
2109
+ cursor: "pointer",
2110
+ opacity: 1
2111
+ };
2112
+ } else {
2113
+ variantStyle = {
2114
+ backgroundColor: surfaceColor,
2115
+ color: textColor,
2116
+ border: `1px solid ${borderColor}`,
2117
+ cursor: actionDisabled ? "not-allowed" : "pointer",
2118
+ opacity: actionDisabled ? 0.4 : 1
2119
+ };
2120
+ }
2121
+ return /* @__PURE__ */ jsxs8(
2122
+ "button",
2123
+ {
2124
+ type: "button",
2125
+ "data-variant": action.variant,
2126
+ onClick: () => {
2127
+ if (!actionDisabled) onCustomAction?.(action.id);
2128
+ },
2129
+ disabled: actionDisabled,
2130
+ title: action.disabled === true ? action.disabledReason : void 0,
2131
+ style: {
2132
+ ...customActionsInRow ? { flex: "1 1 0px", minWidth: 0, paddingLeft: "4px", paddingRight: "4px" } : { flex: 1, minWidth: "120px" },
2133
+ height: touchSize,
2134
+ borderRadius: "6px",
2135
+ fontSize: "11px",
2136
+ fontWeight: 600,
2137
+ fontFamily: "monospace",
2138
+ letterSpacing: "0.5px",
2139
+ textTransform: "uppercase",
2140
+ transition: "all 0.15s",
2141
+ ...variantStyle
2142
+ },
2143
+ children: [
2144
+ isComplete && !action.icon && /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", style: { marginRight: "6px" }, children: "\u2713" }),
2145
+ action.icon && /* @__PURE__ */ jsx9("span", { style: { marginRight: "6px" }, children: action.icon }),
2146
+ action.label
2147
+ ]
2148
+ },
2149
+ action.id
2150
+ );
2151
+ })
2152
+ }
2153
+ )
2101
2154
  ] }),
2102
2155
  binding.state.lastCommandResult && showToast && showBuiltInToast && /* @__PURE__ */ jsx9(
2103
2156
  "div",