mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-24 04:39:02 +03:00
fix nullable fields
This commit is contained in:
@@ -2,24 +2,19 @@
|
||||
import type { StrictRJSFSchema } from "@rjsf/utils";
|
||||
|
||||
/**
|
||||
* Checks if a schema is anyOf with exactly [PrimitiveType, null]
|
||||
* where the primitive has no additional constraints
|
||||
* Checks if a schema is anyOf/oneOf with exactly [Type, null].
|
||||
* This indicates a nullable field in Pydantic schemas.
|
||||
*/
|
||||
export function isSimpleNullableField(schema: StrictRJSFSchema): boolean {
|
||||
if (
|
||||
!schema.anyOf ||
|
||||
!Array.isArray(schema.anyOf) ||
|
||||
schema.anyOf.length !== 2
|
||||
) {
|
||||
export function isNullableUnionSchema(schema: StrictRJSFSchema): boolean {
|
||||
const union = schema.anyOf ?? schema.oneOf;
|
||||
if (!union || !Array.isArray(union) || union.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const items = schema.anyOf;
|
||||
let hasNull = false;
|
||||
let simpleType: StrictRJSFSchema | null = null;
|
||||
let nonNullCount = 0;
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const item of items) {
|
||||
for (const item of union) {
|
||||
if (typeof item !== "object" || item === null) {
|
||||
return false;
|
||||
}
|
||||
@@ -28,22 +23,19 @@ export function isSimpleNullableField(schema: StrictRJSFSchema): boolean {
|
||||
|
||||
if (itemSchema.type === "null") {
|
||||
hasNull = true;
|
||||
} else if (
|
||||
itemSchema.type &&
|
||||
!("$ref" in itemSchema) &&
|
||||
!("additionalProperties" in itemSchema) &&
|
||||
!("items" in itemSchema) &&
|
||||
!("pattern" in itemSchema) &&
|
||||
!("minimum" in itemSchema) &&
|
||||
!("maximum" in itemSchema) &&
|
||||
!("exclusiveMinimum" in itemSchema) &&
|
||||
!("exclusiveMaximum" in itemSchema)
|
||||
) {
|
||||
simpleType = itemSchema;
|
||||
} else {
|
||||
nonNullCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return hasNull && simpleType !== null;
|
||||
return hasNull && nonNullCount === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backwards-compatible alias for nullable fields
|
||||
*/
|
||||
export function isSimpleNullableField(schema: StrictRJSFSchema): boolean {
|
||||
return isNullableUnionSchema(schema);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,12 +44,13 @@ export function isSimpleNullableField(schema: StrictRJSFSchema): boolean {
|
||||
export function getNonNullSchema(
|
||||
schema: StrictRJSFSchema,
|
||||
): StrictRJSFSchema | null {
|
||||
if (!schema.anyOf || !Array.isArray(schema.anyOf)) {
|
||||
const union = schema.anyOf ?? schema.oneOf;
|
||||
if (!union || !Array.isArray(union)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
(schema.anyOf.find(
|
||||
(union.find(
|
||||
(item) =>
|
||||
typeof item === "object" &&
|
||||
item !== null &&
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Field Template - wraps each form field with label and description
|
||||
import type { FieldTemplateProps } from "@rjsf/utils";
|
||||
import type { FieldTemplateProps, StrictRJSFSchema } from "@rjsf/utils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { isNullableUnionSchema } from "../fields/nullableUtils";
|
||||
|
||||
/**
|
||||
* Build the i18n translation key path for nested fields using the field path
|
||||
@@ -47,6 +48,8 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
// Boolean fields (switches) render label inline
|
||||
const isBoolean = schema.type === "boolean";
|
||||
|
||||
const isNullableUnion = isNullableUnionSchema(schema as StrictRJSFSchema);
|
||||
|
||||
// Get translation path for this field
|
||||
const translationPath = buildTranslationPath(fieldPathId.path);
|
||||
|
||||
@@ -99,8 +102,9 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
isAdvanced && "border-l-2 border-muted pl-4",
|
||||
isBoolean && "flex items-center justify-between gap-4",
|
||||
)}
|
||||
data-field-id={translationPath}
|
||||
>
|
||||
{displayLabel && finalLabel && !isBoolean && (
|
||||
{displayLabel && finalLabel && !isBoolean && !isNullableUnion && (
|
||||
<Label
|
||||
htmlFor={id}
|
||||
className={cn(
|
||||
@@ -122,7 +126,7 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
{required && <span className="ml-1 text-destructive">*</span>}
|
||||
</Label>
|
||||
)}
|
||||
{finalDescription && (
|
||||
{finalDescription && !isNullableUnion && (
|
||||
<p className="max-w-md text-sm text-muted-foreground">
|
||||
{String(finalDescription)}
|
||||
</p>
|
||||
@@ -132,10 +136,8 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{finalDescription && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{String(finalDescription)}
|
||||
</p>
|
||||
{finalDescription && !isNullableUnion && (
|
||||
<p className="text-sm text-muted-foreground">{finalDescription}</p>
|
||||
)}
|
||||
{children}
|
||||
</>
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
StrictRJSFSchema,
|
||||
FormContextType,
|
||||
} from "@rjsf/utils";
|
||||
import { isSimpleNullableField } from "../fields/nullableUtils";
|
||||
import { isNullableUnionSchema } from "../fields/nullableUtils";
|
||||
|
||||
/**
|
||||
* Custom MultiSchemaFieldTemplate that:
|
||||
@@ -23,7 +23,7 @@ export function MultiSchemaFieldTemplate<
|
||||
const { schema, selector, optionSchemaField } = props;
|
||||
|
||||
// Check if this is a simple nullable field that should be handled specially
|
||||
if (isSimpleNullableField(schema)) {
|
||||
if (isNullableUnionSchema(schema)) {
|
||||
// For simple nullable fields, just render the field directly without the dropdown selector
|
||||
// This handles the case where empty input = null
|
||||
return <>{optionSchemaField}</>;
|
||||
|
||||
Reference in New Issue
Block a user