@integry/sdk 4.7.18 → 4.7.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integry/sdk",
3
- "version": "4.7.18",
3
+ "version": "4.7.21",
4
4
  "description": "Integry SDK",
5
5
  "main": "dist/umd/index.umd.js",
6
6
  "module": "dist/esm/index.csm.js",
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  /* eslint-disable no-nested-ternary */
2
4
  import { html } from 'htm/preact';
3
5
  import { useState, useRef, useEffect } from 'preact/hooks';
@@ -5,8 +7,8 @@ import { Input } from '@/components/Input';
5
7
  import { TextArea } from '@/components/TextArea';
6
8
  import { useOnClickOutside } from '@/hooks/useOnClickOutside';
7
9
  import { FieldDropdown } from '@/components/MultipurposeField/TagMenu';
8
- import { NestedObject } from '@/interfaces';
9
- import { TagProps } from '@/components/Tag';
10
+ import type { NestedObject } from '@/interfaces';
11
+ import type { TagProps } from '@/components/Tag';
10
12
  // import { TagsMenu } from '@/components/TagsMenu';
11
13
  // @ts-ignore
12
14
  import Tagify from '@yaireo/tagify';
@@ -100,10 +102,10 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
100
102
  const [tempPlaceholder, setTempPlaceholder] = useState('');
101
103
  const [searchValue, setSearchValue] = useState('');
102
104
  const [tagify, setTagify] = useState<Tagify | null>(null);
103
- const [defaultTagifyValue, setDefaultTagifyValue] = useState('');
104
105
 
105
106
  const menuRef = useRef<HTMLDivElement | null>(null);
106
107
  const inputRef = useRef<HTMLInputElement>();
108
+ const appIconMapRef = useRef<Record<string, string>>({});
107
109
 
108
110
  const handleTabKeyDown = (event: KeyboardEvent) => {
109
111
  if (event.key === 'Tab') {
@@ -155,6 +157,58 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
155
157
  };
156
158
  }, [value, title]);
157
159
 
160
+ // Helper function to extract step name from tag value
161
+ const getStepNameFromValue = (tagValue: string): string | null => {
162
+ const parts = tagValue.split('.');
163
+ // For values like "steps.slack_post_message_1.output.ok", "storage.user_data.value",
164
+ // "setup_form.api_key.value", "parameters.timeout.value" return the second part
165
+ if (parts.length >= 2) {
166
+ return parts[1];
167
+ }
168
+ // If there's no second part, return null
169
+ return null;
170
+ };
171
+
172
+ // Build flattened appIcon map when tagsTree changes
173
+ useEffect(() => {
174
+ if (!tagsTree) {
175
+ appIconMapRef.current = {};
176
+ return;
177
+ }
178
+
179
+ const flattenedMap: Record<string, string> = {};
180
+
181
+ // Traverse tagsTree and build flattened map
182
+ const traverseAndMap = (tags: any[]) => {
183
+ if (!Array.isArray(tags)) return;
184
+
185
+ tags.forEach((tag) => {
186
+ if (tag.machineName && tag.appIcon) {
187
+ flattenedMap[tag.machineName] = tag.appIcon;
188
+ }
189
+ // Recursively traverse nested tags
190
+ if (tag.tags && Array.isArray(tag.tags)) {
191
+ traverseAndMap(tag.tags);
192
+ }
193
+ });
194
+ };
195
+
196
+ // Traverse all categories in tagsTree
197
+ for (const categoryKey in tagsTree) {
198
+ const category = tagsTree[categoryKey];
199
+ if (category.tags && Array.isArray(category.tags)) {
200
+ traverseAndMap(category.tags);
201
+ }
202
+ }
203
+
204
+ appIconMapRef.current = flattenedMap;
205
+ }, [tagsTree]);
206
+
207
+ // Helper function to get appIcon from precomputed map
208
+ const getAppIconFromMap = (stepName: string): string | null => {
209
+ return appIconMapRef.current[stepName] || null;
210
+ };
211
+
158
212
  useEffect(() => {
159
213
  const textfield = inputRef.current;
160
214
  const scalarValue = Array.isArray(value) ? value[0] : value;
@@ -178,9 +232,52 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
178
232
  });
179
233
  tagify
180
234
  .on('focus', handleFieldFocus)
235
+ .on('blur', handleFieldBlur)
181
236
  .on('remove', handleOnTagRemove)
182
237
  .on('change', handleOnTagRemove);
183
238
  setTagify(tagify);
239
+
240
+ tagify.on('click', (e: any) => {
241
+ const tagData = e.detail.data;
242
+ if (!tagData?.appIcon) {
243
+ return; // Ignore clicks on tags without appIcon
244
+ }
245
+ const tagElm = e.detail.tag;
246
+
247
+ // Remove the tag
248
+ tagify.removeTag(tagElm);
249
+
250
+ // Place caret after the removed tag
251
+ const selection = window.getSelection();
252
+ const range = document.createRange();
253
+
254
+ // Insert editable text node (without appIcon in the text)
255
+ const editableText = document.createTextNode(`{${tagData.value}} `);
256
+
257
+ // Insert text after the removed tag
258
+ tagElm.parentNode.insertBefore(editableText, tagElm.nextSibling);
259
+
260
+ // Move caret to end of inserted text
261
+ range.setStart(editableText, editableText.length);
262
+ range.setEnd(editableText, editableText.length);
263
+ selection?.removeAllRanges();
264
+ selection?.addRange(range);
265
+
266
+ // Optional: trigger input event so Tagify re-parses
267
+ tagify.DOM.input.dispatchEvent(new Event('input'));
268
+ });
269
+
270
+ tagify.on('keydown', (e: any) => {
271
+ if (
272
+ e?.detail?.event?.code === 'Enter' &&
273
+ tagsTree &&
274
+ type === 'TEXTAREA'
275
+ ) {
276
+ e.preventDefault();
277
+ document.execCommand('insertHTML', false, '<br>');
278
+ return false;
279
+ }
280
+ });
184
281
  }
185
282
  } else if (
186
283
  textfield &&
@@ -214,10 +311,18 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
214
311
  };
215
312
  const handleOnTagRemove = (e: any) => {
216
313
  const el: any = document.getElementById(`tagify_${fieldId}`);
314
+
217
315
  let newValue = el.__tagify.DOM.input.innerText;
316
+
317
+ // Process tags being removed
218
318
  el.__tagify.value.forEach((tag: any) => {
219
- newValue = newValue.replace(tag.text, `{${tag.value}}`);
319
+ newValue = newValue
320
+ .replace(tag.text, `{${tag.value}}`)
321
+ .replace('Step: ', '')
322
+ .replace('User variable: ', '')
323
+ .replace('Setup form: ', '');
220
324
  });
325
+
221
326
  newValue = newValue.trim();
222
327
  // remove zero width space
223
328
  newValue = newValue.replace(/\u200B/g, '');
@@ -225,7 +330,6 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
225
330
  el.value = '';
226
331
  newValue = '';
227
332
  }
228
- // newValue = newValue.replace(/^\n+|\n+$/g, ''); // remove leading and trailing newlines
229
333
  newValue = newValue.replace(/<br>/g, '\n'); // replace <br> with newlines
230
334
  newValue = newValue.replace(/\n{/g, '{').replace(/}\n/g, '}'); // remove \n before and after tags
231
335
 
@@ -303,9 +407,7 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
303
407
 
304
408
  // Returns all those strings which are enclosed in curly braces {}
305
409
  const getTags = (str: string) => {
306
- const regex = /\{([^{}]+)}/g;
307
- const matches = str.match(regex);
308
- return matches;
410
+ return str.match(/{[^{}"'<>:]+}/g) || [];
309
411
  };
310
412
 
311
413
  const checkIfTagExists = (AOUElement: string, tagValue: string) => {
@@ -319,14 +421,18 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
319
421
 
320
422
  const getDefaultTagifyValue = (value: any) => {
321
423
  const tags = getTags(value || '');
424
+
322
425
  if (tags && tags.length > 0) {
323
426
  let newValue = value;
427
+
324
428
  if (type === 'TEXTAREA') {
325
429
  newValue = newValue.replace(/\n/g, '<br>');
326
430
  }
431
+
327
432
  tags.forEach((tag) => {
328
433
  const tagValue = tag.replace(/{|}/g, '');
329
434
  const tagValueParts = tagValue.split('.');
435
+
330
436
  let tagData = (activityOutputDataRaw || []).find((element: any) => {
331
437
  return (
332
438
  element.value === tagValue ||
@@ -340,8 +446,16 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
340
446
  : tagValue,
341
447
  title: tagValue,
342
448
  };
449
+
450
+ // Try to get appIcon from tagsTree based on step name
451
+ const stepName = getStepNameFromValue(tagValue);
452
+ const appIconFromTree = stepName ? getAppIconFromMap(stepName) : null;
453
+
454
+ if (appIconFromTree) {
455
+ tagData.appIcon = appIconFromTree;
456
+ }
457
+
343
458
  if (tagsTree) {
344
- // this component has been called with explicit tagTree hence we don't need to rely on activity output data any more.
345
459
  tagData = {
346
460
  value: tagValue,
347
461
  text:
@@ -349,19 +463,29 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
349
463
  ? prettifyString(tagValueParts[tagValueParts.length - 1])
350
464
  : tagValue,
351
465
  title: tagValue,
466
+ ...(appIconFromTree && { appIcon: appIconFromTree }),
352
467
  };
353
468
  }
469
+
354
470
  if (tagData) {
471
+ // Include appIcon in the JSON if it exists
472
+ const tagDataForJson = {
473
+ value: tagData.value,
474
+ text: tagData.text,
475
+ title: getTagData(tagData.text),
476
+ ...(tagData.appIcon && { appIcon: tagData.appIcon }),
477
+ };
478
+
355
479
  newValue = newValue?.replace(
356
480
  tag,
357
- `[[{"value":"${tagData.value}","text":"${
358
- tagData.text
359
- }","title":"${getTagData(tagData.text)}"}]]`,
481
+ `[[${JSON.stringify(tagDataForJson)}]]`,
360
482
  );
361
483
  }
362
484
  });
485
+
363
486
  return newValue;
364
487
  }
488
+
365
489
  return value;
366
490
  };
367
491
 
@@ -515,6 +639,12 @@ const MultipurposeField = (props: MultipurposeFieldProps) => {
515
639
  }
516
640
  };
517
641
 
642
+ const handleFieldBlur = (e: any) => {
643
+ const el: any = document.getElementById(`tagify_${fieldId}`);
644
+ const currentTagifyValue = getDefaultTagifyValue(el.value || '');
645
+ el.value = currentTagifyValue;
646
+ };
647
+
518
648
  const getStringFromEnd = (str: string): string => {
519
649
  // eslint-disable-next-line @typescript-eslint/no-shadow
520
650
  const regex = /\{([^{}]+)$/;
@@ -46,6 +46,7 @@
46
46
 
47
47
  [class^='tagify__tag'] {
48
48
  z-index: 0;
49
+ vertical-align: middle;
49
50
  }
50
51
  --tag-bg: #bfdaff;
51
52
  --tag-hover: #bfdaff;
@@ -41,6 +41,7 @@ interface TagListProps {
41
41
  globalSearchMode?: boolean;
42
42
  onSelect?: (tag: Record<string, unknown>) => void;
43
43
  activeTab?: string;
44
+ parentAppIcon?: string; // Add parent app icon to props
44
45
  }
45
46
 
46
47
  interface TagsMenuProps {
@@ -88,6 +89,7 @@ const arrayToObject = (arr: unknown[]): Record<string, unknown> =>
88
89
  newObj[`[${index}]`] = item;
89
90
  return newObj;
90
91
  }, {});
92
+
91
93
  // Check if output data matches the search term
92
94
  const outputMatchesSearch = (output: unknown, term: string): boolean => {
93
95
  if (!term || !output) return false;
@@ -355,57 +357,7 @@ const ValuesList = ({
355
357
  </div>
356
358
  `;
357
359
 
358
- // Helper function to find parent tag with appIcon
359
- const findParentWithAppIcon = (
360
- tags: Record<string, unknown> | unknown[],
361
- path: string,
362
- ): string | undefined => {
363
- if (!path) return undefined;
364
-
365
- const pathParts = path.split('.');
366
- let currentTags = tags;
367
- let parentWithIcon: string | undefined;
368
-
369
- // Navigate through the path to find the parent with appIcon
370
- for (let i = 0; i < pathParts.length; i += 1) {
371
- const part = pathParts[i];
372
-
373
- // Skip 'output' parts in the path
374
- if (part === 'output') {
375
- // Skip processing for 'output'
376
- return undefined;
377
- }
378
-
379
- // Handle array indices
380
- if (part.startsWith('[') && part.endsWith(']')) {
381
- const index = Number.parseInt(part.substring(1, part.length - 1), 10);
382
- if (Array.isArray(currentTags) && index < currentTags.length) {
383
- const item = currentTags[index];
384
- if (isTagNode(item) && item.appIcon) {
385
- parentWithIcon = item.appIcon;
386
- }
387
- currentTags = item as Record<string, unknown>;
388
- } else {
389
- break;
390
- }
391
- }
392
- // Handle object properties
393
- if (!Array.isArray(currentTags) && currentTags[part]) {
394
- const item = currentTags[part];
395
- if (isTagNode(item) && item.appIcon) {
396
- parentWithIcon = item.appIcon;
397
- }
398
- currentTags = item as Record<string, unknown>;
399
- } else {
400
- break;
401
- }
402
- }
403
-
404
- return parentWithIcon;
405
- };
406
-
407
- // Update the TagList component to add the "add tag" button functionality
408
-
360
+ // Update the TagList component to pass parent app icon and include it in selections
409
361
  const TagList = ({
410
362
  tags,
411
363
  searchTerm,
@@ -416,6 +368,7 @@ const TagList = ({
416
368
  /* Default empty implementation */
417
369
  },
418
370
  activeTab = '',
371
+ parentAppIcon = undefined, // Add parent app icon parameter
419
372
  }: TagListProps) => {
420
373
  // Track which nodes are expanded to show nested steps
421
374
  const [expandedNodes, setExpandedNodes] = useState<Record<string, boolean>>(
@@ -659,19 +612,25 @@ const TagList = ({
659
612
  return stepName;
660
613
  };
661
614
 
662
- // Function to handle selecting a tag
615
+ // Modified function to handle selecting a tag with parent app icon
663
616
  const handleSelectTag = (
664
617
  key: string,
665
618
  value: unknown,
666
619
  currentPath: string,
620
+ nodeAppIcon?: string,
667
621
  ) => {
668
622
  const tagPath = `${activeTab}.${extractStepsFromOutputPath(currentPath)}`;
623
+
624
+ // Determine which app icon to use - prioritize node's own icon, then parent icon
625
+ const appIconToUse = nodeAppIcon || parentAppIcon;
626
+
669
627
  onSelect({
670
628
  currentPath: tagPath
671
629
  .replace(/\bparameters\.parameters\b/g, 'parameters')
672
630
  .replace(/\bauthorization\.authorization\b/g, 'authorization'),
673
631
  key,
674
632
  value: escapeHTMLIfNeeded(value),
633
+ parentAppIcon: appIconToUse, // Include parent app icon in the response
675
634
  });
676
635
  };
677
636
 
@@ -882,6 +841,12 @@ const TagList = ({
882
841
  tagsParamForOutput = value as Record<string, unknown>;
883
842
  }
884
843
 
844
+ // Determine the app icon to pass down - use current node's icon or parent's icon
845
+ const currentNodeAppIcon = isTagNode(value)
846
+ ? value.appIcon
847
+ : undefined;
848
+ const appIconToPass = currentNodeAppIcon || parentAppIcon;
849
+
885
850
  const handleMenuRowClcik = (e: Event) => {
886
851
  if (isStep && hasOutput) {
887
852
  // For step nodes with output, toggle output expansion
@@ -895,7 +860,7 @@ const TagList = ({
895
860
  toggleOutputExpand(key, e);
896
861
  } else {
897
862
  // For leaf nodes, select the value
898
- handleSelectTag(key, value, currentPath);
863
+ handleSelectTag(key, value, currentPath, currentNodeAppIcon);
899
864
  }
900
865
  };
901
866
 
@@ -923,7 +888,12 @@ const TagList = ({
923
888
  toggleOutputExpand(key, e);
924
889
  } else {
925
890
  // For leaf nodes, select the value
926
- handleSelectTag(key, value, currentPath);
891
+ handleSelectTag(
892
+ key,
893
+ value,
894
+ currentPath,
895
+ currentNodeAppIcon,
896
+ );
927
897
  }
928
898
  }}
929
899
  class="${styles.listItemContent}"
@@ -1007,7 +977,12 @@ const TagList = ({
1007
977
  class="${styles.addTagButton}"
1008
978
  onClick=${(e: Event) => {
1009
979
  e.stopPropagation();
1010
- handleSelectTag(key, value, currentPath);
980
+ handleSelectTag(
981
+ key,
982
+ value,
983
+ currentPath,
984
+ currentNodeAppIcon,
985
+ );
1011
986
  }}
1012
987
  title="Add this tag"
1013
988
  >
@@ -1211,6 +1186,7 @@ const TagList = ({
1211
1186
  globalSearchMode=${globalSearchMode}
1212
1187
  onSelect=${onSelect}
1213
1188
  activeTab=${activeTab}
1189
+ parentAppIcon=${appIconToPass}
1214
1190
  />
1215
1191
  `;
1216
1192
  }
@@ -1234,6 +1210,7 @@ const TagList = ({
1234
1210
  globalSearchMode=${globalSearchMode}
1235
1211
  onSelect=${onSelect}
1236
1212
  activeTab=${activeTab}
1213
+ parentAppIcon=${appIconToPass}
1237
1214
  />`
1238
1215
  : ''}
1239
1216
  <!-- Display expanded array content with additional debug info -->
@@ -1247,6 +1224,7 @@ const TagList = ({
1247
1224
  globalSearchMode=${globalSearchMode}
1248
1225
  onSelect=${onSelect}
1249
1226
  activeTab=${activeTab}
1227
+ parentAppIcon=${appIconToPass}
1250
1228
  />
1251
1229
  </div>`
1252
1230
  : ''}
@@ -1267,6 +1245,7 @@ const TagList = ({
1267
1245
  globalSearchMode=${globalSearchMode}
1268
1246
  onSelect=${onSelect}
1269
1247
  activeTab=${activeTab}
1248
+ parentAppIcon=${appIconToPass}
1270
1249
  />
1271
1250
  </div>`
1272
1251
  : ''}
@@ -1278,7 +1257,7 @@ const TagList = ({
1278
1257
  `;
1279
1258
  };
1280
1259
 
1281
- // Update the TabMenu component to search across all tabs
1260
+ // Update the TabMenu component to search across all tabs and pass data to TagList
1282
1261
  const TabMenu = ({ data, onSelect, onOptionClick }: TagMenuProps) => {
1283
1262
  const [activeTab, setActiveTab] = useState<string>(Object.keys(data)[0]);
1284
1263
  const [searchTerm, setSearchTerm] = useState<string>('');
@@ -1609,6 +1588,9 @@ const TabMenu = ({ data, onSelect, onOptionClick }: TagMenuProps) => {
1609
1588
  globalSearchMode=${globalSearchMode}
1610
1589
  onSelect=${onSelect}
1611
1590
  activeTab=${data[activeTab].machineName || activeTab}
1591
+ parentAppIcon=${isTagNode(activeData)
1592
+ ? activeData.appIcon
1593
+ : undefined}
1612
1594
  />
1613
1595
  </div>`}
1614
1596
  </div>
@@ -1683,7 +1665,6 @@ export function TagsMenu({
1683
1665
  ...modifiedData, // Spread the remaining tabs after "Values"
1684
1666
  };
1685
1667
  }
1686
-
1687
1668
  return html`
1688
1669
  <div class="${styles.tagsMenuWrapper}">
1689
1670
  <${TabMenu}
@@ -2083,6 +2083,7 @@ class ActionForm extends Component<ActionFormPropsType, ActionFormStateType> {
2083
2083
  idKeyPath=${uiField?.id_key_path}
2084
2084
  typeKeyPath=${uiField?.type_key_path}
2085
2085
  titleKeyPath=${uiField?.title_key_path}
2086
+ isRequiredKeyPath=${uiField?.is_required_key_path}
2086
2087
  onChangeCallback=${this.props
2087
2088
  .onFieldChangeCallback
2088
2089
  ? (val: any) => {
@@ -31,6 +31,7 @@ export type DynamicFieldsProps = {
31
31
  idKeyPath?: string;
32
32
  titleKeyPath?: string;
33
33
  typeKeyPath?: string;
34
+ isRequiredKeyPath?: string;
34
35
  onChange?: (val: any) => void;
35
36
  dataSourceBody?: any;
36
37
  parentFieldsChanged?: boolean;
@@ -50,6 +51,7 @@ interface DynamicDataItem {
50
51
  output?: any;
51
52
  next_page?: string;
52
53
  type?: string;
54
+ is_required?: boolean;
53
55
  }
54
56
 
55
57
  interface ApiResponse {
@@ -166,13 +168,21 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
166
168
  if (Array.isArray(res)) {
167
169
  transformedItems = res;
168
170
  } else if (res.output && Array.isArray(res.output)) {
169
- const { idKeyPath, titleKeyPath, typeKeyPath } = props;
171
+ const {
172
+ idKeyPath,
173
+ titleKeyPath,
174
+ typeKeyPath,
175
+ isRequiredKeyPath,
176
+ } = props;
170
177
 
171
178
  if (idKeyPath && titleKeyPath) {
172
179
  transformedItems = res.output.map((item: any) => ({
173
180
  id: resolveValue(idKeyPath, item),
174
181
  title: resolveValue(titleKeyPath, item),
175
182
  type: 'TEXTFIELD',
183
+ ...(isRequiredKeyPath
184
+ ? { is_required: resolveValue(isRequiredKeyPath, item) }
185
+ : {}),
176
186
  }));
177
187
  } else {
178
188
  setDynamicItems([]);
@@ -358,7 +368,9 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
358
368
  id="${`${setIndex}_${el.id}`}"
359
369
  title="${el.title}"
360
370
  placeholder="${dynamicField.placeholder}"
361
- isRequired="${dynamicField.is_required}"
371
+ isRequired="${el.is_required !== undefined
372
+ ? el.is_required
373
+ : dynamicField.is_required}"
362
374
  isEditable="${dynamicField.is_editable}"
363
375
  fieldId=${`${setIndex}_${el.id}`}
364
376
  onChange=${(val: any) => {
@@ -413,7 +425,9 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
413
425
  id="${el.id}"
414
426
  title="${el.title}"
415
427
  placeholder="${dynamicField.placeholder}"
416
- isRequired="${dynamicField.is_required}"
428
+ isRequired="${el.is_required !== undefined
429
+ ? el.is_required
430
+ : dynamicField.is_required}"
417
431
  isEditable="${dynamicField.is_editable}"
418
432
  fieldId=${el.id}
419
433
  onChange=${(val: any) => {
@@ -646,6 +646,10 @@ class FunctionForm extends Component<
646
646
  fieldDetails.meta.ui.ui_field?.type === 'CUSTOM_FIELDS'
647
647
  ? fieldDetails.meta.ui.ui_field.title_key_path || ''
648
648
  : undefined,
649
+ isRequiredKeyPath:
650
+ fieldDetails.meta.ui.ui_field?.type === 'CUSTOM_FIELDS'
651
+ ? fieldDetails.meta.ui.ui_field.is_required_key_path || ''
652
+ : undefined,
649
653
  dataSourceBody:
650
654
  fieldDetails.meta.ui.ui_field?.type === 'DYNAMIC_DROPDOWN' ||
651
655
  fieldDetails.meta.ui.ui_field?.type === 'CUSTOM_FIELDS'
@@ -897,6 +901,7 @@ class FunctionForm extends Component<
897
901
  idKeyPath=${field.idKeyPath}
898
902
  typeKeyPath=${field.typeKeyPath}
899
903
  titleKeyPath=${field.titleKeyPath}
904
+ isRequiredKeyPath=${field.isRequiredKeyPath}
900
905
  onChange=${(val: any) => {
901
906
  this.onFieldChange(field.id, val, field.dataType, false);
902
907
  }}
@@ -313,6 +313,7 @@ export interface UiField {
313
313
  type_key_path?: string;
314
314
  title_key_path?: string;
315
315
  id_key_path?: string;
316
+ is_required_key_path?: string;
316
317
  }
317
318
 
318
319
  export interface TemplateField {