@camstack/ui-library 1.2.21 → 1.2.22
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 +10 -9
- package/dist/index.cjs +39 -27
- package/dist/index.js +39 -27
- package/package.json +1 -1
|
@@ -6,17 +6,18 @@ import { DeviceControlProps } from './control-registry';
|
|
|
6
6
|
* Drops into a 200 px control column without towering over sibling rows.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* The row/card-sized alarm control.
|
|
9
|
+
* The row/card-sized alarm control — ONE LINE, always.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* you its state.
|
|
11
|
+
* Two earlier attempts were wrong in instructive ways. The first rendered only
|
|
12
|
+
* the segment strip, so the sole sign a panel was armed was which of four
|
|
13
|
+
* buttons happened to be highlighted — and in a grid card the strip overflowed
|
|
14
|
+
* and cut that button off. The second let the strip WRAP, which stopped the
|
|
15
|
+
* overflow by making the row two and three lines tall.
|
|
17
16
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* A state badge plus four buttons does not fit a phone-width row, and no amount
|
|
18
|
+
* of sizing makes it. So the row shows what a row is for — **the state** — and
|
|
19
|
+
* offers the modes through a select, which is one line at any width. The four
|
|
20
|
+
* segments live in the hero, where there is room for them.
|
|
20
21
|
*/
|
|
21
22
|
export declare function AlarmInlineControl({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
|
|
22
23
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -26770,7 +26770,7 @@ function AlarmGlyph({ icon, sizeClass = "h-8 w-8" }) {
|
|
|
26770
26770
|
"aria-hidden": "true"
|
|
26771
26771
|
});
|
|
26772
26772
|
}
|
|
26773
|
-
function Segment({ label, active, busy, onSelect
|
|
26773
|
+
function Segment({ label, active, busy, onSelect }) {
|
|
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, compact = false }) {
|
|
|
26780
26780
|
e.stopPropagation();
|
|
26781
26781
|
if (!busy) onSelect();
|
|
26782
26782
|
},
|
|
26783
|
-
className: cn("rounded-md
|
|
26783
|
+
className: cn("flex-1 rounded-md px-3 py-2 text-sm font-medium transition-colors", 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,23 +26790,21 @@ function Segment({ label, active, busy, onSelect, compact = false }) {
|
|
|
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 }) {
|
|
26794
26794
|
const armed = isArmedState(state);
|
|
26795
26795
|
const transitioning = isTransitioningState(state);
|
|
26796
26796
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
26797
|
-
className: cn("flex min-w-0
|
|
26797
|
+
className: cn("flex min-w-0 items-stretch gap-1 rounded-lg border border-border bg-surface-subtle 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
|
|
26803
|
-
compact
|
|
26802
|
+
onSelect: onDisarm
|
|
26804
26803
|
}), availableModes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segment, {
|
|
26805
26804
|
label: ARM_MODE_LABEL[mode],
|
|
26806
26805
|
active: !transitioning && state === `armed_${mode}`,
|
|
26807
26806
|
busy,
|
|
26808
|
-
onSelect: () => onArm(mode)
|
|
26809
|
-
compact
|
|
26807
|
+
onSelect: () => onArm(mode)
|
|
26810
26808
|
}, mode))]
|
|
26811
26809
|
});
|
|
26812
26810
|
}
|
|
@@ -26848,38 +26846,52 @@ function useAlarmControl(trpc, deviceId, optimistic) {
|
|
|
26848
26846
|
* Drops into a 200 px control column without towering over sibling rows.
|
|
26849
26847
|
*/
|
|
26850
26848
|
/**
|
|
26851
|
-
* The row/card-sized alarm control.
|
|
26849
|
+
* The row/card-sized alarm control — ONE LINE, always.
|
|
26852
26850
|
*
|
|
26853
|
-
*
|
|
26854
|
-
*
|
|
26855
|
-
*
|
|
26856
|
-
*
|
|
26857
|
-
*
|
|
26858
|
-
* you its state.
|
|
26851
|
+
* Two earlier attempts were wrong in instructive ways. The first rendered only
|
|
26852
|
+
* the segment strip, so the sole sign a panel was armed was which of four
|
|
26853
|
+
* buttons happened to be highlighted — and in a grid card the strip overflowed
|
|
26854
|
+
* and cut that button off. The second let the strip WRAP, which stopped the
|
|
26855
|
+
* overflow by making the row two and three lines tall.
|
|
26859
26856
|
*
|
|
26860
|
-
*
|
|
26861
|
-
*
|
|
26857
|
+
* A state badge plus four buttons does not fit a phone-width row, and no amount
|
|
26858
|
+
* of sizing makes it. So the row shows what a row is for — **the state** — and
|
|
26859
|
+
* offers the modes through a select, which is one line at any width. The four
|
|
26860
|
+
* segments live in the hero, where there is room for them.
|
|
26862
26861
|
*/
|
|
26863
26862
|
function AlarmInlineControl({ trpc, deviceId, optimistic }) {
|
|
26864
26863
|
const { display, capPresent, busy, handleDisarm, handleArm } = useAlarmControl(trpc, deviceId, optimistic);
|
|
26865
26864
|
if (!capPresent) return null;
|
|
26866
26865
|
if (display === void 0) return null;
|
|
26867
26866
|
const config = ALARM_STATE_CONFIG[display.state];
|
|
26867
|
+
const current = isArmedState(display.state) ? display.state.replace("armed_", "") : "";
|
|
26868
26868
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
26869
|
-
className: "flex min-w-0
|
|
26869
|
+
className: "flex min-w-0 items-center gap-2",
|
|
26870
26870
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
26871
|
-
className: cn("inline-flex shrink-0 items-center gap-1
|
|
26871
|
+
className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] font-medium whitespace-nowrap", config.colorClass, isTransitioningState(display.state) && "animate-pulse"),
|
|
26872
26872
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlarmGlyph, {
|
|
26873
26873
|
icon: config.icon,
|
|
26874
|
-
sizeClass: "h-3
|
|
26874
|
+
sizeClass: "h-3 w-3"
|
|
26875
26875
|
}), config.label]
|
|
26876
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
26877
|
-
|
|
26878
|
-
|
|
26879
|
-
|
|
26880
|
-
|
|
26881
|
-
|
|
26882
|
-
|
|
26876
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
26877
|
+
"aria-label": "Alarm mode",
|
|
26878
|
+
disabled: busy,
|
|
26879
|
+
value: current,
|
|
26880
|
+
onClick: (e) => e.stopPropagation(),
|
|
26881
|
+
onChange: (e) => {
|
|
26882
|
+
e.stopPropagation();
|
|
26883
|
+
const next = e.target.value;
|
|
26884
|
+
if (next === "") handleDisarm();
|
|
26885
|
+
else handleArm(next);
|
|
26886
|
+
},
|
|
26887
|
+
className: cn("min-w-0 shrink rounded-md border border-border bg-surface-subtle px-1.5 py-0.5 text-xs text-foreground-muted", busy && "cursor-not-allowed opacity-40"),
|
|
26888
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
26889
|
+
value: "",
|
|
26890
|
+
children: "Disarm"
|
|
26891
|
+
}), display.availableModes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
26892
|
+
value: mode,
|
|
26893
|
+
children: ARM_MODE_LABEL[mode]
|
|
26894
|
+
}, mode))]
|
|
26883
26895
|
})]
|
|
26884
26896
|
});
|
|
26885
26897
|
}
|
package/dist/index.js
CHANGED
|
@@ -26746,7 +26746,7 @@ function AlarmGlyph({ icon, sizeClass = "h-8 w-8" }) {
|
|
|
26746
26746
|
"aria-hidden": "true"
|
|
26747
26747
|
});
|
|
26748
26748
|
}
|
|
26749
|
-
function Segment({ label, active, busy, onSelect
|
|
26749
|
+
function Segment({ label, active, busy, onSelect }) {
|
|
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, compact = false }) {
|
|
|
26756
26756
|
e.stopPropagation();
|
|
26757
26757
|
if (!busy) onSelect();
|
|
26758
26758
|
},
|
|
26759
|
-
className: cn("rounded-md
|
|
26759
|
+
className: cn("flex-1 rounded-md px-3 py-2 text-sm font-medium transition-colors", 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,23 +26766,21 @@ function Segment({ label, active, busy, onSelect, compact = false }) {
|
|
|
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 }) {
|
|
26770
26770
|
const armed = isArmedState(state);
|
|
26771
26771
|
const transitioning = isTransitioningState(state);
|
|
26772
26772
|
return /* @__PURE__ */ jsxs("div", {
|
|
26773
|
-
className: cn("flex min-w-0
|
|
26773
|
+
className: cn("flex min-w-0 items-stretch gap-1 rounded-lg border border-border bg-surface-subtle p-1"),
|
|
26774
26774
|
children: [/* @__PURE__ */ jsx(Segment, {
|
|
26775
26775
|
label: "Disarm",
|
|
26776
26776
|
active: !armed && !transitioning,
|
|
26777
26777
|
busy,
|
|
26778
|
-
onSelect: onDisarm
|
|
26779
|
-
compact
|
|
26778
|
+
onSelect: onDisarm
|
|
26780
26779
|
}), availableModes.map((mode) => /* @__PURE__ */ jsx(Segment, {
|
|
26781
26780
|
label: ARM_MODE_LABEL[mode],
|
|
26782
26781
|
active: !transitioning && state === `armed_${mode}`,
|
|
26783
26782
|
busy,
|
|
26784
|
-
onSelect: () => onArm(mode)
|
|
26785
|
-
compact
|
|
26783
|
+
onSelect: () => onArm(mode)
|
|
26786
26784
|
}, mode))]
|
|
26787
26785
|
});
|
|
26788
26786
|
}
|
|
@@ -26824,38 +26822,52 @@ function useAlarmControl(trpc, deviceId, optimistic) {
|
|
|
26824
26822
|
* Drops into a 200 px control column without towering over sibling rows.
|
|
26825
26823
|
*/
|
|
26826
26824
|
/**
|
|
26827
|
-
* The row/card-sized alarm control.
|
|
26825
|
+
* The row/card-sized alarm control — ONE LINE, always.
|
|
26828
26826
|
*
|
|
26829
|
-
*
|
|
26830
|
-
*
|
|
26831
|
-
*
|
|
26832
|
-
*
|
|
26833
|
-
*
|
|
26834
|
-
* you its state.
|
|
26827
|
+
* Two earlier attempts were wrong in instructive ways. The first rendered only
|
|
26828
|
+
* the segment strip, so the sole sign a panel was armed was which of four
|
|
26829
|
+
* buttons happened to be highlighted — and in a grid card the strip overflowed
|
|
26830
|
+
* and cut that button off. The second let the strip WRAP, which stopped the
|
|
26831
|
+
* overflow by making the row two and three lines tall.
|
|
26835
26832
|
*
|
|
26836
|
-
*
|
|
26837
|
-
*
|
|
26833
|
+
* A state badge plus four buttons does not fit a phone-width row, and no amount
|
|
26834
|
+
* of sizing makes it. So the row shows what a row is for — **the state** — and
|
|
26835
|
+
* offers the modes through a select, which is one line at any width. The four
|
|
26836
|
+
* segments live in the hero, where there is room for them.
|
|
26838
26837
|
*/
|
|
26839
26838
|
function AlarmInlineControl({ trpc, deviceId, optimistic }) {
|
|
26840
26839
|
const { display, capPresent, busy, handleDisarm, handleArm } = useAlarmControl(trpc, deviceId, optimistic);
|
|
26841
26840
|
if (!capPresent) return null;
|
|
26842
26841
|
if (display === void 0) return null;
|
|
26843
26842
|
const config = ALARM_STATE_CONFIG[display.state];
|
|
26843
|
+
const current = isArmedState(display.state) ? display.state.replace("armed_", "") : "";
|
|
26844
26844
|
return /* @__PURE__ */ jsxs("div", {
|
|
26845
|
-
className: "flex min-w-0
|
|
26845
|
+
className: "flex min-w-0 items-center gap-2",
|
|
26846
26846
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
26847
|
-
className: cn("inline-flex shrink-0 items-center gap-1
|
|
26847
|
+
className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] font-medium whitespace-nowrap", config.colorClass, isTransitioningState(display.state) && "animate-pulse"),
|
|
26848
26848
|
children: [/* @__PURE__ */ jsx(AlarmGlyph, {
|
|
26849
26849
|
icon: config.icon,
|
|
26850
|
-
sizeClass: "h-3
|
|
26850
|
+
sizeClass: "h-3 w-3"
|
|
26851
26851
|
}), config.label]
|
|
26852
|
-
}), /* @__PURE__ */
|
|
26853
|
-
|
|
26854
|
-
|
|
26855
|
-
|
|
26856
|
-
|
|
26857
|
-
|
|
26858
|
-
|
|
26852
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
26853
|
+
"aria-label": "Alarm mode",
|
|
26854
|
+
disabled: busy,
|
|
26855
|
+
value: current,
|
|
26856
|
+
onClick: (e) => e.stopPropagation(),
|
|
26857
|
+
onChange: (e) => {
|
|
26858
|
+
e.stopPropagation();
|
|
26859
|
+
const next = e.target.value;
|
|
26860
|
+
if (next === "") handleDisarm();
|
|
26861
|
+
else handleArm(next);
|
|
26862
|
+
},
|
|
26863
|
+
className: cn("min-w-0 shrink rounded-md border border-border bg-surface-subtle px-1.5 py-0.5 text-xs text-foreground-muted", busy && "cursor-not-allowed opacity-40"),
|
|
26864
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
26865
|
+
value: "",
|
|
26866
|
+
children: "Disarm"
|
|
26867
|
+
}), display.availableModes.map((mode) => /* @__PURE__ */ jsx("option", {
|
|
26868
|
+
value: mode,
|
|
26869
|
+
children: ARM_MODE_LABEL[mode]
|
|
26870
|
+
}, mode))]
|
|
26859
26871
|
})]
|
|
26860
26872
|
});
|
|
26861
26873
|
}
|