mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-05 19:41:15 +03:00
add embedded mode to BaseSection so parents can host the save action
This commit is contained in:
parent
c6eadfebb8
commit
f9600cafe3
@ -175,6 +175,9 @@ export interface BaseSectionProps {
|
|||||||
isSavingAll?: boolean;
|
isSavingAll?: boolean;
|
||||||
/** Callback when this section's saving state changes */
|
/** Callback when this section's saving state changes */
|
||||||
onSavingChange?: (isSaving: boolean) => void;
|
onSavingChange?: (isSaving: boolean) => void;
|
||||||
|
/** When true, render the form fields only; suppress the internal save/undo bar.
|
||||||
|
* The parent owns the save action and reads pending data via `onPendingDataChange`. */
|
||||||
|
embedded?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateSectionOptions {
|
export interface CreateSectionOptions {
|
||||||
@ -211,6 +214,7 @@ export function ConfigSection({
|
|||||||
onDeleteProfileSection,
|
onDeleteProfileSection,
|
||||||
isSavingAll = false,
|
isSavingAll = false,
|
||||||
onSavingChange,
|
onSavingChange,
|
||||||
|
embedded = false,
|
||||||
}: ConfigSectionProps) {
|
}: ConfigSectionProps) {
|
||||||
// For replay level, treat as camera-level config access
|
// For replay level, treat as camera-level config access
|
||||||
const effectiveLevel = level === "replay" ? "camera" : level;
|
const effectiveLevel = level === "replay" ? "camera" : level;
|
||||||
@ -1048,121 +1052,123 @@ export function ConfigSection({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
{!embedded && (
|
||||||
className={cn(
|
|
||||||
"w-full border-t border-secondary bg-background pt-0",
|
|
||||||
!noStickyButtons && "sticky bottom-0 z-50",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-center gap-4 pt-2 md:flex-row",
|
"w-full border-t border-secondary bg-background pt-0",
|
||||||
hasChanges ? "justify-between" : "justify-end",
|
!noStickyButtons && "sticky bottom-0 z-50",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{hasChanges && (
|
<div
|
||||||
<div className="flex items-center gap-2">
|
className={cn(
|
||||||
<span className="text-sm text-unsaved">
|
"flex flex-col items-center gap-4 pt-2 md:flex-row",
|
||||||
{t("unsavedChanges", {
|
hasChanges ? "justify-between" : "justify-end",
|
||||||
ns: "views/settings",
|
)}
|
||||||
defaultValue: "You have unsaved changes",
|
>
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
<SaveAllPreviewPopover
|
|
||||||
items={sectionPreviewItems}
|
|
||||||
className="h-7 w-7"
|
|
||||||
align="start"
|
|
||||||
side="top"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex w-full flex-col gap-2 sm:flex-row sm:items-center md:w-auto">
|
|
||||||
{((effectiveLevel === "camera" && isOverridden) ||
|
|
||||||
effectiveLevel === "global") &&
|
|
||||||
!hasChanges &&
|
|
||||||
!skipSave &&
|
|
||||||
!profileName && (
|
|
||||||
<Button
|
|
||||||
onClick={() => setIsResetDialogOpen(true)}
|
|
||||||
variant="outline"
|
|
||||||
disabled={isSaving || isResettingToDefault || disabled}
|
|
||||||
className="flex flex-1 gap-2"
|
|
||||||
>
|
|
||||||
{isResettingToDefault && (
|
|
||||||
<ActivityIndicator className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
{effectiveLevel === "global"
|
|
||||||
? t("button.resetToDefault", {
|
|
||||||
ns: "common",
|
|
||||||
defaultValue: "Reset to Default",
|
|
||||||
})
|
|
||||||
: t("button.resetToGlobal", {
|
|
||||||
ns: "common",
|
|
||||||
defaultValue: "Reset to Global",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{profileName &&
|
|
||||||
profileOverridesSection &&
|
|
||||||
!hasChanges &&
|
|
||||||
!skipSave &&
|
|
||||||
onDeleteProfileSection && (
|
|
||||||
<Button
|
|
||||||
onClick={() => setIsDeleteProfileDialogOpen(true)}
|
|
||||||
variant="outline"
|
|
||||||
disabled={isSaving || disabled}
|
|
||||||
className="flex flex-1 gap-2"
|
|
||||||
>
|
|
||||||
{t("profiles.removeOverride", {
|
|
||||||
ns: "views/settings",
|
|
||||||
defaultValue: "Remove Profile Override",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm text-unsaved">
|
||||||
|
{t("unsavedChanges", {
|
||||||
|
ns: "views/settings",
|
||||||
|
defaultValue: "You have unsaved changes",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
<SaveAllPreviewPopover
|
||||||
|
items={sectionPreviewItems}
|
||||||
|
className="h-7 w-7"
|
||||||
|
align="start"
|
||||||
|
side="top"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex w-full flex-col gap-2 sm:flex-row sm:items-center md:w-auto">
|
||||||
|
{((effectiveLevel === "camera" && isOverridden) ||
|
||||||
|
effectiveLevel === "global") &&
|
||||||
|
!hasChanges &&
|
||||||
|
!skipSave &&
|
||||||
|
!profileName && (
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsResetDialogOpen(true)}
|
||||||
|
variant="outline"
|
||||||
|
disabled={isSaving || isResettingToDefault || disabled}
|
||||||
|
className="flex flex-1 gap-2"
|
||||||
|
>
|
||||||
|
{isResettingToDefault && (
|
||||||
|
<ActivityIndicator className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
{effectiveLevel === "global"
|
||||||
|
? t("button.resetToDefault", {
|
||||||
|
ns: "common",
|
||||||
|
defaultValue: "Reset to Default",
|
||||||
|
})
|
||||||
|
: t("button.resetToGlobal", {
|
||||||
|
ns: "common",
|
||||||
|
defaultValue: "Reset to Global",
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{profileName &&
|
||||||
|
profileOverridesSection &&
|
||||||
|
!hasChanges &&
|
||||||
|
!skipSave &&
|
||||||
|
onDeleteProfileSection && (
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsDeleteProfileDialogOpen(true)}
|
||||||
|
variant="outline"
|
||||||
|
disabled={isSaving || disabled}
|
||||||
|
className="flex flex-1 gap-2"
|
||||||
|
>
|
||||||
|
{t("profiles.removeOverride", {
|
||||||
|
ns: "views/settings",
|
||||||
|
defaultValue: "Remove Profile Override",
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{hasChanges && (
|
||||||
|
<Button
|
||||||
|
onClick={handleReset}
|
||||||
|
variant="outline"
|
||||||
|
disabled={isSaving || isSavingAll || disabled}
|
||||||
|
className="flex min-w-36 flex-1 gap-2"
|
||||||
|
>
|
||||||
|
{t("button.undo", { ns: "common", defaultValue: "Undo" })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
onClick={handleReset}
|
onClick={handleSave}
|
||||||
variant="outline"
|
variant="select"
|
||||||
disabled={isSaving || isSavingAll || disabled}
|
disabled={
|
||||||
|
!hasChanges ||
|
||||||
|
hasValidationErrors ||
|
||||||
|
isSaving ||
|
||||||
|
isSavingAll ||
|
||||||
|
disabled
|
||||||
|
}
|
||||||
className="flex min-w-36 flex-1 gap-2"
|
className="flex min-w-36 flex-1 gap-2"
|
||||||
>
|
>
|
||||||
{t("button.undo", { ns: "common", defaultValue: "Undo" })}
|
{isSaving ? (
|
||||||
|
<>
|
||||||
|
<ActivityIndicator className="h-4 w-4" />
|
||||||
|
{skipSave
|
||||||
|
? t("button.applying", {
|
||||||
|
ns: "common",
|
||||||
|
defaultValue: "Applying...",
|
||||||
|
})
|
||||||
|
: t("button.saving", {
|
||||||
|
ns: "common",
|
||||||
|
defaultValue: "Saving...",
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
) : skipSave ? (
|
||||||
|
t("button.apply", { ns: "common", defaultValue: "Apply" })
|
||||||
|
) : (
|
||||||
|
t("button.save", { ns: "common", defaultValue: "Save" })
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
<Button
|
|
||||||
onClick={handleSave}
|
|
||||||
variant="select"
|
|
||||||
disabled={
|
|
||||||
!hasChanges ||
|
|
||||||
hasValidationErrors ||
|
|
||||||
isSaving ||
|
|
||||||
isSavingAll ||
|
|
||||||
disabled
|
|
||||||
}
|
|
||||||
className="flex min-w-36 flex-1 gap-2"
|
|
||||||
>
|
|
||||||
{isSaving ? (
|
|
||||||
<>
|
|
||||||
<ActivityIndicator className="h-4 w-4" />
|
|
||||||
{skipSave
|
|
||||||
? t("button.applying", {
|
|
||||||
ns: "common",
|
|
||||||
defaultValue: "Applying...",
|
|
||||||
})
|
|
||||||
: t("button.saving", {
|
|
||||||
ns: "common",
|
|
||||||
defaultValue: "Saving...",
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
) : skipSave ? (
|
|
||||||
t("button.apply", { ns: "common", defaultValue: "Apply" })
|
|
||||||
) : (
|
|
||||||
t("button.save", { ns: "common", defaultValue: "Save" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<AlertDialog open={isResetDialogOpen} onOpenChange={setIsResetDialogOpen}>
|
<AlertDialog open={isResetDialogOpen} onOpenChange={setIsResetDialogOpen}>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user