diff --git a/web/src/components/config-form/section-configs/record.ts b/web/src/components/config-form/section-configs/record.ts index d1c2a94e64..d4dd481b86 100644 --- a/web/src/components/config-form/section-configs/record.ts +++ b/web/src/components/config-form/section-configs/record.ts @@ -44,7 +44,14 @@ const record: SectionConfigOverrides = { hiddenFields: ["enabled_in_config", "sync_recordings"], advancedFields: ["expire_interval", "preview", "export"], uiSchema: { + continuous: { + "ui:options": { defaultOpen: true, disableCollapsible: true }, + }, + motion: { + "ui:options": { defaultOpen: true, disableCollapsible: true }, + }, export: { + "ui:options": { defaultOpen: true, disableCollapsible: true }, hwaccel_args: { "ui:widget": "FfmpegArgsWidget", "ui:options": { @@ -59,9 +66,12 @@ const record: SectionConfigOverrides = { "detections.retain.mode": { "ui:options": { enumI18nPrefix: "retainMode" }, }, - "preview.quality": { - "ui:options": { - enumI18nPrefix: "previewQuality", + preview: { + "ui:options": { defaultOpen: true, disableCollapsible: true }, + quality: { + "ui:options": { + enumI18nPrefix: "previewQuality", + }, }, }, }, diff --git a/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx b/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx index 9d703b3c8d..009a53b588 100644 --- a/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx +++ b/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx @@ -156,7 +156,8 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { }; const hasModifiedDescendants = checkSubtreeModified(fieldPath); - const [isOpen, setIsOpen] = useState(hasModifiedDescendants); + const defaultOpen = uiSchema?.["ui:options"]?.defaultOpen === true; + const [isOpen, setIsOpen] = useState(hasModifiedDescendants || defaultOpen); const resetKey = `${formContext?.level ?? "global"}::${ formContext?.cameraName ?? "global" }`; @@ -192,6 +193,8 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { (uiSchema?.["ui:groups"] as Record | undefined) || {}; const disableNestedCard = uiSchema?.["ui:options"]?.disableNestedCard === true; + const disableCollapsible = + uiSchema?.["ui:options"]?.disableCollapsible === true; const isHiddenProp = (prop: (typeof properties)[number]) => (prop.content.props as RjsfElementProps).uiSchema?.["ui:widget"] === @@ -228,10 +231,10 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { useEffect(() => { if (lastResetKeyRef.current !== resetKey) { lastResetKeyRef.current = resetKey; - setIsOpen(hasModifiedDescendants); + setIsOpen(hasModifiedDescendants || defaultOpen); setShowAdvanced(hasModifiedAdvanced); } - }, [resetKey, hasModifiedDescendants, hasModifiedAdvanced]); + }, [resetKey, hasModifiedDescendants, hasModifiedAdvanced, defaultOpen]); const { children } = props as ObjectFieldTemplateProps & { children?: ReactNode; }; @@ -458,6 +461,75 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { ); } + // Label/description/docs header shared by the collapsible and static layouts. + const cardHeaderContent = ( +
+ + {inferredLabel} + {objectRequiresRestart && } + + {inferredDescription && ( +

+ {inferredDescription} +

+ )} + {fieldDocsUrl && ( +
+ e.stopPropagation()} + > + {t("readTheDocumentation", { ns: "common" })} + + +
+ )} +
+ ); + + // Body shared by the collapsible and static layouts. + const cardBody = hasCustomChildren ? ( + children + ) : ( + <> + {renderGroupedFields(regularProps)} + + + + {renderGroupedFields(advancedProps)} + + + ); + + // Static (non-collapsible) card: keep the labeled header, always show content. + if (disableCollapsible) { + return ( + + {cardHeaderContent} + {cardBody} + + ); + } + // Nested objects render as collapsible cards return ( @@ -465,38 +537,7 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
-
- - {inferredLabel} - {objectRequiresRestart && ( - - )} - - {inferredDescription && ( -

- {inferredDescription} -

- )} - {fieldDocsUrl && ( -
- e.stopPropagation()} - > - {t("readTheDocumentation", { ns: "common" })} - - -
- )} -
+ {cardHeaderContent} {isOpen ? ( ) : ( @@ -506,31 +547,7 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { - - {hasCustomChildren ? ( - children - ) : ( - <> - {renderGroupedFields(regularProps)} - - - - {renderGroupedFields(advancedProps)} - - - )} - + {cardBody}