@evoke-platform/ui-components 1.1.0-testing.1 → 1.1.0-testing.11

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 (40) hide show
  1. package/dist/published/components/core/DateTimePicker/DateTimePicker.js +7 -1
  2. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.d.ts +4 -5
  3. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +37 -29
  4. package/dist/published/components/custom/CriteriaBuilder/PropertyTree.js +4 -10
  5. package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +39 -14
  6. package/dist/published/components/custom/Form/Common/Form.d.ts +7 -1
  7. package/dist/published/components/custom/Form/Common/Form.js +112 -94
  8. package/dist/published/components/custom/Form/Common/FormComponentWrapper.d.ts +5 -0
  9. package/dist/published/components/custom/Form/Common/FormComponentWrapper.js +15 -4
  10. package/dist/published/components/custom/Form/FormComponents/ButtonComponent.d.ts +1 -2
  11. package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentComponent.d.ts +1 -2
  12. package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.d.ts +1 -1
  13. package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.js +1 -1
  14. package/dist/published/components/custom/Form/FormComponents/ImageComponent/ImageComponent.d.ts +0 -3
  15. package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectComponent.d.ts +0 -1
  16. package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectPropertyInput.js +7 -3
  17. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ManyToMany/DropdownRepeatableField.js +6 -2
  18. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ManyToMany/DropdownRepeatableFieldInput.js +1 -0
  19. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableFieldComponent.d.ts +0 -1
  20. package/dist/published/components/custom/Form/FormComponents/UserComponent/UserComponent.d.ts +0 -3
  21. package/dist/published/components/custom/Form/index.d.ts +2 -1
  22. package/dist/published/components/custom/Form/utils.js +21 -4
  23. package/dist/published/components/custom/FormField/FormField.d.ts +2 -0
  24. package/dist/published/components/custom/FormField/FormField.js +3 -1
  25. package/dist/published/components/custom/FormField/Select/Select.js +28 -4
  26. package/dist/published/components/custom/FormField/Select/Select.test.js +41 -0
  27. package/dist/published/components/custom/OverflowTextField/OverflowTextField.d.ts +4 -0
  28. package/dist/published/components/custom/OverflowTextField/OverflowTextField.js +13 -0
  29. package/dist/published/components/custom/OverflowTextField/index.d.ts +2 -0
  30. package/dist/published/components/custom/OverflowTextField/index.js +2 -0
  31. package/dist/published/components/custom/index.d.ts +3 -2
  32. package/dist/published/components/custom/index.js +2 -2
  33. package/dist/published/index.d.ts +6 -3
  34. package/dist/published/index.js +4 -2
  35. package/dist/published/stories/Form.stories.d.ts +6 -0
  36. package/dist/published/stories/Form.stories.js +82 -0
  37. package/dist/published/stories/FormField.stories.js +2 -0
  38. package/dist/published/stories/OverflowTextField.stories.d.ts +5 -0
  39. package/dist/published/stories/OverflowTextField.stories.js +28 -0
  40. package/package.json +2 -8
@@ -24,6 +24,12 @@ const DateTimePicker = (props) => {
24
24
  handleChange(newValue, keyboardInputValue);
25
25
  };
26
26
  return (React.createElement(UIThemeProvider, null,
27
- React.createElement(MUIDateTimePicker, { value: value, onChange: onChange, renderInput: (params) => React.createElement(TextField, { ...params }), ...rest })));
27
+ React.createElement(MUIDateTimePicker, { value: value, onChange: onChange, renderInput: (params) => React.createElement(TextField, { ...params }), PaperProps: {
28
+ sx: {
29
+ '&.MuiPickersPopper-paper': {
30
+ borderRadius: '12px',
31
+ },
32
+ },
33
+ }, ...rest })));
28
34
  };
29
35
  export default DateTimePicker;
@@ -1,4 +1,4 @@
1
- import { ElementType } from 'react';
1
+ /// <reference types="react" />
2
2
  import 'react-querybuilder/dist/query-builder.css';
3
3
  import { EvokeObject } from '../../../types';
4
4
  import { ObjectProperty, Operator, PresetValue, TreeViewObject } from './types';
@@ -10,10 +10,9 @@ export type CriteriaInputProps = {
10
10
  originalCriteria?: Record<string, unknown>;
11
11
  enablePresetValues?: boolean;
12
12
  presetValues?: PresetValue[];
13
- dynamicContentInput?: {
14
- component: ElementType;
15
- previousSteps: unknown[];
16
- trigger?: Record<string, unknown>;
13
+ customValueEditor?: {
14
+ component: (props: ValueEditorProps) => JSX.Element;
15
+ props?: Record<string, unknown>;
17
16
  };
18
17
  operators?: Operator[];
19
18
  disabledCriteria?: {
@@ -7,8 +7,9 @@ import { QueryBuilder, RuleGroupBodyComponents, RuleGroupHeaderComponents, TestI
7
7
  import 'react-querybuilder/dist/query-builder.css';
8
8
  import escape from 'string-escape-regex';
9
9
  import { TrashCan } from '../../../icons/custom';
10
- import { Autocomplete, Button, IconButton, TextField } from '../../core';
10
+ import { Autocomplete, Button, IconButton } from '../../core';
11
11
  import { Box } from '../../layout';
12
+ import { OverflowTextField } from '../OverflowTextField';
12
13
  import { difference } from '../util';
13
14
  import PropertyTree from './PropertyTree';
14
15
  import { parseMongoDB, traversePropertyPath } from './utils';
@@ -32,7 +33,6 @@ const styles = {
32
33
  buttons: {
33
34
  padding: '6px 16px',
34
35
  fontSize: '0.875rem',
35
- marginRight: '0px',
36
36
  boxShadow: 'none',
37
37
  },
38
38
  };
@@ -198,7 +198,7 @@ const customSelector = (props) => {
198
198
  };
199
199
  return (React.createElement(React.Fragment, null, isTreeViewEnabled ? (React.createElement(PropertyTree, { value: val ?? value, rootObject: object, fetchObject: fetchObject, handleTreePropertySelect: handleTreePropertySelect })) : (React.createElement(Autocomplete, { options: opts, value: val ?? null, getOptionLabel: (option) => {
200
200
  if (typeof option === 'string') {
201
- return opts.find((o) => option === o.name)?.label || '';
201
+ return opts.find((o) => option === o.name)?.label || option;
202
202
  }
203
203
  return option.label;
204
204
  }, isOptionEqualToValue: (option, value) => {
@@ -212,7 +212,10 @@ const customSelector = (props) => {
212
212
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
213
213
  onChange: (event, newValue) => {
214
214
  handleOnChange(newValue?.value.name);
215
- }, renderInput: (params) => React.createElement(TextField, { ...params, placeholder: placeholder, size: "small" }), sx: { width: width, background: '#fff' }, disableClearable: true, readOnly: readOnly }))));
215
+ }, renderInput: (params) => (React.createElement(OverflowTextField, { value: opts.find((o) => value === o.name)?.label || '', ...params, placeholder: placeholder, size: "small", inputProps: {
216
+ ...params.inputProps,
217
+ 'aria-label': placeholder,
218
+ } })), sx: { width: width, background: readOnly ? '#f4f6f8' : '#fff' }, disableClearable: true, readOnly: readOnly }))));
216
219
  };
217
220
  const customCombinator = (props) => {
218
221
  const { value, handleOnChange, context, level, path } = props;
@@ -277,7 +280,7 @@ export const valueEditor = (props) => {
277
280
  return ValueEditor(props);
278
281
  };
279
282
  const CriteriaBuilder = (props) => {
280
- const { properties, criteria, setCriteria, originalCriteria, enablePresetValues, presetValues, operators, dynamicContentInput, disabled, disabledCriteria, hideBorder, presetGroupLabel, treeViewOpts, disableRegexEscapeChars, } = props;
283
+ const { properties, criteria, setCriteria, originalCriteria, enablePresetValues, presetValues, operators, disabled, disabledCriteria, hideBorder, presetGroupLabel, customValueEditor, treeViewOpts, disableRegexEscapeChars, } = props;
281
284
  const [query, setQuery] = useState(undefined);
282
285
  const [propertyTreeMap, setPropertyTreeMap] = useState();
283
286
  useEffect(() => {
@@ -287,33 +290,41 @@ const CriteriaBuilder = (props) => {
287
290
  !isEmpty(treeViewOpts) && updatePropertyTreeMap(updatedQuery);
288
291
  setQuery({
289
292
  ...updatedQuery,
290
- rules: processRules(updatedQuery.rules),
293
+ rules: processRules(updatedQuery.rules, true),
291
294
  });
292
295
  }
293
296
  else {
294
297
  setQuery({ combinator: 'and', rules: [] });
295
298
  }
296
299
  }, [originalCriteria]);
297
- function processRules(rules) {
300
+ const processRules = (rules, isSavedValue) => {
298
301
  return rules.map((rule) => {
299
302
  if ('rules' in rule) {
300
303
  return {
301
304
  ...rule,
302
- rules: processRules(rule.rules),
305
+ rules: processRules(rule.rules, isSavedValue),
303
306
  };
304
307
  }
305
308
  else {
306
309
  const propertyType = properties.find((property) => property.id === rule.field)?.type;
310
+ let adjustedValue = rule.value;
311
+ if ((propertyType === 'array' ||
312
+ ((propertyType === 'string' || propertyType === 'richText') &&
313
+ (rule.operator === 'in' || rule.operator === 'notIn'))) &&
314
+ isSavedValue) {
315
+ adjustedValue = rule.value?.split(',');
316
+ }
317
+ else if ((rule.operator === 'null' || rule.operator === 'notNull') && rule.value) {
318
+ adjustedValue = null;
319
+ }
307
320
  return {
308
321
  ...rule,
309
- value: propertyType === 'array' ||
310
- (propertyType === 'string' && (rule.operator === 'in' || rule.operator === 'notIn'))
311
- ? rule.value?.split(',')
312
- : rule.value,
322
+ operator: propertyType === 'array' && rule.operator === '=' ? 'in' : rule.operator,
323
+ value: adjustedValue,
313
324
  };
314
325
  }
315
326
  });
316
- }
327
+ };
317
328
  // this retrieves the properties from a treeview for each property in the query
318
329
  // they are then used in the custom query builder components to determine the input type etc
319
330
  const updatePropertyTreeMap = (q) => {
@@ -358,8 +369,12 @@ const CriteriaBuilder = (props) => {
358
369
  }
359
370
  };
360
371
  const handleQueryChange = (q) => {
361
- setQuery(q);
362
- const newCriteria = JSON.parse(formatQuery(q, {
372
+ const processedQuery = {
373
+ ...q,
374
+ rules: processRules(q.rules, false),
375
+ };
376
+ setQuery(processedQuery);
377
+ const newCriteria = JSON.parse(formatQuery(processedQuery, {
363
378
  format: 'mongodb',
364
379
  ruleProcessor: (rule, options) => {
365
380
  let newRule = rule;
@@ -375,16 +390,12 @@ const CriteriaBuilder = (props) => {
375
390
  return defaultRuleProcessorMongoDB(newRule, options);
376
391
  },
377
392
  }));
378
- //when q has no rules, it formats and parses to { $and: [{ $expr: true }] }
379
- const allRulesDeleted = isEmpty(difference(newCriteria, { $and: [{ $expr: true }] }));
380
- // since the Add Condition / Add Group buttons add rules with all the fields empty,
381
- // we need to check if the first rule was added because q will still parse to { $and: [{ $expr: true }] }
382
- const firstRuleAdded = isEmpty(criteria) && q.rules.length > 0;
383
- if (allRulesDeleted && !firstRuleAdded) {
384
- setCriteria(undefined);
393
+ if (!isEmpty(difference(newCriteria, { $and: [{ $expr: true }] }))) {
394
+ setCriteria(newCriteria);
385
395
  }
386
396
  else {
387
- setCriteria(newCriteria);
397
+ if (q.rules.length === 0)
398
+ setCriteria(undefined);
388
399
  }
389
400
  };
390
401
  const fields = useMemo(() => {
@@ -428,7 +439,6 @@ const CriteriaBuilder = (props) => {
428
439
  borderStyle: 'hidden',
429
440
  background: '#fff',
430
441
  maxWidth: '70vw',
431
- margin: 'auto',
432
442
  },
433
443
  '.ruleGroup-header': {
434
444
  display: 'block',
@@ -487,9 +497,9 @@ const CriteriaBuilder = (props) => {
487
497
  ruleGroup: CustomRuleGroup,
488
498
  removeGroupAction: customDelete,
489
499
  removeRuleAction: customDelete,
490
- valueEditor: valueEditor,
500
+ valueEditor: customValueEditor ? customValueEditor.component : valueEditor,
491
501
  }, context: {
492
- dynamicContentInput,
502
+ ...(customValueEditor?.props ?? {}),
493
503
  presetValues,
494
504
  enablePresetValues,
495
505
  presetGroupLabel,
@@ -516,8 +526,6 @@ const CriteriaBuilder = (props) => {
516
526
  alignItems: 'center',
517
527
  marginBottom: '10px',
518
528
  maxWidth: '71vw',
519
- marginLeft: 'auto',
520
- marginRight: 'auto',
521
529
  } },
522
530
  React.createElement(Box, null,
523
531
  React.createElement(Button, { sx: {
@@ -547,7 +555,7 @@ const CriteriaBuilder = (props) => {
547
555
  backgroundColor: 'transparent',
548
556
  },
549
557
  ...styles.buttons,
550
- }, onClick: handleClearAll, title: "Clear all conditions", disabled: isEmpty(query.rules) }, "Clear All"))));
558
+ }, onClick: handleClearAll, title: "Clear all conditions", disabled: isEmpty(query.rules) }, "Clear all"))));
551
559
  }
552
560
  return React.createElement(React.Fragment, null);
553
561
  };
@@ -1,6 +1,7 @@
1
1
  import { ChevronRight, ExpandMore } from '@mui/icons-material';
2
2
  import React, { useEffect, useState } from 'react';
3
- import { Autocomplete, MenuItem, TextField, Tooltip, TreeView } from '../../core';
3
+ import { Autocomplete, MenuItem, TreeView } from '../../core';
4
+ import { OverflowTextField } from '../OverflowTextField';
4
5
  import PropertyTreeItem from './PropertyTreeItem';
5
6
  import { fetchDisplayNamePath, findPropertyById, setIdPaths, truncateNamePath, updateTreeNode } from './utils';
6
7
  const PropertyTree = ({ fetchObject, handleTreePropertySelect, rootObject, value }) => {
@@ -126,15 +127,8 @@ const PropertyTree = ({ fetchObject, handleTreePropertySelect, rootObject, value
126
127
  : objectPropertyNamePathMap[option.value] ?? '';
127
128
  return truncateNamePath(namePath, NAME_PATH_LIMIT);
128
129
  }, renderInput: (params) => {
129
- let isTruncated = false;
130
- const fullDisplayValue = value && objectPropertyNamePathMap[value];
131
- let displayValue = fullDisplayValue;
132
- if (!!displayValue && displayValue.length > NAME_PATH_LIMIT) {
133
- isTruncated = true;
134
- displayValue = truncateNamePath(displayValue, NAME_PATH_LIMIT);
135
- }
136
- return (React.createElement(Tooltip, { title: isTruncated ? fullDisplayValue : '', arrow: true },
137
- React.createElement(TextField, { ...params, "aria-label": fullDisplayValue, value: displayValue, size: "small", placeholder: "Select a property", variant: "outlined" })));
130
+ const fullDisplayName = value && objectPropertyNamePathMap[value];
131
+ return (React.createElement(OverflowTextField, { ...params, "aria-label": fullDisplayName, value: fullDisplayName, size: "small", placeholder: "Select a property", variant: "outlined" }));
138
132
  }, isOptionEqualToValue: (option, val) => {
139
133
  if (typeof val === 'string') {
140
134
  return option.value === val;
@@ -1,9 +1,9 @@
1
1
  import { Instant, LocalDate, LocalDateTime, LocalTime, ZoneId } from '@js-joda/core';
2
2
  import { ClearRounded } from '@mui/icons-material';
3
3
  import { Box, darken, lighten, styled } from '@mui/material';
4
- import { DateTimePicker, TimePicker } from '@mui/x-date-pickers';
5
- import React, { useRef, useState } from 'react';
6
- import { Autocomplete, Chip, DatePicker, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
4
+ import { TimePicker } from '@mui/x-date-pickers';
5
+ import React, { useEffect, useRef, useState } from 'react';
6
+ import { Autocomplete, Chip, DatePicker, DateTimePicker, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
7
7
  import { NumericFormat } from '../FormField/InputFieldComponent';
8
8
  const GroupHeader = styled('div')(({ theme }) => ({
9
9
  position: 'sticky',
@@ -18,7 +18,7 @@ const GroupHeader = styled('div')(({ theme }) => ({
18
18
  }));
19
19
  const GroupItems = styled('ul')({ padding: 0 });
20
20
  const ValueEditor = (props) => {
21
- const { handleOnChange, value, operator, context, level, rule } = props;
21
+ const { handleOnChange, value, operator, context, level, rule, fieldData } = props;
22
22
  let inputType = props.inputType;
23
23
  let values = props.values;
24
24
  const property = context.propertyTreeMap?.[rule.field];
@@ -47,6 +47,20 @@ const ValueEditor = (props) => {
47
47
  readOnly =
48
48
  Object.entries(context.disabledCriteria.criteria).some(([key, value]) => key === rule.field && value === rule.value && rule.operator === '=') && level === context.disabledCriteria.level;
49
49
  }
50
+ const styles = {
51
+ input: {
52
+ width: '33%',
53
+ background: readOnly ? '#f4f6f8' : '#fff',
54
+ },
55
+ };
56
+ useEffect(() => {
57
+ if (!['in', 'notIn'].includes(operator) && Array.isArray(value)) {
58
+ handleOnChange('');
59
+ }
60
+ else if (['in', 'notIn'].includes(operator) && !Array.isArray(value)) {
61
+ handleOnChange([]);
62
+ }
63
+ }, [operator]);
50
64
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
65
  const onClick = (e) => {
52
66
  // if property is date and date picker is open, don't open preset values
@@ -85,11 +99,11 @@ const ValueEditor = (props) => {
85
99
  if (inputType === 'date') {
86
100
  // date editor
87
101
  return (React.createElement(LocalizationProvider, null,
88
- React.createElement(DatePicker, { inputRef: inputRef, disabled: disabled, value: disabled ? null : value, onChange: handleOnChange, onClose: onClose, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: { width: '33%', background: '#fff' } })), readOnly: readOnly })));
102
+ React.createElement(DatePicker, { inputRef: inputRef, disabled: disabled, value: disabled ? null : value, onChange: handleOnChange, onClose: onClose, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: styles.input })), readOnly: readOnly })));
89
103
  }
90
104
  else if (inputType === 'time') {
91
105
  return (React.createElement(LocalizationProvider, null,
92
- React.createElement(TimePicker, { inputRef: inputRef, disabled: disabled, value: disabled || !value ? null : value, onChange: handleOnChange, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: { width: '33%', background: '#fff' } })), readOnly: readOnly })));
106
+ React.createElement(TimePicker, { inputRef: inputRef, disabled: disabled, value: disabled || !value ? null : value, onChange: handleOnChange, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: styles.input })), readOnly: readOnly })));
93
107
  }
94
108
  else if (inputType === 'date-time') {
95
109
  const dateTimeValue = parseISOStringToLocalDateTime(value);
@@ -110,7 +124,7 @@ const ValueEditor = (props) => {
110
124
  handleOnChange(new Date(localDateTime.toString()).toISOString());
111
125
  }, onClose: onClose, PopperProps: {
112
126
  anchorEl,
113
- }, renderInput: (params) => (React.createElement(Box, { sx: { width: '33%', background: '#fff' }, ref: setAnchorEl },
127
+ }, renderInput: (params) => (React.createElement(Box, { sx: styles.input, ref: setAnchorEl },
114
128
  React.createElement(TextField, { ...params, disabled: disabled, onClick: onClick, placeholder: "Value", size: "small", inputRef: inputRef }))), readOnly: readOnly })));
115
129
  }
116
130
  else if (inputType === 'number' || inputType === 'integer') {
@@ -125,7 +139,7 @@ const ValueEditor = (props) => {
125
139
  handleOnChange(uniqueSelections.length ? uniqueSelections : '');
126
140
  },
127
141
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
- isOptionEqualToValue: (option, value) => option === value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small", sx: { backgroundColor: '#fff' } })), groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: { width: '33%', background: '#fff' }, readOnly: readOnly }));
142
+ isOptionEqualToValue: (option, value) => option === value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small" })), groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: styles.input, readOnly: readOnly }));
129
143
  }
130
144
  else {
131
145
  return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => {
@@ -137,7 +151,7 @@ const ValueEditor = (props) => {
137
151
  }
138
152
  }, ...(inputType === 'number'
139
153
  ? { InputProps: { inputComponent: NumericFormat } }
140
- : { type: 'number' }), placeholder: "Value", size: "small", onClick: onClick, sx: { width: '33%', background: '#fff' }, readOnly: readOnly }));
154
+ : { type: 'number' }), placeholder: "Value", size: "small", onClick: onClick, sx: styles.input, readOnly: readOnly }));
141
155
  }
142
156
  }
143
157
  else {
@@ -147,7 +161,7 @@ const ValueEditor = (props) => {
147
161
  ...(presetValues?.sort((a, b) => a.label.localeCompare(b.label)) ?? []),
148
162
  ];
149
163
  if (isMultiple || values?.length) {
150
- return (React.createElement(Autocomplete, { freeSolo: inputType !== 'array' && inputType !== 'select', multiple: isMultiple, options: options, value: isMultiple ? (Array.isArray(value) ? value : []) : value,
164
+ return (React.createElement(Autocomplete, { freeSolo: inputType !== 'array' && fieldData.valueEditorType !== 'select', multiple: isMultiple, options: options, value: isMultiple ? (Array.isArray(value) ? value : []) : Array.isArray(value) ? '' : value,
151
165
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
166
  onChange: (event, newValue) => {
153
167
  let value;
@@ -161,14 +175,25 @@ const ValueEditor = (props) => {
161
175
  }
162
176
  handleOnChange(value);
163
177
  }, onBlur: () => {
164
- if (inputValue && (operator === 'in' || operator === 'notIn')) {
178
+ if (inputValue &&
179
+ (options.some((option) => option.name === inputValue) || !options.length) &&
180
+ (operator === 'in' || operator === 'notIn')) {
181
+ const newValues = Array.isArray(value) ? [...value, inputValue] : [inputValue];
182
+ handleOnChange(Array.from(new Set(newValues)));
183
+ setInputValue('');
184
+ }
185
+ }, onKeyDown: (event) => {
186
+ if (event.key === 'Enter' &&
187
+ inputValue &&
188
+ (options.some((option) => option.name === inputValue) || !options.length) &&
189
+ (operator === 'in' || operator === 'notIn')) {
165
190
  const newValues = Array.isArray(value) ? [...value, inputValue] : [inputValue];
166
191
  handleOnChange(Array.from(new Set(newValues)));
167
192
  setInputValue('');
168
193
  }
169
194
  }, onInputChange: (event, newInputValue) => {
170
195
  setInputValue(newInputValue);
171
- }, inputValue: inputValue, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params?.label, ...params, size: "small", sx: { backgroundColor: '#fff' } })),
196
+ }, inputValue: inputValue, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params?.label, ...params, size: "small" })),
172
197
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
173
198
  getOptionLabel: (option) => {
174
199
  if (typeof option === 'string') {
@@ -185,10 +210,10 @@ const ValueEditor = (props) => {
185
210
  else {
186
211
  return option?.label === value?.label;
187
212
  }
188
- }, groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: { width: '33%' }, readOnly: readOnly }));
213
+ }, groupBy: (option) => isPresetValue(option.value?.name) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: styles.input, readOnly: readOnly }));
189
214
  }
190
215
  else {
191
- return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => handleOnChange(e.target.value), onClick: onClick, placeholder: "Value", size: "small", sx: { width: '33%', background: '#fff' }, readOnly: readOnly }));
216
+ return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => handleOnChange(e.target.value), onClick: onClick, placeholder: "Value", size: "small", sx: styles.input, readOnly: readOnly }));
192
217
  }
193
218
  }
194
219
  };
@@ -1,6 +1,6 @@
1
- /// <reference types="react" />
2
1
  import { ApiServices, Obj, ObjectInstance, UserAccount } from '@evoke-platform/context';
3
2
  import { ReactComponent } from '@formio/react';
3
+ import React from 'react';
4
4
  import '../../../../styles/form-component.css';
5
5
  import { Document, ObjectPropertyInputProps } from '../types';
6
6
  type OnSaveResponse = {
@@ -34,6 +34,12 @@ export type FormProps = {
34
34
  queryAddresses?: unknown;
35
35
  fieldHeight?: 'small' | 'medium';
36
36
  richTextEditor?: typeof ReactComponent;
37
+ hideButtons?: boolean;
38
+ formRef?: React.MutableRefObject<FormRef | undefined>;
39
+ };
40
+ export type FormRef = {
41
+ submit: (submission: Record<string, unknown>, type: 'submit' | 'draft', setError: (error?: Record<string, unknown>) => void, setSubmitting?: (value: boolean) => void) => void;
42
+ data?: Record<string, unknown>;
37
43
  };
38
44
  export declare function Form(props: FormProps): JSX.Element;
39
45
  export default Form;
@@ -17,8 +17,9 @@ const usePrevious = (value) => {
17
17
  };
18
18
  Utils.Evaluator.noeval = true;
19
19
  export function Form(props) {
20
- const { clearable, closeModal, onSave, submitButtonLabel, instance, object, objectInputCommonProps, actionId, actionType, associatedObject, onAutoSave, apiServices, navigateTo, document, queryAddresses, user, isReadOnly, fieldHeight, richTextEditor, } = props;
20
+ const { clearable, closeModal, onSave, submitButtonLabel, instance, object, objectInputCommonProps, actionId, actionType, associatedObject, onAutoSave, apiServices, navigateTo, document, queryAddresses, user, isReadOnly, fieldHeight, richTextEditor, hideButtons, formRef, } = props;
21
21
  const [formKey, setFormKey] = useState();
22
+ const [formData, setFormData] = useState();
22
23
  const [componentProps, setComponentProps] = useState([]);
23
24
  const [snackbarError, setSnackbarError] = useState();
24
25
  const prevFormKey = usePrevious(formKey);
@@ -70,6 +71,14 @@ export function Form(props) {
70
71
  });
71
72
  buildComponents();
72
73
  };
74
+ useEffect(() => {
75
+ if (formRef) {
76
+ formRef.current = {
77
+ submit: saveHandler,
78
+ data: formData,
79
+ };
80
+ }
81
+ }, [formRef, formData]);
73
82
  useEffect(() => {
74
83
  // TODO: Check why formKey is not being set for create actions
75
84
  if (((actionType === 'update' || actionType === 'delete') && !!instance) || !!document) {
@@ -130,112 +139,117 @@ export function Form(props) {
130
139
  apiServices,
131
140
  user: userAccount,
132
141
  }, undefined, isReadOnly, allDefaultPages, navigateTo, queryAddresses, apiServices, !!closeModal, fieldHeight, richTextEditor);
133
- const components = isReadOnly ? newComponentProps : [...newComponentProps, BottomButtons];
134
- setComponentProps(components);
142
+ if (!hideButtons && !isReadOnly) {
143
+ newComponentProps.push(BottomButtons);
144
+ }
145
+ setComponentProps(newComponentProps);
135
146
  }
136
147
  else {
137
- // this condition is used for the delete action's modal form
138
- setComponentProps([
139
- ...(await addObjectPropertiesToComponentProps(visibleObjectProperties, [
140
- {
141
- html: `<p>${action?.type === 'delete' ? 'This action cannot be undone.' : 'Are you sure?'}</p>`,
142
- label: 'Content',
143
- customClass: '',
144
- refreshOnChange: false,
145
- hidden: false,
146
- modalEdit: false,
147
- conditional: {
148
- show: null,
149
- when: null,
150
- eq: '',
151
- },
152
- type: 'Content',
153
- key: 'content',
154
- input: false,
155
- placeholder: '',
156
- prefix: '',
157
- suffix: '',
148
+ const components = await addObjectPropertiesToComponentProps(visibleObjectProperties, [
149
+ {
150
+ html: `<p>${action?.type === 'delete' ? 'This action cannot be undone.' : 'Are you sure?'}</p>`,
151
+ label: 'Content',
152
+ customClass: '',
153
+ refreshOnChange: false,
154
+ hidden: false,
155
+ modalEdit: false,
156
+ conditional: {
157
+ show: null,
158
+ when: null,
159
+ eq: '',
160
+ },
161
+ type: 'Content',
162
+ key: 'content',
163
+ input: false,
164
+ placeholder: '',
165
+ prefix: '',
166
+ suffix: '',
167
+ multiple: false,
168
+ defaultValue: null,
169
+ protected: false,
170
+ unique: false,
171
+ persistent: true,
172
+ clearOnHide: true,
173
+ refreshOn: '',
174
+ redrawOn: '',
175
+ tableView: false,
176
+ dataGridLabel: false,
177
+ labelPosition: 'top',
178
+ description: '',
179
+ errorLabel: '',
180
+ tooltip: '',
181
+ hideLabel: false,
182
+ tabindex: '',
183
+ disabled: false,
184
+ autofocus: false,
185
+ dbIndex: false,
186
+ customDefaultValue: '',
187
+ calculateValue: '',
188
+ calculateServer: false,
189
+ widget: null,
190
+ attributes: {},
191
+ validateOn: 'change',
192
+ validate: {
193
+ required: false,
194
+ custom: '',
195
+ customPrivate: false,
196
+ strictDateValidation: false,
158
197
  multiple: false,
159
- defaultValue: null,
160
- protected: false,
161
198
  unique: false,
162
- persistent: true,
163
- clearOnHide: true,
164
- refreshOn: '',
165
- redrawOn: '',
166
- tableView: false,
167
- dataGridLabel: false,
168
- labelPosition: 'top',
169
- description: '',
170
- errorLabel: '',
171
- tooltip: '',
172
- hideLabel: false,
173
- tabindex: '',
174
- disabled: false,
175
- autofocus: false,
176
- dbIndex: false,
177
- customDefaultValue: '',
178
- calculateValue: '',
179
- calculateServer: false,
180
- widget: null,
181
- attributes: {},
182
- validateOn: 'change',
183
- validate: {
184
- required: false,
185
- custom: '',
186
- customPrivate: false,
187
- strictDateValidation: false,
188
- multiple: false,
189
- unique: false,
190
- },
191
- overlay: {
192
- style: '',
193
- left: '',
194
- top: '',
195
- width: '',
196
- height: '',
197
- },
198
- allowCalculateOverride: false,
199
- encrypted: false,
200
- showCharCount: false,
201
- showWordCount: false,
202
- properties: {},
203
- allowMultipleMasks: false,
204
- addons: [],
205
- id: 'eahbwo',
206
199
  },
207
- ], instance, {
208
- ...objectInputCommonProps,
209
- defaultPages: allDefaultPages,
210
- navigateTo,
211
- apiServices,
212
- user: userAccount,
213
- }, undefined, undefined, undefined, undefined, undefined, undefined, !!closeModal, fieldHeight, richTextEditor)),
214
- BottomButtons,
215
- ]);
200
+ overlay: {
201
+ style: '',
202
+ left: '',
203
+ top: '',
204
+ width: '',
205
+ height: '',
206
+ },
207
+ allowCalculateOverride: false,
208
+ encrypted: false,
209
+ showCharCount: false,
210
+ showWordCount: false,
211
+ properties: {},
212
+ allowMultipleMasks: false,
213
+ addons: [],
214
+ id: 'eahbwo',
215
+ },
216
+ ], instance, {
217
+ ...objectInputCommonProps,
218
+ defaultPages: allDefaultPages,
219
+ navigateTo,
220
+ apiServices,
221
+ user: userAccount,
222
+ }, undefined, undefined, undefined, undefined, undefined, undefined, !!closeModal, fieldHeight, richTextEditor);
223
+ if (!hideButtons) {
224
+ components.push(BottomButtons);
225
+ }
226
+ // this condition is used for the delete action's modal form
227
+ setComponentProps(components);
216
228
  }
217
229
  }
218
230
  else if (object?.properties) {
219
231
  // If form.io form is not configured and no inputProperties are
220
232
  // set, use object properties to build form.
221
233
  const propertiesInForm = object.properties.filter((prop) => prop.id !== associatedObject?.propertyId && (action?.type !== 'create' || prop.type !== 'document'));
222
- setComponentProps([
223
- ...buildComponentPropsFromObjectProperties(propertiesInForm, object.id, instance, {
224
- ...objectInputCommonProps,
225
- defaultPages: allDefaultPages,
226
- navigateTo,
227
- apiServices,
228
- user,
229
- }, !!action, undefined, isReadOnly, queryAddresses, !!closeModal, fieldHeight, richTextEditor),
230
- !isReadOnly && BottomButtons,
231
- ]);
234
+ const components = buildComponentPropsFromObjectProperties(propertiesInForm, object.id, instance, {
235
+ ...objectInputCommonProps,
236
+ defaultPages: allDefaultPages,
237
+ navigateTo,
238
+ apiServices,
239
+ user,
240
+ }, !!action, undefined, isReadOnly, queryAddresses, !!closeModal, fieldHeight, richTextEditor);
241
+ if (!isReadOnly && !hideButtons) {
242
+ components.push(BottomButtons);
243
+ }
244
+ setComponentProps(components);
232
245
  }
233
246
  else if (document) {
234
247
  const documentProperties = toPairs(flatten(document ?? {}));
235
- setComponentProps([
236
- ...buildComponentPropsFromDocumentProperties(documentProperties, isReadOnly, undefined, fieldHeight),
237
- !isReadOnly && BottomButtons,
238
- ]);
248
+ const components = buildComponentPropsFromDocumentProperties(documentProperties, isReadOnly, undefined, fieldHeight);
249
+ if (!isReadOnly && !hideButtons) {
250
+ components.push(BottomButtons);
251
+ }
252
+ setComponentProps(components);
239
253
  }
240
254
  };
241
255
  const uploadDocuments = async (files, metadata) => {
@@ -427,7 +441,11 @@ export function Form(props) {
427
441
  ],
428
442
  };
429
443
  return (React.createElement(Box, null,
430
- componentProps.length ? (React.createElement(FormIO, { key: closeModal ? undefined : formKey, form: {
444
+ componentProps.length ? (React.createElement(FormIO
445
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
446
+ , {
447
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
448
+ onChange: (e) => !isEqual(e.data, formData) && setFormData(e.data), key: closeModal ? undefined : formKey, form: {
431
449
  display: 'form',
432
450
  components: componentProps,
433
451
  }, formReady: handleFormReady })) : (React.createElement(Box, null,