form-craft-package 1.8.1-dev.0 → 1.8.1-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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/api/client.ts +2 -2
  3. package/src/components/common/currency-field.tsx +2 -2
  4. package/src/components/common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook.ts +64 -0
  5. package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts +111 -0
  6. package/src/components/common/custom-hooks/use-node-condition.hook/use-node-condition.hook.ts +18 -0
  7. package/src/components/common/custom-hooks/use-node-condition.hook/visibility-store.ts +103 -0
  8. package/src/components/common/custom-hooks/use-node-condition.hook/visibility-utils.ts +138 -0
  9. package/src/components/common/custom-hooks/use-window-width.hook.ts +6 -3
  10. package/src/components/form/1-list/table-header.tsx +14 -10
  11. package/src/components/form/1-list/table.tsx +11 -8
  12. package/src/components/form/2-details/index.tsx +20 -4
  13. package/src/components/form/3-preview/index.tsx +12 -8
  14. package/src/components/form/layout-renderer/1-row/index.tsx +55 -71
  15. package/src/components/form/layout-renderer/2-col/index.tsx +16 -46
  16. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +13 -14
  17. package/src/components/form/layout-renderer/3-element/10-currency.tsx +9 -12
  18. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +2 -1
  19. package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +53 -0
  20. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +91 -75
  21. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +2 -1
  22. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +2 -1
  23. package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +2 -1
  24. package/src/components/form/layout-renderer/3-element/6-signature.tsx +3 -2
  25. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +5 -1
  26. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +72 -123
  27. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +2 -1
  28. package/src/components/form/layout-renderer/3-element/index.tsx +63 -70
  29. package/src/constants.ts +33 -0
  30. package/src/enums/index.ts +3 -2
  31. package/src/functions/forms/data-render-functions.tsx +3 -3
  32. package/src/functions/forms/get-data-list-option-value.ts +7 -1
  33. package/src/types/companies/site-layout/authenticated/index.tsx +1 -1
  34. package/src/types/forms/index.ts +1 -1
  35. package/src/types/forms/layout-elements/index.ts +4 -7
  36. package/src/types/index.ts +0 -2
  37. package/src/components/common/custom-hooks/use-check-element-conditions.hook.ts +0 -85
  38. package/src/components/form/index.tsx +0 -0
  39. 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.1-dev.0",
3
+ "version": "1.8.1-dev.2",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
package/src/api/client.ts CHANGED
@@ -12,7 +12,7 @@ import axios, {
12
12
 
13
13
  import { PageViewTypEnum, LOCAL_STORAGE_KEYS_ENUM, SHARED_COOKIE_KEYS } from '../enums'
14
14
  import { IDynamicForm } from '../types'
15
- import { constructDynamicFormHref, fetchDynamicForms } from '../functions'
15
+ import { constructDynamicFormHref, cookieHandler, fetchDynamicForms } from '../functions'
16
16
  import { CLIENT_ID, CLIENT_SECRET } from '../constants'
17
17
  import { breadcrumbStore } from '../components/common/custom-hooks/use-breadcrumb.hook'
18
18
 
@@ -120,7 +120,7 @@ apiClient.interceptors.response.use(
120
120
  .catch((refreshErr) => {
121
121
  processQueue(refreshErr, null)
122
122
  window.location.href = '/login'
123
- return Promise.reject(refreshErr)
123
+ cookieHandler.empty()
124
124
  })
125
125
  .finally(() => {
126
126
  isRefreshing = false
@@ -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
 
@@ -1,17 +1,21 @@
1
1
  import { Form } from 'antd'
2
- import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
3
  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,
@@ -22,7 +26,7 @@ import {
22
26
 
23
27
  export default function FormDataListHeaderComponent({
24
28
  layoutConfig,
25
- titleComponent,
29
+ dataCount,
26
30
  updateDynamicFilter,
27
31
  headerLayoutContext,
28
32
  }: IFormDataListHeaderComponent) {
@@ -107,17 +111,17 @@ 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}
114
118
  rowData={row}
115
- titleComponent={titleComponent}
119
+ dataCount={dataCount}
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
  />
@@ -131,7 +135,7 @@ export default function FormDataListHeaderComponent({
131
135
  type IFormDataListHeaderComponent = {
132
136
  layoutConfig?: IDndLayoutStructure_Responsive
133
137
  updateDynamicFilter: (match?: string) => void
134
- titleComponent?: ReactNode
138
+ dataCount?: 'pending' | number
135
139
  headerLayoutContext: IDataListHeaderLayoutContext
136
140
  }
137
141
 
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react'
2
2
  import FormDataListHeaderComponent from './table-header'
3
3
  import useDebounced from '../../common/custom-hooks/use-debounce.hook'
4
- import { Spin, Table } from 'antd'
4
+ import { Empty, Table } from 'antd'
5
5
  import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
6
6
  import { SorterResult } from 'antd/es/table/interface'
7
7
  import { constructDynamicFormHref, renderTableColumns, revertProjectionKey } from '../../../functions/forms'
@@ -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,
@@ -88,7 +89,7 @@ export default function FormDataListTableComponent({
88
89
 
89
90
  setLoading(false)
90
91
  },
91
- [headerLayoutContext, layoutsConfigs],
92
+ [headerLayoutContext],
92
93
  )
93
94
 
94
95
  useEffect(() => {
@@ -101,6 +102,7 @@ export default function FormDataListTableComponent({
101
102
  )
102
103
 
103
104
  // if (loading) return
105
+ console.log(loading, parentLoadings)
104
106
  if ((loading || parentLoadings.initial) && loadingBlock) return loadingBlock
105
107
 
106
108
  return (
@@ -111,16 +113,12 @@ export default function FormDataListTableComponent({
111
113
  setParentLoading(true)
112
114
  setFilterReqData((c) => ({ ...c, match, current: 1 }))
113
115
  }}
114
- titleComponent={
115
- <>
116
- <span className="pr-1">{otherConfigs?.title}</span>
117
- {otherConfigs?.showCount && <span>({parentLoadings.data ? <Spin size="small" /> : dataList.total})</span>}
118
- </>
119
- }
116
+ dataCount={otherConfigs?.showCount ? (parentLoadings.data ? 'pending' : dataList.total) : undefined}
120
117
  headerLayoutContext={headerLayoutContext}
121
118
  />
122
119
  {(!otherConfigs || otherConfigs.listType === FormDataListViewTypeEnum.Table) && (
123
120
  <Table<IFormDataListData>
121
+ className={ELEMENTS_DEFAULT_CLASS.DataTable}
124
122
  dataSource={dataList.data}
125
123
  columns={tableColumnsFiltered}
126
124
  rowKey={(record) => record._id}
@@ -145,6 +143,11 @@ export default function FormDataListTableComponent({
145
143
  }
146
144
  }
147
145
  loading={parentLoadings.data}
146
+ locale={{
147
+ emptyText: (
148
+ <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={otherConfigs?.noDataText || 'No Data'} />
149
+ ),
150
+ }}
148
151
  onChange={(cbPagination, _, sorter: SorterResult<IFormDataListData> | SorterResult<IFormDataListData>[]) => {
149
152
  console.log('ON TABLE CHANGE', cbPagination, sorter)
150
153
  setParentLoading(true)