diff --git a/app/package.json b/app/package.json
index a2f2ea5..b19a44e 100644
--- a/app/package.json
+++ b/app/package.json
@@ -3,7 +3,7 @@
"type": "module",
"sideEffects": false,
"bin": "./dist/cli/index.js",
- "version": "0.7.0-rc.8",
+ "version": "0.7.0-rc.11",
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, Remix, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
"homepage": "https://bknd.io",
"repository": {
@@ -55,6 +55,7 @@
"oauth4webapi": "^2.11.1",
"object-path-immutable": "^4.1.2",
"radix-ui": "^1.1.2",
+ "json-schema-to-ts": "^3.1.1",
"swr": "^2.2.5"
},
"devDependencies": {
@@ -75,7 +76,6 @@
"clsx": "^2.1.1",
"esbuild-postcss": "^0.0.4",
"jotai": "^2.10.1",
- "json-schema-to-ts": "^3.1.1",
"open": "^10.1.0",
"openapi-types": "^12.1.3",
"postcss": "^8.4.47",
diff --git a/app/src/ui/components/code/JsonViewer.tsx b/app/src/ui/components/code/JsonViewer.tsx
index 3eaa6c4..e2ac8d8 100644
--- a/app/src/ui/components/code/JsonViewer.tsx
+++ b/app/src/ui/components/code/JsonViewer.tsx
@@ -1,4 +1,3 @@
-import { IconCopy } from "@tabler/icons-react";
import { TbCopy } from "react-icons/tb";
import { JsonView } from "react-json-view-lite";
import { twMerge } from "tailwind-merge";
diff --git a/app/src/ui/components/form/json-schema-form/ArrayField.tsx b/app/src/ui/components/form/json-schema-form/ArrayField.tsx
index 905fb96..1c95b0e 100644
--- a/app/src/ui/components/form/json-schema-form/ArrayField.tsx
+++ b/app/src/ui/components/form/json-schema-form/ArrayField.tsx
@@ -31,7 +31,7 @@ export const ArrayField = ({
onChange={(e: any) => {
// @ts-ignore
const selected = Array.from(e.target.selectedOptions).map((o) => o.value);
- setValue(pointer, selected);
+ setValue(ctx.path, selected);
}}
/>
diff --git a/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx b/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx
index a788a65..aae5329 100644
--- a/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx
+++ b/app/src/ui/components/form/json-schema-form/FieldWrapper.tsx
@@ -1,4 +1,3 @@
-import { Popover } from "@mantine/core";
import { IconBug } from "@tabler/icons-react";
import type { JsonSchema } from "json-schema-library";
import { Children, type ReactElement, type ReactNode, cloneElement, isValidElement } from "react";
@@ -10,6 +9,7 @@ import {
useFormError,
useFormValue
} from "ui/components/form/json-schema-form/Form";
+import { Popover } from "ui/components/overlay/Popover";
import { getLabel } from "./utils";
export type FieldwrapperProps = {
@@ -62,6 +62,7 @@ export function FieldWrapper({
{label} {required && *}
)}
+
{Children.count(children) === 1 && isValidElement(children)
@@ -96,14 +97,15 @@ const FieldDebug = ({
const errors = useFormError(name, { strict: true });
return (
-
- {/* @todo: use radix */}
-
-
-
-
-
+
);
diff --git a/app/src/ui/components/form/json-schema-form/Form.tsx b/app/src/ui/components/form/json-schema-form/Form.tsx
index e7f1a6a..c26cb01 100644
--- a/app/src/ui/components/form/json-schema-form/Form.tsx
+++ b/app/src/ui/components/form/json-schema-form/Form.tsx
@@ -43,24 +43,9 @@ type FormState = {
data: Data;
};
-export type FormProps<
- Schema extends JSONSchema = JSONSchema,
- Data = Schema extends JSONSchema ? FromSchema : any,
- InitialData = Schema extends JSONSchema ? FromSchema : any
-> = Omit, "onChange" | "onSubmit"> & {
- schema: Schema;
- validateOn?: "change" | "submit";
- initialOpts?: LibTemplateOptions;
- ignoreKeys?: string[];
- onChange?: (data: Partial, name: string, value: any) => void;
- onSubmit?: (data: Data) => void | Promise;
- onInvalidSubmit?: (errors: JsonError[], data: Partial) => void;
- hiddenSubmit?: boolean;
- options?: {
- debug?: boolean;
- keepEmpty?: boolean;
- };
- initialValues?: InitialData;
+type FormOptions = {
+ debug?: boolean;
+ keepEmpty?: boolean;
};
export type FormContext = {
@@ -72,7 +57,7 @@ export type FormContext = {
submitting: boolean;
schema: LibJsonSchema;
lib: Draft2019;
- options: FormProps["options"];
+ options: FormOptions;
root: string;
_formStateAtom: PrimitiveAtom>;
};
@@ -81,8 +66,8 @@ const FormContext = createContext>(undefined!);
FormContext.displayName = "FormContext";
export function Form<
- Schema extends JSONSchema = JSONSchema,
- Data = Schema extends JSONSchema ? FromSchema : any
+ const Schema extends JSONSchema,
+ const Data = Schema extends JSONSchema ? FromSchema : any
>({
schema: _schema,
initialValues: _initialValues,
@@ -96,7 +81,18 @@ export function Form<
ignoreKeys = [],
options = {},
...props
-}: FormProps) {
+}: Omit, "onChange" | "onSubmit"> & {
+ schema: Schema;
+ validateOn?: "change" | "submit";
+ initialOpts?: LibTemplateOptions;
+ ignoreKeys?: string[];
+ onChange?: (data: Partial, name: string, value: any) => void;
+ onSubmit?: (data: Data) => void | Promise;
+ onInvalidSubmit?: (errors: JsonError[], data: Partial) => void;
+ hiddenSubmit?: boolean;
+ options?: FormOptions;
+ initialValues?: Schema extends JSONSchema ? FromSchema : never;
+}) {
const [schema, initial] = omitSchema(_schema, ignoreKeys, _initialValues);
const lib = useMemo(() => new Draft2019(schema), [JSON.stringify(schema)]);
const initialValues = initial ?? lib.getTemplate(undefined, schema, initialOpts);
diff --git a/app/src/ui/components/overlay/Popover.tsx b/app/src/ui/components/overlay/Popover.tsx
index 22c9e5f..09e71bd 100644
--- a/app/src/ui/components/overlay/Popover.tsx
+++ b/app/src/ui/components/overlay/Popover.tsx
@@ -1,7 +1,7 @@
import { useClickOutside } from "@mantine/hooks";
-import { type ReactElement, cloneElement, useState } from "react";
+import { type ComponentPropsWithoutRef, type ReactElement, cloneElement, useState } from "react";
import { twMerge } from "tailwind-merge";
-import { useEvent } from "../../hooks/use-event";
+import { useEvent } from "ui/hooks/use-event";
export type PopoverProps = {
className?: string;
@@ -10,6 +10,7 @@ export type PopoverProps = {
backdrop?: boolean;
target: (props: { toggle: () => void }) => ReactElement;
children: ReactElement<{ onClick: () => void }>;
+ overlayProps?: ComponentPropsWithoutRef<"div">;
};
export function Popover({
@@ -18,20 +19,21 @@ export function Popover({
defaultOpen = false,
backdrop = false,
position = "bottom-start",
- className,
+ overlayProps,
+ className
}: PopoverProps) {
const [open, setOpen] = useState(defaultOpen);
const clickoutsideRef = useClickOutside(() => setOpen(false));
const toggle = useEvent((delay: number = 50) =>
- setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0),
+ setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0)
);
const pos = {
"bottom-start": "mt-1 top-[100%]",
"bottom-end": "right-0 top-[100%] mt-1",
"top-start": "bottom-[100%] mb-1",
- "top-end": "bottom-[100%] right-0 mb-1",
+ "top-end": "bottom-[100%] right-0 mb-1"
}[position];
return (
@@ -43,9 +45,11 @@ export function Popover({
{cloneElement(children as any, { onClick: toggle })}
{open && (
{target({ toggle })}
diff --git a/app/src/ui/index.ts b/app/src/ui/index.ts
index 6631083..33a1c59 100644
--- a/app/src/ui/index.ts
+++ b/app/src/ui/index.ts
@@ -1,2 +1,3 @@
export { default as Admin, type BkndAdminProps } from "./Admin";
export * from "./components/form/json-schema-form";
+export { JsonViewer } from "./components/code/JsonViewer";
diff --git a/app/src/ui/routes/test/tests/json-schema-form3.tsx b/app/src/ui/routes/test/tests/json-schema-form3.tsx
index 9edd99f..0640872 100644
--- a/app/src/ui/routes/test/tests/json-schema-form3.tsx
+++ b/app/src/ui/routes/test/tests/json-schema-form3.tsx
@@ -30,7 +30,7 @@ const schema2 = {
}
},
required: ["age"]
-};
+} as const satisfies JSONSchema;
export default function JsonSchemaForm3() {
const { schema: _schema, config } = useBknd();
@@ -46,7 +46,9 @@ export default function JsonSchemaForm3() {
return (
- {/*
+ />*/}
{/*