form-craft-package 1.0.4 → 1.1.1

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 (34) hide show
  1. package/index.ts +2 -1
  2. package/package.json +6 -2
  3. package/src/components/common/button.tsx +1 -1
  4. package/src/components/common/custom-hooks/index.ts +2 -0
  5. package/src/components/common/custom-hooks/use-find-dynamic-form.ts +1 -8
  6. package/src/components/common/disabled-field-indicator.tsx +1 -1
  7. package/src/components/common/loading-skeletons/details.tsx +11 -0
  8. package/src/components/common/loading-skeletons/index.tsx +3 -0
  9. package/src/components/common/loading-skeletons/table.tsx +30 -0
  10. package/src/components/form/1-list/header.tsx +118 -0
  11. package/src/components/form/1-list/index.tsx +122 -0
  12. package/src/components/form/2-details/index.tsx +124 -0
  13. package/src/components/form/layout-renderer/1-row/index.tsx +40 -15
  14. package/src/components/form/layout-renderer/2-col/index.tsx +43 -21
  15. package/src/components/form/layout-renderer/3-element/1-dynamic-button.tsx +288 -248
  16. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +0 -4
  17. package/src/components/form/layout-renderer/3-element/3-read-only.tsx +29 -0
  18. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +37 -0
  19. package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +42 -0
  20. package/src/components/form/layout-renderer/3-element/6-signature.tsx +93 -0
  21. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +147 -0
  22. package/src/components/form/layout-renderer/3-element/index.tsx +66 -32
  23. package/src/components/index.tsx +3 -0
  24. package/src/components/modals/form-data-loading.modal.tsx +4 -4
  25. package/src/constants.ts +1 -1
  26. package/src/enums.ts +12 -5
  27. package/src/functions/data-render-functions.tsx +90 -0
  28. package/src/functions/index.ts +82 -2
  29. package/src/types/{form-data-list → form}/index.ts +8 -0
  30. package/src/types/index.ts +6 -11
  31. package/src/types/layout-elements/element-data-render-logic.ts +2 -1
  32. package/src/types/layout-elements/index.ts +28 -20
  33. package/src/types/layout-elements/style.ts +8 -3
  34. package/src/functions/data-list-functions.tsx +0 -41
package/index.ts CHANGED
@@ -5,4 +5,5 @@ export * from './src/functions'
5
5
  export * from './src/functions/json-handlers'
6
6
  export * from './src/functions/form-schema-validator'
7
7
  export * from './src/functions/get-element-props'
8
- export * from './src/components'
8
+ export * from './src/components'
9
+ export * from './src/components/common/custom-hooks'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.0.4",
3
+ "version": "1.1.1",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -11,7 +11,10 @@
11
11
  "license": "ISC",
12
12
  "description": "",
13
13
  "dependencies": {
14
- "ajv": "^8.17.1"
14
+ "@types/react-google-recaptcha": "^2.1.9",
15
+ "ajv": "^8.17.1",
16
+ "react-google-recaptcha": "^3.1.0",
17
+ "react-signature-canvas": "^1.0.7"
15
18
  },
16
19
  "peerDependencies": {
17
20
  "antd": ">=5.21.6",
@@ -29,6 +32,7 @@
29
32
  "@types/js-cookie": "^3.0.6",
30
33
  "@types/react": "^18.3.18",
31
34
  "@types/react-dom": "^18.3.5",
35
+ "@types/react-signature-canvas": "^1.0.6",
32
36
  "react": "^18.3.1",
33
37
  "react-dom": "^18.3.1",
34
38
  "ts-node": "^10.9.2",
@@ -19,7 +19,7 @@ type ButtonProps = {
19
19
  )
20
20
 
21
21
  // FP = Filler Portal
22
- export const ButtonFP = ({
22
+ export const Button_FillerPortal = ({
23
23
  primary,
24
24
  danger,
25
25
  link,
@@ -0,0 +1,2 @@
1
+ export * from './use-find-dynamic-form'
2
+ export * from './use-notification.hook'
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect } from 'react'
2
2
  import { useParams } from 'react-router-dom'
3
3
  import { LOCAL_STORAGE_KEYS_ENUM } from '../../../constants'
4
+ import { IDynamicForm } from '../../../types'
4
5
 
5
6
  export const useFindDynamiForm = () => {
6
7
  const { formName } = useParams<{ formName: string }>()
@@ -23,11 +24,3 @@ export const useFindDynamiForm = () => {
23
24
 
24
25
  return foundItem
25
26
  }
26
-
27
- interface IDynamicForm {
28
- id: number
29
- keepVersionHistory: boolean
30
- logo: string | null
31
- name: string
32
- version: number
33
- }
@@ -9,7 +9,7 @@ export const DisabledFieldIndicator = ({
9
9
  right?: number
10
10
  left?: number
11
11
  }) => (
12
- <div style={{ position: 'absolute', top, right, left, cursor: 'no-drop', color: '#a1a1a1' }}>
12
+ <div className="absolute cursor-nodrop" style={{ top, right, left, color: '#a1a1a1' }}>
13
13
  <div className="absolute">
14
14
  <FaPencilAlt />
15
15
  </div>
@@ -0,0 +1,11 @@
1
+ import { Spin } from 'antd'
2
+
3
+ export default function FormDataListSkeleton_Details() {
4
+ return (
5
+ <div className="p-3">
6
+ <div className="text-primary italic h-[300px] text-30 rounded-md bg-white flex items-center justify-center gap-2">
7
+ <Spin size="large" /> Loading data...
8
+ </div>
9
+ </div>
10
+ )
11
+ }
@@ -0,0 +1,3 @@
1
+ export default function SkeletonBlock({ height = 32, width = '100%' }: { height?: number; width?: number | string }) {
2
+ return <div style={{ height, width }} className="skeleton-loader" />
3
+ }
@@ -0,0 +1,30 @@
1
+ import SkeletonBlock from '.'
2
+
3
+ export default function FormDataListSkeleton_Table() {
4
+ return (
5
+ <>
6
+ <div className="bg-white p-3 flex items-center justify-between rounded-md mb-2">
7
+ <SkeletonBlock width="200px" />
8
+ <div className="flex items-center gap-2">
9
+ <SkeletonBlock width="150px" />
10
+ <SkeletonBlock width="150px" />
11
+ <SkeletonBlock width="150px" />
12
+ </div>
13
+ </div>
14
+ <div className="border border-[#d4d4d8] rounded bg-white">
15
+ <div className="flex justify-between p-3 border-b border-[#d4d4d8]">
16
+ {[...Array(5)].map((_, elIdx) => (
17
+ <div key={elIdx}>
18
+ <SkeletonBlock height={20} width="150px" />
19
+ </div>
20
+ ))}
21
+ </div>
22
+ <div className="px-3 py-2 flex flex-col gap-3">
23
+ {[...Array(5)].map((_, elIdx) => (
24
+ <SkeletonBlock key={elIdx} />
25
+ ))}
26
+ </div>
27
+ </div>
28
+ </>
29
+ )
30
+ }
@@ -0,0 +1,118 @@
1
+ import { Form } from 'antd'
2
+ import { ReactNode, useEffect, useState } from 'react'
3
+ import { IReqDataConfig } from '.'
4
+ import dayjs from 'dayjs'
5
+ import { IFilterConfig, IFilterNested, IFilterSimple, IFormLayoutRow } from '../../../types'
6
+ import { extractFiltersFromLayout } from '../../../functions'
7
+ import { FilterConfigTypeEnum } from '../../../enums'
8
+ import { LayoutRendererRow } from '../layout-renderer/1-row'
9
+ import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
10
+
11
+ export default function FormDataListHeaderComponent({
12
+ layout,
13
+ userId,
14
+ formId,
15
+ titleComponent,
16
+ setPaginatedReqData,
17
+ onCustomFunctionCall,
18
+ }: IFormDataListHeaderComponent) {
19
+ const [dataListHeaderFormRef] = Form.useForm()
20
+ const [appliedFilters, setAppliedFilters] = useState<IFilterSimple>({})
21
+ const [filterConfigs, setFilterConfigs] = useState<IFilterNested>({})
22
+
23
+ useEffect(() => {
24
+ if (dataListHeaderFormRef) dataListHeaderFormRef.setFieldValue('formId', formId)
25
+ }, [dataListHeaderFormRef, formId])
26
+
27
+ useEffect(() => {
28
+ setFilterConfigs(extractFiltersFromLayout(layout))
29
+ }, [layout])
30
+
31
+ useEffect(() => {
32
+ const allCombinedFilters = {
33
+ customFilter: {},
34
+ dynamicFilter: {},
35
+ }
36
+ Object.values(appliedFilters).forEach((config) => {
37
+ if (config.type === FilterConfigTypeEnum.NoFilter) return
38
+
39
+ if (config.config?.customFilter)
40
+ allCombinedFilters.customFilter = { ...allCombinedFilters.customFilter, ...config.config.customFilter }
41
+ if (config.config?.dynamicFilter)
42
+ allCombinedFilters.dynamicFilter = { ...allCombinedFilters.dynamicFilter, ...config.config.dynamicFilter }
43
+
44
+ if (config.type === FilterConfigTypeEnum.ByMe)
45
+ allCombinedFilters.customFilter = { ...allCombinedFilters.customFilter, createdById: userId }
46
+ })
47
+
48
+ setPaginatedReqData((c) => ({
49
+ ...c,
50
+ customFilter: allCombinedFilters.customFilter,
51
+ dynamicFilter: JSON.stringify(allCombinedFilters.dynamicFilter),
52
+ }))
53
+ }, [appliedFilters])
54
+
55
+ return (
56
+ <Form
57
+ layout="vertical"
58
+ form={dataListHeaderFormRef}
59
+ onValuesChange={(changedValues) => {
60
+ Object.entries(changedValues).forEach(([key, value]) => {
61
+ const config = filterConfigs[key]
62
+ if (!config) {
63
+ console.warn(`Key '${key}' does not have a filter config`)
64
+ return
65
+ }
66
+ if ('type' in config) {
67
+ if (typeof value !== 'string') value = dayjs(value as Date).toISOString()
68
+
69
+ if (config.config && 'dynamicFilter' in config.config && config.config.dynamicFilter) {
70
+ let stringifiedDynamicFilter = JSON.stringify(config.config.dynamicFilter)
71
+ stringifiedDynamicFilter = stringifiedDynamicFilter.replaceAll('{{VALUE}}', value as string)
72
+
73
+ config.config.dynamicFilter = JSON.parse(stringifiedDynamicFilter)
74
+ }
75
+ if (config.config && 'customFilter' in config.config && config.config.customFilter) {
76
+ let stringifiedCustomFilter = JSON.stringify(config.config.customFilter)
77
+ stringifiedCustomFilter = stringifiedCustomFilter.replaceAll('{{VALUE}}', value as string)
78
+
79
+ config.config.customFilter = JSON.parse(stringifiedCustomFilter)
80
+ }
81
+
82
+ setAppliedFilters((c) => ({ ...c, [key]: config as IFilterConfig }))
83
+ } else {
84
+ const nestedConfig = config as IFilterSimple
85
+ if (typeof value === 'string' && value in nestedConfig)
86
+ setAppliedFilters((c) => ({ ...c, [key]: nestedConfig[value as string] as IFilterConfig }))
87
+ else console.warn(`Invalid key '${value}' for nestedConfig`)
88
+ }
89
+ })
90
+ }}
91
+ >
92
+ {layout.map((row, rowIdx) => (
93
+ <LayoutRendererRow
94
+ key={rowIdx}
95
+ rowData={row}
96
+ formRef={dataListHeaderFormRef}
97
+ titleComponent={titleComponent}
98
+ renderButton={(btnProps, conditions) => (
99
+ <DynamicFormButtonRender
100
+ btnProps={btnProps}
101
+ conditions={conditions}
102
+ formRef={dataListHeaderFormRef}
103
+ onCustomFunctionCall={onCustomFunctionCall}
104
+ />
105
+ )}
106
+ />
107
+ ))}
108
+ </Form>
109
+ )
110
+ }
111
+
112
+ type IFormDataListHeaderComponent = {
113
+ layout: IFormLayoutRow[]
114
+ setPaginatedReqData: React.Dispatch<React.SetStateAction<IReqDataConfig>>
115
+ titleComponent?: ReactNode
116
+ formId?: number
117
+ userId?: string | number
118
+ } & ICustomFunctionCall
@@ -0,0 +1,122 @@
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import client from '../../../functions/axios-handler'
3
+ import { parseJSON } from '../../../functions/json-handlers'
4
+ import { generateTableColumns } from '../../../functions'
5
+ import FormDataListSkeleton_Table from '../../common/loading-skeletons/table'
6
+ import FormDataListHeaderComponent from './header'
7
+ import { FormDataListViewTypeEnum } from '../../../enums'
8
+ import { Table } from 'antd'
9
+ import { IFormData, IFormDataListConfig, IFormLayoutRow, IFormSchema, ITableColumn } from '../../../types'
10
+ import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
11
+
12
+ function FormDataListComponent({ formId, userId, onCustomFunctionCall }: IFormDataListComponent) {
13
+ const [loadings, setLoadings] = useState({ schema: true, data: true })
14
+ const [tableColumns, setTableColumns] = useState<ITableColumn[]>([])
15
+ const [paginatedReqData, setPaginatedReqData] = useState<IReqDataConfig>({
16
+ customFilter: { deletedDate: null },
17
+ dynamicFilter: JSON.stringify({}),
18
+ sort: {},
19
+ })
20
+ const [pagination, setPagination] = useState<IPagination | undefined>()
21
+ const [otherGenericConfigs, setOtherGenericConfigs] = useState<Partial<IFormDataListConfig> | undefined>(undefined)
22
+ const [formData, setFormData] = useState({ data: [], total: 0 })
23
+ const [headerLayout, setHeaderLayout] = useState<IFormLayoutRow[]>([])
24
+
25
+ useEffect(() => {
26
+ if (formId) {
27
+ client
28
+ .get(`/api/form/${formId}`)
29
+ .then((res) => {
30
+ if (res.status === 200) {
31
+ const parsedData: IFormSchema | null = parseJSON(res.data.data)
32
+ if (parsedData) {
33
+ const { elements, header, pagination: apiPagination, ...restConfigs } = parsedData.dataListConfig
34
+ setTableColumns(generateTableColumns(elements))
35
+ setPagination({
36
+ current: 1,
37
+ pageSize: apiPagination?.defaultPageSize ?? 10,
38
+ showSizeChanger: apiPagination?.showSizeChanger ?? false,
39
+ pageSizeOptions: apiPagination?.pageSizeOptions ?? [],
40
+ })
41
+ setHeaderLayout(header.layout)
42
+ setOtherGenericConfigs(restConfigs)
43
+ }
44
+ }
45
+ })
46
+ .finally(() => setLoadings((c) => ({ ...c, schema: false })))
47
+ }
48
+ }, [formId])
49
+
50
+ const fetchFormDataList = useCallback(
51
+ (dynamicFormId: number) => {
52
+ if (!pagination) {
53
+ setLoadings((c) => ({ ...c, data: false }))
54
+ return
55
+ }
56
+
57
+ client
58
+ .post(`/api/formdata/list/${dynamicFormId}`, paginatedReqData)
59
+ .then((res) => {
60
+ if (res.status === 200)
61
+ setFormData({ ...res.data, data: res.data.data.map((d: IFormData) => ({ ...d, ...JSON.parse(d.data) })) })
62
+ })
63
+ .finally(() => setLoadings((c) => ({ ...c, data: false })))
64
+ },
65
+ [paginatedReqData, pagination],
66
+ )
67
+
68
+ useEffect(() => {
69
+ if (formId) fetchFormDataList(formId)
70
+ }, [formId, fetchFormDataList])
71
+
72
+ if (loadings.schema) return <FormDataListSkeleton_Table />
73
+
74
+ return (
75
+ <div>
76
+ <FormDataListHeaderComponent
77
+ layout={headerLayout}
78
+ setPaginatedReqData={setPaginatedReqData}
79
+ userId={userId}
80
+ formId={formId}
81
+ titleComponent={
82
+ <div className="text-primary font-bold text-18 flex items-center gap-2">
83
+ <span>{otherGenericConfigs?.title}</span>
84
+ {otherGenericConfigs?.showCount && <span>({formData.total})</span>}
85
+ </div>
86
+ }
87
+ onCustomFunctionCall={onCustomFunctionCall}
88
+ />
89
+ {otherGenericConfigs?.listType === FormDataListViewTypeEnum.Table && (
90
+ <Table<IFormData>
91
+ dataSource={formData.data}
92
+ columns={tableColumns}
93
+ rowKey={(record) => record.id}
94
+ pagination={pagination}
95
+ loading={loadings.data}
96
+ onChange={(pagination, _, sorter) => {
97
+ console.log('ON TABLE CHANGE', pagination, sorter)
98
+ }}
99
+ />
100
+ )}
101
+ </div>
102
+ )
103
+ }
104
+ export { FormDataListComponent }
105
+
106
+ export interface IReqDataConfig {
107
+ customFilter: {}
108
+ dynamicFilter: {}
109
+ sort: {}
110
+ }
111
+
112
+ interface IPagination {
113
+ current: number
114
+ pageSize: number
115
+ showSizeChanger?: boolean
116
+ pageSizeOptions?: number[]
117
+ }
118
+
119
+ type IFormDataListComponent = {
120
+ formId?: number
121
+ userId: string | number
122
+ } & ICustomFunctionCall
@@ -0,0 +1,124 @@
1
+ import { Form } from 'antd'
2
+ import { useCallback, useEffect, useState } from 'react'
3
+ import { IFormLayoutRow, IFormSchema, IMigrationRule } from '../../../types'
4
+ import { NEW_FORM_DATA_IDENTIFIER } from '../../../constants'
5
+ import client from '../../../functions/axios-handler'
6
+ import { parseJSON } from '../../../functions/json-handlers'
7
+ import { LayoutRendererRow } from '../layout-renderer/1-row'
8
+ import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
9
+ import FormDataListSkeleton_Details from '../../common/loading-skeletons/details'
10
+ import { useLocation } from 'react-router-dom'
11
+ import { queryParamsToObject } from '../../../functions'
12
+
13
+ export default function FormDataDetailsComponent({
14
+ isPublic,
15
+ formId,
16
+ formKey,
17
+ formDataId,
18
+ companyKey,
19
+ baseServerUrl,
20
+ onCustomFunctionCall,
21
+ }: IFormDataDetailsComponent) {
22
+ const [formDataRef] = Form.useForm()
23
+ const [loadings, setLoadings] = useState({ layout: true, data: true })
24
+ const [layout, setLayout] = useState<IFormLayoutRow[]>([])
25
+ const location = useLocation()
26
+
27
+ useEffect(() => {
28
+ // this is for converting the screen to PDF
29
+ if (isPublic && location.search) {
30
+ const formValues = queryParamsToObject(location.search)
31
+ formDataRef.setFieldsValue(formValues)
32
+ }
33
+ }, [isPublic, location, formDataRef])
34
+
35
+ useEffect(() => {
36
+ if (formDataRef) formDataRef.setFieldsValue({ formId, formKey, baseServerUrl, formDataId, companyKey })
37
+ }, [formDataRef, formId, formKey, baseServerUrl, formDataId, companyKey])
38
+
39
+ const fetchFormData = useCallback(
40
+ (dFormId: number, migrationRules: { [key: string]: IMigrationRule[] }) => {
41
+ if (formDataId === NEW_FORM_DATA_IDENTIFIER) setLoadings((c) => ({ ...c, data: false }))
42
+ else {
43
+ client
44
+ .get(`/api/formdata/${dFormId}/${formDataId}`)
45
+ .then((res) => {
46
+ if (res.status === 200) {
47
+ const { data: jsonData, ...restFormData } = res.data
48
+ const parsedFormData = parseJSON(jsonData)
49
+ if (parsedFormData) formDataRef.setFieldsValue({ ...restFormData, ...parsedFormData })
50
+ console.log({ parsedFormData })
51
+ console.log({ migrationRules })
52
+ }
53
+ })
54
+ .finally(() => setLoadings((c) => ({ ...c, data: false })))
55
+ }
56
+ },
57
+ [formDataId, formDataRef],
58
+ )
59
+
60
+ useEffect(() => {
61
+ if (formId || formKey) {
62
+ const endpoint = isPublic ? `/api/site/${formKey}` : `/api/form/${formId}`
63
+ client
64
+ .get(endpoint)
65
+ .then((res) => {
66
+ if (res.status === 200) {
67
+ const parsedData: IFormSchema | null = parseJSON(res.data.data)
68
+ if (parsedData) {
69
+ const { layout, migrationRules, convertScreenToPdf = false } = parsedData.detailsConfig
70
+
71
+ if (isPublic) {
72
+ formDataRef.setFieldValue('convertScreenToPdf', convertScreenToPdf)
73
+ setLoadings((c) => ({ ...c, data: false }))
74
+ } else fetchFormData(formId, migrationRules)
75
+ setLayout(layout)
76
+ }
77
+ }
78
+ })
79
+ .finally(() => setLoadings((c) => ({ ...c, layout: false })))
80
+ }
81
+ }, [formId, formKey, isPublic, formDataRef, fetchFormData])
82
+
83
+ if (loadings.layout || loadings.data) return <FormDataListSkeleton_Details />
84
+
85
+ return (
86
+ <Form layout="vertical" name="dynamic_form_data_form" form={formDataRef}>
87
+ {layout.map((row, rowIdx) => (
88
+ <LayoutRendererRow
89
+ key={rowIdx}
90
+ rowData={row}
91
+ formRef={formDataRef}
92
+ renderButton={(btnProps, conditions) => (
93
+ <DynamicFormButtonRender
94
+ isPublic={isPublic}
95
+ btnProps={btnProps}
96
+ conditions={conditions}
97
+ formDataId={formDataId}
98
+ formRef={formDataRef}
99
+ onCustomFunctionCall={onCustomFunctionCall}
100
+ />
101
+ )}
102
+ />
103
+ ))}
104
+ </Form>
105
+ )
106
+ }
107
+
108
+ type IFormDataDetailsComponent = {
109
+ baseServerUrl?: string
110
+ companyKey?: string
111
+ formDataId: string
112
+ } & (IDataDetailsPublicProps | IDataDetailsPrivateProps) &
113
+ ICustomFunctionCall
114
+
115
+ interface IDataDetailsPublicProps {
116
+ isPublic: true
117
+ formKey: string
118
+ formId?: number
119
+ }
120
+ interface IDataDetailsPrivateProps {
121
+ isPublic: false
122
+ formId: number
123
+ formKey?: string
124
+ }
@@ -1,27 +1,52 @@
1
+ import { FormInstance } from 'antd'
1
2
  import { ResponsivenessDeviceEnum } from '../../../../enums'
2
- import { getFlexContainerStyle, getFlexItemStyle } from '../../../../functions'
3
- import { IDataRender_ButtonProps, IFormLayoutRow } from '../../../../types'
3
+ import { getFlexContainerStyle, getFlexItemStyle, kebabCaseToCamelCase } from '../../../../functions'
4
+ import { IDataRender_ButtonProps, IFormLayoutElementConditions, IFormLayoutRow } from '../../../../types'
4
5
  import LayoutRendererCol from '../2-col'
5
- import { ReactElement, ReactNode } from 'react'
6
+ import { ReactElement, ReactNode, useMemo } from 'react'
6
7
 
7
8
  export const LayoutRendererRow = ({
8
9
  rowData,
10
+ formRef,
9
11
  titleComponent,
10
12
  breakpointDevice = ResponsivenessDeviceEnum.Default,
11
13
  renderButton,
12
- }: {
14
+ }: ILayoutRendererRow) => {
15
+ const styleConfig = useMemo(() => {
16
+ if (!rowData.style || !rowData.style[breakpointDevice]) return {}
17
+ return kebabCaseToCamelCase(rowData.style[breakpointDevice])
18
+ }, [rowData.style, breakpointDevice])
19
+
20
+ return (
21
+ <>
22
+ {rowData.props?.name && <div>{rowData.props?.name}</div>}
23
+ <div
24
+ style={{
25
+ ...styleConfig,
26
+ ...getFlexContainerStyle(breakpointDevice, rowData.responsiveness),
27
+ }}
28
+ >
29
+ {rowData.children.map((col, colIdx) => (
30
+ <div key={colIdx} style={getFlexItemStyle(breakpointDevice, colIdx, rowData.responsiveness)}>
31
+ <LayoutRendererCol
32
+ key={colIdx}
33
+ colData={col}
34
+ formRef={formRef}
35
+ titleComponent={titleComponent}
36
+ breakpointDevice={breakpointDevice}
37
+ renderButton={renderButton}
38
+ />
39
+ </div>
40
+ ))}
41
+ </div>
42
+ </>
43
+ )
44
+ }
45
+
46
+ interface ILayoutRendererRow {
13
47
  rowData: IFormLayoutRow
48
+ formRef: FormInstance
14
49
  titleComponent?: ReactNode
15
50
  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
- )
51
+ renderButton?: (props: IDataRender_ButtonProps, conditions?: IFormLayoutElementConditions) => ReactElement
27
52
  }
@@ -1,32 +1,54 @@
1
1
  import { ReactElement, ReactNode } from 'react'
2
2
  import { LayoutRendererRow } from '../1-row'
3
3
  import LayoutRendererElement from '../3-element'
4
- import { IDataRender_ButtonProps, IFormLayoutCol, IFormLayoutElement, IFormLayoutRow } from '../../../../types'
5
- import { FormLayoutNodeEnum } from '../../../../enums'
4
+ import {
5
+ IDataRender_ButtonProps,
6
+ IFormLayoutCol,
7
+ IFormLayoutElement,
8
+ IFormLayoutElementConditions,
9
+ IFormLayoutRow,
10
+ } from '../../../../types'
11
+ import { FormLayoutNodeEnum, ResponsivenessDeviceEnum } from '../../../../enums'
12
+ import { FormInstance } from 'antd'
6
13
 
7
14
  export default function LayoutRendererCol({
8
15
  colData,
16
+ formRef,
9
17
  titleComponent,
18
+ breakpointDevice,
10
19
  renderButton,
11
- }: {
20
+ }: ILayoutRendererCol) {
21
+ return colData.children.map((item: IFormLayoutRow | IFormLayoutElement) =>
22
+ item.nodeType === FormLayoutNodeEnum.Row ? (
23
+ <LayoutRendererRow
24
+ key={item.id}
25
+ rowData={item}
26
+ formRef={formRef}
27
+ titleComponent={titleComponent}
28
+ breakpointDevice={breakpointDevice}
29
+ renderButton={renderButton}
30
+ />
31
+ ) : (
32
+ <div className="flex flex-col" key={item.id}>
33
+ <LayoutRendererElement
34
+ elementData={item}
35
+ formRef={formRef}
36
+ titleComponent={titleComponent}
37
+ breakpointDevice={breakpointDevice}
38
+ renderButton={renderButton}
39
+ />
40
+ {item.props && 'description' in item.props && (
41
+ <span className="text-neutral text-12 italic">{item.props.description}</span>
42
+ )}
43
+ </div>
44
+ ),
45
+ )
46
+ }
47
+
48
+ interface ILayoutRendererCol {
12
49
  colData: IFormLayoutCol
50
+ formRef: FormInstance
13
51
  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
- )
52
+ breakpointDevice: ResponsivenessDeviceEnum
53
+ renderButton?: (props: IDataRender_ButtonProps, conditions?: IFormLayoutElementConditions) => ReactElement
32
54
  }