@camstack/ui-library 1.2.19 → 1.2.20
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/composites/device-controls/alarm-hero-card.d.ts +13 -0
- package/dist/index.cjs +43 -17
- package/dist/index.js +43 -17
- package/package.json +1 -1
|
@@ -5,6 +5,19 @@ import { DeviceControlProps } from './control-registry';
|
|
|
5
5
|
* no glyph, no state label, no card padding. Cap-gated on `alarm-panel`.
|
|
6
6
|
* Drops into a 200 px control column without towering over sibling rows.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* The row/card-sized alarm control.
|
|
10
|
+
*
|
|
11
|
+
* It leads with the STATE, and that is the fix rather than the decoration: the
|
|
12
|
+
* first version rendered only the segment strip, so the sole indication that a
|
|
13
|
+
* panel was armed was which of four buttons happened to be highlighted — and in
|
|
14
|
+
* a grid card the strip overflowed and cut the highlighted one off entirely. A
|
|
15
|
+
* security control whose state you have to infer from a button is not showing
|
|
16
|
+
* you its state.
|
|
17
|
+
*
|
|
18
|
+
* The strip is allowed to WRAP for the same reason: four modes do not fit a
|
|
19
|
+
* card at phone width, and a truncated alarm control is worse than a tall one.
|
|
20
|
+
*/
|
|
8
21
|
export declare function AlarmInlineControl({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|
|
9
22
|
/**
|
|
10
23
|
* AlarmHeroCard — segmented arm/disarm hero for AlarmPanel devices.
|
package/dist/index.cjs
CHANGED
|
@@ -26752,25 +26752,25 @@ function isArmedState(state) {
|
|
|
26752
26752
|
function isTransitioningState(state) {
|
|
26753
26753
|
return state === "arming" || state === "disarming";
|
|
26754
26754
|
}
|
|
26755
|
-
function AlarmGlyph({ icon }) {
|
|
26755
|
+
function AlarmGlyph({ icon, sizeClass = "h-8 w-8" }) {
|
|
26756
26756
|
if (icon === "disarmed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShieldOff, {
|
|
26757
|
-
className:
|
|
26757
|
+
className: sizeClass,
|
|
26758
26758
|
"aria-hidden": "true"
|
|
26759
26759
|
});
|
|
26760
26760
|
if (icon === "triggered") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShieldAlert, {
|
|
26761
|
-
className:
|
|
26761
|
+
className: sizeClass,
|
|
26762
26762
|
"aria-hidden": "true"
|
|
26763
26763
|
});
|
|
26764
26764
|
if (icon === "pending") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Shield, {
|
|
26765
|
-
className:
|
|
26765
|
+
className: sizeClass,
|
|
26766
26766
|
"aria-hidden": "true"
|
|
26767
26767
|
});
|
|
26768
26768
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShieldCheck, {
|
|
26769
|
-
className:
|
|
26769
|
+
className: sizeClass,
|
|
26770
26770
|
"aria-hidden": "true"
|
|
26771
26771
|
});
|
|
26772
26772
|
}
|
|
26773
|
-
function Segment({ label, active, busy, onSelect }) {
|
|
26773
|
+
function Segment({ label, active, busy, onSelect, compact = false }) {
|
|
26774
26774
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
26775
26775
|
type: "button",
|
|
26776
26776
|
"aria-label": label,
|
|
@@ -26780,7 +26780,7 @@ function Segment({ label, active, busy, onSelect }) {
|
|
|
26780
26780
|
e.stopPropagation();
|
|
26781
26781
|
if (!busy) onSelect();
|
|
26782
26782
|
},
|
|
26783
|
-
className: cn("
|
|
26783
|
+
className: cn("rounded-md font-medium transition-colors", compact ? "px-2 py-1 text-xs" : "flex-1 px-3 py-2 text-sm", active ? "bg-primary text-primary-foreground shadow-sm" : "text-foreground-muted hover:bg-surface-hover", busy && "opacity-40 cursor-not-allowed"),
|
|
26784
26784
|
children: label
|
|
26785
26785
|
});
|
|
26786
26786
|
}
|
|
@@ -26790,21 +26790,23 @@ function Segment({ label, active, busy, onSelect }) {
|
|
|
26790
26790
|
* segment row. Transitioning states (arming/disarming) leave NO segment
|
|
26791
26791
|
* highlighted so the Disarm button does not look active mid-arm.
|
|
26792
26792
|
*/
|
|
26793
|
-
function AlarmSegments({ state, availableModes, busy, onDisarm, onArm }) {
|
|
26793
|
+
function AlarmSegments({ state, availableModes, busy, onDisarm, onArm, compact = false }) {
|
|
26794
26794
|
const armed = isArmedState(state);
|
|
26795
26795
|
const transitioning = isTransitioningState(state);
|
|
26796
26796
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
26797
|
-
className: "flex items-stretch gap-1 rounded-lg border border-border bg-surface-subtle p-1",
|
|
26797
|
+
className: cn("flex min-w-0 flex-wrap items-stretch gap-1 rounded-lg border border-border bg-surface-subtle", compact ? "p-0.5" : "p-1"),
|
|
26798
26798
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segment, {
|
|
26799
26799
|
label: "Disarm",
|
|
26800
26800
|
active: !armed && !transitioning,
|
|
26801
26801
|
busy,
|
|
26802
|
-
onSelect: onDisarm
|
|
26802
|
+
onSelect: onDisarm,
|
|
26803
|
+
compact
|
|
26803
26804
|
}), availableModes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segment, {
|
|
26804
26805
|
label: ARM_MODE_LABEL[mode],
|
|
26805
26806
|
active: !transitioning && state === `armed_${mode}`,
|
|
26806
26807
|
busy,
|
|
26807
|
-
onSelect: () => onArm(mode)
|
|
26808
|
+
onSelect: () => onArm(mode),
|
|
26809
|
+
compact
|
|
26808
26810
|
}, mode))]
|
|
26809
26811
|
});
|
|
26810
26812
|
}
|
|
@@ -26845,16 +26847,40 @@ function useAlarmControl(trpc, deviceId, optimistic) {
|
|
|
26845
26847
|
* no glyph, no state label, no card padding. Cap-gated on `alarm-panel`.
|
|
26846
26848
|
* Drops into a 200 px control column without towering over sibling rows.
|
|
26847
26849
|
*/
|
|
26850
|
+
/**
|
|
26851
|
+
* The row/card-sized alarm control.
|
|
26852
|
+
*
|
|
26853
|
+
* It leads with the STATE, and that is the fix rather than the decoration: the
|
|
26854
|
+
* first version rendered only the segment strip, so the sole indication that a
|
|
26855
|
+
* panel was armed was which of four buttons happened to be highlighted — and in
|
|
26856
|
+
* a grid card the strip overflowed and cut the highlighted one off entirely. A
|
|
26857
|
+
* security control whose state you have to infer from a button is not showing
|
|
26858
|
+
* you its state.
|
|
26859
|
+
*
|
|
26860
|
+
* The strip is allowed to WRAP for the same reason: four modes do not fit a
|
|
26861
|
+
* card at phone width, and a truncated alarm control is worse than a tall one.
|
|
26862
|
+
*/
|
|
26848
26863
|
function AlarmInlineControl({ trpc, deviceId, optimistic }) {
|
|
26849
26864
|
const { display, capPresent, busy, handleDisarm, handleArm } = useAlarmControl(trpc, deviceId, optimistic);
|
|
26850
26865
|
if (!capPresent) return null;
|
|
26851
26866
|
if (display === void 0) return null;
|
|
26852
|
-
|
|
26853
|
-
|
|
26854
|
-
|
|
26855
|
-
|
|
26856
|
-
|
|
26857
|
-
|
|
26867
|
+
const config = ALARM_STATE_CONFIG[display.state];
|
|
26868
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
26869
|
+
className: "flex min-w-0 flex-wrap items-center gap-2",
|
|
26870
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
26871
|
+
className: cn("inline-flex shrink-0 items-center gap-1.5 rounded-md border px-2 py-1 text-[11px] font-medium", config.colorClass),
|
|
26872
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlarmGlyph, {
|
|
26873
|
+
icon: config.icon,
|
|
26874
|
+
sizeClass: "h-3.5 w-3.5"
|
|
26875
|
+
}), config.label]
|
|
26876
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlarmSegments, {
|
|
26877
|
+
state: display.state,
|
|
26878
|
+
availableModes: display.availableModes,
|
|
26879
|
+
busy,
|
|
26880
|
+
onDisarm: handleDisarm,
|
|
26881
|
+
onArm: handleArm,
|
|
26882
|
+
compact: true
|
|
26883
|
+
})]
|
|
26858
26884
|
});
|
|
26859
26885
|
}
|
|
26860
26886
|
/**
|
package/dist/index.js
CHANGED
|
@@ -26728,25 +26728,25 @@ function isArmedState(state) {
|
|
|
26728
26728
|
function isTransitioningState(state) {
|
|
26729
26729
|
return state === "arming" || state === "disarming";
|
|
26730
26730
|
}
|
|
26731
|
-
function AlarmGlyph({ icon }) {
|
|
26731
|
+
function AlarmGlyph({ icon, sizeClass = "h-8 w-8" }) {
|
|
26732
26732
|
if (icon === "disarmed") return /* @__PURE__ */ jsx(ShieldOff, {
|
|
26733
|
-
className:
|
|
26733
|
+
className: sizeClass,
|
|
26734
26734
|
"aria-hidden": "true"
|
|
26735
26735
|
});
|
|
26736
26736
|
if (icon === "triggered") return /* @__PURE__ */ jsx(ShieldAlert, {
|
|
26737
|
-
className:
|
|
26737
|
+
className: sizeClass,
|
|
26738
26738
|
"aria-hidden": "true"
|
|
26739
26739
|
});
|
|
26740
26740
|
if (icon === "pending") return /* @__PURE__ */ jsx(Shield, {
|
|
26741
|
-
className:
|
|
26741
|
+
className: sizeClass,
|
|
26742
26742
|
"aria-hidden": "true"
|
|
26743
26743
|
});
|
|
26744
26744
|
return /* @__PURE__ */ jsx(ShieldCheck, {
|
|
26745
|
-
className:
|
|
26745
|
+
className: sizeClass,
|
|
26746
26746
|
"aria-hidden": "true"
|
|
26747
26747
|
});
|
|
26748
26748
|
}
|
|
26749
|
-
function Segment({ label, active, busy, onSelect }) {
|
|
26749
|
+
function Segment({ label, active, busy, onSelect, compact = false }) {
|
|
26750
26750
|
return /* @__PURE__ */ jsx("button", {
|
|
26751
26751
|
type: "button",
|
|
26752
26752
|
"aria-label": label,
|
|
@@ -26756,7 +26756,7 @@ function Segment({ label, active, busy, onSelect }) {
|
|
|
26756
26756
|
e.stopPropagation();
|
|
26757
26757
|
if (!busy) onSelect();
|
|
26758
26758
|
},
|
|
26759
|
-
className: cn("
|
|
26759
|
+
className: cn("rounded-md font-medium transition-colors", compact ? "px-2 py-1 text-xs" : "flex-1 px-3 py-2 text-sm", active ? "bg-primary text-primary-foreground shadow-sm" : "text-foreground-muted hover:bg-surface-hover", busy && "opacity-40 cursor-not-allowed"),
|
|
26760
26760
|
children: label
|
|
26761
26761
|
});
|
|
26762
26762
|
}
|
|
@@ -26766,21 +26766,23 @@ function Segment({ label, active, busy, onSelect }) {
|
|
|
26766
26766
|
* segment row. Transitioning states (arming/disarming) leave NO segment
|
|
26767
26767
|
* highlighted so the Disarm button does not look active mid-arm.
|
|
26768
26768
|
*/
|
|
26769
|
-
function AlarmSegments({ state, availableModes, busy, onDisarm, onArm }) {
|
|
26769
|
+
function AlarmSegments({ state, availableModes, busy, onDisarm, onArm, compact = false }) {
|
|
26770
26770
|
const armed = isArmedState(state);
|
|
26771
26771
|
const transitioning = isTransitioningState(state);
|
|
26772
26772
|
return /* @__PURE__ */ jsxs("div", {
|
|
26773
|
-
className: "flex items-stretch gap-1 rounded-lg border border-border bg-surface-subtle p-1",
|
|
26773
|
+
className: cn("flex min-w-0 flex-wrap items-stretch gap-1 rounded-lg border border-border bg-surface-subtle", compact ? "p-0.5" : "p-1"),
|
|
26774
26774
|
children: [/* @__PURE__ */ jsx(Segment, {
|
|
26775
26775
|
label: "Disarm",
|
|
26776
26776
|
active: !armed && !transitioning,
|
|
26777
26777
|
busy,
|
|
26778
|
-
onSelect: onDisarm
|
|
26778
|
+
onSelect: onDisarm,
|
|
26779
|
+
compact
|
|
26779
26780
|
}), availableModes.map((mode) => /* @__PURE__ */ jsx(Segment, {
|
|
26780
26781
|
label: ARM_MODE_LABEL[mode],
|
|
26781
26782
|
active: !transitioning && state === `armed_${mode}`,
|
|
26782
26783
|
busy,
|
|
26783
|
-
onSelect: () => onArm(mode)
|
|
26784
|
+
onSelect: () => onArm(mode),
|
|
26785
|
+
compact
|
|
26784
26786
|
}, mode))]
|
|
26785
26787
|
});
|
|
26786
26788
|
}
|
|
@@ -26821,16 +26823,40 @@ function useAlarmControl(trpc, deviceId, optimistic) {
|
|
|
26821
26823
|
* no glyph, no state label, no card padding. Cap-gated on `alarm-panel`.
|
|
26822
26824
|
* Drops into a 200 px control column without towering over sibling rows.
|
|
26823
26825
|
*/
|
|
26826
|
+
/**
|
|
26827
|
+
* The row/card-sized alarm control.
|
|
26828
|
+
*
|
|
26829
|
+
* It leads with the STATE, and that is the fix rather than the decoration: the
|
|
26830
|
+
* first version rendered only the segment strip, so the sole indication that a
|
|
26831
|
+
* panel was armed was which of four buttons happened to be highlighted — and in
|
|
26832
|
+
* a grid card the strip overflowed and cut the highlighted one off entirely. A
|
|
26833
|
+
* security control whose state you have to infer from a button is not showing
|
|
26834
|
+
* you its state.
|
|
26835
|
+
*
|
|
26836
|
+
* The strip is allowed to WRAP for the same reason: four modes do not fit a
|
|
26837
|
+
* card at phone width, and a truncated alarm control is worse than a tall one.
|
|
26838
|
+
*/
|
|
26824
26839
|
function AlarmInlineControl({ trpc, deviceId, optimistic }) {
|
|
26825
26840
|
const { display, capPresent, busy, handleDisarm, handleArm } = useAlarmControl(trpc, deviceId, optimistic);
|
|
26826
26841
|
if (!capPresent) return null;
|
|
26827
26842
|
if (display === void 0) return null;
|
|
26828
|
-
|
|
26829
|
-
|
|
26830
|
-
|
|
26831
|
-
|
|
26832
|
-
|
|
26833
|
-
|
|
26843
|
+
const config = ALARM_STATE_CONFIG[display.state];
|
|
26844
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
26845
|
+
className: "flex min-w-0 flex-wrap items-center gap-2",
|
|
26846
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
26847
|
+
className: cn("inline-flex shrink-0 items-center gap-1.5 rounded-md border px-2 py-1 text-[11px] font-medium", config.colorClass),
|
|
26848
|
+
children: [/* @__PURE__ */ jsx(AlarmGlyph, {
|
|
26849
|
+
icon: config.icon,
|
|
26850
|
+
sizeClass: "h-3.5 w-3.5"
|
|
26851
|
+
}), config.label]
|
|
26852
|
+
}), /* @__PURE__ */ jsx(AlarmSegments, {
|
|
26853
|
+
state: display.state,
|
|
26854
|
+
availableModes: display.availableModes,
|
|
26855
|
+
busy,
|
|
26856
|
+
onDisarm: handleDisarm,
|
|
26857
|
+
onArm: handleArm,
|
|
26858
|
+
compact: true
|
|
26859
|
+
})]
|
|
26834
26860
|
});
|
|
26835
26861
|
}
|
|
26836
26862
|
/**
|