@camstack/ui-library 1.2.226 → 1.2.228
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-item/quick-detail-action.d.ts +6 -0
- package/dist/composites/device-list/generic-mode.d.ts +5 -0
- package/dist/composites/device-picker/SelectedChip.d.ts +3 -1
- package/dist/composites/index.d.ts +4 -0
- package/dist/composites/notification-targets-field.d.ts +66 -0
- package/dist/composites/notification-targets-options.d.ts +11 -0
- package/dist/composites/option-select/OptionSelect.d.ts +30 -0
- package/dist/composites/option-select/index.d.ts +4 -0
- package/dist/composites/option-select/option-select-model.d.ts +56 -0
- package/dist/contexts/device-quick-detail.d.ts +9 -0
- package/dist/generated/system-hooks.d.ts +22 -2
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/use-url-query.d.ts +5 -0
- package/dist/index.cjs +1150 -311
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1123 -311
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/button.d.ts +1 -1
- package/dist/primitives/icon-button.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6762,8 +6762,10 @@ var useButtonPress = trpc.button.press.useMutation;
|
|
|
6762
6762
|
var useCalendarsListCalendars = trpc.calendars.listCalendars.useQuery;
|
|
6763
6763
|
/** Generated alias around `trpc.calendars.listEvents.useQuery`. */
|
|
6764
6764
|
var useCalendarsListEvents = trpc.calendars.listEvents.useQuery;
|
|
6765
|
-
/** Generated alias around `trpc.calendars.
|
|
6766
|
-
var
|
|
6765
|
+
/** Generated alias around `trpc.calendars.getPreferences.useQuery`. */
|
|
6766
|
+
var useCalendarsGetPreferences = trpc.calendars.getPreferences.useQuery;
|
|
6767
|
+
/** Generated alias around `trpc.calendars.setCalendarPreference.useMutation`. */
|
|
6768
|
+
var useCalendarsSetCalendarPreference = trpc.calendars.setCalendarPreference.useMutation;
|
|
6767
6769
|
/** Generated alias around `trpc.cameraCredentials.getCredentials.useQuery`. */
|
|
6768
6770
|
var useCameraCredentialsGetCredentials = trpc.cameraCredentials.getCredentials.useQuery;
|
|
6769
6771
|
/** Generated alias around `trpc.cameraCredentials.getStatus.useQuery`. */
|
|
@@ -6850,6 +6852,10 @@ var useCoreBlocksSetEnabled = trpc.coreBlocks.setEnabled.useMutation;
|
|
|
6850
6852
|
var useCoreBlocksRestart = trpc.coreBlocks.restart.useMutation;
|
|
6851
6853
|
/** Generated alias around `trpc.coreBlocks.compile.useMutation`. */
|
|
6852
6854
|
var useCoreBlocksCompile = trpc.coreBlocks.compile.useMutation;
|
|
6855
|
+
/** Generated alias around `trpc.coreBlocks.getBlockSettings.useQuery`. */
|
|
6856
|
+
var useCoreBlocksGetBlockSettings = trpc.coreBlocks.getBlockSettings.useQuery;
|
|
6857
|
+
/** Generated alias around `trpc.coreBlocks.updateBlockSettings.useMutation`. */
|
|
6858
|
+
var useCoreBlocksUpdateBlockSettings = trpc.coreBlocks.updateBlockSettings.useMutation;
|
|
6853
6859
|
/** Generated alias around `trpc.coreBlocks.getTypeDefs.useQuery`. */
|
|
6854
6860
|
var useCoreBlocksGetTypeDefs = trpc.coreBlocks.getTypeDefs.useQuery;
|
|
6855
6861
|
/** Generated alias around `trpc.coreBlocks.createComposition.useMutation`. */
|
|
@@ -7590,8 +7596,12 @@ var useNodesExecuteQuery = trpc.nodes.executeQuery.useMutation;
|
|
|
7590
7596
|
var useNotificationOutputListTargetKinds = trpc.notificationOutput.listTargetKinds.useQuery;
|
|
7591
7597
|
/** Generated alias around `trpc.notificationOutput.listTargets.useQuery`. */
|
|
7592
7598
|
var useNotificationOutputListTargets = trpc.notificationOutput.listTargets.useQuery;
|
|
7599
|
+
/** Generated alias around `trpc.notificationOutput.getTarget.useQuery`. */
|
|
7600
|
+
var useNotificationOutputGetTarget = trpc.notificationOutput.getTarget.useQuery;
|
|
7593
7601
|
/** Generated alias around `trpc.notificationOutput.discoverTargets.useQuery`. */
|
|
7594
7602
|
var useNotificationOutputDiscoverTargets = trpc.notificationOutput.discoverTargets.useQuery;
|
|
7603
|
+
/** Generated alias around `trpc.notificationOutput.probeTargetConfig.useMutation`. */
|
|
7604
|
+
var useNotificationOutputProbeTargetConfig = trpc.notificationOutput.probeTargetConfig.useMutation;
|
|
7595
7605
|
/** Generated alias around `trpc.notificationOutput.send.useMutation`. */
|
|
7596
7606
|
var useNotificationOutputSend = trpc.notificationOutput.send.useMutation;
|
|
7597
7607
|
/** Generated alias around `trpc.notificationOutput.testTarget.useMutation`. */
|
|
@@ -7614,6 +7624,16 @@ var useNotificationRulesUpdateRule = trpc.notificationRules.updateRule.useMutati
|
|
|
7614
7624
|
var useNotificationRulesDeleteRule = trpc.notificationRules.deleteRule.useMutation;
|
|
7615
7625
|
/** Generated alias around `trpc.notificationRules.setRuleEnabled.useMutation`. */
|
|
7616
7626
|
var useNotificationRulesSetRuleEnabled = trpc.notificationRules.setRuleEnabled.useMutation;
|
|
7627
|
+
/** Generated alias around `trpc.notificationRules.listSequences.useQuery`. */
|
|
7628
|
+
var useNotificationRulesListSequences = trpc.notificationRules.listSequences.useQuery;
|
|
7629
|
+
/** Generated alias around `trpc.notificationRules.getSequence.useQuery`. */
|
|
7630
|
+
var useNotificationRulesGetSequence = trpc.notificationRules.getSequence.useQuery;
|
|
7631
|
+
/** Generated alias around `trpc.notificationRules.createSequence.useMutation`. */
|
|
7632
|
+
var useNotificationRulesCreateSequence = trpc.notificationRules.createSequence.useMutation;
|
|
7633
|
+
/** Generated alias around `trpc.notificationRules.updateSequence.useMutation`. */
|
|
7634
|
+
var useNotificationRulesUpdateSequence = trpc.notificationRules.updateSequence.useMutation;
|
|
7635
|
+
/** Generated alias around `trpc.notificationRules.deleteSequence.useMutation`. */
|
|
7636
|
+
var useNotificationRulesDeleteSequence = trpc.notificationRules.deleteSequence.useMutation;
|
|
7617
7637
|
/** Generated alias around `trpc.notificationRules.getClipRetentionAsks.useQuery`. */
|
|
7618
7638
|
var useNotificationRulesGetClipRetentionAsks = trpc.notificationRules.getClipRetentionAsks.useQuery;
|
|
7619
7639
|
/** Generated alias around `trpc.notificationRules.listDeviceMutes.useQuery`. */
|
|
@@ -12376,8 +12396,8 @@ function selectedDeviceOptions(options, selected) {
|
|
|
12376
12396
|
*/
|
|
12377
12397
|
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";
|
|
12378
12398
|
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";
|
|
12379
|
-
var LABEL_CLASS$
|
|
12380
|
-
var DESC_CLASS$
|
|
12399
|
+
var LABEL_CLASS$3 = "block text-[11px] font-medium text-foreground mb-1 break-words";
|
|
12400
|
+
var DESC_CLASS$2 = "text-[10px] text-foreground-subtle mt-0.5 break-words";
|
|
12381
12401
|
/** The compact projection of the listAll rows the device fields consume. */
|
|
12382
12402
|
function toCandidates(rows) {
|
|
12383
12403
|
if (!Array.isArray(rows)) return [];
|
|
@@ -12464,14 +12484,14 @@ function FacetDropdown$1({ label, values, chosen, onChange }) {
|
|
|
12464
12484
|
})]
|
|
12465
12485
|
})] });
|
|
12466
12486
|
}
|
|
12467
|
-
function FieldShell({ label, description, required, translationFn, children }) {
|
|
12487
|
+
function FieldShell$1({ label, description, required, translationFn, children }) {
|
|
12468
12488
|
const resolvedLabel = resolveLabel(label, translationFn);
|
|
12469
12489
|
const resolvedDescription = resolveLabel(description, translationFn);
|
|
12470
12490
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
12471
12491
|
className: "min-w-0",
|
|
12472
12492
|
children: [
|
|
12473
12493
|
resolvedLabel !== void 0 && resolvedLabel !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
12474
|
-
className: LABEL_CLASS$
|
|
12494
|
+
className: LABEL_CLASS$3,
|
|
12475
12495
|
children: [resolvedLabel, required && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
12476
12496
|
className: "text-danger ml-0.5",
|
|
12477
12497
|
children: "*"
|
|
@@ -12479,7 +12499,7 @@ function FieldShell({ label, description, required, translationFn, children }) {
|
|
|
12479
12499
|
}),
|
|
12480
12500
|
children,
|
|
12481
12501
|
resolvedDescription && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
12482
|
-
className: DESC_CLASS$
|
|
12502
|
+
className: DESC_CLASS$2,
|
|
12483
12503
|
children: resolvedDescription
|
|
12484
12504
|
})
|
|
12485
12505
|
]
|
|
@@ -12489,7 +12509,7 @@ function DeviceSelectField({ field, value, onChange, disabled, translationFn })
|
|
|
12489
12509
|
const { data } = useDeviceManagerListAll({});
|
|
12490
12510
|
const options = (0, react$1.useMemo)(() => devicesToOptions(toCandidates(data), field.deviceFilter), [data, field.deviceFilter]);
|
|
12491
12511
|
const current = value === void 0 || value === null ? "" : String(value);
|
|
12492
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell, {
|
|
12512
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell$1, {
|
|
12493
12513
|
label: field.label,
|
|
12494
12514
|
description: field.description,
|
|
12495
12515
|
required: field.required,
|
|
@@ -12567,7 +12587,7 @@ function DeviceMultiSelectField({ field, value, onChange, disabled, translationF
|
|
|
12567
12587
|
className: "min-w-0",
|
|
12568
12588
|
children: [
|
|
12569
12589
|
label !== void 0 && label !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
12570
|
-
className: LABEL_CLASS$
|
|
12590
|
+
className: LABEL_CLASS$3,
|
|
12571
12591
|
children: label
|
|
12572
12592
|
}),
|
|
12573
12593
|
selectedOptions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -12690,6 +12710,720 @@ function DeviceMultiSelectField({ field, value, onChange, disabled, translationF
|
|
|
12690
12710
|
})
|
|
12691
12711
|
}),
|
|
12692
12712
|
description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
12713
|
+
className: DESC_CLASS$2,
|
|
12714
|
+
children: description
|
|
12715
|
+
})
|
|
12716
|
+
]
|
|
12717
|
+
});
|
|
12718
|
+
}
|
|
12719
|
+
//#endregion
|
|
12720
|
+
//#region ../../node_modules/class-variance-authority/dist/index.mjs
|
|
12721
|
+
/**
|
|
12722
|
+
* Copyright 2022 Joe Bell. All rights reserved.
|
|
12723
|
+
*
|
|
12724
|
+
* This file is licensed to you under the Apache License, Version 2.0
|
|
12725
|
+
* (the "License"); you may not use this file except in compliance with the
|
|
12726
|
+
* License. You may obtain a copy of the License at
|
|
12727
|
+
*
|
|
12728
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12729
|
+
*
|
|
12730
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12731
|
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
12732
|
+
* WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the
|
|
12733
|
+
* License for the specific language governing permissions and limitations under
|
|
12734
|
+
* the License.
|
|
12735
|
+
*/ var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
|
|
12736
|
+
var cx = clsx;
|
|
12737
|
+
var cva = (base, config) => (props) => {
|
|
12738
|
+
var _config_compoundVariants;
|
|
12739
|
+
if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
|
|
12740
|
+
const { variants, defaultVariants } = config;
|
|
12741
|
+
const getVariantClassNames = Object.keys(variants).map((variant) => {
|
|
12742
|
+
const variantProp = props === null || props === void 0 ? void 0 : props[variant];
|
|
12743
|
+
const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
|
|
12744
|
+
if (variantProp === null) return null;
|
|
12745
|
+
const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
|
|
12746
|
+
return variants[variant][variantKey];
|
|
12747
|
+
});
|
|
12748
|
+
const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
|
|
12749
|
+
let [key, value] = param;
|
|
12750
|
+
if (value === void 0) return acc;
|
|
12751
|
+
acc[key] = value;
|
|
12752
|
+
return acc;
|
|
12753
|
+
}, {});
|
|
12754
|
+
return cx(base, getVariantClassNames, config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param) => {
|
|
12755
|
+
let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
|
|
12756
|
+
return Object.entries(compoundVariantOptions).every((param) => {
|
|
12757
|
+
let [key, value] = param;
|
|
12758
|
+
return Array.isArray(value) ? value.includes({
|
|
12759
|
+
...defaultVariants,
|
|
12760
|
+
...propsWithoutUndefined
|
|
12761
|
+
}[key]) : {
|
|
12762
|
+
...defaultVariants,
|
|
12763
|
+
...propsWithoutUndefined
|
|
12764
|
+
}[key] === value;
|
|
12765
|
+
}) ? [
|
|
12766
|
+
...acc,
|
|
12767
|
+
cvClass,
|
|
12768
|
+
cvClassName
|
|
12769
|
+
] : acc;
|
|
12770
|
+
}, []), props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
|
|
12771
|
+
};
|
|
12772
|
+
//#endregion
|
|
12773
|
+
//#region src/primitives/badge.tsx
|
|
12774
|
+
var badgeVariants = cva("inline-flex items-center rounded-full text-xs font-medium px-2 py-0.5", {
|
|
12775
|
+
variants: {
|
|
12776
|
+
variant: {
|
|
12777
|
+
default: "bg-surface-hover text-foreground",
|
|
12778
|
+
success: "bg-success/15 text-success",
|
|
12779
|
+
warning: "bg-warning/15 text-warning",
|
|
12780
|
+
danger: "bg-danger/15 text-danger",
|
|
12781
|
+
info: "bg-info/15 text-info"
|
|
12782
|
+
},
|
|
12783
|
+
styleVariant: {
|
|
12784
|
+
solid: "",
|
|
12785
|
+
outline: "border bg-transparent"
|
|
12786
|
+
}
|
|
12787
|
+
},
|
|
12788
|
+
defaultVariants: {
|
|
12789
|
+
variant: "default",
|
|
12790
|
+
styleVariant: "solid"
|
|
12791
|
+
}
|
|
12792
|
+
});
|
|
12793
|
+
var Badge = (0, react$1.forwardRef)(({ className, variant, style, ...props }, ref) => {
|
|
12794
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
12795
|
+
ref,
|
|
12796
|
+
className: cn(badgeVariants({
|
|
12797
|
+
variant,
|
|
12798
|
+
styleVariant: style
|
|
12799
|
+
}), className),
|
|
12800
|
+
...props
|
|
12801
|
+
});
|
|
12802
|
+
});
|
|
12803
|
+
Badge.displayName = "Badge";
|
|
12804
|
+
//#endregion
|
|
12805
|
+
//#region src/composites/event-kind-visuals.tsx
|
|
12806
|
+
/**
|
|
12807
|
+
* Event-kind VISUALS — the UI-side half of the unified taxonomy dictionary
|
|
12808
|
+
* (`@camstack/types` `EVENT_TAXONOMY` owns the serializable data:
|
|
12809
|
+
* color / iconId / labelKey; this owns `iconId → lucide component` and
|
|
12810
|
+
* `labelKey → t()`).
|
|
12811
|
+
*
|
|
12812
|
+
* Every UI that renders an event kind (timeline, events tree, chips,
|
|
12813
|
+
* detection canvas/tree) resolves its glyph + translated label here — no
|
|
12814
|
+
* per-component hardcoded icon or label.
|
|
12815
|
+
*/
|
|
12816
|
+
/**
|
|
12817
|
+
* `iconId` → lucide component. Built ONLY from statically-imported icons
|
|
12818
|
+
* (bundle-safe). Unknown ids resolve to the neutral `Tag` fallback.
|
|
12819
|
+
*/
|
|
12820
|
+
var EVENT_KIND_ICONS = {
|
|
12821
|
+
motion: Activity,
|
|
12822
|
+
audio: Volume2,
|
|
12823
|
+
person: User,
|
|
12824
|
+
vehicle: Car,
|
|
12825
|
+
animal: PawPrint,
|
|
12826
|
+
package: Package,
|
|
12827
|
+
sensor: Radio,
|
|
12828
|
+
control: SlidersHorizontal,
|
|
12829
|
+
car: Car,
|
|
12830
|
+
truck: Truck,
|
|
12831
|
+
bus: Bus,
|
|
12832
|
+
motorcycle: Bike,
|
|
12833
|
+
bicycle: Bike,
|
|
12834
|
+
airplane: Plane,
|
|
12835
|
+
boat: Sailboat,
|
|
12836
|
+
train: TrainFront,
|
|
12837
|
+
bird: Bird,
|
|
12838
|
+
cat: Cat,
|
|
12839
|
+
dog: Dog,
|
|
12840
|
+
horse: PawPrint,
|
|
12841
|
+
sheep: PawPrint,
|
|
12842
|
+
cow: PawPrint,
|
|
12843
|
+
elephant: PawPrint,
|
|
12844
|
+
bear: PawPrint,
|
|
12845
|
+
zebra: PawPrint,
|
|
12846
|
+
giraffe: PawPrint,
|
|
12847
|
+
door: DoorOpen,
|
|
12848
|
+
pir: Radar,
|
|
12849
|
+
smoke: Flame,
|
|
12850
|
+
water: Droplet,
|
|
12851
|
+
gas: Wind,
|
|
12852
|
+
vibration: Vibrate,
|
|
12853
|
+
tamper: ShieldAlert,
|
|
12854
|
+
presence: UserCheck,
|
|
12855
|
+
generic: Tag,
|
|
12856
|
+
lock: Lock,
|
|
12857
|
+
siren: Siren,
|
|
12858
|
+
switch: ToggleRight,
|
|
12859
|
+
doorbell: BellRing,
|
|
12860
|
+
button: CircleDot,
|
|
12861
|
+
"audio-speech": MessageSquare,
|
|
12862
|
+
"audio-scream": Megaphone,
|
|
12863
|
+
"audio-crying": Baby,
|
|
12864
|
+
"audio-laughter": Laugh,
|
|
12865
|
+
"audio-music": Music,
|
|
12866
|
+
"audio-dog": Dog,
|
|
12867
|
+
"audio-cat": Cat,
|
|
12868
|
+
"audio-bird": Bird,
|
|
12869
|
+
"audio-animal": PawPrint,
|
|
12870
|
+
"audio-alarm": BellRing,
|
|
12871
|
+
"audio-doorbell": BellRing,
|
|
12872
|
+
"audio-glass_breaking": TriangleAlert,
|
|
12873
|
+
"audio-gunshot": Crosshair,
|
|
12874
|
+
"audio-vehicle": Car,
|
|
12875
|
+
"audio-siren": Siren,
|
|
12876
|
+
"audio-fire": Flame,
|
|
12877
|
+
"audio-water": Droplets,
|
|
12878
|
+
"audio-wind": Wind,
|
|
12879
|
+
"audio-door": DoorOpen,
|
|
12880
|
+
"audio-footsteps": Footprints,
|
|
12881
|
+
"audio-crowd": Users,
|
|
12882
|
+
"audio-telephone": Phone,
|
|
12883
|
+
"audio-engine": Cog,
|
|
12884
|
+
"audio-tools": Hammer,
|
|
12885
|
+
"audio-silence": VolumeX
|
|
12886
|
+
};
|
|
12887
|
+
/** Resolve an `iconId` to a lucide component (falls back to `Tag`). */
|
|
12888
|
+
function resolveEventKindIcon(iconId) {
|
|
12889
|
+
if (Object.hasOwn(EVENT_KIND_ICONS, iconId)) return EVENT_KIND_ICONS[iconId];
|
|
12890
|
+
if (iconId.startsWith("audio-")) return Volume2;
|
|
12891
|
+
return Tag;
|
|
12892
|
+
}
|
|
12893
|
+
/**
|
|
12894
|
+
* Translated label for an event kind — `t(labelKey)` when a translation
|
|
12895
|
+
* exists, else the English `label` fallback. Robust to a missing key: an
|
|
12896
|
+
* i18n backend that echoes the key unchanged (react-i18next default) is
|
|
12897
|
+
* treated as "no translation" and the fallback label is returned, so the UI
|
|
12898
|
+
* never renders a raw `eventKind.*` key.
|
|
12899
|
+
*/
|
|
12900
|
+
function eventKindLabel(descriptor, t) {
|
|
12901
|
+
if (t === void 0) return descriptor.label;
|
|
12902
|
+
const translated = t(descriptor.labelKey);
|
|
12903
|
+
return translated === descriptor.labelKey || translated.length === 0 ? descriptor.label : translated;
|
|
12904
|
+
}
|
|
12905
|
+
/** Small convenience component: renders the lucide glyph for an `iconId`. */
|
|
12906
|
+
function EventKindGlyph({ iconId, size = 16, color, className }) {
|
|
12907
|
+
return (0, react$1.createElement)(resolveEventKindIcon(iconId), {
|
|
12908
|
+
size,
|
|
12909
|
+
color,
|
|
12910
|
+
className
|
|
12911
|
+
});
|
|
12912
|
+
}
|
|
12913
|
+
//#endregion
|
|
12914
|
+
//#region src/composites/device-picker/SelectedChip.tsx
|
|
12915
|
+
/**
|
|
12916
|
+
* Removable selection chip + the search-box shell shared by DevicePicker and
|
|
12917
|
+
* SearchableMultiSelectField (admin-ui). Lives here so addon MFEs can render
|
|
12918
|
+
* the same picker without importing admin-ui.
|
|
12919
|
+
*/
|
|
12920
|
+
/** The search box's shell — shared with multiselect fields that mirror this picker. */
|
|
12921
|
+
var INPUT_SHELL = "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";
|
|
12922
|
+
/** One selected entry as a removable chip. */
|
|
12923
|
+
function SelectedChip({ label, onRemove, warning = false, iconId, iconUrl, testId }) {
|
|
12924
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
12925
|
+
"data-testid": testId,
|
|
12926
|
+
className: warning ? "inline-flex items-center gap-1 rounded-md bg-warning/10 px-1.5 py-0.5 text-[11px] text-warning max-w-full" : "inline-flex items-center gap-1 rounded-md bg-primary/10 px-1.5 py-0.5 text-[11px] text-primary max-w-full",
|
|
12927
|
+
children: [
|
|
12928
|
+
iconUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
12929
|
+
src: iconUrl,
|
|
12930
|
+
alt: "",
|
|
12931
|
+
className: "h-3 w-3 shrink-0 rounded-sm object-contain"
|
|
12932
|
+
}) : iconId !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventKindGlyph, {
|
|
12933
|
+
iconId,
|
|
12934
|
+
size: 12
|
|
12935
|
+
}) : null,
|
|
12936
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
12937
|
+
className: "truncate",
|
|
12938
|
+
children: label
|
|
12939
|
+
}),
|
|
12940
|
+
onRemove !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
12941
|
+
type: "button",
|
|
12942
|
+
className: "shrink-0 hover:text-danger",
|
|
12943
|
+
"aria-label": `Remove ${label}`,
|
|
12944
|
+
onClick: onRemove,
|
|
12945
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "h-2.5 w-2.5" })
|
|
12946
|
+
})
|
|
12947
|
+
]
|
|
12948
|
+
});
|
|
12949
|
+
}
|
|
12950
|
+
//#endregion
|
|
12951
|
+
//#region src/composites/option-select/option-select-model.ts
|
|
12952
|
+
/** Number of options at/above which search is enabled by default (unless creatable). */
|
|
12953
|
+
var OPTION_SELECT_SEARCH_MIN_OPTIONS = 8;
|
|
12954
|
+
/** Fold an option into a single lower-cased haystack (value + label + description). */
|
|
12955
|
+
function optionSearchText(opt) {
|
|
12956
|
+
return [
|
|
12957
|
+
opt.value,
|
|
12958
|
+
opt.label,
|
|
12959
|
+
opt.description
|
|
12960
|
+
].filter((p) => typeof p === "string" && p.length > 0).join(" ").toLowerCase();
|
|
12961
|
+
}
|
|
12962
|
+
/**
|
|
12963
|
+
* Filter options by a free-text query. An empty / whitespace-only query
|
|
12964
|
+
* returns every option (identity, preserving order).
|
|
12965
|
+
*/
|
|
12966
|
+
function filterOptions(options, query) {
|
|
12967
|
+
const tokens = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
12968
|
+
if (tokens.length === 0) return options;
|
|
12969
|
+
return options.filter((opt) => {
|
|
12970
|
+
const hay = optionSearchText(opt);
|
|
12971
|
+
return tokens.every((t) => hay.includes(t));
|
|
12972
|
+
});
|
|
12973
|
+
}
|
|
12974
|
+
/**
|
|
12975
|
+
* Split options into the currently-selected set (in selection order) and the
|
|
12976
|
+
* rest. Selected values with no matching option are surfaced as synthetic
|
|
12977
|
+
* options whose label falls back to the raw value.
|
|
12978
|
+
*/
|
|
12979
|
+
function partitionSelected(options, selected) {
|
|
12980
|
+
const byValue = new Map(options.map((o) => [o.value, o]));
|
|
12981
|
+
const selectedSet = new Set(selected);
|
|
12982
|
+
return {
|
|
12983
|
+
selectedOptions: selected.map((v) => byValue.get(v) ?? {
|
|
12984
|
+
value: v,
|
|
12985
|
+
label: v
|
|
12986
|
+
}),
|
|
12987
|
+
unselected: options.filter((o) => !selectedSet.has(o.value))
|
|
12988
|
+
};
|
|
12989
|
+
}
|
|
12990
|
+
function resolveSearchable(optionCount, creatable, searchable) {
|
|
12991
|
+
if (searchable !== void 0) return searchable;
|
|
12992
|
+
return optionCount >= 8 || creatable;
|
|
12993
|
+
}
|
|
12994
|
+
/** Trimmed non-empty draft from the search box, or null when absent. */
|
|
12995
|
+
function draftFromQuery(query) {
|
|
12996
|
+
const trimmed = query.trim();
|
|
12997
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12998
|
+
}
|
|
12999
|
+
/**
|
|
13000
|
+
* True when an existing catalog option already represents this draft (value or
|
|
13001
|
+
* label, case-insensitive). Used to hide the “Use «query»” row when redundant.
|
|
13002
|
+
*/
|
|
13003
|
+
function catalogHasDraft(options, draft) {
|
|
13004
|
+
const lower = draft.toLowerCase();
|
|
13005
|
+
return options.some((o) => o.value.toLowerCase() === lower || o.label.toLowerCase() === lower);
|
|
13006
|
+
}
|
|
13007
|
+
/**
|
|
13008
|
+
* Show the footer “Add new…” action only when creatable AND the caller
|
|
13009
|
+
* provided `onCreateRequest` (e.g. open a dedicated create dialog).
|
|
13010
|
+
*
|
|
13011
|
+
* Without that hook the footer was a no-op (it only focused the search box),
|
|
13012
|
+
* which read as “Add new is broken”. Free-form create already works via the
|
|
13013
|
+
* “Use «query»” row once the operator types — no footer needed.
|
|
13014
|
+
*/
|
|
13015
|
+
function shouldShowAddNewFooter(creatable, hasCreateRequest) {
|
|
13016
|
+
return creatable && hasCreateRequest;
|
|
13017
|
+
}
|
|
13018
|
+
/**
|
|
13019
|
+
* Show the “Use «query»” commit row: creatable, a non-empty draft, and no
|
|
13020
|
+
* catalog option that already matches the draft.
|
|
13021
|
+
*/
|
|
13022
|
+
function shouldShowUseQueryRow(creatable, query, options) {
|
|
13023
|
+
if (!creatable) return false;
|
|
13024
|
+
const draft = draftFromQuery(query);
|
|
13025
|
+
if (draft === null) return false;
|
|
13026
|
+
return !catalogHasDraft(options, draft);
|
|
13027
|
+
}
|
|
13028
|
+
//#endregion
|
|
13029
|
+
//#region src/composites/option-select/OptionSelect.tsx
|
|
13030
|
+
var LISTBOX_GAP_PX = 4;
|
|
13031
|
+
var LISTBOX_MAX_HEIGHT_PX = 224;
|
|
13032
|
+
var VIEWPORT_MARGIN_PX = 8;
|
|
13033
|
+
var LABEL_CLASS$2 = "block text-[11px] font-medium text-foreground mb-1 break-words";
|
|
13034
|
+
var DESC_CLASS$1 = "text-[10px] text-foreground-subtle mt-0.5 break-words";
|
|
13035
|
+
function OptionIcon({ opt }) {
|
|
13036
|
+
if (opt.iconUrl !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
13037
|
+
src: opt.iconUrl,
|
|
13038
|
+
alt: "",
|
|
13039
|
+
className: "h-3.5 w-3.5 shrink-0 rounded-sm object-contain"
|
|
13040
|
+
});
|
|
13041
|
+
if (opt.iconId !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventKindGlyph, {
|
|
13042
|
+
iconId: opt.iconId,
|
|
13043
|
+
size: 13
|
|
13044
|
+
});
|
|
13045
|
+
return null;
|
|
13046
|
+
}
|
|
13047
|
+
function OptionSelect(props) {
|
|
13048
|
+
const { mode, options, placeholder = "Select…", disabled = false, emptyMessage = "No options available.", creatable = false, createLabel = "Add new…", onCreateRequest, label, description, testId, optionTestIdPrefix, listboxId: listboxIdProp } = props;
|
|
13049
|
+
const isDisabled = disabled;
|
|
13050
|
+
const searchable = resolveSearchable(options.length, creatable, props.searchable);
|
|
13051
|
+
const listboxId = listboxIdProp ?? testId ?? "option-select-listbox";
|
|
13052
|
+
const selectedValues = mode === "single" ? props.value.length > 0 ? [props.value] : [] : props.value;
|
|
13053
|
+
const maxItems = mode === "multi" ? props.maxItems : void 0;
|
|
13054
|
+
const atMax = mode === "multi" && maxItems !== void 0 && selectedValues.length >= maxItems;
|
|
13055
|
+
const [query, setQuery] = (0, react$1.useState)("");
|
|
13056
|
+
const [open, setOpen] = (0, react$1.useState)(false);
|
|
13057
|
+
const [highlight, setHighlight] = (0, react$1.useState)(0);
|
|
13058
|
+
const [coords, setCoords] = (0, react$1.useState)(null);
|
|
13059
|
+
const inputRef = (0, react$1.useRef)(null);
|
|
13060
|
+
const triggerRef = (0, react$1.useRef)(null);
|
|
13061
|
+
const listboxRef = (0, react$1.useRef)(null);
|
|
13062
|
+
const blurTimer = (0, react$1.useRef)(null);
|
|
13063
|
+
const computeListboxCoords = () => {
|
|
13064
|
+
const trigger = triggerRef.current;
|
|
13065
|
+
if (trigger === null) return null;
|
|
13066
|
+
const rect = trigger.getBoundingClientRect();
|
|
13067
|
+
const spaceBelow = window.innerHeight - rect.bottom - VIEWPORT_MARGIN_PX;
|
|
13068
|
+
const spaceAbove = rect.top - VIEWPORT_MARGIN_PX;
|
|
13069
|
+
const preferBelow = spaceBelow >= Math.min(LISTBOX_MAX_HEIGHT_PX, spaceAbove) || spaceBelow >= 120;
|
|
13070
|
+
const maxHeight = Math.max(96, Math.min(LISTBOX_MAX_HEIGHT_PX, preferBelow ? spaceBelow : spaceAbove));
|
|
13071
|
+
const left = Math.max(VIEWPORT_MARGIN_PX, Math.min(rect.left, window.innerWidth - rect.width - VIEWPORT_MARGIN_PX));
|
|
13072
|
+
if (preferBelow) return {
|
|
13073
|
+
top: rect.bottom + LISTBOX_GAP_PX,
|
|
13074
|
+
left,
|
|
13075
|
+
width: rect.width,
|
|
13076
|
+
maxHeight,
|
|
13077
|
+
placement: "below"
|
|
13078
|
+
};
|
|
13079
|
+
return {
|
|
13080
|
+
top: Math.max(VIEWPORT_MARGIN_PX, rect.top - LISTBOX_GAP_PX - maxHeight),
|
|
13081
|
+
left,
|
|
13082
|
+
width: rect.width,
|
|
13083
|
+
maxHeight,
|
|
13084
|
+
placement: "above"
|
|
13085
|
+
};
|
|
13086
|
+
};
|
|
13087
|
+
const { selectedOptions } = (0, react$1.useMemo)(() => partitionSelected(options, selectedValues), [options, selectedValues]);
|
|
13088
|
+
const filtered = (0, react$1.useMemo)(() => filterOptions(options, query), [options, query]);
|
|
13089
|
+
const showUseQuery = shouldShowUseQueryRow(creatable, query, options);
|
|
13090
|
+
const showAddNewFooter = shouldShowAddNewFooter(creatable, onCreateRequest !== void 0);
|
|
13091
|
+
const dropdownRows = (0, react$1.useMemo)(() => {
|
|
13092
|
+
const rows = filtered.map((opt, index) => ({
|
|
13093
|
+
kind: "option",
|
|
13094
|
+
opt,
|
|
13095
|
+
index
|
|
13096
|
+
}));
|
|
13097
|
+
if (showUseQuery) {
|
|
13098
|
+
const draft = draftFromQuery(query);
|
|
13099
|
+
if (draft !== null) rows.push({
|
|
13100
|
+
kind: "use-query",
|
|
13101
|
+
draft
|
|
13102
|
+
});
|
|
13103
|
+
}
|
|
13104
|
+
return rows;
|
|
13105
|
+
}, [
|
|
13106
|
+
filtered,
|
|
13107
|
+
showUseQuery,
|
|
13108
|
+
query
|
|
13109
|
+
]);
|
|
13110
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
13111
|
+
if (!open || isDisabled) {
|
|
13112
|
+
setCoords(null);
|
|
13113
|
+
return;
|
|
13114
|
+
}
|
|
13115
|
+
const update = () => {
|
|
13116
|
+
setCoords(computeListboxCoords());
|
|
13117
|
+
};
|
|
13118
|
+
update();
|
|
13119
|
+
window.addEventListener("resize", update);
|
|
13120
|
+
window.addEventListener("scroll", update, true);
|
|
13121
|
+
return () => {
|
|
13122
|
+
window.removeEventListener("resize", update);
|
|
13123
|
+
window.removeEventListener("scroll", update, true);
|
|
13124
|
+
};
|
|
13125
|
+
}, [
|
|
13126
|
+
open,
|
|
13127
|
+
isDisabled,
|
|
13128
|
+
selectedValues.length,
|
|
13129
|
+
query,
|
|
13130
|
+
dropdownRows.length
|
|
13131
|
+
]);
|
|
13132
|
+
(0, react$1.useEffect)(() => {
|
|
13133
|
+
if (!open) return;
|
|
13134
|
+
const onPointer = (e) => {
|
|
13135
|
+
const target = e.target;
|
|
13136
|
+
if (listboxRef.current?.contains(target)) return;
|
|
13137
|
+
if (triggerRef.current?.contains(target)) return;
|
|
13138
|
+
setOpen(false);
|
|
13139
|
+
};
|
|
13140
|
+
document.addEventListener("mousedown", onPointer);
|
|
13141
|
+
return () => {
|
|
13142
|
+
document.removeEventListener("mousedown", onPointer);
|
|
13143
|
+
};
|
|
13144
|
+
}, [open]);
|
|
13145
|
+
const commitDraft = (draft) => {
|
|
13146
|
+
const trimmed = draft.trim();
|
|
13147
|
+
if (trimmed.length === 0) return;
|
|
13148
|
+
if (onCreateRequest !== void 0) {
|
|
13149
|
+
onCreateRequest(trimmed);
|
|
13150
|
+
setQuery("");
|
|
13151
|
+
setOpen(false);
|
|
13152
|
+
return;
|
|
13153
|
+
}
|
|
13154
|
+
if (mode === "single") {
|
|
13155
|
+
props.onChange(trimmed);
|
|
13156
|
+
setQuery("");
|
|
13157
|
+
setOpen(false);
|
|
13158
|
+
return;
|
|
13159
|
+
}
|
|
13160
|
+
if (props.value.includes(trimmed)) return;
|
|
13161
|
+
if (maxItems !== void 0 && props.value.length >= maxItems) return;
|
|
13162
|
+
props.onChange([...props.value, trimmed]);
|
|
13163
|
+
setQuery("");
|
|
13164
|
+
};
|
|
13165
|
+
const toggleMulti = (optValue) => {
|
|
13166
|
+
if (mode !== "multi" || isDisabled) return;
|
|
13167
|
+
if (props.value.includes(optValue)) props.onChange(props.value.filter((v) => v !== optValue));
|
|
13168
|
+
else {
|
|
13169
|
+
if (maxItems !== void 0 && props.value.length >= maxItems) return;
|
|
13170
|
+
props.onChange([...props.value, optValue]);
|
|
13171
|
+
}
|
|
13172
|
+
};
|
|
13173
|
+
const selectSingle = (optValue) => {
|
|
13174
|
+
if (mode !== "single" || isDisabled) return;
|
|
13175
|
+
props.onChange(optValue);
|
|
13176
|
+
setQuery("");
|
|
13177
|
+
setOpen(false);
|
|
13178
|
+
};
|
|
13179
|
+
const activateRow = (row) => {
|
|
13180
|
+
if (row.kind === "option") {
|
|
13181
|
+
if (row.opt.disabled === true) return;
|
|
13182
|
+
if (mode === "single") selectSingle(row.opt.value);
|
|
13183
|
+
else toggleMulti(row.opt.value);
|
|
13184
|
+
} else if (row.kind === "use-query") commitDraft(row.draft);
|
|
13185
|
+
};
|
|
13186
|
+
const rowCount = dropdownRows.length + (showAddNewFooter && !showUseQuery ? 1 : 0);
|
|
13187
|
+
const handleKeyDown = (e) => {
|
|
13188
|
+
if (e.key === "ArrowDown") {
|
|
13189
|
+
e.preventDefault();
|
|
13190
|
+
setOpen(true);
|
|
13191
|
+
setHighlight((h) => rowCount === 0 ? 0 : (h + 1) % rowCount);
|
|
13192
|
+
} else if (e.key === "ArrowUp") {
|
|
13193
|
+
e.preventDefault();
|
|
13194
|
+
setOpen(true);
|
|
13195
|
+
setHighlight((h) => rowCount === 0 ? 0 : (h - 1 + rowCount) % rowCount);
|
|
13196
|
+
} else if (e.key === "Enter") {
|
|
13197
|
+
e.preventDefault();
|
|
13198
|
+
if (showUseQuery && highlight === dropdownRows.length) {
|
|
13199
|
+
const draft = draftFromQuery(query);
|
|
13200
|
+
if (draft !== null) commitDraft(draft);
|
|
13201
|
+
return;
|
|
13202
|
+
}
|
|
13203
|
+
if (showAddNewFooter && !showUseQuery && highlight === dropdownRows.length) {
|
|
13204
|
+
inputRef.current?.focus();
|
|
13205
|
+
return;
|
|
13206
|
+
}
|
|
13207
|
+
const row = dropdownRows[highlight];
|
|
13208
|
+
if (row) activateRow(row);
|
|
13209
|
+
else if (creatable) {
|
|
13210
|
+
const draft = draftFromQuery(query);
|
|
13211
|
+
if (draft !== null) commitDraft(draft);
|
|
13212
|
+
}
|
|
13213
|
+
} else if (e.key === "Escape") setOpen(false);
|
|
13214
|
+
else if (e.key === "Backspace" && query === "" && mode === "multi" && props.value.length > 0) props.onChange(props.value.slice(0, -1));
|
|
13215
|
+
};
|
|
13216
|
+
const handleNonSearchKeyDown = (e) => {
|
|
13217
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
13218
|
+
e.preventDefault();
|
|
13219
|
+
setOpen((o) => !o);
|
|
13220
|
+
} else if (e.key === "Escape") setOpen(false);
|
|
13221
|
+
};
|
|
13222
|
+
const removeChip = (value) => {
|
|
13223
|
+
if (mode === "single") props.onChange("");
|
|
13224
|
+
else props.onChange(props.value.filter((v) => v !== value));
|
|
13225
|
+
};
|
|
13226
|
+
const triggerPlaceholder = atMax === true ? `Max ${maxItems} selected` : placeholder;
|
|
13227
|
+
const showEmptyCatalog = options.length === 0 && !showUseQuery && dropdownRows.length === 0 && open;
|
|
13228
|
+
let dropdownBody;
|
|
13229
|
+
if (showEmptyCatalog) dropdownBody = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
13230
|
+
className: "px-2.5 py-2 text-[11px] text-foreground-subtle",
|
|
13231
|
+
children: emptyMessage
|
|
13232
|
+
});
|
|
13233
|
+
else if (dropdownRows.length === 0 && !creatable) dropdownBody = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
13234
|
+
className: "px-2.5 py-2 text-[11px] text-foreground-subtle italic",
|
|
13235
|
+
children: [
|
|
13236
|
+
"No matches for “",
|
|
13237
|
+
query,
|
|
13238
|
+
"”"
|
|
13239
|
+
]
|
|
13240
|
+
});
|
|
13241
|
+
else dropdownBody = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: dropdownRows.map((row, i) => {
|
|
13242
|
+
if (row.kind === "use-query") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
13243
|
+
type: "button",
|
|
13244
|
+
role: "option",
|
|
13245
|
+
"aria-selected": false,
|
|
13246
|
+
className: [
|
|
13247
|
+
"flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs transition-colors",
|
|
13248
|
+
i === highlight ? "bg-primary/10" : "hover:bg-surface-hover",
|
|
13249
|
+
"cursor-pointer"
|
|
13250
|
+
].join(" "),
|
|
13251
|
+
onMouseEnter: () => setHighlight(i),
|
|
13252
|
+
onClick: () => commitDraft(row.draft),
|
|
13253
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "h-3.5 w-3.5 shrink-0 text-primary" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
13254
|
+
className: "truncate text-foreground",
|
|
13255
|
+
children: [
|
|
13256
|
+
"Use “",
|
|
13257
|
+
row.draft,
|
|
13258
|
+
"”"
|
|
13259
|
+
]
|
|
13260
|
+
})]
|
|
13261
|
+
}, "__use-query__");
|
|
13262
|
+
if (row.kind !== "option") return null;
|
|
13263
|
+
const { opt } = row;
|
|
13264
|
+
const checked = mode === "single" ? props.value === opt.value : props.value.includes(opt.value);
|
|
13265
|
+
const blockedByMax = mode === "multi" && !checked && atMax === true;
|
|
13266
|
+
const optionDisabled = opt.disabled === true || blockedByMax;
|
|
13267
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
13268
|
+
type: "button",
|
|
13269
|
+
role: "option",
|
|
13270
|
+
"aria-selected": checked,
|
|
13271
|
+
disabled: optionDisabled,
|
|
13272
|
+
"data-testid": optionTestIdPrefix !== void 0 ? `${optionTestIdPrefix}-${opt.value}` : void 0,
|
|
13273
|
+
className: [
|
|
13274
|
+
"flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs transition-colors",
|
|
13275
|
+
i === highlight ? "bg-primary/10" : "hover:bg-surface-hover",
|
|
13276
|
+
optionDisabled ? "opacity-40 cursor-not-allowed" : "cursor-pointer"
|
|
13277
|
+
].join(" "),
|
|
13278
|
+
onMouseEnter: () => setHighlight(i),
|
|
13279
|
+
onClick: () => activateRow(row),
|
|
13280
|
+
children: [
|
|
13281
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13282
|
+
className: ["flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded border", checked ? "border-primary bg-primary text-white" : "border-border"].join(" "),
|
|
13283
|
+
children: checked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Check, { className: "h-2.5 w-2.5" })
|
|
13284
|
+
}),
|
|
13285
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionIcon, { opt }),
|
|
13286
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13287
|
+
className: "min-w-0 flex-1 truncate text-foreground",
|
|
13288
|
+
children: opt.label
|
|
13289
|
+
}),
|
|
13290
|
+
opt.badge !== void 0 && opt.badge.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
13291
|
+
variant: "default",
|
|
13292
|
+
children: opt.badge
|
|
13293
|
+
}) : null
|
|
13294
|
+
]
|
|
13295
|
+
}, opt.value);
|
|
13296
|
+
}) });
|
|
13297
|
+
const footerAddNewIndex = dropdownRows.length;
|
|
13298
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
13299
|
+
className: "min-w-0 space-y-1",
|
|
13300
|
+
"data-testid": testId,
|
|
13301
|
+
children: [
|
|
13302
|
+
label !== void 0 && label.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
13303
|
+
className: LABEL_CLASS$2,
|
|
13304
|
+
children: label
|
|
13305
|
+
}),
|
|
13306
|
+
mode === "multi" && selectedOptions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
13307
|
+
className: "flex flex-wrap gap-1 mb-1.5",
|
|
13308
|
+
children: selectedOptions.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedChip, {
|
|
13309
|
+
label: opt.label,
|
|
13310
|
+
...opt.iconUrl !== void 0 ? { iconUrl: opt.iconUrl } : {},
|
|
13311
|
+
...opt.iconId !== void 0 ? { iconId: opt.iconId } : {},
|
|
13312
|
+
...!isDisabled ? { onRemove: () => removeChip(opt.value) } : {},
|
|
13313
|
+
...optionTestIdPrefix !== void 0 ? { testId: `${optionTestIdPrefix}-chip-${opt.value}` } : {}
|
|
13314
|
+
}, opt.value))
|
|
13315
|
+
}),
|
|
13316
|
+
mode === "single" && selectedOptions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
13317
|
+
className: "flex flex-wrap gap-1 mb-1.5",
|
|
13318
|
+
children: selectedOptions.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectedChip, {
|
|
13319
|
+
label: opt.label,
|
|
13320
|
+
...opt.iconUrl !== void 0 ? { iconUrl: opt.iconUrl } : {},
|
|
13321
|
+
...opt.iconId !== void 0 ? { iconId: opt.iconId } : {},
|
|
13322
|
+
...!isDisabled ? { onRemove: () => removeChip(opt.value) } : {}
|
|
13323
|
+
}, opt.value))
|
|
13324
|
+
}),
|
|
13325
|
+
searchable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
13326
|
+
ref: (el) => {
|
|
13327
|
+
triggerRef.current = el;
|
|
13328
|
+
},
|
|
13329
|
+
className: INPUT_SHELL + (isDisabled ? " opacity-50" : ""),
|
|
13330
|
+
children: [
|
|
13331
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Search, { className: "h-3.5 w-3.5 shrink-0 text-foreground-subtle" }),
|
|
13332
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
13333
|
+
ref: inputRef,
|
|
13334
|
+
type: "text",
|
|
13335
|
+
role: "combobox",
|
|
13336
|
+
"aria-expanded": open,
|
|
13337
|
+
"aria-controls": listboxId,
|
|
13338
|
+
className: "min-w-0 flex-1 bg-transparent text-xs text-foreground outline-none placeholder:text-foreground-disabled",
|
|
13339
|
+
value: query,
|
|
13340
|
+
placeholder: triggerPlaceholder,
|
|
13341
|
+
disabled: isDisabled,
|
|
13342
|
+
onChange: (e) => {
|
|
13343
|
+
setQuery(e.target.value);
|
|
13344
|
+
setOpen(true);
|
|
13345
|
+
setHighlight(0);
|
|
13346
|
+
},
|
|
13347
|
+
onFocus: () => setOpen(true),
|
|
13348
|
+
onBlur: () => {
|
|
13349
|
+
blurTimer.current = setTimeout(() => {
|
|
13350
|
+
const active = document.activeElement;
|
|
13351
|
+
if (listboxRef.current?.contains(active)) return;
|
|
13352
|
+
setOpen(false);
|
|
13353
|
+
}, 120);
|
|
13354
|
+
},
|
|
13355
|
+
onKeyDown: handleKeyDown
|
|
13356
|
+
}),
|
|
13357
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, {
|
|
13358
|
+
className: "h-3.5 w-3.5 shrink-0 cursor-pointer text-foreground-subtle",
|
|
13359
|
+
onMouseDown: (e) => {
|
|
13360
|
+
e.preventDefault();
|
|
13361
|
+
if (blurTimer.current) clearTimeout(blurTimer.current);
|
|
13362
|
+
setOpen((o) => !o);
|
|
13363
|
+
inputRef.current?.focus();
|
|
13364
|
+
}
|
|
13365
|
+
})
|
|
13366
|
+
]
|
|
13367
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
13368
|
+
ref: (el) => {
|
|
13369
|
+
triggerRef.current = el;
|
|
13370
|
+
},
|
|
13371
|
+
type: "button",
|
|
13372
|
+
role: "combobox",
|
|
13373
|
+
"aria-expanded": open,
|
|
13374
|
+
"aria-controls": listboxId,
|
|
13375
|
+
disabled: isDisabled,
|
|
13376
|
+
className: [
|
|
13377
|
+
INPUT_SHELL,
|
|
13378
|
+
"w-full justify-between text-xs text-foreground",
|
|
13379
|
+
isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
|
|
13380
|
+
].join(" "),
|
|
13381
|
+
onClick: () => !isDisabled && setOpen((o) => !o),
|
|
13382
|
+
onKeyDown: handleNonSearchKeyDown,
|
|
13383
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13384
|
+
className: "truncate text-foreground-subtle",
|
|
13385
|
+
children: selectedOptions[0]?.label ?? triggerPlaceholder
|
|
13386
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-foreground-subtle" })]
|
|
13387
|
+
}),
|
|
13388
|
+
open && !isDisabled && coords !== null && typeof document !== "undefined" && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
13389
|
+
ref: listboxRef,
|
|
13390
|
+
id: listboxId,
|
|
13391
|
+
role: "listbox",
|
|
13392
|
+
style: {
|
|
13393
|
+
position: "fixed",
|
|
13394
|
+
top: coords.top,
|
|
13395
|
+
left: coords.left,
|
|
13396
|
+
width: coords.width,
|
|
13397
|
+
maxHeight: coords.maxHeight,
|
|
13398
|
+
zIndex: 9999
|
|
13399
|
+
},
|
|
13400
|
+
className: "overflow-y-auto rounded-md border border-border bg-surface shadow-lg",
|
|
13401
|
+
onMouseDown: (e) => {
|
|
13402
|
+
e.preventDefault();
|
|
13403
|
+
if (blurTimer.current) clearTimeout(blurTimer.current);
|
|
13404
|
+
},
|
|
13405
|
+
children: [dropdownBody, showAddNewFooter && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
13406
|
+
type: "button",
|
|
13407
|
+
className: [
|
|
13408
|
+
"flex w-full items-center gap-2 border-t border-border px-2.5 py-1.5 text-left text-xs transition-colors",
|
|
13409
|
+
footerAddNewIndex === highlight ? "bg-primary/10" : "hover:bg-surface-hover",
|
|
13410
|
+
"cursor-pointer text-primary"
|
|
13411
|
+
].join(" "),
|
|
13412
|
+
onMouseEnter: () => setHighlight(footerAddNewIndex),
|
|
13413
|
+
onClick: () => {
|
|
13414
|
+
const draft = draftFromQuery(query);
|
|
13415
|
+
if (draft !== null) {
|
|
13416
|
+
onCreateRequest?.(draft);
|
|
13417
|
+
setQuery("");
|
|
13418
|
+
setOpen(false);
|
|
13419
|
+
return;
|
|
13420
|
+
}
|
|
13421
|
+
inputRef.current?.focus();
|
|
13422
|
+
},
|
|
13423
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "h-3.5 w-3.5 shrink-0" }), createLabel]
|
|
13424
|
+
})]
|
|
13425
|
+
}), document.body),
|
|
13426
|
+
description !== void 0 && description.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
12693
13427
|
className: DESC_CLASS$1,
|
|
12694
13428
|
children: description
|
|
12695
13429
|
})
|
|
@@ -12697,6 +13431,242 @@ function DeviceMultiSelectField({ field, value, onChange, disabled, translationF
|
|
|
12697
13431
|
});
|
|
12698
13432
|
}
|
|
12699
13433
|
//#endregion
|
|
13434
|
+
//#region src/composites/notification-targets-options.ts
|
|
13435
|
+
function kindIconKey(addonId, kind) {
|
|
13436
|
+
return `${addonId}:${kind}`;
|
|
13437
|
+
}
|
|
13438
|
+
function targetsToOptions(targets, kindIconUrls, noteIds, noteLabel) {
|
|
13439
|
+
return targets.map((target) => {
|
|
13440
|
+
const iconUrl = target.addonId !== void 0 && target.kind !== void 0 ? kindIconUrls.get(kindIconKey(target.addonId, target.kind)) : void 0;
|
|
13441
|
+
let badge;
|
|
13442
|
+
if (noteIds?.has(target.id) === true) badge = noteLabel;
|
|
13443
|
+
else if (!target.enabled) badge = "disabled";
|
|
13444
|
+
return {
|
|
13445
|
+
value: target.id,
|
|
13446
|
+
label: target.name,
|
|
13447
|
+
...iconUrl !== void 0 ? { iconUrl } : {},
|
|
13448
|
+
...badge !== void 0 ? { badge } : {}
|
|
13449
|
+
};
|
|
13450
|
+
});
|
|
13451
|
+
}
|
|
13452
|
+
function buildKindIconMap(rows) {
|
|
13453
|
+
const map = /* @__PURE__ */ new Map();
|
|
13454
|
+
if (!Array.isArray(rows)) return map;
|
|
13455
|
+
for (const row of rows) {
|
|
13456
|
+
if (row === null || typeof row !== "object") continue;
|
|
13457
|
+
const kind = Reflect.get(row, "kind");
|
|
13458
|
+
const addonId = Reflect.get(row, "addonId");
|
|
13459
|
+
const iconUrl = Reflect.get(row, "iconUrl");
|
|
13460
|
+
if (typeof kind !== "string" || typeof addonId !== "string") continue;
|
|
13461
|
+
if (typeof iconUrl === "string" && iconUrl.length > 0) map.set(kindIconKey(addonId, kind), iconUrl);
|
|
13462
|
+
}
|
|
13463
|
+
return map;
|
|
13464
|
+
}
|
|
13465
|
+
function toTargetOptions(rows) {
|
|
13466
|
+
if (!Array.isArray(rows)) return [];
|
|
13467
|
+
const out = [];
|
|
13468
|
+
for (const row of rows) {
|
|
13469
|
+
if (row === null || typeof row !== "object") continue;
|
|
13470
|
+
const id = Reflect.get(row, "id");
|
|
13471
|
+
const name = Reflect.get(row, "name");
|
|
13472
|
+
const enabled = Reflect.get(row, "enabled");
|
|
13473
|
+
const kind = Reflect.get(row, "kind");
|
|
13474
|
+
const addonId = Reflect.get(row, "addonId");
|
|
13475
|
+
if (typeof id !== "string" || typeof name !== "string") continue;
|
|
13476
|
+
if (typeof kind !== "string" || typeof addonId !== "string") continue;
|
|
13477
|
+
out.push({
|
|
13478
|
+
id,
|
|
13479
|
+
name,
|
|
13480
|
+
enabled: enabled === true,
|
|
13481
|
+
kind,
|
|
13482
|
+
addonId
|
|
13483
|
+
});
|
|
13484
|
+
}
|
|
13485
|
+
return out;
|
|
13486
|
+
}
|
|
13487
|
+
//#endregion
|
|
13488
|
+
//#region src/composites/notification-targets-field.tsx
|
|
13489
|
+
/**
|
|
13490
|
+
* Shared notification-output target pickers.
|
|
13491
|
+
*
|
|
13492
|
+
* The Notification Center editors (Summaries, Timelapses, Rules, Calendar
|
|
13493
|
+
* events) and ConfigUISchema forms (script settings, addon settings) all
|
|
13494
|
+
* pick the same catalog — `notificationOutput.listTargets`. One presentational
|
|
13495
|
+
* control plus two ConfigUISchema wrappers so a script can declare
|
|
13496
|
+
* `notification-target-select` / `notification-target-multiselect` without
|
|
13497
|
+
* inventing a parallel picker.
|
|
13498
|
+
*
|
|
13499
|
+
* A DISABLED target is offered and marked, never hidden: it is a legitimate
|
|
13500
|
+
* choice (the operator re-enables it later), and hiding it would make a rule
|
|
13501
|
+
* whose only target is off look like a rule with no target at all.
|
|
13502
|
+
*
|
|
13503
|
+
* Single and multi both render through `OptionSelect` (dropdown + pills,
|
|
13504
|
+
* searchable, kind brand icons via `iconUrl`).
|
|
13505
|
+
*/
|
|
13506
|
+
var DEFAULT_EMPTY = "No delivery target is configured. Add one on the Targets tab first — without one the notification is rendered and thrown away.";
|
|
13507
|
+
/**
|
|
13508
|
+
* Multi-select of delivery targets (NC Summaries / Timelapses / Rules shape).
|
|
13509
|
+
*/
|
|
13510
|
+
function NotificationTargetsField({ targets, value, onChange, disabled = false, maxItems, testIdPrefix = "notification-target", emptyMessage = DEFAULT_EMPTY, noteIds, noteLabel = "note", label, description, kindIconUrls: kindIconUrlsProp }) {
|
|
13511
|
+
const kindsQuery = useNotificationOutputListTargetKinds({});
|
|
13512
|
+
const kindIconUrlsFromQuery = (0, react$1.useMemo)(() => buildKindIconMap(kindsQuery.data), [kindsQuery.data]);
|
|
13513
|
+
const kindIconUrls = kindIconUrlsProp ?? kindIconUrlsFromQuery;
|
|
13514
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionSelect, {
|
|
13515
|
+
mode: "multi",
|
|
13516
|
+
options: (0, react$1.useMemo)(() => targetsToOptions(targets, kindIconUrls, noteIds, noteLabel), [
|
|
13517
|
+
targets,
|
|
13518
|
+
kindIconUrls,
|
|
13519
|
+
noteIds,
|
|
13520
|
+
noteLabel
|
|
13521
|
+
]),
|
|
13522
|
+
value,
|
|
13523
|
+
onChange,
|
|
13524
|
+
disabled,
|
|
13525
|
+
searchable: true,
|
|
13526
|
+
creatable: false,
|
|
13527
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
13528
|
+
emptyMessage,
|
|
13529
|
+
placeholder: "Search targets…",
|
|
13530
|
+
label,
|
|
13531
|
+
description,
|
|
13532
|
+
testId: `${testIdPrefix}s`,
|
|
13533
|
+
optionTestIdPrefix: testIdPrefix,
|
|
13534
|
+
listboxId: `${testIdPrefix}-listbox`
|
|
13535
|
+
});
|
|
13536
|
+
}
|
|
13537
|
+
/**
|
|
13538
|
+
* Single-select of one delivery target (Calendar events / script settings).
|
|
13539
|
+
*/
|
|
13540
|
+
function NotificationTargetSelectField({ targets, value, onChange, disabled = false, emptyMessage = DEFAULT_EMPTY, placeholder = "Select…", testId = "notification-target-select", label, description, kindIconUrls: kindIconUrlsProp }) {
|
|
13541
|
+
const kindsQuery = useNotificationOutputListTargetKinds({});
|
|
13542
|
+
const kindIconUrlsFromQuery = (0, react$1.useMemo)(() => buildKindIconMap(kindsQuery.data), [kindsQuery.data]);
|
|
13543
|
+
const kindIconUrls = kindIconUrlsProp ?? kindIconUrlsFromQuery;
|
|
13544
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionSelect, {
|
|
13545
|
+
mode: "single",
|
|
13546
|
+
options: (0, react$1.useMemo)(() => targetsToOptions(targets, kindIconUrls, void 0, "note"), [targets, kindIconUrls]),
|
|
13547
|
+
value,
|
|
13548
|
+
onChange,
|
|
13549
|
+
disabled,
|
|
13550
|
+
searchable: true,
|
|
13551
|
+
creatable: false,
|
|
13552
|
+
emptyMessage,
|
|
13553
|
+
placeholder,
|
|
13554
|
+
label,
|
|
13555
|
+
description,
|
|
13556
|
+
testId,
|
|
13557
|
+
optionTestIdPrefix: testId,
|
|
13558
|
+
listboxId: `${testId}-listbox`
|
|
13559
|
+
});
|
|
13560
|
+
}
|
|
13561
|
+
function FieldShell({ label, description, required, translationFn, children }) {
|
|
13562
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
13563
|
+
className: "space-y-1",
|
|
13564
|
+
children: [
|
|
13565
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
13566
|
+
className: "block text-[11px] font-medium text-foreground mb-1 break-words",
|
|
13567
|
+
children: [resolveLabel(label, translationFn), required === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13568
|
+
className: "text-danger",
|
|
13569
|
+
children: " *"
|
|
13570
|
+
}) : null]
|
|
13571
|
+
}),
|
|
13572
|
+
children,
|
|
13573
|
+
description !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
13574
|
+
className: "text-[10px] text-foreground-subtle mt-0.5 break-words",
|
|
13575
|
+
children: resolveLabel(description, translationFn)
|
|
13576
|
+
})
|
|
13577
|
+
]
|
|
13578
|
+
});
|
|
13579
|
+
}
|
|
13580
|
+
/**
|
|
13581
|
+
* ConfigUISchema `notification-target-multiselect` — fetches listTargets and
|
|
13582
|
+
* stores a `string[]` of target ids.
|
|
13583
|
+
*/
|
|
13584
|
+
function ConfigNotificationTargetMultiSelectFieldView({ field, value, onChange, disabled, translationFn }) {
|
|
13585
|
+
const targetsQuery = useNotificationOutputListTargets({});
|
|
13586
|
+
const kindsQuery = useNotificationOutputListTargetKinds({});
|
|
13587
|
+
const targets = (0, react$1.useMemo)(() => toTargetOptions(targetsQuery.data), [targetsQuery.data]);
|
|
13588
|
+
const kindIconUrls = (0, react$1.useMemo)(() => buildKindIconMap(kindsQuery.data), [kindsQuery.data]);
|
|
13589
|
+
const selected = Array.isArray(value) ? value.filter((v) => typeof v === "string") : typeof value === "string" && value.length > 0 ? [value] : [];
|
|
13590
|
+
const pending = targetsQuery.isPending || kindsQuery.isPending;
|
|
13591
|
+
const error = targetsQuery.isError ? targetsQuery.error : kindsQuery.isError ? kindsQuery.error : null;
|
|
13592
|
+
if (pending) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell, {
|
|
13593
|
+
label: field.label,
|
|
13594
|
+
description: field.description,
|
|
13595
|
+
required: field.required,
|
|
13596
|
+
translationFn,
|
|
13597
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
13598
|
+
className: "text-[11px] text-foreground-subtle",
|
|
13599
|
+
children: "Loading targets…"
|
|
13600
|
+
})
|
|
13601
|
+
});
|
|
13602
|
+
if (error !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell, {
|
|
13603
|
+
label: field.label,
|
|
13604
|
+
description: field.description,
|
|
13605
|
+
required: field.required,
|
|
13606
|
+
translationFn,
|
|
13607
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
13608
|
+
className: "text-[11px] text-danger",
|
|
13609
|
+
children: ["Could not load targets: ", error instanceof Error ? error.message : String(error)]
|
|
13610
|
+
})
|
|
13611
|
+
});
|
|
13612
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NotificationTargetsField, {
|
|
13613
|
+
targets,
|
|
13614
|
+
value: selected,
|
|
13615
|
+
onChange: (next) => onChange(next),
|
|
13616
|
+
disabled: disabled === true || field.disabled === true,
|
|
13617
|
+
...field.maxItems !== void 0 ? { maxItems: field.maxItems } : {},
|
|
13618
|
+
kindIconUrls,
|
|
13619
|
+
label: resolveLabel(field.label, translationFn),
|
|
13620
|
+
description: field.description !== void 0 ? resolveLabel(field.description, translationFn) : void 0,
|
|
13621
|
+
emptyMessage: "No delivery target is configured. Add one under Notification Center → Targets first.",
|
|
13622
|
+
testIdPrefix: `config-target-${field.key}`
|
|
13623
|
+
});
|
|
13624
|
+
}
|
|
13625
|
+
/**
|
|
13626
|
+
* ConfigUISchema `notification-target-select` — fetches listTargets and stores
|
|
13627
|
+
* one target id string.
|
|
13628
|
+
*/
|
|
13629
|
+
function ConfigNotificationTargetSelectFieldView({ field, value, onChange, disabled, translationFn }) {
|
|
13630
|
+
const targetsQuery = useNotificationOutputListTargets({});
|
|
13631
|
+
const kindsQuery = useNotificationOutputListTargetKinds({});
|
|
13632
|
+
const targets = (0, react$1.useMemo)(() => toTargetOptions(targetsQuery.data), [targetsQuery.data]);
|
|
13633
|
+
const kindIconUrls = (0, react$1.useMemo)(() => buildKindIconMap(kindsQuery.data), [kindsQuery.data]);
|
|
13634
|
+
const current = typeof value === "string" ? value : "";
|
|
13635
|
+
const pending = targetsQuery.isPending || kindsQuery.isPending;
|
|
13636
|
+
const error = targetsQuery.isError ? targetsQuery.error : kindsQuery.isError ? kindsQuery.error : null;
|
|
13637
|
+
if (pending) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell, {
|
|
13638
|
+
label: field.label,
|
|
13639
|
+
description: field.description,
|
|
13640
|
+
required: field.required,
|
|
13641
|
+
translationFn,
|
|
13642
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
13643
|
+
className: "text-[11px] text-foreground-subtle",
|
|
13644
|
+
children: "Loading targets…"
|
|
13645
|
+
})
|
|
13646
|
+
});
|
|
13647
|
+
if (error !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell, {
|
|
13648
|
+
label: field.label,
|
|
13649
|
+
description: field.description,
|
|
13650
|
+
required: field.required,
|
|
13651
|
+
translationFn,
|
|
13652
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
13653
|
+
className: "text-[11px] text-danger",
|
|
13654
|
+
children: ["Could not load targets: ", error instanceof Error ? error.message : String(error)]
|
|
13655
|
+
})
|
|
13656
|
+
});
|
|
13657
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NotificationTargetSelectField, {
|
|
13658
|
+
targets,
|
|
13659
|
+
value: current,
|
|
13660
|
+
onChange: (next) => onChange(next),
|
|
13661
|
+
disabled: disabled === true || field.disabled === true,
|
|
13662
|
+
kindIconUrls,
|
|
13663
|
+
label: resolveLabel(field.label, translationFn),
|
|
13664
|
+
description: field.description !== void 0 ? resolveLabel(field.description, translationFn) : void 0,
|
|
13665
|
+
emptyMessage: "No delivery target is configured. Add one under Notification Center → Targets first.",
|
|
13666
|
+
testId: `config-target-${field.key}`
|
|
13667
|
+
});
|
|
13668
|
+
}
|
|
13669
|
+
//#endregion
|
|
12700
13670
|
//#region src/contexts/custom-field-renderers.tsx
|
|
12701
13671
|
/**
|
|
12702
13672
|
* Custom field renderer registry.
|
|
@@ -12750,115 +13720,6 @@ function useCustomFieldRenderer(type) {
|
|
|
12750
13720
|
return (0, react$1.useContext)(CustomFieldRenderersContext)[type] ?? null;
|
|
12751
13721
|
}
|
|
12752
13722
|
//#endregion
|
|
12753
|
-
//#region src/composites/event-kind-visuals.tsx
|
|
12754
|
-
/**
|
|
12755
|
-
* Event-kind VISUALS — the UI-side half of the unified taxonomy dictionary
|
|
12756
|
-
* (`@camstack/types` `EVENT_TAXONOMY` owns the serializable data:
|
|
12757
|
-
* color / iconId / labelKey; this owns `iconId → lucide component` and
|
|
12758
|
-
* `labelKey → t()`).
|
|
12759
|
-
*
|
|
12760
|
-
* Every UI that renders an event kind (timeline, events tree, chips,
|
|
12761
|
-
* detection canvas/tree) resolves its glyph + translated label here — no
|
|
12762
|
-
* per-component hardcoded icon or label.
|
|
12763
|
-
*/
|
|
12764
|
-
/**
|
|
12765
|
-
* `iconId` → lucide component. Built ONLY from statically-imported icons
|
|
12766
|
-
* (bundle-safe). Unknown ids resolve to the neutral `Tag` fallback.
|
|
12767
|
-
*/
|
|
12768
|
-
var EVENT_KIND_ICONS = {
|
|
12769
|
-
motion: Activity,
|
|
12770
|
-
audio: Volume2,
|
|
12771
|
-
person: User,
|
|
12772
|
-
vehicle: Car,
|
|
12773
|
-
animal: PawPrint,
|
|
12774
|
-
package: Package,
|
|
12775
|
-
sensor: Radio,
|
|
12776
|
-
control: SlidersHorizontal,
|
|
12777
|
-
car: Car,
|
|
12778
|
-
truck: Truck,
|
|
12779
|
-
bus: Bus,
|
|
12780
|
-
motorcycle: Bike,
|
|
12781
|
-
bicycle: Bike,
|
|
12782
|
-
airplane: Plane,
|
|
12783
|
-
boat: Sailboat,
|
|
12784
|
-
train: TrainFront,
|
|
12785
|
-
bird: Bird,
|
|
12786
|
-
cat: Cat,
|
|
12787
|
-
dog: Dog,
|
|
12788
|
-
horse: PawPrint,
|
|
12789
|
-
sheep: PawPrint,
|
|
12790
|
-
cow: PawPrint,
|
|
12791
|
-
elephant: PawPrint,
|
|
12792
|
-
bear: PawPrint,
|
|
12793
|
-
zebra: PawPrint,
|
|
12794
|
-
giraffe: PawPrint,
|
|
12795
|
-
door: DoorOpen,
|
|
12796
|
-
pir: Radar,
|
|
12797
|
-
smoke: Flame,
|
|
12798
|
-
water: Droplet,
|
|
12799
|
-
gas: Wind,
|
|
12800
|
-
vibration: Vibrate,
|
|
12801
|
-
tamper: ShieldAlert,
|
|
12802
|
-
presence: UserCheck,
|
|
12803
|
-
generic: Tag,
|
|
12804
|
-
lock: Lock,
|
|
12805
|
-
siren: Siren,
|
|
12806
|
-
switch: ToggleRight,
|
|
12807
|
-
doorbell: BellRing,
|
|
12808
|
-
button: CircleDot,
|
|
12809
|
-
"audio-speech": MessageSquare,
|
|
12810
|
-
"audio-scream": Megaphone,
|
|
12811
|
-
"audio-crying": Baby,
|
|
12812
|
-
"audio-laughter": Laugh,
|
|
12813
|
-
"audio-music": Music,
|
|
12814
|
-
"audio-dog": Dog,
|
|
12815
|
-
"audio-cat": Cat,
|
|
12816
|
-
"audio-bird": Bird,
|
|
12817
|
-
"audio-animal": PawPrint,
|
|
12818
|
-
"audio-alarm": BellRing,
|
|
12819
|
-
"audio-doorbell": BellRing,
|
|
12820
|
-
"audio-glass_breaking": TriangleAlert,
|
|
12821
|
-
"audio-gunshot": Crosshair,
|
|
12822
|
-
"audio-vehicle": Car,
|
|
12823
|
-
"audio-siren": Siren,
|
|
12824
|
-
"audio-fire": Flame,
|
|
12825
|
-
"audio-water": Droplets,
|
|
12826
|
-
"audio-wind": Wind,
|
|
12827
|
-
"audio-door": DoorOpen,
|
|
12828
|
-
"audio-footsteps": Footprints,
|
|
12829
|
-
"audio-crowd": Users,
|
|
12830
|
-
"audio-telephone": Phone,
|
|
12831
|
-
"audio-engine": Cog,
|
|
12832
|
-
"audio-tools": Hammer,
|
|
12833
|
-
"audio-silence": VolumeX
|
|
12834
|
-
};
|
|
12835
|
-
/** Resolve an `iconId` to a lucide component (falls back to `Tag`). */
|
|
12836
|
-
function resolveEventKindIcon(iconId) {
|
|
12837
|
-
if (Object.hasOwn(EVENT_KIND_ICONS, iconId)) return EVENT_KIND_ICONS[iconId];
|
|
12838
|
-
if (iconId.startsWith("audio-")) return Volume2;
|
|
12839
|
-
return Tag;
|
|
12840
|
-
}
|
|
12841
|
-
/**
|
|
12842
|
-
* Translated label for an event kind — `t(labelKey)` when a translation
|
|
12843
|
-
* exists, else the English `label` fallback. Robust to a missing key: an
|
|
12844
|
-
* i18n backend that echoes the key unchanged (react-i18next default) is
|
|
12845
|
-
* treated as "no translation" and the fallback label is returned, so the UI
|
|
12846
|
-
* never renders a raw `eventKind.*` key.
|
|
12847
|
-
*/
|
|
12848
|
-
function eventKindLabel(descriptor, t) {
|
|
12849
|
-
if (t === void 0) return descriptor.label;
|
|
12850
|
-
const translated = t(descriptor.labelKey);
|
|
12851
|
-
return translated === descriptor.labelKey || translated.length === 0 ? descriptor.label : translated;
|
|
12852
|
-
}
|
|
12853
|
-
/** Small convenience component: renders the lucide glyph for an `iconId`. */
|
|
12854
|
-
function EventKindGlyph({ iconId, size = 16, color, className }) {
|
|
12855
|
-
return (0, react$1.createElement)(resolveEventKindIcon(iconId), {
|
|
12856
|
-
size,
|
|
12857
|
-
color,
|
|
12858
|
-
className
|
|
12859
|
-
});
|
|
12860
|
-
}
|
|
12861
|
-
//#endregion
|
|
12862
13723
|
//#region src/composites/confirm-dialog.tsx
|
|
12863
13724
|
/**
|
|
12864
13725
|
* The ONE modal dialog of the product — confirm, alert and prompt.
|
|
@@ -19106,59 +19967,6 @@ function useCapabilityPresence(capabilityName) {
|
|
|
19106
19967
|
return classifyCapability(query.data);
|
|
19107
19968
|
}
|
|
19108
19969
|
//#endregion
|
|
19109
|
-
//#region ../../node_modules/class-variance-authority/dist/index.mjs
|
|
19110
|
-
/**
|
|
19111
|
-
* Copyright 2022 Joe Bell. All rights reserved.
|
|
19112
|
-
*
|
|
19113
|
-
* This file is licensed to you under the Apache License, Version 2.0
|
|
19114
|
-
* (the "License"); you may not use this file except in compliance with the
|
|
19115
|
-
* License. You may obtain a copy of the License at
|
|
19116
|
-
*
|
|
19117
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
19118
|
-
*
|
|
19119
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19120
|
-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
19121
|
-
* WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the
|
|
19122
|
-
* License for the specific language governing permissions and limitations under
|
|
19123
|
-
* the License.
|
|
19124
|
-
*/ var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
|
|
19125
|
-
var cx = clsx;
|
|
19126
|
-
var cva = (base, config) => (props) => {
|
|
19127
|
-
var _config_compoundVariants;
|
|
19128
|
-
if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
|
|
19129
|
-
const { variants, defaultVariants } = config;
|
|
19130
|
-
const getVariantClassNames = Object.keys(variants).map((variant) => {
|
|
19131
|
-
const variantProp = props === null || props === void 0 ? void 0 : props[variant];
|
|
19132
|
-
const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
|
|
19133
|
-
if (variantProp === null) return null;
|
|
19134
|
-
const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
|
|
19135
|
-
return variants[variant][variantKey];
|
|
19136
|
-
});
|
|
19137
|
-
const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
|
|
19138
|
-
let [key, value] = param;
|
|
19139
|
-
if (value === void 0) return acc;
|
|
19140
|
-
acc[key] = value;
|
|
19141
|
-
return acc;
|
|
19142
|
-
}, {});
|
|
19143
|
-
return cx(base, getVariantClassNames, config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param) => {
|
|
19144
|
-
let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
|
|
19145
|
-
return Object.entries(compoundVariantOptions).every((param) => {
|
|
19146
|
-
let [key, value] = param;
|
|
19147
|
-
return Array.isArray(value) ? value.includes({
|
|
19148
|
-
...defaultVariants,
|
|
19149
|
-
...propsWithoutUndefined
|
|
19150
|
-
}[key]) : {
|
|
19151
|
-
...defaultVariants,
|
|
19152
|
-
...propsWithoutUndefined
|
|
19153
|
-
}[key] === value;
|
|
19154
|
-
}) ? [
|
|
19155
|
-
...acc,
|
|
19156
|
-
cvClass,
|
|
19157
|
-
cvClassName
|
|
19158
|
-
] : acc;
|
|
19159
|
-
}, []), props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
|
|
19160
|
-
};
|
|
19161
|
-
//#endregion
|
|
19162
19970
|
//#region src/primitives/button.tsx
|
|
19163
19971
|
var buttonVariants = cva("inline-flex items-center justify-center rounded-md font-medium transition-colors cursor-pointer disabled:opacity-50 disabled:pointer-events-none", {
|
|
19164
19972
|
variants: {
|
|
@@ -20021,38 +20829,6 @@ function ClipPanel({ deviceId, clip, profile, onProfileChange }) {
|
|
|
20021
20829
|
});
|
|
20022
20830
|
}
|
|
20023
20831
|
//#endregion
|
|
20024
|
-
//#region src/primitives/badge.tsx
|
|
20025
|
-
var badgeVariants = cva("inline-flex items-center rounded-full text-xs font-medium px-2 py-0.5", {
|
|
20026
|
-
variants: {
|
|
20027
|
-
variant: {
|
|
20028
|
-
default: "bg-surface-hover text-foreground",
|
|
20029
|
-
success: "bg-success/15 text-success",
|
|
20030
|
-
warning: "bg-warning/15 text-warning",
|
|
20031
|
-
danger: "bg-danger/15 text-danger",
|
|
20032
|
-
info: "bg-info/15 text-info"
|
|
20033
|
-
},
|
|
20034
|
-
styleVariant: {
|
|
20035
|
-
solid: "",
|
|
20036
|
-
outline: "border bg-transparent"
|
|
20037
|
-
}
|
|
20038
|
-
},
|
|
20039
|
-
defaultVariants: {
|
|
20040
|
-
variant: "default",
|
|
20041
|
-
styleVariant: "solid"
|
|
20042
|
-
}
|
|
20043
|
-
});
|
|
20044
|
-
var Badge = (0, react$1.forwardRef)(({ className, variant, style, ...props }, ref) => {
|
|
20045
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
20046
|
-
ref,
|
|
20047
|
-
className: cn(badgeVariants({
|
|
20048
|
-
variant,
|
|
20049
|
-
styleVariant: style
|
|
20050
|
-
}), className),
|
|
20051
|
-
...props
|
|
20052
|
-
});
|
|
20053
|
-
});
|
|
20054
|
-
Badge.displayName = "Badge";
|
|
20055
|
-
//#endregion
|
|
20056
20832
|
//#region src/lib/format-duration.ts
|
|
20057
20833
|
/**
|
|
20058
20834
|
* How long something lasted, the way this product says it.
|
|
@@ -26083,6 +26859,7 @@ function MultiSelectField({ field, value, onChange, disabled, translationFn }) {
|
|
|
26083
26859
|
function PasswordField({ field, value, onChange, disabled, translationFn }) {
|
|
26084
26860
|
const [show, setShow] = (0, react$1.useState)(false);
|
|
26085
26861
|
const showToggle = field.showToggle !== false;
|
|
26862
|
+
const text = value === void 0 || value === null ? "" : String(value);
|
|
26086
26863
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldWrapper, {
|
|
26087
26864
|
label: field.label,
|
|
26088
26865
|
description: field.description,
|
|
@@ -26094,14 +26871,20 @@ function PasswordField({ field, value, onChange, disabled, translationFn }) {
|
|
|
26094
26871
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
26095
26872
|
type: show ? "text" : "password",
|
|
26096
26873
|
className: INPUT_CLASS + (showToggle ? " pr-9" : ""),
|
|
26097
|
-
value:
|
|
26874
|
+
value: text,
|
|
26098
26875
|
placeholder: field.placeholder,
|
|
26099
26876
|
disabled: disabled || field.disabled,
|
|
26100
|
-
|
|
26877
|
+
autoComplete: "new-password",
|
|
26878
|
+
spellCheck: false,
|
|
26879
|
+
onChange: (e) => onChange(e.target.value),
|
|
26880
|
+
onFocus: (e) => {
|
|
26881
|
+
if (text.length > 0 && /^[•●*]{4,}$/u.test(text)) e.currentTarget.select();
|
|
26882
|
+
}
|
|
26101
26883
|
}), showToggle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
26102
26884
|
type: "button",
|
|
26103
26885
|
className: "absolute right-2 top-1/2 -translate-y-1/2 text-foreground-subtle hover:text-foreground",
|
|
26104
26886
|
tabIndex: -1,
|
|
26887
|
+
"aria-label": show ? "Hide password" : "Show password",
|
|
26105
26888
|
onClick: () => setShow((s) => !s),
|
|
26106
26889
|
children: show ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EyeOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Eye, { className: "h-3.5 w-3.5" })
|
|
26107
26890
|
})]
|
|
@@ -26247,70 +27030,30 @@ function SliderField({ field, value, onChange, disabled, translationFn }) {
|
|
|
26247
27030
|
});
|
|
26248
27031
|
}
|
|
26249
27032
|
function TagsField({ field, value, onChange, disabled, translationFn }) {
|
|
26250
|
-
const tags = Array.isArray(value) ? value : [];
|
|
26251
|
-
const
|
|
26252
|
-
const
|
|
26253
|
-
|
|
26254
|
-
|
|
26255
|
-
|
|
26256
|
-
|
|
26257
|
-
onChange([...tags, trimmed]);
|
|
26258
|
-
setInput("");
|
|
26259
|
-
}, [
|
|
26260
|
-
tags,
|
|
26261
|
-
field.maxTags,
|
|
26262
|
-
onChange
|
|
26263
|
-
]);
|
|
26264
|
-
const removeTag = (tag) => onChange(tags.filter((t) => t !== tag));
|
|
26265
|
-
const handleKeyDown = (e) => {
|
|
26266
|
-
if (e.key === "Enter" || e.key === ",") {
|
|
26267
|
-
e.preventDefault();
|
|
26268
|
-
addTag(input);
|
|
26269
|
-
} else if (e.key === "Backspace" && input === "" && tags.length > 0) removeTag(tags[tags.length - 1]);
|
|
26270
|
-
};
|
|
26271
|
-
const isDisabled = disabled || field.disabled;
|
|
27033
|
+
const tags = Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
27034
|
+
const creatable = field.creatable !== false;
|
|
27035
|
+
const options = (field.suggestions ?? []).map((s) => ({
|
|
27036
|
+
value: s,
|
|
27037
|
+
label: s
|
|
27038
|
+
}));
|
|
27039
|
+
const isDisabled = disabled === true || field.disabled === true;
|
|
26272
27040
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldWrapper, {
|
|
26273
27041
|
label: field.label,
|
|
26274
27042
|
description: field.description,
|
|
26275
27043
|
required: field.required,
|
|
26276
27044
|
span: field.span,
|
|
26277
27045
|
translationFn,
|
|
26278
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
26279
|
-
|
|
26280
|
-
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26288
|
-
|
|
26289
|
-
type: "button",
|
|
26290
|
-
onClick: () => removeTag(tag),
|
|
26291
|
-
className: "hover:text-danger",
|
|
26292
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "h-2.5 w-2.5" })
|
|
26293
|
-
})]
|
|
26294
|
-
}, tag)),
|
|
26295
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
26296
|
-
ref: inputRef,
|
|
26297
|
-
type: "text",
|
|
26298
|
-
className: "flex-1 min-w-[80px] bg-transparent text-sm text-foreground outline-none placeholder:text-foreground-disabled",
|
|
26299
|
-
value: input,
|
|
26300
|
-
placeholder: tags.length === 0 ? field.placeholder ?? "Add tags…" : "",
|
|
26301
|
-
disabled: isDisabled,
|
|
26302
|
-
list: `tags-suggestions-${field.key}`,
|
|
26303
|
-
onChange: (e) => setInput(e.target.value),
|
|
26304
|
-
onKeyDown: handleKeyDown,
|
|
26305
|
-
onBlur: () => {
|
|
26306
|
-
if (input.trim()) addTag(input);
|
|
26307
|
-
}
|
|
26308
|
-
}),
|
|
26309
|
-
field.suggestions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("datalist", {
|
|
26310
|
-
id: `tags-suggestions-${field.key}`,
|
|
26311
|
-
children: field.suggestions.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { value: s }, s))
|
|
26312
|
-
})
|
|
26313
|
-
]
|
|
27046
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OptionSelect, {
|
|
27047
|
+
mode: "multi",
|
|
27048
|
+
options,
|
|
27049
|
+
value: tags,
|
|
27050
|
+
onChange: (next) => onChange(next),
|
|
27051
|
+
disabled: isDisabled,
|
|
27052
|
+
creatable,
|
|
27053
|
+
...field.createLabel !== void 0 ? { createLabel: field.createLabel } : {},
|
|
27054
|
+
...field.maxTags !== void 0 ? { maxItems: field.maxTags } : {},
|
|
27055
|
+
placeholder: field.placeholder ?? "Search or add…",
|
|
27056
|
+
testId: `config-tags-${field.key}`
|
|
26314
27057
|
})
|
|
26315
27058
|
});
|
|
26316
27059
|
}
|
|
@@ -27542,6 +28285,20 @@ function FormField({ field, values, onChange, disabled, translationFn, onTestFie
|
|
|
27542
28285
|
disabled,
|
|
27543
28286
|
translationFn
|
|
27544
28287
|
});
|
|
28288
|
+
case "notification-target-select": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigNotificationTargetSelectFieldView, {
|
|
28289
|
+
field,
|
|
28290
|
+
value,
|
|
28291
|
+
onChange: handleChange,
|
|
28292
|
+
disabled,
|
|
28293
|
+
translationFn
|
|
28294
|
+
});
|
|
28295
|
+
case "notification-target-multiselect": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigNotificationTargetMultiSelectFieldView, {
|
|
28296
|
+
field,
|
|
28297
|
+
value,
|
|
28298
|
+
onChange: handleChange,
|
|
28299
|
+
disabled,
|
|
28300
|
+
translationFn
|
|
28301
|
+
});
|
|
27545
28302
|
case "probe": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProbeField, {
|
|
27546
28303
|
field,
|
|
27547
28304
|
value,
|
|
@@ -29074,9 +29831,6 @@ var DialogContent = (0, react$1.forwardRef)(({ className, width, placement, scro
|
|
|
29074
29831
|
if (open && !el.open) el.showModal();
|
|
29075
29832
|
if (!open && el.open) el.close();
|
|
29076
29833
|
}, [open, dialogRef]);
|
|
29077
|
-
const handleClick = (e) => {
|
|
29078
|
-
if (e.target === e.currentTarget) setOpen(false);
|
|
29079
|
-
};
|
|
29080
29834
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
|
|
29081
29835
|
ref: dialogRef,
|
|
29082
29836
|
id: contentId,
|
|
@@ -29085,7 +29839,6 @@ var DialogContent = (0, react$1.forwardRef)(({ className, width, placement, scro
|
|
|
29085
29839
|
placement,
|
|
29086
29840
|
scroll
|
|
29087
29841
|
}), className),
|
|
29088
|
-
onClick: handleClick,
|
|
29089
29842
|
onClose: () => setOpen(false),
|
|
29090
29843
|
...props,
|
|
29091
29844
|
children
|
|
@@ -39282,7 +40035,7 @@ function ControlWidget({ slice, busy, setValueFn, layout }) {
|
|
|
39282
40035
|
value: e.target.value
|
|
39283
40036
|
} });
|
|
39284
40037
|
},
|
|
39285
|
-
className: cn("w-full max-w-full min-w-0 truncate rounded
|
|
40038
|
+
className: cn("w-full max-w-full min-w-0 truncate rounded bg-transparent px-0.5 py-0.5 text-xs", busy && "opacity-40 cursor-not-allowed"),
|
|
39286
40039
|
children: slice.options.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
39287
40040
|
value: opt,
|
|
39288
40041
|
children: opt
|
|
@@ -40361,12 +41114,13 @@ function BatteryBar({ percent }) {
|
|
|
40361
41114
|
});
|
|
40362
41115
|
}
|
|
40363
41116
|
function SensorHeroInner({ trpc, device }) {
|
|
40364
|
-
const roleDescriptor = device.role != null ? ROLE_DESCRIPTOR[device.role] :
|
|
41117
|
+
const roleDescriptor = device.role != null ? ROLE_DESCRIPTOR[device.role] : ROLE_DESCRIPTOR["temperature-sensor"];
|
|
40365
41118
|
const isBattery = isBatterySensorRole(device.role);
|
|
40366
41119
|
const { data: roleDefaultsData } = useDeviceManagerGetRoleDisplayDefaults({}, { staleTime: 6e4 });
|
|
40367
41120
|
const dev = useDeviceProxy(trpc, device.id);
|
|
40368
41121
|
const numericSlice = useDeviceStateSlice(dev !== null && roleDescriptor !== void 0 ? roleDescriptor.getSliceHandle(dev.state) : void 0);
|
|
40369
41122
|
const binaryBatterySlice = useDeviceStateSlice(dev !== null && isBattery ? dev.state.binary : void 0);
|
|
41123
|
+
const occupancySlice = useDeviceStateSlice(dev !== null && !isBattery ? dev.state.binary : void 0);
|
|
40370
41124
|
const useBinaryBattery = isBattery && roleDescriptor?.kind === "numeric" && !hasBatteryNumericValue(numericSlice, roleDescriptor.field) && binaryBatterySlice !== void 0;
|
|
40371
41125
|
const descriptor = useBinaryBattery ? BINARY_BATTERY_DESCRIPTOR : roleDescriptor;
|
|
40372
41126
|
const slice = useBinaryBattery ? binaryBatterySlice : numericSlice;
|
|
@@ -40390,12 +41144,14 @@ function SensorHeroInner({ trpc, device }) {
|
|
|
40390
41144
|
const resolved = resolveSensorDisplay({
|
|
40391
41145
|
display: device.display,
|
|
40392
41146
|
capName: device.role === "numeric-sensor" ? "numeric-sensor" : void 0,
|
|
40393
|
-
roleDefault: device.role != null ? roleDefaultsData?.defaults[device.role] :
|
|
41147
|
+
roleDefault: device.role != null ? roleDefaultsData?.defaults[device.role] : roleDefaultsData?.defaults["temperature-sensor"],
|
|
40394
41148
|
sliceUnit: liveUnit,
|
|
40395
41149
|
slicePrecision,
|
|
40396
41150
|
canonicalUnit: descriptor.canonicalUnit
|
|
40397
41151
|
});
|
|
40398
41152
|
const unit = resolved.unit;
|
|
41153
|
+
const formatted = formatNumeric(resolved.toDisplayValue(rawValue), resolved.precision);
|
|
41154
|
+
const occupancyOn = occupancySlice !== void 0 && isBinarySliceValue(occupancySlice) && typeof occupancySlice["on"] === "boolean" ? Boolean(occupancySlice["on"]) : null;
|
|
40399
41155
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
40400
41156
|
className: "flex flex-col items-center justify-center gap-2 rounded-lg border border-border bg-surface p-4",
|
|
40401
41157
|
children: [
|
|
@@ -40403,13 +41159,18 @@ function SensorHeroInner({ trpc, device }) {
|
|
|
40403
41159
|
className: "flex items-baseline gap-1",
|
|
40404
41160
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
40405
41161
|
className: "text-3xl font-semibold tabular-nums text-foreground",
|
|
40406
|
-
children:
|
|
41162
|
+
children: formatted
|
|
40407
41163
|
}), unit && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
40408
41164
|
className: "text-base text-foreground-subtle",
|
|
40409
41165
|
children: unit
|
|
40410
41166
|
})]
|
|
40411
41167
|
}),
|
|
40412
41168
|
isBattery && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BatteryBar, { percent: rawValue }),
|
|
41169
|
+
occupancyOn !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
41170
|
+
className: cn("inline-flex items-center rounded px-2 py-0.5 text-xs font-medium", occupancyOn ? "bg-warning/15 text-warning" : "bg-surface-subtle text-foreground-subtle"),
|
|
41171
|
+
"data-testid": "sensor-hero-occupancy",
|
|
41172
|
+
children: occupancyOn ? "Occupied" : "Clear"
|
|
41173
|
+
}),
|
|
40413
41174
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
40414
41175
|
className: "text-sm text-foreground-subtle",
|
|
40415
41176
|
children: device.name
|
|
@@ -48239,11 +49000,11 @@ function useDeviceAdminLink(trpc, deviceId) {
|
|
|
48239
49000
|
cap: (d) => d.deviceAdminLink,
|
|
48240
49001
|
slice: () => void 0
|
|
48241
49002
|
});
|
|
48242
|
-
const
|
|
49003
|
+
const capBound = dev !== null && cap !== void 0 && cap !== null && (dev.binding?.entries.some((e) => e.capName === "device-admin-link") ?? true);
|
|
48243
49004
|
const [state, setState] = (0, react$1.useState)({ kind: "pending" });
|
|
48244
49005
|
(0, react$1.useEffect)(() => {
|
|
48245
|
-
if (!
|
|
48246
|
-
setState({ kind: "
|
|
49006
|
+
if (!capBound || cap === void 0 || cap === null) {
|
|
49007
|
+
setState({ kind: "none" });
|
|
48247
49008
|
return;
|
|
48248
49009
|
}
|
|
48249
49010
|
let live = true;
|
|
@@ -48264,7 +49025,7 @@ function useDeviceAdminLink(trpc, deviceId) {
|
|
|
48264
49025
|
live = false;
|
|
48265
49026
|
};
|
|
48266
49027
|
}, [
|
|
48267
|
-
|
|
49028
|
+
capBound,
|
|
48268
49029
|
cap,
|
|
48269
49030
|
deviceId
|
|
48270
49031
|
]);
|
|
@@ -49338,6 +50099,51 @@ function DeviceItemPreview({ trpc, device, status, enabled, showStatusPills = fa
|
|
|
49338
50099
|
});
|
|
49339
50100
|
}
|
|
49340
50101
|
//#endregion
|
|
50102
|
+
//#region src/contexts/device-quick-detail.tsx
|
|
50103
|
+
/**
|
|
50104
|
+
* Optional host-provided "open quick detail for this device id" callback.
|
|
50105
|
+
*
|
|
50106
|
+
* The devices table (`DeviceItem` / `DeviceList`) draws a discreet quick-
|
|
50107
|
+
* detail glyph whenever a host has published a handler — admin-ui mounts
|
|
50108
|
+
* ONE provider at the app shell with the modal. Call sites never wire the
|
|
50109
|
+
* button themselves; absence of a provider means no glyph (unit tests,
|
|
50110
|
+
* embeds that do not host the modal).
|
|
50111
|
+
*/
|
|
50112
|
+
var DeviceQuickDetailContext = createSharedContext("camstack:device-quick-detail", null);
|
|
50113
|
+
function DeviceQuickDetailProvider({ open, children }) {
|
|
50114
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceQuickDetailContext.Provider, {
|
|
50115
|
+
value: open,
|
|
50116
|
+
children
|
|
50117
|
+
});
|
|
50118
|
+
}
|
|
50119
|
+
/** The host's open callback, or null when no provider is mounted. */
|
|
50120
|
+
function useDeviceQuickDetailOpen() {
|
|
50121
|
+
return (0, react$1.useContext)(DeviceQuickDetailContext);
|
|
50122
|
+
}
|
|
50123
|
+
//#endregion
|
|
50124
|
+
//#region src/composites/device-item/quick-detail-action.tsx
|
|
50125
|
+
/**
|
|
50126
|
+
* Built-in row glyph for the host's quick-detail modal. Renders nothing
|
|
50127
|
+
* unless a `DeviceQuickDetailProvider` is mounted above — same "draw
|
|
50128
|
+
* nothing rather than something dead" rule as `AdminLinkQuickAction`.
|
|
50129
|
+
*/
|
|
50130
|
+
function DeviceQuickDetailAction({ deviceId, className }) {
|
|
50131
|
+
const open = useDeviceQuickDetailOpen();
|
|
50132
|
+
if (open === null) return null;
|
|
50133
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50134
|
+
type: "button",
|
|
50135
|
+
onClick: (e) => {
|
|
50136
|
+
e.stopPropagation();
|
|
50137
|
+
open(deviceId);
|
|
50138
|
+
},
|
|
50139
|
+
className: cn("inline-flex items-center justify-center rounded text-foreground-subtle hover:text-foreground transition-colors flex-shrink-0", className),
|
|
50140
|
+
title: "Quick detail",
|
|
50141
|
+
"aria-label": "Quick detail",
|
|
50142
|
+
"data-testid": "device-quick-detail",
|
|
50143
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Settings, { className: "h-3 w-3" })
|
|
50144
|
+
});
|
|
50145
|
+
}
|
|
50146
|
+
//#endregion
|
|
49341
50147
|
//#region src/composites/device-item/status-dot.tsx
|
|
49342
50148
|
/** The states that breathe (are "alive"). Offline + disabled are static. */
|
|
49343
50149
|
function isAlive(status) {
|
|
@@ -49615,6 +50421,7 @@ function DeviceItem(props) {
|
|
|
49615
50421
|
flags,
|
|
49616
50422
|
controlPlacement: "right"
|
|
49617
50423
|
}),
|
|
50424
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceQuickDetailAction, { deviceId: device.id }),
|
|
49618
50425
|
renderRowAction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
49619
50426
|
onClick: (e) => e.stopPropagation(),
|
|
49620
50427
|
children: renderRowAction(device)
|
|
@@ -49697,6 +50504,7 @@ function DeviceItem(props) {
|
|
|
49697
50504
|
flags,
|
|
49698
50505
|
controlPlacement: "right"
|
|
49699
50506
|
}),
|
|
50507
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceQuickDetailAction, { deviceId: device.id }),
|
|
49700
50508
|
renderRowAction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
49701
50509
|
onClick: (e) => e.stopPropagation(),
|
|
49702
50510
|
children: renderRowAction(device)
|
|
@@ -49798,6 +50606,7 @@ function DeviceItemTableRow(props) {
|
|
|
49798
50606
|
enabled: showSnapshot && isCamera,
|
|
49799
50607
|
showStatusPills: true
|
|
49800
50608
|
}),
|
|
50609
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceQuickDetailAction, { deviceId: device.id }),
|
|
49801
50610
|
renderRowAction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
49802
50611
|
onClick: (e) => e.stopPropagation(),
|
|
49803
50612
|
children: renderRowAction(device)
|
|
@@ -50895,10 +51704,10 @@ function useDeviceListRealtimeSync() {
|
|
|
50895
51704
|
* `renderItem`.
|
|
50896
51705
|
*/
|
|
50897
51706
|
function GenericModeList(props) {
|
|
50898
|
-
const { items, getKey, getSearchText, renderItem, getFilterType, mapFilterOption, filterOptions, filterLabel = "Type", columns, renderExpanded, isExpanded, rowClassName, onRowClick, isLoading = false, searchPlaceholder = "Search", emptyLabel = "No items found.", pageSize = 100, toolbar } = props;
|
|
51707
|
+
const { items, getKey, getSearchText, renderItem, getFilterType, mapFilterOption, filterOptions, filterLabel = "Type", initialTypeFilters = [], columns, renderExpanded, isExpanded, rowClassName, onRowClick, isLoading = false, searchPlaceholder = "Search", emptyLabel = "No items found.", pageSize = 100, toolbar } = props;
|
|
50899
51708
|
const [searchInput, setSearchInput] = (0, react$1.useState)("");
|
|
50900
51709
|
const debouncedSearch = useDebouncedString(searchInput, 300);
|
|
50901
|
-
const [typeFilters, setTypeFilters] = (0, react$1.useState)([]);
|
|
51710
|
+
const [typeFilters, setTypeFilters] = (0, react$1.useState)(() => [...initialTypeFilters]);
|
|
50902
51711
|
const [page, setPage] = (0, react$1.useState)(1);
|
|
50903
51712
|
(0, react$1.useEffect)(() => {
|
|
50904
51713
|
setPage(1);
|
|
@@ -55764,39 +56573,6 @@ function VersionBadge({ version, preRelease, className }) {
|
|
|
55764
56573
|
});
|
|
55765
56574
|
}
|
|
55766
56575
|
//#endregion
|
|
55767
|
-
//#region src/composites/device-picker/SelectedChip.tsx
|
|
55768
|
-
/**
|
|
55769
|
-
* Removable selection chip + the search-box shell shared by DevicePicker and
|
|
55770
|
-
* SearchableMultiSelectField (admin-ui). Lives here so addon MFEs can render
|
|
55771
|
-
* the same picker without importing admin-ui.
|
|
55772
|
-
*/
|
|
55773
|
-
/** The search box's shell — shared with multiselect fields that mirror this picker. */
|
|
55774
|
-
var INPUT_SHELL = "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";
|
|
55775
|
-
/** One selected entry as a removable chip. */
|
|
55776
|
-
function SelectedChip({ label, onRemove, warning = false, iconId, testId }) {
|
|
55777
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
55778
|
-
"data-testid": testId,
|
|
55779
|
-
className: warning ? "inline-flex items-center gap-1 rounded-md bg-warning/10 px-1.5 py-0.5 text-[11px] text-warning max-w-full" : "inline-flex items-center gap-1 rounded-md bg-primary/10 px-1.5 py-0.5 text-[11px] text-primary max-w-full",
|
|
55780
|
-
children: [
|
|
55781
|
-
iconId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventKindGlyph, {
|
|
55782
|
-
iconId,
|
|
55783
|
-
size: 12
|
|
55784
|
-
}),
|
|
55785
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55786
|
-
className: "truncate",
|
|
55787
|
-
children: label
|
|
55788
|
-
}),
|
|
55789
|
-
onRemove !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
55790
|
-
type: "button",
|
|
55791
|
-
className: "shrink-0 hover:text-danger",
|
|
55792
|
-
"aria-label": `Remove ${label}`,
|
|
55793
|
-
onClick: onRemove,
|
|
55794
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "h-2.5 w-2.5" })
|
|
55795
|
-
})
|
|
55796
|
-
]
|
|
55797
|
-
});
|
|
55798
|
-
}
|
|
55799
|
-
//#endregion
|
|
55800
56576
|
//#region src/composites/device-picker/device-picker-model.ts
|
|
55801
56577
|
/** Numbers numerically, anything else by its text — the name tie-break. */
|
|
55802
56578
|
function compareIds(a, b) {
|
|
@@ -57249,6 +58025,42 @@ function useZoneEditing() {
|
|
|
57249
58025
|
return (0, react$1.useContext)(ZoneEditingContext);
|
|
57250
58026
|
}
|
|
57251
58027
|
//#endregion
|
|
58028
|
+
//#region src/hooks/use-url-query.ts
|
|
58029
|
+
/**
|
|
58030
|
+
* Tiny URL query sync for surfaces that cannot assume react-router
|
|
58031
|
+
* (federated addon pages) or that only need a handful of params.
|
|
58032
|
+
*
|
|
58033
|
+
* Reads on mount + `popstate`; writes with `history.replaceState`. Empty /
|
|
58034
|
+
* null values are deleted so the URL stays short.
|
|
58035
|
+
*/
|
|
58036
|
+
function readParam(key) {
|
|
58037
|
+
return new URLSearchParams(window.location.search).get(key);
|
|
58038
|
+
}
|
|
58039
|
+
function writeParam(key, value) {
|
|
58040
|
+
const params = new URLSearchParams(window.location.search);
|
|
58041
|
+
if (value === null || value === "") params.delete(key);
|
|
58042
|
+
else params.set(key, value);
|
|
58043
|
+
const qs = params.toString();
|
|
58044
|
+
const url = qs ? `${window.location.pathname}?${qs}` : window.location.pathname;
|
|
58045
|
+
window.history.replaceState(null, "", url);
|
|
58046
|
+
}
|
|
58047
|
+
/**
|
|
58048
|
+
* One query param as React state. `null` means absent.
|
|
58049
|
+
* Changing it replaces the URL without a navigation.
|
|
58050
|
+
*/
|
|
58051
|
+
function useUrlQueryParam(key) {
|
|
58052
|
+
const [value, setValue] = (0, react$1.useState)(() => readParam(key));
|
|
58053
|
+
(0, react$1.useEffect)(() => {
|
|
58054
|
+
const onPop = () => setValue(readParam(key));
|
|
58055
|
+
window.addEventListener("popstate", onPop);
|
|
58056
|
+
return () => window.removeEventListener("popstate", onPop);
|
|
58057
|
+
}, [key]);
|
|
58058
|
+
return [value, (0, react$1.useCallback)((next) => {
|
|
58059
|
+
writeParam(key, next);
|
|
58060
|
+
setValue(next);
|
|
58061
|
+
}, [key])];
|
|
58062
|
+
}
|
|
58063
|
+
//#endregion
|
|
57252
58064
|
//#region src/hooks/use-device-cap-slice.ts
|
|
57253
58065
|
/**
|
|
57254
58066
|
* useDeviceCapSlice — generic runtime-state slice reader for an arbitrary
|
|
@@ -58138,6 +58950,8 @@ exports.CodeBlock = CodeBlock;
|
|
|
58138
58950
|
exports.CollapsibleCard = CollapsibleCard;
|
|
58139
58951
|
exports.ConfigFormBuilder = ConfigFormBuilder;
|
|
58140
58952
|
exports.ConfigFormField = FormField;
|
|
58953
|
+
exports.ConfigNotificationTargetMultiSelectFieldView = ConfigNotificationTargetMultiSelectFieldView;
|
|
58954
|
+
exports.ConfigNotificationTargetSelectFieldView = ConfigNotificationTargetSelectFieldView;
|
|
58141
58955
|
exports.ConfigSchemaField = ConfigSchemaField;
|
|
58142
58956
|
exports.ConfirmActionButton = ConfirmActionButton;
|
|
58143
58957
|
exports.ConfirmDialogProvider = ConfirmDialogProvider;
|
|
@@ -58179,6 +58993,7 @@ exports.DeviceItem = DeviceItem;
|
|
|
58179
58993
|
exports.DeviceList = DeviceList;
|
|
58180
58994
|
exports.DeviceMultiSelectField = DeviceMultiSelectField;
|
|
58181
58995
|
exports.DevicePicker = DevicePicker;
|
|
58996
|
+
exports.DeviceQuickDetailProvider = DeviceQuickDetailProvider;
|
|
58182
58997
|
exports.DeviceSelectField = DeviceSelectField;
|
|
58183
58998
|
exports.DeviceSelectorPicker = DeviceSelectorPicker;
|
|
58184
58999
|
exports.DeviceStepMatrix = DeviceStepMatrix;
|
|
@@ -58272,7 +59087,11 @@ exports.NetworkLinkBadge = NetworkLinkBadge;
|
|
|
58272
59087
|
exports.NodeMultiSelectField = NodeMultiSelectField;
|
|
58273
59088
|
exports.NodePicker = NodePicker;
|
|
58274
59089
|
exports.NodeSelectField = NodeSelectField;
|
|
59090
|
+
exports.NotificationTargetSelectField = NotificationTargetSelectField;
|
|
59091
|
+
exports.NotificationTargetsField = NotificationTargetsField;
|
|
59092
|
+
exports.OPTION_SELECT_SEARCH_MIN_OPTIONS = OPTION_SELECT_SEARCH_MIN_OPTIONS;
|
|
58275
59093
|
exports.OfflineBadge = OfflineBadge;
|
|
59094
|
+
exports.OptionSelect = OptionSelect;
|
|
58276
59095
|
exports.PHASE_CONFIG = PHASE_CONFIG;
|
|
58277
59096
|
exports.PICKER_CHIP_LIMIT = PICKER_CHIP_LIMIT;
|
|
58278
59097
|
exports.PICKER_DEFAULT_GROUP_BY = PICKER_DEFAULT_GROUP_BY;
|
|
@@ -58410,6 +59229,7 @@ exports.ariaSortForColumn = ariaSortForColumn;
|
|
|
58410
59229
|
exports.badgeFor = badgeFor;
|
|
58411
59230
|
exports.badgeFor$1 = badgeFor;
|
|
58412
59231
|
exports.buildStepTreeFromSchema = buildStepTreeFromSchema;
|
|
59232
|
+
exports.catalogHasDraft = catalogHasDraft;
|
|
58413
59233
|
exports.childEntityId = childEntityId;
|
|
58414
59234
|
exports.childListName = childListName;
|
|
58415
59235
|
exports.chipOverflow = chipOverflow;
|
|
@@ -58442,11 +59262,13 @@ exports.deviceTypeLabel = deviceTypeLabel;
|
|
|
58442
59262
|
exports.deviceTypeMeta = deviceTypeMeta;
|
|
58443
59263
|
exports.deviceTypeMetaOf = deviceTypeMetaOf;
|
|
58444
59264
|
exports.devicesToOptions = devicesToOptions;
|
|
59265
|
+
exports.draftFromQuery = draftFromQuery;
|
|
58445
59266
|
exports.emptyDevicesWidens = emptyDevicesWidens;
|
|
58446
59267
|
exports.ensureMfHostInit = ensureMfHostInit;
|
|
58447
59268
|
exports.eventKindLabel = eventKindLabel;
|
|
58448
59269
|
exports.filterDataTableRows = filterDataTableRows;
|
|
58449
59270
|
exports.filterDeviceOptions = filterDeviceOptions;
|
|
59271
|
+
exports.filterOptionSelectOptions = filterOptions;
|
|
58450
59272
|
exports.filterPickerDevices = filterPickerDevices;
|
|
58451
59273
|
exports.findTimezone = _camstack_types.findTimezone;
|
|
58452
59274
|
exports.flattenPickerGrouping = flattenPickerGrouping;
|
|
@@ -58494,11 +59316,13 @@ exports.nextTableSort = nextTableSort;
|
|
|
58494
59316
|
exports.normalizeForSearch = normalizeForSearch;
|
|
58495
59317
|
exports.offListSelections = offListSelections;
|
|
58496
59318
|
exports.openTopLayer = openTopLayer;
|
|
59319
|
+
exports.optionSelectSearchText = optionSearchText;
|
|
58497
59320
|
exports.orphanDeviceLabel = orphanDeviceLabel;
|
|
58498
59321
|
exports.overrideEntityIdFromLink = overrideEntityIdFromLink;
|
|
58499
59322
|
exports.pageRows = pageRows;
|
|
58500
59323
|
exports.parsePickerGroupBy = parsePickerGroupBy;
|
|
58501
59324
|
exports.parseRecordedServerMessage = parseRecordedServerMessage;
|
|
59325
|
+
exports.partitionOptionSelectSelected = partitionSelected;
|
|
58502
59326
|
exports.pickerFacetFilterShown = pickerFacetFilterShown;
|
|
58503
59327
|
exports.pickerFacetKey = pickerFacetKey;
|
|
58504
59328
|
exports.pickerFacetOptions = pickerFacetOptions;
|
|
@@ -58522,6 +59346,7 @@ exports.resolveDisplayIcon = resolveDisplayIcon;
|
|
|
58522
59346
|
exports.resolveEffectiveDefaultModel = resolveEffectiveDefaultModel;
|
|
58523
59347
|
exports.resolveEffectiveStepModel = resolveEffectiveStepModel;
|
|
58524
59348
|
exports.resolveEventKindIcon = resolveEventKindIcon;
|
|
59349
|
+
exports.resolveOptionSelectSearchable = resolveSearchable;
|
|
58525
59350
|
exports.resolvePrimaryChild = resolvePrimaryChild;
|
|
58526
59351
|
exports.resolveSelectedDevices = resolveSelectedDevices;
|
|
58527
59352
|
exports.resolveSensorDisplay = resolveSensorDisplay;
|
|
@@ -58536,6 +59361,8 @@ exports.selectedDeviceOptions = selectedDeviceOptions;
|
|
|
58536
59361
|
exports.serializeRecordedCommand = serializeRecordedCommand;
|
|
58537
59362
|
exports.shouldCommit = shouldCommit;
|
|
58538
59363
|
exports.shouldEmit = shouldEmit;
|
|
59364
|
+
exports.shouldShowAddNewFooter = shouldShowAddNewFooter;
|
|
59365
|
+
exports.shouldShowUseQueryRow = shouldShowUseQueryRow;
|
|
58539
59366
|
exports.shouldUseSingleNode = shouldUseSingleNode;
|
|
58540
59367
|
exports.showsNoCamerasHint = showsNoCamerasHint;
|
|
58541
59368
|
exports.sortRows = sortRows;
|
|
@@ -58677,9 +59504,10 @@ exports.useBrokerTestConnection = useBrokerTestConnection;
|
|
|
58677
59504
|
exports.useBrokerTestSettings = useBrokerTestSettings;
|
|
58678
59505
|
exports.useBrokerUnsubscribe = useBrokerUnsubscribe;
|
|
58679
59506
|
exports.useButtonPress = useButtonPress;
|
|
58680
|
-
exports.
|
|
59507
|
+
exports.useCalendarsGetPreferences = useCalendarsGetPreferences;
|
|
58681
59508
|
exports.useCalendarsListCalendars = useCalendarsListCalendars;
|
|
58682
59509
|
exports.useCalendarsListEvents = useCalendarsListEvents;
|
|
59510
|
+
exports.useCalendarsSetCalendarPreference = useCalendarsSetCalendarPreference;
|
|
58683
59511
|
exports.useCameraCredentialsGetCredentials = useCameraCredentialsGetCredentials;
|
|
58684
59512
|
exports.useCameraCredentialsGetStatus = useCameraCredentialsGetStatus;
|
|
58685
59513
|
exports.useCameraGridLayoutApplyDeviceSettingsPatch = useCameraGridLayoutApplyDeviceSettingsPatch;
|
|
@@ -58725,6 +59553,7 @@ exports.useCoreBlocksCreate = useCoreBlocksCreate;
|
|
|
58725
59553
|
exports.useCoreBlocksCreateComposition = useCoreBlocksCreateComposition;
|
|
58726
59554
|
exports.useCoreBlocksDelete = useCoreBlocksDelete;
|
|
58727
59555
|
exports.useCoreBlocksGet = useCoreBlocksGet;
|
|
59556
|
+
exports.useCoreBlocksGetBlockSettings = useCoreBlocksGetBlockSettings;
|
|
58728
59557
|
exports.useCoreBlocksGetComposition = useCoreBlocksGetComposition;
|
|
58729
59558
|
exports.useCoreBlocksGetCustomization = useCoreBlocksGetCustomization;
|
|
58730
59559
|
exports.useCoreBlocksGetTypeDefs = useCoreBlocksGetTypeDefs;
|
|
@@ -58734,6 +59563,7 @@ exports.useCoreBlocksListCompositions = useCoreBlocksListCompositions;
|
|
|
58734
59563
|
exports.useCoreBlocksRestart = useCoreBlocksRestart;
|
|
58735
59564
|
exports.useCoreBlocksSetEnabled = useCoreBlocksSetEnabled;
|
|
58736
59565
|
exports.useCoreBlocksUpdate = useCoreBlocksUpdate;
|
|
59566
|
+
exports.useCoreBlocksUpdateBlockSettings = useCoreBlocksUpdateBlockSettings;
|
|
58737
59567
|
exports.useCoreBlocksUpdateComposition = useCoreBlocksUpdateComposition;
|
|
58738
59568
|
exports.useCoreBlocksValidateComposition = useCoreBlocksValidateComposition;
|
|
58739
59569
|
exports.useCoverClose = useCoverClose;
|
|
@@ -58915,6 +59745,7 @@ exports.useDeviceProviderSupportsManualCreation = useDeviceProviderSupportsManua
|
|
|
58915
59745
|
exports.useDeviceProviderTestCreationField = useDeviceProviderTestCreationField;
|
|
58916
59746
|
exports.useDeviceProxy = useDeviceProxy;
|
|
58917
59747
|
exports.useDeviceProxy$1 = useDeviceProxy;
|
|
59748
|
+
exports.useDeviceQuickDetailOpen = useDeviceQuickDetailOpen;
|
|
58918
59749
|
exports.useDeviceSnapshot = useDeviceSnapshot;
|
|
58919
59750
|
exports.useDeviceSnapshotImage = useDeviceSnapshotImage;
|
|
58920
59751
|
exports.useDeviceSnapshotImage$1 = useDeviceSnapshotImage;
|
|
@@ -59131,8 +59962,10 @@ exports.useNodesTopology = useNodesTopology;
|
|
|
59131
59962
|
exports.useNodesUndeployAddon = useNodesUndeployAddon;
|
|
59132
59963
|
exports.useNotificationOutputDeleteTarget = useNotificationOutputDeleteTarget;
|
|
59133
59964
|
exports.useNotificationOutputDiscoverTargets = useNotificationOutputDiscoverTargets;
|
|
59965
|
+
exports.useNotificationOutputGetTarget = useNotificationOutputGetTarget;
|
|
59134
59966
|
exports.useNotificationOutputListTargetKinds = useNotificationOutputListTargetKinds;
|
|
59135
59967
|
exports.useNotificationOutputListTargets = useNotificationOutputListTargets;
|
|
59968
|
+
exports.useNotificationOutputProbeTargetConfig = useNotificationOutputProbeTargetConfig;
|
|
59136
59969
|
exports.useNotificationOutputSend = useNotificationOutputSend;
|
|
59137
59970
|
exports.useNotificationOutputSetTargetEnabled = useNotificationOutputSetTargetEnabled;
|
|
59138
59971
|
exports.useNotificationOutputTestTarget = useNotificationOutputTestTarget;
|
|
@@ -59141,9 +59974,11 @@ exports.useNotificationRulesActivateAlarmProfile = useNotificationRulesActivateA
|
|
|
59141
59974
|
exports.useNotificationRulesCancelSnooze = useNotificationRulesCancelSnooze;
|
|
59142
59975
|
exports.useNotificationRulesCreateAlarmProfile = useNotificationRulesCreateAlarmProfile;
|
|
59143
59976
|
exports.useNotificationRulesCreateRule = useNotificationRulesCreateRule;
|
|
59977
|
+
exports.useNotificationRulesCreateSequence = useNotificationRulesCreateSequence;
|
|
59144
59978
|
exports.useNotificationRulesCreateSnooze = useNotificationRulesCreateSnooze;
|
|
59145
59979
|
exports.useNotificationRulesDeleteAlarmProfile = useNotificationRulesDeleteAlarmProfile;
|
|
59146
59980
|
exports.useNotificationRulesDeleteRule = useNotificationRulesDeleteRule;
|
|
59981
|
+
exports.useNotificationRulesDeleteSequence = useNotificationRulesDeleteSequence;
|
|
59147
59982
|
exports.useNotificationRulesGetAlarmConfig = useNotificationRulesGetAlarmConfig;
|
|
59148
59983
|
exports.useNotificationRulesGetAlarmSensors = useNotificationRulesGetAlarmSensors;
|
|
59149
59984
|
exports.useNotificationRulesGetClipRetentionAsks = useNotificationRulesGetClipRetentionAsks;
|
|
@@ -59151,9 +59986,11 @@ exports.useNotificationRulesGetConditionCatalog = useNotificationRulesGetConditi
|
|
|
59151
59986
|
exports.useNotificationRulesGetHistory = useNotificationRulesGetHistory;
|
|
59152
59987
|
exports.useNotificationRulesGetRule = useNotificationRulesGetRule;
|
|
59153
59988
|
exports.useNotificationRulesGetRuleLastRuns = useNotificationRulesGetRuleLastRuns;
|
|
59989
|
+
exports.useNotificationRulesGetSequence = useNotificationRulesGetSequence;
|
|
59154
59990
|
exports.useNotificationRulesGetTemplateCatalog = useNotificationRulesGetTemplateCatalog;
|
|
59155
59991
|
exports.useNotificationRulesListDeviceMutes = useNotificationRulesListDeviceMutes;
|
|
59156
59992
|
exports.useNotificationRulesListRules = useNotificationRulesListRules;
|
|
59993
|
+
exports.useNotificationRulesListSequences = useNotificationRulesListSequences;
|
|
59157
59994
|
exports.useNotificationRulesListSnoozes = useNotificationRulesListSnoozes;
|
|
59158
59995
|
exports.useNotificationRulesPreviewStateSummary = useNotificationRulesPreviewStateSummary;
|
|
59159
59996
|
exports.useNotificationRulesPreviewTemplate = useNotificationRulesPreviewTemplate;
|
|
@@ -59164,6 +60001,7 @@ exports.useNotificationRulesSetDeviceMuted = useNotificationRulesSetDeviceMuted;
|
|
|
59164
60001
|
exports.useNotificationRulesSetRuleEnabled = useNotificationRulesSetRuleEnabled;
|
|
59165
60002
|
exports.useNotificationRulesTestRule = useNotificationRulesTestRule;
|
|
59166
60003
|
exports.useNotificationRulesUpdateRule = useNotificationRulesUpdateRule;
|
|
60004
|
+
exports.useNotificationRulesUpdateSequence = useNotificationRulesUpdateSequence;
|
|
59167
60005
|
exports.useNotifierCancel = useNotifierCancel;
|
|
59168
60006
|
exports.useNotifierGetStatus = useNotifierGetStatus;
|
|
59169
60007
|
exports.useNotifierSend = useNotifierSend;
|
|
@@ -59645,6 +60483,7 @@ exports.useToastOnToast = useToastOnToast;
|
|
|
59645
60483
|
exports.useTurnProviderGetTurnServers = useTurnProviderGetTurnServers;
|
|
59646
60484
|
exports.useUpdateGetStatus = useUpdateGetStatus;
|
|
59647
60485
|
exports.useUpdateInstallUpdate = useUpdateInstallUpdate;
|
|
60486
|
+
exports.useUrlQueryParam = useUrlQueryParam;
|
|
59648
60487
|
exports.useUserManagementConfirmTotp = useUserManagementConfirmTotp;
|
|
59649
60488
|
exports.useUserManagementCreateApiKey = useUserManagementCreateApiKey;
|
|
59650
60489
|
exports.useUserManagementCreateScopedToken = useUserManagementCreateScopedToken;
|