@evoke-platform/ui-components 1.6.0-testing.8 → 1.6.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.
Files changed (70) hide show
  1. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +1 -1
  2. package/dist/published/components/custom/Form/FormComponents/ImageComponent/Image.js +2 -2
  3. package/dist/published/components/custom/FormField/AddressFieldComponent/addressFieldComponent.js +1 -1
  4. package/dist/published/components/custom/FormField/BooleanSelect/BooleanSelect.js +3 -3
  5. package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +7 -1
  6. package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +7 -1
  7. package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.js +6 -3
  8. package/dist/published/components/custom/FormField/TimePickerSelect/TimePickerSelect.js +13 -3
  9. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +19 -0
  10. package/dist/published/components/custom/FormV2/FormRenderer.js +183 -0
  11. package/dist/published/components/custom/FormV2/components/AccordionSections.d.ts +4 -0
  12. package/dist/published/components/custom/FormV2/components/AccordionSections.js +131 -0
  13. package/dist/published/components/custom/FormV2/components/ActionButtons.d.ts +19 -0
  14. package/dist/published/components/custom/FormV2/components/ActionButtons.js +106 -0
  15. package/dist/published/components/custom/FormV2/components/FieldWrapper.d.ts +24 -0
  16. package/dist/published/components/custom/FormV2/components/FieldWrapper.js +100 -0
  17. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +12 -0
  18. package/dist/published/components/custom/FormV2/components/FormContext.js +8 -0
  19. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.d.ts +17 -0
  20. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +50 -0
  21. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +14 -0
  22. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +88 -0
  23. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DocumentViewerCell.d.ts +13 -0
  24. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DocumentViewerCell.js +140 -0
  25. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.d.ts +17 -0
  26. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +233 -0
  27. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +40 -0
  28. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +95 -0
  29. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.d.ts +12 -0
  30. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +526 -0
  31. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.d.ts +12 -0
  32. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +93 -0
  33. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +16 -0
  34. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +73 -0
  35. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +13 -0
  36. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +179 -0
  37. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.d.ts +12 -0
  38. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +108 -0
  39. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.d.ts +16 -0
  40. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +129 -0
  41. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.d.ts +15 -0
  42. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +226 -0
  43. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.d.ts +4 -0
  44. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +439 -0
  45. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +29 -0
  46. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +74 -0
  47. package/dist/published/components/custom/FormV2/components/FormSections.d.ts +4 -0
  48. package/dist/published/components/custom/FormV2/components/FormSections.js +104 -0
  49. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +2 -0
  50. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +209 -0
  51. package/dist/published/components/custom/FormV2/components/TabNav.d.ts +10 -0
  52. package/dist/published/components/custom/FormV2/components/TabNav.js +23 -0
  53. package/dist/published/components/custom/FormV2/components/ValidationFiles/Validation.d.ts +3 -0
  54. package/dist/published/components/custom/FormV2/components/ValidationFiles/Validation.js +176 -0
  55. package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrorDisplay.d.ts +10 -0
  56. package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrorDisplay.js +45 -0
  57. package/dist/published/components/custom/FormV2/components/types.d.ts +122 -0
  58. package/dist/published/components/custom/FormV2/components/types.js +1 -0
  59. package/dist/published/components/custom/FormV2/components/utils.d.ts +47 -0
  60. package/dist/published/components/custom/FormV2/components/utils.js +434 -0
  61. package/dist/published/components/custom/FormV2/index.d.ts +1 -0
  62. package/dist/published/components/custom/FormV2/index.js +1 -0
  63. package/dist/published/components/custom/ResponsiveOverflow/ResponsiveOverflow.js +5 -8
  64. package/dist/published/components/custom/index.d.ts +1 -0
  65. package/dist/published/components/custom/index.js +1 -0
  66. package/dist/published/index.d.ts +2 -2
  67. package/dist/published/index.js +2 -2
  68. package/dist/published/theme/hooks.d.ts +7 -0
  69. package/dist/published/theme/hooks.js +9 -0
  70. package/package.json +4 -2
@@ -186,7 +186,7 @@ const customSelector = (props) => {
186
186
  return opts.find((o) => option === o.name)?.label || option;
187
187
  }
188
188
  return option.label;
189
- }, isOptionEqualToValue: (option, value) => {
189
+ }, getOptionKey: (option) => option.name, isOptionEqualToValue: (option, value) => {
190
190
  if (typeof option === 'string') {
191
191
  return option === value;
192
192
  }
@@ -1,6 +1,6 @@
1
- import { BackupOutlined, ClearRounded } from '@mui/icons-material';
2
1
  import React, { useEffect, useState } from 'react';
3
2
  import { useDropzone } from 'react-dropzone';
3
+ import { ClearRounded, UploadCloud } from '../../../../../icons';
4
4
  import { CardMedia, IconButton, Typography } from '../../../../core';
5
5
  import { Box, Grid } from '../../../../layout';
6
6
  export function blobToDataUrl(blob) {
@@ -84,7 +84,7 @@ export const Image = (props) => {
84
84
  React.createElement("input", { ...getInputProps({ id }), multiple: false }),
85
85
  React.createElement(Grid, { container: true, sx: { width: '100%' } },
86
86
  React.createElement(Grid, { item: true, xs: 12, sx: { display: 'flex', justifyContent: 'center', paddingBottom: '5px' } },
87
- React.createElement(BackupOutlined, { sx: { color: '#919EAB', height: '1.5em', width: '1.5em' } })),
87
+ React.createElement(UploadCloud, { sx: { color: '#919EAB', width: '50px', height: '30px' } })),
88
88
  React.createElement(Grid, { item: true, xs: 12 },
89
89
  React.createElement(Typography, { variant: "body2", sx: { color: '#212B36', textAlign: 'center' } },
90
90
  "Drag and drop or",
@@ -45,7 +45,7 @@ const AddressFieldComponent = (props) => {
45
45
  setAnchorEl(null);
46
46
  };
47
47
  return (React.createElement(Box, null,
48
- !mask ? (React.createElement(TextField, { id: id, inputRef: textFieldRef, onChange: !readOnly ? handleChange : undefined, error: error, errorMessage: errorMessage, value: value, fullWidth: true, onBlur: onBlur, size: size ?? 'medium', placeholder: placeholder, InputProps: {
48
+ !mask ? (React.createElement(TextField, { id: id, inputRef: textFieldRef, onChange: !readOnly ? handleChange : undefined, error: error, errorMessage: errorMessage, value: value, fullWidth: true, onBlur: onBlur, size: size ?? 'medium', placeholder: readOnly ? undefined : placeholder, InputProps: {
49
49
  type: 'search',
50
50
  autoComplete: 'off',
51
51
  }, required: required, readOnly: readOnly, multiline: property.type === 'string' && !readOnly && isMultiLineText, rows: isMultiLineText ? (rows ? rows : 3) : undefined, ...(additionalProps ?? {}) })) : (React.createElement(InputMask, { mask: mask, maskChar: inputMaskPlaceholderChar ?? '_', value: value, onChange: !readOnly ? handleChange : undefined, onBlur: onBlur, alwaysShowMask: true }, (() => (React.createElement(TextField, { id: id, inputRef: textFieldRef, sx: readOnly
@@ -56,12 +56,12 @@ const BooleanSelect = (props) => {
56
56
  return readOnlyComponent();
57
57
  }
58
58
  return displayOption === 'dropdown' ? (React.createElement(Autocomplete, { renderInput: (params) => (React.createElement(TextField, { ...params, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, sx: { background: 'white' }, placeholder: placeholder, size: size ?? 'medium' })), value: booleanOptions.find((opt) => opt.value === value) ?? '', onChange: (e, selectedValue) => handleChange(selectedValue.value), isOptionEqualToValue: (option, val) => option?.value === val?.value, options: booleanOptions, disableClearable: true, sx: { background: 'white', borderRadius: '8px' }, ...(additionalProps ?? {}), sortBy: "NONE", required: strictlyTrue })) : (React.createElement(FormControl, { required: strictlyTrue, error: error, fullWidth: true },
59
- React.createElement(FormControlLabel, { labelPlacement: "end", label: labelComponent(), control: displayOption === 'switch' ? (React.createElement(Switch, { id: id, "aria-required": strictlyTrue, "aria-invalid": error, size: size ?? 'medium', name: property.id, checked: value, onChange: (e) => handleChange(e.target.checked), sx: {
59
+ React.createElement(FormControlLabel, { labelPlacement: "end", label: labelComponent(), sx: { marginLeft: '-8px' }, control: displayOption === 'switch' ? (React.createElement(Switch, { id: id, "aria-required": strictlyTrue, "aria-invalid": error, size: size ?? 'medium', name: property.id, checked: value, onChange: (e) => handleChange(e.target.checked), sx: {
60
60
  alignSelf: 'start',
61
- } })) : (React.createElement(Checkbox, { id: id, "aria-required": strictlyTrue, "aria-invalid": error, size: size ?? 'medium', checked: value, name: property.id, onChange: (e) => handleChange(e.target.checked), sx: {
61
+ }, ...(additionalProps ?? {}) })) : (React.createElement(Checkbox, { id: id, "aria-required": strictlyTrue, "aria-invalid": error, size: size ?? 'medium', checked: value, name: property.id, onChange: (e) => handleChange(e.target.checked), sx: {
62
62
  alignSelf: 'start',
63
63
  padding: '4px 9px 9px 9px',
64
- } })) }),
64
+ }, ...(additionalProps ?? {}) })) }),
65
65
  error && errorMessage?.length && React.createElement(FormHelperText, { sx: { marginX: 0 } }, errorMessage),
66
66
  descriptionComponent()));
67
67
  };
@@ -1,4 +1,5 @@
1
1
  import { DateTimeFormatter } from '@js-joda/core';
2
+ import { omit } from 'lodash';
2
3
  import React, { useEffect, useState } from 'react';
3
4
  import { InvalidDate, LocalDate, nativeJs } from '../../../../util';
4
5
  import { DatePicker, LocalizationProvider, TextField } from '../../../core';
@@ -36,6 +37,11 @@ const DatePickerSelect = (props) => {
36
37
  onChange && onChange(property.id, date, property);
37
38
  };
38
39
  return readOnly ? (React.createElement(InputFieldComponent, { ...{ ...props, defaultValue: asMonthDayYearFormat(value) } })) : (React.createElement(LocalizationProvider, null,
39
- React.createElement(DatePicker, { value: value, onChange: handleChange, inputFormat: "MM/dd/yyyy", renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium', ...(additionalProps ?? {}) })) })));
40
+ React.createElement(DatePicker, { value: value, onChange: handleChange, inputFormat: "MM/dd/yyyy", renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium',
41
+ // merges MUI inputProps with additionalProps.inputProps in a way that still shows the value
42
+ inputProps: {
43
+ ...params.inputProps,
44
+ ...(additionalProps?.inputProps ?? {}),
45
+ }, ...omit(additionalProps, ['inputProps']) })) })));
40
46
  };
41
47
  export default DatePickerSelect;
@@ -1,4 +1,5 @@
1
1
  import { LocalDate, LocalDateTime, LocalTime, nativeJs } from '@js-joda/core';
2
+ import { omit } from 'lodash';
2
3
  import React, { useEffect, useState } from 'react';
3
4
  import { InvalidDate } from '../../../../util';
4
5
  import { DateTimePicker, LocalizationProvider, TextField } from '../../../core';
@@ -43,6 +44,11 @@ const DateTimePickerSelect = (props) => {
43
44
  props.onChange && props.onChange(property.id, date, property);
44
45
  };
45
46
  return readOnly ? (React.createElement(InputFieldComponent, { ...{ ...props, defaultValue: formatDateTime(value) } })) : (React.createElement(LocalizationProvider, null,
46
- React.createElement(DateTimePicker, { value: value, onChange: handleChange, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium', ...(additionalProps ?? {}) })) })));
47
+ React.createElement(DateTimePicker, { value: value, onChange: handleChange, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium',
48
+ // merges MUI inputProps with additionalProps.inputProps in a way that still shows the value
49
+ inputProps: {
50
+ ...params.inputProps,
51
+ ...(additionalProps?.inputProps ?? {}),
52
+ }, ...omit(additionalProps, ['inputProps']) })) })));
47
53
  };
48
54
  export default DateTimePickerSelect;
@@ -36,9 +36,12 @@ const InputFieldComponent = (props) => {
36
36
  setInputValue(selectValue);
37
37
  };
38
38
  const InputProps = property.type === 'number'
39
- ? { inputComponent: NumericFormat, inputProps: { min, max, readOnly } }
39
+ ? {
40
+ inputComponent: NumericFormat,
41
+ inputProps: { min, max, readOnly, ...(additionalProps?.inputProps ?? {}) },
42
+ }
40
43
  : property.type === 'integer'
41
- ? { inputProps: { min, max } }
44
+ ? { inputProps: { min, max, ...(additionalProps?.inputProps ?? {}) } }
42
45
  : null;
43
46
  return property.enum && !readOnly ? (React.createElement(Autocomplete, { id: id,
44
47
  // note: this is different between widgets and builder
@@ -62,7 +65,7 @@ const InputFieldComponent = (props) => {
62
65
  backgroundColor: '#f4f6f8',
63
66
  },
64
67
  }),
65
- }, error: error, errorMessage: errorMessage, value: value, onChange: !readOnly ? handleChange : undefined, InputProps: { ...InputProps, readOnly: readOnly }, required: required, fullWidth: true, onBlur: onBlur, placeholder: placeholder, size: size ?? 'medium', type: property.type === 'integer' ? 'number' : 'text', multiline: property.type === 'string' && !readOnly && isMultiLineText, rows: isMultiLineText ? (rows ? rows : 3) : undefined, ...(additionalProps ?? {}) })) : (React.createElement(InputMask, { mask: mask, maskChar: inputMaskPlaceholderChar ?? '_', value: value, onChange: !readOnly ? handleChange : undefined, onBlur: onBlur, alwaysShowMask: true }, (() => (React.createElement(TextField, { id: id, sx: readOnly
68
+ }, error: error, errorMessage: errorMessage, value: value, onChange: !readOnly ? handleChange : undefined, InputProps: { ...InputProps, readOnly: readOnly }, required: required, fullWidth: true, onBlur: onBlur, placeholder: readOnly ? undefined : placeholder, size: size ?? 'medium', type: property.type === 'integer' ? 'number' : 'text', multiline: property.type === 'string' && !readOnly && isMultiLineText, rows: isMultiLineText ? (rows ? rows : 3) : undefined, ...(additionalProps ?? {}) })) : (React.createElement(InputMask, { mask: mask, maskChar: inputMaskPlaceholderChar ?? '_', value: value, onChange: !readOnly ? handleChange : undefined, onBlur: onBlur, alwaysShowMask: true }, (() => (React.createElement(TextField, { id: id, sx: readOnly
66
69
  ? {
67
70
  '& .MuiOutlinedInput-notchedOutline': {
68
71
  border: 'none',
@@ -1,9 +1,11 @@
1
1
  import { LocalDateTime } from '@js-joda/core';
2
2
  import { TimePicker } from '@mui/x-date-pickers';
3
- import { isUndefined, padStart } from 'lodash';
3
+ import { isUndefined, omit, padStart } from 'lodash';
4
+ import { DateTime } from 'luxon';
4
5
  import React, { useEffect, useState } from 'react';
5
6
  import { InvalidDate } from '../../../../util';
6
7
  import { LocalizationProvider, TextField } from '../../../core';
8
+ import InputFieldComponent from '../InputFieldComponent/InputFieldComponent';
7
9
  const TimePickerSelect = (props) => {
8
10
  const { id, property, defaultValue, error, errorMessage, readOnly, required, size, onBlur, placeholder, additionalProps, } = props;
9
11
  const values = defaultValue ? defaultValue.split(':') : undefined;
@@ -39,7 +41,15 @@ const TimePickerSelect = (props) => {
39
41
  props.onChange && props.onChange(property.id, date, property);
40
42
  }
41
43
  };
42
- return (React.createElement(LocalizationProvider, null,
43
- React.createElement(TimePicker, { value: value, onChange: handleChange, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium', placeholder: placeholder, readOnly: readOnly, ...(additionalProps ?? {}) })), readOnly: readOnly })));
44
+ return readOnly ? (React.createElement(InputFieldComponent, { ...{
45
+ ...props,
46
+ defaultValue: value instanceof LocalDateTime ? DateTime.fromISO(value.toString()).toFormat('hh:mm a') : '',
47
+ } })) : (React.createElement(LocalizationProvider, null,
48
+ React.createElement(TimePicker, { value: value, onChange: handleChange, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium', placeholder: placeholder,
49
+ // merges MUI inputProps with additionalProps.inputProps in a way that still shows the value
50
+ inputProps: {
51
+ ...params.inputProps,
52
+ ...(additionalProps?.inputProps ?? {}),
53
+ }, ...omit(additionalProps, ['inputProps']) })), readOnly: readOnly })));
44
54
  };
45
55
  export default TimePickerSelect;
@@ -0,0 +1,19 @@
1
+ import { EvokeForm, ObjectInstance } from '@evoke-platform/context';
2
+ import React, { ComponentType } from 'react';
3
+ import { FieldErrors, FieldValues } from 'react-hook-form';
4
+ import { BaseProps, Document, SimpleEditorProps } from './components/types';
5
+ export type FormProps = BaseProps & {
6
+ richTextEditor?: ComponentType<SimpleEditorProps>;
7
+ hideButtons?: boolean;
8
+ value?: FieldValues;
9
+ onSubmit?: (data: FieldValues) => void;
10
+ fieldHeight?: 'small' | 'medium';
11
+ stickyFooter?: boolean;
12
+ onCancel?: () => void;
13
+ form: EvokeForm;
14
+ instance?: ObjectInstance | Document;
15
+ onChange: (id: string, value: unknown) => void;
16
+ onValidationChange?: (errors: FieldErrors) => void;
17
+ };
18
+ declare function FormRenderer(props: FormProps): React.JSX.Element;
19
+ export default FormRenderer;
@@ -0,0 +1,183 @@
1
+ import { useObject } from '@evoke-platform/context';
2
+ import { isEqual } from 'lodash';
3
+ import React, { useEffect, useMemo, useState } from 'react';
4
+ import { useForm } from 'react-hook-form';
5
+ import { useResponsive } from '../../../theme';
6
+ import { Button, Skeleton, Typography } from '../../core';
7
+ import { Box } from '../../layout';
8
+ import ActionButtons from './components/ActionButtons';
9
+ import { FormContext } from './components/FormContext';
10
+ import { RecursiveEntryRenderer } from './components/RecursiveEntryRenderer';
11
+ import { convertDocToParameters, convertPropertiesToParams } from './components/utils';
12
+ import { handleValidation } from './components/ValidationFiles/Validation';
13
+ import ValidationErrorDisplay from './components/ValidationFiles/ValidationErrorDisplay';
14
+ function FormRenderer(props) {
15
+ const { onSubmit, value, fieldHeight, richTextEditor, hideButtons, stickyFooter, onCancel, form, instance, onChange, onValidationChange, } = props;
16
+ const { entries, name: title, objectId, actionId, display } = form;
17
+ const { register, unregister, setValue, reset, handleSubmit, formState: { errors, isSubmitted }, getValues, } = useForm({
18
+ defaultValues: value,
19
+ });
20
+ const hasSections = entries.some((entry) => entry.type === 'sections');
21
+ const isModal = !!document.querySelector('.MuiDialog-container');
22
+ const { isSm, isXs, smallerThan } = useResponsive();
23
+ const isSmallerThanMd = smallerThan('md');
24
+ const objectStore = useObject(objectId);
25
+ const [expandedSections, setExpandedSections] = useState([]);
26
+ const [fetchedOptions, setFetchedOptions] = useState({});
27
+ const [expandAll, setExpandAll] = useState();
28
+ const [action, setAction] = useState();
29
+ const [object, setObject] = useState();
30
+ const [triggerFieldReset, setTriggerFieldReset] = useState(false);
31
+ const updateFetchedOptions = (newData) => {
32
+ setFetchedOptions((prev) => ({
33
+ ...prev,
34
+ ...newData,
35
+ }));
36
+ };
37
+ function handleExpandAll() {
38
+ setExpandAll(true);
39
+ }
40
+ function handleCollapseAll() {
41
+ setExpandAll(false);
42
+ }
43
+ useEffect(() => {
44
+ (async () => {
45
+ try {
46
+ const object = await objectStore.get({ sanitized: true });
47
+ setObject(object);
48
+ if (actionId) {
49
+ const action = object?.actions?.find((a) => a.id === actionId);
50
+ setAction(action);
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.error('Failed to fetch object or action:', error);
55
+ }
56
+ })();
57
+ }, [objectStore, actionId]);
58
+ useEffect(() => {
59
+ const currentValues = getValues();
60
+ if (value) {
61
+ for (const key of Object.keys(currentValues)) {
62
+ if (!isEqual(currentValues[key], value[key])) {
63
+ setValue(key, value[key], { shouldValidate: true });
64
+ }
65
+ }
66
+ if (triggerFieldReset === true) {
67
+ setTriggerFieldReset(false);
68
+ }
69
+ }
70
+ }, [value]);
71
+ useEffect(() => {
72
+ if (onValidationChange) {
73
+ onValidationChange(errors);
74
+ }
75
+ }, [errors, onValidationChange]);
76
+ const handleReset = () => {
77
+ if (onCancel) {
78
+ onCancel();
79
+ }
80
+ else {
81
+ reset(instance); // clears react-hook-form state back to default values
82
+ }
83
+ setTriggerFieldReset(true);
84
+ };
85
+ const parameters = useMemo(() => {
86
+ if (form.id === 'documentForm') {
87
+ return convertDocToParameters(instance);
88
+ }
89
+ else if (action?.parameters) {
90
+ return action.parameters;
91
+ }
92
+ else if (object) {
93
+ // if forms actionId is synced with object properties
94
+ return convertPropertiesToParams(object);
95
+ }
96
+ }, [form.id, action?.parameters, object, instance]);
97
+ useEffect(() => {
98
+ handleValidation(entries, register, getValues(), action?.parameters, instance);
99
+ }, []);
100
+ if (entries && parameters && (!actionId || action)) {
101
+ return (React.createElement(React.Fragment, null,
102
+ React.createElement(Box, { sx: {
103
+ paddingX: isSmallerThanMd ? 2 : 3,
104
+ paddingTop: '0px',
105
+ borderBottom: '2px solid #F4F6F8',
106
+ } },
107
+ React.createElement(Box, { sx: {
108
+ display: 'flex',
109
+ justifyContent: 'space-between',
110
+ alignItems: 'center',
111
+ flexWrap: 'wrap',
112
+ paddingY: isSm || isXs ? 2 : 3,
113
+ } },
114
+ React.createElement(Typography, { sx: {
115
+ fontSize: '20px',
116
+ lineHeight: '30px',
117
+ fontWeight: 700,
118
+ flexGrow: '1',
119
+ } }, title),
120
+ isSmallerThanMd && hasSections && (React.createElement(Box, { sx: {
121
+ display: 'flex',
122
+ alignItems: 'center',
123
+ maxHeight: '22px',
124
+ } },
125
+ React.createElement(Button, { variant: "text", size: "small", disableRipple: true, disabled: expandedSections.every((section) => section.expanded === true), sx: {
126
+ color: '#212B36',
127
+ borderRight: '1px solid #e5e8eb',
128
+ borderRadius: '0px',
129
+ '&:hover': {
130
+ backgroundColor: 'transparent',
131
+ },
132
+ fontWeight: 400,
133
+ fontSize: '14px',
134
+ }, onClick: handleExpandAll }, "Expand all"),
135
+ React.createElement(Button, { variant: "text", size: "small", disableRipple: true, disabled: expandedSections.every((section) => section.expanded === false), sx: {
136
+ color: '#212B36',
137
+ '&:hover': {
138
+ backgroundColor: 'transparent',
139
+ },
140
+ fontWeight: 400,
141
+ fontSize: '14px',
142
+ }, onClick: handleCollapseAll }, "Collapse all")))),
143
+ React.createElement(ValidationErrorDisplay, { errors: errors, show: !!(isSubmitted && errors), formId: form.id, title: title })),
144
+ React.createElement(FormContext.Provider, { value: {
145
+ fetchedOptions,
146
+ setFetchedOptions: updateFetchedOptions,
147
+ getValues,
148
+ stickyFooter,
149
+ object,
150
+ } },
151
+ React.createElement(Box, { sx: {
152
+ padding: isModal ? '0px' : isSm || isXs ? 2 : 3,
153
+ paddingBottom: '0px',
154
+ paddingTop: !hasSections ? undefined : '0px',
155
+ } },
156
+ entries.map((entry, index) => (React.createElement(RecursiveEntryRenderer, { fieldHeight: fieldHeight, key: index, entry: entry, handleChange: onChange, errors: errors, showSubmitError: !!(isSubmitted && errors), instance: instance, richTextEditor: richTextEditor, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, triggerFieldReset: triggerFieldReset, parameters: parameters }))),
157
+ !hideButtons && (actionId || form.id === 'documentForm') && onSubmit && (React.createElement(Box, { sx: {
158
+ ...(stickyFooter === false ? { position: 'static' } : { position: 'sticky' }),
159
+ bottom: isModal ? -5 : isSmallerThanMd ? 0 : 24,
160
+ zIndex: 1000,
161
+ borderTop: action?.type !== 'delete' ? '1px solid #f4f6f8' : 'none',
162
+ backgroundColor: '#fff',
163
+ paddingY: isSmallerThanMd ? '16px' : '20px',
164
+ paddingX: isSmallerThanMd ? '16px' : '20px',
165
+ display: 'flex',
166
+ justifyContent: isXs ? 'center' : 'flex-end',
167
+ alignItems: 'center',
168
+ marginX: isSmallerThanMd ? -2 : -3,
169
+ marginBottom: '1px',
170
+ borderRadius: '0px 0px 6px 6px',
171
+ } },
172
+ React.createElement(ActionButtons, { onSubmit: onSubmit, handleSubmit: handleSubmit, isModal: isModal, actionType: action?.type, submitButtonLabel: display?.submitLabel, onReset: handleReset, errors: errors, unregister: unregister, entries: entries, setValue: setValue, formId: form.id, instance: instance })))))));
173
+ }
174
+ else {
175
+ return (React.createElement(Box, { p: 2 },
176
+ React.createElement(Skeleton, null),
177
+ React.createElement(Skeleton, null),
178
+ React.createElement(Skeleton, null),
179
+ React.createElement(Skeleton, null),
180
+ React.createElement(Skeleton, null)));
181
+ }
182
+ }
183
+ export default FormRenderer;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { SectionsProps } from './types';
3
+ declare function AccordionSections(props: SectionsProps): React.JSX.Element;
4
+ export default AccordionSections;
@@ -0,0 +1,131 @@
1
+ import { ExpandMoreOutlined } from '@mui/icons-material';
2
+ import { nanoid } from 'nanoid';
3
+ import React, { useEffect } from 'react';
4
+ import { useResponsive } from '../../../../theme';
5
+ import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../../core';
6
+ import { Box } from '../../../layout';
7
+ import { RecursiveEntryRenderer } from './RecursiveEntryRenderer';
8
+ import { getErrorCountForSection } from './utils';
9
+ function AccordionSections(props) {
10
+ const { entry, handleChange, fieldHeight, richTextEditor, instance, expandedSections, setExpandedSections, expandAll, setExpandAll, errors, parameters, triggerFieldReset, showSubmitError, } = props;
11
+ const { isMd, isLg, isXl } = useResponsive();
12
+ const lastSection = entry.sections.length - 1;
13
+ const sectionsWithIds = React.useMemo(() => assignIds(entry.sections), [entry]);
14
+ function collectNestedSections(entries = []) {
15
+ const nestedSections = [];
16
+ entries.forEach((entry) => {
17
+ if (entry.type === 'sections') {
18
+ nestedSections.push(entry);
19
+ }
20
+ else if (entry.type === 'columns') {
21
+ const columnEntry = entry;
22
+ columnEntry.columns.forEach((column) => {
23
+ const columnSections = collectNestedSections(column.entries);
24
+ nestedSections.push(...columnSections);
25
+ });
26
+ }
27
+ });
28
+ return nestedSections;
29
+ }
30
+ // need to add ids to section so expanded sections can differentiate between sections with the same label
31
+ function assignIds(sections) {
32
+ const result = [];
33
+ sections.forEach((section) => {
34
+ const newSection = {
35
+ ...section,
36
+ id: nanoid(),
37
+ };
38
+ result.push(newSection);
39
+ if (section.entries) {
40
+ const nestedSections = collectNestedSections(section.entries);
41
+ nestedSections.forEach((nestedSection) => {
42
+ nestedSection.sections = assignIds(nestedSection.sections);
43
+ });
44
+ }
45
+ });
46
+ return result;
47
+ }
48
+ function getExpandedSections(sections, expandAll) {
49
+ const expandedSections = [];
50
+ const processSections = (sectionList) => {
51
+ sectionList.forEach((section, index) => {
52
+ expandedSections.push({
53
+ label: section.label,
54
+ expanded: expandAll ?? (index === 0 || isMd || isLg || isXl),
55
+ id: section?.id,
56
+ });
57
+ if (section.entries) {
58
+ const nestedSections = collectNestedSections(section.entries).flatMap((s) => s.sections);
59
+ processSections(nestedSections);
60
+ }
61
+ });
62
+ };
63
+ processSections(sections);
64
+ return expandedSections;
65
+ }
66
+ useEffect(() => {
67
+ if (expandAll !== null) {
68
+ setExpandedSections(getExpandedSections(sectionsWithIds, expandAll));
69
+ }
70
+ }, [expandAll, sectionsWithIds]);
71
+ const handleAccordionChange = (id) => {
72
+ const updatedSections = expandedSections.map((section) => section.id === id ? { ...section, expanded: !section.expanded } : section);
73
+ setExpandedSections(updatedSections);
74
+ setExpandAll(null);
75
+ };
76
+ return (React.createElement(Box, null, sectionsWithIds.map((section, sectionIndex) => {
77
+ const errorCount = getErrorCountForSection(section, errors);
78
+ return (React.createElement(Accordion, { key: section.id, expanded: expandedSections.find((expandedSection) => expandedSection.id === section.id)?.expanded ??
79
+ !!expandAll, onChange: () => handleAccordionChange(section.id), defaultExpanded: sectionIndex === 0, sx: {
80
+ border: '1px solid #dbe0e4',
81
+ boxShadow: 'none',
82
+ marginBottom: '16px',
83
+ borderRadius: '6px',
84
+ '&:before': {
85
+ display: 'none',
86
+ },
87
+ } },
88
+ React.createElement(AccordionSummary, { sx: {
89
+ '&.Mui-expanded': {
90
+ borderBottom: '1px solid #dbe0e4',
91
+ minHeight: '44px',
92
+ borderBottomLeftRadius: '0px',
93
+ borderBottomRightRadius: '0px',
94
+ },
95
+ minHeight: '44px',
96
+ maxHeight: '44px',
97
+ backgroundColor: '#F4F6F8',
98
+ borderRadius: '5px',
99
+ // MUI accordion summaries have different border radius for the first and last item
100
+ borderTopLeftRadius: sectionIndex === 0 ? '3px' : undefined,
101
+ borderTopRightRadius: sectionIndex === 0 ? '3px' : undefined,
102
+ borderBottomRightRadius: sectionIndex === lastSection ? '3px' : undefined,
103
+ borderBottomLeftRadius: sectionIndex === lastSection ? '3px' : undefined,
104
+ }, expandIcon: React.createElement(ExpandMoreOutlined, { fontSize: "medium" }) },
105
+ React.createElement(Box, { sx: {
106
+ display: 'flex',
107
+ alignItems: 'center',
108
+ width: '100%',
109
+ justifyContent: 'space-between',
110
+ } },
111
+ React.createElement(Typography, { sx: {
112
+ fontWeight: '600',
113
+ } }, section.label),
114
+ errorCount > 0 && showSubmitError && (React.createElement(Box, { sx: {
115
+ ml: 1,
116
+ bgcolor: '#FF4842',
117
+ color: 'white',
118
+ borderRadius: '50%',
119
+ minWidth: '20px',
120
+ minHeight: '20px',
121
+ display: 'flex',
122
+ justifyContent: 'center',
123
+ alignItems: 'center',
124
+ fontSize: '12px',
125
+ margin: '0px',
126
+ marginRight: '16px',
127
+ } }, errorCount)))),
128
+ React.createElement(AccordionDetails, null, section.entries?.map((sectionEntry, index) => (React.createElement(RecursiveEntryRenderer, { key: sectionEntry.type + index, entry: sectionEntry, handleChange: handleChange, errors: errors, fieldHeight: fieldHeight, richTextEditor: richTextEditor, instance: instance, expandedSections: expandedSections, setExpandedSections: setExpandedSections, expandAll: expandAll, setExpandAll: setExpandAll, parameters: parameters, triggerFieldReset: triggerFieldReset, showSubmitError: showSubmitError }))))));
129
+ })));
130
+ }
131
+ export default AccordionSections;
@@ -0,0 +1,19 @@
1
+ import { ActionType, FormEntry } from '@evoke-platform/context';
2
+ import React from 'react';
3
+ import { FieldErrors, FieldValues, UseFormReturn, UseFormSetValue, UseFormUnregister } from 'react-hook-form';
4
+ type ActionButtonProps = {
5
+ onSubmit: (data: FieldValues) => void;
6
+ handleSubmit: UseFormReturn['handleSubmit'];
7
+ isModal: boolean;
8
+ submitButtonLabel?: string;
9
+ actionType?: ActionType;
10
+ onReset: () => void;
11
+ errors?: FieldErrors;
12
+ unregister: UseFormUnregister<FieldValues>;
13
+ entries: FormEntry[];
14
+ setValue: UseFormSetValue<FieldValues>;
15
+ formId?: string;
16
+ instance?: FieldValues;
17
+ };
18
+ declare function ActionButtons(props: ActionButtonProps): React.JSX.Element;
19
+ export default ActionButtons;
@@ -0,0 +1,106 @@
1
+ import { isEmpty, omit } from 'lodash';
2
+ import React, { useContext, useState } from 'react';
3
+ import { useResponsive } from '../../../../theme';
4
+ import { Button, LoadingButton } from '../../../core';
5
+ import { Box } from '../../../layout';
6
+ import { FormContext } from './FormContext';
7
+ import { entryIsVisible, getEntryId, getNestedParameterIds, isAddressProperty, scrollIntoViewWithOffset, } from './utils';
8
+ function ActionButtons(props) {
9
+ const { onSubmit, submitButtonLabel, actionType, handleSubmit, onReset, unregister, errors, isModal, entries, setValue, formId, instance, } = props;
10
+ const { isXs } = useResponsive();
11
+ const [isSubmitLoading, setIsSubmitLoading] = useState(false);
12
+ const { getValues } = useContext(FormContext);
13
+ const unregisterHiddenFields = (entriesToCheck) => {
14
+ entriesToCheck.forEach((entry) => {
15
+ if (entry.type === 'sections' || entry.type === 'columns') {
16
+ const subEntries = entry.type === 'sections' ? entry.sections : entry.columns;
17
+ subEntries.forEach((subEntry) => {
18
+ if (subEntry.entries) {
19
+ unregisterHiddenFields(subEntry.entries);
20
+ }
21
+ });
22
+ }
23
+ if (!entryIsVisible(entry, getValues(), instance)) {
24
+ if (entry.type === 'sections' || entry.type === 'columns') {
25
+ const fieldsToUnregister = getNestedParameterIds(entry);
26
+ fieldsToUnregister.forEach(processFieldUnregister);
27
+ }
28
+ else {
29
+ const fieldId = getEntryId(entry);
30
+ if (fieldId)
31
+ processFieldUnregister(fieldId);
32
+ }
33
+ }
34
+ });
35
+ };
36
+ const processFieldUnregister = (fieldId) => {
37
+ if (isAddressProperty(fieldId)) {
38
+ // Unregister entire 'address' to clear hidden field errors, then restore existing values since unregistering address.line1 etc is not working
39
+ let addressValues = getValues('address');
40
+ addressValues = omit(addressValues, fieldId.split('.')[1]);
41
+ unregister('address');
42
+ setValue('address', addressValues);
43
+ }
44
+ else {
45
+ unregister(fieldId);
46
+ }
47
+ };
48
+ async function showErrorsOrSubmit() {
49
+ setIsSubmitLoading(true);
50
+ unregisterHiddenFields(entries ?? []);
51
+ try {
52
+ await handleSubmit((data) => onSubmit(actionType === 'delete' ? {} : data))();
53
+ }
54
+ finally {
55
+ setIsSubmitLoading(false);
56
+ }
57
+ if (!isEmpty(errors)) {
58
+ setTimeout(() => {
59
+ const errorElement = document.getElementById(`validation-error-display-${formId}`);
60
+ let modal = errorElement?.closest('.MuiPaper-root');
61
+ const hasCloseIcon = modal?.querySelector('svg[data-testid="CloseIcon"]');
62
+ modal = hasCloseIcon ? document.querySelector('.MuiDialogContent-root') : modal;
63
+ if (errorElement) {
64
+ scrollIntoViewWithOffset(errorElement, 170, modal);
65
+ }
66
+ }, 0);
67
+ }
68
+ }
69
+ return (React.createElement(Box, { sx: {
70
+ display: 'flex',
71
+ justifyContent: 'flex-end',
72
+ flexWrap: 'wrap-reverse',
73
+ width: '100%',
74
+ } },
75
+ React.createElement(Button, { key: "cancel", variant: "outlined", sx: {
76
+ margin: '5px',
77
+ marginX: isXs ? '0px' : undefined,
78
+ color: 'black',
79
+ border: '1px solid rgb(206, 212, 218)',
80
+ width: isXs ? '100%' : 'auto',
81
+ '&:hover': {
82
+ backgroundColor: '#f2f4f7',
83
+ border: '1px solid rgb(206, 212, 218)',
84
+ },
85
+ }, onClick: () => onReset() }, isModal ? 'Cancel' : 'Discard Changes'),
86
+ React.createElement(LoadingButton, { key: "submit", variant: "contained", sx: {
87
+ lineHeight: '2.75',
88
+ margin: '5px 0 5px 5px',
89
+ marginX: isXs ? '0px' : undefined,
90
+ padding: '0 23px',
91
+ backgroundColor: actionType === 'delete' ? '#A12723' : 'primary',
92
+ borderRadius: '8px',
93
+ boxShadow: 'none',
94
+ whiteSpace: 'nowrap',
95
+ width: isXs ? '100%' : 'auto',
96
+ '& .MuiCircularProgress-root': {
97
+ color: 'white',
98
+ },
99
+ '&:hover': {
100
+ backgroundColor: actionType === 'delete' ? ' #8C2421' : '#014E7B',
101
+ },
102
+ }, onClick: () => {
103
+ showErrorsOrSubmit();
104
+ }, loading: isSubmitLoading }, submitButtonLabel || 'Submit')));
105
+ }
106
+ export default ActionButtons;