diff --git a/packages/fabrix/src/customRenderer.tsx b/packages/fabrix/src/customRenderer.tsx
index 68cc9b8a..d9672f79 100644
--- a/packages/fabrix/src/customRenderer.tsx
+++ b/packages/fabrix/src/customRenderer.tsx
@@ -6,6 +6,7 @@ import {
getComponentFn,
getComponentRendererFn,
} from "@renderer";
+import { CustomComponentFormRenderer } from "@renderers/custom/form";
import { CustomComponentTableRenderer } from "@renderers/custom/table";
export type ComponentRendererProps<
@@ -43,6 +44,22 @@ export const FabrixCustomComponent = (
/>
);
}
+ case "form": {
+ ensureFieldType(fieldConfig, "form");
+ return (
+
+ );
+ }
default: {
throw new Error(`Unsupported component type: ${componentEntry.type}`);
}
diff --git a/packages/fabrix/src/renderer.tsx b/packages/fabrix/src/renderer.tsx
index 03f626d2..733ac2d9 100644
--- a/packages/fabrix/src/renderer.tsx
+++ b/packages/fabrix/src/renderer.tsx
@@ -1,14 +1,10 @@
import { DirectiveNode, DocumentNode, OperationTypeNode, parse } from "graphql";
import { ReactNode, useCallback, useContext, useMemo } from "react";
import { findDirective, parseDirectiveArguments } from "@directive";
-import { ViewRenderer } from "@renderers/fields";
-import { FormRenderer } from "@renderers/form";
+import { DefaultViewRenderer } from "@renderers/fields";
+import { DefaultFormRenderer } from "@renderers/form";
import { FabrixContext, FabrixContextType } from "@context";
-import {
- FabrixComponentFieldsRenderer,
- Loader,
- resolveFieldTypesFromTypename,
-} from "@renderers/shared";
+import { FabrixComponentFieldsRenderer, Loader } from "@renderers/shared";
import { directiveSchemaMap } from "@directive/schema";
import { mergeFieldConfigs } from "@readers/shared";
import { buildDefaultViewFieldConfigs, viewFieldMerger } from "@readers/field";
@@ -276,16 +272,13 @@ export const FabrixComponent = (props: FabrixComponentProps) => {
(
field: FieldConfig,
data: Value,
- context: FabrixContextType,
componentFieldsRenderer?: FabrixComponentFieldsRenderer,
) => {
const commonProps = {
- context,
rootField: {
name: field.name,
fields: field.configs.fields,
data,
- type: resolveFieldTypesFromTypename(context, data),
document: field.document,
className: props.contentClassName,
componentFieldsRenderer,
@@ -293,9 +286,9 @@ export const FabrixComponent = (props: FabrixComponentProps) => {
};
switch (field.type) {
case "view":
- return ;
+ return ;
case "form": {
- return ;
+ return ;
}
default:
return null;
@@ -359,17 +352,12 @@ export const getComponentRendererFn = (
type RendererFn = (
field: FieldConfig,
data: Value,
- context: FabrixContextType,
componentFieldsRenderer?: FabrixComponentFieldsRenderer,
) => ReactNode;
export const getComponentFn =
(props: FabrixComponentProps, rendererFn: RendererFn) =>
- (
- fieldConfig: FieldConfigs,
- data: FabrixComponentData,
- context: FabrixContextType,
- ) =>
+ (fieldConfig: FieldConfigs, data: FabrixComponentData) =>
(
name: string,
extraProps?: FabrixComponentChildrenExtraProps,
@@ -385,7 +373,7 @@ export const getComponentFn =
key={extraProps?.key}
className={`fabrix renderer container ${props.containerClassName ?? ""} ${extraProps?.className ?? ""}`}
>
- {rendererFn(field, data[name], context, componentFieldsRenderer)}
+ {rendererFn(field, data[name], componentFieldsRenderer)}
);
};
diff --git a/packages/fabrix/src/renderers/custom/form.tsx b/packages/fabrix/src/renderers/custom/form.tsx
new file mode 100644
index 00000000..a9a8c4a6
--- /dev/null
+++ b/packages/fabrix/src/renderers/custom/form.tsx
@@ -0,0 +1,24 @@
+import { ComponentRendererProps } from "@customRenderer";
+import { Value } from "@fetcher";
+import { FormComponentEntry } from "@registry";
+import { FabrixComponentProps } from "@renderer";
+import { FieldConfigByType } from "@renderers/shared";
+import { renderForm } from "@renderers/form";
+
+export const CustomComponentFormRenderer = (
+ props: FabrixComponentProps & {
+ fieldConfig: FieldConfigByType<"form">;
+ component: ComponentRendererProps;
+ data: Value;
+ },
+) =>
+ renderForm({
+ component: props.component.entry.component,
+ customProps: props.component.customProps,
+ rootField: {
+ name: props.fieldConfig.name,
+ fields: props.fieldConfig.configs.fields,
+ data: props.data,
+ document: props.fieldConfig.document,
+ },
+ });
diff --git a/packages/fabrix/src/renderers/fields.tsx b/packages/fabrix/src/renderers/fields.tsx
index 6b0d0a16..24c503a7 100644
--- a/packages/fabrix/src/renderers/fields.tsx
+++ b/packages/fabrix/src/renderers/fields.tsx
@@ -14,15 +14,14 @@ import { getTableMode, renderTable } from "./table";
export type ViewFields = FieldConfigByType<"view">["configs"]["fields"];
type ViewField = ViewFields[number];
-export const ViewRenderer = ({
- context,
+export const DefaultViewRenderer = ({
rootField,
componentFieldsRenderer,
className,
}: CommonFabrixComponentRendererProps) => {
// If the query is the one that can be rendered as a table, we will render the table component instead of the fields.
const tableType = useMemo(() => getTableMode(rootField.fields), [rootField]);
-
+ const context = useContext(FabrixContext);
const renderFields = useCallback(() => {
if (componentFieldsRenderer) {
return componentFieldsRenderer({
@@ -152,8 +151,9 @@ const renderField = ({
return;
}
+ const type = resolveFieldTypesFromTypename(context, rootField.data);
const fieldName = field.field.getName();
- const fieldType = rootField.type?.[fieldName];
+ const fieldType = type?.[fieldName];
assertObjectValue(rootField.data);
diff --git a/packages/fabrix/src/renderers/form.tsx b/packages/fabrix/src/renderers/form.tsx
index 4cb60664..600c80c7 100644
--- a/packages/fabrix/src/renderers/form.tsx
+++ b/packages/fabrix/src/renderers/form.tsx
@@ -1,27 +1,58 @@
-import { createElement, useCallback } from "react";
+import { createElement, useContext } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useMutation } from "urql";
-import { FabrixContextType } from "../context";
+import { DocumentNode } from "graphql";
+import { FabrixContext, FabrixContextType } from "@context";
+import { FormComponentEntry } from "@registry";
import {
buildClassName,
CommonFabrixComponentRendererProps,
defaultFieldType,
+ FabrixComponentFieldsRenderer,
FieldConfigByType,
getFieldConfigByKey,
Loader,
} from "./shared";
import { buildAjvSchema } from "./form/validation";
import { ajvResolver } from "./form/ajvResolver";
+import { RootField } from "./fields";
export type FormFields = FieldConfigByType<"form">["configs"]["fields"];
export type FormField = FormFields[number];
-export const FormRenderer = ({
- context,
- rootField,
- componentFieldsRenderer,
- className,
-}: CommonFabrixComponentRendererProps) => {
+export const DefaultFormRenderer = (
+ props: CommonFabrixComponentRendererProps,
+) => {
+ const context = useContext(FabrixContext);
+ const component = context.componentRegistry.getDefaultComponentByType("form");
+ if (!component) {
+ return;
+ }
+
+ return renderForm({
+ ...props,
+ component,
+ customProps: {},
+ });
+};
+
+export const renderForm = (props: {
+ component: FormComponentEntry["component"];
+ customProps: unknown;
+ componentFieldsRenderer?: FabrixComponentFieldsRenderer;
+ className?: string;
+ rootField: RootField & {
+ document: DocumentNode;
+ };
+}) => {
+ const {
+ component,
+ customProps,
+ componentFieldsRenderer,
+ className,
+ rootField,
+ } = props;
+ const context = useContext(FabrixContext);
const formContext = useForm({
resolver: ajvResolver(buildAjvSchema(rootField.fields)),
});
@@ -33,52 +64,22 @@ export const FormRenderer = ({
formContext.reset();
});
- const renderFields = useCallback(() => {
- if (componentFieldsRenderer) {
- return componentFieldsRenderer({
- getField: (name, extraProps) => {
- const field = getFieldConfigByKey(rootField.fields, name);
- if (!field) {
- return null;
- }
-
- return renderField({
- indexKey: extraProps?.key ?? `${rootField.name}-${name}`,
- extraClassName: extraProps?.className,
- field: {
- ...field,
- ...extraProps,
- },
- context,
- });
- },
- });
- }
-
- return rootField.fields
- .sort((a, b) => (a.config.index ?? 0) - (b.config.index ?? 0))
- .flatMap((field, fieldIndex) =>
- renderField({
- indexKey: `${rootField.name}-${fieldIndex}`,
- field,
- context,
- }),
- );
- }, [context, rootField.name, componentFieldsRenderer]);
-
if (context.schemaLoader.status === "loading") {
return ;
}
- const component = context.componentRegistry.getDefaultComponentByType("form");
- if (!component) {
- return;
- }
-
return createElement(component, {
name: rootField.name,
renderFields: () => {
- return {renderFields()};
+ return (
+
+ {renderFormFields({
+ ...rootField,
+ componentFieldsRenderer,
+ className,
+ })}
+
+ );
},
renderSubmit: (submitRenderer) =>
submitRenderer({
@@ -88,11 +89,52 @@ export const FormRenderer = ({
renderReset: (resetRenderer) =>
resetRenderer({ reset: () => formContext.reset() }),
className: `fabrix form col-row ${className ?? ""}`,
- customProps: {},
+ customProps,
});
};
-const renderField = (props: {
+export const renderFormFields = (props: {
+ name: string;
+ fields: FormFields;
+ componentFieldsRenderer?: FabrixComponentFieldsRenderer;
+ className?: string;
+}) => {
+ const context = useContext(FabrixContext);
+ const { name, fields, componentFieldsRenderer } = props;
+
+ if (componentFieldsRenderer) {
+ return componentFieldsRenderer({
+ getField: (fieldName, extraProps) => {
+ const field = getFieldConfigByKey(fields, fieldName);
+ if (!field) {
+ return null;
+ }
+
+ return renderFormField({
+ indexKey: extraProps?.key ?? `${name}-${fieldName}`,
+ extraClassName: extraProps?.className,
+ field: {
+ ...field,
+ ...extraProps,
+ },
+ context,
+ });
+ },
+ });
+ }
+
+ return fields
+ .sort((a, b) => (a.config.index ?? 0) - (b.config.index ?? 0))
+ .flatMap((field, fieldIndex) =>
+ renderFormField({
+ indexKey: `${name}-${fieldIndex}`,
+ field,
+ context,
+ }),
+ );
+};
+
+const renderFormField = (props: {
indexKey: string;
field: FormField;
context: FabrixContextType;
diff --git a/packages/fabrix/src/renderers/shared.tsx b/packages/fabrix/src/renderers/shared.tsx
index a0d4370d..1cf4ef53 100644
--- a/packages/fabrix/src/renderers/shared.tsx
+++ b/packages/fabrix/src/renderers/shared.tsx
@@ -52,12 +52,10 @@ export type RendererQuery = {
documentResolver: DocumentResolver;
};
export type CommonFabrixComponentRendererProps = {
- context: FabrixContextType;
rootField: {
name: string;
fields: F;
data: Value;
- type: Record;
document: DocumentNode;
};
componentFieldsRenderer?: FabrixComponentFieldsRenderer;