75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
import React, { useCallback } from "react";
|
|
import { Box, FormControl, InputLabel, Select, MenuItem, Typography } from "@mui/material";
|
|
import type { FieldConfig } from "../../../types";
|
|
import { FormFieldRenderer } from "../FormFieldRenderer";
|
|
|
|
interface Props {
|
|
field: FieldConfig;
|
|
value: any;
|
|
onChange: (value: any) => void;
|
|
error?: string;
|
|
}
|
|
|
|
export function DiscriminatorField({ field, value, onChange, error }: Props) {
|
|
const options = field.oneOfOptions ?? [];
|
|
const discProp = field.discriminatorProperty ?? "type";
|
|
const currentType = value?.[discProp] ?? "";
|
|
|
|
function defaultFieldValue(field: FieldConfig): any {
|
|
if (field.enumValues) return field.enumValues[0];
|
|
if (field.isArray) return [];
|
|
if (field.type === "object") return {};
|
|
if (field.type === "number" || field.type === "integer") return 0;
|
|
return null;
|
|
}
|
|
|
|
const handleTypeChange = useCallback((e: any) => {
|
|
const newType = e.target.value;
|
|
const option = options.find((o) => o.value === newType);
|
|
const newValue: Record<string, any> = { [discProp]: newType };
|
|
if (option) {
|
|
for (const f of option.fields) {
|
|
newValue[f.name] = defaultFieldValue(f);
|
|
}
|
|
}
|
|
onChange(newValue);
|
|
}, [discProp, onChange, options]);
|
|
|
|
const handleFieldChange = useCallback((fieldName: string, fieldValue: any) => {
|
|
onChange({ ...(value ?? {}), [fieldName]: fieldValue });
|
|
}, [onChange, value]);
|
|
|
|
const activeFields = options.find((o) => o.value === currentType)?.fields ?? [];
|
|
|
|
return (
|
|
<Box>
|
|
<FormControl fullWidth size="small" sx={{ mb: 2 }}>
|
|
<InputLabel>{field.label}</InputLabel>
|
|
<Select
|
|
value={currentType}
|
|
label={field.label}
|
|
onChange={handleTypeChange}
|
|
error={!!error}
|
|
>
|
|
<MenuItem value="" disabled>Select type</MenuItem>
|
|
{options.map((opt) => (
|
|
<MenuItem key={opt.value} value={opt.value}>{opt.label}</MenuItem>
|
|
))}
|
|
</Select>
|
|
</FormControl>
|
|
{currentType && activeFields.length > 0 && (
|
|
<Box sx={{ pl: 2, borderLeft: "2px solid", borderColor: "divider" }}>
|
|
{activeFields.map((f) => (
|
|
<FormFieldRenderer
|
|
key={f.name}
|
|
field={f}
|
|
value={value?.[f.name]}
|
|
onChange={(v) => handleFieldChange(f.name, v)}
|
|
/>
|
|
))}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|