@bpmn-io/form-js-viewer 1.5.0-alpha.0 → 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 (78) hide show
  1. package/dist/assets/form-js-base.css +244 -16
  2. package/dist/assets/form-js.css +676 -670
  3. package/dist/index.cjs +2100 -722
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.es.js +2083 -717
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/types/Form.d.ts +15 -4
  8. package/dist/types/core/FieldFactory.d.ts +3 -1
  9. package/dist/types/core/FormFieldRegistry.d.ts +2 -1
  10. package/dist/types/core/FormLayouter.d.ts +2 -2
  11. package/dist/types/core/Importer.d.ts +1 -1
  12. package/dist/types/core/PathRegistry.d.ts +8 -4
  13. package/dist/types/core/Validator.d.ts +1 -1
  14. package/dist/types/core/index.d.ts +8 -8
  15. package/dist/types/features/{expression-language → expressionLanguage}/ConditionChecker.d.ts +9 -5
  16. package/dist/types/features/{expression-language → expressionLanguage}/FeelExpressionLanguage.d.ts +1 -1
  17. package/dist/types/features/{expression-language → expressionLanguage}/FeelersTemplating.d.ts +1 -1
  18. package/dist/types/features/{expression-language → expressionLanguage}/index.d.ts +4 -4
  19. package/dist/types/features/index.d.ts +4 -2
  20. package/dist/types/features/markdown/MarkdownRenderer.d.ts +1 -1
  21. package/dist/types/features/markdown/index.d.ts +2 -2
  22. package/dist/types/features/repeatRender/RepeatRenderManager.d.ts +21 -0
  23. package/dist/types/features/repeatRender/index.d.ts +7 -0
  24. package/dist/types/features/viewerCommands/ViewerCommands.d.ts +2 -2
  25. package/dist/types/features/viewerCommands/cmd/UpdateFieldValidationHandler.d.ts +1 -1
  26. package/dist/types/features/viewerCommands/index.d.ts +3 -3
  27. package/dist/types/index.d.ts +1 -1
  28. package/dist/types/render/Renderer.d.ts +1 -1
  29. package/dist/types/render/components/Label.d.ts +21 -1
  30. package/dist/types/render/components/Sanitizer.d.ts +8 -0
  31. package/dist/types/render/components/Util.d.ts +1 -1
  32. package/dist/types/render/components/form-fields/Button.d.ts +3 -3
  33. package/dist/types/render/components/form-fields/Checkbox.d.ts +4 -4
  34. package/dist/types/render/components/form-fields/Checklist.d.ts +5 -5
  35. package/dist/types/render/components/form-fields/Datetime.d.ts +4 -4
  36. package/dist/types/render/components/form-fields/Default.d.ts +4 -4
  37. package/dist/types/render/components/form-fields/DynamicList.d.ts +18 -0
  38. package/dist/types/render/components/form-fields/Group.d.ts +4 -4
  39. package/dist/types/render/components/form-fields/IFrame.d.ts +12 -0
  40. package/dist/types/render/components/form-fields/Image.d.ts +3 -3
  41. package/dist/types/render/components/form-fields/Number.d.ts +4 -4
  42. package/dist/types/render/components/form-fields/Radio.d.ts +5 -5
  43. package/dist/types/render/components/form-fields/Select.d.ts +5 -5
  44. package/dist/types/render/components/form-fields/Separator.d.ts +3 -3
  45. package/dist/types/render/components/form-fields/Spacer.d.ts +3 -3
  46. package/dist/types/render/components/form-fields/Table.d.ts +71 -0
  47. package/dist/types/render/components/form-fields/Taglist.d.ts +5 -5
  48. package/dist/types/render/components/form-fields/Text.d.ts +3 -3
  49. package/dist/types/render/components/form-fields/Textarea.d.ts +4 -4
  50. package/dist/types/render/components/form-fields/Textfield.d.ts +4 -4
  51. package/dist/types/render/components/form-fields/parts/ChildrenRenderer.d.ts +1 -0
  52. package/dist/types/render/components/index.d.ts +5 -2
  53. package/dist/types/render/components/util/optionsUtil.d.ts +8 -0
  54. package/dist/types/render/context/FormRenderContext.d.ts +4 -3
  55. package/dist/types/render/context/LocalExpressionContext.d.ts +7 -0
  56. package/dist/types/render/context/index.d.ts +1 -0
  57. package/dist/types/render/hooks/index.d.ts +1 -0
  58. package/dist/types/render/hooks/useCleanupMultiSelectValues.d.ts +1 -0
  59. package/dist/types/render/hooks/useCleanupSingleSelectValue.d.ts +1 -0
  60. package/dist/types/render/hooks/useExpressionEvaluation.d.ts +3 -3
  61. package/dist/types/render/hooks/useFilteredFormData.d.ts +1 -0
  62. package/dist/types/render/hooks/useOptionsAsync.d.ts +28 -0
  63. package/dist/types/render/hooks/useScrollIntoView.d.ts +18 -0
  64. package/dist/types/render/hooks/useTemplateEvaluation.d.ts +1 -1
  65. package/dist/types/render/index.d.ts +4 -3
  66. package/dist/types/util/constants/DatetimeConstants.d.ts +6 -6
  67. package/dist/types/util/constants/OptionsSourceConstants.d.ts +19 -0
  68. package/dist/types/util/constants/index.d.ts +1 -1
  69. package/dist/types/util/getSchemaVariables.d.ts +39 -0
  70. package/dist/types/util/index.d.ts +3 -52
  71. package/dist/types/util/simple.d.ts +20 -0
  72. package/dist/types/util/structure.d.ts +1 -0
  73. package/package.json +5 -5
  74. package/dist/types/render/components/form-fields/parts/Grid.d.ts +0 -1
  75. package/dist/types/render/components/util/valuesUtil.d.ts +0 -8
  76. package/dist/types/render/hooks/useValuesAsync.d.ts +0 -28
  77. package/dist/types/util/constants/ValuesSourceConstants.d.ts +0 -19
  78. /package/dist/types/features/{expression-language → expressionLanguage}/variableExtractionHelpers.d.ts +0 -0
package/dist/index.es.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import Ids from 'ids';
2
- import { isString, get, isNumber, set, findIndex, isArray, isObject, values, uniqueBy, isFunction, bind, assign, isNil, groupBy, flatten, isUndefined } from 'min-dash';
2
+ import { isString, get, some, isNumber, set, findIndex, isArray, isObject, isNil, isDefined, values, uniqueBy, isFunction, bind, assign, groupBy, flatten, isUndefined } from 'min-dash';
3
3
  import Big from 'big.js';
4
- import { parseExpression, parseUnaryTests, evaluate, unaryTest } from 'feelin';
5
- import { evaluate as evaluate$1, parser, buildSimpleTree } from 'feelers';
6
4
  import classNames from 'classnames';
7
- import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
5
+ import { jsx, jsxs, Fragment } from 'preact/jsx-runtime';
8
6
  import { useContext, useMemo, useEffect, useRef, useState, useCallback, useLayoutEffect } from 'preact/hooks';
9
- import { createContext, createElement, Fragment, render } from 'preact';
7
+ import { createContext, createElement, Fragment as Fragment$1, render } from 'preact';
8
+ import flatpickr from 'flatpickr';
10
9
  import * as React from 'preact/compat';
11
10
  import { createPortal } from 'preact/compat';
12
- import flatpickr from 'flatpickr';
13
11
  import Markup from 'preact-markup';
14
12
  import { Injector } from 'didi';
13
+ import { parseExpression, parseUnaryTests, evaluate, unaryTest } from 'feelin';
14
+ import { evaluate as evaluate$1, parser, buildSimpleTree } from 'feelers';
15
15
  import showdown from 'showdown';
16
16
 
17
17
  const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', options = {}) => {
@@ -393,6 +393,92 @@ class FeelersTemplating {
393
393
  }
394
394
  FeelersTemplating.$inject = [];
395
395
 
396
+ // config ///////////////////
397
+
398
+ const MINUTES_IN_DAY = 60 * 24;
399
+ const DATETIME_SUBTYPES = {
400
+ DATE: 'date',
401
+ TIME: 'time',
402
+ DATETIME: 'datetime'
403
+ };
404
+ const TIME_SERIALISING_FORMATS = {
405
+ UTC_OFFSET: 'utc_offset',
406
+ UTC_NORMALIZED: 'utc_normalized',
407
+ NO_TIMEZONE: 'no_timezone'
408
+ };
409
+ const DATETIME_SUBTYPES_LABELS = {
410
+ [DATETIME_SUBTYPES.DATE]: 'Date',
411
+ [DATETIME_SUBTYPES.TIME]: 'Time',
412
+ [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
413
+ };
414
+ const TIME_SERIALISINGFORMAT_LABELS = {
415
+ [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
416
+ [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
417
+ [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
418
+ };
419
+ const DATETIME_SUBTYPE_PATH = ['subtype'];
420
+ const DATE_LABEL_PATH = ['dateLabel'];
421
+ const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
422
+ const TIME_LABEL_PATH = ['timeLabel'];
423
+ const TIME_USE24H_PATH = ['use24h'];
424
+ const TIME_INTERVAL_PATH = ['timeInterval'];
425
+ const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
426
+
427
+ // config ///////////////////
428
+
429
+ const OPTIONS_SOURCES = {
430
+ STATIC: 'static',
431
+ INPUT: 'input',
432
+ EXPRESSION: 'expression'
433
+ };
434
+ const OPTIONS_SOURCE_DEFAULT = OPTIONS_SOURCES.STATIC;
435
+ const OPTIONS_SOURCES_LABELS = {
436
+ [OPTIONS_SOURCES.STATIC]: 'Static',
437
+ [OPTIONS_SOURCES.INPUT]: 'Input data',
438
+ [OPTIONS_SOURCES.EXPRESSION]: 'Expression'
439
+ };
440
+ const OPTIONS_SOURCES_PATHS = {
441
+ [OPTIONS_SOURCES.STATIC]: ['values'],
442
+ [OPTIONS_SOURCES.INPUT]: ['valuesKey'],
443
+ [OPTIONS_SOURCES.EXPRESSION]: ['valuesExpression']
444
+ };
445
+ const OPTIONS_SOURCES_DEFAULTS = {
446
+ [OPTIONS_SOURCES.STATIC]: [{
447
+ label: 'Value',
448
+ value: 'value'
449
+ }],
450
+ [OPTIONS_SOURCES.INPUT]: '',
451
+ [OPTIONS_SOURCES.EXPRESSION]: '='
452
+ };
453
+
454
+ // helpers ///////////////////
455
+
456
+ function getOptionsSource(field) {
457
+ for (const source of Object.values(OPTIONS_SOURCES)) {
458
+ if (get(field, OPTIONS_SOURCES_PATHS[source]) !== undefined) {
459
+ return source;
460
+ }
461
+ }
462
+ return OPTIONS_SOURCE_DEFAULT;
463
+ }
464
+
465
+ function createInjector(bootstrapModules) {
466
+ const injector = new Injector(bootstrapModules);
467
+ injector.init();
468
+ return injector;
469
+ }
470
+
471
+ /**
472
+ * @param {string?} prefix
473
+ *
474
+ * @returns Element
475
+ */
476
+ function createFormContainer(prefix = 'fjs') {
477
+ const container = document.createElement('div');
478
+ container.classList.add(`${prefix}-container`);
479
+ return container;
480
+ }
481
+
396
482
  function formFieldClasses(type, {
397
483
  errors = [],
398
484
  disabled = false,
@@ -418,14 +504,19 @@ function gridColumnClasses(formField) {
418
504
  // always fall back to top-down on smallest screens
419
505
  'cds--col-sm-16', 'cds--col-md-16');
420
506
  }
421
- function prefixId(id, formId) {
507
+ function prefixId(id, formId, indexes) {
508
+ let result = 'fjs-form';
422
509
  if (formId) {
423
- return `fjs-form-${formId}-${id}`;
510
+ result += `-${formId}`;
424
511
  }
425
- return `fjs-form-${id}`;
512
+ result += `-${id}`;
513
+ Object.values(indexes || {}).forEach(index => {
514
+ result += `_${index}`;
515
+ });
516
+ return result;
426
517
  }
427
518
 
428
- const type$d = 'button';
519
+ const type$f = 'button';
429
520
  function Button(props) {
430
521
  const {
431
522
  disabled,
@@ -437,7 +528,7 @@ function Button(props) {
437
528
  action = 'submit'
438
529
  } = field;
439
530
  return jsx("div", {
440
- class: formFieldClasses(type$d),
531
+ class: formFieldClasses(type$f),
441
532
  children: jsx("button", {
442
533
  class: "fjs-button",
443
534
  type: action,
@@ -449,7 +540,7 @@ function Button(props) {
449
540
  });
450
541
  }
451
542
  Button.config = {
452
- type: type$d,
543
+ type: type$f,
453
544
  keyed: false,
454
545
  label: 'Button',
455
546
  group: 'action',
@@ -460,27 +551,30 @@ Button.config = {
460
551
  };
461
552
 
462
553
  const FormRenderContext = createContext({
463
- EmptyRoot: props => {
554
+ Empty: props => {
464
555
  return null;
465
556
  },
466
- Empty: props => {
557
+ Hidden: props => {
467
558
  return null;
468
559
  },
469
560
  Children: props => {
470
561
  return jsx("div", {
471
562
  class: props.class,
563
+ style: props.style,
472
564
  children: props.children
473
565
  });
474
566
  },
475
567
  Element: props => {
476
568
  return jsx("div", {
477
569
  class: props.class,
570
+ style: props.style,
478
571
  children: props.children
479
572
  });
480
573
  },
481
574
  Row: props => {
482
575
  return jsx("div", {
483
576
  class: props.class,
577
+ style: props.style,
484
578
  children: props.children
485
579
  });
486
580
  },
@@ -490,16 +584,24 @@ const FormRenderContext = createContext({
490
584
  }
491
585
  return jsx("div", {
492
586
  class: props.class,
587
+ style: props.style,
493
588
  children: props.children
494
589
  });
495
590
  },
496
- hoveredId: [],
497
- setHoveredId: newValue => {
498
- console.log(`setHoveredId not defined, called with '${newValue}'`);
591
+ hoverInfo: {
592
+ cleanup: () => {}
499
593
  }
500
594
  });
501
595
  var FormRenderContext$1 = FormRenderContext;
502
596
 
597
+ const LocalExpressionContext = createContext({
598
+ data: null,
599
+ this: null,
600
+ parent: null,
601
+ i: null
602
+ });
603
+ var LocalExpressionContext$1 = LocalExpressionContext;
604
+
503
605
  /**
504
606
  * @param {string} type
505
607
  * @param {boolean} [strict]
@@ -520,24 +622,77 @@ function useService(type, strict) {
520
622
  return getService(type, strict);
521
623
  }
522
624
 
625
+ function isRequired(field) {
626
+ return field.required;
627
+ }
628
+ function pathParse(path) {
629
+ if (!path) {
630
+ return [];
631
+ }
632
+ return path.split('.').map(key => {
633
+ return isNaN(parseInt(key)) ? key : parseInt(key);
634
+ });
635
+ }
636
+ function pathsEqual(a, b) {
637
+ return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
638
+ }
639
+ const indices = {};
640
+ function generateIndexForType(type) {
641
+ if (type in indices) {
642
+ indices[type]++;
643
+ } else {
644
+ indices[type] = 1;
645
+ }
646
+ return indices[type];
647
+ }
648
+ function generateIdForType(type) {
649
+ return `${type}${generateIndexForType(type)}`;
650
+ }
651
+
523
652
  /**
524
- * Returns the conditionally filtered data of a form reactively.
525
- * Memoised to minimize re-renders
653
+ * @template T
654
+ * @param {T} data
655
+ * @param {(this: any, key: string, value: any) => any} [replacer]
656
+ * @return {T}
657
+ */
658
+ function clone(data, replacer) {
659
+ return JSON.parse(JSON.stringify(data, replacer));
660
+ }
661
+
662
+ /**
663
+ * Transform a LocalExpressionContext object into a usable FEEL context.
526
664
  *
665
+ * @param {Object} context - The LocalExpressionContext object.
666
+ * @returns {Object} The usable FEEL context.
527
667
  */
528
- function useFilteredFormData() {
668
+
669
+ function buildExpressionContext(context) {
529
670
  const {
530
- initialData,
531
- data
532
- } = useService('form')._getState();
533
- const conditionChecker = useService('conditionChecker', false);
534
- return useMemo(() => {
535
- const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
536
- return {
537
- ...initialData,
538
- ...newData
539
- };
540
- }, [conditionChecker, data, initialData]);
671
+ data,
672
+ ...specialContextKeys
673
+ } = context;
674
+ return {
675
+ ...specialContextKeys,
676
+ ...data,
677
+ ..._wrapObjectKeysWithUnderscores(specialContextKeys)
678
+ };
679
+ }
680
+ function runRecursively(formField, fn) {
681
+ const components = formField.components || [];
682
+ components.forEach((component, index) => {
683
+ runRecursively(component, fn);
684
+ });
685
+ fn(formField);
686
+ }
687
+
688
+ // helpers //////////////////////
689
+
690
+ function _wrapObjectKeysWithUnderscores(obj) {
691
+ const newObj = {};
692
+ for (const [key, value] of Object.entries(obj)) {
693
+ newObj[`_${key}_`] = value;
694
+ }
695
+ return newObj;
541
696
  }
542
697
 
543
698
  /**
@@ -549,29 +704,125 @@ function useFilteredFormData() {
549
704
  */
550
705
  function useCondition(condition) {
551
706
  const conditionChecker = useService('conditionChecker', false);
552
- const filteredData = useFilteredFormData();
707
+ const expressionContextInfo = useContext(LocalExpressionContext$1);
553
708
  return useMemo(() => {
554
- return conditionChecker ? conditionChecker.check(condition, filteredData) : null;
555
- }, [conditionChecker, condition, filteredData]);
709
+ return conditionChecker ? conditionChecker.check(condition, buildExpressionContext(expressionContextInfo)) : null;
710
+ }, [conditionChecker, condition, expressionContextInfo]);
711
+ }
712
+
713
+ /**
714
+ * Custom hook to scroll an element into view only when it is not visible within the viewport.
715
+ *
716
+ * @param {Object} targetRef - A ref pointing to the DOM element to scroll into view.
717
+ * @param {Array} deps - An array of dependencies that trigger the effect.
718
+ * @param {Array} flagRefs - An array of refs that are used as flags to control when to scroll.
719
+ * @param {Object} [scrollOptions={}] - Options defining the behavior of the scrolling.
720
+ * @param {String} [scrollOptions.align='center'] - The alignment of the element within the viewport.
721
+ * @param {String} [scrollOptions.behavior='auto'] - The scrolling behavior.
722
+ * @param {Number} [scrollOptions.offset=0] - An offset that is added to the scroll position.
723
+ * @param {Boolean} [scrollOptions.scrollIfVisible=false] - Whether to scroll even if the element is visible.
724
+ */
725
+ function useScrollIntoView(targetRef, deps, scrollOptions = null, flagRefs = []) {
726
+ useEffect(() => {
727
+ // return early if flags are not raised, or component is not mounted
728
+ if (some(flagRefs, ref => !ref.current) || !targetRef.current) {
729
+ return;
730
+ }
731
+ for (let i = 0; i < flagRefs.length; i++) {
732
+ flagRefs[i].current = false;
733
+ }
734
+ const itemToBeScrolled = targetRef.current;
735
+ const scrollContainer = _getNearestScrollableAncestor(itemToBeScrolled);
736
+ if (!scrollContainer) {
737
+ return;
738
+ }
739
+ const itemRect = itemToBeScrolled.getBoundingClientRect();
740
+ const containerRect = scrollContainer.getBoundingClientRect();
741
+
742
+ // should scroll if visible or scrollIfVisible option is set
743
+ const shouldScroll = scrollOptions.scrollIfVisible || !(itemRect.top >= containerRect.top && itemRect.bottom <= containerRect.bottom);
744
+ if (!shouldScroll) {
745
+ return;
746
+ }
747
+ const {
748
+ align = 'center',
749
+ offset = 0,
750
+ behavior = 'auto'
751
+ } = scrollOptions;
752
+ const topOffset = _getTopOffset(itemToBeScrolled, scrollContainer, {
753
+ align,
754
+ offset
755
+ });
756
+ scrollContainer.scroll({
757
+ top: topOffset,
758
+ behavior
759
+ });
760
+
761
+ // eslint-disable-next-line react-hooks/exhaustive-deps
762
+ }, deps);
763
+ }
764
+
765
+ // helper //////////////////////
766
+
767
+ function _getNearestScrollableAncestor(el) {
768
+ while (el) {
769
+ if (el.scrollHeight > el.clientHeight) {
770
+ return el;
771
+ }
772
+ el = el.parentElement;
773
+ }
774
+ }
775
+ function _getTopOffset(item, scrollContainer, options) {
776
+ const itemRect = item.getBoundingClientRect();
777
+ const containerRect = scrollContainer.getBoundingClientRect();
778
+ if (options.align === 'top') {
779
+ return itemRect.top - containerRect.top + scrollContainer.scrollTop - options.offset;
780
+ } else if (options.align === 'bottom') {
781
+ return itemRect.bottom - containerRect.top - scrollContainer.clientHeight + scrollContainer.scrollTop + options.offset;
782
+ } else if (options.align === 'center') {
783
+ return itemRect.top - containerRect.top - scrollContainer.clientHeight / 2 + scrollContainer.scrollTop + itemRect.height / 2 + options.offset;
784
+ }
785
+ return 0;
556
786
  }
557
787
 
558
788
  /**
559
789
  * Evaluate a string reactively based on the expressionLanguage and form data.
560
790
  * If the string is not an expression, it is returned as is.
561
- * Memoised to minimize re-renders.
562
- *
563
- * @param {string} value
791
+ * The function is memoized to minimize re-renders.
564
792
  *
793
+ * @param {string} value - The string to evaluate.
794
+ * @returns {any} - Evaluated value or the original value if not an expression.
565
795
  */
566
796
  function useExpressionEvaluation(value) {
567
- const formData = useFilteredFormData();
568
797
  const expressionLanguage = useService('expressionLanguage');
798
+ const expressionContextInfo = useContext(LocalExpressionContext$1);
569
799
  return useMemo(() => {
570
800
  if (expressionLanguage && expressionLanguage.isExpression(value)) {
571
- return expressionLanguage.evaluate(value, formData);
801
+ return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
572
802
  }
573
803
  return value;
574
- }, [expressionLanguage, formData, value]);
804
+ }, [expressionLanguage, expressionContextInfo, value]);
805
+ }
806
+
807
+ /**
808
+ * Returns the conditionally filtered data of a form reactively.
809
+ * Memoised to minimize re-renders
810
+ *
811
+ * Warning: costly operation, use with care
812
+ */
813
+ function useFilteredFormData() {
814
+ const {
815
+ initialData,
816
+ data
817
+ } = useService('form')._getState();
818
+ const conditionChecker = useService('conditionChecker', false);
819
+ return useMemo(() => {
820
+ const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
821
+ return {
822
+ ...initialData,
823
+ ...newData
824
+ };
825
+ }, [conditionChecker, data, initialData]);
575
826
  }
576
827
 
577
828
  function useKeyDownAction(targetKey, action, listenerElement = window) {
@@ -604,7 +855,7 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
604
855
  function useReadonly(formField, properties = {}) {
605
856
  const expressionLanguage = useService('expressionLanguage');
606
857
  const conditionChecker = useService('conditionChecker', false);
607
- const filteredData = useFilteredFormData();
858
+ const expressionContextInfo = useContext(LocalExpressionContext$1);
608
859
  const {
609
860
  readonly
610
861
  } = formField;
@@ -612,7 +863,7 @@ function useReadonly(formField, properties = {}) {
612
863
  return true;
613
864
  }
614
865
  if (expressionLanguage && expressionLanguage.isExpression(readonly)) {
615
- return conditionChecker ? conditionChecker.check(readonly, filteredData) : false;
866
+ return conditionChecker ? conditionChecker.check(readonly, buildExpressionContext(expressionContextInfo)) : false;
616
867
  }
617
868
  return readonly || false;
618
869
  }
@@ -659,15 +910,15 @@ function compare(a, b) {
659
910
  * @param {Function} [options.buildDebugString]
660
911
  *
661
912
  */
662
- function useTemplateEvaluation(value, options) {
663
- const filteredData = useFilteredFormData();
913
+ function useTemplateEvaluation(value, options = {}) {
664
914
  const templating = useService('templating');
915
+ const expressionContextInfo = useContext(LocalExpressionContext$1);
665
916
  return useMemo(() => {
666
917
  if (templating && templating.isTemplate(value)) {
667
- return templating.evaluate(value, filteredData, options);
918
+ return templating.evaluate(value, buildExpressionContext(expressionContextInfo), options);
668
919
  }
669
920
  return value;
670
- }, [filteredData, templating, value, options]);
921
+ }, [templating, value, expressionContextInfo, options]);
671
922
  }
672
923
 
673
924
  /**
@@ -747,11 +998,13 @@ function Label(props) {
747
998
  });
748
999
  }
749
1000
 
750
- const type$c = 'checkbox';
1001
+ const type$e = 'checkbox';
751
1002
  function Checkbox(props) {
752
1003
  const {
753
1004
  disabled,
754
1005
  errors = [],
1006
+ errorMessageId,
1007
+ domId,
755
1008
  onBlur,
756
1009
  onFocus,
757
1010
  field,
@@ -760,7 +1013,6 @@ function Checkbox(props) {
760
1013
  } = props;
761
1014
  const {
762
1015
  description,
763
- id,
764
1016
  label,
765
1017
  validate = {}
766
1018
  } = field;
@@ -775,12 +1027,8 @@ function Checkbox(props) {
775
1027
  value: target.checked
776
1028
  });
777
1029
  };
778
- const {
779
- formId
780
- } = useContext(FormContext$1);
781
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
782
1030
  return jsxs("div", {
783
- class: classNames(formFieldClasses(type$c, {
1031
+ class: classNames(formFieldClasses(type$e, {
784
1032
  errors,
785
1033
  disabled,
786
1034
  readonly
@@ -788,7 +1036,7 @@ function Checkbox(props) {
788
1036
  'fjs-checked': value
789
1037
  }),
790
1038
  children: [jsx(Label, {
791
- id: prefixId(id, formId),
1039
+ id: domId,
792
1040
  label: label,
793
1041
  required: required,
794
1042
  children: jsx("input", {
@@ -796,7 +1044,7 @@ function Checkbox(props) {
796
1044
  class: "fjs-input",
797
1045
  disabled: disabled,
798
1046
  readOnly: readonly,
799
- id: prefixId(id, formId),
1047
+ id: domId,
800
1048
  type: "checkbox",
801
1049
  onChange: onChange,
802
1050
  onBlur: () => onBlur && onBlur(),
@@ -812,7 +1060,7 @@ function Checkbox(props) {
812
1060
  });
813
1061
  }
814
1062
  Checkbox.config = {
815
- type: type$c,
1063
+ type: type$e,
816
1064
  keyed: true,
817
1065
  label: 'Checkbox',
818
1066
  group: 'selection',
@@ -826,54 +1074,54 @@ Checkbox.config = {
826
1074
  };
827
1075
 
828
1076
  // parses the options data from the provided form field and form data
829
- function getValuesData(formField, formData) {
1077
+ function getOptionsData(formField, formData) {
830
1078
  const {
831
- valuesKey,
832
- values
1079
+ valuesKey: optionsKey,
1080
+ values: staticOptions
833
1081
  } = formField;
834
- return valuesKey ? get(formData, [valuesKey]) : values;
1082
+ return optionsKey ? get(formData, [optionsKey]) : staticOptions;
835
1083
  }
836
1084
 
837
1085
  // transforms the provided options into a normalized format, trimming invalid options
838
- function normalizeValuesData(valuesData) {
839
- return valuesData.filter(_isValueSomething).map(v => _normalizeValueData(v)).filter(v => v);
1086
+ function normalizeOptionsData(optionsData) {
1087
+ return optionsData.filter(_isOptionSomething).map(v => _normalizeOptionsData(v)).filter(v => v);
840
1088
  }
841
- function _normalizeValueData(valueData) {
842
- if (_isAllowedValue(valueData)) {
1089
+ function _normalizeOptionsData(optionData) {
1090
+ if (_isAllowedOption(optionData)) {
843
1091
  // if a primitive is provided, use it as label and value
844
1092
  return {
845
- value: valueData,
846
- label: `${valueData}`
1093
+ value: optionData,
1094
+ label: `${optionData}`
847
1095
  };
848
1096
  }
849
- if (typeof valueData === 'object') {
850
- if (!valueData.label && _isAllowedValue(valueData.value)) {
1097
+ if (typeof optionData === 'object') {
1098
+ if (!optionData.label && _isAllowedOption(optionData.value)) {
851
1099
  // if no label is provided, use the value as label
852
1100
  return {
853
- value: valueData.value,
854
- label: `${valueData.value}`
1101
+ value: optionData.value,
1102
+ label: `${optionData.value}`
855
1103
  };
856
1104
  }
857
- if (_isValueSomething(valueData.value) && _isAllowedValue(valueData.label)) {
1105
+ if (_isOptionSomething(optionData.value) && _isAllowedOption(optionData.label)) {
858
1106
  // if both value and label are provided, use them as is, in this scenario, the value may also be an object
859
- return valueData;
1107
+ return optionData;
860
1108
  }
861
1109
  }
862
1110
  return null;
863
1111
  }
864
- function _isAllowedValue(value) {
865
- return _isReadableType(value) && _isValueSomething(value);
1112
+ function _isAllowedOption(option) {
1113
+ return _isReadableType(option) && _isOptionSomething(option);
866
1114
  }
867
- function _isReadableType(value) {
868
- return ['number', 'string', 'boolean'].includes(typeof value);
1115
+ function _isReadableType(option) {
1116
+ return ['number', 'string', 'boolean'].includes(typeof option);
869
1117
  }
870
- function _isValueSomething(value) {
871
- return value || value === 0 || value === false;
1118
+ function _isOptionSomething(option) {
1119
+ return option || option === 0 || option === false;
872
1120
  }
873
1121
  function createEmptyOptions(options = {}) {
874
1122
  const defaults = {};
875
1123
 
876
- // provide default values if valuesKey and valuesExpression are not set
1124
+ // provide default options if valuesKey and valuesExpression are not set
877
1125
  if (!options.valuesKey && !options.valuesExpression) {
878
1126
  defaults.values = [{
879
1127
  label: 'Value',
@@ -896,102 +1144,95 @@ const LOAD_STATES = {
896
1144
  };
897
1145
 
898
1146
  /**
899
- * @typedef {Object} ValuesGetter
900
- * @property {Object[]} values - The values data
901
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
1147
+ * @typedef {Object} OptionsGetter
1148
+ * @property {Object[]} options - The options data
1149
+ * @property {(LOAD_STATES)} loadState - The options data's loading state, to use for conditional rendering
902
1150
  */
903
1151
 
904
1152
  /**
905
- * A hook to load values for single and multiselect components.
1153
+ * A hook to load options for single and multiselect components.
906
1154
  *
907
- * @param {Object} field - The form field to handle values for
908
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
1155
+ * @param {Object} field - The form field to handle options for
1156
+ * @return {OptionsGetter} optionsGetter - A options getter object providing loading state and options
909
1157
  */
910
- function useValuesAsync (field) {
1158
+ function useOptionsAsync (field) {
911
1159
  const {
912
- valuesExpression,
913
- valuesKey,
914
- values: staticValues
1160
+ valuesExpression: optionsExpression,
1161
+ valuesKey: optionsKey,
1162
+ values: staticOptions
915
1163
  } = field;
916
- const [valuesGetter, setValuesGetter] = useState({
917
- values: [],
1164
+ const [optionsGetter, setOptionsGetter] = useState({
1165
+ options: [],
918
1166
  error: undefined,
919
- state: LOAD_STATES.LOADING
1167
+ loadState: LOAD_STATES.LOADING
920
1168
  });
921
1169
  const initialData = useService('form')._getState().initialData;
922
- const expressionEvaluation = useExpressionEvaluation(valuesExpression);
923
- const evaluatedValues = useDeepCompareState(expressionEvaluation || [], []);
1170
+ const expressionEvaluation = useExpressionEvaluation(optionsExpression);
1171
+ const evaluatedOptions = useDeepCompareState(expressionEvaluation || [], []);
924
1172
  useEffect(() => {
925
- let values = [];
1173
+ let options = [];
926
1174
 
927
- // dynamic values
928
- if (valuesKey !== undefined) {
929
- const keyedValues = (initialData || {})[valuesKey];
930
- if (keyedValues && Array.isArray(keyedValues)) {
931
- values = keyedValues;
1175
+ // dynamic options
1176
+ if (optionsKey !== undefined) {
1177
+ const keyedOptions = (initialData || {})[optionsKey];
1178
+ if (keyedOptions && Array.isArray(keyedOptions)) {
1179
+ options = keyedOptions;
932
1180
  }
933
1181
 
934
- // static values
935
- } else if (staticValues !== undefined) {
936
- values = Array.isArray(staticValues) ? staticValues : [];
1182
+ // static options
1183
+ } else if (staticOptions !== undefined) {
1184
+ options = Array.isArray(staticOptions) ? staticOptions : [];
937
1185
 
938
1186
  // expression
939
- } else if (valuesExpression) {
940
- if (evaluatedValues && Array.isArray(evaluatedValues)) {
941
- values = evaluatedValues;
1187
+ } else if (optionsExpression) {
1188
+ if (evaluatedOptions && Array.isArray(evaluatedOptions)) {
1189
+ options = evaluatedOptions;
942
1190
  }
943
1191
  } else {
944
- setValuesGetter(buildErrorState('No values source defined in the form definition'));
1192
+ setOptionsGetter(buildErrorState('No options source defined in the form definition'));
945
1193
  return;
946
1194
  }
947
1195
 
948
1196
  // normalize data to support primitives and partially defined objects
949
- values = normalizeValuesData(values);
950
- setValuesGetter(buildLoadedState(values));
951
- }, [valuesKey, staticValues, initialData, valuesExpression, evaluatedValues]);
952
- return valuesGetter;
1197
+ options = normalizeOptionsData(options);
1198
+ setOptionsGetter(buildLoadedState(options));
1199
+ }, [optionsKey, staticOptions, initialData, optionsExpression, evaluatedOptions]);
1200
+ return optionsGetter;
953
1201
  }
954
1202
  const buildErrorState = error => ({
955
- values: [],
1203
+ options: [],
956
1204
  error,
957
- state: LOAD_STATES.ERROR
1205
+ loadState: LOAD_STATES.ERROR
958
1206
  });
959
- const buildLoadedState = values => ({
960
- values,
1207
+ const buildLoadedState = options => ({
1208
+ options,
961
1209
  error: undefined,
962
- state: LOAD_STATES.LOADED
1210
+ loadState: LOAD_STATES.LOADED
963
1211
  });
964
1212
 
965
- // config ///////////////////
1213
+ function useCleanupMultiSelectValues (props) {
1214
+ const {
1215
+ field,
1216
+ options,
1217
+ loadState,
1218
+ onChange,
1219
+ values
1220
+ } = props;
966
1221
 
967
- const MINUTES_IN_DAY = 60 * 24;
968
- const DATETIME_SUBTYPES = {
969
- DATE: 'date',
970
- TIME: 'time',
971
- DATETIME: 'datetime'
972
- };
973
- const TIME_SERIALISING_FORMATS = {
974
- UTC_OFFSET: 'utc_offset',
975
- UTC_NORMALIZED: 'utc_normalized',
976
- NO_TIMEZONE: 'no_timezone'
977
- };
978
- const DATETIME_SUBTYPES_LABELS = {
979
- [DATETIME_SUBTYPES.DATE]: 'Date',
980
- [DATETIME_SUBTYPES.TIME]: 'Time',
981
- [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
982
- };
983
- const TIME_SERIALISINGFORMAT_LABELS = {
984
- [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
985
- [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
986
- [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
987
- };
988
- const DATETIME_SUBTYPE_PATH = ['subtype'];
989
- const DATE_LABEL_PATH = ['dateLabel'];
990
- const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
991
- const TIME_LABEL_PATH = ['timeLabel'];
992
- const TIME_USE24H_PATH = ['use24h'];
993
- const TIME_INTERVAL_PATH = ['timeInterval'];
994
- const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
1222
+ // Ensures that the values are always a subset of the possible options
1223
+ useEffect(() => {
1224
+ if (loadState !== LOAD_STATES.LOADED) {
1225
+ return;
1226
+ }
1227
+ const hasValuesNotInOptions = values.some(v => !options.map(o => o.value).includes(v));
1228
+ if (hasValuesNotInOptions) {
1229
+ onChange({
1230
+ field,
1231
+ value: values.filter(v => options.map(o => o.value).includes(v))
1232
+ });
1233
+ }
1234
+ }, [field, options, onChange, JSON.stringify(values), loadState]);
1235
+ }
995
1236
 
996
1237
  const ENTER_KEYDOWN_EVENT = new KeyboardEvent('keydown', {
997
1238
  code: 'Enter',
@@ -1175,7 +1416,7 @@ function sanitizeSingleSelectValue(options) {
1175
1416
  value
1176
1417
  } = options;
1177
1418
  try {
1178
- const validValues = normalizeValuesData(getValuesData(formField, data)).map(v => v.value);
1419
+ const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
1179
1420
  return validValues.includes(value) ? value : null;
1180
1421
  } catch (error) {
1181
1422
  // use default value in case of formatting error
@@ -1190,7 +1431,7 @@ function sanitizeMultiSelectValue(options) {
1190
1431
  value
1191
1432
  } = options;
1192
1433
  try {
1193
- const validValues = normalizeValuesData(getValuesData(formField, data)).map(v => v.value);
1434
+ const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
1194
1435
  return value.filter(v => validValues.includes(v));
1195
1436
  } catch (error) {
1196
1437
  // use default value in case of formatting error
@@ -1199,20 +1440,21 @@ function sanitizeMultiSelectValue(options) {
1199
1440
  }
1200
1441
  }
1201
1442
 
1202
- const type$b = 'checklist';
1443
+ const type$d = 'checklist';
1203
1444
  function Checklist(props) {
1204
1445
  const {
1205
1446
  disabled,
1206
1447
  errors = [],
1448
+ errorMessageId,
1449
+ domId,
1207
1450
  onBlur,
1208
1451
  onFocus,
1209
1452
  field,
1210
1453
  readonly,
1211
- value = []
1454
+ value: values = []
1212
1455
  } = props;
1213
1456
  const {
1214
1457
  description,
1215
- id,
1216
1458
  label,
1217
1459
  validate = {}
1218
1460
  } = field;
@@ -1221,7 +1463,7 @@ function Checklist(props) {
1221
1463
  required
1222
1464
  } = validate;
1223
1465
  const toggleCheckbox = v => {
1224
- let newValue = [...value];
1466
+ let newValue = [...values];
1225
1467
  if (!newValue.includes(v)) {
1226
1468
  newValue.push(v);
1227
1469
  } else {
@@ -1245,15 +1487,18 @@ function Checklist(props) {
1245
1487
  onFocus && onFocus();
1246
1488
  };
1247
1489
  const {
1248
- state: loadState,
1249
- values: options
1250
- } = useValuesAsync(field);
1251
- const {
1252
- formId
1253
- } = useContext(FormContext$1);
1254
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
1490
+ loadState,
1491
+ options
1492
+ } = useOptionsAsync(field);
1493
+ useCleanupMultiSelectValues({
1494
+ field,
1495
+ loadState,
1496
+ options,
1497
+ values,
1498
+ onChange: props.onChange
1499
+ });
1255
1500
  return jsxs("div", {
1256
- class: classNames(formFieldClasses(type$b, {
1501
+ class: classNames(formFieldClasses(type$d, {
1257
1502
  errors,
1258
1503
  disabled,
1259
1504
  readonly
@@ -1262,27 +1507,28 @@ function Checklist(props) {
1262
1507
  children: [jsx(Label, {
1263
1508
  label: label,
1264
1509
  required: required
1265
- }), loadState == LOAD_STATES.LOADED && options.map((v, index) => {
1510
+ }), loadState == LOAD_STATES.LOADED && options.map((o, index) => {
1511
+ const itemDomId = `${domId}-${index}`;
1266
1512
  return jsx(Label, {
1267
- id: prefixId(`${id}-${index}`, formId),
1268
- label: v.label,
1513
+ id: itemDomId,
1514
+ label: o.label,
1269
1515
  class: classNames({
1270
- 'fjs-checked': value.includes(v.value)
1516
+ 'fjs-checked': values.includes(o.value)
1271
1517
  }),
1272
1518
  required: false,
1273
1519
  children: jsx("input", {
1274
- checked: value.includes(v.value),
1520
+ checked: values.includes(o.value),
1275
1521
  class: "fjs-input",
1276
1522
  disabled: disabled,
1277
1523
  readOnly: readonly,
1278
- id: prefixId(`${id}-${index}`, formId),
1524
+ id: itemDomId,
1279
1525
  type: "checkbox",
1280
- onClick: () => toggleCheckbox(v.value),
1526
+ onClick: () => toggleCheckbox(o.value),
1281
1527
  onBlur: onCheckboxBlur,
1282
1528
  onFocus: onCheckboxFocus,
1283
1529
  "aria-describedby": errorMessageId
1284
1530
  })
1285
- }, `${id}-${index}`);
1531
+ });
1286
1532
  }), jsx(Description, {
1287
1533
  description: description
1288
1534
  }), jsx(Errors, {
@@ -1292,9 +1538,9 @@ function Checklist(props) {
1292
1538
  });
1293
1539
  }
1294
1540
  Checklist.config = {
1295
- type: type$b,
1541
+ type: type$d,
1296
1542
  keyed: true,
1297
- label: 'Checklist',
1543
+ label: 'Checkbox group',
1298
1544
  group: 'selection',
1299
1545
  emptyValue: [],
1300
1546
  sanitizeValue: sanitizeMultiSelectValue,
@@ -1305,6 +1551,7 @@ const noop$1 = () => false;
1305
1551
  function FormField(props) {
1306
1552
  const {
1307
1553
  field,
1554
+ indexes,
1308
1555
  onChange
1309
1556
  } = props;
1310
1557
  const formFields = useService('formFields'),
@@ -1320,14 +1567,19 @@ function FormField(props) {
1320
1567
  } = form._getState();
1321
1568
  const {
1322
1569
  Element,
1323
- Empty,
1570
+ Hidden,
1324
1571
  Column
1325
1572
  } = useContext(FormRenderContext$1);
1573
+ const {
1574
+ formId
1575
+ } = useContext(FormContext$1);
1326
1576
  const FormFieldComponent = formFields.get(field.type);
1327
1577
  if (!FormFieldComponent) {
1328
1578
  throw new Error(`cannot render field <${field.type}>`);
1329
1579
  }
1330
- const valuePath = useMemo(() => pathRegistry.getValuePath(field), [field, pathRegistry]);
1580
+ const valuePath = useMemo(() => pathRegistry.getValuePath(field, {
1581
+ indexes
1582
+ }), [field, indexes, pathRegistry]);
1331
1583
  const initialValue = useMemo(() => get(initialData, valuePath), [initialData, valuePath]);
1332
1584
  const readonly = useReadonly(field, properties);
1333
1585
  const value = get(data, valuePath);
@@ -1336,12 +1588,12 @@ function FormField(props) {
1336
1588
  const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
1337
1589
  const onBlur = useCallback(() => {
1338
1590
  if (viewerCommands) {
1339
- viewerCommands.updateFieldValidation(field, value);
1591
+ viewerCommands.updateFieldValidation(field, value, indexes);
1340
1592
  }
1341
1593
  eventBus.fire('formField.blur', {
1342
1594
  formField: field
1343
1595
  });
1344
- }, [eventBus, viewerCommands, field, value]);
1596
+ }, [eventBus, viewerCommands, field, value, indexes]);
1345
1597
  const onFocus = useCallback(() => {
1346
1598
  eventBus.fire('formField.focus', {
1347
1599
  formField: field
@@ -1349,13 +1601,25 @@ function FormField(props) {
1349
1601
  }, [eventBus, field]);
1350
1602
  useEffect(() => {
1351
1603
  if (viewerCommands && initialValue) {
1352
- viewerCommands.updateFieldValidation(field, initialValue);
1604
+ viewerCommands.updateFieldValidation(field, initialValue, indexes);
1353
1605
  }
1354
- }, [viewerCommands, field, initialValue]);
1606
+ }, [viewerCommands, field, initialValue, JSON.stringify(indexes)]);
1355
1607
  const hidden = useCondition(field.conditional && field.conditional.hide || null);
1608
+ const onChangeIndexed = useCallback(update => {
1609
+ // add indexes of the keyed field to the update, if any
1610
+ onChange(FormFieldComponent.config.keyed ? {
1611
+ ...update,
1612
+ indexes
1613
+ } : update);
1614
+ }, [onChange, FormFieldComponent.config.keyed, indexes]);
1356
1615
  if (hidden) {
1357
- return jsx(Empty, {});
1616
+ return jsx(Hidden, {
1617
+ field: field
1618
+ });
1358
1619
  }
1620
+ const domId = `${prefixId(field.id, formId, indexes)}`;
1621
+ const fieldErrors = get(errors, [field.id, ...Object.values(indexes || {})]) || [];
1622
+ const errorMessageId = errors.length === 0 ? undefined : `${domId}-error-message`;
1359
1623
  return jsx(Column, {
1360
1624
  field: field,
1361
1625
  class: gridColumnClasses(field),
@@ -1365,8 +1629,10 @@ function FormField(props) {
1365
1629
  children: jsx(FormFieldComponent, {
1366
1630
  ...props,
1367
1631
  disabled: disabled,
1368
- errors: errors[field.id],
1369
- onChange: disabled || readonly ? noop$1 : onChange,
1632
+ errors: fieldErrors,
1633
+ errorMessageId: errorMessageId,
1634
+ domId: domId,
1635
+ onChange: disabled || readonly ? noop$1 : onChangeIndexed,
1370
1636
  onBlur: disabled || readonly ? noop$1 : onBlur,
1371
1637
  onFocus: disabled || readonly ? noop$1 : onFocus,
1372
1638
  readonly: readonly,
@@ -1376,26 +1642,109 @@ function FormField(props) {
1376
1642
  });
1377
1643
  }
1378
1644
 
1379
- function Grid(props) {
1645
+ function ChildrenRenderer(props) {
1380
1646
  const {
1381
- Children,
1382
- Row
1647
+ Children
1383
1648
  } = useContext(FormRenderContext$1);
1384
1649
  const {
1385
1650
  field,
1386
1651
  Empty
1387
1652
  } = props;
1388
1653
  const {
1389
- id,
1654
+ id
1655
+ } = field;
1656
+ const repeatRenderManager = useService('repeatRenderManager', false);
1657
+ const isRepeating = repeatRenderManager && repeatRenderManager.isFieldRepeating(id);
1658
+ const Repeater = repeatRenderManager.Repeater;
1659
+ const RepeatFooter = repeatRenderManager.RepeatFooter;
1660
+ return isRepeating ? jsx(RepeatChildrenRenderer, {
1661
+ ...props,
1662
+ ChildrenRoot: Children,
1663
+ Empty,
1664
+ Repeater,
1665
+ RepeatFooter,
1666
+ repeatRenderManager
1667
+ }) : jsx(SimpleChildrenRenderer, {
1668
+ ...props,
1669
+ ChildrenRoot: Children,
1670
+ Empty
1671
+ });
1672
+ }
1673
+ function SimpleChildrenRenderer(props) {
1674
+ const {
1675
+ ChildrenRoot,
1676
+ Empty,
1677
+ field
1678
+ } = props;
1679
+ const {
1390
1680
  components = []
1391
1681
  } = field;
1392
- const formLayouter = useService('formLayouter');
1393
- const formFieldRegistry = useService('formFieldRegistry');
1394
- const rows = formLayouter.getRows(id);
1395
- return jsxs(Children, {
1682
+ const isEmpty = !components.length;
1683
+ return jsxs(ChildrenRoot, {
1396
1684
  class: "fjs-vertical-layout fjs-children cds--grid cds--grid--condensed",
1397
1685
  field: field,
1398
- children: [rows.map(row => {
1686
+ children: [jsx(RowsRenderer, {
1687
+ ...props
1688
+ }), isEmpty ? jsx(Empty, {
1689
+ field: field
1690
+ }) : null]
1691
+ });
1692
+ }
1693
+ function RepeatChildrenRenderer(props) {
1694
+ const {
1695
+ ChildrenRoot,
1696
+ repeatRenderManager,
1697
+ Empty,
1698
+ field,
1699
+ ...restProps
1700
+ } = props;
1701
+ const {
1702
+ components = []
1703
+ } = field;
1704
+ const useSharedState = useState({
1705
+ isCollapsed: true
1706
+ });
1707
+ const Repeater = repeatRenderManager.Repeater;
1708
+ const RepeatFooter = repeatRenderManager.RepeatFooter;
1709
+ return jsxs(Fragment, {
1710
+ children: [jsxs(ChildrenRoot, {
1711
+ class: "fjs-vertical-layout fjs-children cds--grid cds--grid--condensed",
1712
+ field: field,
1713
+ children: [Repeater ? jsx(Repeater, {
1714
+ ...restProps,
1715
+ useSharedState,
1716
+ field,
1717
+ RowsRenderer
1718
+ }) : jsx(RowsRenderer, {
1719
+ ...restProps,
1720
+ field
1721
+ }), !components.length ? jsx(Empty, {
1722
+ field: field
1723
+ }) : null]
1724
+ }), RepeatFooter ? jsx(RepeatFooter, {
1725
+ ...restProps,
1726
+ useSharedState,
1727
+ field
1728
+ }) : null]
1729
+ });
1730
+ }
1731
+ function RowsRenderer(props) {
1732
+ const {
1733
+ field,
1734
+ indexes
1735
+ } = props;
1736
+ const {
1737
+ id: parentId,
1738
+ verticalAlignment = 'start'
1739
+ } = field;
1740
+ const formLayouter = useService('formLayouter');
1741
+ const formFieldRegistry = useService('formFieldRegistry');
1742
+ const rows = formLayouter.getRows(parentId);
1743
+ const {
1744
+ Row
1745
+ } = useContext(FormRenderContext$1);
1746
+ return jsxs(Fragment, {
1747
+ children: [" ", rows.map(row => {
1399
1748
  const {
1400
1749
  components = []
1401
1750
  } = row;
@@ -1405,31 +1754,35 @@ function Grid(props) {
1405
1754
  return jsx(Row, {
1406
1755
  row: row,
1407
1756
  class: "fjs-layout-row cds--row",
1408
- children: components.map(id => {
1409
- const childField = formFieldRegistry.get(id);
1757
+ style: {
1758
+ alignItems: verticalAlignment
1759
+ },
1760
+ children: components.map(childId => {
1761
+ const childField = formFieldRegistry.get(childId);
1410
1762
  if (!childField) {
1411
1763
  return null;
1412
1764
  }
1413
1765
  return createElement(FormField, {
1414
1766
  ...props,
1415
- key: childField.id,
1416
- field: childField
1767
+ key: childId,
1768
+ field: childField,
1769
+ indexes: indexes
1417
1770
  });
1418
1771
  })
1419
1772
  });
1420
- }), components.length ? null : jsx(Empty, {})]
1773
+ }), " "]
1421
1774
  });
1422
1775
  }
1423
1776
 
1424
1777
  function FormComponent$1(props) {
1425
1778
  const {
1426
- EmptyRoot
1779
+ Empty
1427
1780
  } = useContext(FormRenderContext$1);
1428
1781
  const fullProps = {
1429
1782
  ...props,
1430
- Empty: EmptyRoot
1783
+ Empty
1431
1784
  };
1432
- return jsx(Grid, {
1785
+ return jsx(ChildrenRenderer, {
1433
1786
  ...fullProps
1434
1787
  });
1435
1788
  }
@@ -1444,24 +1797,6 @@ FormComponent$1.config = {
1444
1797
  })
1445
1798
  };
1446
1799
 
1447
- var _path$i;
1448
- function _extends$l() { _extends$l = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$l.apply(this, arguments); }
1449
- var SvgCalendar = function SvgCalendar(props) {
1450
- return /*#__PURE__*/React.createElement("svg", _extends$l({
1451
- xmlns: "http://www.w3.org/2000/svg",
1452
- width: 14,
1453
- height: 15,
1454
- fill: "none",
1455
- viewBox: "0 0 28 30"
1456
- }, props), _path$i || (_path$i = /*#__PURE__*/React.createElement("path", {
1457
- fill: "currentColor",
1458
- fillRule: "evenodd",
1459
- d: "M19 2H9V0H7v2H2a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-5V0h-2v2ZM7 7V4H2v5h24V4h-5v3h-2V4H9v3H7Zm-5 4v17h24V11H2Z",
1460
- clipRule: "evenodd"
1461
- })));
1462
- };
1463
- var CalendarIcon = SvgCalendar;
1464
-
1465
1800
  /**
1466
1801
  * Returns date format for the provided locale.
1467
1802
  * If the locale is not provided, uses the browser's locale.
@@ -1530,6 +1865,24 @@ function flatpickerizeDateFormat(dateFormat) {
1530
1865
  return dateFormat;
1531
1866
  }
1532
1867
 
1868
+ var _path$u;
1869
+ function _extends$w() { _extends$w = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$w.apply(this, arguments); }
1870
+ var SvgCalendar = function SvgCalendar(props) {
1871
+ return /*#__PURE__*/React.createElement("svg", _extends$w({
1872
+ xmlns: "http://www.w3.org/2000/svg",
1873
+ width: 14,
1874
+ height: 15,
1875
+ fill: "none",
1876
+ viewBox: "0 0 28 30"
1877
+ }, props), _path$u || (_path$u = /*#__PURE__*/React.createElement("path", {
1878
+ fill: "currentColor",
1879
+ fillRule: "evenodd",
1880
+ d: "M19 2H9V0H7v2H2a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-5V0h-2v2ZM7 7V4H2v5h24V4h-5v3h-2V4H9v3H7Zm-5 4v17h24V11H2Z",
1881
+ clipRule: "evenodd"
1882
+ })));
1883
+ };
1884
+ var CalendarIcon = SvgCalendar;
1885
+
1533
1886
  function InputAdorner(props) {
1534
1887
  const {
1535
1888
  pre,
@@ -1570,12 +1923,11 @@ function InputAdorner(props) {
1570
1923
 
1571
1924
  function Datepicker(props) {
1572
1925
  const {
1573
- id,
1574
1926
  label,
1927
+ domId,
1575
1928
  collapseLabelOnEmpty,
1576
1929
  onDateTimeBlur,
1577
1930
  onDateTimeFocus,
1578
- formId,
1579
1931
  required,
1580
1932
  disabled,
1581
1933
  disallowPassedDates,
@@ -1675,11 +2027,10 @@ function Datepicker(props) {
1675
2027
  }
1676
2028
  onDateTimeBlur(e);
1677
2029
  }, [isInputDirty, onDateTimeBlur]);
1678
- const fullId = `${prefixId(id, formId)}--date`;
1679
2030
  return jsxs("div", {
1680
2031
  class: "fjs-datetime-subsection",
1681
2032
  children: [jsx(Label, {
1682
- id: fullId,
2033
+ id: domId,
1683
2034
  label: label,
1684
2035
  collapseOnEmpty: collapseLabelOnEmpty,
1685
2036
  required: required
@@ -1697,7 +2048,7 @@ function Datepicker(props) {
1697
2048
  children: jsx("input", {
1698
2049
  ref: dateInputRef,
1699
2050
  type: "text",
1700
- id: fullId,
2051
+ id: domId,
1701
2052
  class: "fjs-input",
1702
2053
  disabled: disabled,
1703
2054
  readOnly: readonly,
@@ -1716,19 +2067,19 @@ function Datepicker(props) {
1716
2067
  });
1717
2068
  }
1718
2069
 
1719
- var _path$h, _path2$3;
1720
- function _extends$k() { _extends$k = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$k.apply(this, arguments); }
2070
+ var _path$t, _path2$5;
2071
+ function _extends$v() { _extends$v = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$v.apply(this, arguments); }
1721
2072
  var SvgClock = function SvgClock(props) {
1722
- return /*#__PURE__*/React.createElement("svg", _extends$k({
2073
+ return /*#__PURE__*/React.createElement("svg", _extends$v({
1723
2074
  xmlns: "http://www.w3.org/2000/svg",
1724
2075
  width: 16,
1725
2076
  height: 16,
1726
2077
  fill: "none",
1727
2078
  viewBox: "0 0 28 29"
1728
- }, props), _path$h || (_path$h = /*#__PURE__*/React.createElement("path", {
2079
+ }, props), _path$t || (_path$t = /*#__PURE__*/React.createElement("path", {
1729
2080
  fill: "currentColor",
1730
2081
  d: "M13 14.41 18.59 20 20 18.59l-5-5.01V5h-2v9.41Z"
1731
- })), _path2$3 || (_path2$3 = /*#__PURE__*/React.createElement("path", {
2082
+ })), _path2$5 || (_path2$5 = /*#__PURE__*/React.createElement("path", {
1732
2083
  fill: "currentColor",
1733
2084
  fillRule: "evenodd",
1734
2085
  d: "M6.222 25.64A14 14 0 1 0 21.778 2.36 14 14 0 0 0 6.222 25.64ZM7.333 4.023a12 12 0 1 1 13.334 19.955A12 12 0 0 1 7.333 4.022Z",
@@ -1835,12 +2186,11 @@ function DropdownList(props) {
1835
2186
 
1836
2187
  function Timepicker(props) {
1837
2188
  const {
1838
- id,
1839
2189
  label,
1840
2190
  collapseLabelOnEmpty,
1841
2191
  onDateTimeBlur,
1842
2192
  onDateTimeFocus,
1843
- formId,
2193
+ domId,
1844
2194
  required,
1845
2195
  disabled,
1846
2196
  readonly,
@@ -1949,11 +2299,10 @@ function Timepicker(props) {
1949
2299
  setDropdownIsOpen(false);
1950
2300
  propagateRawToMinute(value);
1951
2301
  };
1952
- const fullId = `${prefixId(id, formId)}--time`;
1953
2302
  return jsxs("div", {
1954
2303
  class: "fjs-datetime-subsection",
1955
2304
  children: [jsx(Label, {
1956
- id: fullId,
2305
+ id: domId,
1957
2306
  label: label,
1958
2307
  collapseOnEmpty: collapseLabelOnEmpty,
1959
2308
  required: required
@@ -1967,7 +2316,7 @@ function Timepicker(props) {
1967
2316
  children: [jsx("input", {
1968
2317
  ref: timeInputRef,
1969
2318
  type: "text",
1970
- id: fullId,
2319
+ id: domId,
1971
2320
  class: "fjs-input",
1972
2321
  value: rawValue,
1973
2322
  disabled: disabled,
@@ -1999,11 +2348,12 @@ function Timepicker(props) {
1999
2348
  });
2000
2349
  }
2001
2350
 
2002
- const type$a = 'datetime';
2351
+ const type$c = 'datetime';
2003
2352
  function Datetime(props) {
2004
2353
  const {
2005
2354
  disabled,
2006
2355
  errors = [],
2356
+ domId,
2007
2357
  onBlur,
2008
2358
  onFocus,
2009
2359
  field,
@@ -2140,12 +2490,11 @@ function Datetime(props) {
2140
2490
  }, []);
2141
2491
  const errorMessageId = allErrors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
2142
2492
  const datePickerProps = {
2143
- id,
2144
2493
  label: dateLabel,
2145
2494
  collapseLabelOnEmpty: !timeLabel,
2146
2495
  onDateTimeBlur,
2147
2496
  onDateTimeFocus,
2148
- formId,
2497
+ domId: `${domId}-date`,
2149
2498
  required,
2150
2499
  disabled,
2151
2500
  disallowPassedDates,
@@ -2155,12 +2504,11 @@ function Datetime(props) {
2155
2504
  'aria-describedby': errorMessageId
2156
2505
  };
2157
2506
  const timePickerProps = {
2158
- id,
2159
2507
  label: timeLabel,
2160
2508
  collapseLabelOnEmpty: !dateLabel,
2161
2509
  onDateTimeBlur,
2162
2510
  onDateTimeFocus,
2163
- formId,
2511
+ domId: `${domId}-time`,
2164
2512
  required,
2165
2513
  disabled,
2166
2514
  readonly,
@@ -2171,7 +2519,7 @@ function Datetime(props) {
2171
2519
  'aria-describedby': errorMessageId
2172
2520
  };
2173
2521
  return jsxs("div", {
2174
- class: formFieldClasses(type$a, {
2522
+ class: formFieldClasses(type$c, {
2175
2523
  errors: allErrors,
2176
2524
  disabled,
2177
2525
  readonly
@@ -2195,7 +2543,7 @@ function Datetime(props) {
2195
2543
  });
2196
2544
  }
2197
2545
  Datetime.config = {
2198
- type: type$a,
2546
+ type: type$c,
2199
2547
  keyed: true,
2200
2548
  label: 'Date time',
2201
2549
  group: 'basic-input',
@@ -2291,7 +2639,7 @@ function PoweredBy(props) {
2291
2639
  setOpen(open);
2292
2640
  };
2293
2641
  }
2294
- return jsxs(Fragment, {
2642
+ return jsxs(Fragment$1, {
2295
2643
  children: [createPortal(jsx(Lightbox, {
2296
2644
  open: open,
2297
2645
  onBackdropClick: toggleOpen(false)
@@ -2324,32 +2672,39 @@ function FormComponent(props) {
2324
2672
  event.preventDefault();
2325
2673
  onReset();
2326
2674
  };
2675
+ const filteredFormData = useFilteredFormData();
2676
+ const localExpressionContext = useMemo(() => ({
2677
+ data: filteredFormData,
2678
+ parent: null,
2679
+ this: filteredFormData,
2680
+ i: []
2681
+ }), [filteredFormData]);
2327
2682
  return jsxs("form", {
2328
2683
  class: "fjs-form",
2329
2684
  onSubmit: handleSubmit,
2330
2685
  onReset: handleReset,
2331
2686
  "aria-label": ariaLabel,
2332
2687
  noValidate: true,
2333
- children: [jsx(FormField, {
2334
- field: schema,
2335
- onChange: onChange
2688
+ children: [jsx(LocalExpressionContext$1.Provider, {
2689
+ value: localExpressionContext,
2690
+ children: jsx(FormField, {
2691
+ field: schema,
2692
+ onChange: onChange
2693
+ })
2336
2694
  }), jsx(PoweredBy, {})]
2337
2695
  });
2338
2696
  }
2339
2697
 
2340
2698
  function Group(props) {
2341
2699
  const {
2342
- field
2700
+ field,
2701
+ domId
2343
2702
  } = props;
2344
2703
  const {
2345
2704
  label,
2346
- id,
2347
2705
  type,
2348
2706
  showOutline
2349
2707
  } = field;
2350
- const {
2351
- formId
2352
- } = useContext(FormContext$1);
2353
2708
  const {
2354
2709
  Empty
2355
2710
  } = useContext(FormRenderContext$1);
@@ -2358,15 +2713,15 @@ function Group(props) {
2358
2713
  Empty
2359
2714
  };
2360
2715
  return jsxs("div", {
2361
- className: classNames(formFieldClasses(type), {
2716
+ className: classNames(formFieldClasses(type), 'fjs-form-field-grouplike', {
2362
2717
  'fjs-outlined': showOutline
2363
2718
  }),
2364
2719
  role: "group",
2365
- "aria-labelledby": prefixId(id, formId),
2720
+ "aria-labelledby": domId,
2366
2721
  children: [jsx(Label, {
2367
- id: prefixId(id, formId),
2722
+ id: domId,
2368
2723
  label: label
2369
- }), jsx(Grid, {
2724
+ }), jsx(ChildrenRenderer, {
2370
2725
  ...fullProps
2371
2726
  })]
2372
2727
  });
@@ -2375,7 +2730,7 @@ Group.config = {
2375
2730
  type: 'group',
2376
2731
  pathed: true,
2377
2732
  label: 'Group',
2378
- group: 'presentation',
2733
+ group: 'container',
2379
2734
  create: (options = {}) => ({
2380
2735
  components: [],
2381
2736
  showOutline: true,
@@ -2389,6 +2744,7 @@ const ALLOWED_NODES = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'em', 'a', 'p
2389
2744
  const ALLOWED_ATTRIBUTES = ['align', 'alt', 'class', 'href', 'id', 'name', 'rel', 'target', 'src'];
2390
2745
  const ALLOWED_URI_PATTERN = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; // eslint-disable-line no-useless-escape
2391
2746
  const ALLOWED_IMAGE_SRC_PATTERN = /^(https?|data):.*/i; // eslint-disable-line no-useless-escape
2747
+ const ALLOWED_IFRAME_SRC_PATTERN = /^(https):\/\/*/i; // eslint-disable-line no-useless-escape
2392
2748
  const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g; // eslint-disable-line no-control-regex
2393
2749
 
2394
2750
  const FORM_ELEMENT = document.createElement('form');
@@ -2429,6 +2785,18 @@ function sanitizeImageSource(src) {
2429
2785
  return valid ? src : '';
2430
2786
  }
2431
2787
 
2788
+ /**
2789
+ * Sanitizes an iframe source to ensure we only allow for links
2790
+ * that start with http(s).
2791
+ *
2792
+ * @param {string} src
2793
+ * @returns {string}
2794
+ */
2795
+ function sanitizeIFrameSource(src) {
2796
+ const valid = ALLOWED_IFRAME_SRC_PATTERN.test(src);
2797
+ return valid ? src : '';
2798
+ }
2799
+
2432
2800
  /**
2433
2801
  * Recursively sanitize a HTML node, potentially
2434
2802
  * removing it, its children or attributes.
@@ -2510,9 +2878,77 @@ function isValidAttribute(lcTag, lcName, value) {
2510
2878
  return true;
2511
2879
  }
2512
2880
 
2513
- function _extends$j() { _extends$j = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$j.apply(this, arguments); }
2881
+ const type$b = 'iframe';
2882
+ const DEFAULT_HEIGHT = 300;
2883
+ function IFrame(props) {
2884
+ const {
2885
+ field,
2886
+ disabled,
2887
+ readonly
2888
+ } = props;
2889
+ const {
2890
+ height = DEFAULT_HEIGHT,
2891
+ id,
2892
+ label,
2893
+ url
2894
+ } = field;
2895
+ const evaluatedUrl = useSingleLineTemplateEvaluation(url, {
2896
+ debug: true
2897
+ });
2898
+ const safeUrl = useMemo(() => sanitizeIFrameSource(evaluatedUrl), [evaluatedUrl]);
2899
+ const evaluatedLabel = useSingleLineTemplateEvaluation(label, {
2900
+ debug: true
2901
+ });
2902
+ const {
2903
+ formId
2904
+ } = useContext(FormContext$1);
2905
+ return jsxs("div", {
2906
+ class: formFieldClasses(type$b, {
2907
+ disabled,
2908
+ readonly
2909
+ }),
2910
+ children: [jsx(Label, {
2911
+ id: prefixId(id, formId),
2912
+ label: evaluatedLabel
2913
+ }), !evaluatedUrl && jsx(IFramePlaceholder, {
2914
+ text: "No content to show."
2915
+ }), evaluatedUrl && safeUrl && jsx("iframe", {
2916
+ src: safeUrl,
2917
+ title: evaluatedLabel,
2918
+ height: height,
2919
+ class: "fjs-iframe",
2920
+ id: prefixId(id, formId),
2921
+ sandbox: ""
2922
+ }), evaluatedUrl && !safeUrl && jsx(IFramePlaceholder, {
2923
+ text: "External content couldn't be loaded."
2924
+ })]
2925
+ });
2926
+ }
2927
+ function IFramePlaceholder(props) {
2928
+ const {
2929
+ text = 'iFrame'
2930
+ } = props;
2931
+ return jsx("div", {
2932
+ class: "fjs-iframe-placeholder",
2933
+ children: jsx("p", {
2934
+ class: "fjs-iframe-placeholder-text",
2935
+ children: text
2936
+ })
2937
+ });
2938
+ }
2939
+ IFrame.config = {
2940
+ type: type$b,
2941
+ keyed: false,
2942
+ label: 'iFrame',
2943
+ group: 'container',
2944
+ create: (options = {}) => ({
2945
+ ...options
2946
+ })
2947
+ };
2948
+
2949
+ function _extends$u() { _extends$u = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$u.apply(this, arguments); }
2514
2950
  var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
2515
- return /*#__PURE__*/React.createElement("svg", _extends$j({
2951
+ return /*#__PURE__*/React.createElement("svg", _extends$u({
2516
2952
  xmlns: "http://www.w3.org/2000/svg",
2517
2953
  xmlSpace: "preserve",
2518
2954
  width: 64,
@@ -2551,7 +2987,7 @@ var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
2551
2987
  };
2552
2988
  var ImagePlaceholder = SvgImagePlaceholder;
2553
2989
 
2554
- const type$9 = 'image';
2990
+ const type$a = 'image';
2555
2991
  function Image(props) {
2556
2992
  const {
2557
2993
  field
@@ -2572,7 +3008,7 @@ function Image(props) {
2572
3008
  formId
2573
3009
  } = useContext(FormContext$1);
2574
3010
  return jsx("div", {
2575
- class: formFieldClasses(type$9),
3011
+ class: formFieldClasses(type$a),
2576
3012
  children: jsxs("div", {
2577
3013
  class: "fjs-image-container",
2578
3014
  children: [safeSource && jsx("img", {
@@ -2590,7 +3026,7 @@ function Image(props) {
2590
3026
  });
2591
3027
  }
2592
3028
  Image.config = {
2593
- type: type$9,
3029
+ type: type$a,
2594
3030
  keyed: false,
2595
3031
  label: 'Image view',
2596
3032
  group: 'presentation',
@@ -2617,14 +3053,14 @@ function TemplatedInputAdorner(props) {
2617
3053
  });
2618
3054
  }
2619
3055
 
2620
- var _path$g;
2621
- function _extends$i() { _extends$i = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$i.apply(this, arguments); }
3056
+ var _path$s;
3057
+ function _extends$t() { _extends$t = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$t.apply(this, arguments); }
2622
3058
  var SvgAngelDown = function SvgAngelDown(props) {
2623
- return /*#__PURE__*/React.createElement("svg", _extends$i({
3059
+ return /*#__PURE__*/React.createElement("svg", _extends$t({
2624
3060
  xmlns: "http://www.w3.org/2000/svg",
2625
3061
  width: 8,
2626
3062
  height: 8
2627
- }, props), _path$g || (_path$g = /*#__PURE__*/React.createElement("path", {
3063
+ }, props), _path$s || (_path$s = /*#__PURE__*/React.createElement("path", {
2628
3064
  fill: "currentColor",
2629
3065
  fillRule: "evenodd",
2630
3066
  stroke: "currentColor",
@@ -2635,14 +3071,14 @@ var SvgAngelDown = function SvgAngelDown(props) {
2635
3071
  };
2636
3072
  var AngelDownIcon = SvgAngelDown;
2637
3073
 
2638
- var _path$f;
2639
- function _extends$h() { _extends$h = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$h.apply(this, arguments); }
3074
+ var _path$r;
3075
+ function _extends$s() { _extends$s = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$s.apply(this, arguments); }
2640
3076
  var SvgAngelUp = function SvgAngelUp(props) {
2641
- return /*#__PURE__*/React.createElement("svg", _extends$h({
3077
+ return /*#__PURE__*/React.createElement("svg", _extends$s({
2642
3078
  xmlns: "http://www.w3.org/2000/svg",
2643
3079
  width: 8,
2644
3080
  height: 8
2645
- }, props), _path$f || (_path$f = /*#__PURE__*/React.createElement("path", {
3081
+ }, props), _path$r || (_path$r = /*#__PURE__*/React.createElement("path", {
2646
3082
  fill: "currentColor",
2647
3083
  fillRule: "evenodd",
2648
3084
  stroke: "currentColor",
@@ -2678,11 +3114,13 @@ function isNullEquivalentValue(value) {
2678
3114
  return value === undefined || value === null || value === '';
2679
3115
  }
2680
3116
 
2681
- const type$8 = 'number';
3117
+ const type$9 = 'number';
2682
3118
  function Numberfield(props) {
2683
3119
  const {
2684
3120
  disabled,
2685
3121
  errors = [],
3122
+ errorMessageId,
3123
+ domId,
2686
3124
  onBlur,
2687
3125
  onFocus,
2688
3126
  field,
@@ -2692,7 +3130,6 @@ function Numberfield(props) {
2692
3130
  } = props;
2693
3131
  const {
2694
3132
  description,
2695
- id,
2696
3133
  label,
2697
3134
  appearance = {},
2698
3135
  validate = {},
@@ -2812,18 +3249,14 @@ function Numberfield(props) {
2812
3249
  e.preventDefault();
2813
3250
  }
2814
3251
  };
2815
- const {
2816
- formId
2817
- } = useContext(FormContext$1);
2818
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
2819
3252
  return jsxs("div", {
2820
- class: formFieldClasses(type$8, {
3253
+ class: formFieldClasses(type$9, {
2821
3254
  errors,
2822
3255
  disabled,
2823
3256
  readonly
2824
3257
  }),
2825
3258
  children: [jsx(Label, {
2826
- id: prefixId(id, formId),
3259
+ id: domId,
2827
3260
  label: label,
2828
3261
  required: required
2829
3262
  }), jsx(TemplatedInputAdorner, {
@@ -2843,7 +3276,7 @@ function Numberfield(props) {
2843
3276
  class: "fjs-input",
2844
3277
  disabled: disabled,
2845
3278
  readOnly: readonly,
2846
- id: prefixId(id, formId),
3279
+ id: domId,
2847
3280
  onKeyDown: onKeyDown,
2848
3281
  onKeyPress: onKeyPress,
2849
3282
  onBlur: () => onBlur && onBlur(),
@@ -2890,7 +3323,7 @@ function Numberfield(props) {
2890
3323
  });
2891
3324
  }
2892
3325
  Numberfield.config = {
2893
- type: type$8,
3326
+ type: type$9,
2894
3327
  keyed: true,
2895
3328
  label: 'Number',
2896
3329
  group: 'basic-input',
@@ -2899,13 +3332,10 @@ Numberfield.config = {
2899
3332
  value,
2900
3333
  formField
2901
3334
  }) => {
2902
- // null state is allowed
2903
- if (isNullEquivalentValue(value)) return null;
2904
-
2905
- // if data cannot be parsed as a valid number, go into invalid NaN state
2906
- if (!isValidNumber(value)) return 'NaN';
3335
+ // invalid value types are sanitized to null
3336
+ if (isNullEquivalentValue(value) || !isValidNumber(value)) return null;
2907
3337
 
2908
- // otherwise parse to formatting type
3338
+ // otherwise, we return a string or a number depending on the form field configuration
2909
3339
  return formField.serializeToString ? value.toString() : Number(value);
2910
3340
  },
2911
3341
  create: (options = {}) => ({
@@ -2913,11 +3343,37 @@ Numberfield.config = {
2913
3343
  })
2914
3344
  };
2915
3345
 
2916
- const type$7 = 'radio';
3346
+ function useCleanupSingleSelectValue (props) {
3347
+ const {
3348
+ field,
3349
+ options,
3350
+ loadState,
3351
+ onChange,
3352
+ value
3353
+ } = props;
3354
+
3355
+ // Ensures that the value is always one of the possible options
3356
+ useEffect(() => {
3357
+ if (loadState !== LOAD_STATES.LOADED) {
3358
+ return;
3359
+ }
3360
+ const hasValueNotInOptions = value && !options.map(o => o.value).includes(value);
3361
+ if (hasValueNotInOptions) {
3362
+ onChange({
3363
+ field,
3364
+ value: null
3365
+ });
3366
+ }
3367
+ }, [field, options, onChange, value, loadState]);
3368
+ }
3369
+
3370
+ const type$8 = 'radio';
2917
3371
  function Radio(props) {
2918
3372
  const {
2919
3373
  disabled,
2920
3374
  errors = [],
3375
+ errorMessageId,
3376
+ domId,
2921
3377
  onBlur,
2922
3378
  onFocus,
2923
3379
  field,
@@ -2926,7 +3382,6 @@ function Radio(props) {
2926
3382
  } = props;
2927
3383
  const {
2928
3384
  description,
2929
- id,
2930
3385
  label,
2931
3386
  validate = {}
2932
3387
  } = field;
@@ -2953,15 +3408,18 @@ function Radio(props) {
2953
3408
  onFocus && onFocus();
2954
3409
  };
2955
3410
  const {
2956
- state: loadState,
2957
- values: options
2958
- } = useValuesAsync(field);
2959
- const {
2960
- formId
2961
- } = useContext(FormContext$1);
2962
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3411
+ loadState,
3412
+ options
3413
+ } = useOptionsAsync(field);
3414
+ useCleanupSingleSelectValue({
3415
+ field,
3416
+ loadState,
3417
+ options,
3418
+ value,
3419
+ onChange: props.onChange
3420
+ });
2963
3421
  return jsxs("div", {
2964
- class: formFieldClasses(type$7, {
3422
+ class: formFieldClasses(type$8, {
2965
3423
  errors,
2966
3424
  disabled,
2967
3425
  readonly
@@ -2971,8 +3429,9 @@ function Radio(props) {
2971
3429
  label: label,
2972
3430
  required: required
2973
3431
  }), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
3432
+ const itemDomId = `${domId}-${index}`;
2974
3433
  return jsx(Label, {
2975
- id: prefixId(`${id}-${index}`, formId),
3434
+ id: itemDomId,
2976
3435
  label: option.label,
2977
3436
  class: classNames({
2978
3437
  'fjs-checked': option.value === value
@@ -2983,14 +3442,14 @@ function Radio(props) {
2983
3442
  class: "fjs-input",
2984
3443
  disabled: disabled,
2985
3444
  readOnly: readonly,
2986
- id: prefixId(`${id}-${index}`, formId),
3445
+ id: itemDomId,
2987
3446
  type: "radio",
2988
3447
  onClick: () => onChange(option.value),
2989
3448
  onBlur: onRadioBlur,
2990
3449
  onFocus: onRadioFocus,
2991
3450
  "aria-describedby": errorMessageId
2992
3451
  })
2993
- }, `${id}-${index}`);
3452
+ }, index);
2994
3453
  }), jsx(Description, {
2995
3454
  description: description
2996
3455
  }), jsx(Errors, {
@@ -3000,23 +3459,23 @@ function Radio(props) {
3000
3459
  });
3001
3460
  }
3002
3461
  Radio.config = {
3003
- type: type$7,
3462
+ type: type$8,
3004
3463
  keyed: true,
3005
- label: 'Radio',
3464
+ label: 'Radio group',
3006
3465
  group: 'selection',
3007
3466
  emptyValue: null,
3008
3467
  sanitizeValue: sanitizeSingleSelectValue,
3009
3468
  create: createEmptyOptions
3010
3469
  };
3011
3470
 
3012
- var _path$e;
3013
- function _extends$g() { _extends$g = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$g.apply(this, arguments); }
3471
+ var _path$q;
3472
+ function _extends$r() { _extends$r = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$r.apply(this, arguments); }
3014
3473
  var SvgXMark = function SvgXMark(props) {
3015
- return /*#__PURE__*/React.createElement("svg", _extends$g({
3474
+ return /*#__PURE__*/React.createElement("svg", _extends$r({
3016
3475
  xmlns: "http://www.w3.org/2000/svg",
3017
3476
  width: 8,
3018
3477
  height: 8
3019
- }, props), _path$e || (_path$e = /*#__PURE__*/React.createElement("path", {
3478
+ }, props), _path$q || (_path$q = /*#__PURE__*/React.createElement("path", {
3020
3479
  fill: "currentColor",
3021
3480
  fillRule: "evenodd",
3022
3481
  stroke: "currentColor",
@@ -3029,7 +3488,7 @@ var XMarkIcon = SvgXMark;
3029
3488
 
3030
3489
  function SearchableSelect(props) {
3031
3490
  const {
3032
- id,
3491
+ domId,
3033
3492
  disabled,
3034
3493
  errors,
3035
3494
  onBlur,
@@ -3038,9 +3497,6 @@ function SearchableSelect(props) {
3038
3497
  readonly,
3039
3498
  value
3040
3499
  } = props;
3041
- const {
3042
- formId
3043
- } = useContext(FormContext$1);
3044
3500
  const [filter, setFilter] = useState('');
3045
3501
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3046
3502
  const [shouldApplyFilter, setShouldApplyFilter] = useState(true);
@@ -3048,9 +3504,16 @@ function SearchableSelect(props) {
3048
3504
  const searchbarRef = useRef();
3049
3505
  const eventBus = useService('eventBus');
3050
3506
  const {
3051
- state: loadState,
3052
- values: options
3053
- } = useValuesAsync(field);
3507
+ loadState,
3508
+ options
3509
+ } = useOptionsAsync(field);
3510
+ useCleanupSingleSelectValue({
3511
+ field,
3512
+ loadState,
3513
+ options,
3514
+ value,
3515
+ onChange: props.onChange
3516
+ });
3054
3517
 
3055
3518
  // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
3056
3519
  const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
@@ -3140,9 +3603,8 @@ function SearchableSelect(props) {
3140
3603
  setFilter(valueLabel);
3141
3604
  onBlur && onBlur();
3142
3605
  }, [onBlur, valueLabel]);
3143
- return jsxs(Fragment$1, {
3606
+ return jsxs(Fragment, {
3144
3607
  children: [jsxs("div", {
3145
- id: prefixId(`${id}`, formId),
3146
3608
  class: classNames('fjs-input-group', {
3147
3609
  'disabled': disabled,
3148
3610
  'readonly': readonly
@@ -3154,7 +3616,7 @@ function SearchableSelect(props) {
3154
3616
  readOnly: readonly,
3155
3617
  class: "fjs-input",
3156
3618
  ref: searchbarRef,
3157
- id: prefixId(`${id}-search`, formId),
3619
+ id: domId,
3158
3620
  onChange: onInputChange,
3159
3621
  type: "text",
3160
3622
  value: filter,
@@ -3194,7 +3656,7 @@ function SearchableSelect(props) {
3194
3656
 
3195
3657
  function SimpleSelect(props) {
3196
3658
  const {
3197
- id,
3659
+ domId,
3198
3660
  disabled,
3199
3661
  errors,
3200
3662
  onBlur,
@@ -3203,16 +3665,20 @@ function SimpleSelect(props) {
3203
3665
  readonly,
3204
3666
  value
3205
3667
  } = props;
3206
- const {
3207
- formId
3208
- } = useContext(FormContext$1);
3209
3668
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3210
3669
  const selectRef = useRef();
3211
3670
  const inputRef = useRef();
3212
3671
  const {
3213
- state: loadState,
3214
- values: options
3215
- } = useValuesAsync(field);
3672
+ loadState,
3673
+ options
3674
+ } = useOptionsAsync(field);
3675
+ useCleanupSingleSelectValue({
3676
+ field,
3677
+ loadState,
3678
+ options,
3679
+ value,
3680
+ onChange: props.onChange
3681
+ });
3216
3682
 
3217
3683
  // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
3218
3684
  const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
@@ -3255,10 +3721,9 @@ function SimpleSelect(props) {
3255
3721
  onBlur && onBlur();
3256
3722
  }
3257
3723
  }, [onBlur, readonly]);
3258
- return jsxs(Fragment$1, {
3724
+ return jsxs(Fragment, {
3259
3725
  children: [jsxs("div", {
3260
3726
  ref: selectRef,
3261
- id: prefixId(`${id}`, formId),
3262
3727
  class: classNames('fjs-input-group', {
3263
3728
  disabled,
3264
3729
  readonly
@@ -3272,11 +3737,11 @@ function SimpleSelect(props) {
3272
3737
  class: classNames('fjs-select-display', {
3273
3738
  'fjs-select-placeholder': !value
3274
3739
  }),
3275
- id: prefixId(`${id}-display`, formId),
3740
+ id: `${domId}-display`,
3276
3741
  children: valueLabel || 'Select'
3277
3742
  }), !disabled && jsx("input", {
3278
3743
  ref: inputRef,
3279
- id: prefixId(`${id}-search`, formId),
3744
+ id: domId,
3280
3745
  class: "fjs-select-hidden-input",
3281
3746
  value: valueLabel,
3282
3747
  onFocus: onInputFocus,
@@ -3309,11 +3774,13 @@ function SimpleSelect(props) {
3309
3774
  });
3310
3775
  }
3311
3776
 
3312
- const type$6 = 'select';
3777
+ const type$7 = 'select';
3313
3778
  function Select(props) {
3314
3779
  const {
3315
3780
  disabled,
3316
3781
  errors = [],
3782
+ errorMessageId,
3783
+ domId,
3317
3784
  onBlur,
3318
3785
  onFocus,
3319
3786
  field,
@@ -3323,7 +3790,6 @@ function Select(props) {
3323
3790
  } = props;
3324
3791
  const {
3325
3792
  description,
3326
- id,
3327
3793
  label,
3328
3794
  searchable = false,
3329
3795
  validate = {}
@@ -3331,12 +3797,8 @@ function Select(props) {
3331
3797
  const {
3332
3798
  required
3333
3799
  } = validate;
3334
- const {
3335
- formId
3336
- } = useContext(FormContext$1);
3337
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3338
- const selectProps = useMemo(() => ({
3339
- id,
3800
+ const selectProps = {
3801
+ domId,
3340
3802
  disabled,
3341
3803
  errors,
3342
3804
  onBlur,
@@ -3346,9 +3808,9 @@ function Select(props) {
3346
3808
  onChange,
3347
3809
  readonly,
3348
3810
  'aria-describedby': errorMessageId
3349
- }), [disabled, errors, field, id, value, onChange, onBlur, onFocus, readonly, errorMessageId]);
3811
+ };
3350
3812
  return jsxs("div", {
3351
- class: formFieldClasses(type$6, {
3813
+ class: formFieldClasses(type$7, {
3352
3814
  errors,
3353
3815
  disabled,
3354
3816
  readonly
@@ -3360,7 +3822,7 @@ function Select(props) {
3360
3822
  }
3361
3823
  },
3362
3824
  children: [jsx(Label, {
3363
- id: prefixId(`${id}-search`, formId),
3825
+ id: domId,
3364
3826
  label: label,
3365
3827
  required: required
3366
3828
  }), searchable ? jsx(SearchableSelect, {
@@ -3376,7 +3838,7 @@ function Select(props) {
3376
3838
  });
3377
3839
  }
3378
3840
  Select.config = {
3379
- type: type$6,
3841
+ type: type$7,
3380
3842
  keyed: true,
3381
3843
  label: 'Select',
3382
3844
  group: 'selection',
@@ -3385,15 +3847,15 @@ Select.config = {
3385
3847
  create: createEmptyOptions
3386
3848
  };
3387
3849
 
3388
- const type$5 = 'separator';
3850
+ const type$6 = 'separator';
3389
3851
  function Separator() {
3390
3852
  return jsx("div", {
3391
- class: formFieldClasses(type$5),
3853
+ class: formFieldClasses(type$6),
3392
3854
  children: jsx("hr", {})
3393
3855
  });
3394
3856
  }
3395
3857
  Separator.config = {
3396
- type: type$5,
3858
+ type: type$6,
3397
3859
  keyed: false,
3398
3860
  label: 'Separator',
3399
3861
  group: 'presentation',
@@ -3402,7 +3864,7 @@ Separator.config = {
3402
3864
  })
3403
3865
  };
3404
3866
 
3405
- const type$4 = 'spacer';
3867
+ const type$5 = 'spacer';
3406
3868
  function Spacer(props) {
3407
3869
  const {
3408
3870
  field
@@ -3411,14 +3873,14 @@ function Spacer(props) {
3411
3873
  height = 60
3412
3874
  } = field;
3413
3875
  return jsx("div", {
3414
- class: formFieldClasses(type$4),
3876
+ class: formFieldClasses(type$5),
3415
3877
  style: {
3416
3878
  height: height
3417
3879
  }
3418
3880
  });
3419
3881
  }
3420
3882
  Spacer.config = {
3421
- type: type$4,
3883
+ type: type$5,
3422
3884
  keyed: false,
3423
3885
  label: 'Spacer',
3424
3886
  group: 'presentation',
@@ -3428,33 +3890,85 @@ Spacer.config = {
3428
3890
  })
3429
3891
  };
3430
3892
 
3431
- function SkipLink(props) {
3893
+ function DynamicList(props) {
3432
3894
  const {
3433
- className,
3434
- label,
3435
- onSkip
3895
+ field,
3896
+ domId,
3897
+ readonly
3436
3898
  } = props;
3437
- const onKeyDown = useCallback(event => {
3438
- if (event.key === 'Enter') {
3439
- event.preventDefault();
3440
- event.stopPropagation();
3441
- onSkip();
3442
- }
3443
- }, [onSkip]);
3444
- return jsx("a", {
3445
- href: "#",
3446
- class: classNames('fjs-skip-link', className),
3447
- onKeyDown: onKeyDown,
3448
- children: label
3449
- });
3450
- }
3451
-
3452
- const type$3 = 'taglist';
3453
- function Taglist(props) {
3899
+ const {
3900
+ label,
3901
+ type,
3902
+ showOutline
3903
+ } = field;
3904
+ const {
3905
+ Empty
3906
+ } = useContext(FormRenderContext$1);
3907
+ const fullProps = {
3908
+ ...props,
3909
+ Empty
3910
+ };
3911
+ return jsxs("div", {
3912
+ className: classNames(formFieldClasses(type, {
3913
+ readonly
3914
+ }), 'fjs-form-field-grouplike', {
3915
+ 'fjs-outlined': showOutline
3916
+ }),
3917
+ role: "group",
3918
+ "aria-labelledby": domId,
3919
+ children: [jsx(Label, {
3920
+ id: domId,
3921
+ label: label
3922
+ }), jsx(ChildrenRenderer, {
3923
+ ...fullProps
3924
+ })]
3925
+ });
3926
+ }
3927
+ DynamicList.config = {
3928
+ type: 'dynamiclist',
3929
+ pathed: true,
3930
+ repeatable: true,
3931
+ label: 'Dynamic list',
3932
+ group: 'container',
3933
+ create: (options = {}) => ({
3934
+ components: [],
3935
+ showOutline: true,
3936
+ isRepeating: true,
3937
+ allowAddRemove: true,
3938
+ defaultRepetitions: 1,
3939
+ ...options
3940
+ })
3941
+ };
3942
+
3943
+ function SkipLink(props) {
3944
+ const {
3945
+ className,
3946
+ label,
3947
+ onSkip
3948
+ } = props;
3949
+ const onKeyDown = useCallback(event => {
3950
+ if (event.key === 'Enter') {
3951
+ event.preventDefault();
3952
+ event.stopPropagation();
3953
+ onSkip();
3954
+ }
3955
+ }, [onSkip]);
3956
+ return jsx("a", {
3957
+ href: "#",
3958
+ class: classNames('fjs-skip-link', className),
3959
+ onKeyDown: onKeyDown,
3960
+ children: label
3961
+ });
3962
+ }
3963
+
3964
+ const type$4 = 'taglist';
3965
+ function Taglist(props) {
3454
3966
  const {
3455
3967
  disabled,
3456
3968
  errors = [],
3969
+ errorMessageId,
3457
3970
  onFocus,
3971
+ domId,
3458
3972
  onBlur,
3459
3973
  field,
3460
3974
  readonly,
@@ -3462,46 +3976,43 @@ function Taglist(props) {
3462
3976
  } = props;
3463
3977
  const {
3464
3978
  description,
3465
- id,
3466
3979
  label,
3467
3980
  validate = {}
3468
3981
  } = field;
3469
3982
  const {
3470
3983
  required
3471
3984
  } = validate;
3472
- const {
3473
- formId
3474
- } = useContext(FormContext$1);
3475
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3476
3985
  const [filter, setFilter] = useState('');
3477
- const [filteredOptions, setFilteredOptions] = useState([]);
3478
3986
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3479
- const [hasOptionsLeft, setHasOptionsLeft] = useState(true);
3480
3987
  const [isEscapeClosed, setIsEscapeClose] = useState(false);
3481
3988
  const focusScopeRef = useRef();
3482
3989
  const inputRef = useRef();
3483
3990
  const eventBus = useService('eventBus');
3484
3991
  const {
3485
- state: loadState,
3486
- values: options
3487
- } = useValuesAsync(field);
3992
+ loadState,
3993
+ options
3994
+ } = useOptionsAsync(field);
3995
+ useCleanupMultiSelectValues({
3996
+ field,
3997
+ loadState,
3998
+ options,
3999
+ values,
4000
+ onChange: props.onChange
4001
+ });
3488
4002
 
3489
4003
  // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
3490
4004
  const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3491
4005
  [o.value]: options[x]
3492
4006
  }))), [options]);
4007
+ const hasOptionsLeft = useMemo(() => options.length > values.length, [options.length, values.length]);
3493
4008
 
3494
4009
  // Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array
3495
- useEffect(() => {
3496
- if (loadState === LOAD_STATES.LOADED) {
3497
- setFilteredOptions(options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value)));
3498
- } else {
3499
- setFilteredOptions([]);
4010
+ const filteredOptions = useMemo(() => {
4011
+ if (loadState !== LOAD_STATES.LOADED) {
4012
+ return [];
3500
4013
  }
3501
- }, [filter, JSON.stringify(values), options, loadState]);
3502
- useEffect(() => {
3503
- setHasOptionsLeft(options.length > values.length);
3504
- }, [options.length, values.length]);
4014
+ return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value));
4015
+ }, [filter, options, JSON.stringify(values), loadState]);
3505
4016
  const selectValue = value => {
3506
4017
  if (filter) {
3507
4018
  setFilter('');
@@ -3593,7 +4104,7 @@ function Taglist(props) {
3593
4104
  const shouldDisplayDropdown = useMemo(() => !disabled && loadState === LOAD_STATES.LOADED && isDropdownExpanded && !isEscapeClosed, [disabled, isDropdownExpanded, isEscapeClosed, loadState]);
3594
4105
  return jsxs("div", {
3595
4106
  ref: focusScopeRef,
3596
- class: formFieldClasses(type$3, {
4107
+ class: formFieldClasses(type$4, {
3597
4108
  errors,
3598
4109
  disabled,
3599
4110
  readonly
@@ -3607,7 +4118,7 @@ function Taglist(props) {
3607
4118
  children: [jsx(Label, {
3608
4119
  label: label,
3609
4120
  required: required,
3610
- id: prefixId(`${id}-search`, formId)
4121
+ id: domId
3611
4122
  }), !disabled && !readonly && !!values.length && jsx(SkipLink, {
3612
4123
  className: "fjs-taglist-skip-link",
3613
4124
  label: "Skip to search",
@@ -3628,7 +4139,7 @@ function Taglist(props) {
3628
4139
  onMouseDown: e => e.preventDefault(),
3629
4140
  children: [jsx("span", {
3630
4141
  class: "fjs-taglist-tag-label",
3631
- children: valueToOptionMap[v] ? valueToOptionMap[v].label : `unexpected value{${v}}`
4142
+ children: valueToOptionMap[v] ? valueToOptionMap[v].label : undefined
3632
4143
  }), !disabled && !readonly && jsx("button", {
3633
4144
  type: "button",
3634
4145
  title: "Remove tag",
@@ -3645,7 +4156,7 @@ function Taglist(props) {
3645
4156
  readOnly: readonly,
3646
4157
  class: "fjs-taglist-input",
3647
4158
  ref: inputRef,
3648
- id: prefixId(`${id}-search`, formId),
4159
+ id: domId,
3649
4160
  onChange: onInputChange,
3650
4161
  type: "text",
3651
4162
  value: filter,
@@ -3675,7 +4186,7 @@ function Taglist(props) {
3675
4186
  });
3676
4187
  }
3677
4188
  Taglist.config = {
3678
- type: type$3,
4189
+ type: type$4,
3679
4190
  keyed: true,
3680
4191
  label: 'Tag list',
3681
4192
  group: 'selection',
@@ -3684,7 +4195,7 @@ Taglist.config = {
3684
4195
  create: createEmptyOptions
3685
4196
  };
3686
4197
 
3687
- const type$2 = 'text';
4198
+ const type$3 = 'text';
3688
4199
  function Text(props) {
3689
4200
  const form = useService('form');
3690
4201
  const {
@@ -3711,7 +4222,7 @@ function Text(props) {
3711
4222
  const html = markdownRenderer.render(markdown);
3712
4223
  return sanitizeHTML(html);
3713
4224
  }, [markdownRenderer, markdown]);
3714
- const OverridenTargetLink = useMemo(() => BuildOverridenTargetLink(textLinkTarget), [textLinkTarget]);
4225
+ const OverriddenTargetLink = useMemo(() => BuildOverriddenTargetLink(textLinkTarget), [textLinkTarget]);
3715
4226
  const componentOverrides = useMemo(() => {
3716
4227
  if (disableLinks) {
3717
4228
  return {
@@ -3720,13 +4231,13 @@ function Text(props) {
3720
4231
  }
3721
4232
  if (textLinkTarget) {
3722
4233
  return {
3723
- 'a': OverridenTargetLink
4234
+ 'a': OverriddenTargetLink
3724
4235
  };
3725
4236
  }
3726
4237
  return {};
3727
- }, [disableLinks, OverridenTargetLink, textLinkTarget]);
4238
+ }, [disableLinks, OverriddenTargetLink, textLinkTarget]);
3728
4239
  return jsx("div", {
3729
- class: formFieldClasses(type$2),
4240
+ class: formFieldClasses(type$3),
3730
4241
  children: jsx(Markup, {
3731
4242
  markup: safeHtml,
3732
4243
  components: componentOverrides,
@@ -3735,7 +4246,7 @@ function Text(props) {
3735
4246
  });
3736
4247
  }
3737
4248
  Text.config = {
3738
- type: type$2,
4249
+ type: type$3,
3739
4250
  keyed: false,
3740
4251
  label: 'Text view',
3741
4252
  group: 'presentation',
@@ -3744,7 +4255,7 @@ Text.config = {
3744
4255
  ...options
3745
4256
  })
3746
4257
  };
3747
- function BuildOverridenTargetLink(target) {
4258
+ function BuildOverriddenTargetLink(target) {
3748
4259
  return function ({
3749
4260
  children,
3750
4261
  ...rest
@@ -3768,11 +4279,13 @@ function DisabledLink({
3768
4279
  });
3769
4280
  }
3770
4281
 
3771
- const type$1 = 'textfield';
4282
+ const type$2 = 'textfield';
3772
4283
  function Textfield(props) {
3773
4284
  const {
3774
4285
  disabled,
3775
4286
  errors = [],
4287
+ errorMessageId,
4288
+ domId,
3776
4289
  onBlur,
3777
4290
  onFocus,
3778
4291
  field,
@@ -3781,7 +4294,6 @@ function Textfield(props) {
3781
4294
  } = props;
3782
4295
  const {
3783
4296
  description,
3784
- id,
3785
4297
  label,
3786
4298
  appearance = {},
3787
4299
  validate = {}
@@ -3801,18 +4313,14 @@ function Textfield(props) {
3801
4313
  value: target.value
3802
4314
  });
3803
4315
  };
3804
- const {
3805
- formId
3806
- } = useContext(FormContext$1);
3807
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3808
4316
  return jsxs("div", {
3809
- class: formFieldClasses(type$1, {
4317
+ class: formFieldClasses(type$2, {
3810
4318
  errors,
3811
4319
  disabled,
3812
4320
  readonly
3813
4321
  }),
3814
4322
  children: [jsx(Label, {
3815
- id: prefixId(id, formId),
4323
+ id: domId,
3816
4324
  label: label,
3817
4325
  required: required
3818
4326
  }), jsx(TemplatedInputAdorner, {
@@ -3824,7 +4332,7 @@ function Textfield(props) {
3824
4332
  class: "fjs-input",
3825
4333
  disabled: disabled,
3826
4334
  readOnly: readonly,
3827
- id: prefixId(id, formId),
4335
+ id: domId,
3828
4336
  onInput: onChange,
3829
4337
  onBlur: () => onBlur && onBlur(),
3830
4338
  onFocus: () => onFocus && onFocus(),
@@ -3841,7 +4349,7 @@ function Textfield(props) {
3841
4349
  });
3842
4350
  }
3843
4351
  Textfield.config = {
3844
- type: type$1,
4352
+ type: type$2,
3845
4353
  keyed: true,
3846
4354
  label: 'Text field',
3847
4355
  group: 'basic-input',
@@ -3849,7 +4357,7 @@ Textfield.config = {
3849
4357
  sanitizeValue: ({
3850
4358
  value
3851
4359
  }) => {
3852
- if (isArray(value) || isObject(value)) {
4360
+ if (isArray(value) || isObject(value) || isNil(value)) {
3853
4361
  return '';
3854
4362
  }
3855
4363
 
@@ -3864,11 +4372,13 @@ Textfield.config = {
3864
4372
  })
3865
4373
  };
3866
4374
 
3867
- const type = 'textarea';
4375
+ const type$1 = 'textarea';
3868
4376
  function Textarea(props) {
3869
4377
  const {
3870
4378
  disabled,
3871
4379
  errors = [],
4380
+ errorMessageId,
4381
+ domId,
3872
4382
  onBlur,
3873
4383
  onFocus,
3874
4384
  field,
@@ -3877,7 +4387,6 @@ function Textarea(props) {
3877
4387
  } = props;
3878
4388
  const {
3879
4389
  description,
3880
- id,
3881
4390
  label,
3882
4391
  validate = {}
3883
4392
  } = field;
@@ -3899,25 +4408,21 @@ function Textarea(props) {
3899
4408
  useEffect(() => {
3900
4409
  autoSizeTextarea(textareaRef.current);
3901
4410
  }, []);
3902
- const {
3903
- formId
3904
- } = useContext(FormContext$1);
3905
- const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3906
4411
  return jsxs("div", {
3907
- class: formFieldClasses(type, {
4412
+ class: formFieldClasses(type$1, {
3908
4413
  errors,
3909
4414
  disabled,
3910
4415
  readonly
3911
4416
  }),
3912
4417
  children: [jsx(Label, {
3913
- id: prefixId(id, formId),
4418
+ id: domId,
3914
4419
  label: label,
3915
4420
  required: required
3916
4421
  }), jsx("textarea", {
3917
4422
  class: "fjs-textarea",
3918
4423
  disabled: disabled,
3919
4424
  readonly: readonly,
3920
- id: prefixId(id, formId),
4425
+ id: domId,
3921
4426
  onInput: onInput,
3922
4427
  onBlur: () => onBlur && onBlur(),
3923
4428
  onFocus: () => onFocus && onFocus(),
@@ -3933,14 +4438,14 @@ function Textarea(props) {
3933
4438
  });
3934
4439
  }
3935
4440
  Textarea.config = {
3936
- type,
4441
+ type: type$1,
3937
4442
  keyed: true,
3938
4443
  label: 'Text area',
3939
4444
  group: 'basic-input',
3940
4445
  emptyValue: '',
3941
4446
  sanitizeValue: ({
3942
4447
  value
3943
- }) => isArray(value) || isObject(value) ? '' : String(value),
4448
+ }) => isArray(value) || isObject(value) || isNil(value) ? '' : String(value),
3944
4449
  create: (options = {}) => ({
3945
4450
  ...options
3946
4451
  })
@@ -3964,84 +4469,419 @@ const autoSizeTextarea = textarea => {
3964
4469
  textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
3965
4470
  };
3966
4471
 
3967
- var _path$d;
3968
- function _extends$f() { _extends$f = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$f.apply(this, arguments); }
4472
+ var _path$p;
4473
+ function _extends$q() { _extends$q = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$q.apply(this, arguments); }
4474
+ var SvgArrowDown = function SvgArrowDown(props) {
4475
+ return /*#__PURE__*/React.createElement("svg", _extends$q({
4476
+ xmlns: "http://www.w3.org/2000/svg",
4477
+ viewBox: "0 0 32 32"
4478
+ }, props), _path$p || (_path$p = /*#__PURE__*/React.createElement("path", {
4479
+ fill: "currentcolor",
4480
+ d: "M24.59 16.59 17 24.17V4h-2v20.17l-7.59-7.58L6 18l10 10 10-10-1.41-1.41z"
4481
+ })));
4482
+ };
4483
+ var ArrowDownIcon = SvgArrowDown;
4484
+
4485
+ var _path$o;
4486
+ function _extends$p() { _extends$p = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$p.apply(this, arguments); }
4487
+ var SvgArrowUp = function SvgArrowUp(props) {
4488
+ return /*#__PURE__*/React.createElement("svg", _extends$p({
4489
+ xmlns: "http://www.w3.org/2000/svg",
4490
+ viewBox: "0 0 32 32"
4491
+ }, props), _path$o || (_path$o = /*#__PURE__*/React.createElement("path", {
4492
+ fill: "currentcolor",
4493
+ d: "M16 4 6 14l1.41 1.41L15 7.83V28h2V7.83l7.59 7.58L26 14 16 4z"
4494
+ })));
4495
+ };
4496
+ var ArrowUpIcon = SvgArrowUp;
4497
+
4498
+ var _path$n;
4499
+ function _extends$o() { _extends$o = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$o.apply(this, arguments); }
4500
+ var SvgCaretLeft = function SvgCaretLeft(props) {
4501
+ return /*#__PURE__*/React.createElement("svg", _extends$o({
4502
+ xmlns: "http://www.w3.org/2000/svg",
4503
+ xmlSpace: "preserve",
4504
+ viewBox: "0 0 32 32"
4505
+ }, props), _path$n || (_path$n = /*#__PURE__*/React.createElement("path", {
4506
+ fill: "currentcolor",
4507
+ d: "m20 24-10-8 10-8z"
4508
+ })));
4509
+ };
4510
+ var CaretLeftIcon = SvgCaretLeft;
4511
+
4512
+ var _path$m;
4513
+ function _extends$n() { _extends$n = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$n.apply(this, arguments); }
4514
+ var SvgCaretRight = function SvgCaretRight(props) {
4515
+ return /*#__PURE__*/React.createElement("svg", _extends$n({
4516
+ xmlns: "http://www.w3.org/2000/svg",
4517
+ xmlSpace: "preserve",
4518
+ viewBox: "0 0 32 32"
4519
+ }, props), _path$m || (_path$m = /*#__PURE__*/React.createElement("path", {
4520
+ fill: "currentcolor",
4521
+ d: "m12 8 10 8-10 8z"
4522
+ })));
4523
+ };
4524
+ var CaretRightIcon = SvgCaretRight;
4525
+
4526
+ const type = 'table';
4527
+
4528
+ /**
4529
+ * @typedef {('asc'|'desc')} Direction
4530
+ *
4531
+ * @typedef Sorting
4532
+ * @property {string} key
4533
+ * @property {Direction} direction
4534
+ *
4535
+ * @typedef Column
4536
+ * @property {string} label
4537
+ * @property {string} key
4538
+ *
4539
+ * @typedef Props
4540
+ * @property {Object} field
4541
+ * @property {string} field.id
4542
+ * @property {Array<Column>} [field.columns]
4543
+ * @property {string} [field.columnsExpression]
4544
+ * @property {string} [field.label]
4545
+ * @property {number} [field.rowCount]
4546
+ * @property {string} [field.dataSource]
4547
+ *
4548
+ * @param {Props} props
4549
+ * @returns {import("preact").JSX.Element}
4550
+ */
4551
+ function Table(props) {
4552
+ const {
4553
+ field
4554
+ } = props;
4555
+ const {
4556
+ columns = [],
4557
+ columnsExpression,
4558
+ dataSource = '',
4559
+ rowCount,
4560
+ id,
4561
+ label
4562
+ } = field;
4563
+
4564
+ /** @type {[(null|Sorting), import("preact/hooks").StateUpdater<null|Sorting>]} */
4565
+ const [sortBy, setSortBy] = useState(null);
4566
+ const evaluatedColumns = useEvaluatedColumns(columnsExpression || '', columns);
4567
+ const columnKeys = evaluatedColumns.map(({
4568
+ key
4569
+ }) => key);
4570
+ const evaluatedDataSource = useExpressionEvaluation(dataSource);
4571
+ const data = Array.isArray(evaluatedDataSource) ? evaluatedDataSource : [];
4572
+ const sortedData = sortBy === null ? data : sortByColumn(data, sortBy.key, sortBy.direction);
4573
+
4574
+ /** @type {unknown[][]} */
4575
+ const chunkedData = isNumber(rowCount) ? chunk(sortedData, rowCount) : [sortedData];
4576
+ const [currentPage, setCurrentPage] = useState(0);
4577
+ const currentChunk = chunkedData[currentPage] || [];
4578
+ useEffect(() => {
4579
+ setCurrentPage(0);
4580
+ }, [rowCount, sortBy]);
4581
+
4582
+ /** @param {string} key */
4583
+ function toggleSortBy(key) {
4584
+ setSortBy(current => {
4585
+ if (current === null || current.key !== key) {
4586
+ return {
4587
+ key,
4588
+ direction: 'asc'
4589
+ };
4590
+ }
4591
+ if (current.direction === 'desc') {
4592
+ return null;
4593
+ }
4594
+ return {
4595
+ key,
4596
+ direction: 'desc'
4597
+ };
4598
+ });
4599
+ }
4600
+ return jsxs("div", {
4601
+ class: formFieldClasses(type),
4602
+ children: [jsx(Label, {
4603
+ id: prefixId(id),
4604
+ label: label
4605
+ }), jsxs("div", {
4606
+ class: classNames('fjs-table-middle-container', {
4607
+ 'fjs-table-empty': evaluatedColumns.length === 0
4608
+ }),
4609
+ children: [evaluatedColumns.length === 0 ? 'Nothing to show.' : jsx("div", {
4610
+ class: "fjs-table-inner-container",
4611
+ children: jsxs("table", {
4612
+ class: "fjs-table",
4613
+ id: prefixId(id),
4614
+ children: [jsx("thead", {
4615
+ class: "fjs-table-head",
4616
+ children: jsx("tr", {
4617
+ class: "fjs-table-tr",
4618
+ children: evaluatedColumns.map(({
4619
+ key,
4620
+ label
4621
+ }) => {
4622
+ const displayLabel = label || key;
4623
+ return jsx("th", {
4624
+ tabIndex: 0,
4625
+ class: "fjs-table-th",
4626
+ onClick: () => {
4627
+ toggleSortBy(key);
4628
+ },
4629
+ onKeyDown: event => {
4630
+ if (['Enter', 'Space'].includes(event.code)) {
4631
+ toggleSortBy(key);
4632
+ }
4633
+ },
4634
+ "aria-label": getHeaderAriaLabel(sortBy, key, displayLabel),
4635
+ children: jsxs("span", {
4636
+ class: "fjs-table-th-label",
4637
+ children: [displayLabel, sortBy !== null && sortBy.key === key ? jsx(Fragment, {
4638
+ children: sortBy.direction === 'asc' ? jsx(ArrowUpIcon, {
4639
+ class: "fjs-table-sort-icon-asc"
4640
+ }) : jsx(ArrowDownIcon, {
4641
+ class: "fjs-table-sort-icon-desc"
4642
+ })
4643
+ }) : null]
4644
+ })
4645
+ }, key);
4646
+ })
4647
+ })
4648
+ }), currentChunk.length === 0 ? jsx("tbody", {
4649
+ class: "fjs-table-body",
4650
+ children: jsx("tr", {
4651
+ class: "fjs-table-tr",
4652
+ children: jsx("td", {
4653
+ class: "fjs-table-td",
4654
+ colSpan: evaluatedColumns.length,
4655
+ children: "Nothing to show."
4656
+ })
4657
+ })
4658
+ }) : jsx("tbody", {
4659
+ class: "fjs-table-body",
4660
+ children: currentChunk.map((row, index) => jsx("tr", {
4661
+ class: "fjs-table-tr",
4662
+ children: columnKeys.map(key => jsx("td", {
4663
+ class: "fjs-table-td",
4664
+ children: row[key]
4665
+ }, key))
4666
+ }, index))
4667
+ })]
4668
+ })
4669
+ }), isNumber(rowCount) && chunkedData.length > 1 && evaluatedColumns.length > 0 ? jsxs("nav", {
4670
+ class: "fjs-table-nav",
4671
+ children: [jsxs("span", {
4672
+ class: "fjs-table-nav-label",
4673
+ children: [currentPage + 1, " of ", chunkedData.length]
4674
+ }), jsx("button", {
4675
+ type: "button",
4676
+ class: "fjs-table-nav-button",
4677
+ onClick: () => {
4678
+ setCurrentPage(page => Math.max(page - 1, 0));
4679
+ },
4680
+ disabled: currentPage === 0,
4681
+ "aria-label": "Previous page",
4682
+ children: jsx(CaretLeftIcon, {})
4683
+ }), jsx("button", {
4684
+ type: "button",
4685
+ class: "fjs-table-nav-button",
4686
+ onClick: () => {
4687
+ setCurrentPage(page => Math.min(page + 1, chunkedData.length - 1));
4688
+ },
4689
+ disabled: currentPage >= chunkedData.length - 1,
4690
+ "aria-label": "Next page",
4691
+ children: jsx(CaretRightIcon, {})
4692
+ })]
4693
+ }) : null]
4694
+ })]
4695
+ });
4696
+ }
4697
+ Table.config = {
4698
+ type,
4699
+ keyed: false,
4700
+ label: 'Table',
4701
+ group: 'presentation',
4702
+ create: (options = {}) => {
4703
+ const {
4704
+ id,
4705
+ columnsExpression,
4706
+ columns,
4707
+ rowCount,
4708
+ ...remainingOptions
4709
+ } = options;
4710
+ if (isDefined(id) && isNumber(rowCount)) {
4711
+ remainingOptions['rowCount'] = rowCount;
4712
+ }
4713
+ if (isString(columnsExpression)) {
4714
+ return {
4715
+ ...remainingOptions,
4716
+ id,
4717
+ columnsExpression
4718
+ };
4719
+ }
4720
+ if (Array.isArray(columns) && columns.every(isColumn)) {
4721
+ return {
4722
+ ...remainingOptions,
4723
+ id,
4724
+ columns
4725
+ };
4726
+ }
4727
+ return {
4728
+ ...remainingOptions,
4729
+ rowCount: 10,
4730
+ columns: [{
4731
+ label: 'ID',
4732
+ key: 'id'
4733
+ }, {
4734
+ label: 'Name',
4735
+ key: 'name'
4736
+ }, {
4737
+ label: 'Date',
4738
+ key: 'date'
4739
+ }]
4740
+ };
4741
+ },
4742
+ initialDemoData: [{
4743
+ id: 1,
4744
+ name: 'John Doe',
4745
+ date: '31.01.2023'
4746
+ }, {
4747
+ id: 2,
4748
+ name: 'Erika Muller',
4749
+ date: '20.02.2023'
4750
+ }, {
4751
+ id: 3,
4752
+ name: 'Dominic Leaf',
4753
+ date: '11.03.2023'
4754
+ }]
4755
+ };
4756
+
4757
+ // helpers /////////////////////////////
4758
+
4759
+ /**
4760
+ * @param {string|void} columnsExpression
4761
+ * @param {Column[]} fallbackColumns
4762
+ * @returns {Column[]}
4763
+ */
4764
+ function useEvaluatedColumns(columnsExpression, fallbackColumns) {
4765
+ /** @type {Column[]|null} */
4766
+ const evaluation = useExpressionEvaluation(columnsExpression || '');
4767
+ return Array.isArray(evaluation) && evaluation.every(isColumn) ? evaluation : fallbackColumns;
4768
+ }
4769
+
4770
+ /**
4771
+ * @param {any} column
4772
+ * @returns {column is Column}
4773
+ */
4774
+ function isColumn(column) {
4775
+ return isObject(column) && isString(column['label']) && isString(column['key']);
4776
+ }
4777
+
4778
+ /**
4779
+ * @param {Array} array
4780
+ * @param {number} size
4781
+ * @returns {Array}
4782
+ */
4783
+ function chunk(array, size) {
4784
+ return array.reduce((chunks, item, index) => {
4785
+ if (index % size === 0) {
4786
+ chunks.push([item]);
4787
+ } else {
4788
+ chunks[chunks.length - 1].push(item);
4789
+ }
4790
+ return chunks;
4791
+ }, []);
4792
+ }
4793
+
4794
+ /**
4795
+ * @param {unknown[]} array
4796
+ * @param {string} key
4797
+ * @param {Direction} direction
4798
+ * @returns {unknown[]}
4799
+ */
4800
+ function sortByColumn(array, key, direction) {
4801
+ return [...array].sort((a, b) => {
4802
+ if (!isObject(a) || !isObject(b)) {
4803
+ return 0;
4804
+ }
4805
+ if (direction === 'asc') {
4806
+ return a[key] > b[key] ? 1 : -1;
4807
+ }
4808
+ return a[key] < b[key] ? 1 : -1;
4809
+ });
4810
+ }
4811
+
4812
+ /**
4813
+ * @param {null|Sorting} sortBy
4814
+ * @param {string} key
4815
+ * @param {string} label
4816
+ */
4817
+ function getHeaderAriaLabel(sortBy, key, label) {
4818
+ if (sortBy === null || sortBy.key !== key) {
4819
+ return `Click to sort by ${label} descending`;
4820
+ }
4821
+ if (sortBy.direction === 'asc') {
4822
+ return 'Click to remove sorting';
4823
+ }
4824
+ return `Click to sort by ${label} ascending`;
4825
+ }
4826
+
4827
+ var _path$l;
4828
+ function _extends$m() { _extends$m = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$m.apply(this, arguments); }
3969
4829
  var SvgButton = function SvgButton(props) {
3970
- return /*#__PURE__*/React.createElement("svg", _extends$f({
4830
+ return /*#__PURE__*/React.createElement("svg", _extends$m({
3971
4831
  xmlns: "http://www.w3.org/2000/svg",
3972
4832
  width: 54,
3973
4833
  height: 54,
3974
4834
  fill: "currentcolor"
3975
- }, props), _path$d || (_path$d = /*#__PURE__*/React.createElement("path", {
4835
+ }, props), _path$l || (_path$l = /*#__PURE__*/React.createElement("path", {
3976
4836
  fillRule: "evenodd",
3977
4837
  d: "M45 17a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V20a3 3 0 0 1 3-3h36zm-9 8.889H18v2.222h18v-2.222z"
3978
4838
  })));
3979
4839
  };
3980
4840
  var ButtonIcon = SvgButton;
3981
4841
 
3982
- var _path$c;
3983
- function _extends$e() { _extends$e = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$e.apply(this, arguments); }
4842
+ var _path$k;
4843
+ function _extends$l() { _extends$l = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$l.apply(this, arguments); }
3984
4844
  var SvgCheckbox = function SvgCheckbox(props) {
3985
- return /*#__PURE__*/React.createElement("svg", _extends$e({
4845
+ return /*#__PURE__*/React.createElement("svg", _extends$l({
3986
4846
  xmlns: "http://www.w3.org/2000/svg",
3987
4847
  width: 54,
3988
4848
  height: 54,
3989
4849
  fill: "currentcolor"
3990
- }, props), _path$c || (_path$c = /*#__PURE__*/React.createElement("path", {
4850
+ }, props), _path$k || (_path$k = /*#__PURE__*/React.createElement("path", {
3991
4851
  d: "M34 18H20a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V20a2 2 0 0 0-2-2zm-9 14-5-5 1.41-1.41L25 29.17l7.59-7.59L34 23l-9 9z"
3992
4852
  })));
3993
4853
  };
3994
4854
  var CheckboxIcon = SvgCheckbox;
3995
4855
 
3996
- var _g, _use, _use2, _use3, _defs;
3997
- function _extends$d() { _extends$d = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$d.apply(this, arguments); }
4856
+ var _path$j;
4857
+ function _extends$k() { _extends$k = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$k.apply(this, arguments); }
3998
4858
  var SvgChecklist = function SvgChecklist(props) {
3999
- return /*#__PURE__*/React.createElement("svg", _extends$d({
4859
+ return /*#__PURE__*/React.createElement("svg", _extends$k({
4000
4860
  xmlns: "http://www.w3.org/2000/svg",
4001
- xmlnsXlink: "http://www.w3.org/1999/xlink",
4002
4861
  width: 54,
4003
4862
  height: 54,
4004
- fill: "currentcolor"
4005
- }, props), _g || (_g = /*#__PURE__*/React.createElement("g", {
4006
- fillRule: "evenodd"
4007
- }, /*#__PURE__*/React.createElement("use", {
4008
- xlinkHref: "#Checklist_svg__a"
4009
- }), /*#__PURE__*/React.createElement("use", {
4010
- xlinkHref: "#Checklist_svg__a",
4011
- y: 24
4012
- }), /*#__PURE__*/React.createElement("use", {
4013
- xlinkHref: "#Checklist_svg__a",
4014
- y: 12
4015
- }))), _use || (_use = /*#__PURE__*/React.createElement("use", {
4016
- xlinkHref: "#Checklist_svg__b"
4017
- })), _use2 || (_use2 = /*#__PURE__*/React.createElement("use", {
4018
- xlinkHref: "#Checklist_svg__b",
4019
- y: 12
4020
- })), _use3 || (_use3 = /*#__PURE__*/React.createElement("use", {
4021
- xlinkHref: "#Checklist_svg__b",
4022
- y: 24
4023
- })), _defs || (_defs = /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
4024
- id: "Checklist_svg__a",
4025
- d: "M18 12h-6v6h6v-6zm-6-2a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2h-6z"
4026
- }), /*#__PURE__*/React.createElement("path", {
4027
- id: "Checklist_svg__b",
4028
- d: "M23 14.5a1 1 0 0 1 1-1h19a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1v-1z"
4029
- }))));
4863
+ fill: "none"
4864
+ }, props), _path$j || (_path$j = /*#__PURE__*/React.createElement("path", {
4865
+ fill: "currentColor",
4866
+ fillRule: "evenodd",
4867
+ d: "M14.35 24.75H19v4.65h-4.65v-4.65Zm-1.414-1.414a2 2 0 0 1 1.414-.586H19a2 2 0 0 1 2 2v4.65a2 2 0 0 1-2 2h-4.65a2 2 0 0 1-2-2v-4.65a2 2 0 0 1 .586-1.414ZM14.35 37.05H19v4.65h-4.65v-4.65Zm-1.414-1.414a2 2 0 0 1 1.414-.586H19a2 2 0 0 1 2 2v4.65a2 2 0 0 1-2 2h-4.65a2 2 0 0 1-2-2v-4.65a2 2 0 0 1 .586-1.414ZM14.35 12.45H19v4.65h-4.65v-4.65Zm-1.414-1.414a2 2 0 0 1 1.414-.586H19a2 2 0 0 1 2 2v4.65a2 2 0 0 1-2 2h-4.65a2 2 0 0 1-2-2v-4.65a2 2 0 0 1 .586-1.414Zm12.007 14.977a1 1 0 0 0-.293.707v.65a1 1 0 0 0 1 1h15a1 1 0 0 0 1-1v-.65a1 1 0 0 0-1-1h-15a1 1 0 0 0-.707.293Zm0 12.3a1 1 0 0 0-.293.707v.65a1 1 0 0 0 1 1h15a1 1 0 0 0 1-1v-.65a1 1 0 0 0-1-1h-15a1 1 0 0 0-.707.293Zm0-24.6a1 1 0 0 0-.293.707v.65a1 1 0 0 0 1 1h15a1 1 0 0 0 1-1v-.65a1 1 0 0 0-1-1h-15a1 1 0 0 0-.707.293Z",
4868
+ clipRule: "evenodd"
4869
+ })));
4030
4870
  };
4031
4871
  var ChecklistIcon = SvgChecklist;
4032
4872
 
4033
- var _path$b, _path2$2, _path3;
4034
- function _extends$c() { _extends$c = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$c.apply(this, arguments); }
4873
+ var _path$i, _path2$4, _path3;
4874
+ function _extends$j() { _extends$j = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$j.apply(this, arguments); }
4035
4875
  var SvgDatetime = function SvgDatetime(props) {
4036
- return /*#__PURE__*/React.createElement("svg", _extends$c({
4876
+ return /*#__PURE__*/React.createElement("svg", _extends$j({
4037
4877
  xmlns: "http://www.w3.org/2000/svg",
4038
4878
  width: 54,
4039
4879
  height: 54,
4040
4880
  fill: "currentcolor"
4041
- }, props), _path$b || (_path$b = /*#__PURE__*/React.createElement("path", {
4881
+ }, props), _path$i || (_path$i = /*#__PURE__*/React.createElement("path", {
4042
4882
  fillRule: "evenodd",
4043
4883
  d: "M37.908 13.418h-5.004v-2.354h-1.766v2.354H21.13v-2.354h-1.766v2.354H14.36a2.07 2.07 0 0 0-2.06 2.06v23.549a2.07 2.07 0 0 0 2.06 2.06h6.77v-1.766h-6.358a.707.707 0 0 1-.706-.706V15.89c0-.39.316-.707.706-.707h4.592v2.355h1.766v-2.355h10.008v2.355h1.766v-2.355h4.592a.71.71 0 0 1 .707.707v6.358h1.765v-6.77c0-1.133-.927-2.06-2.06-2.06z"
4044
- })), _path2$2 || (_path2$2 = /*#__PURE__*/React.createElement("path", {
4884
+ })), _path2$4 || (_path2$4 = /*#__PURE__*/React.createElement("path", {
4045
4885
  d: "m35.13 37.603 1.237-1.237-3.468-3.475v-5.926h-1.754v6.654l3.984 3.984Z"
4046
4886
  })), _path3 || (_path3 = /*#__PURE__*/React.createElement("path", {
4047
4887
  fillRule: "evenodd",
@@ -4050,27 +4890,27 @@ var SvgDatetime = function SvgDatetime(props) {
4050
4890
  };
4051
4891
  var DatetimeIcon = SvgDatetime;
4052
4892
 
4053
- var _path$a, _path2$1;
4054
- function _extends$b() { _extends$b = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$b.apply(this, arguments); }
4893
+ var _path$h, _path2$3;
4894
+ function _extends$i() { _extends$i = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$i.apply(this, arguments); }
4055
4895
  var SvgTaglist = function SvgTaglist(props) {
4056
- return /*#__PURE__*/React.createElement("svg", _extends$b({
4896
+ return /*#__PURE__*/React.createElement("svg", _extends$i({
4057
4897
  xmlns: "http://www.w3.org/2000/svg",
4058
4898
  width: 54,
4059
4899
  height: 54,
4060
4900
  fill: "currentcolor"
4061
- }, props), _path$a || (_path$a = /*#__PURE__*/React.createElement("path", {
4901
+ }, props), _path$h || (_path$h = /*#__PURE__*/React.createElement("path", {
4062
4902
  fillRule: "evenodd",
4063
4903
  d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36Zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1Z"
4064
- })), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
4904
+ })), _path2$3 || (_path2$3 = /*#__PURE__*/React.createElement("path", {
4065
4905
  d: "M11 22a1 1 0 0 1 1-1h19a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H12a1 1 0 0 1-1-1V22Z"
4066
4906
  })));
4067
4907
  };
4068
4908
  var TaglistIcon = SvgTaglist;
4069
4909
 
4070
4910
  var _rect, _rect2, _rect3;
4071
- function _extends$a() { _extends$a = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$a.apply(this, arguments); }
4911
+ function _extends$h() { _extends$h = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$h.apply(this, arguments); }
4072
4912
  var SvgForm = function SvgForm(props) {
4073
- return /*#__PURE__*/React.createElement("svg", _extends$a({
4913
+ return /*#__PURE__*/React.createElement("svg", _extends$h({
4074
4914
  xmlns: "http://www.w3.org/2000/svg",
4075
4915
  width: 54,
4076
4916
  height: 54
@@ -4096,152 +4936,189 @@ var SvgForm = function SvgForm(props) {
4096
4936
  };
4097
4937
  var FormIcon = SvgForm;
4098
4938
 
4099
- var _path$9;
4100
- function _extends$9() { _extends$9 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$9.apply(this, arguments); }
4939
+ var _path$g;
4940
+ function _extends$g() { _extends$g = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$g.apply(this, arguments); }
4101
4941
  var SvgGroup = function SvgGroup(props) {
4102
- return /*#__PURE__*/React.createElement("svg", _extends$9({
4942
+ return /*#__PURE__*/React.createElement("svg", _extends$g({
4103
4943
  xmlns: "http://www.w3.org/2000/svg",
4104
4944
  width: 54,
4105
4945
  height: 54,
4106
4946
  fill: "currentcolor"
4107
- }, props), _path$9 || (_path$9 = /*#__PURE__*/React.createElement("path", {
4947
+ }, props), _path$g || (_path$g = /*#__PURE__*/React.createElement("path", {
4108
4948
  fillRule: "evenodd",
4109
4949
  d: "M8 33v5a1 1 0 0 0 1 1h4v2H9a3 3 0 0 1-3-3v-5h2Zm18 6v2H15v-2h11Zm13 0v2H28v-2h11Zm9-6v5a3 3 0 0 1-3 3h-4v-2h4a1 1 0 0 0 .993-.883L46 38v-5h2ZM8 22v9H6v-9h2Zm40 0v9h-2v-9h2Zm-35-9v2H9a1 1 0 0 0-.993.883L8 16v4H6v-4a3 3 0 0 1 3-3h4Zm32 0a3 3 0 0 1 3 3v4h-2v-4a1 1 0 0 0-.883-.993L45 15h-4v-2h4Zm-6 0v2H28v-2h11Zm-13 0v2H15v-2h11Z"
4110
4950
  })));
4111
4951
  };
4112
4952
  var GroupIcon = SvgGroup;
4113
4953
 
4114
- var _path$8;
4115
- function _extends$8() { _extends$8 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$8.apply(this, arguments); }
4954
+ var _path$f;
4955
+ function _extends$f() { _extends$f = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$f.apply(this, arguments); }
4116
4956
  var SvgNumber = function SvgNumber(props) {
4117
- return /*#__PURE__*/React.createElement("svg", _extends$8({
4957
+ return /*#__PURE__*/React.createElement("svg", _extends$f({
4118
4958
  xmlns: "http://www.w3.org/2000/svg",
4119
4959
  width: 54,
4120
4960
  height: 54,
4121
4961
  fill: "currentcolor"
4122
- }, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
4962
+ }, props), _path$f || (_path$f = /*#__PURE__*/React.createElement("path", {
4123
4963
  fillRule: "evenodd",
4124
4964
  d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1zM35 28.444h7l-3.5 4-3.5-4zM35 26h7l-3.5-4-3.5 4z"
4125
4965
  })));
4126
4966
  };
4127
4967
  var NumberIcon = SvgNumber;
4128
4968
 
4129
- var _path$7;
4130
- function _extends$7() { _extends$7 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$7.apply(this, arguments); }
4969
+ var _path$e;
4970
+ function _extends$e() { _extends$e = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$e.apply(this, arguments); }
4131
4971
  var SvgRadio = function SvgRadio(props) {
4132
- return /*#__PURE__*/React.createElement("svg", _extends$7({
4972
+ return /*#__PURE__*/React.createElement("svg", _extends$e({
4133
4973
  xmlns: "http://www.w3.org/2000/svg",
4134
4974
  width: 54,
4135
4975
  height: 54,
4136
4976
  fill: "currentcolor"
4137
- }, props), _path$7 || (_path$7 = /*#__PURE__*/React.createElement("path", {
4977
+ }, props), _path$e || (_path$e = /*#__PURE__*/React.createElement("path", {
4138
4978
  d: "M27 22c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10zm0 18a8 8 0 1 1 0-16 8 8 0 1 1 0 16z"
4139
4979
  })));
4140
4980
  };
4141
4981
  var RadioIcon = SvgRadio;
4142
4982
 
4143
- var _path$6;
4144
- function _extends$6() { _extends$6 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$6.apply(this, arguments); }
4983
+ var _path$d;
4984
+ function _extends$d() { _extends$d = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$d.apply(this, arguments); }
4145
4985
  var SvgSelect = function SvgSelect(props) {
4146
- return /*#__PURE__*/React.createElement("svg", _extends$6({
4986
+ return /*#__PURE__*/React.createElement("svg", _extends$d({
4147
4987
  xmlns: "http://www.w3.org/2000/svg",
4148
4988
  width: 54,
4149
4989
  height: 54,
4150
4990
  fill: "currentcolor"
4151
- }, props), _path$6 || (_path$6 = /*#__PURE__*/React.createElement("path", {
4991
+ }, props), _path$d || (_path$d = /*#__PURE__*/React.createElement("path", {
4152
4992
  fillRule: "evenodd",
4153
4993
  d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1zm-12 7h9l-4.5 6-4.5-6z"
4154
4994
  })));
4155
4995
  };
4156
4996
  var SelectIcon = SvgSelect;
4157
4997
 
4158
- var _path$5;
4159
- function _extends$5() { _extends$5 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$5.apply(this, arguments); }
4998
+ var _path$c;
4999
+ function _extends$c() { _extends$c = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$c.apply(this, arguments); }
4160
5000
  var SvgSeparator = function SvgSeparator(props) {
4161
- return /*#__PURE__*/React.createElement("svg", _extends$5({
5001
+ return /*#__PURE__*/React.createElement("svg", _extends$c({
4162
5002
  xmlns: "http://www.w3.org/2000/svg",
4163
5003
  width: 54,
4164
5004
  height: 54,
4165
5005
  fill: "none"
4166
- }, props), _path$5 || (_path$5 = /*#__PURE__*/React.createElement("path", {
5006
+ }, props), _path$c || (_path$c = /*#__PURE__*/React.createElement("path", {
4167
5007
  fill: "currentColor",
4168
5008
  d: "M26.293 16.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L27 18.414l-3.293 3.293a1 1 0 0 1-1.414-1.414l4-4ZM9 26h36v2H9v-2Zm13.293 7.707 4 4a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L27 35.586l-3.293-3.293a1 1 0 0 0-1.414 1.414Z"
4169
5009
  })));
4170
5010
  };
4171
5011
  var SeparatorIcon = SvgSeparator;
4172
5012
 
4173
- var _path$4;
4174
- function _extends$4() { _extends$4 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$4.apply(this, arguments); }
5013
+ var _path$b;
5014
+ function _extends$b() { _extends$b = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$b.apply(this, arguments); }
4175
5015
  var SvgSpacer = function SvgSpacer(props) {
4176
- return /*#__PURE__*/React.createElement("svg", _extends$4({
5016
+ return /*#__PURE__*/React.createElement("svg", _extends$b({
4177
5017
  xmlns: "http://www.w3.org/2000/svg",
4178
5018
  width: 54,
4179
5019
  height: 54,
4180
5020
  fill: "none"
4181
- }, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
5021
+ }, props), _path$b || (_path$b = /*#__PURE__*/React.createElement("path", {
4182
5022
  fill: "currentColor",
4183
5023
  d: "M9 15v2h36v-2H9Zm0 22v2h36v-2H9Zm17.293-17.707a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L27 21.414l-3.293 3.293a1 1 0 0 1-1.414-1.414l4-4Zm-4 11.414 4 4a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L27 32.586l-3.293-3.293a1 1 0 0 0-1.414 1.414Z"
4184
5024
  })));
4185
5025
  };
4186
5026
  var SpacerIcon = SvgSpacer;
4187
5027
 
4188
- var _path$3;
4189
- function _extends$3() { _extends$3 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$3.apply(this, arguments); }
5028
+ var _path$a;
5029
+ function _extends$a() { _extends$a = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$a.apply(this, arguments); }
5030
+ var SvgDynamicList = function SvgDynamicList(props) {
5031
+ return /*#__PURE__*/React.createElement("svg", _extends$a({
5032
+ xmlns: "http://www.w3.org/2000/svg",
5033
+ width: 54,
5034
+ height: 54,
5035
+ fill: "none"
5036
+ }, props), _path$a || (_path$a = /*#__PURE__*/React.createElement("path", {
5037
+ fill: "currentColor",
5038
+ fillRule: "evenodd",
5039
+ d: "M2.7 43.296v1.254c0 .746.604 1.35 1.35 1.35h1.275v-1.795c.049.14.075.29.075.445v-1.254h-.075V43.2H4.05c.177 0 .347.034.502.096H2.7Zm2.7-2.507v-2.507H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.015V23.24H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.014V13.21H2.7v2.507h2.7Zm-2.7-5.014h1.852a1.346 1.346 0 0 1-.502.096h1.275v-.096H5.4V9.45c0 .156-.026.306-.075.445V8.1H4.05A1.35 1.35 0 0 0 2.7 9.45v1.254Zm5.175.096h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1-2.7v1.795a1.348 1.348 0 0 1-.075-.445v1.254h.075v.096h1.275c-.177 0-.347-.034-.502-.096H51.3V9.45a1.35 1.35 0 0 0-1.35-1.35h-1.275Zm-.075 5.11v2.508h2.7V13.21h-2.7Zm0 5.015v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7V23.24h-2.7Zm0 5.015v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7v-2.507h-2.7Zm2.7 5.014h-1.852c.155-.062.325-.096.502-.096h-1.275v.096H48.6v1.254c0-.156.026-.305.075-.445V45.9h1.275a1.35 1.35 0 0 0 1.35-1.35v-1.254Zm-5.175-.096h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7ZM16.2 17.55a4.05 4.05 0 0 1 4.05 4.05v1.35A4.05 4.05 0 0 1 16.2 27h-1.35a4.05 4.05 0 0 1-4.05-4.05V21.6a4.05 4.05 0 0 1 4.05-4.05h1.35Zm0 2.7h-1.35a1.35 1.35 0 0 0-1.35 1.35v1.35c0 .746.604 1.35 1.35 1.35h1.35a1.35 1.35 0 0 0 1.35-1.35V21.6a1.35 1.35 0 0 0-1.35-1.35Zm27 1.35a4.05 4.05 0 0 0-4.05-4.05H29.7a4.05 4.05 0 0 0-4.05 4.05v1.35A4.05 4.05 0 0 0 29.7 27h9.45a4.05 4.05 0 0 0 4.05-4.05V21.6Zm-13.5-1.35h9.45c.746 0 1.35.604 1.35 1.35v1.35a1.35 1.35 0 0 1-1.35 1.35H29.7a1.35 1.35 0 0 1-1.35-1.35V21.6c0-.746.604-1.35 1.35-1.35ZM43.2 37.8a4.05 4.05 0 0 0-4.05-4.05H29.7a4.05 4.05 0 0 0-4.05 4.05v1.35h2.7V37.8c0-.746.604-1.35 1.35-1.35h9.45c.746 0 1.35.604 1.35 1.35v1.35h2.7V37.8Zm-27-4.05a4.05 4.05 0 0 1 4.05 4.05v1.35h-2.7V37.8a1.35 1.35 0 0 0-1.35-1.35h-1.35a1.35 1.35 0 0 0-1.35 1.35v1.35h-2.7V37.8a4.05 4.05 0 0 1 4.05-4.05h1.35Z",
5040
+ clipRule: "evenodd"
5041
+ })));
5042
+ };
5043
+ var DynamicListIcon = SvgDynamicList;
5044
+
5045
+ var _path$9;
5046
+ function _extends$9() { _extends$9 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$9.apply(this, arguments); }
4190
5047
  var SvgText = function SvgText(props) {
4191
- return /*#__PURE__*/React.createElement("svg", _extends$3({
5048
+ return /*#__PURE__*/React.createElement("svg", _extends$9({
4192
5049
  xmlns: "http://www.w3.org/2000/svg",
4193
5050
  width: 54,
4194
5051
  height: 54,
4195
5052
  fill: "currentcolor"
4196
- }, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
5053
+ }, props), _path$9 || (_path$9 = /*#__PURE__*/React.createElement("path", {
4197
5054
  d: "M20.58 33.77h-3l-1.18-3.08H11l-1.1 3.08H7l5.27-13.54h2.89zm-5-5.36-1.86-5-1.83 5zM22 20.23h5.41a15.47 15.47 0 0 1 2.4.14 3.42 3.42 0 0 1 1.41.55 3.47 3.47 0 0 1 1 1.14 3 3 0 0 1 .42 1.58 3.26 3.26 0 0 1-1.91 2.94 3.63 3.63 0 0 1 1.91 1.22 3.28 3.28 0 0 1 .66 2 4 4 0 0 1-.43 1.8 3.63 3.63 0 0 1-1.09 1.4 3.89 3.89 0 0 1-1.83.65q-.69.07-3.3.09H22zm2.73 2.25v3.13h3.8a1.79 1.79 0 0 0 1.1-.49 1.41 1.41 0 0 0 .41-1 1.49 1.49 0 0 0-.35-1 1.54 1.54 0 0 0-1-.48c-.27 0-1.05-.05-2.34-.05zm0 5.39v3.62h2.57a11.52 11.52 0 0 0 1.88-.09 1.65 1.65 0 0 0 1-.54 1.6 1.6 0 0 0 .38-1.14 1.75 1.75 0 0 0-.29-1 1.69 1.69 0 0 0-.86-.62 9.28 9.28 0 0 0-2.41-.23zm19.62.92 2.65.84a5.94 5.94 0 0 1-2 3.29A5.74 5.74 0 0 1 41.38 34a5.87 5.87 0 0 1-4.44-1.84 7.09 7.09 0 0 1-1.73-5A7.43 7.43 0 0 1 37 21.87 6 6 0 0 1 41.54 20a5.64 5.64 0 0 1 4 1.47A5.33 5.33 0 0 1 47 24l-2.7.65a2.8 2.8 0 0 0-2.86-2.27A3.09 3.09 0 0 0 39 23.42a5.31 5.31 0 0 0-.93 3.5 5.62 5.62 0 0 0 .93 3.65 3 3 0 0 0 2.4 1.09 2.72 2.72 0 0 0 1.82-.66 4 4 0 0 0 1.13-2.21z"
4198
5055
  })));
4199
5056
  };
4200
5057
  var TextIcon = SvgText;
4201
5058
 
4202
- var _path$2;
4203
- function _extends$2() { _extends$2 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$2.apply(this, arguments); }
5059
+ var _path$8;
5060
+ function _extends$8() { _extends$8 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$8.apply(this, arguments); }
4204
5061
  var SvgTextfield = function SvgTextfield(props) {
4205
- return /*#__PURE__*/React.createElement("svg", _extends$2({
5062
+ return /*#__PURE__*/React.createElement("svg", _extends$8({
4206
5063
  xmlns: "http://www.w3.org/2000/svg",
4207
5064
  width: 54,
4208
5065
  height: 54,
4209
5066
  fill: "currentcolor"
4210
- }, props), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
5067
+ }, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
4211
5068
  fillRule: "evenodd",
4212
5069
  d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1zm-32 4v10h-2V22h2z"
4213
5070
  })));
4214
5071
  };
4215
5072
  var TextfieldIcon = SvgTextfield;
4216
5073
 
4217
- var _path$1;
4218
- function _extends$1() { _extends$1 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$1.apply(this, arguments); }
5074
+ var _path$7;
5075
+ function _extends$7() { _extends$7 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$7.apply(this, arguments); }
4219
5076
  var SvgTextarea = function SvgTextarea(props) {
4220
- return /*#__PURE__*/React.createElement("svg", _extends$1({
5077
+ return /*#__PURE__*/React.createElement("svg", _extends$7({
4221
5078
  xmlns: "http://www.w3.org/2000/svg",
4222
5079
  width: 54,
4223
5080
  height: 54,
4224
5081
  fill: "currentcolor"
4225
- }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
5082
+ }, props), _path$7 || (_path$7 = /*#__PURE__*/React.createElement("path", {
4226
5083
  fillRule: "evenodd",
4227
5084
  d: "M45 13a3 3 0 0 1 3 3v22a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V16a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v22a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V16a1 1 0 0 0-1-1zm-1.136 15.5.849.849-6.364 6.364-.849-.849 6.364-6.364zm.264 3.5.849.849-2.828 2.828-.849-.849L44.128 34zM13 19v10h-2V19h2z"
4228
5085
  })));
4229
5086
  };
4230
5087
  var TextareaIcon = SvgTextarea;
4231
5088
 
4232
- var _path, _path2;
4233
- function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
5089
+ var _path$6, _path2$2;
5090
+ function _extends$6() { _extends$6 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$6.apply(this, arguments); }
5091
+ var SvgIFrame = function SvgIFrame(props) {
5092
+ return /*#__PURE__*/React.createElement("svg", _extends$6({
5093
+ xmlns: "http://www.w3.org/2000/svg",
5094
+ width: 54,
5095
+ height: 54,
5096
+ fill: "none"
5097
+ }, props), _path$6 || (_path$6 = /*#__PURE__*/React.createElement("path", {
5098
+ fill: "currentcolor",
5099
+ d: "M34.467 37.3 41 31l-6.533-6.3-1.32 1.273L38.36 31l-5.213 5.027 1.32 1.273ZM19.533 24.7 13 31l6.533 6.3 1.32-1.273L15.64 31l5.214-5.027-1.32-1.273Zm4.127 14.832 1.805.468 4.875-17.532L28.535 22 23.66 39.532Z"
5100
+ })), _path2$2 || (_path2$2 = /*#__PURE__*/React.createElement("path", {
5101
+ fill: "currentcolor",
5102
+ fillRule: "evenodd",
5103
+ d: "M46 9a3 3 0 0 1 3 3v30a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3V12a3 3 0 0 1 3-3h38Zm0 2H8a1 1 0 0 0-1 1v4h40v-4a1 1 0 0 0-1-1ZM7 42V18h40v24a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1Z",
5104
+ clipRule: "evenodd"
5105
+ })));
5106
+ };
5107
+ var IFrameIcon = SvgIFrame;
5108
+
5109
+ var _path$5, _path2$1;
5110
+ function _extends$5() { _extends$5 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$5.apply(this, arguments); }
4234
5111
  var SvgImage = function SvgImage(props) {
4235
- return /*#__PURE__*/React.createElement("svg", _extends({
5112
+ return /*#__PURE__*/React.createElement("svg", _extends$5({
4236
5113
  xmlns: "http://www.w3.org/2000/svg",
4237
5114
  width: 54,
4238
5115
  height: 54,
4239
5116
  fill: "currentcolor"
4240
- }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
5117
+ }, props), _path$5 || (_path$5 = /*#__PURE__*/React.createElement("path", {
4241
5118
  fillRule: "evenodd",
4242
5119
  d: "M34.636 21.91A3.818 3.818 0 1 1 27 21.908a3.818 3.818 0 0 1 7.636 0Zm-2 0A1.818 1.818 0 1 1 29 21.908a1.818 1.818 0 0 1 3.636 0Z",
4243
5120
  clipRule: "evenodd"
4244
- })), _path2 || (_path2 = /*#__PURE__*/React.createElement("path", {
5121
+ })), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
4245
5122
  fillRule: "evenodd",
4246
5123
  d: "M15 13a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V15a2 2 0 0 0-2-2H15Zm24 2H15v12.45l4.71-4.709a1.91 1.91 0 0 1 2.702 0l6.695 6.695 2.656-1.77a1.91 1.91 0 0 1 2.411.239L39 32.73V15ZM15 39v-8.754a.975.975 0 0 0 .168-.135l5.893-5.893 6.684 6.685a1.911 1.911 0 0 0 2.41.238l2.657-1.77 6.02 6.02c.052.051.108.097.168.135V39H15Z",
4247
5124
  clipRule: "evenodd"
@@ -4249,6 +5126,22 @@ var SvgImage = function SvgImage(props) {
4249
5126
  };
4250
5127
  var ImageIcon = SvgImage;
4251
5128
 
5129
+ var _path$4;
5130
+ function _extends$4() { _extends$4 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$4.apply(this, arguments); }
5131
+ var SvgTable = function SvgTable(props) {
5132
+ return /*#__PURE__*/React.createElement("svg", _extends$4({
5133
+ xmlns: "http://www.w3.org/2000/svg",
5134
+ fill: "none",
5135
+ viewBox: "0 0 54 54"
5136
+ }, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
5137
+ fill: "currentcolor",
5138
+ fillRule: "evenodd",
5139
+ d: "M42.545 12.273A2.455 2.455 0 0 1 45 14.727v24.546a2.455 2.455 0 0 1-2.455 2.454h-31.09A2.455 2.455 0 0 1 9 39.273V14.727a2.455 2.455 0 0 1 2.455-2.454zM27.818 40.09h14.727a.818.818 0 0 0 .819-.818v-4.91H27.818Zm-1.636-5.727v5.727H11.455a.818.818 0 0 1-.819-.818v-4.91zm1.636-1.637h15.546V27H27.818ZM26.182 27v5.727H10.636V27zm1.636-1.636h15.546v-5.728H27.818Zm-1.636-5.728v5.728H10.636v-5.728z",
5140
+ clipRule: "evenodd"
5141
+ })));
5142
+ };
5143
+ var TableIcon = SvgTable;
5144
+
4252
5145
  const iconsByType = type => {
4253
5146
  return {
4254
5147
  button: ButtonIcon,
@@ -4257,130 +5150,42 @@ const iconsByType = type => {
4257
5150
  columns: GroupIcon,
4258
5151
  datetime: DatetimeIcon,
4259
5152
  group: GroupIcon,
5153
+ iframe: IFrameIcon,
4260
5154
  image: ImageIcon,
4261
5155
  number: NumberIcon,
4262
5156
  radio: RadioIcon,
4263
5157
  select: SelectIcon,
4264
5158
  separator: SeparatorIcon,
4265
5159
  spacer: SpacerIcon,
5160
+ dynamiclist: DynamicListIcon,
4266
5161
  taglist: TaglistIcon,
4267
5162
  text: TextIcon,
4268
5163
  textfield: TextfieldIcon,
4269
5164
  textarea: TextareaIcon,
5165
+ table: TableIcon,
4270
5166
  default: FormIcon
4271
5167
  }[type];
4272
5168
  };
4273
5169
 
4274
- const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, Image, Numberfield, Datetime, Radio, Select, Spacer, Separator, Taglist, Text, Textfield, Textarea];
5170
+ const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, IFrame, DynamicList, Image, Numberfield, Datetime, Radio, Select, Spacer, Separator, DynamicList, Taglist, Text, Textfield, Textarea, Table];
4275
5171
 
4276
5172
  class FormFields {
4277
5173
  constructor() {
4278
5174
  this._formFields = {};
4279
- formFields.forEach(formField => {
4280
- this.register(formField.config.type, formField);
4281
- });
4282
- }
4283
- register(type, formField) {
4284
- this._formFields[type] = formField;
4285
- }
4286
- get(type) {
4287
- return this._formFields[type];
4288
- }
4289
- }
4290
-
4291
- // config ///////////////////
4292
-
4293
- const VALUES_SOURCES = {
4294
- STATIC: 'static',
4295
- INPUT: 'input',
4296
- EXPRESSION: 'expression'
4297
- };
4298
- const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
4299
- const VALUES_SOURCES_LABELS = {
4300
- [VALUES_SOURCES.STATIC]: 'Static',
4301
- [VALUES_SOURCES.INPUT]: 'Input data',
4302
- [VALUES_SOURCES.EXPRESSION]: 'Expression'
4303
- };
4304
- const VALUES_SOURCES_PATHS = {
4305
- [VALUES_SOURCES.STATIC]: ['values'],
4306
- [VALUES_SOURCES.INPUT]: ['valuesKey'],
4307
- [VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
4308
- };
4309
- const VALUES_SOURCES_DEFAULTS = {
4310
- [VALUES_SOURCES.STATIC]: [{
4311
- label: 'Value',
4312
- value: 'value'
4313
- }],
4314
- [VALUES_SOURCES.INPUT]: '',
4315
- [VALUES_SOURCES.EXPRESSION]: '='
4316
- };
4317
-
4318
- // helpers ///////////////////
4319
-
4320
- function getValuesSource(field) {
4321
- for (const source of Object.values(VALUES_SOURCES)) {
4322
- if (get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
4323
- return source;
4324
- }
4325
- }
4326
- return VALUES_SOURCE_DEFAULT;
4327
- }
4328
-
4329
- function createInjector(bootstrapModules) {
4330
- const injector = new Injector(bootstrapModules);
4331
- injector.init();
4332
- return injector;
4333
- }
4334
-
4335
- /**
4336
- * @param {string?} prefix
4337
- *
4338
- * @returns Element
4339
- */
4340
- function createFormContainer(prefix = 'fjs') {
4341
- const container = document.createElement('div');
4342
- container.classList.add(`${prefix}-container`);
4343
- return container;
4344
- }
4345
-
4346
- const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
4347
- const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
4348
- function isRequired(field) {
4349
- return field.required;
4350
- }
4351
- function pathParse(path) {
4352
- if (!path) {
4353
- return [];
4354
- }
4355
- return path.split('.').map(key => {
4356
- return isNaN(parseInt(key)) ? key : parseInt(key);
4357
- });
4358
- }
4359
- function pathsEqual(a, b) {
4360
- return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
4361
- }
4362
- const indices = {};
4363
- function generateIndexForType(type) {
4364
- if (type in indices) {
4365
- indices[type]++;
4366
- } else {
4367
- indices[type] = 1;
5175
+ formFields.forEach(formField => {
5176
+ this.register(formField.config.type, formField);
5177
+ });
5178
+ }
5179
+ register(type, formField) {
5180
+ this._formFields[type] = formField;
5181
+ }
5182
+ get(type) {
5183
+ return this._formFields[type];
4368
5184
  }
4369
- return indices[type];
4370
- }
4371
- function generateIdForType(type) {
4372
- return `${type}${generateIndexForType(type)}`;
4373
5185
  }
4374
5186
 
4375
- /**
4376
- * @template T
4377
- * @param {T} data
4378
- * @param {(this: any, key: string, value: any) => any} [replacer]
4379
- * @return {T}
4380
- */
4381
- function clone(data, replacer) {
4382
- return JSON.parse(JSON.stringify(data, replacer));
4383
- }
5187
+ const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression', 'url', 'dataSource', 'columnsExpression'];
5188
+ const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text', 'url'];
4384
5189
 
4385
5190
  /**
4386
5191
  * @typedef { import('../types').Schema } Schema
@@ -4487,13 +5292,24 @@ function getSchemaVariables(schema, options = {}) {
4487
5292
  // remove duplicates
4488
5293
  return Array.from(new Set(variables));
4489
5294
  }
4490
- function runRecursively(formField, fn) {
4491
- const components = formField.components || [];
4492
- components.forEach((component, index) => {
4493
- runRecursively(component, fn);
4494
- });
4495
- fn(formField);
4496
- }
5295
+
5296
+ /**
5297
+ * Get the ancestry list of a form field.
5298
+ *
5299
+ * @param {string} formFieldId
5300
+ * @param {import('../core/FormFieldRegistry').default} formFieldRegistry
5301
+ *
5302
+ * @return {Array<string>} ancestry list
5303
+ */
5304
+ const getAncestryList = (formFieldId, formFieldRegistry) => {
5305
+ const ids = [];
5306
+ let currentFormField = formFieldRegistry.get(formFieldId);
5307
+ while (currentFormField) {
5308
+ ids.push(currentFormField.id);
5309
+ currentFormField = formFieldRegistry.get(currentFormField._parent);
5310
+ }
5311
+ return ids;
5312
+ };
4497
5313
 
4498
5314
  /**
4499
5315
  * @typedef {object} Condition
@@ -4510,36 +5326,102 @@ class ConditionChecker {
4510
5326
  /**
4511
5327
  * For given data, remove properties based on condition.
4512
5328
  *
4513
- * @param {Object<string, any>} properties
4514
5329
  * @param {Object<string, any>} data
5330
+ * @param {Object<string, any>} contextData
4515
5331
  * @param {Object} [options]
4516
5332
  * @param {Function} [options.getFilterPath]
5333
+ * @param {boolean} [options.leafNodeDeletionOnly]
4517
5334
  */
4518
- applyConditions(properties, data = {}, options = {}) {
4519
- const newProperties = clone(properties);
5335
+ applyConditions(data, contextData = {}, options = {}) {
5336
+ const workingData = clone(data);
4520
5337
  const {
4521
- getFilterPath = field => this._pathRegistry.getValuePath(field)
5338
+ getFilterPath = (field, indexes) => this._pathRegistry.getValuePath(field, {
5339
+ indexes
5340
+ }),
5341
+ leafNodeDeletionOnly = false
4522
5342
  } = options;
4523
- const form = this._formFieldRegistry.getAll().find(field => field.type === 'default');
5343
+ const _applyConditionsWithinScope = (rootField, scopeContext, startHidden = false) => {
5344
+ const {
5345
+ indexes = {},
5346
+ expressionIndexes = [],
5347
+ scopeData = contextData,
5348
+ parentScopeData = null
5349
+ } = scopeContext;
5350
+ this._pathRegistry.executeRecursivelyOnFields(rootField, ({
5351
+ field,
5352
+ isClosed,
5353
+ isRepeatable,
5354
+ context
5355
+ }) => {
5356
+ const {
5357
+ conditional,
5358
+ components,
5359
+ id
5360
+ } = field;
5361
+
5362
+ // build the expression context in the right format
5363
+ const localExpressionContext = buildExpressionContext({
5364
+ this: scopeData,
5365
+ data: contextData,
5366
+ i: expressionIndexes,
5367
+ parent: parentScopeData
5368
+ });
5369
+ context.isHidden = startHidden || context.isHidden || conditional && this._checkHideCondition(conditional, localExpressionContext);
5370
+
5371
+ // if a field is repeatable and visible, we need to implement custom recursion on its children
5372
+ if (isRepeatable && (!context.isHidden || leafNodeDeletionOnly)) {
5373
+ // prevent the regular recursion behavior of executeRecursivelyOnFields
5374
+ context.preventRecursion = true;
5375
+ const repeaterValuePath = this._pathRegistry.getValuePath(field, {
5376
+ indexes
5377
+ });
5378
+ const repeaterValue = get(contextData, repeaterValuePath);
5379
+
5380
+ // quit early if there are no children or data associated with the repeater
5381
+ if (!Array.isArray(repeaterValue) || !repeaterValue.length || !Array.isArray(components) || !components.length) {
5382
+ return;
5383
+ }
5384
+ for (let i = 0; i < repeaterValue.length; i++) {
5385
+ // create a new scope context for each index
5386
+ const newScopeContext = {
5387
+ indexes: {
5388
+ ...indexes,
5389
+ [id]: i
5390
+ },
5391
+ expressionIndexes: [...expressionIndexes, i + 1],
5392
+ scopeData: repeaterValue[i],
5393
+ parentScopeData: scopeData
5394
+ };
5395
+
5396
+ // for each child component, apply conditions within the new repetition scope
5397
+ components.forEach(component => {
5398
+ _applyConditionsWithinScope(component, newScopeContext, context.isHidden);
5399
+ });
5400
+ }
5401
+ }
5402
+
5403
+ // if we have a hidden repeatable field, and the data structure allows, we clear it directly at the root and stop recursion
5404
+ if (context.isHidden && !leafNodeDeletionOnly && isRepeatable) {
5405
+ context.preventRecursion = true;
5406
+ this._cleanlyClearDataAtPath(getFilterPath(field, indexes), workingData);
5407
+ }
5408
+
5409
+ // for simple leaf fields, we always clear
5410
+ if (context.isHidden && isClosed) {
5411
+ this._cleanlyClearDataAtPath(getFilterPath(field, indexes), workingData);
5412
+ }
5413
+ });
5414
+ };
5415
+
5416
+ // apply conditions starting with the root of the form
5417
+ const form = this._formFieldRegistry.getForm();
4524
5418
  if (!form) {
4525
5419
  throw new Error('form field registry has no form');
4526
5420
  }
4527
- this._pathRegistry.executeRecursivelyOnFields(form, ({
4528
- field,
4529
- isClosed,
4530
- context
4531
- }) => {
4532
- const {
4533
- conditional: condition
4534
- } = field;
4535
- context.isHidden = context.isHidden || condition && this._checkHideCondition(condition, data);
4536
-
4537
- // only clear the leaf nodes, as groups may both point to the same path
4538
- if (context.isHidden && isClosed) {
4539
- this._clearObjectValueRecursively(getFilterPath(field), newProperties);
4540
- }
5421
+ _applyConditionsWithinScope(form, {
5422
+ scopeData: contextData
4541
5423
  });
4542
- return newProperties;
5424
+ return workingData;
4543
5425
  }
4544
5426
 
4545
5427
  /**
@@ -4583,16 +5465,22 @@ class ConditionChecker {
4583
5465
  const result = this.check(condition.hide, data);
4584
5466
  return result === true;
4585
5467
  }
4586
- _clearObjectValueRecursively(valuePath, obj) {
5468
+ _cleanlyClearDataAtPath(valuePath, obj) {
4587
5469
  const workingValuePath = [...valuePath];
4588
5470
  let recurse = false;
4589
5471
  do {
4590
5472
  set(obj, workingValuePath, undefined);
4591
5473
  workingValuePath.pop();
4592
5474
  const parentObject = get(obj, workingValuePath);
4593
- recurse = isObject(parentObject) && !values(parentObject).length && !!workingValuePath.length;
5475
+ recurse = !!workingValuePath.length && (this._isEmptyObject(parentObject) || this._isEmptyArray(parentObject));
4594
5476
  } while (recurse);
4595
5477
  }
5478
+ _isEmptyObject(parentObject) {
5479
+ return isObject(parentObject) && !values(parentObject).length;
5480
+ }
5481
+ _isEmptyArray(parentObject) {
5482
+ return Array.isArray(parentObject) && (!parentObject.length || parentObject.every(item => item === undefined));
5483
+ }
4596
5484
  }
4597
5485
  ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'eventBus'];
4598
5486
 
@@ -5107,14 +5995,15 @@ class UpdateFieldValidationHandler {
5107
5995
  execute(context) {
5108
5996
  const {
5109
5997
  field,
5110
- value
5998
+ value,
5999
+ indexes
5111
6000
  } = context;
5112
6001
  const {
5113
6002
  errors
5114
6003
  } = this._form._getState();
5115
6004
  context.oldErrors = clone(errors);
5116
6005
  const fieldErrors = this._validator.validateField(field, value);
5117
- const updatedErrors = set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
6006
+ const updatedErrors = set(errors, [field.id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
5118
6007
  this._form._setState({
5119
6008
  errors: updatedErrors
5120
6009
  });
@@ -5144,10 +6033,11 @@ class ViewerCommands {
5144
6033
  'formField.validation.update': UpdateFieldValidationHandler
5145
6034
  };
5146
6035
  }
5147
- updateFieldValidation(field, value) {
6036
+ updateFieldValidation(field, value, indexes) {
5148
6037
  const context = {
5149
6038
  field,
5150
- value
6039
+ value,
6040
+ indexes
5151
6041
  };
5152
6042
  this._commandStack.execute('formField.validation.update', context);
5153
6043
  }
@@ -5160,6 +6050,266 @@ var ViewerCommandsModule = {
5160
6050
  viewerCommands: ['type', ViewerCommands]
5161
6051
  };
5162
6052
 
6053
+ var _path$3;
6054
+ function _extends$3() { _extends$3 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$3.apply(this, arguments); }
6055
+ var SvgExpand = function SvgExpand(props) {
6056
+ return /*#__PURE__*/React.createElement("svg", _extends$3({
6057
+ xmlns: "http://www.w3.org/2000/svg",
6058
+ width: 16,
6059
+ height: 16,
6060
+ fill: "none"
6061
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
6062
+ fill: "currentColor",
6063
+ d: "M2 9h5.5v3.086l-1.293-1.293-.707.707L8 14l2.5-2.5-.707-.707L8.5 12.086V9H14V8H2v1Zm11-7H3a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1Zm0 3H3V3h10v2Z"
6064
+ })));
6065
+ };
6066
+ var ExpandSvg = SvgExpand;
6067
+
6068
+ var _path$2;
6069
+ function _extends$2() { _extends$2 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$2.apply(this, arguments); }
6070
+ var SvgCollapse = function SvgCollapse(props) {
6071
+ return /*#__PURE__*/React.createElement("svg", _extends$2({
6072
+ xmlns: "http://www.w3.org/2000/svg",
6073
+ width: 16,
6074
+ height: 16,
6075
+ fill: "none"
6076
+ }, props), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
6077
+ fill: "currentColor",
6078
+ d: "M13 10H3a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1Zm0 3H3v-2h10v2ZM8.5 3.914l1.293 1.293.707-.707L8 2 5.5 4.5l.707.707L7.5 3.914V7H2v1h12V7H8.5V3.914Z"
6079
+ })));
6080
+ };
6081
+ var CollapseSvg = SvgCollapse;
6082
+
6083
+ var _path$1, _path2;
6084
+ function _extends$1() { _extends$1 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$1.apply(this, arguments); }
6085
+ var SvgAdd = function SvgAdd(props) {
6086
+ return /*#__PURE__*/React.createElement("svg", _extends$1({
6087
+ xmlns: "http://www.w3.org/2000/svg",
6088
+ width: 16,
6089
+ height: 16,
6090
+ fill: "none"
6091
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
6092
+ fill: "currentColor",
6093
+ d: "M8 2c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6Zm0-1C4.15 1 1 4.15 1 8s3.15 7 7 7 7-3.15 7-7-3.15-7-7-7Z"
6094
+ })), _path2 || (_path2 = /*#__PURE__*/React.createElement("path", {
6095
+ fill: "currentColor",
6096
+ d: "M12 7.5H8.5V4h-1v3.5H4v1h3.5V12h1V8.5H12v-1Z"
6097
+ })));
6098
+ };
6099
+ var AddSvg = SvgAdd;
6100
+
6101
+ var _path;
6102
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
6103
+ var SvgDelete = function SvgDelete(props) {
6104
+ return /*#__PURE__*/React.createElement("svg", _extends({
6105
+ xmlns: "http://www.w3.org/2000/svg",
6106
+ width: 16,
6107
+ height: 16,
6108
+ fill: "none"
6109
+ }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
6110
+ fill: "currentColor",
6111
+ d: "m12 4.7-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7Z"
6112
+ })));
6113
+ };
6114
+ var DeleteSvg = SvgDelete;
6115
+
6116
+ // disable react hook rules as the linter is confusing the functional components within a class as class components
6117
+ class RepeatRenderManager {
6118
+ constructor(form, formFields, formFieldRegistry, pathRegistry) {
6119
+ this._form = form;
6120
+ this._formFields = formFields;
6121
+ this._formFieldRegistry = formFieldRegistry;
6122
+ this._pathRegistry = pathRegistry;
6123
+ this.Repeater = this.Repeater.bind(this);
6124
+ this.RepeatFooter = this.RepeatFooter.bind(this);
6125
+ }
6126
+
6127
+ /**
6128
+ * Checks whether a field is currently repeating its children.
6129
+ *
6130
+ * @param {string} id - The id of the field to check
6131
+ * @returns {boolean} - True if repeatable, false otherwise
6132
+ */
6133
+ isFieldRepeating(id) {
6134
+ if (!id) {
6135
+ return false;
6136
+ }
6137
+ const formField = this._formFieldRegistry.get(id);
6138
+ const formFieldDefinition = this._formFields.get(formField.type);
6139
+ return formFieldDefinition.config.repeatable && formField.isRepeating;
6140
+ }
6141
+ Repeater(props) {
6142
+ const {
6143
+ RowsRenderer,
6144
+ indexes,
6145
+ useSharedState,
6146
+ ...restProps
6147
+ } = props;
6148
+ const [sharedRepeatState] = useSharedState;
6149
+ const {
6150
+ data
6151
+ } = this._form._getState();
6152
+ const repeaterField = props.field;
6153
+ const dataPath = this._pathRegistry.getValuePath(repeaterField, {
6154
+ indexes
6155
+ });
6156
+ const values = get(data, dataPath) || [];
6157
+ const nonCollapsedItems = this._getNonCollapsedItems(repeaterField);
6158
+ const collapseEnabled = !repeaterField.disableCollapse && values.length > nonCollapsedItems;
6159
+ const isCollapsed = collapseEnabled && sharedRepeatState.isCollapsed;
6160
+ const hasChildren = repeaterField.components && repeaterField.components.length > 0;
6161
+ const showRemove = repeaterField.allowAddRemove && hasChildren;
6162
+ const displayValues = isCollapsed ? values.slice(0, nonCollapsedItems) : values;
6163
+ const onDeleteItem = index => {
6164
+ const updatedValues = values.slice();
6165
+ updatedValues.splice(index, 1);
6166
+ props.onChange({
6167
+ field: repeaterField,
6168
+ value: updatedValues,
6169
+ indexes
6170
+ });
6171
+ };
6172
+ const parentExpressionContextInfo = useContext(LocalExpressionContext$1);
6173
+ return jsx(Fragment, {
6174
+ children: displayValues.map((value, index) => {
6175
+ const elementProps = {
6176
+ ...restProps,
6177
+ indexes: {
6178
+ ...(indexes || {}),
6179
+ [repeaterField.id]: index
6180
+ }
6181
+ };
6182
+ const localExpressionContextInfo = useMemo(() => ({
6183
+ data: parentExpressionContextInfo.data,
6184
+ this: value,
6185
+ parent: buildExpressionContext(parentExpressionContextInfo),
6186
+ i: [...parentExpressionContextInfo.i, index + 1]
6187
+ }), [index, value]);
6188
+ return !showRemove ? jsx(LocalExpressionContext$1.Provider, {
6189
+ value: localExpressionContextInfo,
6190
+ children: jsx(RowsRenderer, {
6191
+ ...elementProps
6192
+ })
6193
+ }) : jsxs("div", {
6194
+ class: "fjs-repeat-row-container",
6195
+ children: [jsx("div", {
6196
+ class: "fjs-repeat-row-rows",
6197
+ children: jsx(LocalExpressionContext$1.Provider, {
6198
+ value: localExpressionContextInfo,
6199
+ children: jsx(RowsRenderer, {
6200
+ ...elementProps
6201
+ })
6202
+ })
6203
+ }), jsx("button", {
6204
+ class: "fjs-repeat-row-remove",
6205
+ type: "button",
6206
+ "aria-label": `Remove list item ${index + 1}`,
6207
+ onClick: () => onDeleteItem(index),
6208
+ children: jsx("div", {
6209
+ class: "fjs-repeat-row-remove-icon-container",
6210
+ children: jsx(DeleteSvg, {})
6211
+ })
6212
+ })]
6213
+ });
6214
+ })
6215
+ });
6216
+ }
6217
+ RepeatFooter(props) {
6218
+ const addButtonRef = useRef(null);
6219
+ const {
6220
+ useSharedState,
6221
+ indexes,
6222
+ field: repeaterField,
6223
+ readonly,
6224
+ disabled
6225
+ } = props;
6226
+ const [sharedRepeatState, setSharedRepeatState] = useSharedState;
6227
+ const {
6228
+ data
6229
+ } = this._form._getState();
6230
+ const dataPath = this._pathRegistry.getValuePath(repeaterField, {
6231
+ indexes
6232
+ });
6233
+ const values = get(data, dataPath) || [];
6234
+ const nonCollapsedItems = this._getNonCollapsedItems(repeaterField);
6235
+ const collapseEnabled = !repeaterField.disableCollapse && values.length > nonCollapsedItems;
6236
+ const isCollapsed = collapseEnabled && sharedRepeatState.isCollapsed;
6237
+ const hasChildren = repeaterField.components && repeaterField.components.length > 0;
6238
+ const showAdd = repeaterField.allowAddRemove && hasChildren;
6239
+ const toggle = () => {
6240
+ setSharedRepeatState(state => ({
6241
+ ...state,
6242
+ isCollapsed: !isCollapsed
6243
+ }));
6244
+ };
6245
+ const shouldScroll = useRef(false);
6246
+ const onAddItem = () => {
6247
+ const updatedValues = values.slice();
6248
+ const newItem = this._form._getInitializedFieldData(this._form._getState().data, {
6249
+ container: repeaterField,
6250
+ indexes: {
6251
+ ...indexes,
6252
+ [repeaterField.id]: updatedValues.length
6253
+ }
6254
+ });
6255
+ updatedValues.push(newItem);
6256
+ shouldScroll.current = true;
6257
+ props.onChange({
6258
+ field: repeaterField,
6259
+ value: updatedValues,
6260
+ indexes
6261
+ });
6262
+ setSharedRepeatState(state => ({
6263
+ ...state,
6264
+ isCollapsed: false
6265
+ }));
6266
+ };
6267
+ useScrollIntoView(addButtonRef, [values.length], {
6268
+ align: 'bottom',
6269
+ behavior: 'auto',
6270
+ offset: 20
6271
+ }, [shouldScroll]);
6272
+ return jsxs("div", {
6273
+ className: classNames('fjs-repeat-render-footer', {
6274
+ 'fjs-remove-allowed': repeaterField.allowAddRemove
6275
+ }),
6276
+ children: [showAdd ? jsx("button", {
6277
+ readOnly: readonly,
6278
+ disabled: disabled || readonly,
6279
+ class: "fjs-repeat-render-add",
6280
+ type: "button",
6281
+ ref: addButtonRef,
6282
+ onClick: onAddItem,
6283
+ children: jsxs(Fragment, {
6284
+ children: [jsx(AddSvg, {}), " ", 'Add new']
6285
+ })
6286
+ }) : null, collapseEnabled ? jsx("button", {
6287
+ class: "fjs-repeat-render-collapse",
6288
+ type: "button",
6289
+ onClick: toggle,
6290
+ children: isCollapsed ? jsxs(Fragment, {
6291
+ children: [jsx(ExpandSvg, {}), " ", `Expand all (${values.length})`]
6292
+ }) : jsxs(Fragment, {
6293
+ children: [jsx(CollapseSvg, {}), " ", 'Collapse']
6294
+ })
6295
+ }) : null]
6296
+ });
6297
+ }
6298
+ _getNonCollapsedItems(field) {
6299
+ const DEFAULT_NON_COLLAPSED_ITEMS = 5;
6300
+ const {
6301
+ nonCollapsedItems
6302
+ } = field;
6303
+ return nonCollapsedItems ? nonCollapsedItems : DEFAULT_NON_COLLAPSED_ITEMS;
6304
+ }
6305
+ }
6306
+ RepeatRenderManager.$inject = ['form', 'formFields', 'formFieldRegistry', 'pathRegistry'];
6307
+
6308
+ var RepeatRenderModule = {
6309
+ __init__: ['repeatRenderManager'],
6310
+ repeatRenderManager: ['type', RepeatRenderManager]
6311
+ };
6312
+
5163
6313
  var FN_REF = '__fn';
5164
6314
  var DEFAULT_PRIORITY = 1000;
5165
6315
  var slice = Array.prototype.slice;
@@ -5560,7 +6710,7 @@ EventBus.prototype._invokeListener = function (event, args, listener) {
5560
6710
  * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
5561
6711
  *
5562
6712
  * @param {string} event
5563
- * @param {EventBusListener} listener
6713
+ * @param {EventBusListener} newListener
5564
6714
  */
5565
6715
  EventBus.prototype._addListener = function (event, newListener) {
5566
6716
  var listener = this._getListeners(event),
@@ -5908,10 +7058,17 @@ class FieldFactory {
5908
7058
 
5909
7059
  const parent = _parent && this._formFieldRegistry.get(_parent);
5910
7060
  const parentPath = parent && this._pathRegistry.getValuePath(parent) || [];
5911
- if (config.keyed && key && !this._pathRegistry.canClaimPath([...parentPath, ...key.split('.')], true)) {
7061
+ const knownAncestorIds = getAncestryList(_parent, this._formFieldRegistry);
7062
+ if (config.keyed && key && !this._pathRegistry.canClaimPath([...parentPath, ...key.split('.')], {
7063
+ isClosed: true,
7064
+ knownAncestorIds
7065
+ })) {
5912
7066
  throw new Error(`binding path '${[...parentPath, key].join('.')}' is already claimed`);
5913
7067
  }
5914
- if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], false)) {
7068
+ if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], {
7069
+ isRepeatable: config.repeatable,
7070
+ knownAncestorIds
7071
+ })) {
5915
7072
  throw new Error(`binding path '${[...parentPath, ...path.split('.')].join('.')}' is already claimed`);
5916
7073
  }
5917
7074
  const labelAttrs = applyDefaults && config.label ? {
@@ -5924,9 +7081,23 @@ class FieldFactory {
5924
7081
  this._ensureId(field);
5925
7082
  if (config.keyed) {
5926
7083
  this._ensureKey(field);
7084
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), {
7085
+ isClosed: true,
7086
+ claimerId: field.id,
7087
+ knownAncestorIds: getAncestryList(_parent, this._formFieldRegistry)
7088
+ });
5927
7089
  }
5928
- if (config.pathed && path) {
5929
- this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), false);
7090
+ if (config.pathed) {
7091
+ if (config.repeatable) {
7092
+ this._enforceDefaultPath(field);
7093
+ }
7094
+ if (field.path) {
7095
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), {
7096
+ isRepeatable: config.repeatable,
7097
+ claimerId: field.id,
7098
+ knownAncestorIds: getAncestryList(_parent, this._formFieldRegistry)
7099
+ });
7100
+ }
5930
7101
  }
5931
7102
  return field;
5932
7103
  }
@@ -5943,16 +7114,23 @@ class FieldFactory {
5943
7114
  }
5944
7115
  _ensureKey(field) {
5945
7116
  if (!field.key) {
5946
- let random;
5947
- const parent = this._formFieldRegistry.get(field._parent);
5948
-
5949
- // ensure key uniqueness at level
5950
- do {
5951
- random = Math.random().toString(36).substring(7);
5952
- } while (parent && parent.components.some(child => child.key === random));
5953
- field.key = `${field.type}_${random}`;
7117
+ field.key = this._getUniqueKeyPath(field);
7118
+ }
7119
+ }
7120
+ _enforceDefaultPath(field) {
7121
+ if (!field.path) {
7122
+ field.path = this._getUniqueKeyPath(field);
5954
7123
  }
5955
- this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), true);
7124
+ }
7125
+ _getUniqueKeyPath(field) {
7126
+ let random;
7127
+ const parent = this._formFieldRegistry.get(field._parent);
7128
+
7129
+ // ensure key uniqueness at level
7130
+ do {
7131
+ random = Math.random().toString(36).substring(7);
7132
+ } while (parent && parent.components.some(child => child.key === random));
7133
+ return `${field.type}_${random}`;
5956
7134
  }
5957
7135
  }
5958
7136
  FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
@@ -5989,34 +7167,61 @@ FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
5989
7167
  * ]
5990
7168
  */
5991
7169
  class PathRegistry {
5992
- constructor(formFieldRegistry, formFields) {
7170
+ constructor(formFieldRegistry, formFields, injector) {
5993
7171
  this._formFieldRegistry = formFieldRegistry;
5994
7172
  this._formFields = formFields;
7173
+ this._injector = injector;
5995
7174
  this._dataPaths = [];
5996
7175
  }
5997
- canClaimPath(path, closed = false) {
7176
+ canClaimPath(path, options = {}) {
7177
+ const {
7178
+ isClosed = false,
7179
+ isRepeatable = false,
7180
+ skipAncestryCheck = false,
7181
+ claimerId = null,
7182
+ knownAncestorIds = []
7183
+ } = options;
5998
7184
  let node = {
5999
7185
  children: this._dataPaths
6000
7186
  };
7187
+
7188
+ // (1) if we reach a leaf node, we cannot claim it, if we reach an open node, we can
7189
+ // if we reach a repeatable node, we need to ensure that the claimer is (or will be) an ancestor of the repeater
6001
7190
  for (const segment of path) {
6002
7191
  node = _getNextSegment(node, segment);
6003
-
6004
- // if no node at that path, we can claim it no matter what
6005
7192
  if (!node) {
6006
7193
  return true;
6007
7194
  }
6008
-
6009
- // if we reach a leaf node, definitely not claimable
7195
+ if (node.isRepeatable && !skipAncestryCheck) {
7196
+ if (!(claimerId || knownAncestorIds.length)) {
7197
+ throw new Error('cannot claim a path that contains a repeater without specifying a claimerId or knownAncestorIds');
7198
+ }
7199
+ const isValidRepeatClaim = knownAncestorIds.includes(node.repeaterId) || claimerId && getAncestryList(claimerId, this._formFieldRegistry).includes(node.repeaterId);
7200
+ if (!isValidRepeatClaim) {
7201
+ return false;
7202
+ }
7203
+ }
6010
7204
  if (node.children === null) {
6011
7205
  return false;
6012
7206
  }
6013
7207
  }
6014
7208
 
6015
- // if after all segments we reach a node with children, we can claim it only openly
6016
- return !closed;
7209
+ // (2) if the path lands in the middle of the tree, we can only claim an open, non-repeatable path
7210
+ return !(isClosed || isRepeatable);
6017
7211
  }
6018
- claimPath(path, closed = false) {
6019
- if (!this.canClaimPath(path, closed)) {
7212
+ claimPath(path, options = {}) {
7213
+ const {
7214
+ isClosed = false,
7215
+ isRepeatable = false,
7216
+ claimerId = null,
7217
+ knownAncestorIds = []
7218
+ } = options;
7219
+ if (!this.canClaimPath(path, {
7220
+ isClosed,
7221
+ isRepeatable,
7222
+ claimerId,
7223
+ knownAncestorIds
7224
+ })) {
6020
7225
  throw new Error(`cannot claim path '${path.join('.')}'`);
6021
7226
  }
6022
7227
  let node = {
@@ -6036,9 +7241,15 @@ class PathRegistry {
6036
7241
  }
6037
7242
  node = child;
6038
7243
  }
6039
- if (closed) {
7244
+ if (isClosed) {
6040
7245
  node.children = null;
6041
7246
  }
7247
+
7248
+ // add some additional info when we make a repeatable path claim
7249
+ if (isRepeatable) {
7250
+ node.isRepeatable = true;
7251
+ node.repeaterId = claimerId;
7252
+ }
6042
7253
  }
6043
7254
  unclaimPath(path) {
6044
7255
  // verification Pass
@@ -6087,6 +7298,7 @@ class PathRegistry {
6087
7298
  const callResult = fn({
6088
7299
  field,
6089
7300
  isClosed: true,
7301
+ isRepeatable: false,
6090
7302
  context
6091
7303
  });
6092
7304
  return result && callResult;
@@ -6094,16 +7306,22 @@ class PathRegistry {
6094
7306
  const callResult = fn({
6095
7307
  field,
6096
7308
  isClosed: false,
7309
+ isRepeatable: formFieldConfig.repeatable,
6097
7310
  context
6098
7311
  });
6099
7312
  result = result && callResult;
6100
7313
  }
6101
- if (field.components) {
7314
+
7315
+ // stop executing if false is specifically returned or if preventing recursion
7316
+ if (result === false || context.preventRecursion) {
7317
+ return result;
7318
+ }
7319
+ if (Array.isArray(field.components)) {
6102
7320
  for (const child of field.components) {
6103
7321
  const callResult = this.executeRecursivelyOnFields(child, fn, clone(context));
6104
7322
  result = result && callResult;
6105
7323
 
6106
- // only stop executing if false is specifically returned, not if undefined
7324
+ // stop executing if false is specifically returned
6107
7325
  if (result === false) {
6108
7326
  return result;
6109
7327
  }
@@ -6118,6 +7336,7 @@ class PathRegistry {
6118
7336
  * @param {Object} field - The field object with properties: `key`, `path`, `id`, and optionally `_parent`.
6119
7337
  * @param {Object} [options={}] - Configuration options.
6120
7338
  * @param {Object} [options.replacements={}] - A map of field IDs to alternative path arrays.
7339
+ * @param {Object} [options.indexes=null] - A map of parent IDs to the index of the field within said parent, leave null to get an unindexed path.
6121
7340
  * @param {Object} [options.cutoffNode] - The ID of the parent field at which to stop generating the path.
6122
7341
  *
6123
7342
  * @returns {(Array<string>|undefined)} An array of strings representing the binding path, or undefined if not determinable.
@@ -6125,11 +7344,14 @@ class PathRegistry {
6125
7344
  getValuePath(field, options = {}) {
6126
7345
  const {
6127
7346
  replacements = {},
7347
+ indexes = null,
6128
7348
  cutoffNode = null
6129
7349
  } = options;
6130
7350
  let localValuePath = [];
6131
7351
  const hasReplacement = Object.prototype.hasOwnProperty.call(replacements, field.id);
6132
7352
  const formFieldConfig = this._formFields.get(field.type).config;
7353
+
7354
+ // uses path overrides instead of true path to calculate a potential value path
6133
7355
  if (hasReplacement) {
6134
7356
  const replacement = replacements[field.id];
6135
7357
  if (replacement === null || replacement === undefined || replacement === '') {
@@ -6146,6 +7368,13 @@ class PathRegistry {
6146
7368
  } else if (formFieldConfig.pathed && field.path) {
6147
7369
  localValuePath = field.path.split('.');
6148
7370
  }
7371
+
7372
+ // add potential indexes of repeated fields
7373
+ if (indexes) {
7374
+ localValuePath = this._addIndexes(localValuePath, field, indexes);
7375
+ }
7376
+
7377
+ // if parent exists and isn't cutoff node, add parent's value path
6149
7378
  if (field._parent && field._parent !== cutoffNode) {
6150
7379
  const parent = this._formFieldRegistry.get(field._parent);
6151
7380
  return [...(this.getValuePath(parent, options) || []), ...localValuePath];
@@ -6155,6 +7384,13 @@ class PathRegistry {
6155
7384
  clear() {
6156
7385
  this._dataPaths = [];
6157
7386
  }
7387
+ _addIndexes(localValuePath, field, indexes) {
7388
+ const repeatRenderManager = this._injector.get('repeatRenderManager', false);
7389
+ if (repeatRenderManager && repeatRenderManager.isFieldRepeating(field._parent)) {
7390
+ return [indexes[field._parent], ...localValuePath];
7391
+ }
7392
+ return localValuePath;
7393
+ }
6158
7394
  }
6159
7395
  const _getNextSegment = (node, segment) => {
6160
7396
  if (isArray(node.children)) {
@@ -6162,7 +7398,7 @@ const _getNextSegment = (node, segment) => {
6162
7398
  }
6163
7399
  return null;
6164
7400
  };
6165
- PathRegistry.$inject = ['formFieldRegistry', 'formFields'];
7401
+ PathRegistry.$inject = ['formFieldRegistry', 'formFields', 'injector'];
6166
7402
 
6167
7403
  /**
6168
7404
  * @typedef { { id: String, components: Array<String> } } FormRow
@@ -6254,7 +7490,7 @@ class FormLayouter {
6254
7490
  type,
6255
7491
  components
6256
7492
  } = formField;
6257
- if (type !== 'default' && type !== 'group' || !components) {
7493
+ if (!['default', 'group', 'dynamiclist'].includes(type) || !components) {
6258
7494
  return;
6259
7495
  }
6260
7496
 
@@ -6346,6 +7582,9 @@ class FormFieldRegistry {
6346
7582
  getAll() {
6347
7583
  return Object.values(this._formFields);
6348
7584
  }
7585
+ getForm() {
7586
+ return this.getAll().find(formField => formField.type === 'default');
7587
+ }
6349
7588
  forEach(callback) {
6350
7589
  this.getAll().forEach(formField => callback(formField));
6351
7590
  }
@@ -6537,7 +7776,7 @@ class Form {
6537
7776
  schema: importedSchema,
6538
7777
  warnings
6539
7778
  } = this.get('importer').importSchema(schema);
6540
- const initializedData = this._initializeFieldData(clone(data));
7779
+ const initializedData = this._getInitializedFieldData(clone(data));
6541
7780
  this._setState({
6542
7781
  data: initializedData,
6543
7782
  errors: {},
@@ -6593,26 +7832,68 @@ class Form {
6593
7832
  * @returns {Errors}
6594
7833
  */
6595
7834
  validate() {
6596
- const formFieldRegistry = this.get('formFieldRegistry'),
7835
+ const formFields = this.get('formFields'),
7836
+ formFieldRegistry = this.get('formFieldRegistry'),
6597
7837
  pathRegistry = this.get('pathRegistry'),
6598
7838
  validator = this.get('validator');
6599
7839
  const {
6600
7840
  data
6601
7841
  } = this._getState();
6602
- const getErrorPath = field => [field.id];
6603
- const errors = formFieldRegistry.getAll().reduce((errors, field) => {
7842
+ const getErrorPath = (field, indexes) => [field.id, ...Object.values(indexes || {})];
7843
+ function validateFieldRecursively(errors, field, indexes) {
6604
7844
  const {
6605
- disabled
7845
+ disabled,
7846
+ type,
7847
+ isRepeating
6606
7848
  } = field;
7849
+ const {
7850
+ config: fieldConfig
7851
+ } = formFields.get(type);
7852
+
7853
+ // (1) Skip disabled fields
6607
7854
  if (disabled) {
6608
- return errors;
7855
+ return;
7856
+ }
7857
+
7858
+ // (2) Validate the field
7859
+ const valuePath = pathRegistry.getValuePath(field, {
7860
+ indexes
7861
+ });
7862
+ const valueData = get(data, valuePath);
7863
+ const fieldErrors = validator.validateField(field, valueData);
7864
+ if (fieldErrors.length) {
7865
+ set(errors, getErrorPath(field, indexes), fieldErrors);
7866
+ }
7867
+
7868
+ // (3) Process parents
7869
+ if (!Array.isArray(field.components)) {
7870
+ return;
7871
+ }
7872
+
7873
+ // (4a) Recurse repeatable parents both across the indexes of repetition and the children
7874
+ if (fieldConfig.repeatable && isRepeating) {
7875
+ if (!Array.isArray(valueData)) {
7876
+ return;
7877
+ }
7878
+ valueData.forEach((_, index) => {
7879
+ field.components.forEach(component => {
7880
+ validateFieldRecursively(errors, component, {
7881
+ ...indexes,
7882
+ [field.id]: index
7883
+ });
7884
+ });
7885
+ });
7886
+ return;
6609
7887
  }
6610
- const value = get(data, pathRegistry.getValuePath(field));
6611
- const fieldErrors = validator.validateField(field, value);
6612
- return set(errors, getErrorPath(field), fieldErrors.length ? fieldErrors : undefined);
6613
- }, /** @type {Errors} */{});
6614
- const filteredErrors = this._applyConditions(errors, data, {
6615
- getFilterPath: getErrorPath
7888
+
7889
+ // (4b) Recurse non-repeatable parents only across the children
7890
+ field.components.forEach(component => validateFieldRecursively(errors, component, indexes));
7891
+ }
7892
+ const workingErrors = {};
7893
+ validateFieldRecursively(workingErrors, formFieldRegistry.getForm());
7894
+ const filteredErrors = this._applyConditions(workingErrors, data, {
7895
+ getFilterPath: getErrorPath,
7896
+ leafNodeDeletionOnly: true
6616
7897
  });
6617
7898
  this._setState({
6618
7899
  errors: filteredErrors
@@ -6710,11 +7991,12 @@ class Form {
6710
7991
  /**
6711
7992
  * @internal
6712
7993
  *
6713
- * @param { { add?: boolean, field: any, remove?: number, value?: any } } update
7994
+ * @param { { add?: boolean, field: any, indexes: object, remove?: number, value?: any } } update
6714
7995
  */
6715
7996
  _update(update) {
6716
7997
  const {
6717
7998
  field,
7999
+ indexes,
6718
8000
  value
6719
8001
  } = update;
6720
8002
  const {
@@ -6724,8 +8006,11 @@ class Form {
6724
8006
  const validator = this.get('validator'),
6725
8007
  pathRegistry = this.get('pathRegistry');
6726
8008
  const fieldErrors = validator.validateField(field, value);
6727
- set(data, pathRegistry.getValuePath(field), value);
6728
- set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
8009
+ const valuePath = pathRegistry.getValuePath(field, {
8010
+ indexes
8011
+ });
8012
+ set(data, valuePath, value);
8013
+ set(errors, [field.id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
6729
8014
  this._setState({
6730
8015
  data: clone(data),
6731
8016
  errors: clone(errors)
@@ -6754,7 +8039,7 @@ class Form {
6754
8039
  * @internal
6755
8040
  */
6756
8041
  _getModules() {
6757
- return [ExpressionLanguageModule, MarkdownModule, ViewerCommandsModule];
8042
+ return [ExpressionLanguageModule, MarkdownModule, ViewerCommandsModule, RepeatRenderModule];
6758
8043
  }
6759
8044
 
6760
8045
  /**
@@ -6768,29 +8053,58 @@ class Form {
6768
8053
  * @internal
6769
8054
  */
6770
8055
  _getSubmitData() {
6771
- const formFieldRegistry = this.get('formFieldRegistry'),
6772
- pathRegistry = this.get('pathRegistry'),
6773
- formFields = this.get('formFields');
8056
+ const formFieldRegistry = this.get('formFieldRegistry');
8057
+ const formFields = this.get('formFields');
8058
+ const pathRegistry = this.get('pathRegistry');
6774
8059
  const formData = this._getState().data;
6775
- const submitData = formFieldRegistry.getAll().reduce((previous, field) => {
8060
+ function collectSubmitDataRecursively(submitData, formField, indexes) {
6776
8061
  const {
6777
8062
  disabled,
6778
8063
  type
6779
- } = field;
8064
+ } = formField;
6780
8065
  const {
6781
8066
  config: fieldConfig
6782
8067
  } = formFields.get(type);
6783
8068
 
6784
- // do not submit disabled form fields or routing fields
6785
- if (disabled || !fieldConfig.keyed) {
6786
- return previous;
8069
+ // (1) Process keyed fields
8070
+ if (!disabled && fieldConfig.keyed) {
8071
+ const valuePath = pathRegistry.getValuePath(formField, {
8072
+ indexes
8073
+ });
8074
+ const value = get(formData, valuePath);
8075
+ set(submitData, valuePath, value);
8076
+ }
8077
+
8078
+ // (2) Process parents
8079
+ if (!Array.isArray(formField.components)) {
8080
+ return;
8081
+ }
8082
+
8083
+ // (3a) Recurse repeatable parents both across the indexes of repetition and the children
8084
+ if (fieldConfig.repeatable && formField.isRepeating) {
8085
+ const valueData = get(formData, pathRegistry.getValuePath(formField, {
8086
+ indexes
8087
+ }));
8088
+ if (!Array.isArray(valueData)) {
8089
+ return;
8090
+ }
8091
+ valueData.forEach((_, index) => {
8092
+ formField.components.forEach(component => {
8093
+ collectSubmitDataRecursively(submitData, component, {
8094
+ ...indexes,
8095
+ [formField.id]: index
8096
+ });
8097
+ });
8098
+ });
8099
+ return;
6787
8100
  }
6788
- const valuePath = pathRegistry.getValuePath(field);
6789
- const value = get(formData, valuePath);
6790
- return set(previous, valuePath, value);
6791
- }, {});
6792
- const filteredSubmitData = this._applyConditions(submitData, formData);
6793
- return filteredSubmitData;
8101
+
8102
+ // (3b) Recurse non-repeatable parents only across the children
8103
+ formField.components.forEach(component => collectSubmitDataRecursively(submitData, component, indexes));
8104
+ }
8105
+ const workingSubmitData = {};
8106
+ collectSubmitDataRecursively(workingSubmitData, formFieldRegistry.getForm(), {});
8107
+ return this._applyConditions(workingSubmitData, formData);
6794
8108
  }
6795
8109
 
6796
8110
  /**
@@ -6804,26 +8118,27 @@ class Form {
6804
8118
  /**
6805
8119
  * @internal
6806
8120
  */
6807
- _initializeFieldData(data) {
6808
- const formFieldRegistry = this.get('formFieldRegistry'),
6809
- formFields = this.get('formFields'),
6810
- pathRegistry = this.get('pathRegistry');
6811
- return formFieldRegistry.getAll().reduce((initializedData, formField) => {
8121
+ _getInitializedFieldData(data, options = {}) {
8122
+ const formFieldRegistry = this.get('formFieldRegistry');
8123
+ const formFields = this.get('formFields');
8124
+ const pathRegistry = this.get('pathRegistry');
8125
+ function initializeFieldDataRecursively(initializedData, formField, indexes) {
6812
8126
  const {
6813
8127
  defaultValue,
6814
- type
8128
+ type,
8129
+ isRepeating
6815
8130
  } = formField;
8131
+ const {
8132
+ config: fieldConfig
8133
+ } = formFields.get(type);
8134
+ const valuePath = pathRegistry.getValuePath(formField, {
8135
+ indexes
8136
+ });
8137
+ let valueData = get(data, valuePath);
6816
8138
 
6817
- // try to get value from data
6818
- // if unavailable - try to get default value from form field
6819
- // if unavailable - get empty value from form field
6820
-
6821
- const valuePath = pathRegistry.getValuePath(formField);
6822
- if (valuePath) {
6823
- const {
6824
- config: fieldConfig
6825
- } = formFields.get(type);
6826
- let valueData = get(data, valuePath);
8139
+ // (1) Process keyed fields
8140
+ if (fieldConfig.keyed) {
8141
+ // (a) Retrieve and sanitize data from input
6827
8142
  if (!isUndefined(valueData) && fieldConfig.sanitizeValue) {
6828
8143
  valueData = fieldConfig.sanitizeValue({
6829
8144
  formField,
@@ -6831,15 +8146,66 @@ class Form {
6831
8146
  value: valueData
6832
8147
  });
6833
8148
  }
8149
+
8150
+ // (b) Initialize field value in output data
6834
8151
  const initializedFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
6835
- return set(initializedData, valuePath, initializedFieldValue);
8152
+ set(initializedData, valuePath, initializedFieldValue);
8153
+ }
8154
+
8155
+ // (2) Process parents
8156
+ if (!Array.isArray(formField.components)) {
8157
+ return;
8158
+ }
8159
+ if (fieldConfig.repeatable && isRepeating) {
8160
+ // (a) Sanitize repeatable parents data if it is not an array
8161
+ if (!valueData || !Array.isArray(valueData)) {
8162
+ valueData = new Array(isUndefined(formField.defaultRepetitions) ? 1 : formField.defaultRepetitions).fill().map(_ => ({})) || [];
8163
+ }
8164
+
8165
+ // (b) Ensure all elements of the array are objects
8166
+ valueData = valueData.map(val => isObject(val) ? val : {});
8167
+
8168
+ // (c) Initialize field value in output data
8169
+ set(initializedData, valuePath, valueData);
8170
+
8171
+ // (d) If indexed ahead of time, recurse repeatable simply across the children
8172
+ if (!isUndefined(indexes[formField.id])) {
8173
+ formField.components.forEach(component => initializeFieldDataRecursively(initializedData, component, {
8174
+ ...indexes
8175
+ }));
8176
+ return;
8177
+ }
8178
+
8179
+ // (e1) Recurse repeatable parents both across the indexes of repetition and the children
8180
+ valueData.forEach((_, index) => {
8181
+ formField.components.forEach(component => initializeFieldDataRecursively(initializedData, component, {
8182
+ ...indexes,
8183
+ [formField.id]: index
8184
+ }));
8185
+ });
8186
+ return;
6836
8187
  }
6837
- return initializedData;
6838
- }, data);
8188
+
8189
+ // (e2) Recurse non-repeatable parents only across the children
8190
+ formField.components.forEach(component => initializeFieldDataRecursively(initializedData, component, indexes));
8191
+ }
8192
+
8193
+ // allows definition of a specific subfield to generate the data for
8194
+ const container = options.container || formFieldRegistry.getForm();
8195
+ const indexes = options.indexes || {};
8196
+ const basePath = pathRegistry.getValuePath(container, {
8197
+ indexes
8198
+ }) || [];
8199
+
8200
+ // if indexing ahead of time, we must add this index to the data path at the end
8201
+ const path = !isUndefined(indexes[container.id]) ? [...basePath, indexes[container.id]] : basePath;
8202
+ const workingData = clone(data);
8203
+ initializeFieldDataRecursively(workingData, container, indexes);
8204
+ return get(workingData, path, {});
6839
8205
  }
6840
8206
  }
6841
8207
 
6842
- const schemaVersion = 12;
8208
+ const schemaVersion = 14;
6843
8209
 
6844
8210
  /**
6845
8211
  * @typedef { import('./types').CreateFormOptions } CreateFormOptions
@@ -6864,5 +8230,5 @@ function createForm(options) {
6864
8230
  });
6865
8231
  }
6866
8232
 
6867
- export { Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, FormComponent$1 as Default, Description, Errors, ExpressionLanguageModule, FeelExpressionLanguage, FeelersTemplating, FieldFactory, Form, FormComponent, FormContext$1 as FormContext, FormField, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext$1 as FormRenderContext, Group, Image, Importer, Label, MINUTES_IN_DAY, MarkdownModule, MarkdownRenderer, Numberfield, PathRegistry, Radio, Select, Separator, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Taglist, Text, Textarea, Textfield, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, VALUES_SOURCES_PATHS, VALUES_SOURCE_DEFAULT, ViewerCommands, ViewerCommandsModule, clone, createForm, createFormContainer, createInjector, formFields, generateIdForType, generateIndexForType, getSchemaVariables, getValuesSource, iconsByType, isRequired, pathParse, pathsEqual, runRecursively, sanitizeHTML, sanitizeImageSource, schemaVersion };
8233
+ export { Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, FormComponent$1 as Default, Description, DynamicList, Errors, ExpressionLanguageModule, FeelExpressionLanguage, FeelersTemplating, FieldFactory, Form, FormComponent, FormContext$1 as FormContext, FormField, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext$1 as FormRenderContext, Group, IFrame, Image, Importer, Label, LocalExpressionContext$1 as LocalExpressionContext, MINUTES_IN_DAY, MarkdownModule, MarkdownRenderer, Numberfield, OPTIONS_SOURCES, OPTIONS_SOURCES_DEFAULTS, OPTIONS_SOURCES_LABELS, OPTIONS_SOURCES_PATHS, OPTIONS_SOURCE_DEFAULT, PathRegistry, Radio, RepeatRenderManager, RepeatRenderModule, Select, Separator, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Table, Taglist, Text, Textarea, Textfield, ViewerCommands, ViewerCommandsModule, buildExpressionContext, clone, createForm, createFormContainer, createInjector, formFields, generateIdForType, generateIndexForType, getAncestryList, getOptionsSource, getSchemaVariables, iconsByType, isRequired, pathParse, pathsEqual, runRecursively, sanitizeHTML, sanitizeIFrameSource, sanitizeImageSource, schemaVersion, useExpressionEvaluation, useSingleLineTemplateEvaluation, useTemplateEvaluation };
6868
8234
  //# sourceMappingURL=index.es.js.map