form-craft-package 1.8.2-dev.1 → 1.8.2-dev.2

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/components/common/custom-hooks/index.ts +1 -0
  3. package/src/components/common/custom-hooks/use-login-handler.ts +3 -3
  4. package/src/components/common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook.ts +62 -22
  5. package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts +54 -42
  6. package/src/components/common/custom-hooks/use-set-persistent-domain.hook.ts +20 -0
  7. package/src/components/companies/3-config-provider/index.tsx +27 -2
  8. package/src/components/form/2-details/index.tsx +14 -5
  9. package/src/components/form/layout-renderer/1-row/index.tsx +5 -1
  10. package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +69 -28
  11. package/src/components/form/layout-renderer/3-element/10-currency.tsx +4 -2
  12. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +4 -2
  13. package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +4 -7
  14. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +203 -225
  15. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +4 -6
  16. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +4 -1
  17. package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +4 -7
  18. package/src/components/form/layout-renderer/3-element/6-signature.tsx +4 -2
  19. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +78 -26
  20. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +4 -2
  21. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +4 -2
  22. package/src/components/form/layout-renderer/3-element/index.tsx +125 -86
  23. package/src/enums/form.enum.ts +4 -0
  24. package/src/enums/index.ts +1 -0
  25. package/src/functions/companies/index.tsx +12 -0
  26. package/src/functions/companies/use-company-config.tsx +14 -10
  27. package/src/functions/forms/data-render-functions.tsx +0 -1
  28. package/src/functions/forms/get-element-props.ts +2 -2
  29. package/src/functions/forms/index.ts +3 -1
  30. package/src/types/companies/site-layout/authenticated/index.tsx +5 -2
  31. package/src/types/forms/index.ts +9 -4
  32. package/src/types/forms/layout-elements/button.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.8.2-dev.1",
3
+ "version": "1.8.2-dev.2",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -3,3 +3,4 @@ export * from './use-notification.hook'
3
3
  export * from './use-lazy-modal-opener.hook'
4
4
  export * from './use-breadcrumb.hook'
5
5
  export * from './use-dayjs-extender.hook'
6
+ export * from './use-set-persistent-domain.hook'
@@ -1,12 +1,12 @@
1
1
  import { useReducer, useState } from 'react'
2
2
  import { useNavigate } from 'react-router-dom'
3
3
  import client, { auth } from '../../../api/client'
4
- import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
5
4
  import { cookieHandler } from '../../../functions/cookie-handler'
6
5
  import { message } from 'antd'
6
+ import { getLocalStorageDomain } from '../../../functions'
7
7
  export function useLoginHandler() {
8
8
  const navigate = useNavigate()
9
- const domain = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Domain) || ''
9
+ const domain = getLocalStorageDomain()
10
10
  const [loading, updateLoading] = useReducer((currState: any, updated: any) => ({ ...currState, ...updated }), {
11
11
  login: false,
12
12
  })
@@ -27,7 +27,7 @@ export function useLoginHandler() {
27
27
  const { authRes, initialPath } = await auth(
28
28
  values.email,
29
29
  values.password,
30
- localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Domain) ?? '',
30
+ getLocalStorageDomain(),
31
31
  values.type || 'password',
32
32
  )
33
33
  if (authRes.status === 200) {
@@ -1,8 +1,9 @@
1
- import { useEffect, useCallback } from 'react'
1
+ import { useEffect } from 'react'
2
2
  import useGetCurrentBreakpoint from '../use-window-width.hook'
3
3
  import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
4
4
  import { disabledStore } from './visibility-store'
5
5
  import { isValidationsMet } from './visibility-utils'
6
+ import { FormInstance } from 'antd'
6
7
  import {
7
8
  IDndLayoutElement,
8
9
  IDndLayoutRow,
@@ -12,16 +13,64 @@ import {
12
13
 
13
14
  export function useSetDisabledElements(
14
15
  layoutConfig?: IDndLayoutStructure_Responsive,
15
- formValues?: { [key: string]: any },
16
+ formDataRef?: FormInstance,
16
17
  formDataId?: string,
17
18
  ): void {
18
19
  const currentBreakpoint = useGetCurrentBreakpoint()
20
+
21
+ useEffect(() => {
22
+ // during the initial renders, handle the initially disabled fields
23
+ if (!formDataRef) return
24
+
25
+ const allValues = formDataRef.getFieldsValue()
26
+
27
+ const layout: IDndLayoutRow[] =
28
+ layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
29
+ const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
30
+ const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
31
+ (acc, el) => {
32
+ const elConditions = el.conditions?.[FormElementConditionalKeyEnum.EnableIf]
33
+ if (elConditions && Array.isArray(elConditions) && elConditions.length > 0) acc[el.key] = el.conditions!
34
+
35
+ return acc
36
+ },
37
+ {} as Record<string, IFormLayoutElementConditions>,
38
+ )
39
+
40
+ const allInputsLoaded =
41
+ layout.length > 0 &&
42
+ Object.keys(elementsMap).length > 0 &&
43
+ Object.keys(configsPerElement).length > 0 &&
44
+ Object.values(allValues).filter(Boolean).length > 0
45
+
46
+ if (!allInputsLoaded) return
47
+
48
+ handleDisabledSet(allValues, currentBreakpoint, layoutConfig, formDataId)
49
+ }, [formDataRef, layoutConfig, currentBreakpoint, formDataId])
50
+ }
51
+
52
+ export function handleDisabledSet(
53
+ allValues: Record<string, any>,
54
+ currentBreakpoint: DeviceBreakpointEnum,
55
+ layoutConfig?: IDndLayoutStructure_Responsive,
56
+ formDataId?: string,
57
+ ) {
58
+ const disabledSet = computeDisabledSet(allValues, currentBreakpoint, layoutConfig, formDataId)
59
+ disabledStore.setIds([...disabledSet])
60
+ }
61
+
62
+ function computeDisabledSet(
63
+ allValues: Record<string, any>,
64
+ currentBreakpoint: DeviceBreakpointEnum,
65
+ layoutConfig?: IDndLayoutStructure_Responsive,
66
+ formDataId?: string,
67
+ ): Set<string> {
19
68
  const layout: IDndLayoutRow[] =
20
69
  layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
21
70
 
22
71
  const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
23
- const dataValues: { [key: string]: any } = formValues && typeof formValues === 'object' ? formValues : {}
24
72
 
73
+ // Pull out only those elements which have EnableIf conditions
25
74
  const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
26
75
  (acc, el) => {
27
76
  const elConditions = el.conditions?.[FormElementConditionalKeyEnum.EnableIf]
@@ -36,29 +85,20 @@ export function useSetDisabledElements(
36
85
  layout.length > 0 &&
37
86
  Object.keys(elementsMap).length > 0 &&
38
87
  Object.keys(configsPerElement).length > 0 &&
39
- Object.values(dataValues).length > 0 &&
88
+ Object.values(allValues).length > 0 &&
40
89
  !!formDataId
41
90
 
42
- const computeIndividuallyDisabled = useCallback((): Set<string> => {
43
- if (!allInputsLoaded) return new Set()
44
-
45
- const disabledSet = new Set<string>()
46
- Object.values(elementsMap).forEach(({ key }) => {
47
- const elementConfigs = configsPerElement[key]
48
- const shouldEnable = isValidationsMet(FormElementConditionalKeyEnum.EnableIf, dataValues, elementConfigs, {
91
+ const disabled = new Set<string>()
92
+ if (allInputsLoaded) {
93
+ for (const el of Object.values(elementsMap)) {
94
+ const conds = configsPerElement[el.key]
95
+ const shouldEnable = isValidationsMet(FormElementConditionalKeyEnum.EnableIf, allValues, conds, {
49
96
  currentBreakpoint,
50
97
  formDataId,
51
98
  })
99
+ if (!shouldEnable) disabled.add(el.key)
100
+ }
101
+ }
52
102
 
53
- if (!shouldEnable) disabledSet.add(key)
54
- })
55
-
56
- return disabledSet
57
- }, [elementsMap, dataValues, configsPerElement, currentBreakpoint, formDataId, allInputsLoaded])
58
-
59
- const individuallyDisabled = computeIndividuallyDisabled()
60
-
61
- useEffect(() => {
62
- if (allInputsLoaded) disabledStore.setIds([...individuallyDisabled])
63
- }, [allInputsLoaded, individuallyDisabled])
103
+ return disabled
64
104
  }
@@ -1,16 +1,49 @@
1
- import { useEffect, useMemo, useCallback } from 'react'
2
- import useGetCurrentBreakpoint from '../use-window-width.hook'
3
1
  import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
4
- import { hiddenStore } from './visibility-store'
5
2
  import { isValidationsMet } from './visibility-utils'
6
- import {
3
+ import { useEffect } from 'react'
4
+ import useGetCurrentBreakpoint from '../use-window-width.hook'
5
+ import { FormInstance } from 'antd'
6
+ import { hiddenStore } from './visibility-store'
7
+ import type {
7
8
  IDndLayoutCol,
8
- IDndLayoutElement,
9
9
  IDndLayoutRow,
10
10
  IDndLayoutStructure_Responsive,
11
11
  IFormLayoutElementConditions,
12
12
  } from '../../../../types'
13
13
 
14
+ export function useSetHiddenNodes(
15
+ layoutConfig?: IDndLayoutStructure_Responsive,
16
+ formDataRef?: FormInstance,
17
+ formDataId?: string,
18
+ ): void {
19
+ const currentBreakpoint = useGetCurrentBreakpoint()
20
+
21
+ useEffect(() => {
22
+ // during the initial renders, handle the initially hidden nodes
23
+ if (!formDataRef) return
24
+
25
+ const allValues = formDataRef.getFieldsValue() || {}
26
+ const layout =
27
+ layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
28
+
29
+ const allInputsLoaded = layout.length > 0 && Object.values(allValues).filter(Boolean).length > 0 && !!formDataId
30
+
31
+ if (!allInputsLoaded) return
32
+
33
+ handleHiddenSet(allValues, currentBreakpoint, layoutConfig, formDataId)
34
+ }, [formDataRef, layoutConfig, currentBreakpoint, formDataId])
35
+ }
36
+
37
+ export function handleHiddenSet(
38
+ allValues: Record<string, any>,
39
+ currentBreakpoint: DeviceBreakpointEnum,
40
+ layoutConfig?: IDndLayoutStructure_Responsive,
41
+ formDataId?: string,
42
+ ) {
43
+ const hiddenIds = computeHiddenIds(allValues, currentBreakpoint, layoutConfig, formDataId)
44
+ hiddenStore.setIds(hiddenIds)
45
+ }
46
+
14
47
  function computeHiddenIdsRecursive(layout: IDndLayoutRow[], individuallyHidden: Set<string>): string[] {
15
48
  const hiddenCols = new Set<string>()
16
49
  const hiddenRows = new Set<string>()
@@ -54,16 +87,15 @@ function computeHiddenIdsRecursive(layout: IDndLayoutRow[], individuallyHidden:
54
87
  return [...individuallyHidden, ...Array.from(hiddenCols), ...Array.from(hiddenRows)]
55
88
  }
56
89
 
57
- export function useSetHiddenNodes(
90
+ function computeHiddenIds(
91
+ allValues: Record<string, any>,
92
+ currentBreakpoint: DeviceBreakpointEnum,
58
93
  layoutConfig?: IDndLayoutStructure_Responsive,
59
- formValues?: { [key: string]: any },
60
94
  formDataId?: string,
61
- ): void {
62
- const currentBreakpoint = useGetCurrentBreakpoint() || DeviceBreakpointEnum.Default
63
- const layout: IDndLayoutRow[] =
95
+ ): string[] {
96
+ const layout =
64
97
  layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
65
- const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
66
- const dataValues: { [key: string]: any } = formValues && typeof formValues === 'object' ? formValues : {}
98
+ const elementsMap = layoutConfig?.elements || {}
67
99
  const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
68
100
  (curr, el) => {
69
101
  const elConditions = el.conditions?.[FormElementConditionalKeyEnum.ShowIf]
@@ -72,40 +104,20 @@ export function useSetHiddenNodes(
72
104
 
73
105
  return curr
74
106
  },
75
- {},
107
+ {} as any,
76
108
  )
77
109
 
78
- const allInputsLoaded =
79
- layout.length > 0 &&
80
- Object.keys(elementsMap).length > 0 &&
81
- Object.keys(configsPerElement).length > 0 &&
82
- !!formDataId
83
-
84
- const computeIndividuallyHidden = useCallback((): Set<string> => {
85
- if (!allInputsLoaded) return new Set()
110
+ const individuallyHidden = new Set<string>()
111
+ for (const el of Object.values(elementsMap)) {
112
+ const elementConfigs = configsPerElement[el.key]
86
113
 
87
- const hiddenSet = new Set<string>()
88
-
89
- Object.values(elementsMap).forEach(({ key }) => {
90
- const elementConfigs = configsPerElement[key]
91
-
92
- const shouldShow = isValidationsMet(FormElementConditionalKeyEnum.ShowIf, dataValues, elementConfigs, {
93
- currentBreakpoint,
94
- formDataId,
95
- })
96
- if (!shouldShow) hiddenSet.add(key)
114
+ const shouldShow = isValidationsMet(FormElementConditionalKeyEnum.ShowIf, allValues, elementConfigs, {
115
+ currentBreakpoint,
116
+ formDataId,
97
117
  })
98
118
 
99
- return hiddenSet
100
- }, [elementsMap, dataValues, configsPerElement, currentBreakpoint, formDataId, allInputsLoaded])
101
-
102
- const allHiddenArray = useMemo(() => {
103
- const individuallyHidden = computeIndividuallyHidden()
104
-
105
- return computeHiddenIdsRecursive(layout, individuallyHidden)
106
- }, [layout, computeIndividuallyHidden])
119
+ if (!shouldShow) individuallyHidden.add(el.key)
120
+ }
107
121
 
108
- useEffect(() => {
109
- if (allInputsLoaded) hiddenStore.setIds(allHiddenArray)
110
- }, [allHiddenArray, allInputsLoaded])
122
+ return computeHiddenIdsRecursive(layout, individuallyHidden)
111
123
  }
@@ -0,0 +1,20 @@
1
+ import { useEffect } from 'react'
2
+ import { useLocation } from 'react-router-dom'
3
+ import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
4
+
5
+ export const useSetPeristentDomain = (domain: string) => {
6
+ const location = useLocation()
7
+ const isLogin = location.pathname === '/login'
8
+
9
+ useEffect(() => {
10
+ if (!isLogin) return
11
+
12
+ if (typeof window === 'undefined') return
13
+
14
+ const current = window.localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.PersistDomain)
15
+ if (current !== domain) {
16
+ window.localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.PersistDomain, domain)
17
+ console.info(`[useSetCompanyDomain] "${LOCAL_STORAGE_KEYS_ENUM.PersistDomain}" set to "${domain}"`)
18
+ }
19
+ }, [isLogin])
20
+ }
@@ -3,14 +3,39 @@ import { ConfigProvider } from 'antd'
3
3
  import { ConfigContext } from '../context'
4
4
  import useSiteMetadata from '../../common/custom-hooks/use-site-meta-data'
5
5
  import { ReactNode } from 'react'
6
+ import { Button_FillerPortal } from '../../common/button'
7
+ import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
8
+ import { useLocation, useNavigate } from 'react-router-dom'
6
9
 
7
10
  export function ConfigProviderLayout({ children }: { children: ReactNode }): JSX.Element {
8
- const { config, theme } = useCompanyConfig()
11
+ const navigate = useNavigate()
12
+ const location = useLocation()
13
+ const { config, theme, canSwitchDomain } = useCompanyConfig()
9
14
  useSiteMetadata(config?.siteIdentity)
10
15
 
16
+ const showSwitchCompany =
17
+ canSwitchDomain &&
18
+ !localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.PersistDomain) &&
19
+ location.pathname.startsWith('/login')
20
+
11
21
  return (
12
22
  <ConfigContext.Provider value={{ config }}>
13
- <ConfigProvider theme={theme}>{children}</ConfigProvider>
23
+ <ConfigProvider theme={theme}>
24
+ {showSwitchCompany && (
25
+ <div className="absolute top-2 right-2 z-20">
26
+ <Button_FillerPortal
27
+ outline
28
+ onClick={() => {
29
+ localStorage.removeItem(LOCAL_STORAGE_KEYS_ENUM.Domain)
30
+ navigate(0)
31
+ }}
32
+ >
33
+ Switch Project
34
+ </Button_FillerPortal>
35
+ </div>
36
+ )}
37
+ {children}
38
+ </ConfigProvider>
14
39
  </ConfigContext.Provider>
15
40
  )
16
41
  }
@@ -12,8 +12,14 @@ import { useManyToManyConnector } from '../../common/custom-hooks/use-many-to-ma
12
12
  import { PageViewTypEnum, DeviceBreakpointEnum, FormPreservedItemKeys, LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
13
13
  import { UserAuth } from '../../../api/user'
14
14
  import { useCacheFormLayoutConfig } from '../../common/custom-hooks/use-cache-form-layout-config.hook'
15
- import { useSetHiddenNodes } from '../../common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook'
16
- import { useSetDisabledElements } from '../../common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook'
15
+ import {
16
+ handleHiddenSet,
17
+ useSetHiddenNodes,
18
+ } from '../../common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook'
19
+ import {
20
+ handleDisabledSet,
21
+ useSetDisabledElements,
22
+ } from '../../common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook'
17
23
  import { ELEMENTS_DEFAULT_CLASS } from '../../../constants'
18
24
  import {
19
25
  DynamicFormButtonRender,
@@ -50,10 +56,9 @@ export default function FormDataDetailsComponent({
50
56
  const currentBreakpoint = useGetCurrentBreakpoint()
51
57
 
52
58
  const { cachedConfig, isConfigLoading } = useCacheFormLayoutConfig(formId, formKey)
53
- const detailsData = Form.useWatch([], formDataRef)
54
59
 
55
- useSetHiddenNodes(cachedConfig?.detailsConfig, detailsData, formDataId)
56
- useSetDisabledElements(cachedConfig?.detailsConfig, detailsData, formDataId)
60
+ useSetHiddenNodes(cachedConfig?.detailsConfig, formDataRef, formDataId)
61
+ useSetDisabledElements(cachedConfig?.detailsConfig, formDataRef, formDataId)
57
62
 
58
63
  useEffect(() => {
59
64
  // for public forms, setting the details form into localStorage, so that buttons work correctly
@@ -176,6 +181,10 @@ export default function FormDataDetailsComponent({
176
181
  name="dynamic_form_data_form"
177
182
  form={formDataRef}
178
183
  className={ELEMENTS_DEFAULT_CLASS.DataDetailsForm}
184
+ onValuesChange={(_changed, allValues) => {
185
+ handleDisabledSet(allValues, currentBreakpoint, cachedConfig?.detailsConfig, formDataId)
186
+ handleHiddenSet(allValues, currentBreakpoint, cachedConfig?.detailsConfig, formDataId)
187
+ }}
179
188
  >
180
189
  {layout.map((row, rowIdx) => (
181
190
  <LayoutRendererRow
@@ -28,7 +28,11 @@ export const LayoutRendererRow = memo(
28
28
  className={ELEMENTS_DEFAULT_CLASS.LayoutRowContainer}
29
29
  >
30
30
  <LayoutRowConditionalHeaderRenderer header={rowData.props?.header}>
31
- <LayoutRowRepeatableRenderer basePath={basePath} repeatingSection={rowData.props?.repeatingSection}>
31
+ <LayoutRowRepeatableRenderer
32
+ basePath={basePath}
33
+ formRef={formContext.formRef}
34
+ repeatingSection={rowData.props?.repeatingSection}
35
+ >
32
36
  {(formListItemProps) => {
33
37
  const style: { [key: string]: any } = { ...styleConfig, ...getGridContainerStyle(rowData.display) }
34
38
  const hiddenColsIndices = rowData.children.reduce(
@@ -1,49 +1,90 @@
1
- import { IButtonElementProps, IRepeatingSectionConfig } from '../../../../types'
2
- import { Form } from 'antd'
1
+ import { IButtonPropsBase, IRepeatingSectionConfig } from '../../../../types'
2
+ import { Form, FormInstance, Tabs } from 'antd'
3
3
  import { Button_FillerPortal } from '../../../common/button'
4
4
  import { FaTimes } from 'react-icons/fa'
5
5
  import { FaPlus } from 'react-icons/fa6'
6
6
  import { getButtonRenderProps } from '../../../../functions/forms/get-element-props'
7
+ import { useNotification } from '../../../common/custom-hooks'
8
+ import { RepeatingSectionTabTitleTypeEnum } from '../../../../enums'
7
9
 
8
10
  /** This component return its children as function to feed the children with dynamic base path */
9
11
  export const LayoutRowRepeatableRenderer = ({
10
12
  children,
13
+ formRef,
11
14
  basePath,
12
15
  repeatingSection,
13
16
  }: {
14
17
  children: (props?: { updatedBasePath: (string | number)[]; removeButton: JSX.Element }) => JSX.Element
18
+ formRef?: FormInstance
15
19
  basePath: (string | number)[]
16
20
  repeatingSection: IRepeatingSectionConfig | undefined
17
21
  }) => {
22
+ const { confirmModal } = useNotification()
23
+
18
24
  if (!!repeatingSection?.name) {
19
25
  return (
20
26
  <Form.List name={[...basePath, repeatingSection.name]} initialValue={[{}]}>
21
- {(fields, { add, remove }) => (
22
- <div className="flex flex-col gap-2 items-end">
23
- {fields.map(({ name: fieldName }) => (
24
- <div className="w-full" key={fieldName}>
25
- {children({
26
- updatedBasePath: [...basePath, repeatingSection.name, fieldName],
27
- removeButton:
28
- (repeatingSection.min ?? 0) < fields.length ? (
29
- <Button_FillerPortal
30
- {...getButtonRenderProps(repeatingSection.removeButton ?? ({} as IButtonElementProps))}
31
- onClick={() => remove(fieldName)}
32
- >
33
- {repeatingSection.removeButton?.label || <FaTimes className="text-danger" />}
34
- </Button_FillerPortal>
35
- ) : (
36
- <></>
37
- ),
38
- })}
39
- </div>
40
- ))}
41
- {(repeatingSection.max ?? 1000) > fields.length && (
42
- <Button_FillerPortal {...getButtonRenderProps(repeatingSection.addButton ?? {} as IButtonElementProps)} onClick={() => add()}>
43
- {repeatingSection.addButton?.label ?? <FaPlus className="text-primary" />}
44
- </Button_FillerPortal>
45
- )}
46
- </div>
27
+ {(repeatItems, { add, remove }) => (
28
+ <Tabs
29
+ size="small"
30
+ defaultActiveKey="0"
31
+ tabBarStyle={{ paddingLeft: 20 }}
32
+ more={{ trigger: 'hover' }}
33
+ tabBarExtraContent={{
34
+ right: (repeatingSection.max ?? 1000) > repeatItems.length && (
35
+ <Button_FillerPortal
36
+ {...getButtonRenderProps(repeatingSection.addButton ?? ({} as IButtonPropsBase))}
37
+ onClick={() => add()}
38
+ >
39
+ {repeatingSection.addButton?.label ?? <FaPlus className="text-primary" />}
40
+ </Button_FillerPortal>
41
+ ),
42
+ }}
43
+ items={repeatItems.map(({ name: itemName }) => {
44
+ const defaultTitle = `Group ${itemName + 1}`
45
+ const tabTitle =
46
+ !repeatingSection?.titleField?.type || !repeatingSection.titleField.value
47
+ ? defaultTitle
48
+ : repeatingSection.titleField.type === RepeatingSectionTabTitleTypeEnum.Field
49
+ ? formRef?.getFieldValue([
50
+ ...basePath,
51
+ repeatingSection.name,
52
+ itemName,
53
+ repeatingSection.titleField.value,
54
+ ]) ?? defaultTitle
55
+ : `${repeatingSection.titleField.value} ${itemName + 1}`
56
+
57
+ return {
58
+ key: String(itemName),
59
+ label: tabTitle,
60
+ children: (
61
+ <div className="w-full" key={itemName}>
62
+ {children({
63
+ updatedBasePath: [...basePath, repeatingSection.name, itemName],
64
+ removeButton:
65
+ (repeatingSection.min ?? 0) < repeatItems.length ? (
66
+ <div className="flex justify-end">
67
+ <Button_FillerPortal
68
+ {...getButtonRenderProps(repeatingSection.removeButton ?? ({} as IButtonPropsBase))}
69
+ onClick={() =>
70
+ confirmModal({
71
+ content: 'Are you sure to remove this item?',
72
+ onOk: () => remove(itemName),
73
+ })
74
+ }
75
+ >
76
+ {repeatingSection.removeButton?.label || <FaTimes className="text-danger" />}
77
+ </Button_FillerPortal>
78
+ </div>
79
+ ) : (
80
+ <></>
81
+ ),
82
+ })}
83
+ </div>
84
+ ),
85
+ }
86
+ })}
87
+ />
47
88
  )}
48
89
  </Form.List>
49
90
  )
@@ -4,10 +4,10 @@ import { ICurrencyInputElement } from '../../../../types'
4
4
  import { IElementBaseProps } from '.'
5
5
  import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
6
6
  import { Form } from 'antd'
7
- import { useEffect, useMemo } from 'react'
7
+ import { memo, useEffect, useMemo } from 'react'
8
8
  import { evaluateValue } from '../../../../functions/forms/evaluate-value'
9
9
 
10
- export default function LayoutRenderer_CurrencyField({
10
+ function LayoutRenderer_CurrencyField({
11
11
  formContext,
12
12
  formItem,
13
13
  elementData,
@@ -43,6 +43,8 @@ export default function LayoutRenderer_CurrencyField({
43
43
  )
44
44
  }
45
45
 
46
+ export default memo(LayoutRenderer_CurrencyField)
47
+
46
48
  type ILayoutRenderer_CurrencyField = {
47
49
  formContext: IFormContext
48
50
  elementData: ICurrencyInputElement
@@ -2,12 +2,12 @@ import { useBreadcrumb } from '../../../common/custom-hooks/use-breadcrumb.hook'
2
2
  import { Button_FillerPortal } from '../../../common/button'
3
3
  import { useNavigate } from 'react-router-dom'
4
4
  import { FaChevronRight } from 'react-icons/fa6'
5
- import React from 'react'
5
+ import React, { memo } from 'react'
6
6
  import { PageViewTypEnum } from '../../../../enums'
7
7
  import { IBreadcrumbElementProps } from '../../../../types'
8
8
  import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
9
9
 
10
- export default function LayoutRenderer_Breadcrumb({ elementProps }: { elementProps: IBreadcrumbElementProps }) {
10
+ function LayoutRenderer_Breadcrumb({ elementProps }: { elementProps: IBreadcrumbElementProps }) {
11
11
  const navigate = useNavigate()
12
12
  const { breadcrumbs, sliceAt } = useBreadcrumb()
13
13
 
@@ -39,3 +39,5 @@ export default function LayoutRenderer_Breadcrumb({ elementProps }: { elementPro
39
39
  </div>
40
40
  )
41
41
  }
42
+
43
+ export default memo(LayoutRenderer_Breadcrumb)
@@ -4,17 +4,12 @@ import { IPickerElement } from '../../../../types'
4
4
  import { IElementBaseProps } from '.'
5
5
  import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
6
6
  import { ElementTypeEnum } from '../../../../enums'
7
- import { useMemo } from 'react'
7
+ import { memo, useMemo } from 'react'
8
8
  import { findMaxAndMinValues } from '../../../../functions'
9
9
  import { VALIDATION_CURRENT_DATE_IDENTIFIER } from '../../../../constants'
10
10
  import dayjs from 'dayjs'
11
11
 
12
- export default function LayoutRenderer_PickerField({
13
- formContext,
14
- formItem,
15
- elementData,
16
- isDisabled,
17
- }: ILayoutRenderer_PickerField) {
12
+ function LayoutRenderer_PickerField({ formContext, formItem, elementData, isDisabled }: ILayoutRenderer_PickerField) {
18
13
  const props = getElementGeneralizedProps(elementData.props)
19
14
 
20
15
  const placeholder =
@@ -47,6 +42,8 @@ export default function LayoutRenderer_PickerField({
47
42
  )
48
43
  }
49
44
 
45
+ export default memo(LayoutRenderer_PickerField)
46
+
50
47
  type ILayoutRenderer_PickerField = {
51
48
  formContext: IFormContext
52
49
  elementData: IPickerElement