@camstack/ui-library 1.2.320 → 1.2.322
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/account-sign-in-field.d.ts +17 -0
- package/dist/composites/device-controls/dishwasher-control.d.ts +7 -0
- package/dist/composites/device-item/header.d.ts +3 -1
- package/dist/composites/device-picker/DevicePicker.d.ts +14 -2
- package/dist/composites/device-picker/index.d.ts +1 -1
- package/dist/composites/index.d.ts +4 -0
- package/dist/composites/running-badge.d.ts +20 -0
- package/dist/composites/running-status.d.ts +20 -0
- package/dist/generated/system-hooks.d.ts +2 -0
- package/dist/index.cjs +477 -63
- package/dist/index.js +474 -65
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -12229,6 +12229,8 @@ var useRecordingOnboardSetSettings = trpc.recordingOnboard.setSettings.useMutati
|
|
|
12229
12229
|
var useRecordingOnboardGetStatus = trpc.recordingOnboard.getStatus.useQuery;
|
|
12230
12230
|
/** Generated alias around `trpc.recordingSignal.getStatus.useQuery`. */
|
|
12231
12231
|
var useRecordingSignalGetStatus = trpc.recordingSignal.getStatus.useQuery;
|
|
12232
|
+
/** Generated alias around `trpc.runningState.getStatus.useQuery`. */
|
|
12233
|
+
var useRunningStateGetStatus = trpc.runningState.getStatus.useQuery;
|
|
12232
12234
|
/** Generated alias around `trpc.sceneMonitor.listScenes.useQuery`. */
|
|
12233
12235
|
var useSceneMonitorListScenes = trpc.sceneMonitor.listScenes.useQuery;
|
|
12234
12236
|
/** Generated alias around `trpc.sceneMonitor.listScenesBatch.useQuery`. */
|
|
@@ -13026,7 +13028,7 @@ function selectedDeviceOptions(options, selected) {
|
|
|
13026
13028
|
* select. Both are inline in ui-library (like `node-select`) so any host that
|
|
13027
13029
|
* renders the shared form field gets them without extra registration.
|
|
13028
13030
|
*/
|
|
13029
|
-
var INPUT_CLASS$
|
|
13031
|
+
var INPUT_CLASS$2 = "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-xs text-foreground focus:border-primary focus:ring-1 focus:ring-primary/30 outline-none disabled:opacity-50 disabled:cursor-not-allowed";
|
|
13030
13032
|
var INPUT_SHELL$1 = "flex items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1.5 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary/30";
|
|
13031
13033
|
var LABEL_CLASS$3 = "block text-[11px] font-medium text-foreground mb-1 break-words";
|
|
13032
13034
|
var DESC_CLASS$2 = "text-[10px] text-foreground-subtle mt-0.5 break-words";
|
|
@@ -13156,7 +13158,7 @@ function DeviceSelectField({ field, value, onChange, disabled, translationFn })
|
|
|
13156
13158
|
required: field.required,
|
|
13157
13159
|
translationFn,
|
|
13158
13160
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
13159
|
-
className: INPUT_CLASS$
|
|
13161
|
+
className: INPUT_CLASS$2,
|
|
13160
13162
|
value: current,
|
|
13161
13163
|
disabled: disabled || field.disabled,
|
|
13162
13164
|
onChange: (e) => onChange(e.target.value),
|
|
@@ -14382,6 +14384,282 @@ function useCustomFieldRenderer(type) {
|
|
|
14382
14384
|
return (0, react$1.useContext)(CustomFieldRenderersContext)[type] ?? null;
|
|
14383
14385
|
}
|
|
14384
14386
|
//#endregion
|
|
14387
|
+
//#region src/primitives/button.tsx
|
|
14388
|
+
var buttonVariants = cva("inline-flex items-center justify-center rounded-md font-medium transition-colors cursor-pointer disabled:opacity-50 disabled:pointer-events-none", {
|
|
14389
|
+
variants: {
|
|
14390
|
+
variant: {
|
|
14391
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14392
|
+
secondary: "bg-surface text-foreground hover:bg-surface-hover",
|
|
14393
|
+
ghost: "hover:bg-surface-hover text-foreground-muted",
|
|
14394
|
+
danger: "bg-danger text-white hover:bg-danger/90",
|
|
14395
|
+
outline: "border border-border bg-transparent text-foreground hover:bg-surface-hover"
|
|
14396
|
+
},
|
|
14397
|
+
size: {
|
|
14398
|
+
sm: "h-7 px-2.5 text-xs gap-1.5",
|
|
14399
|
+
md: "h-8 px-3 text-sm gap-2",
|
|
14400
|
+
lg: "h-9 px-4 text-sm gap-2"
|
|
14401
|
+
}
|
|
14402
|
+
},
|
|
14403
|
+
defaultVariants: {
|
|
14404
|
+
variant: "primary",
|
|
14405
|
+
size: "sm"
|
|
14406
|
+
}
|
|
14407
|
+
});
|
|
14408
|
+
var Button = (0, react$1.forwardRef)(({ className, variant, size, ...props }, ref) => {
|
|
14409
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
14410
|
+
ref,
|
|
14411
|
+
className: cn(buttonVariants({
|
|
14412
|
+
variant,
|
|
14413
|
+
size
|
|
14414
|
+
}), className),
|
|
14415
|
+
...props
|
|
14416
|
+
});
|
|
14417
|
+
});
|
|
14418
|
+
Button.displayName = "Button";
|
|
14419
|
+
//#endregion
|
|
14420
|
+
//#region src/composites/account-sign-in-field.tsx
|
|
14421
|
+
var INPUT_CLASS$1 = "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-xs text-foreground focus:border-primary focus:ring-1 focus:ring-primary/30 outline-none disabled:opacity-50 disabled:cursor-not-allowed";
|
|
14422
|
+
var BARE_CODE = /^[A-Za-z0-9._~-]{8,}$/;
|
|
14423
|
+
function isRecord$3(value) {
|
|
14424
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14425
|
+
}
|
|
14426
|
+
function isPresent(value) {
|
|
14427
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
14428
|
+
if ((0, _camstack_types.isSecretFieldView)(value) || (0, _camstack_types.isSecretMarker)(value)) return true;
|
|
14429
|
+
return false;
|
|
14430
|
+
}
|
|
14431
|
+
function accountText(value) {
|
|
14432
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : "";
|
|
14433
|
+
}
|
|
14434
|
+
function countFromValue(value) {
|
|
14435
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
14436
|
+
if (Array.isArray(value)) return value.length;
|
|
14437
|
+
if (typeof value === "string" && value.trim().startsWith("[")) try {
|
|
14438
|
+
const parsed = JSON.parse(value);
|
|
14439
|
+
if (Array.isArray(parsed)) return parsed.length;
|
|
14440
|
+
} catch {
|
|
14441
|
+
return null;
|
|
14442
|
+
}
|
|
14443
|
+
return null;
|
|
14444
|
+
}
|
|
14445
|
+
function nounFor(count, noun) {
|
|
14446
|
+
return count === 1 ? noun : `${noun}s`;
|
|
14447
|
+
}
|
|
14448
|
+
function choicesOf(value) {
|
|
14449
|
+
if (!Array.isArray(value)) return [];
|
|
14450
|
+
const out = [];
|
|
14451
|
+
for (const row of value) {
|
|
14452
|
+
if (!isRecord$3(row) || typeof row["value"] !== "string" || typeof row["label"] !== "string") continue;
|
|
14453
|
+
out.push({
|
|
14454
|
+
value: row["value"],
|
|
14455
|
+
label: row["label"],
|
|
14456
|
+
...typeof row["brand"] === "string" ? { brand: row["brand"] } : {},
|
|
14457
|
+
...typeof row["region"] === "string" ? { region: row["region"] } : {}
|
|
14458
|
+
});
|
|
14459
|
+
}
|
|
14460
|
+
return out;
|
|
14461
|
+
}
|
|
14462
|
+
function selectedChoice(value) {
|
|
14463
|
+
return typeof value === "string" ? value : "";
|
|
14464
|
+
}
|
|
14465
|
+
function shouldComplete(text) {
|
|
14466
|
+
const trimmed = text.trim();
|
|
14467
|
+
if (trimmed.length === 0) return false;
|
|
14468
|
+
if (trimmed.includes("code=") || trimmed.includes("://") || trimmed.includes("error=")) return true;
|
|
14469
|
+
return BARE_CODE.test(trimmed);
|
|
14470
|
+
}
|
|
14471
|
+
function AccountSignInField({ field, values, disabled, onAction, onChangeMany }) {
|
|
14472
|
+
const t = useUiText();
|
|
14473
|
+
const [phase, setPhase] = (0, react$1.useState)("idle");
|
|
14474
|
+
const [attemptId, setAttemptId] = (0, react$1.useState)(null);
|
|
14475
|
+
const [authorizeUrl, setAuthorizeUrl] = (0, react$1.useState)(null);
|
|
14476
|
+
const [paste, setPaste] = (0, react$1.useState)("");
|
|
14477
|
+
const [error, setError] = (0, react$1.useState)(null);
|
|
14478
|
+
const [working, setWorking] = (0, react$1.useState)(false);
|
|
14479
|
+
const [choices, setChoices] = (0, react$1.useState)([]);
|
|
14480
|
+
const inFlight = (0, react$1.useRef)(false);
|
|
14481
|
+
const account = accountText(values[field.accountKey]);
|
|
14482
|
+
const tokenPresent = field.secretKeys.some((key) => isPresent(values[key]));
|
|
14483
|
+
const signedIn = account.length > 0 || tokenPresent;
|
|
14484
|
+
const count = (field.countKey !== void 0 ? countFromValue(values[field.countKey]) : null) ?? (field.countFromKey !== void 0 ? countFromValue(values[field.countFromKey]) : null);
|
|
14485
|
+
const showPaste = phase === "pasting" || !signedIn && attemptId !== null;
|
|
14486
|
+
const applyResult = (result) => {
|
|
14487
|
+
const patch = {};
|
|
14488
|
+
for (const [formKey, resultKey] of Object.entries(field.assignResult ?? {})) {
|
|
14489
|
+
const assigned = result[resultKey];
|
|
14490
|
+
if (typeof assigned === "string" || typeof assigned === "number") patch[formKey] = assigned;
|
|
14491
|
+
}
|
|
14492
|
+
if (Object.keys(patch).length > 0) onChangeMany?.(patch);
|
|
14493
|
+
const nextChoices = choicesOf(result["choices"]);
|
|
14494
|
+
setChoices(nextChoices.length > 1 ? nextChoices : []);
|
|
14495
|
+
setPhase("idle");
|
|
14496
|
+
setPaste("");
|
|
14497
|
+
setAttemptId(null);
|
|
14498
|
+
setAuthorizeUrl(null);
|
|
14499
|
+
};
|
|
14500
|
+
const begin = async () => {
|
|
14501
|
+
if (!onAction || working) return;
|
|
14502
|
+
setWorking(true);
|
|
14503
|
+
setError(null);
|
|
14504
|
+
try {
|
|
14505
|
+
const result = await onAction(field.beginAction, field.key, {});
|
|
14506
|
+
if (!isRecord$3(result) || typeof result["authorizeUrl"] !== "string" || typeof result["attemptId"] !== "string") {
|
|
14507
|
+
setError(t("uiLibrary.accountSignIn.noLink", "The sign-in page could not be opened. Try again."));
|
|
14508
|
+
return;
|
|
14509
|
+
}
|
|
14510
|
+
if ("verifier" in result || "state" in result) {
|
|
14511
|
+
setError(t("uiLibrary.accountSignIn.noLink", "The sign-in page could not be opened. Try again."));
|
|
14512
|
+
return;
|
|
14513
|
+
}
|
|
14514
|
+
setAttemptId(result["attemptId"]);
|
|
14515
|
+
setAuthorizeUrl(result["authorizeUrl"]);
|
|
14516
|
+
setPhase("pasting");
|
|
14517
|
+
window.open(result["authorizeUrl"], "_blank", "noopener,noreferrer");
|
|
14518
|
+
} catch (err) {
|
|
14519
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
14520
|
+
} finally {
|
|
14521
|
+
setWorking(false);
|
|
14522
|
+
}
|
|
14523
|
+
};
|
|
14524
|
+
const complete = async (callback) => {
|
|
14525
|
+
if (!onAction || attemptId === null || inFlight.current) return;
|
|
14526
|
+
inFlight.current = true;
|
|
14527
|
+
setWorking(true);
|
|
14528
|
+
setError(null);
|
|
14529
|
+
try {
|
|
14530
|
+
const result = await onAction(field.completeAction, field.key, {
|
|
14531
|
+
attemptId,
|
|
14532
|
+
callback
|
|
14533
|
+
});
|
|
14534
|
+
if (!isRecord$3(result)) {
|
|
14535
|
+
setError(t("uiLibrary.accountSignIn.rejected", "The sign-in was not accepted. Start again."));
|
|
14536
|
+
return;
|
|
14537
|
+
}
|
|
14538
|
+
applyResult(result);
|
|
14539
|
+
} catch (err) {
|
|
14540
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
14541
|
+
} finally {
|
|
14542
|
+
inFlight.current = false;
|
|
14543
|
+
setWorking(false);
|
|
14544
|
+
}
|
|
14545
|
+
};
|
|
14546
|
+
const signOut = async () => {
|
|
14547
|
+
if (!onAction || working) return;
|
|
14548
|
+
setWorking(true);
|
|
14549
|
+
setError(null);
|
|
14550
|
+
try {
|
|
14551
|
+
await onAction(field.signOutAction, field.key, {});
|
|
14552
|
+
const patch = { [field.accountKey]: "" };
|
|
14553
|
+
if (field.countKey !== void 0) patch[field.countKey] = 0;
|
|
14554
|
+
if (field.choiceKey !== void 0) patch[field.choiceKey] = "";
|
|
14555
|
+
if (field.countFromKey !== void 0) patch[field.countFromKey] = [];
|
|
14556
|
+
for (const key of field.secretKeys) patch[key] = _camstack_types.SECRET_CLEAR;
|
|
14557
|
+
onChangeMany?.(patch);
|
|
14558
|
+
setChoices([]);
|
|
14559
|
+
setPhase("idle");
|
|
14560
|
+
setAttemptId(null);
|
|
14561
|
+
setPaste("");
|
|
14562
|
+
} catch (err) {
|
|
14563
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
14564
|
+
} finally {
|
|
14565
|
+
setWorking(false);
|
|
14566
|
+
}
|
|
14567
|
+
};
|
|
14568
|
+
const pick = (value) => {
|
|
14569
|
+
const chosen = choices.find((row) => row.value === value);
|
|
14570
|
+
if (chosen === void 0 || field.choiceKey === void 0) return;
|
|
14571
|
+
const patch = { [field.choiceKey]: chosen.value };
|
|
14572
|
+
if (chosen.brand !== void 0) patch["brand"] = chosen.brand;
|
|
14573
|
+
if (chosen.region !== void 0) patch["region"] = chosen.region;
|
|
14574
|
+
onChangeMany?.(patch);
|
|
14575
|
+
};
|
|
14576
|
+
const statusAccount = account.length > 0 ? account : t("uiLibrary.accountSignIn.yourAccount", "your account");
|
|
14577
|
+
const status = count === null ? t("uiLibrary.accountSignIn.signedIn", "Signed in as {{account}}", { account: statusAccount }) : t("uiLibrary.accountSignIn.signedInCount", "Signed in as {{account}} · {{detail}}", {
|
|
14578
|
+
account: statusAccount,
|
|
14579
|
+
detail: field.countNoun !== void 0 ? `${count} ${nounFor(count, field.countNoun)}` : String(count)
|
|
14580
|
+
});
|
|
14581
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14582
|
+
className: "col-span-1 flex min-w-0 flex-col gap-2",
|
|
14583
|
+
children: [
|
|
14584
|
+
signedIn && phase === "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14585
|
+
className: "flex flex-col gap-2",
|
|
14586
|
+
children: [
|
|
14587
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14588
|
+
className: "text-xs text-foreground",
|
|
14589
|
+
children: status
|
|
14590
|
+
}),
|
|
14591
|
+
choices.length > 1 && field.choiceKey !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Select, {
|
|
14592
|
+
"aria-label": field.choiceLabel ?? t("uiLibrary.accountSignIn.choose", "Choose"),
|
|
14593
|
+
options: choices.map((row) => ({
|
|
14594
|
+
value: row.value,
|
|
14595
|
+
label: row.label
|
|
14596
|
+
})),
|
|
14597
|
+
value: selectedChoice(values[field.choiceKey]),
|
|
14598
|
+
onChange: (event) => pick(event.target.value),
|
|
14599
|
+
disabled: disabled || working
|
|
14600
|
+
}),
|
|
14601
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14602
|
+
className: "flex flex-wrap gap-2",
|
|
14603
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
14604
|
+
type: "button",
|
|
14605
|
+
variant: "outline",
|
|
14606
|
+
size: "sm",
|
|
14607
|
+
disabled: disabled || working,
|
|
14608
|
+
onClick: () => void begin(),
|
|
14609
|
+
children: t("uiLibrary.accountSignIn.again", "Sign in again")
|
|
14610
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
14611
|
+
type: "button",
|
|
14612
|
+
variant: "ghost",
|
|
14613
|
+
size: "sm",
|
|
14614
|
+
disabled: disabled || working,
|
|
14615
|
+
onClick: () => void signOut(),
|
|
14616
|
+
children: t("uiLibrary.accountSignIn.signOut", "Sign out")
|
|
14617
|
+
})]
|
|
14618
|
+
})
|
|
14619
|
+
]
|
|
14620
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
14621
|
+
type: "button",
|
|
14622
|
+
variant: "primary",
|
|
14623
|
+
size: "sm",
|
|
14624
|
+
disabled: disabled || working,
|
|
14625
|
+
onClick: () => void begin(),
|
|
14626
|
+
children: working && phase !== "pasting" ? t("uiLibrary.accountSignIn.working", "Working…") : t("uiLibrary.accountSignIn.signInWith", "Sign in with {{provider}}", { provider: field.providerLabel })
|
|
14627
|
+
}),
|
|
14628
|
+
showPaste && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14629
|
+
className: "flex flex-col gap-1.5",
|
|
14630
|
+
children: [authorizeUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
14631
|
+
className: "text-[11px] text-primary break-all",
|
|
14632
|
+
href: authorizeUrl,
|
|
14633
|
+
target: "_blank",
|
|
14634
|
+
rel: "noopener noreferrer",
|
|
14635
|
+
children: t("uiLibrary.accountSignIn.open", "Open the sign-in page")
|
|
14636
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
14637
|
+
className: `${INPUT_CLASS$1} min-h-[60px] resize-y`,
|
|
14638
|
+
"aria-label": t("uiLibrary.accountSignIn.paste", "Redirect address or code"),
|
|
14639
|
+
placeholder: field.pastePlaceholder,
|
|
14640
|
+
value: paste,
|
|
14641
|
+
disabled: disabled || working,
|
|
14642
|
+
onChange: (event) => {
|
|
14643
|
+
const next = event.target.value;
|
|
14644
|
+
setPaste(next);
|
|
14645
|
+
if (shouldComplete(next)) complete(next);
|
|
14646
|
+
}
|
|
14647
|
+
})]
|
|
14648
|
+
}),
|
|
14649
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14650
|
+
className: "text-[11px] text-danger",
|
|
14651
|
+
children: error
|
|
14652
|
+
})
|
|
14653
|
+
]
|
|
14654
|
+
});
|
|
14655
|
+
}
|
|
14656
|
+
function SecretPresenceField({ field, values }) {
|
|
14657
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14658
|
+
className: "col-span-1 text-xs text-foreground",
|
|
14659
|
+
children: isPresent(values[field.sourceKey]) ? field.yesLabel : field.noLabel
|
|
14660
|
+
});
|
|
14661
|
+
}
|
|
14662
|
+
//#endregion
|
|
14385
14663
|
//#region src/composites/confirm-dialog.tsx
|
|
14386
14664
|
/**
|
|
14387
14665
|
* The ONE modal dialog of the product — confirm, alert and prompt.
|
|
@@ -20685,39 +20963,6 @@ var Skeleton = (0, react$1.forwardRef)(({ className, ...props }, ref) => {
|
|
|
20685
20963
|
});
|
|
20686
20964
|
Skeleton.displayName = "Skeleton";
|
|
20687
20965
|
//#endregion
|
|
20688
|
-
//#region src/primitives/button.tsx
|
|
20689
|
-
var buttonVariants = cva("inline-flex items-center justify-center rounded-md font-medium transition-colors cursor-pointer disabled:opacity-50 disabled:pointer-events-none", {
|
|
20690
|
-
variants: {
|
|
20691
|
-
variant: {
|
|
20692
|
-
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
20693
|
-
secondary: "bg-surface text-foreground hover:bg-surface-hover",
|
|
20694
|
-
ghost: "hover:bg-surface-hover text-foreground-muted",
|
|
20695
|
-
danger: "bg-danger text-white hover:bg-danger/90",
|
|
20696
|
-
outline: "border border-border bg-transparent text-foreground hover:bg-surface-hover"
|
|
20697
|
-
},
|
|
20698
|
-
size: {
|
|
20699
|
-
sm: "h-7 px-2.5 text-xs gap-1.5",
|
|
20700
|
-
md: "h-8 px-3 text-sm gap-2",
|
|
20701
|
-
lg: "h-9 px-4 text-sm gap-2"
|
|
20702
|
-
}
|
|
20703
|
-
},
|
|
20704
|
-
defaultVariants: {
|
|
20705
|
-
variant: "primary",
|
|
20706
|
-
size: "sm"
|
|
20707
|
-
}
|
|
20708
|
-
});
|
|
20709
|
-
var Button = (0, react$1.forwardRef)(({ className, variant, size, ...props }, ref) => {
|
|
20710
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
20711
|
-
ref,
|
|
20712
|
-
className: cn(buttonVariants({
|
|
20713
|
-
variant,
|
|
20714
|
-
size
|
|
20715
|
-
}), className),
|
|
20716
|
-
...props
|
|
20717
|
-
});
|
|
20718
|
-
});
|
|
20719
|
-
Button.displayName = "Button";
|
|
20720
|
-
//#endregion
|
|
20721
20966
|
//#region src/composites/cap-settings/consumable-rows.tsx
|
|
20722
20967
|
/** `value` null = unknown (D393) — renders an em-dash, never a fake `0`. */
|
|
20723
20968
|
function formatRemaining(t, r) {
|
|
@@ -29962,7 +30207,7 @@ function ProbeField({ field, value, onChange, disabled, translationFn, onTestFie
|
|
|
29962
30207
|
]
|
|
29963
30208
|
});
|
|
29964
30209
|
}
|
|
29965
|
-
function GroupField({ field, values, onChange, disabled, translationFn, onTestField }) {
|
|
30210
|
+
function GroupField({ field, values, onChange, onChangeMany, disabled, translationFn, onTestField, onAction }) {
|
|
29966
30211
|
const [collapsed, setCollapsed] = (0, react$1.useState)(field.defaultCollapsed ?? false);
|
|
29967
30212
|
const isAccordion = field.style === "accordion";
|
|
29968
30213
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -29986,9 +30231,11 @@ function GroupField({ field, values, onChange, disabled, translationFn, onTestFi
|
|
|
29986
30231
|
field: subField,
|
|
29987
30232
|
values,
|
|
29988
30233
|
onChange,
|
|
30234
|
+
onChangeMany,
|
|
29989
30235
|
disabled,
|
|
29990
30236
|
translationFn,
|
|
29991
|
-
onTestField
|
|
30237
|
+
onTestField,
|
|
30238
|
+
onAction
|
|
29992
30239
|
}, subField.key))
|
|
29993
30240
|
})]
|
|
29994
30241
|
})
|
|
@@ -30404,6 +30651,10 @@ function AddonActionSelectField({ field, value, values, onChange, disabled, onAc
|
|
|
30404
30651
|
}
|
|
30405
30652
|
}
|
|
30406
30653
|
setOptions(list);
|
|
30654
|
+
if (field.hideWhenFewerThan !== void 0 && list.length === 1 && (typeof value !== "string" || value.length === 0)) {
|
|
30655
|
+
const only = list[0]?.[valueKey];
|
|
30656
|
+
if (typeof only === "string" && only.length > 0) onChange(only);
|
|
30657
|
+
}
|
|
30407
30658
|
}).catch((err) => {
|
|
30408
30659
|
if (cancelled) return;
|
|
30409
30660
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -30423,6 +30674,8 @@ function AddonActionSelectField({ field, value, values, onChange, disabled, onAc
|
|
|
30423
30674
|
const valueKey = field.mapOption.value;
|
|
30424
30675
|
const labelKey = field.mapOption.label;
|
|
30425
30676
|
const descKey = field.mapOption.description;
|
|
30677
|
+
if (!onAction && field.hideWhenFewerThan !== void 0) return null;
|
|
30678
|
+
if (field.hideWhenFewerThan !== void 0 && options !== null && options.length < field.hideWhenFewerThan) return null;
|
|
30426
30679
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldWrapper, {
|
|
30427
30680
|
label: field.label,
|
|
30428
30681
|
description: field.description,
|
|
@@ -31055,9 +31308,11 @@ function FormField({ field, values, onChange, onChangeMany, disabled, translatio
|
|
|
31055
31308
|
field,
|
|
31056
31309
|
values,
|
|
31057
31310
|
onChange,
|
|
31311
|
+
onChangeMany,
|
|
31058
31312
|
disabled,
|
|
31059
31313
|
translationFn,
|
|
31060
|
-
onTestField
|
|
31314
|
+
onTestField,
|
|
31315
|
+
onAction
|
|
31061
31316
|
});
|
|
31062
31317
|
case "sub-tabs": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubTabsField, {
|
|
31063
31318
|
field,
|
|
@@ -31086,22 +31341,36 @@ function FormField({ field, values, onChange, onChangeMany, disabled, translatio
|
|
|
31086
31341
|
disabled,
|
|
31087
31342
|
onAction
|
|
31088
31343
|
});
|
|
31089
|
-
case "addon-action-select":
|
|
31344
|
+
case "addon-action-select": {
|
|
31345
|
+
const storedKey = field.writesKey ?? field.key;
|
|
31346
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AddonActionSelectField, {
|
|
31347
|
+
field,
|
|
31348
|
+
value: values[storedKey],
|
|
31349
|
+
values,
|
|
31350
|
+
onChange: (next) => onChange(storedKey, next),
|
|
31351
|
+
disabled,
|
|
31352
|
+
onAction
|
|
31353
|
+
});
|
|
31354
|
+
}
|
|
31355
|
+
case "addon-action-button": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AddonActionButtonField, {
|
|
31090
31356
|
field,
|
|
31091
|
-
value,
|
|
31092
31357
|
values,
|
|
31093
|
-
onChange: handleChange,
|
|
31094
31358
|
disabled,
|
|
31095
|
-
onAction
|
|
31359
|
+
onAction,
|
|
31360
|
+
onChange,
|
|
31361
|
+
onChangeMany
|
|
31096
31362
|
});
|
|
31097
|
-
case "
|
|
31363
|
+
case "account-sign-in": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountSignInField, {
|
|
31098
31364
|
field,
|
|
31099
31365
|
values,
|
|
31100
31366
|
disabled,
|
|
31101
31367
|
onAction,
|
|
31102
|
-
onChange,
|
|
31103
31368
|
onChangeMany
|
|
31104
31369
|
});
|
|
31370
|
+
case "secret-presence": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretPresenceField, {
|
|
31371
|
+
field,
|
|
31372
|
+
values
|
|
31373
|
+
});
|
|
31105
31374
|
case "device-action-button": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceActionButtonField, {
|
|
31106
31375
|
field,
|
|
31107
31376
|
values,
|
|
@@ -33644,6 +33913,99 @@ function BatteryBadge({ status, variant = "compact", className }) {
|
|
|
33644
33913
|
});
|
|
33645
33914
|
}
|
|
33646
33915
|
//#endregion
|
|
33916
|
+
//#region src/composites/running-status.ts
|
|
33917
|
+
function trimmedPhase(phase) {
|
|
33918
|
+
if (phase === null) return void 0;
|
|
33919
|
+
const trimmed = phase.trim();
|
|
33920
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
33921
|
+
}
|
|
33922
|
+
/** Minutes elapsed since `since`, at least 1; null when `since` is not usable. */
|
|
33923
|
+
function runningDurationSince(since, now) {
|
|
33924
|
+
if (since === null || !Number.isFinite(since) || since <= 0) return null;
|
|
33925
|
+
const ms = now - since;
|
|
33926
|
+
if (ms <= 0) return null;
|
|
33927
|
+
const totalMinutes = Math.floor(ms / 6e4);
|
|
33928
|
+
if (totalMinutes < 60) return { minutes: Math.max(1, totalMinutes) };
|
|
33929
|
+
return { hours: Math.floor(totalMinutes / 60) };
|
|
33930
|
+
}
|
|
33931
|
+
function runningStatusParts(running, since, phase, now) {
|
|
33932
|
+
const phaseLabel = trimmedPhase(phase);
|
|
33933
|
+
if (running === null) return { key: "unknown" };
|
|
33934
|
+
if (running === false) return phaseLabel === void 0 ? { key: "idle" } : {
|
|
33935
|
+
key: "idle",
|
|
33936
|
+
phase: phaseLabel
|
|
33937
|
+
};
|
|
33938
|
+
const duration = runningDurationSince(since, now);
|
|
33939
|
+
if (duration === null) return phaseLabel === void 0 ? { key: "running" } : {
|
|
33940
|
+
key: "running",
|
|
33941
|
+
phase: phaseLabel
|
|
33942
|
+
};
|
|
33943
|
+
return {
|
|
33944
|
+
key: "running",
|
|
33945
|
+
duration,
|
|
33946
|
+
...phaseLabel === void 0 ? {} : { phase: phaseLabel }
|
|
33947
|
+
};
|
|
33948
|
+
}
|
|
33949
|
+
//#endregion
|
|
33950
|
+
//#region src/composites/running-badge.tsx
|
|
33951
|
+
function RunningBadge({ running, children, className }) {
|
|
33952
|
+
const t = useUiText();
|
|
33953
|
+
if (running === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33954
|
+
"data-running": "unknown",
|
|
33955
|
+
className: cn("inline-flex shrink-0", className),
|
|
33956
|
+
children
|
|
33957
|
+
});
|
|
33958
|
+
if (running === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33959
|
+
"data-running": "running",
|
|
33960
|
+
className: cn("relative inline-flex shrink-0 rounded-full p-0.5", className),
|
|
33961
|
+
"aria-label": t("uiLibrary.runningBadge.running", "Running"),
|
|
33962
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33963
|
+
className: "pointer-events-none absolute inset-0 rounded-full border border-primary animate-pulse",
|
|
33964
|
+
"aria-hidden": true
|
|
33965
|
+
}), children]
|
|
33966
|
+
});
|
|
33967
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33968
|
+
"data-running": "idle",
|
|
33969
|
+
className: cn("relative inline-flex shrink-0", className),
|
|
33970
|
+
"aria-label": t("uiLibrary.runningBadge.idle", "Idle"),
|
|
33971
|
+
children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33972
|
+
className: "pointer-events-none absolute -bottom-px -right-px h-1.5 w-1.5 rounded-full bg-foreground-subtle",
|
|
33973
|
+
"aria-hidden": true
|
|
33974
|
+
})]
|
|
33975
|
+
});
|
|
33976
|
+
}
|
|
33977
|
+
function RunningStatusLine({ running, since, phase, className, now = Date.now() }) {
|
|
33978
|
+
const t = useUiText();
|
|
33979
|
+
const parts = runningStatusParts(running, since, phase, now);
|
|
33980
|
+
if (parts.key === "unknown") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33981
|
+
className: cn("text-[11px] text-foreground-subtle", className),
|
|
33982
|
+
children: t("uiLibrary.runningStatus.unknown", "Unknown")
|
|
33983
|
+
});
|
|
33984
|
+
if (parts.key === "idle") {
|
|
33985
|
+
const base = t("uiLibrary.runningStatus.idle", "Idle");
|
|
33986
|
+
const text = parts.phase === void 0 ? base : t("uiLibrary.runningStatus.withPhase", "{{state}} · {{phase}}", {
|
|
33987
|
+
state: base,
|
|
33988
|
+
phase: parts.phase
|
|
33989
|
+
});
|
|
33990
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33991
|
+
className: cn("text-[11px] text-foreground-muted", className),
|
|
33992
|
+
children: text
|
|
33993
|
+
});
|
|
33994
|
+
}
|
|
33995
|
+
let base;
|
|
33996
|
+
if (parts.duration === void 0) base = t("uiLibrary.runningStatus.running", "Running");
|
|
33997
|
+
else if ("minutes" in parts.duration) base = t("uiLibrary.runningStatus.runningForMinutes", "Running for {{count}} min", { count: parts.duration.minutes });
|
|
33998
|
+
else base = t("uiLibrary.runningStatus.runningForHours", "Running for {{count}} h", { count: parts.duration.hours });
|
|
33999
|
+
const text = parts.phase === void 0 ? base : t("uiLibrary.runningStatus.withPhase", "{{state}} · {{phase}}", {
|
|
34000
|
+
state: base,
|
|
34001
|
+
phase: parts.phase
|
|
34002
|
+
});
|
|
34003
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
34004
|
+
className: cn("text-[11px] text-foreground-muted", className),
|
|
34005
|
+
children: text
|
|
34006
|
+
});
|
|
34007
|
+
}
|
|
34008
|
+
//#endregion
|
|
33647
34009
|
//#region src/composites/network-link-badge.tsx
|
|
33648
34010
|
/**
|
|
33649
34011
|
* NetworkLinkBadge — compact link indicator for device rows and headers.
|
|
@@ -50584,6 +50946,18 @@ function dishwasherHeadline(status, t = defaultUiText) {
|
|
|
50584
50946
|
function featureLabel(key) {
|
|
50585
50947
|
return (key.split(".").pop() ?? key).replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
50586
50948
|
}
|
|
50949
|
+
/**
|
|
50950
|
+
* Salt and rinse aid are consumable items (D915). Their events must not
|
|
50951
|
+
* reappear on the hero — the Consumables section is the only surface.
|
|
50952
|
+
*/
|
|
50953
|
+
function isConsumableDishwasherEvent(event) {
|
|
50954
|
+
const short = (event.split(".").pop() ?? event).toLowerCase();
|
|
50955
|
+
return short.includes("salt") || short.includes("rinse");
|
|
50956
|
+
}
|
|
50957
|
+
/** Raised events the hero may name. Consumable events are dropped. */
|
|
50958
|
+
function visibleDishwasherEvents(events) {
|
|
50959
|
+
return events.filter((event) => !isConsumableDishwasherEvent(event));
|
|
50960
|
+
}
|
|
50587
50961
|
/** Whole seconds as a short duration: `1 h 24 min`, `42 min`, `30 s`. */
|
|
50588
50962
|
function formatDuration(seconds, t = defaultUiText) {
|
|
50589
50963
|
if (seconds < 60) return t("uiLibrary.dishwasher.durationSeconds", "{{n}} s", { n: seconds });
|
|
@@ -50740,7 +51114,7 @@ function DishwasherPanel({ trpc, deviceId, layout }) {
|
|
|
50740
51114
|
children: [display.doorOpen === true && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
50741
51115
|
className: "inline-flex items-center gap-1 text-xs text-warning",
|
|
50742
51116
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DoorOpen, { className: "h-3.5 w-3.5" }), t("uiLibrary.dishwasher.doorOpen", "Door open")]
|
|
50743
|
-
}), display.activeEvents.map((event) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
51117
|
+
}), visibleDishwasherEvents(display.activeEvents).map((event) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
50744
51118
|
className: "text-xs text-warning",
|
|
50745
51119
|
title: event,
|
|
50746
51120
|
children: featureLabel(event)
|
|
@@ -55230,6 +55604,8 @@ function ChildRow$1({ trpc, child, onNavigate, parentName }) {
|
|
|
55230
55604
|
const showMotionTrigger = enabled && hasMotionTriggerFeature(child);
|
|
55231
55605
|
const roleDefaults = useRoleDisplayDefaults();
|
|
55232
55606
|
useIconGlyphsVersion();
|
|
55607
|
+
const ChildIcon = deviceLeadIcon(child, roleDefaults);
|
|
55608
|
+
const runningSlice = useDeviceState(trpc, child.id, (s) => s.runningState);
|
|
55233
55609
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55234
55610
|
onClick: (e) => {
|
|
55235
55611
|
e.stopPropagation();
|
|
@@ -55237,7 +55613,10 @@ function ChildRow$1({ trpc, child, onNavigate, parentName }) {
|
|
|
55237
55613
|
},
|
|
55238
55614
|
className: "w-full flex items-center gap-2 px-3 py-1.5 text-left hover:bg-surface-hover transition-colors cursor-pointer",
|
|
55239
55615
|
children: [
|
|
55240
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
55616
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RunningBadge, {
|
|
55617
|
+
running: runningSlice === void 0 || runningSlice.running === null ? null : runningSlice.running,
|
|
55618
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChildIcon, { className: "h-3 w-3 text-foreground-subtle flex-shrink-0" })
|
|
55619
|
+
}),
|
|
55241
55620
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55242
55621
|
className: "text-[10.5px] text-foreground flex-1 truncate",
|
|
55243
55622
|
children: childListName(child, parentName)
|
|
@@ -55631,13 +56010,16 @@ var CHEVRON_SLOT_CLASS = {
|
|
|
55631
56010
|
horizontal: "h-3.5 w-3.5",
|
|
55632
56011
|
stacked: "h-4 w-4 mt-0.5"
|
|
55633
56012
|
};
|
|
55634
|
-
function DeviceItemHeader({ device, variant, flags, parent, integrationIcon, hasChildren, expanded, onToggleExpand, onNavigateToParent, stacked = false, nameOverflow = "truncate" }) {
|
|
56013
|
+
function DeviceItemHeader({ trpc, device, variant, flags, parent, integrationIcon, hasChildren, expanded, onToggleExpand, onNavigateToParent, stacked = false, nameOverflow = "truncate" }) {
|
|
55635
56014
|
const t = useUiText();
|
|
55636
56015
|
const containerChildren = useContainerChildren(device.id);
|
|
55637
56016
|
const roleDefaults = useRoleDisplayDefaults();
|
|
55638
56017
|
useIconGlyphsVersion();
|
|
55639
56018
|
const Icon = deviceLeadIcon(effectiveIconDevice(device, containerChildren), roleDefaults);
|
|
56019
|
+
const runningSlice = useDeviceState(trpc, device.id, (s) => s.runningState);
|
|
56020
|
+
const runningBadge = runningSlice === void 0 || runningSlice.running === null ? null : runningSlice.running;
|
|
55640
56021
|
const slotClass = stacked ? CHEVRON_SLOT_CLASS.stacked : CHEVRON_SLOT_CLASS.horizontal;
|
|
56022
|
+
const iconClass = cn("text-foreground-subtle flex-shrink-0", stacked ? "h-4 w-4 mt-0.5" : variant === "minimal" ? "h-3 w-3" : "h-3.5 w-3.5");
|
|
55641
56023
|
const lineClass = nameOverflow === "scroll" ? NAME_TEXT_CLASS : "truncate";
|
|
55642
56024
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55643
56025
|
className: cn(stacked ? "flex items-start gap-2 w-full min-w-0" : "flex items-center gap-2 flex-1 min-w-0"),
|
|
@@ -55657,7 +56039,10 @@ function DeviceItemHeader({ device, variant, flags, parent, integrationIcon, has
|
|
|
55657
56039
|
"data-testid": "chevron-slot",
|
|
55658
56040
|
className: cn("flex-shrink-0", slotClass)
|
|
55659
56041
|
}) : null,
|
|
55660
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
56042
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RunningBadge, {
|
|
56043
|
+
running: runningBadge,
|
|
56044
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: iconClass })
|
|
56045
|
+
}),
|
|
55661
56046
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55662
56047
|
className: "flex-1 min-w-0",
|
|
55663
56048
|
children: [
|
|
@@ -56151,6 +56536,7 @@ function DeviceItem(props) {
|
|
|
56151
56536
|
className: "flex-shrink-0"
|
|
56152
56537
|
}),
|
|
56153
56538
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceItemHeader, {
|
|
56539
|
+
trpc,
|
|
56154
56540
|
device,
|
|
56155
56541
|
variant,
|
|
56156
56542
|
flags,
|
|
@@ -56227,6 +56613,7 @@ function DeviceItem(props) {
|
|
|
56227
56613
|
className: "flex-shrink-0 mt-0.5"
|
|
56228
56614
|
}),
|
|
56229
56615
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceItemHeader, {
|
|
56616
|
+
trpc,
|
|
56230
56617
|
device,
|
|
56231
56618
|
variant,
|
|
56232
56619
|
flags,
|
|
@@ -56318,6 +56705,7 @@ function DeviceItemTableRow(props) {
|
|
|
56318
56705
|
"aria-label": t("uiLibrary.deviceItem.select", "Select {{name}}", { name: device.name }),
|
|
56319
56706
|
className: "flex-shrink-0"
|
|
56320
56707
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceItemHeader, {
|
|
56708
|
+
trpc,
|
|
56321
56709
|
device,
|
|
56322
56710
|
variant,
|
|
56323
56711
|
flags,
|
|
@@ -64939,7 +65327,7 @@ function noItemsSentence(noun, emptyMeansAll, t) {
|
|
|
64939
65327
|
noun: pickerNounLabel(noun, t)
|
|
64940
65328
|
});
|
|
64941
65329
|
}
|
|
64942
|
-
function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, emptyMeansAll = false, savedHadDevices = false, noun = "camera", groupTitle: groupTitleProp, emptyLabel, annotate, disabled = false, testId, showAll = false, lockedIds, lockedNote: lockedNoteProp }) {
|
|
65330
|
+
function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, selection = "multiple", groupBy: pinnedGroupBy, emptyMeansAll = false, savedHadDevices = false, noun = "camera", groupTitle: groupTitleProp, emptyLabel, annotate, disabled = false, testId, showAll = false, lockedIds, lockedNote: lockedNoteProp }) {
|
|
64943
65331
|
const t = useUiText();
|
|
64944
65332
|
const groupTitle = groupTitleProp ?? ((group) => deviceGroupTitle(group, t));
|
|
64945
65333
|
const lockedNote = lockedNoteProp ?? t("uiLibrary.devicePicker.alreadyAdded", "Already added");
|
|
@@ -64972,7 +65360,7 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
64972
65360
|
children: [
|
|
64973
65361
|
listState === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevicesLoadingLine, { noun: plural }),
|
|
64974
65362
|
listState === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevicesFailedLine, { noun: noun === "camera" ? "devices" : plural }),
|
|
64975
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
65363
|
+
!(selection === "single" && chips.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
64976
65364
|
className: "flex flex-wrap items-center gap-1",
|
|
64977
65365
|
children: [
|
|
64978
65366
|
chips.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -65016,7 +65404,9 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65016
65404
|
groupTitle,
|
|
65017
65405
|
noun,
|
|
65018
65406
|
disabled,
|
|
65019
|
-
showAll
|
|
65407
|
+
showAll,
|
|
65408
|
+
selection,
|
|
65409
|
+
...pinnedGroupBy !== void 0 ? { pinnedGroupBy } : {}
|
|
65020
65410
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerPanel, {
|
|
65021
65411
|
devices,
|
|
65022
65412
|
value,
|
|
@@ -65027,7 +65417,8 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65027
65417
|
groupTitle,
|
|
65028
65418
|
noun,
|
|
65029
65419
|
disabled,
|
|
65030
|
-
showAll
|
|
65420
|
+
showAll,
|
|
65421
|
+
selection
|
|
65031
65422
|
})),
|
|
65032
65423
|
widening && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevicesWidenWarning, { noun })
|
|
65033
65424
|
]
|
|
@@ -65038,13 +65429,23 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65038
65429
|
* read below happens only then (for Zone grouping and the Zone filter).
|
|
65039
65430
|
*/
|
|
65040
65431
|
function GroupedPickerPanel(props) {
|
|
65041
|
-
const
|
|
65432
|
+
const stored = useStoredGroupBy();
|
|
65433
|
+
const [localGroupBy, setLocalGroupBy] = (0, react$1.useState)(null);
|
|
65434
|
+
const pinned = props.pinnedGroupBy;
|
|
65435
|
+
const groupBy = pinned !== void 0 ? localGroupBy ?? pinned : stored.groupBy;
|
|
65436
|
+
const onGroupByChange = (next) => {
|
|
65437
|
+
if (pinned !== void 0) {
|
|
65438
|
+
setLocalGroupBy(next);
|
|
65439
|
+
return;
|
|
65440
|
+
}
|
|
65441
|
+
stored.setGroupBy(next);
|
|
65442
|
+
};
|
|
65042
65443
|
const zones = usePickerSiteZones(true);
|
|
65043
65444
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerPanel, {
|
|
65044
65445
|
...props,
|
|
65045
65446
|
grouping: {
|
|
65046
65447
|
groupBy,
|
|
65047
|
-
onGroupByChange
|
|
65448
|
+
onGroupByChange,
|
|
65048
65449
|
zones
|
|
65049
65450
|
}
|
|
65050
65451
|
});
|
|
@@ -65062,8 +65463,9 @@ var NO_ZONES = {
|
|
|
65062
65463
|
zones: []
|
|
65063
65464
|
};
|
|
65064
65465
|
/** The searchable, grouped list — rendered one page at a time. */
|
|
65065
|
-
function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, disabled, locked, lockedNote, showAll, grouping }) {
|
|
65466
|
+
function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, disabled, locked, lockedNote, showAll, selection, grouping }) {
|
|
65066
65467
|
const t = useUiText();
|
|
65468
|
+
const radioName = (0, react$1.useId)();
|
|
65067
65469
|
const [filters, setFilters] = (0, react$1.useState)(PICKER_NO_FILTERS);
|
|
65068
65470
|
const [toggles, setToggles] = (0, react$1.useState)(/* @__PURE__ */ new Map());
|
|
65069
65471
|
const [shown, setShown] = (0, react$1.useState)(60);
|
|
@@ -65137,7 +65539,10 @@ function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, dis
|
|
|
65137
65539
|
}),
|
|
65138
65540
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
65139
65541
|
className: "mt-1 text-[10px] text-foreground-subtle",
|
|
65140
|
-
children: t("uiLibrary.devicePicker.
|
|
65542
|
+
children: selection === "single" ? t("uiLibrary.devicePicker.shownOfSingle", "{{shown}} of {{total}} shown", {
|
|
65543
|
+
shown: matches.length,
|
|
65544
|
+
total: devices.length
|
|
65545
|
+
}) : t("uiLibrary.devicePicker.shownOf", "{{shown}} of {{total}} shown · {{selected}} selected", {
|
|
65141
65546
|
shown: matches.length,
|
|
65142
65547
|
total: devices.length,
|
|
65143
65548
|
selected: value.length
|
|
@@ -65159,7 +65564,9 @@ function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, dis
|
|
|
65159
65564
|
groupBy: shownBy,
|
|
65160
65565
|
disabled,
|
|
65161
65566
|
locked,
|
|
65162
|
-
lockedNote
|
|
65567
|
+
lockedNote,
|
|
65568
|
+
selection,
|
|
65569
|
+
radioName
|
|
65163
65570
|
}, row.key)), page.remaining > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65164
65571
|
type: "button",
|
|
65165
65572
|
className: "mt-1 w-full rounded border border-border py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover",
|
|
@@ -65189,8 +65596,9 @@ function secondLine(device, groupBy) {
|
|
|
65189
65596
|
].filter((part) => part !== null);
|
|
65190
65597
|
return parts.length > 0 ? parts.join(" · ") : void 0;
|
|
65191
65598
|
}
|
|
65192
|
-
function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTitle, groupBy, disabled, locked, lockedNote }) {
|
|
65599
|
+
function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTitle, groupBy, disabled, locked, lockedNote, selection, radioName }) {
|
|
65193
65600
|
const t = useUiText();
|
|
65601
|
+
const single = selection === "single";
|
|
65194
65602
|
if (row.kind === "header") {
|
|
65195
65603
|
const { group, expanded } = row;
|
|
65196
65604
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -65208,7 +65616,7 @@ function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTit
|
|
|
65208
65616
|
className: "truncate",
|
|
65209
65617
|
children: groupTitle(group)
|
|
65210
65618
|
}),
|
|
65211
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
65619
|
+
!single && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
65212
65620
|
className: "font-normal normal-case",
|
|
65213
65621
|
children: [
|
|
65214
65622
|
"(",
|
|
@@ -65220,14 +65628,14 @@ function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTit
|
|
|
65220
65628
|
})
|
|
65221
65629
|
]
|
|
65222
65630
|
}),
|
|
65223
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65631
|
+
!single && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65224
65632
|
type: "button",
|
|
65225
65633
|
disabled,
|
|
65226
65634
|
className: "text-[10px] text-primary hover:underline disabled:opacity-50",
|
|
65227
65635
|
onClick: () => onChange(selectPickerGroup(value, group)),
|
|
65228
65636
|
children: t("uiLibrary.devicePicker.all", "All")
|
|
65229
65637
|
}),
|
|
65230
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65638
|
+
!single && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65231
65639
|
type: "button",
|
|
65232
65640
|
disabled,
|
|
65233
65641
|
className: "text-[10px] text-foreground-subtle hover:underline disabled:opacity-50",
|
|
@@ -65244,10 +65652,11 @@ function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTit
|
|
|
65244
65652
|
className: "flex cursor-pointer items-center gap-2.5 px-3 py-1 text-xs text-foreground hover:bg-surface-hover",
|
|
65245
65653
|
"data-testid": "device-picker-row",
|
|
65246
65654
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
65247
|
-
type: "checkbox",
|
|
65655
|
+
type: single ? "radio" : "checkbox",
|
|
65656
|
+
...single ? { name: radioName } : {},
|
|
65248
65657
|
checked: row.selected || isLocked,
|
|
65249
65658
|
disabled: disabled || isLocked,
|
|
65250
|
-
onChange: () => onChange(togglePickerDevice(value, row.device.id)),
|
|
65659
|
+
onChange: () => onChange(single ? [row.device.id] : togglePickerDevice(value, row.device.id)),
|
|
65251
65660
|
className: "accent-primary",
|
|
65252
65661
|
"data-device-id": String(row.device.id)
|
|
65253
65662
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -68488,6 +68897,8 @@ exports.RotateCcw = RotateCcw;
|
|
|
68488
68897
|
exports.RotateCw = RotateCw;
|
|
68489
68898
|
exports.Route = Route;
|
|
68490
68899
|
exports.RunReport = RunReport;
|
|
68900
|
+
exports.RunningBadge = RunningBadge;
|
|
68901
|
+
exports.RunningStatusLine = RunningStatusLine;
|
|
68491
68902
|
exports.SCENE_CAPTURE_VARIANTS = SCENE_CAPTURE_VARIANTS;
|
|
68492
68903
|
exports.SCENE_CAPTURE_VARIANTS$1 = SCENE_CAPTURE_VARIANTS;
|
|
68493
68904
|
exports.SECRET_MASK_PLACEHOLDER = SECRET_MASK_PLACEHOLDER;
|
|
@@ -68929,6 +69340,8 @@ exports.roiTooLarge = roiTooLarge;
|
|
|
68929
69340
|
exports.roiTooLarge$1 = roiTooLarge;
|
|
68930
69341
|
exports.rootSourceState = rootSourceState;
|
|
68931
69342
|
exports.routerBasename = routerBasename;
|
|
69343
|
+
exports.runningDurationSince = runningDurationSince;
|
|
69344
|
+
exports.runningStatusParts = runningStatusParts;
|
|
68932
69345
|
exports.sameLocationSuggestions = sameLocationSuggestions;
|
|
68933
69346
|
exports.scrubReducer = scrubReducer;
|
|
68934
69347
|
exports.searchSteps = searchSteps;
|
|
@@ -70023,6 +70436,7 @@ exports.useRecordingOnboardSetSettings = useRecordingOnboardSetSettings;
|
|
|
70023
70436
|
exports.useRecordingSignalGetStatus = useRecordingSignalGetStatus;
|
|
70024
70437
|
exports.useRemoteComponent = useRemoteComponent;
|
|
70025
70438
|
exports.useRoleDisplayDefaults = useRoleDisplayDefaults;
|
|
70439
|
+
exports.useRunningStateGetStatus = useRunningStateGetStatus;
|
|
70026
70440
|
exports.useSceneMonitorCaptureReference = useSceneMonitorCaptureReference;
|
|
70027
70441
|
exports.useSceneMonitorCreateScene = useSceneMonitorCreateScene;
|
|
70028
70442
|
exports.useSceneMonitorDeleteReference = useSceneMonitorDeleteReference;
|