form-craft-package 1.9.2 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts +2 -2
- package/src/components/common/custom-hooks/use-node-condition.hook/use-node-condition.hook.ts +19 -2
- package/src/components/common/loading-skeletons/details.tsx +53 -25
- package/src/components/common/loading-skeletons/index.tsx +2 -2
- package/src/components/common/loading-skeletons/table.tsx +17 -9
- package/src/components/companies/3-config-provider/index.tsx +17 -1
- package/src/components/form/layout-renderer/1-row/index.tsx +5 -9
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +3 -3
- package/src/components/form/layout-renderer/3-element/11-breadcrumb/index.scss +10 -0
- package/src/components/form/layout-renderer/3-element/11-breadcrumb/index.tsx +100 -0
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +1 -1
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +48 -28
- package/src/constants.ts +1 -1
- package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +0 -51
package/package.json
CHANGED
package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DeviceBreakpointEnum, FormElementConditionalKeyEnum, FormPreservedItemKeys } from '../../../../enums'
|
|
2
2
|
import { isValidationsMet } from './visibility-utils'
|
|
3
|
-
import {
|
|
3
|
+
import { useLayoutEffect } from 'react'
|
|
4
4
|
import useGetCurrentBreakpoint from '../use-window-width.hook'
|
|
5
5
|
import { Form, FormInstance } from 'antd'
|
|
6
6
|
import { hiddenStore } from './visibility-store'
|
|
@@ -19,7 +19,7 @@ export function useSetHiddenNodes(
|
|
|
19
19
|
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
20
20
|
const isFormValuesSet = Form.useWatch(FormPreservedItemKeys.IsDetailsDataSet, { form: formDataRef, preserve: true })
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
useLayoutEffect(() => {
|
|
23
23
|
// during the initial renders, handle the initially hidden nodes
|
|
24
24
|
if (!formDataRef) return
|
|
25
25
|
|
package/src/components/common/custom-hooks/use-node-condition.hook/use-node-condition.hook.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react'
|
|
1
|
+
import { useLayoutEffect, useState, useSyncExternalStore } from 'react'
|
|
2
2
|
import { disabledStore, hiddenStore } from './visibility-store'
|
|
3
3
|
|
|
4
4
|
export const useIsNodeHidden = (id: string): boolean =>
|
|
@@ -8,7 +8,24 @@ export const useIsNodeHidden = (id: string): boolean =>
|
|
|
8
8
|
() => hiddenStore.getSnapshot().includes(id),
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
-
export const checkIsNodeHidden = (id: string): boolean => hiddenStore.getSnapshot().includes(id)
|
|
11
|
+
// export const checkIsNodeHidden = (id: string): boolean => hiddenStore.getSnapshot().includes(id)
|
|
12
|
+
const arraysEqual = (a: string[], b: string[]) => {
|
|
13
|
+
if (a.length !== b.length) return false
|
|
14
|
+
return a.every((x, i) => x === b[i])
|
|
15
|
+
}
|
|
16
|
+
export function useHiddenIds(): string[] {
|
|
17
|
+
const [ids, setIds] = useState<string[]>(() => hiddenStore.getSnapshot())
|
|
18
|
+
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
|
+
const unsubscribe = hiddenStore.subscribe(() => {
|
|
21
|
+
const next = hiddenStore.getSnapshot()
|
|
22
|
+
setIds((prev) => (arraysEqual(prev, next) ? prev : next))
|
|
23
|
+
})
|
|
24
|
+
return unsubscribe
|
|
25
|
+
}, [])
|
|
26
|
+
|
|
27
|
+
return ids
|
|
28
|
+
}
|
|
12
29
|
|
|
13
30
|
export const useIsNodeDisabled = (id: string): boolean =>
|
|
14
31
|
useSyncExternalStore(
|
|
@@ -1,56 +1,82 @@
|
|
|
1
|
-
import { Col, Row, Spin } from 'antd'
|
|
1
|
+
import { Col, Row, Spin, Grid } from 'antd'
|
|
2
2
|
import SkeletonBlock from '.'
|
|
3
3
|
import SectionPanel from '../section-panel'
|
|
4
|
+
import { memo } from 'react'
|
|
5
|
+
|
|
6
|
+
const { useBreakpoint } = Grid
|
|
7
|
+
|
|
8
|
+
function FormDataListSkeleton_Details() {
|
|
9
|
+
const screens = useBreakpoint()
|
|
4
10
|
|
|
5
|
-
export default function FormDataListSkeleton_Details() {
|
|
6
11
|
return (
|
|
7
12
|
<Row justify="center" className="mt-10">
|
|
8
|
-
<Col
|
|
13
|
+
<Col xs={24} md={18} xl={14} xxl={12}>
|
|
9
14
|
<SectionPanel
|
|
10
15
|
className="border"
|
|
11
16
|
header={{
|
|
12
17
|
title: (
|
|
13
18
|
<div className="flex items-center gap-2 p-3">
|
|
14
|
-
<SkeletonBlock width="150px" />
|
|
15
|
-
<SkeletonBlock width=
|
|
19
|
+
<SkeletonBlock width="w-[150px]" />
|
|
20
|
+
<SkeletonBlock width="w-[32px]" height={32} radius={10} />
|
|
16
21
|
</div>
|
|
17
22
|
),
|
|
18
|
-
action: (
|
|
23
|
+
action: !screens.xs && (
|
|
19
24
|
<div className="pr-3">
|
|
20
|
-
<SkeletonBlock width="150px" />
|
|
25
|
+
<SkeletonBlock width="w-[150px]" />
|
|
21
26
|
</div>
|
|
22
27
|
),
|
|
23
28
|
}}
|
|
24
29
|
>
|
|
25
30
|
<div className="p-3 border-t space-y-4">
|
|
26
31
|
<Row gutter={[20, 0]}>
|
|
27
|
-
<Col
|
|
28
|
-
|
|
32
|
+
<Col xs={24} sm={12}>
|
|
33
|
+
{field}
|
|
34
|
+
</Col>
|
|
35
|
+
<Col xs={0} sm={12}>
|
|
36
|
+
{field}
|
|
37
|
+
</Col>
|
|
29
38
|
</Row>
|
|
30
39
|
<Row gutter={[20, 0]}>
|
|
31
|
-
<Col
|
|
32
|
-
|
|
40
|
+
<Col xs={24} sm={12}>
|
|
41
|
+
{field}
|
|
42
|
+
</Col>
|
|
43
|
+
<Col xs={0} sm={12}>
|
|
44
|
+
{field}
|
|
45
|
+
</Col>
|
|
33
46
|
</Row>
|
|
34
47
|
<div className="flex items-center justify-center gap-2 text-16 italic text-primary my-2">
|
|
35
|
-
<Spin
|
|
48
|
+
<Spin /> Loading layout & fetching data..
|
|
36
49
|
</div>
|
|
37
50
|
<Row gutter={[20, 0]}>
|
|
38
|
-
<Col
|
|
39
|
-
|
|
51
|
+
<Col xs={24} sm={12}>
|
|
52
|
+
{field}
|
|
53
|
+
</Col>
|
|
54
|
+
<Col xs={0} sm={12}>
|
|
55
|
+
{field}
|
|
56
|
+
</Col>
|
|
40
57
|
</Row>
|
|
41
58
|
<Row gutter={[20, 0]}>
|
|
42
|
-
<Col
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
<Col xs={24} sm={12} md={8}>
|
|
60
|
+
{field}
|
|
61
|
+
</Col>
|
|
62
|
+
<Col xs={0} sm={12} md={8}>
|
|
63
|
+
{field}
|
|
64
|
+
</Col>
|
|
65
|
+
<Col xs={0} sm={12} md={8}>
|
|
66
|
+
{field}
|
|
67
|
+
</Col>
|
|
45
68
|
</Row>
|
|
46
69
|
<Row gutter={[20, 0]}>
|
|
47
|
-
<Col
|
|
48
|
-
|
|
49
|
-
|
|
70
|
+
<Col xs={24} sm={12} md={8}>
|
|
71
|
+
{field}
|
|
72
|
+
</Col>
|
|
73
|
+
<Col xs={0} sm={12} md={8}>
|
|
74
|
+
{field}
|
|
75
|
+
</Col>
|
|
76
|
+
<Col xs={0} sm={12} md={8}>
|
|
77
|
+
{field}
|
|
78
|
+
</Col>
|
|
50
79
|
</Row>
|
|
51
|
-
<div className="flex justify-end">
|
|
52
|
-
<SkeletonBlock width={200} />
|
|
53
|
-
</div>
|
|
54
80
|
</div>
|
|
55
81
|
</SectionPanel>
|
|
56
82
|
</Col>
|
|
@@ -60,7 +86,9 @@ export default function FormDataListSkeleton_Details() {
|
|
|
60
86
|
|
|
61
87
|
const field = (
|
|
62
88
|
<div className="flex flex-col gap-1">
|
|
63
|
-
<SkeletonBlock width=
|
|
64
|
-
<SkeletonBlock width="
|
|
89
|
+
<SkeletonBlock width="w-[80px]" height={14} />
|
|
90
|
+
<SkeletonBlock width="w-full" />
|
|
65
91
|
</div>
|
|
66
92
|
)
|
|
93
|
+
|
|
94
|
+
export default memo(FormDataListSkeleton_Details)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export default function SkeletonBlock({
|
|
2
2
|
height = 32,
|
|
3
|
-
width = '
|
|
3
|
+
width = '',
|
|
4
4
|
radius = 4,
|
|
5
5
|
}: {
|
|
6
6
|
height?: number
|
|
7
7
|
width?: number | string
|
|
8
8
|
radius?: number | string
|
|
9
9
|
}) {
|
|
10
|
-
return <div style={{ height,
|
|
10
|
+
return <div style={{ height, borderRadius: radius }} className={`skeleton-loader ${width}`} />
|
|
11
11
|
}
|
|
@@ -1,26 +1,32 @@
|
|
|
1
|
+
import { Grid } from 'antd'
|
|
1
2
|
import SkeletonBlock from '.'
|
|
3
|
+
import { memo } from 'react'
|
|
4
|
+
|
|
5
|
+
const { useBreakpoint } = Grid
|
|
6
|
+
|
|
7
|
+
function FormDataListSkeleton_Table({ small = false }: { small?: boolean }) {
|
|
8
|
+
const screens = useBreakpoint()
|
|
2
9
|
|
|
3
|
-
export default function FormDataListSkeleton_Table({ small = false }: { small?: boolean }) {
|
|
4
10
|
return (
|
|
5
11
|
<>
|
|
6
12
|
<div className={`flex items-center justify-between rounded-md mb-2`}>
|
|
7
|
-
<SkeletonBlock width="
|
|
13
|
+
<SkeletonBlock width="xs:w-[100px] sm:w-[150px]" height={small ? 20 : undefined} />
|
|
8
14
|
<div className="flex items-center gap-2">
|
|
9
|
-
<SkeletonBlock width="150px" height={small ? 20 : undefined} />
|
|
10
|
-
<SkeletonBlock width="150px" height={small ? 20 : undefined} />
|
|
11
|
-
<SkeletonBlock width="150px" height={small ? 20 : undefined} />
|
|
15
|
+
<SkeletonBlock width="xs:w-[100px] sm:w-[150px]" height={small ? 20 : undefined} />
|
|
16
|
+
<SkeletonBlock width="xs:w-[0px] sm:w-[150px]" height={small ? 20 : undefined} />
|
|
17
|
+
{!screens.xs && <SkeletonBlock width="xs:w-[0px] sm:w-[150px]" height={small ? 20 : undefined} />}
|
|
12
18
|
</div>
|
|
13
19
|
</div>
|
|
14
20
|
<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) => (
|
|
21
|
+
<div className="flex justify-between gap-2 p-3 border-b border-[#d4d4d8]">
|
|
22
|
+
{[...Array(screens.xs ? 2 : small ? 4 : 5)].map((_, elIdx) => (
|
|
17
23
|
<div key={elIdx}>
|
|
18
|
-
<SkeletonBlock height={small ? 15 : 20} width="
|
|
24
|
+
<SkeletonBlock height={small ? 15 : 20} width="xs:w-[100px] sm:w-[100px]" />
|
|
19
25
|
</div>
|
|
20
26
|
))}
|
|
21
27
|
</div>
|
|
22
28
|
<div className="px-3 py-2 flex flex-col gap-3">
|
|
23
|
-
{[...Array(5)].map((_, elIdx) => (
|
|
29
|
+
{[...Array(small ? 3 : 5)].map((_, elIdx) => (
|
|
24
30
|
<SkeletonBlock key={elIdx} height={small ? 20 : undefined} />
|
|
25
31
|
))}
|
|
26
32
|
</div>
|
|
@@ -28,3 +34,5 @@ export default function FormDataListSkeleton_Table({ small = false }: { small?:
|
|
|
28
34
|
</>
|
|
29
35
|
)
|
|
30
36
|
}
|
|
37
|
+
|
|
38
|
+
export default memo(FormDataListSkeleton_Table)
|
|
@@ -20,7 +20,23 @@ export function ConfigProviderLayout({ children }: { children: ReactNode }): JSX
|
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
<ConfigContext.Provider value={{ config }}>
|
|
23
|
-
<ConfigProvider
|
|
23
|
+
<ConfigProvider
|
|
24
|
+
theme={{
|
|
25
|
+
...theme,
|
|
26
|
+
token: {
|
|
27
|
+
...theme.token,
|
|
28
|
+
screenXSMin: 320,
|
|
29
|
+
screenXS: 375,
|
|
30
|
+
screenXSMax: 425,
|
|
31
|
+
screenSMMax: 768,
|
|
32
|
+
screenMDMax: 1024,
|
|
33
|
+
screenLGMin: 1025,
|
|
34
|
+
screenLG: 1100,
|
|
35
|
+
screenLGMax: 1200,
|
|
36
|
+
screenXLMax: 1440,
|
|
37
|
+
},
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
24
40
|
{showSwitchCompany && (
|
|
25
41
|
<div className="absolute top-2 right-2 z-20">
|
|
26
42
|
<Button_FillerPortal
|
|
@@ -7,19 +7,15 @@ import { LayoutRowRepeatableRenderer } from './repeatable-render'
|
|
|
7
7
|
import { IDndLayoutElement, IDndLayoutRow, IFormJoin } from '../../../../types'
|
|
8
8
|
import { IDynamicButton_DisplayStateProps } from '../3-element/1-dynamic-button'
|
|
9
9
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
10
|
-
import {
|
|
11
|
-
checkIsNodeHidden,
|
|
12
|
-
useIsNodeHidden,
|
|
13
|
-
} from '../../../common/custom-hooks/use-node-condition.hook/use-node-condition.hook'
|
|
10
|
+
import { useHiddenIds } from '../../../common/custom-hooks/use-node-condition.hook/use-node-condition.hook'
|
|
14
11
|
|
|
15
12
|
export const LayoutRendererRow = memo(
|
|
16
13
|
({ basePath = [], rowData, dataCount, formContext, elements, renderButton }: ILayoutRendererRow) => {
|
|
17
|
-
const
|
|
18
|
-
|
|
14
|
+
const hiddenIds = useHiddenIds()
|
|
19
15
|
|
|
20
16
|
return (
|
|
21
17
|
<div
|
|
22
|
-
style={{ display:
|
|
18
|
+
style={{ display: hiddenIds.includes(rowData.id) ? 'none' : undefined }}
|
|
23
19
|
id={rowData.id}
|
|
24
20
|
className={ELEMENTS_DEFAULT_CLASS.LayoutRowContainer}
|
|
25
21
|
>
|
|
@@ -41,7 +37,7 @@ export const LayoutRendererRow = memo(
|
|
|
41
37
|
...getGridContainerStyle(rowData.display),
|
|
42
38
|
}
|
|
43
39
|
const hiddenColsIndices = rowData.children.reduce(
|
|
44
|
-
(curr: number[], col, colIdx) => (
|
|
40
|
+
(curr: number[], col, colIdx) => (hiddenIds.includes(col.id) ? [...curr, colIdx] : curr),
|
|
45
41
|
[],
|
|
46
42
|
)
|
|
47
43
|
|
|
@@ -55,7 +51,7 @@ export const LayoutRendererRow = memo(
|
|
|
55
51
|
|
|
56
52
|
return (
|
|
57
53
|
<div
|
|
58
|
-
className={`${ELEMENTS_DEFAULT_CLASS.LayoutRow} ${
|
|
54
|
+
className={`${ELEMENTS_DEFAULT_CLASS.LayoutRow} ${hiddenIds.includes(rowData.id) ? 'hidden' : ''}`}
|
|
59
55
|
style={{ ...style, gridTemplateColumns, maxWidth: '100vw', overflowX: 'auto' }}
|
|
60
56
|
>
|
|
61
57
|
{rowData.children.map((col, colIdx) => (
|
|
@@ -266,9 +266,9 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
266
266
|
>
|
|
267
267
|
{isPublic &&
|
|
268
268
|
isNewFormDataPage(formDataId) &&
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
)}
|
|
269
|
+
![ButtonActionCategoryEnum.SaveDataChanges, ButtonActionCategoryEnum.Navigate].includes(
|
|
270
|
+
btnProps.category,
|
|
271
|
+
) && <WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />}
|
|
272
272
|
{t({ key: btnKey, type: TranslationTextTypeEnum.Label })}
|
|
273
273
|
</Button_FillerPortal>
|
|
274
274
|
{!!dataLoadingType && (
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { IBreadcrumb, useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
|
|
2
|
+
import { Button_FillerPortal } from '../../../../common/button'
|
|
3
|
+
import { useNavigate } from 'react-router-dom'
|
|
4
|
+
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa6'
|
|
5
|
+
import React, { memo, useMemo } from 'react'
|
|
6
|
+
import { ELEMENTS_DEFAULT_CLASS } from '../../../../../constants'
|
|
7
|
+
import { IElementBaseProps } from '../'
|
|
8
|
+
import { useTranslation } from '../../../../common/custom-hooks/use-translation.hook/hook'
|
|
9
|
+
import useGetCurrentBreakpoint from '../../../../common/custom-hooks/use-window-width.hook'
|
|
10
|
+
import { Select } from 'antd'
|
|
11
|
+
import {
|
|
12
|
+
DeviceBreakpointEnum,
|
|
13
|
+
PageViewTypEnum,
|
|
14
|
+
TranslationTextSubTypeEnum,
|
|
15
|
+
TranslationTextTypeEnum,
|
|
16
|
+
} from '../../../../../enums'
|
|
17
|
+
|
|
18
|
+
import './index.scss'
|
|
19
|
+
|
|
20
|
+
function LayoutRenderer_Breadcrumb({ formId, elementKey }: { formId?: number } & IElementBaseProps) {
|
|
21
|
+
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
22
|
+
const { t } = useTranslation(formId)
|
|
23
|
+
const navigate = useNavigate()
|
|
24
|
+
const { breadcrumbs, sliceAt } = useBreadcrumb()
|
|
25
|
+
|
|
26
|
+
const [detailText, newText, listText] = t([
|
|
27
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbDetail },
|
|
28
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbNew },
|
|
29
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbList },
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
const showAsSelect = useMemo(
|
|
33
|
+
() =>
|
|
34
|
+
[DeviceBreakpointEnum.Mobile, DeviceBreakpointEnum.TabletPortrait].includes(currentBreakpoint) &&
|
|
35
|
+
breadcrumbs.length > 1,
|
|
36
|
+
[currentBreakpoint, breadcrumbs],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if (showAsSelect)
|
|
40
|
+
return (
|
|
41
|
+
<Select
|
|
42
|
+
suffixIcon={null}
|
|
43
|
+
value={breadcrumbs[breadcrumbs.length - 1].href}
|
|
44
|
+
showSearch
|
|
45
|
+
optionFilterProp="label"
|
|
46
|
+
allowClear={false}
|
|
47
|
+
className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} responsive`}
|
|
48
|
+
onChange={(bc) => {
|
|
49
|
+
const decodedUri = decodeURIComponent(bc)
|
|
50
|
+
const bcIdx = breadcrumbs.findIndex((bc) => bc.href === decodedUri)
|
|
51
|
+
|
|
52
|
+
sliceAt(bcIdx)
|
|
53
|
+
navigate(decodedUri)
|
|
54
|
+
}}
|
|
55
|
+
options={breadcrumbs.map((bc) => ({
|
|
56
|
+
value: bc.href,
|
|
57
|
+
label: (
|
|
58
|
+
<div className="flex gap-2 items-center">
|
|
59
|
+
<FaChevronLeft className="text-primary" size={10} />
|
|
60
|
+
{getText(bc, { newText, detailText, listText })}
|
|
61
|
+
</div>
|
|
62
|
+
),
|
|
63
|
+
}))}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} flex items-center text-12`}>
|
|
69
|
+
{breadcrumbs.map((bc, bcIdx) => (
|
|
70
|
+
<React.Fragment key={bcIdx}>
|
|
71
|
+
<Button_FillerPortal
|
|
72
|
+
link
|
|
73
|
+
disabled={breadcrumbs.length - 1 === bcIdx}
|
|
74
|
+
onClick={() => {
|
|
75
|
+
sliceAt(bcIdx)
|
|
76
|
+
navigate(bc.href)
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{getText(bc, { newText, detailText, listText })}
|
|
80
|
+
</Button_FillerPortal>
|
|
81
|
+
{breadcrumbs.length - 1 !== bcIdx && <FaChevronRight className="text-primary" />}
|
|
82
|
+
</React.Fragment>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default memo(LayoutRenderer_Breadcrumb)
|
|
89
|
+
|
|
90
|
+
const getText = (bc: IBreadcrumb, texts: { newText: string; detailText: string; listText: string }) => (
|
|
91
|
+
<span className="font-normal italic">
|
|
92
|
+
{bc.type === PageViewTypEnum.New ? `${texts.newText} ` : ''}
|
|
93
|
+
{bc.label}
|
|
94
|
+
{bc.type === PageViewTypEnum.Details
|
|
95
|
+
? ` ${texts.detailText}`
|
|
96
|
+
: bc.type === PageViewTypEnum.List
|
|
97
|
+
? ` ${texts.listText}`
|
|
98
|
+
: ''}
|
|
99
|
+
</span>
|
|
100
|
+
)
|
|
@@ -264,7 +264,7 @@ interface IMapperFieldObj {
|
|
|
264
264
|
nameFullPath?: string | (string | number)[]
|
|
265
265
|
label?: string | ReactNode
|
|
266
266
|
description?: string
|
|
267
|
-
placeholder?: string | [string, string]
|
|
267
|
+
placeholder?: string | [string, string] | ReactNode
|
|
268
268
|
disabled?: boolean
|
|
269
269
|
options?: ISelectOption[]
|
|
270
270
|
isMultiValue?: boolean
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { memo, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
PageViewTypEnum,
|
|
4
|
-
FieldElementOptionSourceEnum,
|
|
5
|
-
FilterConfigTypeEnum,
|
|
6
|
-
TranslationTextTypeEnum,
|
|
7
|
-
} from '../../../../enums'
|
|
8
2
|
import { IElementBaseProps } from '.'
|
|
9
3
|
import { LayoutRenderer_FieldElement } from './2-field-element'
|
|
10
4
|
import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
|
|
@@ -16,6 +10,17 @@ import { useFindDynamiForm } from '../../../common/custom-hooks'
|
|
|
16
10
|
import { useNavigate } from 'react-router-dom'
|
|
17
11
|
import { constructDynamicFormHref, getIdEqualsQuery, isNewFormDataPage } from '../../../../functions'
|
|
18
12
|
import { useCacheFormLayoutConfig } from '../../../common/custom-hooks/use-cache-form-layout-config.hook'
|
|
13
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
14
|
+
import useGetCurrentBreakpoint from '../../../common/custom-hooks/use-window-width.hook'
|
|
15
|
+
import {
|
|
16
|
+
PageViewTypEnum,
|
|
17
|
+
FieldElementOptionSourceEnum,
|
|
18
|
+
FilterConfigTypeEnum,
|
|
19
|
+
TranslationTextTypeEnum,
|
|
20
|
+
ElementTypeEnum,
|
|
21
|
+
DeviceBreakpointEnum,
|
|
22
|
+
TranslationTextSubTypeEnum,
|
|
23
|
+
} from '../../../../enums'
|
|
19
24
|
import {
|
|
20
25
|
IFilterConfig,
|
|
21
26
|
IFormJoin,
|
|
@@ -29,7 +34,6 @@ import {
|
|
|
29
34
|
ISelectElement,
|
|
30
35
|
ISelectElementProps,
|
|
31
36
|
} from '../../../../types'
|
|
32
|
-
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
33
37
|
|
|
34
38
|
function LayoutRenderer_FieldsWithOptions({
|
|
35
39
|
formContext,
|
|
@@ -38,6 +42,7 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
38
42
|
isDisabled,
|
|
39
43
|
elementKey,
|
|
40
44
|
}: ILayoutRenderer_FieldsWithOptions) {
|
|
45
|
+
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
41
46
|
const { t } = useTranslation(formContext.formId)
|
|
42
47
|
const navigate = useNavigate()
|
|
43
48
|
const { getFormById } = useFindDynamiForm()
|
|
@@ -178,7 +183,6 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
178
183
|
const fetchDetailsStaticOptions = useCallback(
|
|
179
184
|
(optionSource: IOptionSourceReadFromDetails) => {
|
|
180
185
|
const { formId, optionFieldPath } = optionSource
|
|
181
|
-
setLoading(false)
|
|
182
186
|
if (!formId || !cachedConfig) {
|
|
183
187
|
setLoading(false)
|
|
184
188
|
return
|
|
@@ -191,11 +195,16 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
191
195
|
const element = elements[field]
|
|
192
196
|
const fieldOptionSource = (element as ISelectElement | IRadioElement).props?.optionSource as IOptionSourceConstant
|
|
193
197
|
if (fieldOptionSource?.type === FieldElementOptionSourceEnum.Static && Array.isArray(fieldOptionSource.options))
|
|
194
|
-
setOptions(
|
|
198
|
+
setOptions(
|
|
199
|
+
fieldOptionSource.options.map((op) => ({
|
|
200
|
+
value: op.id,
|
|
201
|
+
label: t({ key: element.key, type: TranslationTextTypeEnum.OptionValue, subType: op.id }),
|
|
202
|
+
})),
|
|
203
|
+
)
|
|
195
204
|
|
|
196
205
|
setLoading(false)
|
|
197
206
|
},
|
|
198
|
-
[cachedConfig],
|
|
207
|
+
[cachedConfig, elementKey],
|
|
199
208
|
)
|
|
200
209
|
|
|
201
210
|
useEffect(() => {
|
|
@@ -236,11 +245,24 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
236
245
|
}
|
|
237
246
|
}, [])
|
|
238
247
|
|
|
239
|
-
const [label, placeholder] = t([
|
|
248
|
+
const [label, placeholder, goToDetailsText] = t([
|
|
240
249
|
{ key: elementData.key, type: TranslationTextTypeEnum.Label },
|
|
241
250
|
{ key: elementData.key, type: TranslationTextTypeEnum.Placeholder },
|
|
251
|
+
{ key: elementData.key, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.Secondary },
|
|
242
252
|
])
|
|
243
253
|
|
|
254
|
+
const type = useMemo(() => {
|
|
255
|
+
// converting Radio into Select on XS, and SM screens if the options are more than 2
|
|
256
|
+
if (
|
|
257
|
+
elementData.elementType === ElementTypeEnum.Radio &&
|
|
258
|
+
[DeviceBreakpointEnum.Mobile, DeviceBreakpointEnum.TabletPortrait].includes(currentBreakpoint) &&
|
|
259
|
+
options.length > 2
|
|
260
|
+
)
|
|
261
|
+
return ElementTypeEnum.Select
|
|
262
|
+
|
|
263
|
+
return elementData.elementType
|
|
264
|
+
}, [currentBreakpoint, elementData.elementType, options.length])
|
|
265
|
+
|
|
244
266
|
return (
|
|
245
267
|
<div className="relative">
|
|
246
268
|
<LayoutRenderer_FieldElement formRef={formRef} elementKey={elementData.key} formId={formContext.formId}>
|
|
@@ -250,25 +272,23 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
250
272
|
label: label && (
|
|
251
273
|
<div className="flex items-center gap-2 w-full">
|
|
252
274
|
<span>{label}</span>
|
|
253
|
-
{(props as ISelectElementProps).goToDetails?.enabled &&
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
</div>
|
|
268
|
-
)}
|
|
275
|
+
{(props as ISelectElementProps).goToDetails?.enabled && goToDetailsText && selectedValue && (
|
|
276
|
+
<div
|
|
277
|
+
className="px-2 text-primary font-bold text-12 hover:underline cursor-pointer"
|
|
278
|
+
onClick={() => {
|
|
279
|
+
const formInfo = getFormById(props.optionSource.formId)
|
|
280
|
+
if (!formInfo) return
|
|
281
|
+
|
|
282
|
+
push({ label: formInfo.name, href: location.pathname, type: PageViewTypEnum.Details })
|
|
283
|
+
navigate(`${constructDynamicFormHref(formInfo.name)}/${selectedValue}`)
|
|
284
|
+
}}
|
|
285
|
+
>
|
|
286
|
+
{goToDetailsText}
|
|
287
|
+
</div>
|
|
288
|
+
)}
|
|
269
289
|
</div>
|
|
270
290
|
),
|
|
271
|
-
type
|
|
291
|
+
type,
|
|
272
292
|
name: formItem.name,
|
|
273
293
|
validations: elementData.validations,
|
|
274
294
|
options,
|
package/src/constants.ts
CHANGED
|
@@ -26,7 +26,7 @@ export const LANGUAGE_LABELS = {
|
|
|
26
26
|
[CountryEnum.CN]: 'Chinese',
|
|
27
27
|
}
|
|
28
28
|
export const DEVICE_BREAKPOINTS = {
|
|
29
|
-
[DeviceBreakpointEnum.Mobile]: '
|
|
29
|
+
[DeviceBreakpointEnum.Mobile]: '425px',
|
|
30
30
|
[DeviceBreakpointEnum.TabletPortrait]: '768px',
|
|
31
31
|
[DeviceBreakpointEnum.TabletLandscape]: '992px',
|
|
32
32
|
[DeviceBreakpointEnum.Laptop]: '1200px',
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { useBreadcrumb } from '../../../common/custom-hooks/use-breadcrumb.hook'
|
|
2
|
-
import { Button_FillerPortal } from '../../../common/button'
|
|
3
|
-
import { useNavigate } from 'react-router-dom'
|
|
4
|
-
import { FaChevronRight } from 'react-icons/fa6'
|
|
5
|
-
import React, { memo } from 'react'
|
|
6
|
-
import { PageViewTypEnum, TranslationTextSubTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
7
|
-
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
8
|
-
import { IElementBaseProps } from '.'
|
|
9
|
-
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
10
|
-
|
|
11
|
-
function LayoutRenderer_Breadcrumb({ formId, elementKey }: { formId?: number } & IElementBaseProps) {
|
|
12
|
-
const { t } = useTranslation(formId)
|
|
13
|
-
const navigate = useNavigate()
|
|
14
|
-
const { breadcrumbs, sliceAt } = useBreadcrumb()
|
|
15
|
-
|
|
16
|
-
const [detailText, newText, listText] = t([
|
|
17
|
-
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbDetail },
|
|
18
|
-
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbNew },
|
|
19
|
-
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbList },
|
|
20
|
-
])
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<div className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} flex items-center text-12`}>
|
|
24
|
-
{breadcrumbs.map((bc, bcIdx) => (
|
|
25
|
-
<React.Fragment key={bcIdx}>
|
|
26
|
-
<Button_FillerPortal
|
|
27
|
-
link
|
|
28
|
-
disabled={breadcrumbs.length - 1 === bcIdx}
|
|
29
|
-
onClick={() => {
|
|
30
|
-
sliceAt(bcIdx)
|
|
31
|
-
navigate(bc.href)
|
|
32
|
-
}}
|
|
33
|
-
>
|
|
34
|
-
<span className="font-normal italic">
|
|
35
|
-
{bc.type === PageViewTypEnum.New ? `${newText} ` : ''}
|
|
36
|
-
{bc.label}
|
|
37
|
-
{bc.type === PageViewTypEnum.Details
|
|
38
|
-
? ` ${detailText}`
|
|
39
|
-
: bc.type === PageViewTypEnum.List
|
|
40
|
-
? ` ${listText}`
|
|
41
|
-
: ''}
|
|
42
|
-
</span>
|
|
43
|
-
</Button_FillerPortal>
|
|
44
|
-
{breadcrumbs.length - 1 !== bcIdx && <FaChevronRight className="text-primary" />}
|
|
45
|
-
</React.Fragment>
|
|
46
|
-
))}
|
|
47
|
-
</div>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export default memo(LayoutRenderer_Breadcrumb)
|