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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.9.2",
3
+ "version": "1.9.3",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,6 +1,6 @@
1
1
  import { DeviceBreakpointEnum, FormElementConditionalKeyEnum, FormPreservedItemKeys } from '../../../../enums'
2
2
  import { isValidationsMet } from './visibility-utils'
3
- import { useEffect } from 'react'
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
- useEffect(() => {
22
+ useLayoutEffect(() => {
23
23
  // during the initial renders, handle the initially hidden nodes
24
24
  if (!formDataRef) return
25
25
 
@@ -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 sm={24} lg={18} xl={14} xxl={12}>
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={32} height={32} radius={10} />
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 span={12}>{field}</Col>
28
- <Col span={12}>{field}</Col>
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 span={12}>{field}</Col>
32
- <Col span={12}>{field}</Col>
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 /> Loading layout & fetching data..
48
+ <Spin /> Loading layout & fetching data..
36
49
  </div>
37
50
  <Row gutter={[20, 0]}>
38
- <Col span={12}>{field}</Col>
39
- <Col span={12}>{field}</Col>
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 span={8}>{field}</Col>
43
- <Col span={8}>{field}</Col>
44
- <Col span={8}>{field}</Col>
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 span={8}>{field}</Col>
48
- <Col span={8}>{field}</Col>
49
- <Col span={8}>{field}</Col>
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={80} height={14} />
64
- <SkeletonBlock width="100%" />
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 = '100%',
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, width, borderRadius: radius }} className="skeleton-loader " />
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="200px" height={small ? 20 : undefined} />
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="150px" />
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 theme={theme}>
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 isHidden = useIsNodeHidden(rowData.id)
18
-
14
+ const hiddenIds = useHiddenIds()
19
15
 
20
16
  return (
21
17
  <div
22
- style={{ display: isHidden ? 'none' : undefined }}
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) => (checkIsNodeHidden(col.id) ? [...curr, colIdx] : curr),
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} ${isHidden ? 'hidden' : ''}`}
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
- btnProps.category !== ButtonActionCategoryEnum.SaveDataChanges && (
270
- <WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
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,10 @@
1
+ .ant-select.ant-select-outlined.fc-breadcrumb.responsive {
2
+ .ant-select-selector {
3
+ @apply bg-transparent border-none;
4
+ }
5
+ &.ant-select-focused {
6
+ .ant-select-selector {
7
+ @apply shadow-none;
8
+ }
9
+ }
10
+ }
@@ -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(fieldOptionSource.options.map((op) => ({ value: op.id, label: op.value })))
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
- (props as ISelectElementProps).goToDetails?.text &&
255
- selectedValue && (
256
- <div
257
- className="px-2 text-primary font-bold text-12 hover:underline cursor-pointer"
258
- onClick={() => {
259
- const formInfo = getFormById(props.optionSource.formId)
260
- if (!formInfo) return
261
-
262
- push({ label: formInfo.name, href: location.pathname, type: PageViewTypEnum.Details })
263
- navigate(`${constructDynamicFormHref(formInfo.name)}/${selectedValue}`)
264
- }}
265
- >
266
- {(props as ISelectElementProps).goToDetails!.text}
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: elementData.elementType,
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]: '576px',
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)