Skip to content

Commit 1ebe74c

Browse files
authored
Feat/native form adapter (#9)
* feat: replace RHF adapter with native form adapter - Add NativeReactFormAdapter (useState-based) - Remove form-adapter.ts (ReactHookFormAdapter) and react-hook-form dependency - Export createNativeReactFormAdapter from index * feat(factory-react): native form state and injectable FieldWrapper - Add ScheptaFormContext, ScheptaFormProvider, useScheptaFormAdapter, useScheptaFieldValue - Add DefaultFieldWrapper; remove field-wrapper.tsx; FieldWrapper injectable via registry - use-schepta-form: native state (useState), createNativeReactFormAdapter - form-factory: ScheptaFormProvider, pass values to context, resolve FieldWrapper from registry - DefaultFormContainer: use useScheptaFormAdapter for submit - Drop react-hook-form from peerDependencies * feat(examples): reorganize forms, add RHF/Formik demos, tsconfig fix - basic-ui: Form -> Forms/NativeForm, FormModal -> Forms/ModalForm - basic-ui: remove custom FormContainer and SubmitButton (use factory defaults) - Add FormWithRHF and FormWithFormik with rhf/ and formik/ components - BasicFormPage: NativeForm, ModalForm, FormWithRHF, FormWithFormik - tsconfig: paths {} to fix rootDir with @schepta/* resolution - chakra/material: FormContainer props alignment - Add formik dependency; update pnpm-lock.yaml ---------
1 parent fdde0d1 commit 1ebe74c

32 files changed

Lines changed: 1405 additions & 303 deletions

examples/react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@schepta/adapter-react": "workspace:*",
1616
"@schepta/core": "workspace:*",
1717
"@schepta/factory-react": "workspace:*",
18+
"formik": "^2.4.6",
1819
"framer-motion": "^10.16.16",
1920
"react": "^18.2.0",
2021
"react-dom": "^18.2.0",

examples/react/src/basic-ui/components/ComponentRegistry.tsx

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,13 @@ import { InputCheckbox } from "./Inputs/InputCheckbox";
55
import { InputTextarea } from "./Inputs/InputTextarea";
66
import { InputNumber } from "./Inputs/InputNumber";
77
import { InputDate } from "./Inputs/InputDate";
8-
import { SubmitButton } from "./SubmitButton";
9-
import { FormContainer } from "./Containers/FormContainer";
108
import { FormField } from "./Containers/FormField";
119
import { FormSectionContainer } from "./Containers/FormSectionContainer";
1210
import { FormSectionTitle } from "./Containers/FormSectionTitle";
1311
import { FormSectionGroupContainer } from "./Containers/FormSectionGroupContainer";
1412
import { FormSectionGroup } from "./Containers/FormSectionGroup";
1513

1614
export const components = {
17-
'FormContainer': createComponentSpec({
18-
id: "FormContainer",
19-
type: "FormContainer",
20-
factory: (props, runtime) => FormContainer,
21-
}),
2215
InputText: createComponentSpec({
2316
id: "InputText",
2417
type: "field",
@@ -55,11 +48,6 @@ export const components = {
5548
type: "field",
5649
factory: (props, runtime) => InputDate,
5750
}),
58-
SubmitButton: createComponentSpec({
59-
id: "SubmitButton",
60-
type: 'content',
61-
factory: (props, runtime) => SubmitButton,
62-
}),
6351
FormField: createComponentSpec({
6452
id: "FormField",
6553
type: 'container',

examples/react/src/basic-ui/components/Containers/FormContainer.tsx

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* Form with Formik
3+
*
4+
* Example component demonstrating how to use Schepta with Formik.
5+
* This shows how to inject custom Formik components via the component registry.
6+
*/
7+
8+
import React, { useState } from 'react';
9+
import { FormFactory } from '@schepta/factory-react';
10+
import { createComponentSpec, FormSchema } from '@schepta/core';
11+
import { FormikFieldWrapper } from '../formik/FormikFieldWrapper';
12+
import { FormikFormContainer } from '../formik/FormikFormContainer';
13+
14+
// Import the same input components from basic-ui
15+
import { InputText } from '../Inputs/InputText';
16+
import { InputSelect } from '../Inputs/InputSelect';
17+
import { InputCheckbox } from '../Inputs/InputCheckbox';
18+
import { InputTextarea } from '../Inputs/InputTextarea';
19+
import { InputNumber } from '../Inputs/InputNumber';
20+
import { InputDate } from '../Inputs/InputDate';
21+
22+
/**
23+
* Formik-specific component registry.
24+
* Registers the Formik FieldWrapper and FormContainer to use
25+
* Formik for form state management.
26+
*/
27+
const formikComponents = {
28+
// Register Formik FieldWrapper - this makes all fields use Formik's context
29+
FieldWrapper: createComponentSpec({
30+
id: 'FieldWrapper',
31+
type: 'field-wrapper',
32+
factory: () => FormikFieldWrapper,
33+
}),
34+
// Register Formik FormContainer - this provides the Formik context
35+
FormContainer: createComponentSpec({
36+
id: 'FormContainer',
37+
type: 'FormContainer',
38+
factory: () => FormikFormContainer,
39+
}),
40+
// Standard input components (same as basic-ui)
41+
InputText: createComponentSpec({
42+
id: 'InputText',
43+
type: 'field',
44+
factory: () => InputText,
45+
}),
46+
InputSelect: createComponentSpec({
47+
id: 'InputSelect',
48+
type: 'field',
49+
factory: () => InputSelect,
50+
}),
51+
InputCheckbox: createComponentSpec({
52+
id: 'InputCheckbox',
53+
type: 'field',
54+
factory: () => InputCheckbox,
55+
}),
56+
InputTextarea: createComponentSpec({
57+
id: 'InputTextarea',
58+
type: 'field',
59+
factory: () => InputTextarea,
60+
}),
61+
InputNumber: createComponentSpec({
62+
id: 'InputNumber',
63+
type: 'field',
64+
factory: () => InputNumber,
65+
}),
66+
InputDate: createComponentSpec({
67+
id: 'InputDate',
68+
type: 'field',
69+
factory: () => InputDate,
70+
}),
71+
InputPhone: createComponentSpec({
72+
id: 'InputPhone',
73+
type: 'field',
74+
factory: () => InputText,
75+
defaultProps: { type: 'tel' },
76+
}),
77+
};
78+
79+
interface FormWithFormikProps {
80+
schema: FormSchema;
81+
}
82+
83+
/**
84+
* FormWithFormik Component
85+
*
86+
* Renders a form using Formik for state management.
87+
* Demonstrates how to integrate external form libraries with Schepta.
88+
*/
89+
export const FormWithFormik: React.FC<FormWithFormikProps> = ({ schema }) => {
90+
const [submittedValues, setSubmittedValues] = useState<Record<string, any> | null>(null);
91+
92+
const handleSubmit = (values: Record<string, any>) => {
93+
console.log('Form submitted (Formik):', values);
94+
setSubmittedValues(values);
95+
};
96+
97+
return (
98+
<>
99+
<div
100+
style={{
101+
border: '1px solid #ddd',
102+
padding: '24px',
103+
borderRadius: '8px',
104+
}}
105+
>
106+
<div style={{
107+
marginBottom: '16px',
108+
padding: '8px 12px',
109+
backgroundColor: '#d4edda',
110+
borderRadius: '4px',
111+
fontSize: '14px',
112+
}}>
113+
This form uses <strong>Formik</strong> for state management.
114+
The FieldWrapper and FormContainer are custom Formik implementations.
115+
</div>
116+
<FormFactory
117+
schema={schema}
118+
components={formikComponents}
119+
onSubmit={handleSubmit}
120+
debug={true}
121+
/>
122+
</div>
123+
{submittedValues && (
124+
<div style={{
125+
marginTop: '24px',
126+
padding: '16px',
127+
background: '#f9fafb',
128+
border: '1px solid #e5e7eb',
129+
borderRadius: '8px',
130+
}}>
131+
<h3 style={{ marginTop: 0 }}>Submitted Values (Formik):</h3>
132+
<pre style={{
133+
background: 'white',
134+
padding: '12px',
135+
borderRadius: '4px',
136+
overflow: 'auto',
137+
fontSize: '13px',
138+
}}>
139+
{JSON.stringify(submittedValues, null, 2)}
140+
</pre>
141+
</div>
142+
)}
143+
</>
144+
);
145+
};
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* Form with React Hook Form
3+
*
4+
* Example component demonstrating how to use Schepta with react-hook-form.
5+
* This shows how to inject custom RHF components via the component registry.
6+
*/
7+
8+
import React, { useState } from 'react';
9+
import { FormFactory } from '@schepta/factory-react';
10+
import { createComponentSpec, FormSchema } from '@schepta/core';
11+
import { RHFFieldWrapper } from '../rhf/RHFFieldWrapper';
12+
import { RHFFormContainer } from '../rhf/RHFFormContainer';
13+
14+
// Import the same input components from basic-ui
15+
import { InputText } from '../Inputs/InputText';
16+
import { InputSelect } from '../Inputs/InputSelect';
17+
import { InputCheckbox } from '../Inputs/InputCheckbox';
18+
import { InputTextarea } from '../Inputs/InputTextarea';
19+
import { InputNumber } from '../Inputs/InputNumber';
20+
import { InputDate } from '../Inputs/InputDate';
21+
22+
/**
23+
* RHF-specific component registry.
24+
* Registers the RHF FieldWrapper and FormContainer to use
25+
* react-hook-form for form state management.
26+
*/
27+
const rhfComponents = {
28+
// Register RHF FieldWrapper - this makes all fields use RHF's Controller
29+
FieldWrapper: createComponentSpec({
30+
id: 'FieldWrapper',
31+
type: 'field-wrapper',
32+
factory: () => RHFFieldWrapper,
33+
}),
34+
// Register RHF FormContainer - this provides the FormProvider context
35+
FormContainer: createComponentSpec({
36+
id: 'FormContainer',
37+
type: 'FormContainer',
38+
factory: () => RHFFormContainer,
39+
}),
40+
// Standard input components (same as basic-ui)
41+
InputText: createComponentSpec({
42+
id: 'InputText',
43+
type: 'field',
44+
factory: () => InputText,
45+
}),
46+
InputSelect: createComponentSpec({
47+
id: 'InputSelect',
48+
type: 'field',
49+
factory: () => InputSelect,
50+
}),
51+
InputCheckbox: createComponentSpec({
52+
id: 'InputCheckbox',
53+
type: 'field',
54+
factory: () => InputCheckbox,
55+
}),
56+
InputTextarea: createComponentSpec({
57+
id: 'InputTextarea',
58+
type: 'field',
59+
factory: () => InputTextarea,
60+
}),
61+
InputNumber: createComponentSpec({
62+
id: 'InputNumber',
63+
type: 'field',
64+
factory: () => InputNumber,
65+
}),
66+
InputDate: createComponentSpec({
67+
id: 'InputDate',
68+
type: 'field',
69+
factory: () => InputDate,
70+
}),
71+
InputPhone: createComponentSpec({
72+
id: 'InputPhone',
73+
type: 'field',
74+
factory: () => InputText,
75+
defaultProps: { type: 'tel' },
76+
}),
77+
};
78+
79+
interface FormWithRHFProps {
80+
schema: FormSchema;
81+
}
82+
83+
/**
84+
* FormWithRHF Component
85+
*
86+
* Renders a form using react-hook-form for state management.
87+
* Demonstrates how to integrate external form libraries with Schepta.
88+
*/
89+
export const FormWithRHF: React.FC<FormWithRHFProps> = ({ schema }) => {
90+
const [submittedValues, setSubmittedValues] = useState<Record<string, any> | null>(null);
91+
92+
const handleSubmit = (values: Record<string, any>) => {
93+
console.log('Form submitted (RHF):', values);
94+
setSubmittedValues(values);
95+
};
96+
97+
return (
98+
<>
99+
<div
100+
style={{
101+
border: '1px solid #ddd',
102+
padding: '24px',
103+
borderRadius: '8px',
104+
}}
105+
>
106+
<div style={{
107+
marginBottom: '16px',
108+
padding: '8px 12px',
109+
backgroundColor: '#e7f3ff',
110+
borderRadius: '4px',
111+
fontSize: '14px',
112+
}}>
113+
This form uses <strong>react-hook-form</strong> for state management.
114+
The FieldWrapper and FormContainer are custom RHF implementations.
115+
</div>
116+
<FormFactory
117+
schema={schema}
118+
components={rhfComponents}
119+
onSubmit={handleSubmit}
120+
debug={true}
121+
/>
122+
</div>
123+
{submittedValues && (
124+
<div style={{
125+
marginTop: '24px',
126+
padding: '16px',
127+
background: '#f9fafb',
128+
border: '1px solid #e5e7eb',
129+
borderRadius: '8px',
130+
}}>
131+
<h3 style={{ marginTop: 0 }}>Submitted Values (RHF):</h3>
132+
<pre style={{
133+
background: 'white',
134+
padding: '12px',
135+
borderRadius: '4px',
136+
overflow: 'auto',
137+
fontSize: '13px',
138+
}}>
139+
{JSON.stringify(submittedValues, null, 2)}
140+
</pre>
141+
</div>
142+
)}
143+
</>
144+
);
145+
};

examples/react/src/basic-ui/components/FormModal.tsx renamed to examples/react/src/basic-ui/components/Forms/ModalForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface FormModalProps {
1313
* The form is rendered in a modal-like structure where the submit button
1414
* is outside the FormFactory component (in the footer).
1515
*/
16-
export const FormModal = ({ schema, onSubmit }: FormModalProps) => {
16+
export const ModalForm = ({ schema, onSubmit }: FormModalProps) => {
1717
const formRef = useRef<FormFactoryRef>(null);
1818
const [submittedValues, setSubmittedValues] = useState<Record<string, any> | null>(null);
1919
const [isOpen, setIsOpen] = useState(false);

examples/react/src/basic-ui/components/Form.tsx renamed to examples/react/src/basic-ui/components/Forms/NativeForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React, { useState } from "react";
2-
import { FormFactory } from "@schepta/factory-react";
2+
import { FormFactory } from '@schepta/factory-react';
33
import { FormSchema } from "@schepta/core";
44

55
interface FormProps {
66
schema: FormSchema;
77
}
88

9-
export const Form = ({ schema }: FormProps) => {
9+
export const NativeForm = ({ schema }: FormProps) => {
1010
const [submittedValues, setSubmittedValues] = useState<Record<string, any> | null>(null);
1111

1212
const handleSubmit = (values: Record<string, any>) => {

0 commit comments

Comments
 (0)