@verifiedinc-public/shared-ui-elements 0.11.6 → 0.11.7-beta.0
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 +11 -24
- package/src/components/Alert/Alert.tsx +8 -0
- package/src/components/Alert/FullWidthAlert.tsx +27 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/Button/index.tsx +8 -0
- package/src/components/CredentialRequestsEditor/CredentialRequestsEditor.context.tsx +98 -0
- package/src/components/CredentialRequestsEditor/components/CredentialRequestsField.tsx +103 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldAccordion.tsx +337 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldDeleteModal.tsx +64 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldDescription.tsx +68 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldMandatory.tsx +84 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldMulti.tsx +74 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldOptionType.tsx +84 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldSection.tsx +48 -0
- package/src/components/CredentialRequestsEditor/components/DataFieldUserInput.tsx +71 -0
- package/src/components/CredentialRequestsEditor/components/RadioOption.tsx +89 -0
- package/src/components/CredentialRequestsEditor/contexts/CredentialRequestFieldContext.tsx +36 -0
- package/src/components/CredentialRequestsEditor/index.tsx +15 -0
- package/src/components/CredentialRequestsEditor/types/compositeCredentialSchema.ts +1 -0
- package/src/components/CredentialRequestsEditor/types/credentialSchemasDto.ts +3 -0
- package/src/components/CredentialRequestsEditor/types/form.ts +28 -0
- package/src/components/CredentialRequestsEditor/types/mandatoryEnum.ts +5 -0
- package/src/components/CredentialRequestsEditor/utils/buildDataFieldValue.ts +65 -0
- package/src/components/CredentialRequestsEditor/utils/prettyField.ts +16 -0
- package/src/components/Image.tsx +10 -0
- package/src/components/QRCodeDisplay/index.tsx +50 -0
- package/src/components/RequiredLabel/index.tsx +15 -0
- package/src/components/TextField/index.tsx +8 -0
- package/src/components/Tip/index.tsx +18 -0
- package/src/components/Typography/index.tsx +8 -0
- package/src/components/When.tsx +28 -0
- package/src/components/form/CountrySelector.tsx +96 -0
- package/src/components/form/DataFieldClearAdornment.tsx +28 -0
- package/src/components/form/DateInput.tsx +117 -0
- package/src/components/form/DefaultInput.tsx +28 -0
- package/src/components/form/InputMask.tsx +41 -0
- package/src/components/form/OTPInput.tsx +246 -0
- package/src/components/form/PhoneInput.tsx +153 -0
- package/src/components/form/SSNInput.tsx +100 -0
- package/src/components/form/SelectInput.tsx +89 -0
- package/src/components/form/TextMaskCustom.tsx +48 -0
- package/src/components/form/index.ts +5 -0
- package/src/components/form/styles/input.ts +24 -0
- package/src/components/index.ts +10 -0
- package/src/components/terms/AcceptTermsNotice.tsx +27 -0
- package/src/components/terms/LegalLink.tsx +22 -0
- package/src/components/verified/VerifiedImage.tsx +272 -0
- package/src/components/verified/VerifiedIncLogo.tsx +11 -0
- package/src/components/verified/index.ts +2 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useCallbackRef.ts +22 -0
- package/src/hooks/useCopyToClipboard.ts +76 -0
- package/src/hooks/useDisclosure.ts +96 -0
- package/src/hooks/useLocalStorage.ts +24 -0
- package/src/hooks/usePrevious.ts +17 -0
- package/src/hooks/useQRCode.ts +62 -0
- package/src/index.ts +5 -0
- package/src/stories/components/Alert.stories.tsx +41 -0
- package/src/stories/components/Button.stories.ts +49 -0
- package/src/stories/components/CredentialRequestsEditor.stories.tsx +98 -0
- package/src/stories/components/QRCodeDisplay.stories.tsx +60 -0
- package/src/stories/components/TextField.stories.ts +59 -0
- package/src/stories/components/Typography.stories.ts +140 -0
- package/src/stories/components/VerifiedImage.stories.tsx +32 -0
- package/src/stories/components/form/DateInput.stories.ts +39 -0
- package/src/stories/components/form/OTPInput.stories.tsx +90 -0
- package/src/stories/components/form/PhoneInput.stories.tsx +34 -0
- package/src/stories/components/form/SSNInput.stories.ts +30 -0
- package/src/stories/components/form/SelectInput.stories.ts +39 -0
- package/src/stories/hooks/useCopyToClipboard.stories.tsx +45 -0
- package/src/styles/colors.ts +34 -0
- package/src/styles/index.ts +2 -0
- package/src/styles/theme.ts +209 -0
- package/src/utils/date.ts +41 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/masks/index.ts +6 -0
- package/src/utils/omitProperty.ts +19 -0
- package/src/utils/phone.ts +76 -0
- package/src/utils/wrapPromise.ts +19 -0
- package/src/validations/birthDate.schema.ts +54 -0
- package/src/validations/date.schema.ts +10 -0
- package/src/validations/description.schema.ts +5 -0
- package/src/validations/email.schema.ts +3 -0
- package/src/validations/field.schema.ts +3 -0
- package/src/validations/index.ts +9 -0
- package/src/validations/phone.schema.ts +6 -0
- package/src/validations/ssn.schema.ts +6 -0
- package/src/validations/state.schema.ts +3 -0
- package/src/validations/unix.schema.ts +11 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@verifiedinc-public/shared-ui-elements",
|
3
|
-
"version": "0.11.
|
3
|
+
"version": "0.11.7-beta.0",
|
4
4
|
"description": "A set of UI components, utilities that is shareable with the core apps.",
|
5
5
|
"private": false,
|
6
6
|
"keywords": [],
|
@@ -12,33 +12,20 @@
|
|
12
12
|
"main": "/dist/shared-ui-elements.mjs",
|
13
13
|
"types": "/dist/index.d.ts",
|
14
14
|
"files": [
|
15
|
-
"dist"
|
15
|
+
"dist",
|
16
|
+
"src"
|
16
17
|
],
|
17
18
|
"exports": {
|
18
19
|
".": {
|
20
|
+
"types": "./dist/index.d.ts",
|
19
21
|
"import": "./dist/shared-ui-elements.mjs",
|
20
|
-
"
|
22
|
+
"require": "./dist/shared-ui-elements.mjs"
|
21
23
|
},
|
22
|
-
"./components":
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"./
|
27
|
-
"import": "./dist/shared-ui-elements.mjs",
|
28
|
-
"types": "./dist/index.d.ts"
|
29
|
-
},
|
30
|
-
"./utils": {
|
31
|
-
"import": "./dist/shared-ui-elements.mjs",
|
32
|
-
"types": "./dist/index.d.ts"
|
33
|
-
},
|
34
|
-
"./validations": {
|
35
|
-
"import": "./dist/shared-ui-elements.mjs",
|
36
|
-
"types": "./dist/index.d.ts"
|
37
|
-
},
|
38
|
-
"./styles": {
|
39
|
-
"import": "./dist/shared-ui-elements.mjs",
|
40
|
-
"types": "./dist/index.d.ts"
|
41
|
-
}
|
24
|
+
"./components": "./src/components/index.ts",
|
25
|
+
"./hooks": "./src/hooks/index.ts",
|
26
|
+
"./styles": "./src/styles/index.ts",
|
27
|
+
"./utils": "./src/utils/index.ts",
|
28
|
+
"./validations": "./src/validations/index.ts"
|
42
29
|
},
|
43
30
|
"scripts": {
|
44
31
|
"vitest": "vitest",
|
@@ -104,6 +91,7 @@
|
|
104
91
|
"tsconfig-paths": "^4.2.0",
|
105
92
|
"tsx": "^3.12.4",
|
106
93
|
"typescript": "^5.2.2",
|
94
|
+
"vite-plugin-commonjs": "^0.10.3",
|
107
95
|
"vite-plugin-dts": "^4.3.0",
|
108
96
|
"vitest": "^1.6.0",
|
109
97
|
"zod": "^3.23.8"
|
@@ -116,7 +104,6 @@
|
|
116
104
|
"@mona-health/react-input-mask": "^3.0.3",
|
117
105
|
"@mui/icons-material": "^5",
|
118
106
|
"@mui/material": "^5",
|
119
|
-
"jsdom": "^24.1.0",
|
120
107
|
"libphonenumber-js": "^1.11.11",
|
121
108
|
"qrcode": "^1.5.4",
|
122
109
|
"react": "^18",
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { Alert, type AlertProps } from '@mui/material';
|
2
|
+
|
3
|
+
interface FullWidthAlertProps extends AlertProps {}
|
4
|
+
|
5
|
+
export function FullWidthAlert({
|
6
|
+
children,
|
7
|
+
sx,
|
8
|
+
...props
|
9
|
+
}: FullWidthAlertProps): React.JSX.Element {
|
10
|
+
return (
|
11
|
+
<>
|
12
|
+
<Alert
|
13
|
+
severity='info'
|
14
|
+
sx={{
|
15
|
+
maxWidth: '100%',
|
16
|
+
width: '100%',
|
17
|
+
textAlign: 'left',
|
18
|
+
alignItems: 'center',
|
19
|
+
...sx,
|
20
|
+
}}
|
21
|
+
{...props}
|
22
|
+
>
|
23
|
+
{children}
|
24
|
+
</Alert>
|
25
|
+
</>
|
26
|
+
);
|
27
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import MUIButton from '@mui/material/Button';
|
2
|
+
import type { ButtonProps as MUIButtonProps } from '@mui/material/Button';
|
3
|
+
|
4
|
+
export type ButtonProps = MUIButtonProps;
|
5
|
+
|
6
|
+
export function Button(props: ButtonProps): React.JSX.Element {
|
7
|
+
return <MUIButton {...props} />;
|
8
|
+
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import { createContext, type ReactNode, useContext, useEffect } from 'react';
|
2
|
+
import { FormProvider, useForm, type WatchObserver } from 'react-hook-form';
|
3
|
+
import debounce from 'lodash/debounce';
|
4
|
+
|
5
|
+
import { omitProperties } from '../../utils/omitProperty';
|
6
|
+
|
7
|
+
import {
|
8
|
+
type CredentialRequests,
|
9
|
+
type CredentialRequestsEditorForm,
|
10
|
+
type CredentialRequestsWithNew,
|
11
|
+
} from './types/form';
|
12
|
+
|
13
|
+
export interface CredentialRequestsEditorFeatures {
|
14
|
+
allowUserInput?: {
|
15
|
+
disabled?: boolean;
|
16
|
+
};
|
17
|
+
description?: {
|
18
|
+
disabled?: boolean;
|
19
|
+
};
|
20
|
+
mandatory?: {
|
21
|
+
disabled?: boolean;
|
22
|
+
};
|
23
|
+
multi?: {
|
24
|
+
disabled?: boolean;
|
25
|
+
};
|
26
|
+
}
|
27
|
+
|
28
|
+
export interface CredentialRequestsEditorProps {
|
29
|
+
addButtonText?: string;
|
30
|
+
credentialRequests: CredentialRequestsWithNew[];
|
31
|
+
schemas: Record<string, any>;
|
32
|
+
children: ReactNode;
|
33
|
+
onChange: (credentialRequests: CredentialRequests[]) => void;
|
34
|
+
features?: CredentialRequestsEditorFeatures;
|
35
|
+
}
|
36
|
+
|
37
|
+
export interface CredentialRequestsEditorContext {
|
38
|
+
addButtonText?: string;
|
39
|
+
schemas: Record<string, any>;
|
40
|
+
features?: CredentialRequestsEditorFeatures;
|
41
|
+
}
|
42
|
+
|
43
|
+
const Context = createContext<CredentialRequestsEditorContext | null>(null);
|
44
|
+
|
45
|
+
export function useCredentialRequestsEditor(): CredentialRequestsEditorContext {
|
46
|
+
const context = useContext(Context);
|
47
|
+
if (!context) {
|
48
|
+
throw new Error(
|
49
|
+
'useCredentialRequestsEditor must be used within a CredentialRequestsEditorProvider',
|
50
|
+
);
|
51
|
+
}
|
52
|
+
return context;
|
53
|
+
}
|
54
|
+
|
55
|
+
export function CredentialRequestsEditorProvider(
|
56
|
+
props: CredentialRequestsEditorProps,
|
57
|
+
): React.JSX.Element {
|
58
|
+
const form = useForm<CredentialRequestsEditorForm>({
|
59
|
+
defaultValues: { credentialRequests: props.credentialRequests },
|
60
|
+
});
|
61
|
+
|
62
|
+
// Listen to credentialRequests changes and call onChange event
|
63
|
+
useEffect(() => {
|
64
|
+
// Debouncing the watch observer to prevent multiple calls in a short period of time since it may dispatch child object change plus the property change
|
65
|
+
const debouncedWatchObserver = debounce<
|
66
|
+
WatchObserver<CredentialRequestsEditorForm>
|
67
|
+
>((data, { name, type }) => {
|
68
|
+
if (data.credentialRequests) {
|
69
|
+
const credentialRequestsData = data.credentialRequests.filter(
|
70
|
+
(credentialRequest) => !!credentialRequest?.type,
|
71
|
+
);
|
72
|
+
|
73
|
+
props.onChange(
|
74
|
+
omitProperties(credentialRequestsData, [
|
75
|
+
'isNew',
|
76
|
+
'id',
|
77
|
+
]) as CredentialRequests[],
|
78
|
+
);
|
79
|
+
}
|
80
|
+
}, 100);
|
81
|
+
const subscription = form.watch(debouncedWatchObserver);
|
82
|
+
return subscription.unsubscribe;
|
83
|
+
}, [form.watch]);
|
84
|
+
|
85
|
+
return (
|
86
|
+
<FormProvider {...form}>
|
87
|
+
<Context.Provider
|
88
|
+
value={{
|
89
|
+
addButtonText: props.addButtonText,
|
90
|
+
schemas: props.schemas,
|
91
|
+
features: props.features,
|
92
|
+
}}
|
93
|
+
>
|
94
|
+
{props.children}
|
95
|
+
</Context.Provider>
|
96
|
+
</FormProvider>
|
97
|
+
);
|
98
|
+
}
|
@@ -0,0 +1,103 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Stack } from '@mui/material';
|
3
|
+
import { Add } from '@mui/icons-material';
|
4
|
+
import {
|
5
|
+
useFieldArray,
|
6
|
+
type UseFieldArrayReturn,
|
7
|
+
useFormContext,
|
8
|
+
} from 'react-hook-form';
|
9
|
+
import { DndProvider } from 'react-dnd';
|
10
|
+
import { HTML5Backend } from 'react-dnd-html5-backend';
|
11
|
+
|
12
|
+
import { Button } from '../../Button';
|
13
|
+
|
14
|
+
import { buildDataFieldValue } from '../utils/buildDataFieldValue';
|
15
|
+
import { CredentialRequestFieldProvider } from '../contexts/CredentialRequestFieldContext';
|
16
|
+
import { DataFieldAccordion } from './DataFieldAccordion';
|
17
|
+
|
18
|
+
import { useCredentialRequestsEditor } from '../CredentialRequestsEditor.context';
|
19
|
+
import {
|
20
|
+
type CredentialRequestsEditorForm,
|
21
|
+
type CredentialRequestsWithNew,
|
22
|
+
} from '../types/form';
|
23
|
+
|
24
|
+
function CredentialRequestField({
|
25
|
+
path = 'credentialRequests',
|
26
|
+
parentFieldArray,
|
27
|
+
parentIndex = 0,
|
28
|
+
level = 0,
|
29
|
+
}: Readonly<{
|
30
|
+
path?: string;
|
31
|
+
parentFieldArray?: UseFieldArrayReturn<CredentialRequestsEditorForm>;
|
32
|
+
parentIndex?: number;
|
33
|
+
level?: number;
|
34
|
+
}>): React.JSX.Element {
|
35
|
+
const customConfig = useCredentialRequestsEditor();
|
36
|
+
const form = useFormContext<CredentialRequestsEditorForm>();
|
37
|
+
const fieldArray = useFieldArray<CredentialRequestsEditorForm>({
|
38
|
+
control: form.control,
|
39
|
+
name: path as any,
|
40
|
+
});
|
41
|
+
|
42
|
+
return (
|
43
|
+
<>
|
44
|
+
{fieldArray.fields.map((field, index) => {
|
45
|
+
const _path = `${path}.${index}`;
|
46
|
+
return (
|
47
|
+
<CredentialRequestFieldProvider
|
48
|
+
key={_path + field.type}
|
49
|
+
path={_path}
|
50
|
+
field={field}
|
51
|
+
fieldArray={fieldArray}
|
52
|
+
index={index}
|
53
|
+
level={level}
|
54
|
+
onAllFieldsDelete={() => {
|
55
|
+
(parentFieldArray ?? fieldArray)?.remove(parentIndex);
|
56
|
+
}}
|
57
|
+
>
|
58
|
+
<DataFieldAccordion />
|
59
|
+
{Array.isArray(field.children) && (
|
60
|
+
<CredentialRequestField
|
61
|
+
key={`${_path}.children`}
|
62
|
+
path={`${_path}.children`}
|
63
|
+
parentFieldArray={fieldArray}
|
64
|
+
parentIndex={index}
|
65
|
+
level={level + 1}
|
66
|
+
/>
|
67
|
+
)}
|
68
|
+
</CredentialRequestFieldProvider>
|
69
|
+
);
|
70
|
+
})}
|
71
|
+
{path === 'credentialRequests' && (
|
72
|
+
<Button
|
73
|
+
type='button'
|
74
|
+
onClick={() => {
|
75
|
+
if (!customConfig) return;
|
76
|
+
const newValue: CredentialRequestsWithNew = {
|
77
|
+
...buildDataFieldValue('', customConfig.schemas),
|
78
|
+
isNew: true,
|
79
|
+
};
|
80
|
+
fieldArray.append(newValue);
|
81
|
+
}}
|
82
|
+
size='large'
|
83
|
+
variant='outlined'
|
84
|
+
startIcon={<Add />}
|
85
|
+
fullWidth
|
86
|
+
sx={{ width: '100%' }}
|
87
|
+
>
|
88
|
+
{customConfig.addButtonText ?? 'Add Credential Request'}
|
89
|
+
</Button>
|
90
|
+
)}
|
91
|
+
</>
|
92
|
+
);
|
93
|
+
}
|
94
|
+
|
95
|
+
export function CredentialRequestsField(): React.JSX.Element {
|
96
|
+
return (
|
97
|
+
<DndProvider backend={HTML5Backend}>
|
98
|
+
<Stack spacing={2}>
|
99
|
+
<CredentialRequestField />
|
100
|
+
</Stack>
|
101
|
+
</DndProvider>
|
102
|
+
);
|
103
|
+
}
|
@@ -0,0 +1,337 @@
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
2
|
+
import {
|
3
|
+
Accordion,
|
4
|
+
AccordionDetails,
|
5
|
+
AccordionSummary,
|
6
|
+
Box,
|
7
|
+
IconButton,
|
8
|
+
Paper,
|
9
|
+
Stack,
|
10
|
+
Typography,
|
11
|
+
useTheme,
|
12
|
+
} from '@mui/material';
|
13
|
+
import {
|
14
|
+
CheckCircle,
|
15
|
+
ChevronLeft,
|
16
|
+
Close,
|
17
|
+
Delete,
|
18
|
+
Menu,
|
19
|
+
} from '@mui/icons-material';
|
20
|
+
import { useDrag, useDrop } from 'react-dnd';
|
21
|
+
import { useController, useFormContext } from 'react-hook-form';
|
22
|
+
|
23
|
+
import { RequiredLabel } from '../../RequiredLabel';
|
24
|
+
|
25
|
+
import { prettyField } from '../utils/prettyField';
|
26
|
+
import {
|
27
|
+
type CredentialRequestsEditorForm,
|
28
|
+
type CredentialRequestsWithNew,
|
29
|
+
} from '../types/form';
|
30
|
+
import { MandatoryEnum } from '../types/mandatoryEnum';
|
31
|
+
import { useCredentialRequestField } from '../contexts/CredentialRequestFieldContext';
|
32
|
+
import { DataFieldOptionType } from './DataFieldOptionType';
|
33
|
+
import { DataFieldDescription } from './DataFieldDescription';
|
34
|
+
import { DataFieldMandatory } from './DataFieldMandatory';
|
35
|
+
import { DataFieldUserInput } from './DataFieldUserInput';
|
36
|
+
import { DataFieldDeleteModal } from './DataFieldDeleteModal';
|
37
|
+
import { DataFieldMulti } from './DataFieldMulti';
|
38
|
+
|
39
|
+
interface DataFieldAccordionProps {
|
40
|
+
defaultExpanded?: boolean;
|
41
|
+
}
|
42
|
+
|
43
|
+
export function DataFieldAccordion(
|
44
|
+
props: DataFieldAccordionProps,
|
45
|
+
): React.JSX.Element {
|
46
|
+
const { defaultExpanded } = props;
|
47
|
+
const credentialRequestField = useCredentialRequestField();
|
48
|
+
const formContext = useFormContext<CredentialRequestsEditorForm>();
|
49
|
+
const field = useController<CredentialRequestsEditorForm>({
|
50
|
+
name: `${credentialRequestField?.path as any}` as any,
|
51
|
+
});
|
52
|
+
const credentialRequest = field.field.value as CredentialRequestsWithNew;
|
53
|
+
const credentialRequests = formContext.watch('credentialRequests');
|
54
|
+
const isNew: boolean = (credentialRequestField?.field as any).isNew;
|
55
|
+
const [expanded, setOpen] = useState((defaultExpanded ?? isNew) || false);
|
56
|
+
const [modalOpen, setModalOpen] = useState(false);
|
57
|
+
|
58
|
+
const accordionRef = useRef<HTMLDivElement | null>(null);
|
59
|
+
|
60
|
+
const fieldType = String(credentialRequestField?.field.type);
|
61
|
+
const type = prettyField(fieldType || 'Choose a type...');
|
62
|
+
|
63
|
+
const theme = useTheme();
|
64
|
+
const chevronClassName = 'chevron';
|
65
|
+
|
66
|
+
const canDrop = useCallback(
|
67
|
+
(item: typeof credentialRequestField) => {
|
68
|
+
const source = item;
|
69
|
+
const target = credentialRequestField;
|
70
|
+
|
71
|
+
if (!source || !target) return false;
|
72
|
+
|
73
|
+
const getParentPath = (path: string): string =>
|
74
|
+
path.split('.').slice(0, -2).join('.');
|
75
|
+
|
76
|
+
const sourcePath = getParentPath(source?.path ?? '');
|
77
|
+
const targetPath = getParentPath(target?.path ?? '');
|
78
|
+
const isSameGroup = sourcePath === targetPath;
|
79
|
+
|
80
|
+
const fromLevel = source.level;
|
81
|
+
const fromIndex = source.index;
|
82
|
+
const toLevel = target.level;
|
83
|
+
const toIndex = target.index;
|
84
|
+
|
85
|
+
// Allow to drop only on the same level and different index
|
86
|
+
if (fromLevel !== toLevel || fromIndex === toIndex || !isSameGroup) {
|
87
|
+
return false;
|
88
|
+
}
|
89
|
+
|
90
|
+
return true;
|
91
|
+
},
|
92
|
+
[credentialRequestField],
|
93
|
+
);
|
94
|
+
|
95
|
+
const [{ opacity }, drag, preview] = useDrag(
|
96
|
+
() => ({
|
97
|
+
type: 'data-field-drag',
|
98
|
+
item: () => credentialRequestField,
|
99
|
+
collect: (monitor) => ({
|
100
|
+
opacity: monitor.isDragging() ? 0 : 1,
|
101
|
+
}),
|
102
|
+
}),
|
103
|
+
[credentialRequestField, credentialRequests],
|
104
|
+
);
|
105
|
+
|
106
|
+
const [{ opacity: dropOpacity }, drop] = useDrop(
|
107
|
+
() => ({
|
108
|
+
accept: 'data-field-drag',
|
109
|
+
canDrop(item) {
|
110
|
+
return canDrop(item as typeof credentialRequestField);
|
111
|
+
},
|
112
|
+
drop(item) {
|
113
|
+
const source = item as typeof credentialRequestField;
|
114
|
+
const target = credentialRequestField;
|
115
|
+
|
116
|
+
if (!source || !target) return;
|
117
|
+
if (!canDrop(source)) return;
|
118
|
+
|
119
|
+
const fromIndex = source.index;
|
120
|
+
const toIndex = target.index;
|
121
|
+
|
122
|
+
credentialRequestField.fieldArray.move(fromIndex, toIndex);
|
123
|
+
},
|
124
|
+
collect: (monitor) => {
|
125
|
+
if (monitor.isOver()) {
|
126
|
+
return {
|
127
|
+
opacity: monitor.canDrop() ? 0.4 : 1,
|
128
|
+
};
|
129
|
+
}
|
130
|
+
return {
|
131
|
+
opacity: 1,
|
132
|
+
};
|
133
|
+
},
|
134
|
+
}),
|
135
|
+
[credentialRequestField, credentialRequests],
|
136
|
+
);
|
137
|
+
|
138
|
+
const handleRemove = (): void => {
|
139
|
+
if (!credentialRequestField) return;
|
140
|
+
setModalOpen(false);
|
141
|
+
|
142
|
+
// Delete parent when the last field was removed from the stack of form fields.
|
143
|
+
// The validation should be against less or equal than 1 because is against a previous state check.
|
144
|
+
if (credentialRequestField.fieldArray.fields.length <= 1) {
|
145
|
+
credentialRequestField.onAllFieldsDelete();
|
146
|
+
return;
|
147
|
+
}
|
148
|
+
|
149
|
+
credentialRequestField.fieldArray.remove(credentialRequestField.index);
|
150
|
+
};
|
151
|
+
|
152
|
+
const renderTitle = (): React.JSX.Element => {
|
153
|
+
const typographyStyle = {
|
154
|
+
fontStyle: fieldType ? 'normal' : 'italic',
|
155
|
+
fontSize: '16px',
|
156
|
+
fontWeight: '800',
|
157
|
+
textAlign: 'left !important',
|
158
|
+
alignSelf: 'flex-start',
|
159
|
+
};
|
160
|
+
|
161
|
+
return (
|
162
|
+
<Typography variant='body1' sx={typographyStyle}>
|
163
|
+
{credentialRequest.mandatory !== MandatoryEnum.NO ? (
|
164
|
+
<RequiredLabel>{type}</RequiredLabel>
|
165
|
+
) : (
|
166
|
+
type
|
167
|
+
)}
|
168
|
+
</Typography>
|
169
|
+
);
|
170
|
+
};
|
171
|
+
|
172
|
+
const renderUserInput = (): React.JSX.Element => {
|
173
|
+
const allowUserInput = credentialRequest.allowUserInput;
|
174
|
+
|
175
|
+
return (
|
176
|
+
<Stack direction='row' alignItems='center' spacing={0.5} pl={5.25}>
|
177
|
+
{allowUserInput ? (
|
178
|
+
<CheckCircle
|
179
|
+
sx={{ fontSize: '12px', color: theme.palette.text.disabled }}
|
180
|
+
/>
|
181
|
+
) : (
|
182
|
+
<Close
|
183
|
+
sx={{ fontSize: '12px', color: theme.palette.text.disabled }}
|
184
|
+
/>
|
185
|
+
)}
|
186
|
+
<Typography
|
187
|
+
variant='body1'
|
188
|
+
color='text.disabled'
|
189
|
+
sx={{
|
190
|
+
fontSize: '12px',
|
191
|
+
fontWeight: '400',
|
192
|
+
alignSelf: 'flex-start',
|
193
|
+
textAlign: 'left!important',
|
194
|
+
}}
|
195
|
+
>
|
196
|
+
Allow User Input
|
197
|
+
</Typography>
|
198
|
+
</Stack>
|
199
|
+
);
|
200
|
+
};
|
201
|
+
|
202
|
+
useEffect(() => {
|
203
|
+
if (!isNew) return;
|
204
|
+
accordionRef.current?.scrollIntoView({ behavior: 'smooth' });
|
205
|
+
}, [isNew]);
|
206
|
+
|
207
|
+
return (
|
208
|
+
<Stack
|
209
|
+
ref={drop}
|
210
|
+
sx={{ position: 'relative', width: '100%', opacity: dropOpacity }}
|
211
|
+
>
|
212
|
+
<Paper
|
213
|
+
ref={(element) => preview(element)}
|
214
|
+
sx={{
|
215
|
+
p: '0!important',
|
216
|
+
width: `calc(100% - ${
|
217
|
+
(credentialRequestField?.level ?? 0) * 30
|
218
|
+
}px)!important`,
|
219
|
+
alignSelf: 'flex-end',
|
220
|
+
opacity,
|
221
|
+
}}
|
222
|
+
>
|
223
|
+
<Box>
|
224
|
+
<Accordion
|
225
|
+
defaultExpanded={isNew}
|
226
|
+
expanded={expanded}
|
227
|
+
sx={{
|
228
|
+
boxShadow: 'none',
|
229
|
+
'&::before': {
|
230
|
+
display: 'none',
|
231
|
+
},
|
232
|
+
my: '0px !important',
|
233
|
+
mt: 0,
|
234
|
+
p: '8px !important',
|
235
|
+
}}
|
236
|
+
data-testid='custom-demo-dialog-data-field-accordion'
|
237
|
+
>
|
238
|
+
<AccordionSummary
|
239
|
+
onClick={() => {
|
240
|
+
setOpen((prev) => !prev);
|
241
|
+
}}
|
242
|
+
expandIcon={
|
243
|
+
<>
|
244
|
+
<IconButton
|
245
|
+
size='small'
|
246
|
+
onClick={(e) => {
|
247
|
+
e.stopPropagation();
|
248
|
+
setModalOpen(true);
|
249
|
+
}}
|
250
|
+
data-testid='custom-demo-dialog-data-field-delete-button'
|
251
|
+
>
|
252
|
+
<Delete
|
253
|
+
fontSize='small'
|
254
|
+
sx={{
|
255
|
+
transform: 'rotate(0deg)',
|
256
|
+
}}
|
257
|
+
/>
|
258
|
+
</IconButton>
|
259
|
+
<Stack
|
260
|
+
className={chevronClassName}
|
261
|
+
sx={{ ml: 1, alignSelf: 'center' }}
|
262
|
+
>
|
263
|
+
<ChevronLeft
|
264
|
+
fontSize='small'
|
265
|
+
sx={{
|
266
|
+
color: '#0dbc3d',
|
267
|
+
transform: 'rotate(0deg)',
|
268
|
+
}}
|
269
|
+
/>
|
270
|
+
</Stack>
|
271
|
+
</>
|
272
|
+
}
|
273
|
+
sx={{
|
274
|
+
px: 0,
|
275
|
+
minHeight: 'auto!important',
|
276
|
+
'& .MuiAccordionSummary-content': {
|
277
|
+
my: '0px !important',
|
278
|
+
},
|
279
|
+
'& .MuiAccordionSummary-expandIconWrapper': {
|
280
|
+
alignSelf: 'flex-start',
|
281
|
+
transform: 'rotate(0deg) !important',
|
282
|
+
[`& .${chevronClassName}`]: {
|
283
|
+
transition: 'transform .3s',
|
284
|
+
},
|
285
|
+
'&.Mui-expanded': {
|
286
|
+
[`& .${chevronClassName}`]: {
|
287
|
+
transform: 'rotate(-90deg)',
|
288
|
+
},
|
289
|
+
},
|
290
|
+
},
|
291
|
+
}}
|
292
|
+
>
|
293
|
+
<Stack sx={{ alignItems: 'flex-start', mr: 0.5 }}>
|
294
|
+
<Stack direction='column' alignItems='flex-start' spacing={0}>
|
295
|
+
<Stack direction='row' alignItems='center' spacing={1}>
|
296
|
+
<IconButton
|
297
|
+
ref={drag}
|
298
|
+
size='small'
|
299
|
+
color='success'
|
300
|
+
onClick={(e) => {
|
301
|
+
e.preventDefault();
|
302
|
+
e.stopPropagation();
|
303
|
+
}}
|
304
|
+
sx={{ cursor: 'grab' }}
|
305
|
+
>
|
306
|
+
<Menu />
|
307
|
+
</IconButton>
|
308
|
+
{renderTitle()}
|
309
|
+
</Stack>
|
310
|
+
{renderUserInput()}
|
311
|
+
</Stack>
|
312
|
+
</Stack>
|
313
|
+
</AccordionSummary>
|
314
|
+
<AccordionDetails sx={{ pt: 3 }}>
|
315
|
+
{expanded && (
|
316
|
+
<Stack spacing={2}>
|
317
|
+
<DataFieldOptionType />
|
318
|
+
<DataFieldDescription />
|
319
|
+
<DataFieldMandatory />
|
320
|
+
<DataFieldUserInput />
|
321
|
+
<DataFieldMulti />
|
322
|
+
</Stack>
|
323
|
+
)}
|
324
|
+
</AccordionDetails>
|
325
|
+
</Accordion>
|
326
|
+
</Box>
|
327
|
+
</Paper>
|
328
|
+
<DataFieldDeleteModal
|
329
|
+
open={modalOpen}
|
330
|
+
onClose={() => {
|
331
|
+
setModalOpen(false);
|
332
|
+
}}
|
333
|
+
onConfirm={handleRemove}
|
334
|
+
/>
|
335
|
+
</Stack>
|
336
|
+
);
|
337
|
+
}
|