form-craft-package 1.0.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.
- package/.prettierrc +9 -0
- package/AJV_JSON_Schema_Guide.md +409 -0
- package/README.md +108 -0
- package/index.ts +8 -0
- package/package.json +40 -0
- package/src/ajv/form/form.schema.json +10 -0
- package/src/ajv/form/layout.schema.json +97 -0
- package/src/ajv/form/migration-rules.schema.json +59 -0
- package/src/ajv/master-portal-only/render-conditions/conditions.schema.json +24 -0
- package/src/ajv/master-portal-only/render-conditions/validate.ts +15 -0
- package/src/components/common/button.tsx +72 -0
- package/src/components/common/custom-hooks/use-find-dynamic-form.ts +33 -0
- package/src/components/common/custom-hooks/use-lazy-modal-opener.hook.ts +20 -0
- package/src/components/common/custom-hooks/use-notification.hook.tsx +157 -0
- package/src/components/common/disabled-field-indicator.tsx +20 -0
- package/src/components/common/warning-icon.tsx +10 -0
- package/src/components/form/layout-renderer/1-row/index.tsx +27 -0
- package/src/components/form/layout-renderer/2-col/index.tsx +32 -0
- package/src/components/form/layout-renderer/3-element/1-dynamic-button.tsx +277 -0
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +220 -0
- package/src/components/form/layout-renderer/3-element/index.tsx +73 -0
- package/src/components/index.tsx +2 -0
- package/src/components/modals/form-data-loading.modal.tsx +48 -0
- package/src/constants.ts +15 -0
- package/src/enums.ts +177 -0
- package/src/functions/axios-handler.ts +158 -0
- package/src/functions/data-list-functions.tsx +41 -0
- package/src/functions/form-schema-validator.ts +50 -0
- package/src/functions/get-element-props.ts +20 -0
- package/src/functions/index.ts +56 -0
- package/src/functions/json-handlers.ts +19 -0
- package/src/functions/validations.ts +120 -0
- package/src/types/form-data-list/index.ts +54 -0
- package/src/types/index.ts +124 -0
- package/src/types/layout-elements/element-data-render-logic.ts +56 -0
- package/src/types/layout-elements/field-option-source.ts +14 -0
- package/src/types/layout-elements/index.ts +224 -0
- package/src/types/layout-elements/style.ts +35 -0
- package/src/types/layout-elements/validation.ts +18 -0
- package/tsconfig.json +111 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "migration-rules.schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"patternProperties": {
|
|
5
|
+
".*": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": { "$ref": "#/definitions/IMigrationRule" }
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"definitions": {
|
|
11
|
+
"IMigrationRule": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"oneOf": [
|
|
14
|
+
{
|
|
15
|
+
"properties": {
|
|
16
|
+
"operation": { "type": "string", "enum": ["RenameField"] },
|
|
17
|
+
"from": { "type": "string" },
|
|
18
|
+
"to": { "type": "string" }
|
|
19
|
+
},
|
|
20
|
+
"required": ["operation", "from", "to"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"properties": {
|
|
24
|
+
"operation": { "type": "string", "enum": ["RemoveField"] },
|
|
25
|
+
"field": { "type": "string" }
|
|
26
|
+
},
|
|
27
|
+
"required": ["operation", "field"]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"properties": {
|
|
31
|
+
"operation": { "type": "string", "enum": ["ConvertDataType"] },
|
|
32
|
+
"field": { "type": "string" },
|
|
33
|
+
"fromType": { "type": "string" },
|
|
34
|
+
"toType": { "type": "string" },
|
|
35
|
+
"defaultValue": {}
|
|
36
|
+
},
|
|
37
|
+
"required": ["operation", "field", "fromType", "toType"]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"properties": {
|
|
41
|
+
"operation": { "type": "string", "enum": ["MakeRequired"] },
|
|
42
|
+
"field": { "type": "string" },
|
|
43
|
+
"defaultValue": {}
|
|
44
|
+
},
|
|
45
|
+
"required": ["operation", "field"]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"properties": {
|
|
49
|
+
"operation": { "type": "string", "enum": ["ValidationRuleChange"] },
|
|
50
|
+
"field": { "type": "string" },
|
|
51
|
+
"minLength": { "type": "integer" },
|
|
52
|
+
"defaultValue": {}
|
|
53
|
+
},
|
|
54
|
+
"required": ["operation", "field"]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "render-conditions.schema",
|
|
4
|
+
"type": "array",
|
|
5
|
+
"items": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"when": { "type": "string" },
|
|
9
|
+
"outcomes": {
|
|
10
|
+
"type": "array",
|
|
11
|
+
"items": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"value": { "type": ["string", "number", "boolean"] },
|
|
15
|
+
"className": { "type": "string" },
|
|
16
|
+
"content": { "type": "string" }
|
|
17
|
+
},
|
|
18
|
+
"required": ["value", "content"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": ["when", "outcomes"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Ajv, { ErrorObject } from 'ajv'
|
|
2
|
+
import conditionsSchema from './conditions.schema.json'
|
|
3
|
+
import { IDataCondition_Render } from '../../../types'
|
|
4
|
+
|
|
5
|
+
export const validateRenderConditionsData = (data: IDataCondition_Render) => {
|
|
6
|
+
const ajv = new Ajv({ allowUnionTypes: true })
|
|
7
|
+
const validate = ajv.compile(conditionsSchema)
|
|
8
|
+
|
|
9
|
+
const isValid = validate(data)
|
|
10
|
+
|
|
11
|
+
let errors: ErrorObject[] = []
|
|
12
|
+
if (!isValid) errors = validate.errors || []
|
|
13
|
+
|
|
14
|
+
return { isValid, errors }
|
|
15
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Button } from 'antd'
|
|
2
|
+
|
|
3
|
+
type ButtonProps = {
|
|
4
|
+
primary?: boolean
|
|
5
|
+
secondary?: boolean
|
|
6
|
+
danger?: boolean
|
|
7
|
+
link?: boolean
|
|
8
|
+
icon?: boolean
|
|
9
|
+
outline?: boolean
|
|
10
|
+
short?: boolean
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
loading?: boolean
|
|
13
|
+
className?: string
|
|
14
|
+
title?: string
|
|
15
|
+
children: any
|
|
16
|
+
} & (
|
|
17
|
+
| { onClick: (e: React.MouseEvent) => void; isFormSubmit?: boolean; form?: string }
|
|
18
|
+
| { onClick?: (e: React.MouseEvent) => void; isFormSubmit: boolean; form: string }
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// FP = Filler Portal
|
|
22
|
+
export const ButtonFP = ({
|
|
23
|
+
primary,
|
|
24
|
+
danger,
|
|
25
|
+
link,
|
|
26
|
+
icon,
|
|
27
|
+
outline = false,
|
|
28
|
+
disabled = false,
|
|
29
|
+
loading = false,
|
|
30
|
+
short = false,
|
|
31
|
+
className,
|
|
32
|
+
children,
|
|
33
|
+
title = '',
|
|
34
|
+
form,
|
|
35
|
+
isFormSubmit = false,
|
|
36
|
+
onClick,
|
|
37
|
+
}: ButtonProps): JSX.Element => {
|
|
38
|
+
const defaultClass = `${outline ? 'btn-outline' : ''} ${short ? 'btn-short text-12' : ''}`
|
|
39
|
+
if (!className) className = defaultClass
|
|
40
|
+
else className = `${className} ${defaultClass}`
|
|
41
|
+
|
|
42
|
+
const buttonProps = {
|
|
43
|
+
disabled,
|
|
44
|
+
loading,
|
|
45
|
+
title,
|
|
46
|
+
className,
|
|
47
|
+
form,
|
|
48
|
+
onClick: (e: React.MouseEvent) => onClick && onClick(e),
|
|
49
|
+
}
|
|
50
|
+
const htmlTypeProp = isFormSubmit ? 'submit' : 'button'
|
|
51
|
+
|
|
52
|
+
if (primary || link)
|
|
53
|
+
return (
|
|
54
|
+
<Button type={primary ? 'primary' : 'link'} {...buttonProps} htmlType={htmlTypeProp}>
|
|
55
|
+
<div className="flex items-center gap-2">{children}</div>
|
|
56
|
+
</Button>
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if (icon)
|
|
60
|
+
return (
|
|
61
|
+
<Button {...buttonProps} className="btn-icon" htmlType={htmlTypeProp}>
|
|
62
|
+
<div className="p-1 rounded-full">{children}</div>
|
|
63
|
+
</Button>
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// secondary || danger || default
|
|
67
|
+
return (
|
|
68
|
+
<Button danger={danger ?? false} {...buttonProps} htmlType={htmlTypeProp}>
|
|
69
|
+
<div className="flex items-center gap-2">{children}</div>
|
|
70
|
+
</Button>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import { useParams } from 'react-router-dom'
|
|
3
|
+
import { LOCAL_STORAGE_KEYS_ENUM } from '../../../constants'
|
|
4
|
+
|
|
5
|
+
export const useFindDynamiForm = () => {
|
|
6
|
+
const { formName } = useParams<{ formName: string }>()
|
|
7
|
+
const [foundItem, setFoundItem] = useState<IDynamicForm | undefined | null>(undefined)
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!formName) return
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const storedData = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms)
|
|
14
|
+
if (storedData) {
|
|
15
|
+
const parsedData: IDynamicForm[] = JSON.parse(storedData)
|
|
16
|
+
const item = parsedData.find((entry) => entry.name.split(' ').join('-').toLocaleLowerCase() === formName)
|
|
17
|
+
setFoundItem(item ?? null)
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('Error reading or parsing localStorage data', error)
|
|
21
|
+
}
|
|
22
|
+
}, [formName])
|
|
23
|
+
|
|
24
|
+
return foundItem
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface IDynamicForm {
|
|
28
|
+
id: number
|
|
29
|
+
keepVersionHistory: boolean
|
|
30
|
+
logo: string | null
|
|
31
|
+
name: string
|
|
32
|
+
version: number
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useState, useTransition } from 'react'
|
|
2
|
+
|
|
3
|
+
export const useLazyModalOpener = () => {
|
|
4
|
+
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
5
|
+
const [isPendingTransition, startTransition] = useTransition()
|
|
6
|
+
|
|
7
|
+
const openModal = () => {
|
|
8
|
+
startTransition(() => {
|
|
9
|
+
setIsModalOpen(true)
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const closeModal = () => {
|
|
14
|
+
startTransition(() => {
|
|
15
|
+
setIsModalOpen(false)
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { isModalOpen, isPendingTransition, openModal, closeModal }
|
|
20
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import React, { createContext, ReactNode, useContext } from 'react'
|
|
2
|
+
import { Modal, notification, NotificationArgsProps } from 'antd'
|
|
3
|
+
|
|
4
|
+
type ModalOptions = {
|
|
5
|
+
title?: string
|
|
6
|
+
content?: React.ReactNode
|
|
7
|
+
okText?: string
|
|
8
|
+
cancelText?: string
|
|
9
|
+
centered?: boolean
|
|
10
|
+
width?: number | string
|
|
11
|
+
onOk?: () => Promise<void> | void
|
|
12
|
+
onCancel?: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const defaultOptions: Partial<ModalOptions> = {
|
|
16
|
+
title: 'Confirmation',
|
|
17
|
+
content: 'Are you sure to proceed?',
|
|
18
|
+
okText: 'Continue',
|
|
19
|
+
cancelText: 'Cancel',
|
|
20
|
+
width: 520,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface NotificationContextType {
|
|
24
|
+
openNotification: (options: NotificationArgsProps) => void
|
|
25
|
+
success: (options: NotificationArgsProps) => void
|
|
26
|
+
error: (options: NotificationArgsProps) => void
|
|
27
|
+
warning: (options: NotificationArgsProps) => void
|
|
28
|
+
destroy: () => void
|
|
29
|
+
confirmModal: (options: ModalOptions) => void
|
|
30
|
+
warningModal: (options: ModalOptions) => void
|
|
31
|
+
infoModal: (options: ModalOptions) => void
|
|
32
|
+
errorModal: (options: ModalOptions) => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const NotificationContext = createContext<NotificationContextType | undefined>(undefined)
|
|
36
|
+
|
|
37
|
+
let notificationInstance: NotificationContextType | null = null
|
|
38
|
+
|
|
39
|
+
export const AntdNotificationProvider = ({ children }: { children: ReactNode }) => {
|
|
40
|
+
const [notificationApi, notificationContextHolder] = notification.useNotification()
|
|
41
|
+
const [modalApi, modalContextHolder] = Modal.useModal()
|
|
42
|
+
|
|
43
|
+
const openNotification = (options: NotificationArgsProps) => {
|
|
44
|
+
notificationApi.open(options)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const success = (options: NotificationArgsProps) => {
|
|
48
|
+
notificationApi.success({ ...options, onClick: () => notificationApi.destroy() })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const error = (options: NotificationArgsProps) => {
|
|
52
|
+
notificationApi.error({ ...options, onClick: () => notificationApi.destroy() })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const warning = (options: NotificationArgsProps) => {
|
|
56
|
+
notificationApi.warning({ ...options, onClick: () => notificationApi.destroy() })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const destroy = () => notificationApi.destroy()
|
|
60
|
+
|
|
61
|
+
const confirmModal = (options: ModalOptions) => {
|
|
62
|
+
modalApi.confirm({
|
|
63
|
+
...defaultOptions,
|
|
64
|
+
...options,
|
|
65
|
+
onOk: async () => {
|
|
66
|
+
try {
|
|
67
|
+
if (options.onOk) await options.onOk()
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Error in onOk:', error)
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
onCancel: options.onCancel || (() => {}),
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const warningModal = (options: ModalOptions) => {
|
|
77
|
+
modalApi.warning({
|
|
78
|
+
...defaultOptions,
|
|
79
|
+
...options,
|
|
80
|
+
onOk: async () => {
|
|
81
|
+
try {
|
|
82
|
+
if (options.onOk) await options.onOk()
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('Error in onOk:', error)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
onCancel: options.onCancel || (() => {}),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const infoModal = (options: ModalOptions) => {
|
|
92
|
+
modalApi.info({
|
|
93
|
+
...defaultOptions,
|
|
94
|
+
...options,
|
|
95
|
+
onOk: async () => {
|
|
96
|
+
try {
|
|
97
|
+
if (options.onOk) await options.onOk()
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Error in onOk:', error)
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
onCancel: options.onCancel || (() => {}),
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const errorModal = (options: ModalOptions) => {
|
|
107
|
+
modalApi.error({
|
|
108
|
+
...defaultOptions,
|
|
109
|
+
...options,
|
|
110
|
+
onOk: async () => {
|
|
111
|
+
try {
|
|
112
|
+
if (options.onOk) await options.onOk()
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('Error in onOk:', error)
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
onCancel: options.onCancel || (() => {}),
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const instance = {
|
|
122
|
+
openNotification,
|
|
123
|
+
success,
|
|
124
|
+
error,
|
|
125
|
+
warning,
|
|
126
|
+
destroy,
|
|
127
|
+
confirmModal,
|
|
128
|
+
warningModal,
|
|
129
|
+
infoModal,
|
|
130
|
+
errorModal,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
notificationInstance = instance
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<NotificationContext.Provider value={instance}>
|
|
137
|
+
{notificationContextHolder}
|
|
138
|
+
{modalContextHolder}
|
|
139
|
+
{children}
|
|
140
|
+
</NotificationContext.Provider>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const useNotification = () => {
|
|
145
|
+
const context = useContext(NotificationContext)
|
|
146
|
+
if (!context) {
|
|
147
|
+
throw new Error('useGlobalNotification must be used within a NotificationProvider')
|
|
148
|
+
}
|
|
149
|
+
return context
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const getNotificationInstance = (): NotificationContextType => {
|
|
153
|
+
if (!notificationInstance) {
|
|
154
|
+
throw new Error('AntdNotificationProvider is not initialized.')
|
|
155
|
+
}
|
|
156
|
+
return notificationInstance
|
|
157
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FaPencilAlt, FaSlash } from 'react-icons/fa'
|
|
2
|
+
|
|
3
|
+
export const DisabledFieldIndicator = ({
|
|
4
|
+
top = 30,
|
|
5
|
+
right = 15,
|
|
6
|
+
left,
|
|
7
|
+
}: {
|
|
8
|
+
top?: number
|
|
9
|
+
right?: number
|
|
10
|
+
left?: number
|
|
11
|
+
}) => (
|
|
12
|
+
<div style={{ position: 'absolute', top, right, left, cursor: 'no-drop', color: '#a1a1a1' }}>
|
|
13
|
+
<div className="absolute">
|
|
14
|
+
<FaPencilAlt />
|
|
15
|
+
</div>
|
|
16
|
+
<div>
|
|
17
|
+
<FaSlash />
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Tooltip } from 'antd'
|
|
2
|
+
import { FaExclamationTriangle } from 'react-icons/fa'
|
|
3
|
+
|
|
4
|
+
export default function WarningIcon({ tooltip = '', size = 16 }: { tooltip: any; size?: number }) {
|
|
5
|
+
return (
|
|
6
|
+
<Tooltip title={tooltip}>
|
|
7
|
+
<FaExclamationTriangle className="text-warning" size={size} />
|
|
8
|
+
</Tooltip>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ResponsivenessDeviceEnum } from '../../../../enums'
|
|
2
|
+
import { getFlexContainerStyle, getFlexItemStyle } from '../../../../functions'
|
|
3
|
+
import { IDataRender_ButtonProps, IFormLayoutRow } from '../../../../types'
|
|
4
|
+
import LayoutRendererCol from '../2-col'
|
|
5
|
+
import { ReactElement, ReactNode } from 'react'
|
|
6
|
+
|
|
7
|
+
export const LayoutRendererRow = ({
|
|
8
|
+
rowData,
|
|
9
|
+
titleComponent,
|
|
10
|
+
breakpointDevice = ResponsivenessDeviceEnum.Default,
|
|
11
|
+
renderButton,
|
|
12
|
+
}: {
|
|
13
|
+
rowData: IFormLayoutRow
|
|
14
|
+
titleComponent?: ReactNode
|
|
15
|
+
breakpointDevice?: ResponsivenessDeviceEnum
|
|
16
|
+
renderButton?: (props: IDataRender_ButtonProps) => ReactElement
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div style={{ ...(rowData.style ?? {}), ...getFlexContainerStyle(breakpointDevice, rowData.responsiveness) }}>
|
|
20
|
+
{rowData.children.map((col, colIdx) => (
|
|
21
|
+
<div key={colIdx} style={getFlexItemStyle(breakpointDevice, colIdx, rowData.responsiveness)}>
|
|
22
|
+
<LayoutRendererCol key={colIdx} colData={col} titleComponent={titleComponent} renderButton={renderButton} />
|
|
23
|
+
</div>
|
|
24
|
+
))}
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react'
|
|
2
|
+
import { LayoutRendererRow } from '../1-row'
|
|
3
|
+
import LayoutRendererElement from '../3-element'
|
|
4
|
+
import { IDataRender_ButtonProps, IFormLayoutCol, IFormLayoutElement, IFormLayoutRow } from '../../../../types'
|
|
5
|
+
import { FormLayoutNodeEnum } from '../../../../enums'
|
|
6
|
+
|
|
7
|
+
export default function LayoutRendererCol({
|
|
8
|
+
colData,
|
|
9
|
+
titleComponent,
|
|
10
|
+
renderButton,
|
|
11
|
+
}: {
|
|
12
|
+
colData: IFormLayoutCol
|
|
13
|
+
titleComponent?: ReactNode
|
|
14
|
+
renderButton?: (props: IDataRender_ButtonProps) => ReactElement
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="space-y-2">
|
|
18
|
+
{colData.children.map((item: IFormLayoutRow | IFormLayoutElement) =>
|
|
19
|
+
item.nodeType === FormLayoutNodeEnum.Row ? (
|
|
20
|
+
<LayoutRendererRow key={item.id} rowData={item} titleComponent={titleComponent} renderButton={renderButton} />
|
|
21
|
+
) : (
|
|
22
|
+
<div className="flex flex-col" key={item.id}>
|
|
23
|
+
<LayoutRendererElement elementData={item} titleComponent={titleComponent} renderButton={renderButton} />
|
|
24
|
+
{item.props && 'description' in item.props && (
|
|
25
|
+
<span className="text-neutral text-12 italic">{item.props.description}</span>
|
|
26
|
+
)}
|
|
27
|
+
</div>
|
|
28
|
+
),
|
|
29
|
+
)}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|