@axdspub/axiom-ui-forms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.dockerignore +4 -0
  2. package/.env +1 -0
  3. package/.eslintrc.json +37 -0
  4. package/.gitlab-ci.yml +72 -0
  5. package/.storybook/main.ts +43 -0
  6. package/.storybook/preview.ts +16 -0
  7. package/.vscode/extensions.json +5 -0
  8. package/.vscode/settings.json +10 -0
  9. package/Dockerfile +34 -0
  10. package/README.md +19 -0
  11. package/craco.config.js +42 -0
  12. package/docker/nginx/conf.d/default.conf +46 -0
  13. package/docker-compose.yml +13 -0
  14. package/package.json +103 -0
  15. package/public/exampleForm.json +77 -0
  16. package/public/favicon.ico +0 -0
  17. package/public/index.html +43 -0
  18. package/public/logo192.png +0 -0
  19. package/public/logo512.png +0 -0
  20. package/public/manifest.json +25 -0
  21. package/public/robots.txt +3 -0
  22. package/rollup.config.mjs +108 -0
  23. package/src/App.tsx +25 -0
  24. package/src/Form/Components/FieldCreator.tsx +206 -0
  25. package/src/Form/Components/FieldLabel.tsx +14 -0
  26. package/src/Form/Components/Inputs/Boolean.tsx +13 -0
  27. package/src/Form/Components/Inputs/JSONString.tsx +40 -0
  28. package/src/Form/Components/Inputs/LongString.tsx +22 -0
  29. package/src/Form/Components/Inputs/Number.tsx +22 -0
  30. package/src/Form/Components/Inputs/Object.tsx +56 -0
  31. package/src/Form/Components/Inputs/RadioGroup.tsx +24 -0
  32. package/src/Form/Components/Inputs/SingleSelect.tsx +24 -0
  33. package/src/Form/Components/Inputs/String.tsx +18 -0
  34. package/src/Form/Components/Inputs/index.tsx +6 -0
  35. package/src/Form/Components/Inputs/inputMap.ts +23 -0
  36. package/src/Form/Components/index.tsx +2 -0
  37. package/src/Form/FormCreator.tsx +62 -0
  38. package/src/Form/FormCreatorTypes.ts +187 -0
  39. package/src/Form/FormMappingTypes.ts +17 -0
  40. package/src/Form/Manage/CopyableJSONOutput.tsx +75 -0
  41. package/src/Form/Manage/FormConfigInput.tsx +61 -0
  42. package/src/Form/Manage/FormMappedOutput.tsx +131 -0
  43. package/src/Form/Manage/FormMappingInput.tsx +60 -0
  44. package/src/Form/Manage/Manage.tsx +132 -0
  45. package/src/Form/Manage/RawFormOutput.tsx +20 -0
  46. package/src/Form/MapTester.tsx +107 -0
  47. package/src/Form/SchemaToForm.tsx +348 -0
  48. package/src/Form/formDefinition.json +8 -0
  49. package/src/Form/helpers.ts +85 -0
  50. package/src/Form/index.ts +1 -0
  51. package/src/Form/testData/assetData.json +65 -0
  52. package/src/Form/testData/exampleParticle.json +112 -0
  53. package/src/Form/testData/fields.json +151 -0
  54. package/src/Form/testData/nestedForm.json +156 -0
  55. package/src/Form/testData/testSchema.json +89 -0
  56. package/src/SetTester.tsx +61 -0
  57. package/src/helpers.ts +36 -0
  58. package/src/index.css +39 -0
  59. package/src/index.tsx +19 -0
  60. package/src/library.ts +3 -0
  61. package/src/reportWebVitals.ts +15 -0
  62. package/src/state/formAtom.ts +21 -0
  63. package/src/state/formMappingAtom.ts +21 -0
  64. package/src/state/formValuesAtom.ts +22 -0
  65. package/src/types/generate-schema.d.ts +8 -0
  66. package/tailwind.config.js +11 -0
  67. package/tsconfig.json +32 -0
  68. package/tsconfig.paths.json +19 -0
@@ -0,0 +1,108 @@
1
+ import resolve from '@rollup/plugin-node-resolve'
2
+ import commonjs from '@rollup/plugin-commonjs'
3
+ import typescript from '@rollup/plugin-typescript'
4
+ import dts from 'rollup-plugin-dts'
5
+ import json from '@rollup/plugin-json'
6
+ import alias from '@rollup/plugin-alias'
7
+ import nodePolyfills from 'rollup-plugin-polyfill-node'
8
+ // import packageJson from './package.json'
9
+
10
+ const globals = {
11
+ 'react/jsx-runtime': 'JSXRuntime',
12
+ react: 'React',
13
+ 'react-dom': 'ReactDOM',
14
+ lodash: 'lodash'
15
+ }
16
+
17
+ const external = [
18
+ 'react',
19
+ 'react-dom',
20
+ 'react-scripts',
21
+ 'react/jsx-runtime',
22
+ 'lodash'
23
+ ]
24
+
25
+ const config = [
26
+ {
27
+ external,
28
+ input: 'src/library.ts',
29
+ onwarn: function (warning, warn) {
30
+ if (warning.code === 'MODULE_LEVEL_DIRECTIVE') {
31
+ return
32
+ }
33
+ warn(warning)
34
+ },
35
+ output: [
36
+ /* {
37
+ file: 'library/cjs.js',
38
+ format: 'cjs',
39
+ sourcemap: true,
40
+ globals
41
+ }, */
42
+ {
43
+ file: 'library/index.js',
44
+ format: 'es',
45
+ sourcemap: true,
46
+ globals
47
+ },
48
+ /* {
49
+ file: 'library/browser.js',
50
+ format: 'iife',
51
+ name: 'AxiomUIforms',
52
+ sourcemap: true,
53
+ globals
54
+ }, */
55
+ {
56
+ file: 'library/umd.js',
57
+ format: 'umd',
58
+ name: 'AxiomUIforms',
59
+ sourcemap: true,
60
+ globals
61
+ }
62
+ ],
63
+ plugins: [
64
+ nodePolyfills(),
65
+ json(),
66
+ resolve({ preferBuiltins: false, browser: true }),
67
+ commonjs(),
68
+ alias({
69
+ entries: {
70
+ '@/*': './src'
71
+ }
72
+ }),
73
+ typescript({ tsconfig: './tsconfig.json' })
74
+ ]
75
+ },
76
+ {
77
+ external,
78
+ input: 'src/library.ts',
79
+ output: [{
80
+ file: 'library/axiom-ui-forms.d.ts',
81
+ format: 'esm',
82
+ globals
83
+ }],
84
+ plugins: [
85
+ json(),
86
+ dts({
87
+ compilerOptions: {
88
+ // enabling declaration (.d.ts) emit
89
+ declaration: true,
90
+
91
+ // optional - in general it's a good practice to decouple declaration files from your actual transpiled JavaScript files
92
+ declarationDir: 'library',
93
+
94
+ // optional if you're using babel to transpile TS -> JS
95
+ emitDeclarationOnly: true,
96
+
97
+ paths: {
98
+ '@/*': [
99
+ './src/*'
100
+ ]
101
+ }
102
+ }
103
+ })
104
+ ]
105
+ }
106
+ ]
107
+
108
+ export default config
package/src/App.tsx ADDED
@@ -0,0 +1,25 @@
1
+ import FormManager from '@/Form/Manage/Manage'
2
+ import React, { type ReactElement } from 'react'
3
+ import { BrowserRouter, Route, Routes } from 'react-router-dom'
4
+ import SetTester from '@/SetTester'
5
+ import MapTester from '@/Form/MapTester'
6
+ import SchemaToForm from '@/Form/SchemaToForm'
7
+
8
+ const App = (): ReactElement => {
9
+ return (
10
+
11
+ <div className='h-screen flex flex-col gap-4'>
12
+ <BrowserRouter>
13
+ <Routes>
14
+ <Route path='/' element={<FormManager />} />
15
+ <Route path='/schema-to-form' element={<SchemaToForm />} />
16
+ <Route path="/set-tester" element={<SetTester />} />
17
+ <Route path="/map-tester" element={<MapTester />} />W
18
+ </Routes>
19
+ </BrowserRouter>
20
+ </div>
21
+
22
+ )
23
+ }
24
+
25
+ export default App
@@ -0,0 +1,206 @@
1
+ import inputMap from '@/Form/Components/Inputs/inputMap'
2
+ import { type IFormValues, type IFieldInputProps, type IFormField, type IValueChangeFn, type IValueType, type IForm } from '@/Form/FormCreatorTypes'
3
+ import { checkCondition, cleanUnusedDependenciesFromFormValues, getFieldValue, getPathFromField } from '@/Form/helpers'
4
+ import { Button, utils } from '@axdspub/axiom-ui-utilities'
5
+ import { CheckIcon, CopyIcon, Cross1Icon, PlusIcon, TrashIcon } from '@radix-ui/react-icons'
6
+ import { set } from 'lodash'
7
+ import React, { useState, type ReactElement } from 'react'
8
+
9
+ interface IFieldCreator {
10
+ field: IFormField
11
+ form: IForm
12
+ onChange?: IValueChangeFn
13
+ className?: string
14
+ defaultClassName?: string
15
+ value?: IValueType | IValueType[]
16
+ formValueState: [IFormValues, (v: IFormValues) => void]
17
+ }
18
+
19
+ const toolButtonClass = 'border-white hover:border-single hover:border-1 hover:border-slate-400'
20
+
21
+ const DeleteMultiple = ({
22
+ doDelete
23
+ }: {
24
+ doDelete: () => void
25
+ }): ReactElement => {
26
+ const [confirm, setConfirm] = useState(false)
27
+
28
+ return (
29
+ <>
30
+ {
31
+ confirm
32
+ ? <p className='flex flex-row gap-2 text-sm'><span className='text-slate-600'>Deleting: </span> Are you sure?
33
+ <Button size='xs' type='submit'
34
+ onClick={() => {
35
+ doDelete()
36
+ setConfirm(false)
37
+ }}>Yes <CheckIcon className='inline ml-2' />
38
+ </Button>
39
+ <Button size='xs' type='alert'
40
+ onClick={() => {
41
+ setConfirm(false)
42
+ }}>Cancel <Cross1Icon className='inline ml-2' />
43
+ </Button>
44
+ </p>
45
+ : <Button size='xs' className={toolButtonClass} onClick={() => { setConfirm(true) }}>
46
+ Delete <TrashIcon className='inline ml-2 fill-white' />
47
+ </Button>
48
+ }
49
+ </>
50
+ )
51
+ }
52
+
53
+ const OneOfMultiple = ({
54
+ InputComponent,
55
+ field,
56
+ form,
57
+ value,
58
+ index,
59
+ onChange,
60
+ values,
61
+ formValueState
62
+
63
+ }: {
64
+ InputComponent: React.FC<IFieldInputProps>
65
+ field: IFormField
66
+ form: IForm
67
+ value: IValueType
68
+ index: number
69
+ onChange: (v: IValueType[] | undefined) => void
70
+ values: IValueType[]
71
+ formValueState: [IFormValues, (v: IFormValues) => void]
72
+
73
+ }): ReactElement => {
74
+ const addValue = (v: IValueType | null): void => {
75
+ const newValues = [...values]
76
+ newValues.splice(index + 1, 0, v)
77
+ onChange(newValues)
78
+ }
79
+
80
+ return (
81
+ <div className='flex flex-col gap-2'>
82
+ <InputComponent
83
+ formValueState={formValueState}
84
+ form={form}
85
+ field={{
86
+ ...field,
87
+ required: false,
88
+ label: index > 0 ? null : field.label,
89
+ id: `${field.id}-${index}`
90
+ }}
91
+ value={value}
92
+ onChange={(v) => {
93
+ const newValues = [...values]
94
+ newValues[index] = String(v)
95
+ onChange(newValues)
96
+ }}
97
+ />
98
+
99
+ <div className='flex flex-row justify-between w-full p-2'>
100
+ {index > 0 && (
101
+ <DeleteMultiple doDelete={() => {
102
+ const newValues = [...values]
103
+ newValues.splice(index, 1)
104
+ onChange(newValues)
105
+ }} />
106
+ )}
107
+ <div className='ml-auto flex gap-2'>
108
+ <Button
109
+ size='xs'
110
+ className={toolButtonClass}
111
+ onClick={() => {
112
+ addValue(null)
113
+ }}>Add <PlusIcon className='inline ml-2' /></Button>
114
+ <Button
115
+ size='xs'
116
+ className={toolButtonClass}
117
+ onClick={() => {
118
+ addValue(structuredClone(value))
119
+ }}>Duplicate <CopyIcon className='inline ml-2' />
120
+ </Button>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ )
125
+ }
126
+
127
+ const MultipleFieldCreator = ({ form, field, onChange, value, formValueState }: IFieldCreator): ReactElement => {
128
+ const [formValues, setFormValues] = formValueState
129
+ const defaultOnChange = (v: IValueType[] | undefined): void => {
130
+ const formValuesCopy = structuredClone(formValues)
131
+ set(formValuesCopy, getPathFromField(field), v)
132
+ setFormValues(formValuesCopy)
133
+ }
134
+
135
+ const initialVal = value !== undefined ? value : getFieldValue(field, formValues)
136
+ const initialValues = (initialVal !== undefined ? (Array.isArray(initialVal) ? initialVal : [initialVal]) : [null])
137
+
138
+ /* const initialValues = (
139
+ formValues[getPathFromField(field)] !== undefined
140
+ ? Array.isArray(formValues[getPathFromField(field)])
141
+ ? formValues[getPathFromField(field)]
142
+ : [formValues[getPathFromField(field)]]
143
+ : [null]
144
+ ) as IValueType[] */
145
+
146
+ const InputComponent = inputMap[field.type]
147
+
148
+ return <div>
149
+ {
150
+ initialValues?.map((value, index) => {
151
+ return <OneOfMultiple
152
+ formValueState={formValueState}
153
+ key={`${field.id}-${index}`}
154
+ InputComponent={InputComponent}
155
+ form={form}
156
+ field={field}
157
+ value={value}
158
+ index={index}
159
+ onChange={onChange ?? defaultOnChange}
160
+ values={initialValues}
161
+ />
162
+ })
163
+ }
164
+ </div>
165
+ }
166
+
167
+ const FieldCreator = ({
168
+ field,
169
+ form,
170
+ value,
171
+ onChange,
172
+ className,
173
+ defaultClassName = 'py-2 flex flex-col gap-8',
174
+ formValueState
175
+ }: IFieldCreator): ReactElement | null => {
176
+ const [formValues, setFormValues] = formValueState
177
+ const InputComponent = inputMap[field.type]
178
+
179
+ const updateFormValues = (v: IFormValues): void => {
180
+ setFormValues(cleanUnusedDependenciesFromFormValues(form, v))
181
+ }
182
+
183
+ if (!checkCondition(field, formValues)) {
184
+ return null
185
+ }
186
+
187
+ const defaultOnChange = (v: IValueType | IValueType[] | undefined): void => {
188
+ const formValuesCopy = structuredClone(formValues)
189
+ set(formValuesCopy, getPathFromField(field), v)
190
+ updateFormValues(formValuesCopy)
191
+ }
192
+ const initialValue = value !== undefined ? value : getFieldValue(field, formValues)
193
+ return InputComponent !== undefined
194
+ ? <div className={utils.makeClassName({
195
+ className,
196
+ defaultClassName
197
+ })}>{
198
+ field.multiple === true
199
+ ? <MultipleFieldCreator field={field} form={form} onChange={onChange} value={initialValue} formValueState={formValueState} />
200
+ : <InputComponent field={field} form={form} onChange={onChange ?? defaultOnChange} value={Array.isArray(initialValue) ? initialValue[0] : initialValue} formValueState={formValueState} />
201
+
202
+ }</div>
203
+ : <p>No component definition for {field.type} ({field.id})</p>
204
+ }
205
+
206
+ export default FieldCreator
@@ -0,0 +1,14 @@
1
+ import { type IFormField } from '@/Form/FormCreatorTypes'
2
+ import React, { type ReactElement } from 'react'
3
+
4
+ export const FieldLabelText = (field: IFormField): ReactElement => {
5
+ return (
6
+ <strong>{field.label} { field.required === true ? <span className='text-red-500'>*</span> : ''}</strong>
7
+ )
8
+ }
9
+
10
+ const FieldLabel = (field: IFormField): ReactElement => {
11
+ return <p className='pb-2'><FieldLabelText {...field} /></p>
12
+ }
13
+
14
+ export default FieldLabel
@@ -0,0 +1,13 @@
1
+ import { FieldLabelText } from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { Checkbox } from '@axdspub/axiom-ui-utilities'
4
+ import React, { type ReactElement } from 'react'
5
+
6
+ const BooleanInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const initialValue = value !== undefined ? value : false
8
+ return <Checkbox id={field.id} testId={field.id} label={<FieldLabelText {...field} />} value={Boolean(initialValue)} onChange={(e) => {
9
+ onChange(e)
10
+ }} />
11
+ }
12
+
13
+ export default BooleanInput
@@ -0,0 +1,40 @@
1
+ import FieldLabel from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { TextArea } from '@axdspub/axiom-ui-utilities'
4
+ import React, { useState, type ReactElement } from 'react'
5
+
6
+ const JSONStringInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const [error, setError] = useState<string | undefined>(undefined)
8
+ const initialValue = value !== undefined ? value : ''
9
+ const getValue = (): string => {
10
+ return initialValue !== undefined && initialValue !== null
11
+ ? typeof initialValue === 'object'
12
+ ? JSON.stringify(initialValue, null, 2)
13
+ : String(initialValue)
14
+ : ''
15
+ }
16
+ return <div>
17
+ <TextArea
18
+ error={error}
19
+ className={
20
+ [
21
+ 'min-h-[500px] bg-slate-50 rounded-lg shadow-inner'
22
+ // 'p-0 bg-[repeating-linear-gradient(to_bottom,var(--tw-gradient-stops))] from-[#efefef] from-[length:0_25px] to-[#FFF] to-[length:25px_50px]'
23
+ ].join(' ')
24
+ }
25
+ id={field.id}
26
+ testId={field.id}
27
+ label={<FieldLabel {...field} />}
28
+ value={getValue()}
29
+ onChange={(e) => {
30
+ try {
31
+ JSON.parse(e ?? '')
32
+ onChange(JSON.parse(e ?? ''))
33
+ setError(undefined)
34
+ } catch (e) {
35
+ setError('Invalid JSON')
36
+ }
37
+ }} /></div>
38
+ }
39
+
40
+ export default JSONStringInput
@@ -0,0 +1,22 @@
1
+ import FieldLabel from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { TextArea } from '@axdspub/axiom-ui-utilities'
4
+ import React, { type ReactElement } from 'react'
5
+
6
+ const LongStringInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const initialValue = value !== undefined ? value : ''
8
+ const getValue = (): string => {
9
+ return initialValue !== undefined && initialValue !== null ? String(initialValue) : ''
10
+ }
11
+ return <div>
12
+ <TextArea
13
+ id={field.id}
14
+ testId={field.id}
15
+ label={<FieldLabel {...field} />}
16
+ value={getValue()}
17
+ onChange={(e) => {
18
+ onChange(e)
19
+ }} /></div>
20
+ }
21
+
22
+ export default LongStringInput
@@ -0,0 +1,22 @@
1
+ import FieldLabel from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { Input } from '@axdspub/axiom-ui-utilities'
4
+ import React, { type ReactElement } from 'react'
5
+
6
+ const NumberInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const initialValue = value !== undefined ? value : ''
8
+ return <div>
9
+ <Input
10
+ id={field.id}
11
+ testId={field.id}
12
+ value={initialValue !== undefined && initialValue !== null ? String(initialValue) : ''}
13
+ label={<FieldLabel {...field} />} onChange={(e) => {
14
+ if (e !== undefined && !isNaN(+e)) {
15
+ onChange(+e)
16
+ } else {
17
+ onChange(undefined)
18
+ }
19
+ }} /></div>
20
+ }
21
+
22
+ export default NumberInput
@@ -0,0 +1,56 @@
1
+ import FieldCreator from '@/Form/Components/FieldCreator'
2
+ import FieldLabel from '@/Form/Components/FieldLabel'
3
+ import { type ICompositeValueType, type IFieldInputProps } from '@/Form/FormCreatorTypes'
4
+ import { utils } from '@axdspub/axiom-ui-utilities'
5
+ import React, { type ReactElement } from 'react'
6
+
7
+ const ObjectInput = ({ form, field, onChange, value, formValueState }: IFieldInputProps): ReactElement => {
8
+ const initialValue = (typeof value === 'object' ? value ?? {} : {}) as ICompositeValueType
9
+ if (field.type === 'object' && field.fields !== undefined) {
10
+ const cl = `${field.layout === 'horizontal' ? `flex flex-row gap-4 ${field.label !== undefined ? 'px-0' : ''}` : 'flex flex-col gap-4'}`
11
+ const fc = field.layout === 'horizontal' ? 'flex-1' : ''
12
+ return (
13
+ <div>
14
+ {
15
+ field.label !== undefined
16
+ ? <FieldLabel {...field} />
17
+ : null
18
+ }
19
+ <div className={`p-4 bg-slate-100 ${cl}`}>
20
+ {
21
+ field.fields.map((childField) => {
22
+ const id = (field.path ?? [field.id]).concat(childField.id).join('.')
23
+ if (childField.type === 'object') {
24
+ childField.path = field.path !== undefined ? field.path.concat(id) : [id]
25
+ childField.level = field.level !== undefined ? field.level + 1 : 1
26
+ }
27
+
28
+ return (
29
+ <FieldCreator
30
+ formValueState={formValueState}
31
+ onChange={field.skip_path === true
32
+ ? undefined
33
+ : (e) => {
34
+ initialValue[childField.id] = e
35
+ onChange({ ...initialValue })
36
+ }}
37
+ className={utils.makeClassName({
38
+ defaultClassName: 'p-0',
39
+ className: fc
40
+ })}
41
+ value={initialValue[childField.id]}
42
+ field={{ ...childField, id }}
43
+ form={form}
44
+ key={id}
45
+ />
46
+ )
47
+ })
48
+ }
49
+ </div>
50
+ </div>
51
+ )
52
+ }
53
+ return <p>Field config for {field.id} is missing &apos;fields&apos;</p>
54
+ }
55
+
56
+ export default ObjectInput
@@ -0,0 +1,24 @@
1
+ import FieldLabel from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { RadioGroup } from '@axdspub/axiom-ui-utilities'
4
+ import React, { type ReactElement } from 'react'
5
+
6
+ const RadioInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const initialValue = value !== undefined ? value : ''
8
+
9
+ if (field.type === 'radio' && field.options !== undefined) {
10
+ return <RadioGroup
11
+ id={field.id}
12
+ label={<FieldLabel {...field} />}
13
+ testId={field.id}
14
+ options={field.options}
15
+ value={initialValue !== undefined && initialValue !== null ? String(initialValue) : ''}
16
+ onChange={(e) => {
17
+ onChange(e?.value)
18
+ }}
19
+ />
20
+ }
21
+ return <p>Field config for {field.id} is missing &apos;options&apos;</p>
22
+ }
23
+
24
+ export default RadioInput
@@ -0,0 +1,24 @@
1
+ import FieldLabel from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { SelectInput } from '@axdspub/axiom-ui-utilities'
4
+ import React, { type ReactElement } from 'react'
5
+
6
+ const SingleSelectInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const initialValue = value !== undefined ? value : ''
8
+
9
+ if (field.type === 'select' && field.options !== undefined) {
10
+ return <SelectInput
11
+ id={field.id}
12
+ label={<FieldLabel {...field} />}
13
+ testId={field.id}
14
+ options={field.options}
15
+ value={initialValue !== undefined && initialValue !== null ? String(initialValue) : ''}
16
+ onChange={(e) => {
17
+ onChange(e?.value)
18
+ }}
19
+ />
20
+ }
21
+ return <p>Field config for {field.id} is missing &apos;options&apos;</p>
22
+ }
23
+
24
+ export default SingleSelectInput
@@ -0,0 +1,18 @@
1
+ import FieldLabel from '@/Form/Components/FieldLabel'
2
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
3
+ import { Input } from '@axdspub/axiom-ui-utilities'
4
+ import React, { type ReactElement } from 'react'
5
+
6
+ const StringInput = ({ field, onChange, value }: IFieldInputProps): ReactElement => {
7
+ const initialValue = value !== undefined ? value : ''
8
+ return <div>
9
+ <Input
10
+ id={field.id}
11
+ testId={field.id}
12
+ value={initialValue !== undefined && initialValue !== null ? String(initialValue) : ''}
13
+ label={<FieldLabel {...field} />} onChange={(e) => {
14
+ onChange(e)
15
+ }} /></div>
16
+ }
17
+
18
+ export default StringInput
@@ -0,0 +1,6 @@
1
+ export { default as BooleanInput } from './Boolean'
2
+ export { default as JSONStringInput } from './JSONString'
3
+ export { default as NumberInput } from './Number'
4
+ export { default as TextInput } from './String'
5
+ export { default as LongTextInput } from './LongString'
6
+ export { default as SelectInput } from './SingleSelect'
@@ -0,0 +1,23 @@
1
+ import BooleanInput from '@/Form/Components/Inputs/Boolean'
2
+ import LongString from '@/Form/Components/Inputs/LongString'
3
+ import StringInput from '@/Form/Components/Inputs/String'
4
+ import ObjectInput from '@/Form/Components/Inputs/Object'
5
+ import Radio from '@/Form/Components/Inputs/RadioGroup'
6
+ import SingleSelect from '@/Form/Components/Inputs/SingleSelect'
7
+ import { type IFieldInputProps } from '@/Form/FormCreatorTypes'
8
+ import JSONStringInput from '@/Form/Components/Inputs/JSONString'
9
+ import NumberInput from '@/Form/Components/Inputs/Number'
10
+
11
+ const inputMap: Record<string, React.FC<IFieldInputProps>> = {
12
+ text: StringInput,
13
+ long_text: LongString,
14
+ number: NumberInput,
15
+ json: JSONStringInput,
16
+ boolean: BooleanInput,
17
+ select: SingleSelect,
18
+ radio: Radio,
19
+ object: ObjectInput
20
+
21
+ }
22
+
23
+ export default inputMap
@@ -0,0 +1,2 @@
1
+ export { default as FieldCreator } from './FieldCreator'
2
+ export * from './Inputs'
@@ -0,0 +1,62 @@
1
+ import FieldCreator from '@/Form/Components/FieldCreator'
2
+ import { type IFormValues, type IForm, type IValueChangeFn } from '@/Form/FormCreatorTypes'
3
+ import { copyAndAddPathToFields } from '@/Form/helpers'
4
+ import { ExclamationTriangleIcon } from '@radix-ui/react-icons'
5
+ import React, { useEffect, useState, type ReactElement } from 'react'
6
+
7
+ const FormCreator = ({
8
+ form,
9
+ formValueState,
10
+ note,
11
+ error,
12
+ onChange
13
+ }: {
14
+ form: IForm
15
+ formValueState: [IFormValues, (v: IFormValues) => void]
16
+ note?: string
17
+ error?: string
18
+ onChange?: IValueChangeFn
19
+
20
+ }): ReactElement => {
21
+ const [activeForm, setActiveForm] = useState<IForm | null>(null)
22
+ useEffect(() => {
23
+ const newForm = copyAndAddPathToFields<IForm>(form)
24
+ setActiveForm(newForm)
25
+ }, [form])
26
+ if (activeForm === null) {
27
+ return <p>Processing</p>
28
+ }
29
+ return (
30
+ <>
31
+ <div>
32
+ <h2 className='text-2xl pb-4 font-bold'>{form.label}</h2>
33
+ {
34
+ note !== undefined
35
+ ? <p className='pb-4'>{note}</p>
36
+ : null
37
+ }
38
+ {
39
+ error !== undefined
40
+ ? <p className='pb-4 text-rose-800'><ExclamationTriangleIcon className='inline mr-2' /> {error}</p>
41
+ : null
42
+ }
43
+ {
44
+ activeForm.description !== undefined
45
+ ? <p className='pb-4'>{form.description}</p>
46
+ : null
47
+ }
48
+ <div className='flex flex-col gap-2'>
49
+ {
50
+ activeForm.fields.map((field) => {
51
+ return (
52
+ <FieldCreator onChange={onChange} form={form} field={field} key={field.id} formValueState={formValueState} />
53
+ )
54
+ })
55
+ }
56
+ </div>
57
+ </div>
58
+ </>
59
+ )
60
+ }
61
+
62
+ export default FormCreator