add select all/select none

This commit is contained in:
Josh Hawkins 2026-05-26 12:24:40 -05:00
parent 692e148ba5
commit 6f02310b21
2 changed files with 50 additions and 6 deletions

View File

@ -566,6 +566,7 @@
"categories": { "categories": {
"legend": "What to copy", "legend": "What to copy",
"selectAll": "Select all", "selectAll": "Select all",
"selectNone": "Select none",
"resetDefaults": "Reset to defaults", "resetDefaults": "Reset to defaults",
"general": "General", "general": "General",
"spatial": "Spatial settings", "spatial": "Spatial settings",
@ -576,9 +577,9 @@
"items": { "items": {
"record": "Recording", "record": "Recording",
"snapshots": "Snapshots", "snapshots": "Snapshots",
"review": "Review (alerts & detections)", "review": "Review (alerts and detections)",
"motion": "Motion sensitivity", "motion": "Motion sensitivity",
"objects": "Detection objects & filters", "objects": "Detected objects and filters",
"audio": "Audio detection", "audio": "Audio detection",
"audio_transcription": "Audio transcription", "audio_transcription": "Audio transcription",
"notifications": "Notifications", "notifications": "Notifications",
@ -594,7 +595,7 @@
"profiles": "Profiles", "profiles": "Profiles",
"detect": "Detect dimensions", "detect": "Detect dimensions",
"zones": "Zones", "zones": "Zones",
"motion_mask": "Motion mask", "motion_mask": "Motion masks",
"object_masks": "Object masks", "object_masks": "Object masks",
"ffmpeg_live": "Stream URLs and roles" "ffmpeg_live": "Stream URLs and roles"
} }

View File

@ -188,6 +188,32 @@ export default function CloneCameraDialog({
}); });
}, []); }, []);
const selectAllCategories = useCallback(() => {
setSelectedCategories((prev) => {
const next = new Set(prev);
const includeSpatial = targetIsNew || resMatch;
for (const cat of CLONE_CATEGORIES) {
if (cat.newCameraOnly && !targetIsNew) continue;
if (cat.group === "spatial" && !includeSpatial) continue;
if (cat.group === "streams") continue;
next.add(cat.key);
}
return next;
});
}, [targetIsNew, resMatch]);
const selectNoneCategories = useCallback(() => {
setSelectedCategories((prev) => {
const next = new Set<CloneCategoryKey>();
for (const cat of CLONE_CATEGORIES) {
if (cat.group === "streams" && prev.has(cat.key)) {
next.add(cat.key);
}
}
return next;
});
}, []);
const visibleCategories = useMemo( const visibleCategories = useMemo(
() => CLONE_CATEGORIES.filter((c) => targetIsNew || !c.newCameraOnly), () => CLONE_CATEGORIES.filter((c) => targetIsNew || !c.newCameraOnly),
[targetIsNew], [targetIsNew],
@ -533,9 +559,26 @@ export default function CloneCameraDialog({
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<Label className="text-base"> <div className="flex flex-row items-center justify-between gap-2">
{t("cameraManagement.clone.categories.legend")} <Label className="text-base">
</Label> {t("cameraManagement.clone.categories.legend")}
</Label>
<div className="flex flex-row items-center gap-2 text-right text-xs text-muted-foreground">
<span
className="cursor-pointer"
onClick={isSubmitting ? undefined : selectAllCategories}
>
{t("cameraManagement.clone.categories.selectAll")}
</span>
<span aria-hidden="true">|</span>
<span
className="cursor-pointer"
onClick={isSubmitting ? undefined : selectNoneCategories}
>
{t("cameraManagement.clone.categories.selectNone")}
</span>
</div>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label className="font-medium"> <Label className="font-medium">