form-craft-package 1.8.0 → 1.8.1-dev.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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/components/common/currency-field.tsx +2 -2
  3. package/src/components/common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook.ts +64 -0
  4. package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts +111 -0
  5. package/src/components/common/custom-hooks/use-node-condition.hook/use-node-condition.hook.ts +18 -0
  6. package/src/components/common/custom-hooks/use-node-condition.hook/visibility-store.ts +103 -0
  7. package/src/components/common/custom-hooks/use-node-condition.hook/visibility-utils.ts +138 -0
  8. package/src/components/common/custom-hooks/use-window-width.hook.ts +6 -3
  9. package/src/components/form/1-list/table-header.tsx +10 -6
  10. package/src/components/form/1-list/table.tsx +2 -0
  11. package/src/components/form/2-details/index.tsx +20 -4
  12. package/src/components/form/3-preview/index.tsx +8 -4
  13. package/src/components/form/layout-renderer/1-row/index.tsx +54 -70
  14. package/src/components/form/layout-renderer/2-col/index.tsx +12 -42
  15. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +13 -14
  16. package/src/components/form/layout-renderer/3-element/10-currency.tsx +9 -12
  17. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +2 -1
  18. package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +53 -0
  19. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +91 -75
  20. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +2 -1
  21. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +2 -1
  22. package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +2 -1
  23. package/src/components/form/layout-renderer/3-element/6-signature.tsx +3 -2
  24. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +5 -1
  25. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +34 -37
  26. package/src/components/form/layout-renderer/3-element/index.tsx +51 -57
  27. package/src/constants.ts +33 -0
  28. package/src/enums/index.ts +3 -1
  29. package/src/functions/forms/data-render-functions.tsx +3 -3
  30. package/src/components/common/custom-hooks/use-check-element-conditions.hook.ts +0 -85
  31. package/src/components/form/index.tsx +0 -0
  32. package/src/functions/forms/conditional-rule-validator.ts +0 -79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.8.0",
3
+ "version": "1.8.1-dev.1",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,7 +1,7 @@
1
1
  import { InputNumber } from 'antd'
2
2
  import { useMemo, useRef } from 'react'
3
3
  import { CountryEnum } from '../../enums'
4
- import { INTERNATIONALIZATION_DATA } from '../../constants'
4
+ import { ELEMENTS_DEFAULT_CLASS, INTERNATIONALIZATION_DATA } from '../../constants'
5
5
 
6
6
  export default function CurrencyField({
7
7
  country = CountryEnum.US,
@@ -61,7 +61,7 @@ export default function CurrencyField({
61
61
  : `${currencySymbol}${parseInt('0').toFixed(decimalPoint)}`
62
62
  }}
63
63
  parser={(value) => (value ? parseFloat(value.replace(/\p{Sc}\s?|(,*)/gu, '')) : 0)}
64
- className="w-full right-align"
64
+ className={`${ELEMENTS_DEFAULT_CLASS.Currency} w-full right-align`}
65
65
  />
66
66
  )
67
67
  }
@@ -0,0 +1,64 @@
1
+ import { useEffect, useCallback } from 'react'
2
+ import useGetCurrentBreakpoint from '../use-window-width.hook'
3
+ import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
4
+ import { disabledStore } from './visibility-store'
5
+ import { isValidationsMet } from './visibility-utils'
6
+ import {
7
+ IDndLayoutElement,
8
+ IDndLayoutRow,
9
+ IDndLayoutStructure_Responsive,
10
+ IFormLayoutElementConditions,
11
+ } from '../../../../types'
12
+
13
+ export function useSetDisabledElements(
14
+ layoutConfig?: IDndLayoutStructure_Responsive,
15
+ formValues?: { [key: string]: any },
16
+ formDataId?: string,
17
+ ): void {
18
+ const currentBreakpoint = useGetCurrentBreakpoint()
19
+ const layout: IDndLayoutRow[] =
20
+ layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
21
+
22
+ const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
23
+ const dataValues: { [key: string]: any } = formValues && typeof formValues === 'object' ? formValues : {}
24
+
25
+ const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
26
+ (acc, el) => {
27
+ const elConditions = el.conditions?.[FormElementConditionalKeyEnum.EnableIf]
28
+ if (elConditions && Array.isArray(elConditions) && elConditions.length > 0) acc[el.key] = el.conditions!
29
+
30
+ return acc
31
+ },
32
+ {} as Record<string, IFormLayoutElementConditions>,
33
+ )
34
+
35
+ const allInputsLoaded =
36
+ layout.length > 0 &&
37
+ Object.keys(elementsMap).length > 0 &&
38
+ Object.keys(configsPerElement).length > 0 &&
39
+ Object.values(dataValues).length > 0 &&
40
+ !!formDataId
41
+
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, {
49
+ currentBreakpoint,
50
+ formDataId,
51
+ })
52
+
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])
64
+ }
@@ -0,0 +1,111 @@
1
+ import { useEffect, useMemo, useCallback } from 'react'
2
+ import useGetCurrentBreakpoint from '../use-window-width.hook'
3
+ import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
4
+ import { hiddenStore } from './visibility-store'
5
+ import { isValidationsMet } from './visibility-utils'
6
+ import {
7
+ IDndLayoutCol,
8
+ IDndLayoutElement,
9
+ IDndLayoutRow,
10
+ IDndLayoutStructure_Responsive,
11
+ IFormLayoutElementConditions,
12
+ } from '../../../../types'
13
+
14
+ function computeHiddenIdsRecursive(layout: IDndLayoutRow[], individuallyHidden: Set<string>): string[] {
15
+ const hiddenCols = new Set<string>()
16
+ const hiddenRows = new Set<string>()
17
+ const rowHiddenCache = new Map<string, boolean>()
18
+
19
+ function isRowHidden(row: IDndLayoutRow): boolean {
20
+ if (rowHiddenCache.has(row.id)) return rowHiddenCache.get(row.id)!
21
+
22
+ const hiddenCols = row.children.map((col) => isColHidden(col as IDndLayoutCol))
23
+ const allColsHidden = hiddenCols.every((hidden) => hidden)
24
+
25
+ rowHiddenCache.set(row.id, allColsHidden)
26
+ return allColsHidden
27
+ }
28
+
29
+ function isColHidden(col: IDndLayoutCol): boolean {
30
+ const allChildrenHidden = col.children.every((child) => {
31
+ if (typeof child === 'string') return individuallyHidden.has(child)
32
+ else return isRowHidden(child)
33
+ })
34
+
35
+ if (allChildrenHidden) hiddenCols.add(col.id)
36
+
37
+ return allChildrenHidden
38
+ }
39
+
40
+ function traverseRows(rows: IDndLayoutRow[]) {
41
+ for (const row of rows) {
42
+ row.children.forEach((col) => {
43
+ col.children.forEach((child) => {
44
+ if (typeof child !== 'string') traverseRows([child])
45
+ })
46
+ })
47
+
48
+ if (isRowHidden(row)) hiddenRows.add(row.id)
49
+ }
50
+ }
51
+
52
+ traverseRows(layout)
53
+
54
+ return [...individuallyHidden, ...Array.from(hiddenCols), ...Array.from(hiddenRows)]
55
+ }
56
+
57
+ export function useSetHiddenNodes(
58
+ layoutConfig?: IDndLayoutStructure_Responsive,
59
+ formValues?: { [key: string]: any },
60
+ formDataId?: string,
61
+ ): void {
62
+ const currentBreakpoint = useGetCurrentBreakpoint() || DeviceBreakpointEnum.Default
63
+ const layout: IDndLayoutRow[] =
64
+ 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 : {}
67
+ const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
68
+ (curr, el) => {
69
+ const elConditions = el.conditions?.[FormElementConditionalKeyEnum.ShowIf]
70
+
71
+ if (Array.isArray(elConditions) && elConditions.length > 0) return { ...curr, [el.key]: el.conditions }
72
+
73
+ return curr
74
+ },
75
+ {},
76
+ )
77
+
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()
86
+
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)
97
+ })
98
+
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])
107
+
108
+ useEffect(() => {
109
+ if (allInputsLoaded) hiddenStore.setIds(allHiddenArray)
110
+ }, [allHiddenArray, allInputsLoaded])
111
+ }
@@ -0,0 +1,18 @@
1
+ import { useSyncExternalStore } from 'react'
2
+ import { disabledStore, hiddenStore } from './visibility-store'
3
+
4
+ export const useIsNodeHidden = (id: string): boolean =>
5
+ useSyncExternalStore(
6
+ (callback) => hiddenStore.subscribe(callback),
7
+ () => hiddenStore.getSnapshot().includes(id),
8
+ () => hiddenStore.getSnapshot().includes(id),
9
+ )
10
+
11
+ export const checkIsNodeHidden = (id: string): boolean => hiddenStore.getSnapshot().includes(id)
12
+
13
+ export const useIsNodeDisabled = (id: string): boolean =>
14
+ useSyncExternalStore(
15
+ (callback) => disabledStore.subscribe(callback),
16
+ () => disabledStore.getSnapshot().includes(id),
17
+ () => disabledStore.getSnapshot().includes(id),
18
+ )
@@ -0,0 +1,103 @@
1
+ import { LOCAL_STORAGE_KEYS_ENUM } from '../../../../enums'
2
+
3
+ type Subscriber = () => void
4
+
5
+ /**
6
+ * Factory: create a LocalStorage-backed store for a list of string IDs.
7
+ * Internally holds:
8
+ * • key: the localStorage key (enum value)
9
+ * • ids: in-memory array of strings
10
+ * • subscribers: a Set of callbacks to notify whenever ids changes
11
+ *
12
+ * Exposes methods:
13
+ * - subscribe(callback): register for change notifications → returns unsubscribe()
14
+ * - getSnapshot(): return current array of IDs (shallow copy)
15
+ * - setIds(newIds): write new array to memory + localStorage + notify subscribers
16
+ */
17
+ function createIdListStore(storageKey: LOCAL_STORAGE_KEYS_ENUM) {
18
+ // 1) Initialize in-memory array from localStorage (or empty array).
19
+ let ids: string[] = []
20
+ try {
21
+ const raw = localStorage.getItem(storageKey)
22
+ if (raw) {
23
+ const parsed = JSON.parse(raw)
24
+ if (Array.isArray(parsed)) {
25
+ ids = parsed
26
+ }
27
+ }
28
+ } catch {
29
+ ids = []
30
+ }
31
+
32
+ // 2) Keep track of subscribers (callbacks)
33
+ const subscribers = new Set<Subscriber>()
34
+
35
+ // 3) Storage-event listener (to pick up changes from other tabs/windows).
36
+ window.addEventListener('storage', (e) => {
37
+ if (e.key === storageKey) {
38
+ try {
39
+ const newArr = e.newValue ? JSON.parse(e.newValue) : []
40
+ if (Array.isArray(newArr)) {
41
+ ids = newArr
42
+ // Notify all subscribers that our in-memory copy changed
43
+ subscribers.forEach((cb) => cb())
44
+ }
45
+ } catch {
46
+ // ignore JSON errors
47
+ }
48
+ }
49
+ })
50
+
51
+ return {
52
+ /**
53
+ * Subscribe to changes. Callback is called whenever setIds or a storage-event modifies this store.
54
+ * Returns an unsubscribe function.
55
+ */
56
+ subscribe(callback: Subscriber): () => void {
57
+ subscribers.add(callback)
58
+ return () => {
59
+ subscribers.delete(callback)
60
+ }
61
+ },
62
+
63
+ /** Return a shallow copy of the current IDs array. */
64
+ getSnapshot(): string[] {
65
+ return [...ids]
66
+ },
67
+
68
+ /**
69
+ * Overwrite the list of IDs (in memory + localStorage), then notify subscribers.
70
+ * If the new array is identical (same length & same items in order), do nothing.
71
+ */
72
+ setIds(newIds: string[]) {
73
+ // Quick check: length mismatch → definitely changed
74
+ if (newIds.length !== ids.length) {
75
+ updateAndNotify(newIds)
76
+ return
77
+ }
78
+ // If same length, compare each item
79
+ for (let i = 0; i < newIds.length; i++) {
80
+ if (newIds[i] !== ids[i]) {
81
+ updateAndNotify(newIds)
82
+ return
83
+ }
84
+ }
85
+ // Otherwise, identical content → no update
86
+ },
87
+ }
88
+
89
+ /** Internal helper: write to memory + localStorage + notify subscribers */
90
+ function updateAndNotify(newIdsArr: string[]) {
91
+ ids = [...newIdsArr]
92
+ try {
93
+ localStorage.setItem(storageKey, JSON.stringify(ids))
94
+ } catch {
95
+ // localStorage quota exceeded? we ignore for now.
96
+ }
97
+ subscribers.forEach((cb) => cb())
98
+ }
99
+ }
100
+
101
+ // Instantiate two separate stores:
102
+ export const hiddenStore = createIdListStore(LOCAL_STORAGE_KEYS_ENUM.HiddenElements)
103
+ export const disabledStore = createIdListStore(LOCAL_STORAGE_KEYS_ENUM.DisabledElements)
@@ -0,0 +1,138 @@
1
+ import { NEW_FORM_DATA_IDENTIFIER, REGEX_PATTERNS } from '../../../../constants'
2
+ import { IFormContext } from '../../../form/layout-renderer/1-row'
3
+ import { isNewFormDataPage } from '../../../../functions'
4
+ import {
5
+ ConditionDependencyTypeEnum,
6
+ DeviceBreakpointEnum,
7
+ FieldValidationEnum,
8
+ FormElementConditionalKeyEnum,
9
+ FormStateEnum,
10
+ } from '../../../../enums'
11
+ import {
12
+ IConditionalValidation,
13
+ IConditionalValidation_AuthUser,
14
+ IConditionalValidation_Field,
15
+ IConditionalValidation_FormState,
16
+ IFormLayoutElementConditions,
17
+ } from '../../../../types'
18
+
19
+ const getValidationConfigs = (validationConfigs: IConditionalValidation[]) =>
20
+ validationConfigs.reduce<{
21
+ fieldValidations: IConditionalValidation_Field[]
22
+ authUserValidations: IConditionalValidation_AuthUser[]
23
+ hasDataIdValidation: IConditionalValidation_FormState | undefined
24
+ alwaysDisabled: boolean
25
+ hideScreens: DeviceBreakpointEnum[]
26
+ }>(
27
+ (curr, v) => {
28
+ if (v.dependentType === ConditionDependencyTypeEnum.Field)
29
+ return { ...curr, fieldValidations: [...curr.fieldValidations, v] }
30
+ else if (v.dependentType === ConditionDependencyTypeEnum.AuthUser)
31
+ return { ...curr, authUserValidations: [...curr.authUserValidations, v] }
32
+ else if (v.dependentType === ConditionDependencyTypeEnum.FormState) return { ...curr, hasDataIdValidation: v }
33
+ else if (v.dependentType === ConditionDependencyTypeEnum.AlwaysFalse) return { ...curr, alwaysDisabled: true }
34
+ else if (v.dependentType === ConditionDependencyTypeEnum.Screen) return { ...curr, hideScreens: v.hideScreens }
35
+
36
+ return curr
37
+ },
38
+ {
39
+ fieldValidations: [],
40
+ authUserValidations: [],
41
+ hasDataIdValidation: undefined,
42
+ alwaysDisabled: false,
43
+ hideScreens: [],
44
+ },
45
+ )
46
+
47
+ /** If any of the validations fails, the function returns false */
48
+ const validateConditions = (validations: IConditionalValidation_Field[], data: Record<string, any>): boolean => {
49
+ return validations.every((validation) => {
50
+ const { field, rule, value: configuredValue } = validation
51
+ const formValue = data[field]
52
+
53
+ switch (rule) {
54
+ case FieldValidationEnum.Required:
55
+ if (!!formValue) return true
56
+ return false
57
+
58
+ case FieldValidationEnum.DataLength:
59
+ return (
60
+ typeof formValue === 'string' && typeof configuredValue === 'number' && formValue.length === configuredValue
61
+ )
62
+
63
+ case FieldValidationEnum.MaxDataLength:
64
+ return (
65
+ typeof formValue === 'string' && typeof configuredValue === 'number' && formValue.length <= configuredValue
66
+ )
67
+
68
+ case FieldValidationEnum.MinDataLength:
69
+ return (
70
+ typeof formValue === 'string' && typeof configuredValue === 'number' && formValue.length >= configuredValue
71
+ )
72
+
73
+ case FieldValidationEnum.MinValue:
74
+ return typeof formValue === 'number' && typeof configuredValue === 'number' && formValue >= configuredValue
75
+
76
+ case FieldValidationEnum.MaxValue:
77
+ return typeof formValue === 'number' && typeof configuredValue === 'number' && formValue <= configuredValue
78
+
79
+ case FieldValidationEnum.Regex:
80
+ return (
81
+ typeof formValue === 'string' &&
82
+ typeof configuredValue === 'string' &&
83
+ new RegExp(configuredValue).test(formValue)
84
+ )
85
+
86
+ case FieldValidationEnum.Email:
87
+ return typeof formValue === 'string' && REGEX_PATTERNS.Email.test(formValue)
88
+
89
+ case FieldValidationEnum.StrictlyEquals:
90
+ if (!configuredValue) return !formValue || [null, undefined, ''].includes(formValue)
91
+ // equals no value
92
+
93
+ return formValue === configuredValue
94
+
95
+ default:
96
+ return false
97
+ }
98
+ })
99
+ }
100
+
101
+ export const isValidationsMet = (
102
+ type: FormElementConditionalKeyEnum,
103
+ dataValues: { [key: string]: any },
104
+ configs?: IFormLayoutElementConditions,
105
+ contextValues: { currentBreakpoint?: DeviceBreakpointEnum } & IFormContext = {},
106
+ ) => {
107
+ const validations = configs?.[type] ?? []
108
+
109
+ if (validations.length === 0) return true
110
+
111
+ const { fieldValidations, authUserValidations, hasDataIdValidation, alwaysDisabled, hideScreens } =
112
+ getValidationConfigs(validations)
113
+
114
+ if (alwaysDisabled) return false
115
+
116
+ // Dependent on other fields
117
+ const isFieldsConditionsMet =
118
+ dataValues && typeof dataValues === 'object' && fieldValidations.length > 0
119
+ ? validateConditions(fieldValidations as IConditionalValidation_Field[], dataValues)
120
+ : true
121
+
122
+ // Dependent on auth user
123
+ const isAuthUserConditionsMet = authUserValidations.length > 0 ? false : true
124
+
125
+ // Dependent on data id
126
+ const isDataIdConditionsMet = hasDataIdValidation
127
+ ? hasDataIdValidation.state === FormStateEnum.New
128
+ ? isNewFormDataPage(contextValues.formDataId)
129
+ : contextValues.formDataId !== NEW_FORM_DATA_IDENTIFIER
130
+ : true
131
+
132
+ // Dependent on screen
133
+ const isScreenConditionsMet = contextValues?.currentBreakpoint
134
+ ? !hideScreens.includes(contextValues.currentBreakpoint)
135
+ : true
136
+
137
+ return isFieldsConditionsMet && isAuthUserConditionsMet && isDataIdConditionsMet && isScreenConditionsMet
138
+ }
@@ -8,16 +8,19 @@ const useGetCurrentBreakpoint = (): DeviceBreakpointEnum => {
8
8
  )
9
9
 
10
10
  useEffect(() => {
11
- const handleResize = () => setWidth(width)
11
+ const handleResize = () => {
12
+ const current = window.visualViewport?.width || window.innerWidth
13
+ setWidth(current)
14
+ }
12
15
 
13
16
  window.addEventListener('resize', handleResize)
14
17
  window.addEventListener('orientationchange', handleResize)
15
- window.visualViewport?.addEventListener('resize', handleResize)
18
+ if (window.visualViewport) window.visualViewport.addEventListener('resize', handleResize)
16
19
 
17
20
  return () => {
18
21
  window.removeEventListener('resize', handleResize)
19
22
  window.removeEventListener('orientationchange', handleResize)
20
- window.visualViewport?.removeEventListener('resize', handleResize)
23
+ if (window.visualViewport) window.visualViewport.removeEventListener('resize', handleResize)
21
24
  }
22
25
  }, [])
23
26
 
@@ -4,14 +4,18 @@ import dayjs from 'dayjs'
4
4
  import { extractFiltersFromLayout } from '../../../functions/forms'
5
5
  import { DeviceBreakpointEnum, FilterConfigTypeEnum, FormPreservedItemKeys } from '../../../enums'
6
6
  import { LayoutRendererRow } from '../layout-renderer/1-row'
7
+ import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
8
+ import { IDataListHeaderLayoutContext } from './table'
7
9
  import {
8
10
  BSON_DATA_IDENTIFIER_PREFIXES,
11
+ ELEMENTS_DEFAULT_CLASS,
9
12
  VALUE_REPLACEMENT_PLACEHOLDER,
10
13
  VALUE_REPLACEMENT_PLACEHOLDER2,
11
14
  } from '../../../constants'
12
- import { DynamicFormButtonRender } from '../layout-renderer/3-element/1-dynamic-button'
13
- import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
14
- import { IDataListHeaderLayoutContext } from './table'
15
+ import {
16
+ DynamicFormButtonRender,
17
+ IDynamicButton_DisplayStateProps,
18
+ } from '../layout-renderer/3-element/1-dynamic-button'
15
19
  import {
16
20
  IDndLayoutStructure_Responsive,
17
21
  IFilterConfig,
@@ -107,7 +111,7 @@ export default function FormDataListHeaderComponent({
107
111
  )
108
112
 
109
113
  return (
110
- <Form layout="vertical" form={filtersFormRef}>
114
+ <Form layout="vertical" form={filtersFormRef} className={ELEMENTS_DEFAULT_CLASS.DataListHeaderForm}>
111
115
  {dndLayout.map((row, rowIdx) => (
112
116
  <LayoutRendererRow
113
117
  key={rowIdx}
@@ -115,9 +119,9 @@ export default function FormDataListHeaderComponent({
115
119
  titleComponent={titleComponent}
116
120
  formContext={formContext}
117
121
  elements={layoutConfig?.elements ?? {}}
118
- renderButton={(btnProps, conditions) => (
122
+ renderButton={(btnProps) => (
119
123
  <DynamicFormButtonRender
120
- displayStateProps={{ btnProps, conditions }}
124
+ displayStateProps={btnProps as IDynamicButton_DisplayStateProps}
121
125
  formContext={formContext}
122
126
  onCustomFunctionCall={onCustomFunctionCall}
123
127
  />
@@ -13,6 +13,7 @@ import { IFormDataListConfig, IFormDataListData, IFormDataTableColumn } from '..
13
13
  import { processOptions } from '../../../functions/forms/get-data-list-option-value'
14
14
  import { IFormContext } from '../layout-renderer/1-row'
15
15
  import { useFindDynamiForm } from '../../common/custom-hooks'
16
+ import { ELEMENTS_DEFAULT_CLASS } from '../../../constants'
16
17
 
17
18
  export default function FormDataListTableComponent({
18
19
  layoutsConfigs,
@@ -121,6 +122,7 @@ export default function FormDataListTableComponent({
121
122
  />
122
123
  {(!otherConfigs || otherConfigs.listType === FormDataListViewTypeEnum.Table) && (
123
124
  <Table<IFormDataListData>
125
+ className={ELEMENTS_DEFAULT_CLASS.DataTable}
124
126
  dataSource={dataList.data}
125
127
  columns={tableColumnsFiltered}
126
128
  rowKey={(record) => record._id}
@@ -2,7 +2,11 @@ import { Form, Spin } from 'antd'
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
3
  import { IDndLayoutStructure_Responsive } from '../../../types'
4
4
  import { LayoutRendererRow } from '../layout-renderer/1-row'
5
- import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
5
+ import {
6
+ DynamicFormButtonRender,
7
+ ICustomFunctionCall,
8
+ IDynamicButton_DisplayStateProps,
9
+ } from '../layout-renderer/3-element/1-dynamic-button'
6
10
  import FormDataListSkeleton_Details from '../../common/loading-skeletons/details'
7
11
  import { useLocation } from 'react-router-dom'
8
12
  import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
@@ -13,6 +17,8 @@ import { useManyToManyConnector } from '../../common/custom-hooks/use-many-to-ma
13
17
  import { PageViewTypEnum, DeviceBreakpointEnum, FormPreservedItemKeys, LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
14
18
  import { UserAuth } from '../../../api/user'
15
19
  import { useCacheFormLayoutConfig } from '../../common/custom-hooks/use-cache-form-layout-config.hook'
20
+ import { useSetHiddenNodes } from '../../common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook'
21
+ import { useSetDisabledElements } from '../../common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook'
16
22
  import {
17
23
  fromMongoDbExtendedJSON,
18
24
  getPickerFieldsWithOriginalTz,
@@ -20,6 +26,7 @@ import {
20
26
  isValidMongoDbId,
21
27
  queryParamsToObject,
22
28
  } from '../../../functions/forms'
29
+ import { ELEMENTS_DEFAULT_CLASS } from '../../../constants'
23
30
 
24
31
  export default function FormDataDetailsComponent({
25
32
  isPublic,
@@ -43,6 +50,10 @@ export default function FormDataDetailsComponent({
43
50
  const currentBreakpoint = useGetCurrentBreakpoint()
44
51
 
45
52
  const { cachedConfig, isConfigLoading } = useCacheFormLayoutConfig(formId, formKey)
53
+ const detailsData = Form.useWatch([], formDataRef)
54
+
55
+ useSetHiddenNodes(cachedConfig?.detailsConfig, detailsData, formDataId)
56
+ useSetDisabledElements(cachedConfig?.detailsConfig, detailsData, formDataId)
46
57
 
47
58
  useEffect(() => {
48
59
  // for public forms, setting the details form into localStorage, so that buttons work correctly
@@ -161,16 +172,21 @@ export default function FormDataDetailsComponent({
161
172
 
162
173
  return (
163
174
  <Spin spinning={loading}>
164
- <Form layout="vertical" name="dynamic_form_data_form" form={formDataRef}>
175
+ <Form
176
+ layout="vertical"
177
+ name="dynamic_form_data_form"
178
+ form={formDataRef}
179
+ className={ELEMENTS_DEFAULT_CLASS.DataDetailsForm}
180
+ >
165
181
  {layout.map((row, rowIdx) => (
166
182
  <LayoutRendererRow
167
183
  key={rowIdx}
168
184
  rowData={row}
169
185
  formContext={formContext}
170
186
  elements={cachedConfig.detailsConfig.elements}
171
- renderButton={(btnProps, conditions) => (
187
+ renderButton={(btnProps) => (
172
188
  <DynamicFormButtonRender
173
- displayStateProps={{ btnProps, conditions, defaultDisabled: true }}
189
+ displayStateProps={btnProps as IDynamicButton_DisplayStateProps}
174
190
  formContext={formContext}
175
191
  onCustomFunctionCall={onCustomFunctionCall}
176
192
  />
@@ -2,9 +2,13 @@ import { Form } from 'antd'
2
2
  import { IDndLayoutStructure } from '../../../types'
3
3
  import { FormPreservedItemKeys } from '../../../enums'
4
4
  import { LayoutRendererRow } from '../layout-renderer/1-row'
5
- import { DynamicFormButtonRender } from '../layout-renderer/3-element/1-dynamic-button'
6
5
  import { useNotification } from '../../common/custom-hooks'
7
6
  import { ReactNode, useEffect, useMemo } from 'react'
7
+ import {
8
+ DynamicFormButtonRender,
9
+ IDynamicButton_DisplayStateProps,
10
+ } from '../layout-renderer/3-element/1-dynamic-button'
11
+ import { ELEMENTS_DEFAULT_CLASS } from '../../../constants'
8
12
 
9
13
  export const FormPreviewComponent = ({
10
14
  layout,
@@ -21,7 +25,7 @@ export const FormPreviewComponent = ({
21
25
  const formContext = useMemo(() => ({ formRef: previewFormRef }), [previewFormRef])
22
26
 
23
27
  return (
24
- <Form layout="vertical" form={previewFormRef}>
28
+ <Form layout="vertical" form={previewFormRef} className={ELEMENTS_DEFAULT_CLASS.PreviewForm}>
25
29
  {layout.map((row, rowIdx) => (
26
30
  <LayoutRendererRow
27
31
  key={rowIdx}
@@ -29,9 +33,9 @@ export const FormPreviewComponent = ({
29
33
  titleComponent={titleComponent}
30
34
  formContext={formContext}
31
35
  elements={elements}
32
- renderButton={(btnProps, conditions) => (
36
+ renderButton={(btnProps) => (
33
37
  <DynamicFormButtonRender
34
- displayStateProps={{ btnProps, conditions }}
38
+ displayStateProps={btnProps as IDynamicButton_DisplayStateProps}
35
39
  formContext={formContext}
36
40
  onCustomFunctionCall={() => infoModal({ content: 'Custom function call!' })}
37
41
  />