mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 12:56:05 +00:00
enhance form field components and add JsonEditor support
- Updated `ObjectField`, `ArrayField`, and `FieldWrapper` components to improve flexibility and integration options by supporting additional props like `wrapperProps`. - Added `JsonEditor` for enhanced object editing capabilities with state management and safety checks. - Refactored utility functions and error handling for improved stability and developer experience. - Introduced new test cases to validate `JsonEditor` functionality and schema-based forms handling.
This commit is contained in:
@@ -1,9 +1,37 @@
|
|||||||
import { Suspense, lazy } from "react";
|
import { Suspense, lazy, useState } from "react";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import type { CodeEditorProps } from "./CodeEditor";
|
import type { CodeEditorProps } from "./CodeEditor";
|
||||||
const CodeEditor = lazy(() => import("./CodeEditor"));
|
const CodeEditor = lazy(() => import("./CodeEditor"));
|
||||||
|
|
||||||
export function JsonEditor({ editable, className, ...props }: CodeEditorProps) {
|
export type JsonEditorProps = Omit<CodeEditorProps, "value" | "onChange"> & {
|
||||||
|
value?: object;
|
||||||
|
onChange?: (value: object) => void;
|
||||||
|
emptyAs?: "null" | "undefined";
|
||||||
|
};
|
||||||
|
|
||||||
|
export function JsonEditor({
|
||||||
|
editable,
|
||||||
|
className,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
emptyAs = "undefined",
|
||||||
|
...props
|
||||||
|
}: JsonEditorProps) {
|
||||||
|
const [editorValue, setEditorValue] = useState<string | null | undefined>(
|
||||||
|
JSON.stringify(value, null, 2),
|
||||||
|
);
|
||||||
|
const handleChange = (given: string) => {
|
||||||
|
const value = given === "" ? (emptyAs === "null" ? null : undefined) : given;
|
||||||
|
try {
|
||||||
|
setEditorValue(value);
|
||||||
|
onChange?.(value ? JSON.parse(value) : value);
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
const handleBlur = (e) => {
|
||||||
|
setEditorValue(JSON.stringify(value, null, 2));
|
||||||
|
onBlur?.(e);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
@@ -14,6 +42,9 @@ export function JsonEditor({ editable, className, ...props }: CodeEditorProps) {
|
|||||||
)}
|
)}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
_extensions={{ json: true }}
|
_extensions={{ json: true }}
|
||||||
|
value={editorValue ?? undefined}
|
||||||
|
onChange={handleChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ export const Group = <E extends ElementType = "div">({
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
{...props}
|
{...props}
|
||||||
|
data-role="group"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex flex-col gap-1.5",
|
"flex flex-col gap-1.5 w-full",
|
||||||
as === "fieldset" && "border border-primary/10 p-3 rounded-md",
|
as === "fieldset" && "border border-primary/10 p-3 rounded-md",
|
||||||
as === "fieldset" && error && "border-red-500",
|
as === "fieldset" && error && "border-red-500",
|
||||||
error && "text-red-500",
|
error && "text-red-500",
|
||||||
|
|||||||
@@ -5,19 +5,29 @@ import { Button } from "ui/components/buttons/Button";
|
|||||||
import { IconButton } from "ui/components/buttons/IconButton";
|
import { IconButton } from "ui/components/buttons/IconButton";
|
||||||
import { Dropdown } from "ui/components/overlay/Dropdown";
|
import { Dropdown } from "ui/components/overlay/Dropdown";
|
||||||
import { useEvent } from "ui/hooks/use-event";
|
import { useEvent } from "ui/hooks/use-event";
|
||||||
import { FieldComponent } from "./Field";
|
import { Field, FieldComponent, type FieldProps } from "./Field";
|
||||||
import { FieldWrapper } from "./FieldWrapper";
|
import { FieldWrapper, type FieldwrapperProps } from "./FieldWrapper";
|
||||||
import { useDerivedFieldContext, useFormValue } from "./Form";
|
import { FormContextOverride, useDerivedFieldContext, useFormValue } from "./Form";
|
||||||
import { coerce, getMultiSchema, getMultiSchemaMatched, isEqual, suffixPath } from "./utils";
|
import { coerce, getMultiSchema, getMultiSchemaMatched, isEqual, suffixPath } from "./utils";
|
||||||
|
|
||||||
export const ArrayField = ({ path = "" }: { path?: string }) => {
|
export type ArrayFieldProps = {
|
||||||
|
path?: string;
|
||||||
|
labelAdd?: string;
|
||||||
|
wrapperProps?: Omit<FieldwrapperProps, "name" | "children">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ArrayField = ({
|
||||||
|
path = "",
|
||||||
|
labelAdd = "Add",
|
||||||
|
wrapperProps = { wrapper: "fieldset" },
|
||||||
|
}: ArrayFieldProps) => {
|
||||||
const { setValue, pointer, required, schema, ...ctx } = useDerivedFieldContext(path);
|
const { setValue, pointer, required, schema, ...ctx } = useDerivedFieldContext(path);
|
||||||
if (!schema || typeof schema === "undefined") return `ArrayField(${path}): no schema ${pointer}`;
|
if (!schema || typeof schema === "undefined") return `ArrayField(${path}): no schema ${pointer}`;
|
||||||
|
|
||||||
// if unique items with enum
|
// if unique items with enum
|
||||||
if (schema.uniqueItems && typeof schema.items === "object" && "enum" in schema.items) {
|
if (schema.uniqueItems && typeof schema.items === "object" && "enum" in schema.items) {
|
||||||
return (
|
return (
|
||||||
<FieldWrapper name={path} schema={schema} wrapper="fieldset">
|
<FieldWrapper {...wrapperProps} name={path} schema={schema}>
|
||||||
<FieldComponent
|
<FieldComponent
|
||||||
required
|
required
|
||||||
name={path}
|
name={path}
|
||||||
@@ -35,7 +45,7 @@ export const ArrayField = ({ path = "" }: { path?: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldWrapper name={path} schema={schema} wrapper="fieldset">
|
<FieldWrapper {...wrapperProps} name={path} schema={schema}>
|
||||||
<ArrayIterator name={path}>
|
<ArrayIterator name={path}>
|
||||||
{({ value }) =>
|
{({ value }) =>
|
||||||
value?.map((v, index: number) => (
|
value?.map((v, index: number) => (
|
||||||
@@ -44,17 +54,21 @@ export const ArrayField = ({ path = "" }: { path?: string }) => {
|
|||||||
}
|
}
|
||||||
</ArrayIterator>
|
</ArrayIterator>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<ArrayAdd path={path} schema={schema} />
|
<ArrayAdd path={path} schema={schema} label={labelAdd} />
|
||||||
</div>
|
</div>
|
||||||
</FieldWrapper>
|
</FieldWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArrayItem = memo(({ path, index, schema }: any) => {
|
const ArrayItem = memo(({ path, index, schema }: any) => {
|
||||||
const { value, ...ctx } = useDerivedFieldContext(path, (ctx) => {
|
const {
|
||||||
|
value,
|
||||||
|
path: absolutePath,
|
||||||
|
...ctx
|
||||||
|
} = useDerivedFieldContext(path, (ctx) => {
|
||||||
return ctx.value?.[index];
|
return ctx.value?.[index];
|
||||||
});
|
});
|
||||||
const itemPath = suffixPath(path, index);
|
const itemPath = suffixPath(absolutePath, index);
|
||||||
let subschema = schema.items;
|
let subschema = schema.items;
|
||||||
const itemsMultiSchema = getMultiSchema(schema.items);
|
const itemsMultiSchema = getMultiSchema(schema.items);
|
||||||
if (itemsMultiSchema) {
|
if (itemsMultiSchema) {
|
||||||
@@ -62,10 +76,6 @@ const ArrayItem = memo(({ path, index, schema }: any) => {
|
|||||||
subschema = _subschema;
|
subschema = _subschema;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdate = useEvent((pointer: string, value: any) => {
|
|
||||||
ctx.setValue(pointer, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleDelete = useEvent((pointer: string) => {
|
const handleDelete = useEvent((pointer: string) => {
|
||||||
ctx.deleteValue(pointer);
|
ctx.deleteValue(pointer);
|
||||||
});
|
});
|
||||||
@@ -76,21 +86,26 @@ const ArrayItem = memo(({ path, index, schema }: any) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={itemPath} className="flex flex-row gap-2">
|
<FormContextOverride prefix={itemPath} schema={subschema!}>
|
||||||
<FieldComponent
|
<div className="flex flex-row gap-2 w-full">
|
||||||
name={itemPath}
|
{/* another wrap is required for primitive schemas */}
|
||||||
schema={subschema!}
|
<AnotherField label={false} />
|
||||||
value={value}
|
{DeleteButton}
|
||||||
onChange={(e) => {
|
</div>
|
||||||
handleUpdate(itemPath, coerce(e.target.value, subschema!));
|
</FormContextOverride>
|
||||||
}}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
{DeleteButton}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}, isEqual);
|
}, isEqual);
|
||||||
|
|
||||||
|
const AnotherField = (props: Partial<FieldProps>) => {
|
||||||
|
const { value } = useFormValue("");
|
||||||
|
|
||||||
|
const inputProps = {
|
||||||
|
// @todo: check, potentially just provide value
|
||||||
|
value: ["string", "number", "boolean"].includes(typeof value) ? value : undefined,
|
||||||
|
};
|
||||||
|
return <Field name={""} label={false} {...props} inputProps={inputProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
const ArrayIterator = memo(
|
const ArrayIterator = memo(
|
||||||
({ name, children }: any) => {
|
({ name, children }: any) => {
|
||||||
return children(useFormValue(name));
|
return children(useFormValue(name));
|
||||||
@@ -98,19 +113,25 @@ const ArrayIterator = memo(
|
|||||||
(prev, next) => prev.value?.length === next.value?.length,
|
(prev, next) => prev.value?.length === next.value?.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
const ArrayAdd = ({ schema, path }: { schema: JsonSchema; path: string }) => {
|
const ArrayAdd = ({
|
||||||
|
schema,
|
||||||
|
path: _path,
|
||||||
|
label = "Add",
|
||||||
|
}: { schema: JsonSchema; path: string; label?: string }) => {
|
||||||
const {
|
const {
|
||||||
setValue,
|
setValue,
|
||||||
value: { currentIndex },
|
value: { currentIndex },
|
||||||
|
path,
|
||||||
...ctx
|
...ctx
|
||||||
} = useDerivedFieldContext(path, (ctx) => {
|
} = useDerivedFieldContext(_path, (ctx) => {
|
||||||
return { currentIndex: ctx.value?.length ?? 0 };
|
return { currentIndex: ctx.value?.length ?? 0 };
|
||||||
});
|
});
|
||||||
const itemsMultiSchema = getMultiSchema(schema.items);
|
const itemsMultiSchema = getMultiSchema(schema.items);
|
||||||
|
const options = { addOptionalProps: true };
|
||||||
|
|
||||||
function handleAdd(template?: any) {
|
function handleAdd(template?: any) {
|
||||||
const newPath = suffixPath(path, currentIndex);
|
const newPath = suffixPath(path, currentIndex);
|
||||||
setValue(newPath, template ?? ctx.lib.getTemplate(undefined, schema!.items));
|
setValue(newPath, template ?? ctx.lib.getTemplate(undefined, schema!.items, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemsMultiSchema) {
|
if (itemsMultiSchema) {
|
||||||
@@ -121,14 +142,14 @@ const ArrayAdd = ({ schema, path }: { schema: JsonSchema; path: string }) => {
|
|||||||
}}
|
}}
|
||||||
items={itemsMultiSchema.map((s, i) => ({
|
items={itemsMultiSchema.map((s, i) => ({
|
||||||
label: s!.title ?? `Option ${i + 1}`,
|
label: s!.title ?? `Option ${i + 1}`,
|
||||||
onClick: () => handleAdd(ctx.lib.getTemplate(undefined, s!)),
|
onClick: () => handleAdd(ctx.lib.getTemplate(undefined, s!, options)),
|
||||||
}))}
|
}))}
|
||||||
onClickItem={console.log}
|
onClickItem={console.log}
|
||||||
>
|
>
|
||||||
<Button IconLeft={IconLibraryPlus}>Add</Button>
|
<Button IconLeft={IconLibraryPlus}>{label}</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Button onClick={() => handleAdd()}>Add</Button>;
|
return <Button onClick={() => handleAdd()}>{label}</Button>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const FieldImpl = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isType(schema.type, "object")) {
|
if (isType(schema.type, "object")) {
|
||||||
return <ObjectField path={name} />;
|
return <ObjectField path={name} wrapperProps={props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isType(schema.type, "array")) {
|
if (isType(schema.type, "array")) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from "ui/components/form/json-schema-form/Form";
|
} from "ui/components/form/json-schema-form/Form";
|
||||||
import { Popover } from "ui/components/overlay/Popover";
|
import { Popover } from "ui/components/overlay/Popover";
|
||||||
import { getLabel } from "./utils";
|
import { getLabel } from "./utils";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export type FieldwrapperProps = {
|
export type FieldwrapperProps = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -25,6 +26,7 @@ export type FieldwrapperProps = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
descriptionPlacement?: "top" | "bottom";
|
descriptionPlacement?: "top" | "bottom";
|
||||||
fieldId?: string;
|
fieldId?: string;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FieldWrapper({
|
export function FieldWrapper({
|
||||||
@@ -38,6 +40,7 @@ export function FieldWrapper({
|
|||||||
descriptionPlacement = "bottom",
|
descriptionPlacement = "bottom",
|
||||||
children,
|
children,
|
||||||
fieldId,
|
fieldId,
|
||||||
|
className,
|
||||||
...props
|
...props
|
||||||
}: FieldwrapperProps) {
|
}: FieldwrapperProps) {
|
||||||
const errors = useFormError(name, { strict: true });
|
const errors = useFormError(name, { strict: true });
|
||||||
@@ -60,7 +63,7 @@ export function FieldWrapper({
|
|||||||
<Formy.Group
|
<Formy.Group
|
||||||
error={errors.length > 0}
|
error={errors.length > 0}
|
||||||
as={wrapper === "fieldset" ? "fieldset" : "div"}
|
as={wrapper === "fieldset" ? "fieldset" : "div"}
|
||||||
className={hidden ? "hidden" : "relative"}
|
className={twMerge(hidden ? "hidden" : "relative", className)}
|
||||||
>
|
>
|
||||||
{errorPlacement === "top" && Errors}
|
{errorPlacement === "top" && Errors}
|
||||||
<FieldDebug name={name} schema={schema} required={required} />
|
<FieldDebug name={name} schema={schema} required={required} />
|
||||||
@@ -76,7 +79,7 @@ export function FieldWrapper({
|
|||||||
)}
|
)}
|
||||||
{descriptionPlacement === "top" && Description}
|
{descriptionPlacement === "top" && Description}
|
||||||
|
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row flex-grow gap-2">
|
||||||
<div className="flex flex-1 flex-col gap-3">
|
<div className="flex flex-1 flex-col gap-3">
|
||||||
{Children.count(children) === 1 && isValidElement(children)
|
{Children.count(children) === 1 && isValidElement(children)
|
||||||
? cloneElement(children, {
|
? cloneElement(children, {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export type ObjectFieldProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ObjectField = ({ path = "", label: _label, wrapperProps = {} }: ObjectFieldProps) => {
|
export const ObjectField = ({ path = "", label: _label, wrapperProps = {} }: ObjectFieldProps) => {
|
||||||
const { schema, ...ctx } = useDerivedFieldContext(path);
|
const { schema } = useDerivedFieldContext(path);
|
||||||
if (!isTypeSchema(schema)) return `ObjectField "${path}": no schema`;
|
if (!isTypeSchema(schema)) return `ObjectField "${path}": no schema`;
|
||||||
const properties = Object.entries(schema.properties ?? {}) as [string, JSONSchema][];
|
const properties = Object.entries(schema.properties ?? {}) as [string, JSONSchema][];
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export const ObjectField = ({ path = "", label: _label, wrapperProps = {} }: Obj
|
|||||||
{...wrapperProps}
|
{...wrapperProps}
|
||||||
>
|
>
|
||||||
{properties.length === 0 ? (
|
{properties.length === 0 ? (
|
||||||
<i className="opacity-50">No properties</i>
|
<ObjectJsonField path={path} />
|
||||||
) : (
|
) : (
|
||||||
properties.map(([prop, schema]) => {
|
properties.map(([prop, schema]) => {
|
||||||
const name = [path, prop].filter(Boolean).join(".");
|
const name = [path, prop].filter(Boolean).join(".");
|
||||||
@@ -40,3 +40,9 @@ export const ObjectField = ({ path = "", label: _label, wrapperProps = {} }: Obj
|
|||||||
</FieldWrapper>
|
</FieldWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ObjectJsonField = ({ path }: { path: string }) => {
|
||||||
|
const { value } = useFormValue(path);
|
||||||
|
const { setValue, path: absolutePath } = useDerivedFieldContext(path);
|
||||||
|
return <JsonEditor value={value} onChange={(value) => setValue(absolutePath, value)} />;
|
||||||
|
};
|
||||||
|
|||||||
@@ -67,18 +67,23 @@ export function isRequired(lib: Draft, pointer: string, schema: JsonSchema, data
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const childSchema = lib.getSchema({ pointer, data, schema });
|
try {
|
||||||
if (typeof childSchema === "object" && "const" in childSchema) {
|
const childSchema = lib.getSchema({ pointer, data, schema });
|
||||||
return true;
|
if (typeof childSchema === "object" && "const" in childSchema) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentPointer = getParentPointer(pointer);
|
const parentPointer = getParentPointer(pointer);
|
||||||
const parentSchema = lib.getSchema({ pointer: parentPointer, data });
|
const parentSchema = lib.getSchema({ pointer: parentPointer, data });
|
||||||
const required = parentSchema?.required?.includes(pointer.split("/").pop()!);
|
const l = pointer.split("/").pop();
|
||||||
|
const required = parentSchema?.required?.includes(l);
|
||||||
|
|
||||||
return !!required;
|
return !!required;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("isRequired", { pointer, schema, data, e });
|
console.warn("isRequired", { pointer, schema, data, e });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
app/src/ui/routes/test/tests/code-editor-test.tsx
Normal file
13
app/src/ui/routes/test/tests/code-editor-test.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { JsonEditor } from "ui/components/code/JsonEditor";
|
||||||
|
import { JsonViewer } from "ui/components/code/JsonViewer";
|
||||||
|
|
||||||
|
export default function CodeEditorTest() {
|
||||||
|
const [value, setValue] = useState({});
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col p-4">
|
||||||
|
<JsonEditor value={value} onChange={setValue} />
|
||||||
|
<JsonViewer json={value} expand={9} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -56,6 +56,14 @@ const authSchema = {
|
|||||||
},
|
},
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
const objectCodeSchema = {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
name: { type: "string" },
|
||||||
|
config: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const formOptions = {
|
const formOptions = {
|
||||||
debug: true,
|
debug: true,
|
||||||
};
|
};
|
||||||
@@ -77,6 +85,45 @@ export default function JsonSchemaForm3() {
|
|||||||
{/* <Form schema={_schema.auth.toJSON()} options={formOptions} /> */}
|
{/* <Form schema={_schema.auth.toJSON()} options={formOptions} /> */}
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
|
schema={objectCodeSchema as any}
|
||||||
|
options={formOptions}
|
||||||
|
initialValues={{
|
||||||
|
name: "Peter",
|
||||||
|
config: {
|
||||||
|
foo: "bar",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Form
|
||||||
|
schema={s
|
||||||
|
.object({
|
||||||
|
name: s.string(),
|
||||||
|
props: s.array(
|
||||||
|
s.object({
|
||||||
|
age: s.number(),
|
||||||
|
config: s.object({}),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.toJSON()}
|
||||||
|
options={formOptions}
|
||||||
|
initialValues={{
|
||||||
|
name: "Peter",
|
||||||
|
props: [{ age: 20, config: { foo: "bar" } }],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form
|
||||||
|
schema={s
|
||||||
|
.object({
|
||||||
|
name: s.string(),
|
||||||
|
props: s.array(s.anyOf([s.string(), s.number()])),
|
||||||
|
})
|
||||||
|
.toJSON()}
|
||||||
|
options={formOptions}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* <Form
|
||||||
options={{
|
options={{
|
||||||
anyOfNoneSelectedMode: "first",
|
anyOfNoneSelectedMode: "first",
|
||||||
debug: true,
|
debug: true,
|
||||||
@@ -98,7 +145,7 @@ export default function JsonSchemaForm3() {
|
|||||||
.optional(),
|
.optional(),
|
||||||
})
|
})
|
||||||
.toJSON()}
|
.toJSON()}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
{/*<Form
|
{/*<Form
|
||||||
onChange={(data) => console.log("change", data)}
|
onChange={(data) => console.log("change", data)}
|
||||||
|
|||||||
Reference in New Issue
Block a user