@bpmn-io/form-js-editor 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1,15 +1,15 @@
1
- import { FormFieldRegistry as FormFieldRegistry$1, clone, FormContext, FormRenderContext, FormComponent, iconsByType, FormFields, getSchemaVariables, Default, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_PATHS, VALUES_SOURCES_LABELS, createFormContainer, createInjector, schemaVersion } from '@bpmn-io/form-js-viewer';
1
+ import { FormFieldRegistry as FormFieldRegistry$1, clone, iconsByType, isExpression, Text as Text$1, FormFields, formFields, FormContext, FormRenderContext, FormComponent, getSchemaVariables, Default, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_PATHS, VALUES_SOURCES_LABELS, createFormContainer, createInjector, schemaVersion } from '@bpmn-io/form-js-viewer';
2
2
  export { schemaVersion } from '@bpmn-io/form-js-viewer';
3
3
  import Ids from 'ids';
4
- import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, set, isUndefined, without, has, isString } from 'min-dash';
4
+ import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, set as set$1, isUndefined, without, has, isString } from 'min-dash';
5
+ import classnames from 'classnames';
6
+ import { jsx, jsxs } from 'preact/jsx-runtime';
5
7
  import { createContext, render, createElement } from 'preact';
6
- import { useContext, useRef, useState, useEffect, useCallback, useMemo, useLayoutEffect } from 'preact/hooks';
8
+ import { useContext, useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect } from 'preact/hooks';
7
9
  import dragula from 'dragula';
8
- import { jsxs, jsx } from 'preact/jsx-runtime';
9
10
  import React, { forwardRef } from 'preact/compat';
10
- import { matches, closest, event, domify, query } from 'min-dom';
11
+ import { classes, closest, event, matches, domify, query } from 'min-dom';
11
12
  import { mutate } from 'array-move';
12
- import classnames from 'classnames';
13
13
  import FeelEditor from '@bpmn-io/feel-editor';
14
14
  import Big from 'big.js';
15
15
 
@@ -671,9 +671,98 @@ var importModule = {
671
671
  importer: ['type', Importer]
672
672
  };
673
673
 
674
+ function editorFormFieldClasses(type, {
675
+ disabled = false
676
+ } = {}) {
677
+ if (!type) {
678
+ throw new Error('type required');
679
+ }
680
+ return classnames('fjs-form-field', `fjs-form-field-${type}`, {
681
+ 'fjs-disabled': disabled
682
+ });
683
+ }
684
+
685
+ 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); }
686
+ var CloseIcon = (({
687
+ styles = {},
688
+ ...props
689
+ }) => /*#__PURE__*/React.createElement("svg", _extends$2({
690
+ width: "16",
691
+ height: "16",
692
+ fill: "currentColor",
693
+ xmlns: "http://www.w3.org/2000/svg"
694
+ }, props), /*#__PURE__*/React.createElement("path", {
695
+ fillRule: "evenodd",
696
+ clipRule: "evenodd",
697
+ d: "M12 4.7l-.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"
698
+ })));
699
+
700
+ 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); }
701
+ var SearchIcon = (({
702
+ styles = {},
703
+ ...props
704
+ }) => /*#__PURE__*/React.createElement("svg", _extends$1({
705
+ width: "15",
706
+ height: "15",
707
+ fill: "none",
708
+ xmlns: "http://www.w3.org/2000/svg"
709
+ }, props), /*#__PURE__*/React.createElement("path", {
710
+ d: "M14.5 13.793l-3.776-3.776a5.508 5.508 0 10-.707.707l3.776 3.776.707-.707zM2 6.5a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0z",
711
+ fill: "currentColor"
712
+ })));
713
+
714
+ function EditorText(props) {
715
+ const {
716
+ type,
717
+ text = ''
718
+ } = props.field;
719
+ const Icon = iconsByType['text'];
720
+ if (!text) {
721
+ return jsx("div", {
722
+ class: editorFormFieldClasses(type),
723
+ children: jsxs("div", {
724
+ class: "fjs-form-field-placeholder",
725
+ children: [jsx(Icon, {
726
+ viewBox: "0 0 54 54"
727
+ }), "Text view is empty"]
728
+ })
729
+ });
730
+ }
731
+ if (isExpression(text)) {
732
+ return jsx("div", {
733
+ class: editorFormFieldClasses(type),
734
+ children: jsxs("div", {
735
+ class: "fjs-form-field-placeholder",
736
+ children: [jsx(Icon, {
737
+ viewBox: "0 0 54 54"
738
+ }), "Text view is populated by an expression"]
739
+ })
740
+ });
741
+ }
742
+ return jsx(Text$1, {
743
+ ...props,
744
+ disableLinks: true
745
+ });
746
+ }
747
+ EditorText.create = Text$1.create;
748
+ EditorText.type = Text$1.type;
749
+ EditorText.keyed = Text$1.keyed;
750
+
751
+ const editorFormFields = [EditorText];
752
+
753
+ class EditorFormFields extends FormFields {
754
+ constructor() {
755
+ super();
756
+ editorFormFields.forEach(formField => {
757
+ this.register(formField.type, formField);
758
+ });
759
+ }
760
+ }
761
+
674
762
  const DragAndDropContext = createContext({
675
763
  drake: null
676
764
  });
765
+ var DragAndDropContext$1 = DragAndDropContext;
677
766
 
678
767
  /**
679
768
  * @param {string} type
@@ -685,11 +774,12 @@ function getService$1(type, strict) {}
685
774
  const FormEditorContext = createContext({
686
775
  getService: getService$1
687
776
  });
777
+ var FormEditorContext$1 = FormEditorContext;
688
778
 
689
779
  function useService$1 (type, strict) {
690
780
  const {
691
781
  getService
692
- } = useContext(FormEditorContext);
782
+ } = useContext(FormEditorContext$1);
693
783
  return getService(type, strict);
694
784
  }
695
785
 
@@ -705,6 +795,149 @@ var ListDeleteIcon = (({
705
795
  d: "M10 4v8c0 1.1-.9 2-2 2H3c-1.1 0-2-.9-2-2V4h9zM8 6H3v4.8c0 .66.5 1.2 1.111 1.2H6.89C7.5 12 8 11.46 8 10.8V6zm3-5H8.5l-1-1h-4l-1 1H0v1.5h11V1z"
706
796
  })));
707
797
 
798
+ const PALETTE_ENTRIES = formFields.filter(f => f.type !== 'default').map(f => {
799
+ return {
800
+ label: f.label,
801
+ type: f.type,
802
+ group: f.group
803
+ };
804
+ });
805
+ const PALETTE_GROUPS = [{
806
+ label: 'Basic input',
807
+ id: 'basic-input'
808
+ }, {
809
+ label: 'Selection',
810
+ id: 'selection'
811
+ }, {
812
+ label: 'Presentation',
813
+ id: 'presentation'
814
+ }, {
815
+ label: 'Action',
816
+ id: 'action'
817
+ }];
818
+ function Palette(props) {
819
+ const [entries, setEntries] = useState(PALETTE_ENTRIES);
820
+ const [searchTerm, setSearchTerm] = useState('');
821
+ const inputRef = useRef();
822
+ const groups = groupEntries(entries);
823
+
824
+ // filter entries on search change
825
+ useEffect(() => {
826
+ const filter = entry => {
827
+ if (!searchTerm) {
828
+ return true;
829
+ }
830
+ const search = entry.label.toLowerCase();
831
+ return searchTerm.toLowerCase().split(/\s/g).every(term => search.includes(term));
832
+ };
833
+ const entries = PALETTE_ENTRIES.filter(filter);
834
+ setEntries(entries);
835
+ }, [searchTerm]);
836
+ const handleInput = useCallback(event => {
837
+ setSearchTerm(() => event.target.value);
838
+ }, [setSearchTerm]);
839
+ const handleClear = useCallback(event => {
840
+ setSearchTerm('');
841
+ inputRef.current.focus();
842
+ }, [inputRef, setSearchTerm]);
843
+ return jsxs("div", {
844
+ class: "fjs-palette",
845
+ children: [jsx("div", {
846
+ class: "fjs-palette-header",
847
+ title: "Components",
848
+ children: "Components"
849
+ }), jsxs("div", {
850
+ class: "fjs-palette-search-container",
851
+ children: [jsx("span", {
852
+ class: "fjs-palette-search-icon",
853
+ children: jsx(SearchIcon, {})
854
+ }), jsx("input", {
855
+ class: "fjs-palette-search",
856
+ ref: inputRef,
857
+ type: "text",
858
+ placeholder: "Search components",
859
+ value: searchTerm,
860
+ onInput: handleInput
861
+ }), searchTerm && jsx("button", {
862
+ title: "Clear content",
863
+ class: "fjs-palette-search-clear",
864
+ onClick: handleClear,
865
+ children: jsx(CloseIcon, {})
866
+ })]
867
+ }), jsxs("div", {
868
+ class: "fjs-palette-entries",
869
+ children: [groups.map(({
870
+ label,
871
+ entries,
872
+ id
873
+ }) => jsxs("div", {
874
+ class: "fjs-palette-group",
875
+ "data-group-id": id,
876
+ children: [jsx("span", {
877
+ class: "fjs-palette-group-title",
878
+ children: label
879
+ }), jsx("div", {
880
+ class: "fjs-palette-fields fjs-drag-container fjs-no-drop",
881
+ children: entries.map(({
882
+ label,
883
+ type
884
+ }) => {
885
+ const Icon = iconsByType[type];
886
+ return jsxs("div", {
887
+ class: "fjs-palette-field fjs-drag-copy fjs-no-drop",
888
+ "data-field-type": type,
889
+ title: `Create a ${label} element`,
890
+ children: [Icon ? jsx(Icon, {
891
+ class: "fjs-palette-field-icon",
892
+ width: "36",
893
+ height: "36",
894
+ viewBox: "0 0 54 54"
895
+ }) : null, jsx("span", {
896
+ class: "fjs-palette-field-text",
897
+ children: label
898
+ })]
899
+ });
900
+ })
901
+ })]
902
+ })), groups.length == 0 && jsx("div", {
903
+ class: "fjs-palette-no-entries",
904
+ children: "No components found."
905
+ })]
906
+ })]
907
+ });
908
+ }
909
+
910
+ // helpers ///////
911
+
912
+ function groupEntries(entries) {
913
+ const groups = PALETTE_GROUPS.map(group => {
914
+ return {
915
+ ...group,
916
+ entries: []
917
+ };
918
+ });
919
+ const getGroup = id => groups.find(group => id === group.id);
920
+ entries.forEach(entry => {
921
+ const {
922
+ group
923
+ } = entry;
924
+ getGroup(group).entries.push(entry);
925
+ });
926
+ return groups.filter(g => g.entries.length);
927
+ }
928
+
929
+ const CURSOR_CLS_PATTERN = /^fjs-cursor-.*$/;
930
+ function set(mode) {
931
+ const classes$1 = classes(document.body);
932
+ classes$1.removeMatching(CURSOR_CLS_PATTERN);
933
+ if (mode) {
934
+ classes$1.add('fjs-cursor-' + mode);
935
+ }
936
+ }
937
+ function unset() {
938
+ set(null);
939
+ }
940
+
708
941
  function ContextPad(props) {
709
942
  if (!props.children) {
710
943
  return null;
@@ -828,6 +1061,9 @@ function FormEditor$1(props) {
828
1061
  drake
829
1062
  };
830
1063
  useEffect(() => {
1064
+ const handleDragEvent = (type, context) => {
1065
+ return eventBus.fire(type, context);
1066
+ };
831
1067
  const createDragulaInstance = () => {
832
1068
  const dragulaInstance = dragula({
833
1069
  isContainer(el) {
@@ -842,6 +1078,56 @@ function FormEditor$1(props) {
842
1078
  slideFactorX: 10,
843
1079
  slideFactorY: 5
844
1080
  });
1081
+
1082
+ // bind life cycle events
1083
+ dragulaInstance.on('drag', (element, source) => {
1084
+ handleDragEvent('drag.start', {
1085
+ element,
1086
+ source
1087
+ });
1088
+ });
1089
+ dragulaInstance.on('dragend', element => {
1090
+ handleDragEvent('drag.end', {
1091
+ element
1092
+ });
1093
+ });
1094
+ dragulaInstance.on('drop', (element, target, source, sibling) => {
1095
+ handleDragEvent('drag.drop', {
1096
+ element,
1097
+ target,
1098
+ source,
1099
+ sibling
1100
+ });
1101
+ });
1102
+ dragulaInstance.on('over', (element, container, source) => {
1103
+ handleDragEvent('drag.hover', {
1104
+ element,
1105
+ container,
1106
+ source
1107
+ });
1108
+ });
1109
+ dragulaInstance.on('out', (element, container, source) => {
1110
+ handleDragEvent('drag.out', {
1111
+ element,
1112
+ container,
1113
+ source
1114
+ });
1115
+ });
1116
+ dragulaInstance.on('cancel', (element, container, source) => {
1117
+ handleDragEvent('drag.cancel', {
1118
+ element,
1119
+ container,
1120
+ source
1121
+ });
1122
+ });
1123
+
1124
+ // set custom styling
1125
+ dragulaInstance.on('drag', () => {
1126
+ set('grabbing');
1127
+ });
1128
+ dragulaInstance.on('dragend', () => {
1129
+ unset();
1130
+ });
845
1131
  dragulaInstance.on('drop', (el, target, source, sibling) => {
846
1132
  dragulaInstance.remove();
847
1133
  if (!target) {
@@ -898,9 +1184,7 @@ function FormEditor$1(props) {
898
1184
  const formContext = {
899
1185
  getService(type, strict = true) {
900
1186
  // TODO(philippfromme): clean up
901
- if (type === 'formFieldRegistry') {
902
- return new Map();
903
- } else if (type === 'form') {
1187
+ if (type === 'form') {
904
1188
  return {
905
1189
  _getState() {
906
1190
  return {
@@ -938,7 +1222,7 @@ function FormEditor$1(props) {
938
1222
  }, [propertiesPanelRef, propertiesPanel, hasDefaultPropertiesPanel]);
939
1223
  return jsxs("div", {
940
1224
  class: "fjs-form-editor",
941
- children: [jsxs(DragAndDropContext.Provider, {
1225
+ children: [jsxs(DragAndDropContext$1.Provider, {
942
1226
  value: dragAndDropContext,
943
1227
  children: [hasDefaultPalette && jsx("div", {
944
1228
  class: "fjs-editor-palette-container",
@@ -976,14 +1260,32 @@ function getFormFieldIndex(parent, formField) {
976
1260
  function CreatePreview(props) {
977
1261
  const {
978
1262
  drake
979
- } = useContext(DragAndDropContext);
1263
+ } = useContext(DragAndDropContext$1);
980
1264
  function handleCloned(clone, original, type) {
981
1265
  const fieldType = clone.dataset.fieldType;
982
1266
  const Icon = iconsByType[fieldType];
1267
+ const {
1268
+ label
1269
+ } = findPaletteEntry(fieldType);
983
1270
  if (fieldType) {
984
1271
  clone.innerHTML = '';
985
1272
  clone.class = 'gu-mirror';
986
- render(jsx(Icon, {}), clone);
1273
+ if (original.classList.contains('fjs-palette-field')) {
1274
+ render(jsxs("div", {
1275
+ class: "fjs-palette-field",
1276
+ children: [jsx(Icon, {
1277
+ class: "fjs-palette-field-icon",
1278
+ width: "36",
1279
+ height: "36",
1280
+ viewBox: "0 0 54 54"
1281
+ }), jsx("span", {
1282
+ class: "fjs-palette-field-text",
1283
+ children: label
1284
+ })]
1285
+ }), clone);
1286
+ } else {
1287
+ render(jsx(Icon, {}), clone);
1288
+ }
987
1289
  }
988
1290
  }
989
1291
  useEffect(() => {
@@ -1004,6 +1306,9 @@ function defaultPalette(paletteConfig) {
1004
1306
  function defaultPropertiesPanel(propertiesPanelConfig) {
1005
1307
  return !(propertiesPanelConfig && propertiesPanelConfig.parent);
1006
1308
  }
1309
+ function findPaletteEntry(type) {
1310
+ return PALETTE_ENTRIES.find(entry => entry.type === type);
1311
+ }
1007
1312
 
1008
1313
  class Renderer {
1009
1314
  constructor(renderConfig, eventBus, formEditor, injector) {
@@ -1029,7 +1334,7 @@ class Renderer {
1029
1334
  }
1030
1335
  return jsx("div", {
1031
1336
  class: `fjs-container fjs-editor-container ${compact ? 'fjs-editor-compact' : ''}`,
1032
- children: jsx(FormEditorContext.Provider, {
1337
+ children: jsx(FormEditorContext$1.Provider, {
1033
1338
  value: formEditorContext,
1034
1339
  children: jsx(FormEditor$1, {})
1035
1340
  })
@@ -1047,7 +1352,7 @@ Renderer.$inject = ['config.renderer', 'eventBus', 'formEditor', 'injector'];
1047
1352
 
1048
1353
  var renderModule = {
1049
1354
  __init__: ['formFields', 'renderer'],
1050
- formFields: ['type', FormFields],
1355
+ formFields: ['type', EditorFormFields],
1051
1356
  renderer: ['type', Renderer]
1052
1357
  };
1053
1358
 
@@ -2364,10 +2669,40 @@ class KeyBehavior extends CommandInterceptor {
2364
2669
  }
2365
2670
  KeyBehavior.$inject = ['eventBus', 'modeling'];
2366
2671
 
2672
+ class ValidateBehavior extends CommandInterceptor {
2673
+ constructor(eventBus) {
2674
+ super(eventBus);
2675
+
2676
+ /**
2677
+ * Remove custom validation if <validationType> is about to be added.
2678
+ */
2679
+ // @ts-ignore-next-line
2680
+ this.preExecute('formField.edit', function (context) {
2681
+ const {
2682
+ properties
2683
+ } = context;
2684
+ const {
2685
+ validate = {}
2686
+ } = properties;
2687
+ if (validate.validationType) {
2688
+ const newValidate = {
2689
+ ...validate
2690
+ };
2691
+ delete newValidate.minLength;
2692
+ delete newValidate.maxLength;
2693
+ delete newValidate.pattern;
2694
+ properties['validate'] = newValidate;
2695
+ }
2696
+ }, true);
2697
+ }
2698
+ }
2699
+ ValidateBehavior.$inject = ['eventBus'];
2700
+
2367
2701
  var behaviorModule = {
2368
- __init__: ['idBehavior', 'keyBehavior'],
2702
+ __init__: ['idBehavior', 'keyBehavior', 'validateBehavior'],
2369
2703
  idBehavior: ['type', IdBehavior],
2370
- keyBehavior: ['type', KeyBehavior]
2704
+ keyBehavior: ['type', KeyBehavior],
2705
+ validateBehavior: ['type', ValidateBehavior]
2371
2706
  };
2372
2707
 
2373
2708
  /**
@@ -2868,79 +3203,6 @@ var SelectionModule = {
2868
3203
  selectionBehavior: ['type', SelectionBehavior]
2869
3204
  };
2870
3205
 
2871
- const types = [{
2872
- label: 'Text field',
2873
- type: 'textfield'
2874
- }, {
2875
- label: 'Text area',
2876
- type: 'textarea'
2877
- }, {
2878
- label: 'Number',
2879
- type: 'number'
2880
- }, {
2881
- label: 'Datetime',
2882
- type: 'datetime'
2883
- }, {
2884
- label: 'Checkbox',
2885
- type: 'checkbox'
2886
- }, {
2887
- label: 'Checklist',
2888
- type: 'checklist'
2889
- }, {
2890
- label: 'Taglist',
2891
- type: 'taglist'
2892
- }, {
2893
- label: 'Radio',
2894
- type: 'radio'
2895
- }, {
2896
- label: 'Select',
2897
- type: 'select'
2898
- }, {
2899
- label: 'Text view',
2900
- type: 'text'
2901
- }, {
2902
- label: 'Image view',
2903
- type: 'image'
2904
- }, {
2905
- label: 'Button',
2906
- type: 'button'
2907
- }];
2908
- function Palette(props) {
2909
- return jsxs("div", {
2910
- class: "fjs-palette",
2911
- children: [jsxs("div", {
2912
- class: "fjs-palette-header",
2913
- title: "Form elements library",
2914
- children: [jsx("span", {
2915
- class: "fjs-hide-compact",
2916
- children: "FORM ELEMENTS "
2917
- }), "LIBRARY"]
2918
- }), jsx("div", {
2919
- class: "fjs-palette-fields fjs-drag-container fjs-no-drop",
2920
- children: types.map(({
2921
- label,
2922
- type
2923
- }) => {
2924
- const Icon = iconsByType[type];
2925
- return jsxs("div", {
2926
- class: "fjs-palette-field fjs-drag-copy fjs-no-drop",
2927
- "data-field-type": type,
2928
- title: `Create a ${label} element`,
2929
- children: [Icon ? jsx(Icon, {
2930
- class: "fjs-palette-field-icon",
2931
- width: "36",
2932
- height: "36",
2933
- viewBox: "0 0 54 54"
2934
- }) : null, jsx("span", {
2935
- class: "fjs-palette-field-text",
2936
- children: label
2937
- })]
2938
- });
2939
- })
2940
- })]
2941
- });
2942
- }
2943
-
2944
3206
  class PaletteRenderer {
2945
3207
  constructor(paletteConfig, eventBus) {
2946
3208
  const {
@@ -3162,19 +3424,19 @@ const ErrorsContext = createContext({
3162
3424
  errors: {}
3163
3425
  });
3164
3426
 
3165
- /**
3166
- * @typedef {Function} <propertiesPanel.showEntry> callback
3167
- *
3168
- * @example
3169
- *
3170
- * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
3171
- * // ...
3172
- * });
3173
- *
3174
- * @param {Object} context
3175
- * @param {boolean} [context.focus]
3176
- *
3177
- * @returns void
3427
+ /**
3428
+ * @typedef {Function} <propertiesPanel.showEntry> callback
3429
+ *
3430
+ * @example
3431
+ *
3432
+ * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
3433
+ * // ...
3434
+ * });
3435
+ *
3436
+ * @param {Object} context
3437
+ * @param {boolean} [context.focus]
3438
+ *
3439
+ * @returns void
3178
3440
  */
3179
3441
  const EventContext = createContext({
3180
3442
  eventBus: null
@@ -3186,20 +3448,20 @@ const LayoutContext = createContext({
3186
3448
  setLayoutForKey: () => {}
3187
3449
  });
3188
3450
 
3189
- /**
3190
- * Accesses the global DescriptionContext and returns a description for a given id and element.
3191
- *
3192
- * @example
3193
- * ```jsx
3194
- * function TextField(props) {
3195
- * const description = useDescriptionContext('input1', element);
3196
- * }
3197
- * ```
3198
- *
3199
- * @param {string} id
3200
- * @param {object} element
3201
- *
3202
- * @returns {string}
3451
+ /**
3452
+ * Accesses the global DescriptionContext and returns a description for a given id and element.
3453
+ *
3454
+ * @example
3455
+ * ```jsx
3456
+ * function TextField(props) {
3457
+ * const description = useDescriptionContext('input1', element);
3458
+ * }
3459
+ * ```
3460
+ *
3461
+ * @param {string} id
3462
+ * @param {object} element
3463
+ *
3464
+ * @returns {string}
3203
3465
  */
3204
3466
  function useDescriptionContext(id, element) {
3205
3467
  const {
@@ -3214,11 +3476,11 @@ function useError(id) {
3214
3476
  return errors[id];
3215
3477
  }
3216
3478
 
3217
- /**
3218
- * Subscribe to an event immediately. Update subscription after inputs changed.
3219
- *
3220
- * @param {string} event
3221
- * @param {Function} callback
3479
+ /**
3480
+ * Subscribe to an event immediately. Update subscription after inputs changed.
3481
+ *
3482
+ * @param {string} event
3483
+ * @param {Function} callback
3222
3484
  */
3223
3485
  function useEvent(event, callback, eventBus) {
3224
3486
  const eventContext = useContext(EventContext);
@@ -3248,20 +3510,20 @@ function useEvent(event, callback, eventBus) {
3248
3510
  }, [callback, event, eventBus]);
3249
3511
  }
3250
3512
 
3251
- /**
3252
- * Creates a state that persists in the global LayoutContext.
3253
- *
3254
- * @example
3255
- * ```jsx
3256
- * function Group(props) {
3257
- * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
3258
- * }
3259
- * ```
3260
- *
3261
- * @param {(string|number)[]} path
3262
- * @param {any} [defaultValue]
3263
- *
3264
- * @returns {[ any, Function ]}
3513
+ /**
3514
+ * Creates a state that persists in the global LayoutContext.
3515
+ *
3516
+ * @example
3517
+ * ```jsx
3518
+ * function Group(props) {
3519
+ * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
3520
+ * }
3521
+ * ```
3522
+ *
3523
+ * @param {(string|number)[]} path
3524
+ * @param {any} [defaultValue]
3525
+ *
3526
+ * @returns {[ any, Function ]}
3265
3527
  */
3266
3528
  function useLayoutState(path, defaultValue) {
3267
3529
  const {
@@ -3280,11 +3542,11 @@ function useLayoutState(path, defaultValue) {
3280
3542
  return [value, setState];
3281
3543
  }
3282
3544
 
3283
- /**
3284
- * @pinussilvestrus: we need to introduce our own hook to persist the previous
3285
- * state on updates.
3286
- *
3287
- * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
3545
+ /**
3546
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
3547
+ * state on updates.
3548
+ *
3549
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
3288
3550
  */
3289
3551
 
3290
3552
  function usePrevious(value) {
@@ -3295,12 +3557,12 @@ function usePrevious(value) {
3295
3557
  return ref.current;
3296
3558
  }
3297
3559
 
3298
- /**
3299
- * Subscribe to `propertiesPanel.showEntry`.
3300
- *
3301
- * @param {string} id
3302
- *
3303
- * @returns {import('preact').Ref}
3560
+ /**
3561
+ * Subscribe to `propertiesPanel.showEntry`.
3562
+ *
3563
+ * @param {string} id
3564
+ *
3565
+ * @returns {import('preact').Ref}
3304
3566
  */
3305
3567
  function useShowEntryEvent(id) {
3306
3568
  const {
@@ -3331,20 +3593,20 @@ function useShowEntryEvent(id) {
3331
3593
  return ref;
3332
3594
  }
3333
3595
 
3334
- /**
3335
- * @callback setSticky
3336
- * @param {boolean} value
3596
+ /**
3597
+ * @callback setSticky
3598
+ * @param {boolean} value
3337
3599
  */
3338
3600
 
3339
- /**
3340
- * Use IntersectionObserver to identify when DOM element is in sticky mode.
3341
- * If sticky is observered setSticky(true) will be called.
3342
- * If sticky mode is left, setSticky(false) will be called.
3343
- *
3344
- *
3345
- * @param {Object} ref
3346
- * @param {string} scrollContainerSelector
3347
- * @param {setSticky} setSticky
3601
+ /**
3602
+ * Use IntersectionObserver to identify when DOM element is in sticky mode.
3603
+ * If sticky is observered setSticky(true) will be called.
3604
+ * If sticky mode is left, setSticky(false) will be called.
3605
+ *
3606
+ *
3607
+ * @param {Object} ref
3608
+ * @param {string} scrollContainerSelector
3609
+ * @param {setSticky} setSticky
3348
3610
  */
3349
3611
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
3350
3612
  useEffect(() => {
@@ -3383,19 +3645,19 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
3383
3645
  }, [ref, scrollContainerSelector, setSticky]);
3384
3646
  }
3385
3647
 
3386
- /**
3387
- * Creates a static function reference with changing body.
3388
- * This is necessary when external libraries require a callback function
3389
- * that has references to state variables.
3390
- *
3391
- * Usage:
3392
- * const callback = useStaticCallback((val) => {val === currentState});
3393
- *
3394
- * The `callback` reference is static and can be safely used in external
3395
- * libraries or as a prop that does not cause rerendering of children.
3396
- *
3397
- * @param {Function} callback function with changing reference
3398
- * @returns {Function} static function reference
3648
+ /**
3649
+ * Creates a static function reference with changing body.
3650
+ * This is necessary when external libraries require a callback function
3651
+ * that has references to state variables.
3652
+ *
3653
+ * Usage:
3654
+ * const callback = useStaticCallback((val) => {val === currentState});
3655
+ *
3656
+ * The `callback` reference is static and can be safely used in external
3657
+ * libraries or as a prop that does not cause rerendering of children.
3658
+ *
3659
+ * @param {Function} callback function with changing reference
3660
+ * @returns {Function} static function reference
3399
3661
  */
3400
3662
  function useStaticCallback(callback) {
3401
3663
  const callbackRef = useRef(callback);
@@ -3487,13 +3749,13 @@ function DataMarker() {
3487
3749
  });
3488
3750
  }
3489
3751
 
3490
- /**
3491
- * @typedef { {
3492
- * text: (element: object) => string,
3493
- * icon?: (element: Object) => import('preact').Component
3494
- * } } PlaceholderDefinition
3495
- *
3496
- * @param { PlaceholderDefinition } props
3752
+ /**
3753
+ * @typedef { {
3754
+ * text: (element: object) => string,
3755
+ * icon?: (element: Object) => import('preact').Component
3756
+ * } } PlaceholderDefinition
3757
+ *
3758
+ * @param { PlaceholderDefinition } props
3497
3759
  */
3498
3760
  function Placeholder(props) {
3499
3761
  const {
@@ -3518,72 +3780,72 @@ const DEFAULT_LAYOUT = {
3518
3780
  };
3519
3781
  const DEFAULT_DESCRIPTION = {};
3520
3782
 
3521
- /**
3522
- * @typedef { {
3523
- * component: import('preact').Component,
3524
- * id: String,
3525
- * isEdited?: Function
3526
- * } } EntryDefinition
3527
- *
3528
- * @typedef { {
3529
- * autoFocusEntry: String,
3530
- * autoOpen?: Boolean,
3531
- * entries: Array<EntryDefinition>,
3532
- * id: String,
3533
- * label: String,
3534
- * remove: (event: MouseEvent) => void
3535
- * } } ListItemDefinition
3536
- *
3537
- * @typedef { {
3538
- * add: (event: MouseEvent) => void,
3539
- * component: import('preact').Component,
3540
- * element: Object,
3541
- * id: String,
3542
- * items: Array<ListItemDefinition>,
3543
- * label: String,
3544
- * shouldSort?: Boolean,
3545
- * shouldOpen?: Boolean
3546
- * } } ListGroupDefinition
3547
- *
3548
- * @typedef { {
3549
- * component?: import('preact').Component,
3550
- * entries: Array<EntryDefinition>,
3551
- * id: String,
3552
- * label: String,
3553
- * shouldOpen?: Boolean
3554
- * } } GroupDefinition
3555
- *
3556
- * @typedef { {
3557
- * [id: String]: GetDescriptionFunction
3558
- * } } DescriptionConfig
3559
- *
3560
- * @callback { {
3561
- * @param {string} id
3562
- * @param {Object} element
3563
- * @returns {string}
3564
- * } } GetDescriptionFunction
3565
- *
3566
- * @typedef { {
3567
- * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
3568
- * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
3569
- * } } PlaceholderProvider
3570
- *
3783
+ /**
3784
+ * @typedef { {
3785
+ * component: import('preact').Component,
3786
+ * id: String,
3787
+ * isEdited?: Function
3788
+ * } } EntryDefinition
3789
+ *
3790
+ * @typedef { {
3791
+ * autoFocusEntry: String,
3792
+ * autoOpen?: Boolean,
3793
+ * entries: Array<EntryDefinition>,
3794
+ * id: String,
3795
+ * label: String,
3796
+ * remove: (event: MouseEvent) => void
3797
+ * } } ListItemDefinition
3798
+ *
3799
+ * @typedef { {
3800
+ * add: (event: MouseEvent) => void,
3801
+ * component: import('preact').Component,
3802
+ * element: Object,
3803
+ * id: String,
3804
+ * items: Array<ListItemDefinition>,
3805
+ * label: String,
3806
+ * shouldSort?: Boolean,
3807
+ * shouldOpen?: Boolean
3808
+ * } } ListGroupDefinition
3809
+ *
3810
+ * @typedef { {
3811
+ * component?: import('preact').Component,
3812
+ * entries: Array<EntryDefinition>,
3813
+ * id: String,
3814
+ * label: String,
3815
+ * shouldOpen?: Boolean
3816
+ * } } GroupDefinition
3817
+ *
3818
+ * @typedef { {
3819
+ * [id: String]: GetDescriptionFunction
3820
+ * } } DescriptionConfig
3821
+ *
3822
+ * @callback { {
3823
+ * @param {string} id
3824
+ * @param {Object} element
3825
+ * @returns {string}
3826
+ * } } GetDescriptionFunction
3827
+ *
3828
+ * @typedef { {
3829
+ * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
3830
+ * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
3831
+ * } } PlaceholderProvider
3832
+ *
3571
3833
  */
3572
3834
 
3573
- /**
3574
- * A basic properties panel component. Describes *how* content will be rendered, accepts
3575
- * data from implementor to describe *what* will be rendered.
3576
- *
3577
- * @param {Object} props
3578
- * @param {Object|Array} props.element
3579
- * @param {import('./components/Header').HeaderProvider} props.headerProvider
3580
- * @param {PlaceholderProvider} [props.placeholderProvider]
3581
- * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
3582
- * @param {Object} [props.layoutConfig]
3583
- * @param {Function} [props.layoutChanged]
3584
- * @param {DescriptionConfig} [props.descriptionConfig]
3585
- * @param {Function} [props.descriptionLoaded]
3586
- * @param {Object} [props.eventBus]
3835
+ /**
3836
+ * A basic properties panel component. Describes *how* content will be rendered, accepts
3837
+ * data from implementor to describe *what* will be rendered.
3838
+ *
3839
+ * @param {Object} props
3840
+ * @param {Object|Array} props.element
3841
+ * @param {import('./components/Header').HeaderProvider} props.headerProvider
3842
+ * @param {PlaceholderProvider} [props.placeholderProvider]
3843
+ * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
3844
+ * @param {Object} [props.layoutConfig]
3845
+ * @param {Function} [props.layoutChanged]
3846
+ * @param {DescriptionConfig} [props.descriptionConfig]
3847
+ * @param {Function} [props.descriptionLoaded]
3848
+ * @param {Object} [props.eventBus]
3587
3849
  */
3588
3850
  function PropertiesPanel(props) {
3589
3851
  const {
@@ -3610,7 +3872,7 @@ function PropertiesPanel(props) {
3610
3872
  };
3611
3873
  const setLayoutForKey = (key, config) => {
3612
3874
  const newLayout = assign({}, layout);
3613
- set(newLayout, key, config);
3875
+ set$1(newLayout, key, config);
3614
3876
  setLayout(newLayout);
3615
3877
  };
3616
3878
  const layoutContext = {
@@ -3808,8 +4070,8 @@ function ListItem(props) {
3808
4070
  }
3809
4071
  const noop$2 = () => {};
3810
4072
 
3811
- /**
3812
- * @param {import('../PropertiesPanel').ListGroupDefinition} props
4073
+ /**
4074
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
3813
4075
  */
3814
4076
  function ListGroup(props) {
3815
4077
  const {
@@ -3972,8 +4234,8 @@ function ListGroup(props) {
3972
4234
 
3973
4235
  // helpers ////////////////////
3974
4236
 
3975
- /**
3976
- * Sorts given items alphanumeric by label
4237
+ /**
4238
+ * Sorts given items alphanumeric by label
3977
4239
  */
3978
4240
  function sortItems(items) {
3979
4241
  return sortBy(items, i => i.label.toLowerCase());
@@ -4047,17 +4309,17 @@ function Checkbox(props) {
4047
4309
  });
4048
4310
  }
4049
4311
 
4050
- /**
4051
- * @param {Object} props
4052
- * @param {Object} props.element
4053
- * @param {String} props.id
4054
- * @param {String} props.description
4055
- * @param {String} props.label
4056
- * @param {Function} props.getValue
4057
- * @param {Function} props.setValue
4058
- * @param {Function} props.onFocus
4059
- * @param {Function} props.onBlur
4060
- * @param {boolean} [props.disabled]
4312
+ /**
4313
+ * @param {Object} props
4314
+ * @param {Object} props.element
4315
+ * @param {String} props.id
4316
+ * @param {String} props.description
4317
+ * @param {String} props.label
4318
+ * @param {Function} props.getValue
4319
+ * @param {Function} props.setValue
4320
+ * @param {Function} props.onFocus
4321
+ * @param {Function} props.onBlur
4322
+ * @param {boolean} [props.disabled]
4061
4323
  */
4062
4324
  function CheckboxEntry(props) {
4063
4325
  const {
@@ -4145,10 +4407,10 @@ const CodeEditor = forwardRef((props, ref) => {
4145
4407
  useEffect(() => {
4146
4408
  let editor;
4147
4409
 
4148
- /* Trigger FEEL toggle when
4149
- *
4150
- * - `backspace` is pressed
4151
- * - AND the cursor is at the beginning of the input
4410
+ /* Trigger FEEL toggle when
4411
+ *
4412
+ * - `backspace` is pressed
4413
+ * - AND the cursor is at the beginning of the input
4152
4414
  */
4153
4415
  const onKeyDown = e => {
4154
4416
  if (e.key !== 'Backspace' || !editor) {
@@ -4219,10 +4481,10 @@ function FeelIndicator(props) {
4219
4481
  }
4220
4482
  const noop$1 = () => {};
4221
4483
 
4222
- /**
4223
- * @param {Object} props
4224
- * @param {Object} props.label
4225
- * @param {String} props.feel
4484
+ /**
4485
+ * @param {Object} props
4486
+ * @param {Object} props.label
4487
+ * @param {String} props.feel
4226
4488
  */
4227
4489
  function FeelIcon(props) {
4228
4490
  const {
@@ -4496,17 +4758,17 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
4496
4758
  });
4497
4759
  });
4498
4760
 
4499
- /**
4500
- * @param {Object} props
4501
- * @param {Object} props.element
4502
- * @param {String} props.id
4503
- * @param {String} props.description
4504
- * @param {Boolean} props.debounce
4505
- * @param {Boolean} props.disabled
4506
- * @param {String} props.label
4507
- * @param {Function} props.getValue
4508
- * @param {Function} props.setValue
4509
- * @param {Function} props.validate
4761
+ /**
4762
+ * @param {Object} props
4763
+ * @param {Object} props.element
4764
+ * @param {String} props.id
4765
+ * @param {String} props.description
4766
+ * @param {Boolean} props.debounce
4767
+ * @param {Boolean} props.disabled
4768
+ * @param {String} props.label
4769
+ * @param {Function} props.getValue
4770
+ * @param {Function} props.setValue
4771
+ * @param {Function} props.validate
4510
4772
  */
4511
4773
  function FeelEntry(props) {
4512
4774
  const {
@@ -4591,19 +4853,19 @@ function FeelEntry(props) {
4591
4853
  });
4592
4854
  }
4593
4855
 
4594
- /**
4595
- * @param {Object} props
4596
- * @param {Object} props.element
4597
- * @param {String} props.id
4598
- * @param {String} props.description
4599
- * @param {Boolean} props.debounce
4600
- * @param {Boolean} props.disabled
4601
- * @param {String} props.label
4602
- * @param {Function} props.getValue
4603
- * @param {Function} props.setValue
4604
- * @param {Function} props.onFocus
4605
- * @param {Function} props.onBlur
4606
- * @param {Function} props.validate
4856
+ /**
4857
+ * @param {Object} props
4858
+ * @param {Object} props.element
4859
+ * @param {String} props.id
4860
+ * @param {String} props.description
4861
+ * @param {Boolean} props.debounce
4862
+ * @param {Boolean} props.disabled
4863
+ * @param {String} props.label
4864
+ * @param {Function} props.getValue
4865
+ * @param {Function} props.setValue
4866
+ * @param {Function} props.onFocus
4867
+ * @param {Function} props.onBlur
4868
+ * @param {Function} props.validate
4607
4869
  */
4608
4870
  function FeelTextArea(props) {
4609
4871
  return jsx(FeelEntry, {
@@ -4682,21 +4944,22 @@ function NumberField(props) {
4682
4944
  });
4683
4945
  }
4684
4946
 
4685
- /**
4686
- * @param {Object} props
4687
- * @param {Boolean} props.debounce
4688
- * @param {String} props.description
4689
- * @param {Boolean} props.disabled
4690
- * @param {Object} props.element
4691
- * @param {Function} props.getValue
4692
- * @param {String} props.id
4693
- * @param {String} props.label
4694
- * @param {String} props.max
4695
- * @param {String} props.min
4696
- * @param {Function} props.setValue
4697
- * @param {Function} props.onFocus
4698
- * @param {Function} props.onBlur
4699
- * @param {String} props.step
4947
+ /**
4948
+ * @param {Object} props
4949
+ * @param {Boolean} props.debounce
4950
+ * @param {String} props.description
4951
+ * @param {Boolean} props.disabled
4952
+ * @param {Object} props.element
4953
+ * @param {Function} props.getValue
4954
+ * @param {String} props.id
4955
+ * @param {String} props.label
4956
+ * @param {String} props.max
4957
+ * @param {String} props.min
4958
+ * @param {Function} props.setValue
4959
+ * @param {Function} props.onFocus
4960
+ * @param {Function} props.onBlur
4961
+ * @param {String} props.step
4962
+ * @param {Function} props.validate
4700
4963
  */
4701
4964
  function NumberFieldEntry(props) {
4702
4965
  const {
@@ -4712,25 +4975,55 @@ function NumberFieldEntry(props) {
4712
4975
  setValue,
4713
4976
  step,
4714
4977
  onFocus,
4715
- onBlur
4978
+ onBlur,
4979
+ validate
4716
4980
  } = props;
4717
- const value = getValue(element);
4981
+ const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
4982
+ const globalError = useError(id);
4983
+ const [localError, setLocalError] = useState(null);
4984
+ let value = getValue(element);
4985
+ const previousValue = usePrevious(value);
4986
+ useEffect(() => {
4987
+ if (isFunction(validate)) {
4988
+ const newValidationError = validate(value) || null;
4989
+ setLocalError(newValidationError);
4990
+ }
4991
+ }, [value]);
4992
+ const onInput = newValue => {
4993
+ let newValidationError = null;
4994
+ if (isFunction(validate)) {
4995
+ newValidationError = validate(newValue) || null;
4996
+ }
4997
+ if (newValidationError) {
4998
+ setCachedInvalidValue(newValue);
4999
+ } else {
5000
+ setValue(newValue);
5001
+ }
5002
+ setLocalError(newValidationError);
5003
+ };
5004
+ if (previousValue === value && localError) {
5005
+ value = cachedInvalidValue;
5006
+ }
5007
+ const error = globalError || localError;
4718
5008
  return jsxs("div", {
4719
- class: "bio-properties-panel-entry",
5009
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
4720
5010
  "data-entry-id": id,
4721
5011
  children: [jsx(NumberField, {
4722
5012
  debounce: debounce,
4723
5013
  disabled: disabled,
4724
5014
  id: id,
4725
5015
  label: label,
4726
- onInput: setValue,
4727
5016
  onFocus: onFocus,
4728
5017
  onBlur: onBlur,
5018
+ onInput: onInput,
4729
5019
  max: max,
4730
5020
  min: min,
4731
5021
  step: step,
4732
5022
  value: value
4733
- }, element), jsx(Description$1, {
5023
+ }, element), error && jsx("div", {
5024
+ class: "bio-properties-panel-error",
5025
+ children: error
5026
+ }), jsx(Description$1, {
4734
5027
  forId: id,
4735
5028
  element: element,
4736
5029
  value: description
@@ -4752,7 +5045,7 @@ function Select(props) {
4752
5045
  label,
4753
5046
  onChange,
4754
5047
  options = [],
4755
- value,
5048
+ value = '',
4756
5049
  disabled,
4757
5050
  onFocus,
4758
5051
  onBlur
@@ -4801,18 +5094,18 @@ function Select(props) {
4801
5094
  });
4802
5095
  }
4803
5096
 
4804
- /**
4805
- * @param {object} props
4806
- * @param {object} props.element
4807
- * @param {string} props.id
4808
- * @param {string} [props.description]
4809
- * @param {string} props.label
4810
- * @param {Function} props.getValue
4811
- * @param {Function} props.setValue
4812
- * @param {Function} props.onFocus
4813
- * @param {Function} props.onBlur
4814
- * @param {Function} props.getOptions
4815
- * @param {boolean} [props.disabled]
5097
+ /**
5098
+ * @param {object} props
5099
+ * @param {object} props.element
5100
+ * @param {string} props.id
5101
+ * @param {string} [props.description]
5102
+ * @param {string} props.label
5103
+ * @param {Function} props.getValue
5104
+ * @param {Function} props.setValue
5105
+ * @param {Function} props.onFocus
5106
+ * @param {Function} props.onBlur
5107
+ * @param {Function} props.getOptions
5108
+ * @param {boolean} [props.disabled]
4816
5109
  */
4817
5110
  function SelectEntry(props) {
4818
5111
  const {
@@ -4914,20 +5207,20 @@ function TextArea(props) {
4914
5207
  });
4915
5208
  }
4916
5209
 
4917
- /**
4918
- * @param {object} props
4919
- * @param {object} props.element
4920
- * @param {string} props.id
4921
- * @param {string} props.description
4922
- * @param {boolean} props.debounce
4923
- * @param {string} props.label
4924
- * @param {Function} props.getValue
4925
- * @param {Function} props.setValue
4926
- * @param {Function} props.onFocus
4927
- * @param {Function} props.onBlur
4928
- * @param {number} props.rows
4929
- * @param {boolean} props.monospace
4930
- * @param {boolean} [props.disabled]
5210
+ /**
5211
+ * @param {object} props
5212
+ * @param {object} props.element
5213
+ * @param {string} props.id
5214
+ * @param {string} props.description
5215
+ * @param {boolean} props.debounce
5216
+ * @param {string} props.label
5217
+ * @param {Function} props.getValue
5218
+ * @param {Function} props.setValue
5219
+ * @param {Function} props.onFocus
5220
+ * @param {Function} props.onBlur
5221
+ * @param {number} props.rows
5222
+ * @param {boolean} props.monospace
5223
+ * @param {boolean} [props.disabled]
4931
5224
  */
4932
5225
  function TextAreaEntry(props) {
4933
5226
  const {
@@ -5030,19 +5323,19 @@ function Textfield(props) {
5030
5323
  });
5031
5324
  }
5032
5325
 
5033
- /**
5034
- * @param {Object} props
5035
- * @param {Object} props.element
5036
- * @param {String} props.id
5037
- * @param {String} props.description
5038
- * @param {Boolean} props.debounce
5039
- * @param {Boolean} props.disabled
5040
- * @param {String} props.label
5041
- * @param {Function} props.getValue
5042
- * @param {Function} props.setValue
5043
- * @param {Function} props.onFocus
5044
- * @param {Function} props.onBlur
5045
- * @param {Function} props.validate
5326
+ /**
5327
+ * @param {Object} props
5328
+ * @param {Object} props.element
5329
+ * @param {String} props.id
5330
+ * @param {String} props.description
5331
+ * @param {Boolean} props.debounce
5332
+ * @param {Boolean} props.disabled
5333
+ * @param {String} props.label
5334
+ * @param {Function} props.getValue
5335
+ * @param {Function} props.setValue
5336
+ * @param {Function} props.onFocus
5337
+ * @param {Function} props.onBlur
5338
+ * @param {Function} props.validate
5046
5339
  */
5047
5340
  function TextfieldEntry(props) {
5048
5341
  const {
@@ -5127,6 +5420,7 @@ function getService(type, strict) {}
5127
5420
  const PropertiesPanelContext = createContext({
5128
5421
  getService
5129
5422
  });
5423
+ var FormPropertiesPanelContext = PropertiesPanelContext;
5130
5424
 
5131
5425
  function arrayAdd(array, index, item) {
5132
5426
  const copy = [...array];
@@ -5278,7 +5572,7 @@ function Action(props) {
5278
5572
  function useService (type, strict) {
5279
5573
  const {
5280
5574
  getService
5281
- } = useContext(PropertiesPanelContext);
5575
+ } = useContext(FormPropertiesPanelContext);
5282
5576
  return getService(type, strict);
5283
5577
  }
5284
5578
 
@@ -5893,6 +6187,46 @@ const SimpleStringComponent = props => {
5893
6187
  });
5894
6188
  };
5895
6189
 
6190
+ function simpleBoolEntryFactory(options) {
6191
+ const {
6192
+ id,
6193
+ label,
6194
+ path,
6195
+ props
6196
+ } = options;
6197
+ const {
6198
+ editField,
6199
+ field
6200
+ } = props;
6201
+ return {
6202
+ id,
6203
+ label,
6204
+ path,
6205
+ field,
6206
+ editField,
6207
+ component: SimpleBoolComponent,
6208
+ isEdited: isEdited$7
6209
+ };
6210
+ }
6211
+ const SimpleBoolComponent = props => {
6212
+ const {
6213
+ id,
6214
+ label,
6215
+ path,
6216
+ field,
6217
+ editField
6218
+ } = props;
6219
+ const getValue = () => get(field, path, '');
6220
+ const setValue = value => editField(field, path, value);
6221
+ return CheckboxEntry({
6222
+ element: field,
6223
+ getValue,
6224
+ id,
6225
+ label,
6226
+ setValue
6227
+ });
6228
+ };
6229
+
5896
6230
  function LabelEntry(props) {
5897
6231
  const {
5898
6232
  field
@@ -6072,11 +6406,15 @@ function NumberDecimalDigits(props) {
6072
6406
  debounce,
6073
6407
  label: 'Decimal digits',
6074
6408
  element: field,
6075
- min: 0,
6076
- step: 1,
6409
+ step: 'any',
6077
6410
  getValue,
6078
6411
  id,
6079
- setValue
6412
+ setValue,
6413
+ validate: value => {
6414
+ if (value === undefined || value === null) return;
6415
+ if (value < 0) return 'Should be greater than or equal to zero.';
6416
+ if (!Number.isInteger(value)) return 'Should be an integer.';
6417
+ }
6080
6418
  });
6081
6419
  }
6082
6420
  function NumberArrowStep(props) {
@@ -6166,7 +6504,7 @@ function SerializeToString(props) {
6166
6504
  element: field,
6167
6505
  getValue,
6168
6506
  id,
6169
- label: 'Serialize to string',
6507
+ label: 'Output as string',
6170
6508
  description: 'Allows arbitrary precision values',
6171
6509
  setValue
6172
6510
  });
@@ -6409,6 +6747,25 @@ function TimeFormatSelect(props) {
6409
6747
  });
6410
6748
  }
6411
6749
 
6750
+ function SelectEntries(props) {
6751
+ const {
6752
+ field
6753
+ } = props;
6754
+ const {
6755
+ type
6756
+ } = field;
6757
+ if (type !== 'select') {
6758
+ return [];
6759
+ }
6760
+ const entries = [simpleBoolEntryFactory({
6761
+ id: 'searchable',
6762
+ path: ['searchable'],
6763
+ label: 'Searchable',
6764
+ props
6765
+ })];
6766
+ return entries;
6767
+ }
6768
+
6412
6769
  function ValueEntry(props) {
6413
6770
  const {
6414
6771
  editField,
@@ -6447,7 +6804,7 @@ function Label(props) {
6447
6804
  const debounce = useService('debounce');
6448
6805
  const setValue = value => {
6449
6806
  const values = get(field, ['values']);
6450
- return editField(field, 'values', set(values, [index, 'label'], value));
6807
+ return editField(field, 'values', set$1(values, [index, 'label'], value));
6451
6808
  };
6452
6809
  const getValue = () => {
6453
6810
  return get(field, ['values', index, 'label']);
@@ -6473,7 +6830,7 @@ function Value$1(props) {
6473
6830
  const debounce = useService('debounce');
6474
6831
  const setValue = value => {
6475
6832
  const values = get(field, ['values']);
6476
- return editField(field, 'values', set(values, [index, 'value'], value));
6833
+ return editField(field, 'values', set$1(values, [index, 'value'], value));
6477
6834
  };
6478
6835
  const getValue = () => {
6479
6836
  return get(field, ['values', index, 'value']);
@@ -6788,7 +7145,7 @@ function AdornerEntry(props) {
6788
7145
  const onChange = key => {
6789
7146
  return value => {
6790
7147
  const appearance = get(field, ['appearance'], {});
6791
- editField(field, ['appearance'], set(appearance, [key], value));
7148
+ editField(field, ['appearance'], set$1(appearance, [key], value));
6792
7149
  };
6793
7150
  };
6794
7151
  const getValue = key => {
@@ -6938,6 +7295,9 @@ function GeneralGroup(field, editField) {
6938
7295
  }), ...AltTextEntry({
6939
7296
  field,
6940
7297
  editField
7298
+ }), ...SelectEntries({
7299
+ field,
7300
+ editField
6941
7301
  }), ...DisabledEntry({
6942
7302
  field,
6943
7303
  editField
@@ -7001,14 +7361,14 @@ function ValidationGroup(field, editField) {
7001
7361
  type
7002
7362
  } = field;
7003
7363
  const validate = get(field, ['validate'], {});
7004
- const isCustomValidation = [undefined, VALIDATION_TYPE_OPTIONS.custom.value].includes(validate?.validationType);
7364
+ const isCustomValidation = [undefined, VALIDATION_TYPE_OPTIONS.custom.value].includes(validate && validate.validationType);
7005
7365
  if (!(INPUTS.includes(type) && type !== 'checkbox' && type !== 'checklist' && type !== 'taglist')) {
7006
7366
  return null;
7007
7367
  }
7008
7368
  const onChange = key => {
7009
7369
  return value => {
7010
7370
  const validate = get(field, ['validate'], {});
7011
- editField(field, ['validate'], set(validate, [key], value));
7371
+ editField(field, ['validate'], set$1(validate, [key], value));
7012
7372
  };
7013
7373
  };
7014
7374
  const getValue = key => {
@@ -7030,6 +7390,7 @@ function ValidationGroup(field, editField) {
7030
7390
  component: ValidationType,
7031
7391
  getValue,
7032
7392
  field,
7393
+ editField,
7033
7394
  isEdited: isEdited$1,
7034
7395
  onChange
7035
7396
  });
@@ -7148,7 +7509,7 @@ function Pattern(props) {
7148
7509
  element: field,
7149
7510
  getValue: getValue('pattern'),
7150
7511
  id,
7151
- label: 'Regular expression pattern',
7512
+ label: 'Custom regular expression',
7152
7513
  setValue: onChange('pattern')
7153
7514
  });
7154
7515
  }
@@ -7196,15 +7557,7 @@ function ValidationType(props) {
7196
7557
  onChange
7197
7558
  } = props;
7198
7559
  const debounce = useService('debounce');
7199
- const clearCustomValidation = () => {
7200
- onChange('minLength')(undefined);
7201
- onChange('maxLength')(undefined);
7202
- onChange('pattern')(undefined);
7203
- };
7204
7560
  const setValue = validationType => {
7205
- if (validationType) {
7206
- clearCustomValidation();
7207
- }
7208
7561
  onChange('validationType')(validationType || undefined);
7209
7562
  };
7210
7563
  return SelectEntry({
@@ -7212,7 +7565,7 @@ function ValidationType(props) {
7212
7565
  element: field,
7213
7566
  getValue: getValue('validationType'),
7214
7567
  id,
7215
- label: 'Regular expression validation',
7568
+ label: 'Validation pattern',
7216
7569
  setValue,
7217
7570
  getOptions: () => Object.values(VALIDATION_TYPE_OPTIONS)
7218
7571
  });
@@ -7458,7 +7811,7 @@ function FormPropertiesPanel(props) {
7458
7811
  "data-field": selectedFormField && selectedFormField.id,
7459
7812
  onFocusCapture: onFocus,
7460
7813
  onBlurCapture: onBlur,
7461
- children: jsx(PropertiesPanelContext.Provider, {
7814
+ children: jsx(FormPropertiesPanelContext.Provider, {
7462
7815
  value: propertiesPanelContext,
7463
7816
  children: jsx(PropertiesPanel, {
7464
7817
  element: selectedFormField,
@@ -7722,7 +8075,7 @@ class FormEditor {
7722
8075
  * @param {any} value
7723
8076
  */
7724
8077
  setProperty(property, value) {
7725
- const properties = set(this._getState().properties, [property], value);
8078
+ const properties = set$1(this._getState().properties, [property], value);
7726
8079
  this._setState({
7727
8080
  properties
7728
8081
  });