@bpmn-io/form-js-viewer 0.10.1 → 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.
Files changed (69) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +165 -165
  3. package/dist/assets/form-js.css +715 -611
  4. package/dist/index.cjs +953 -691
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.es.js +877 -606
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/types/Form.d.ts +144 -144
  9. package/dist/types/core/ConditionChecker.d.ts +57 -57
  10. package/dist/types/core/EventBus.d.ts +1 -1
  11. package/dist/types/core/FormFieldRegistry.d.ts +17 -17
  12. package/dist/types/core/Validator.d.ts +7 -7
  13. package/dist/types/core/index.d.ts +18 -18
  14. package/dist/types/import/Importer.d.ts +43 -43
  15. package/dist/types/import/index.d.ts +5 -5
  16. package/dist/types/index.d.ts +18 -18
  17. package/dist/types/render/FormFields.d.ts +5 -5
  18. package/dist/types/render/Renderer.d.ts +23 -23
  19. package/dist/types/render/components/Description.d.ts +1 -1
  20. package/dist/types/render/components/Errors.d.ts +1 -1
  21. package/dist/types/render/components/FormComponent.d.ts +1 -1
  22. package/dist/types/render/components/FormField.d.ts +1 -1
  23. package/dist/types/render/components/Label.d.ts +1 -1
  24. package/dist/types/render/components/PoweredBy.d.ts +1 -1
  25. package/dist/types/render/components/Sanitizer.d.ts +8 -8
  26. package/dist/types/render/components/Util.d.ts +17 -17
  27. package/dist/types/render/components/form-fields/Button.d.ts +12 -11
  28. package/dist/types/render/components/form-fields/Checkbox.d.ts +14 -13
  29. package/dist/types/render/components/form-fields/Checklist.d.ts +18 -12
  30. package/dist/types/render/components/form-fields/Datetime.d.ts +13 -11
  31. package/dist/types/render/components/form-fields/Default.d.ts +11 -9
  32. package/dist/types/render/components/form-fields/Image.d.ts +10 -8
  33. package/dist/types/render/components/form-fields/Number.d.ts +15 -14
  34. package/dist/types/render/components/form-fields/Radio.d.ts +18 -12
  35. package/dist/types/render/components/form-fields/Select.d.ts +18 -12
  36. package/dist/types/render/components/form-fields/Taglist.d.ts +18 -12
  37. package/dist/types/render/components/form-fields/Text.d.ts +12 -10
  38. package/dist/types/render/components/form-fields/Textarea.d.ts +14 -13
  39. package/dist/types/render/components/form-fields/Textfield.d.ts +14 -13
  40. package/dist/types/render/components/form-fields/parts/Datepicker.d.ts +1 -1
  41. package/dist/types/render/components/form-fields/parts/DropdownList.d.ts +1 -1
  42. package/dist/types/render/components/form-fields/parts/InputAdorner.d.ts +1 -1
  43. package/dist/types/render/components/form-fields/parts/SearchableSelect.d.ts +1 -0
  44. package/dist/types/render/components/form-fields/parts/SimpleSelect.d.ts +1 -0
  45. package/dist/types/render/components/form-fields/parts/Timepicker.d.ts +1 -1
  46. package/dist/types/render/components/icons/index.d.ts +16 -16
  47. package/dist/types/render/components/index.d.ts +17 -17
  48. package/dist/types/render/components/util/dateTimeUtil.d.ts +12 -12
  49. package/dist/types/render/components/util/numberFieldUtil.d.ts +4 -4
  50. package/dist/types/render/components/util/sanitizerUtil.d.ts +3 -3
  51. package/dist/types/render/context/FormContext.d.ts +12 -12
  52. package/dist/types/render/context/FormRenderContext.d.ts +6 -6
  53. package/dist/types/render/context/index.d.ts +2 -2
  54. package/dist/types/render/hooks/useCondition.d.ts +9 -9
  55. package/dist/types/render/hooks/useEvaluation.d.ts +6 -6
  56. package/dist/types/render/hooks/useExpressionValue.d.ts +5 -5
  57. package/dist/types/render/hooks/useKeyDownAction.d.ts +1 -1
  58. package/dist/types/render/hooks/useService.d.ts +1 -1
  59. package/dist/types/render/hooks/useValuesAsync.d.ts +28 -28
  60. package/dist/types/render/index.d.ts +11 -11
  61. package/dist/types/src/types.d.ts +35 -35
  62. package/dist/types/util/constants/DatetimeConstants.d.ts +24 -24
  63. package/dist/types/util/constants/ValuesSourceConstants.d.ts +18 -15
  64. package/dist/types/util/constants/index.d.ts +2 -2
  65. package/dist/types/util/feel.d.ts +15 -15
  66. package/dist/types/util/form.d.ts +6 -6
  67. package/dist/types/util/index.d.ts +26 -25
  68. package/dist/types/util/injector.d.ts +2 -2
  69. package/package.json +4 -4
package/dist/index.es.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import Ids from 'ids';
2
- import { isString, isArray, isFunction, isNumber, bind, assign, isNil, get, isUndefined, set, isObject } from 'min-dash';
2
+ import { isString, isArray, isFunction, isNumber, bind, assign, isNil, get, isUndefined, set, findIndex, isObject } from 'min-dash';
3
3
  import { unaryTest, evaluate, parseUnaryTests, parseExpressions } from 'feelin';
4
4
  import Big from 'big.js';
5
5
  import snarkdown from '@bpmn-io/snarkdown';
6
6
  import classNames from 'classnames';
7
- import { jsx, jsxs } from 'preact/jsx-runtime';
7
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
8
8
  import { useContext, useState, useEffect, useRef, useCallback, useMemo } from 'preact/hooks';
9
9
  import { createContext, createElement, Fragment, render } from 'preact';
10
10
  import React, { createPortal } from 'preact/compat';
@@ -12,9 +12,9 @@ import flatpickr from 'flatpickr';
12
12
  import Markup from 'preact-markup';
13
13
  import { Injector } from 'didi';
14
14
 
15
- /**
16
- * @typedef {object} Condition
17
- * @property {string} [hide]
15
+ /**
16
+ * @typedef {object} Condition
17
+ * @property {string} [hide]
18
18
  */
19
19
 
20
20
  class ConditionChecker {
@@ -23,11 +23,11 @@ class ConditionChecker {
23
23
  this._eventBus = eventBus;
24
24
  }
25
25
 
26
- /**
27
- * For given data, remove properties based on condition.
28
- *
29
- * @param {Object<string, any>} properties
30
- * @param {Object<string, any>} data
26
+ /**
27
+ * For given data, remove properties based on condition.
28
+ *
29
+ * @param {Object<string, any>} properties
30
+ * @param {Object<string, any>} data
31
31
  */
32
32
  applyConditions(properties, data = {}) {
33
33
  const conditions = this._getConditions();
@@ -46,13 +46,13 @@ class ConditionChecker {
46
46
  return newProperties;
47
47
  }
48
48
 
49
- /**
50
- * Check if given condition is met. Returns null for invalid/missing conditions.
51
- *
52
- * @param {string} condition
53
- * @param {import('../types').Data} [data]
54
- *
55
- * @returns {boolean|null}
49
+ /**
50
+ * Check if given condition is met. Returns null for invalid/missing conditions.
51
+ *
52
+ * @param {string} condition
53
+ * @param {import('../types').Data} [data]
54
+ *
55
+ * @returns {boolean|null}
56
56
  */
57
57
  check(condition, data = {}) {
58
58
  if (!condition) {
@@ -73,12 +73,12 @@ class ConditionChecker {
73
73
  }
74
74
  }
75
75
 
76
- /**
77
- * Check if hide condition is met.
78
- *
79
- * @param {Condition} condition
80
- * @param {Object<string, any>} data
81
- * @returns {boolean}
76
+ /**
77
+ * Check if hide condition is met.
78
+ *
79
+ * @param {Condition} condition
80
+ * @param {Object<string, any>} data
81
+ * @returns {boolean}
82
82
  */
83
83
  _checkHideCondition(condition, data) {
84
84
  if (!condition.hide) {
@@ -88,13 +88,13 @@ class ConditionChecker {
88
88
  return result === true;
89
89
  }
90
90
 
91
- /**
92
- * Evaluate an expression.
93
- *
94
- * @param {string} expression
95
- * @param {import('../types').Data} [data]
96
- *
97
- * @returns {any}
91
+ /**
92
+ * Evaluate an expression.
93
+ *
94
+ * @param {string} expression
95
+ * @param {import('../types').Data} [data]
96
+ *
97
+ * @returns {any}
98
98
  */
99
99
  evaluate(expression, data = {}) {
100
100
  if (!expression) {
@@ -602,7 +602,7 @@ class Validator {
602
602
  if (type === 'number') {
603
603
  const {
604
604
  decimalDigits,
605
- step
605
+ increment
606
606
  } = field;
607
607
  if (value === 'NaN') {
608
608
  errors = [...errors, 'Value is not a number.'];
@@ -610,13 +610,13 @@ class Validator {
610
610
  if (decimalDigits >= 0 && countDecimals(value) > decimalDigits) {
611
611
  errors = [...errors, 'Value is expected to ' + (decimalDigits === 0 ? 'be an integer' : `have at most ${decimalDigits} decimal digit${decimalDigits > 1 ? 's' : ''}`) + '.'];
612
612
  }
613
- if (step) {
613
+ if (increment) {
614
614
  const bigValue = Big(value);
615
- const bigStep = Big(step);
616
- const offset = bigValue.mod(bigStep);
615
+ const bigIncrement = Big(increment);
616
+ const offset = bigValue.mod(bigIncrement);
617
617
  if (offset.cmp(0) !== 0) {
618
618
  const previousValue = bigValue.minus(offset);
619
- const nextValue = previousValue.plus(bigStep);
619
+ const nextValue = previousValue.plus(bigIncrement);
620
620
  errors = [...errors, `Please select a valid value, the two nearest valid values are ${previousValue} and ${nextValue}.`];
621
621
  }
622
622
  }
@@ -631,10 +631,10 @@ class Validator {
631
631
  if (validate.required && (isNil(value) || value === '')) {
632
632
  errors = [...errors, 'Field is required.'];
633
633
  }
634
- if ('min' in validate && value && value < validate.min) {
634
+ if ('min' in validate && (value || value === 0) && value < validate.min) {
635
635
  errors = [...errors, `Field must have minimum value of ${validate.min}.`];
636
636
  }
637
- if ('max' in validate && value && value > validate.max) {
637
+ if ('max' in validate && (value || value === 0) && value > validate.max) {
638
638
  errors = [...errors, `Field must have maximum value of ${validate.max}.`];
639
639
  }
640
640
  if ('minLength' in validate && value && value.trim().length < validate.minLength) {
@@ -703,11 +703,11 @@ class FormFieldRegistry {
703
703
  }
704
704
  FormFieldRegistry.$inject = ['eventBus'];
705
705
 
706
- /**
707
- * Retrieve variable names from given FEEL unary test.
708
- *
709
- * @param {string} unaryTest
710
- * @returns {string[]}
706
+ /**
707
+ * Retrieve variable names from given FEEL unary test.
708
+ *
709
+ * @param {string} unaryTest
710
+ * @returns {string[]}
711
711
  */
712
712
  function getVariableNames(unaryTest) {
713
713
  const tree = parseUnaryTests(unaryTest);
@@ -722,11 +722,11 @@ function getVariableNames(unaryTest) {
722
722
  return Array.from(variables);
723
723
  }
724
724
 
725
- /**
726
- * Retrieve variable names from given FEEL expression.
727
- *
728
- * @param {string} expression
729
- * @returns {string[]}
725
+ /**
726
+ * Retrieve variable names from given FEEL expression.
727
+ *
728
+ * @param {string} expression
729
+ * @returns {string[]}
730
730
  */
731
731
  function getExpressionVariableNames(expression) {
732
732
  const tree = parseExpressions(expression);
@@ -740,7 +740,7 @@ function getExpressionVariableNames(expression) {
740
740
  } while (cursor.next());
741
741
  return Array.from(variables);
742
742
  }
743
- function isExpression$2(value) {
743
+ function isExpression$1(value) {
744
744
  return isString(value) && value.startsWith('=');
745
745
  }
746
746
 
@@ -791,7 +791,10 @@ const VALUES_SOURCES_PATHS = {
791
791
  [VALUES_SOURCES.INPUT]: ['valuesKey']
792
792
  };
793
793
  const VALUES_SOURCES_DEFAULTS = {
794
- [VALUES_SOURCES.STATIC]: [],
794
+ [VALUES_SOURCES.STATIC]: [{
795
+ label: 'Value',
796
+ value: 'value'
797
+ }],
795
798
  [VALUES_SOURCES.INPUT]: ''
796
799
  };
797
800
 
@@ -812,10 +815,10 @@ function createInjector(bootstrapModules) {
812
815
  return injector;
813
816
  }
814
817
 
815
- /**
816
- * @param {string?} prefix
817
- *
818
- * @returns Element
818
+ /**
819
+ * @param {string?} prefix
820
+ *
821
+ * @returns Element
819
822
  */
820
823
  function createFormContainer(prefix = 'fjs') {
821
824
  const container = document.createElement('div');
@@ -860,22 +863,22 @@ function generateIdForType(type) {
860
863
  return `${type}${generateIndexForType(type)}`;
861
864
  }
862
865
 
863
- /**
864
- * @template T
865
- * @param {T} data
866
- * @param {(this: any, key: string, value: any) => any} [replacer]
867
- * @return {T}
866
+ /**
867
+ * @template T
868
+ * @param {T} data
869
+ * @param {(this: any, key: string, value: any) => any} [replacer]
870
+ * @return {T}
868
871
  */
869
872
  function clone(data, replacer) {
870
873
  return JSON.parse(JSON.stringify(data, replacer));
871
874
  }
872
875
 
873
- /**
874
- * Parse the schema for input variables a form might make use of
875
- *
876
- * @param {any} schema
877
- *
878
- * @return {string[]}
876
+ /**
877
+ * Parse the schema for input variables a form might make use of
878
+ *
879
+ * @param {any} schema
880
+ *
881
+ * @return {string[]}
879
882
  */
880
883
  function getSchemaVariables(schema) {
881
884
  if (!schema.components) {
@@ -917,32 +920,26 @@ function getSchemaVariables(schema) {
917
920
  return Array.from(new Set(variables));
918
921
  }
919
922
 
920
- // helper ///////////////
921
-
922
- function isExpression$1(value) {
923
- return isString(value) && value.startsWith('=');
924
- }
925
-
926
923
  class Importer {
927
- /**
928
- * @constructor
929
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
930
- * @param { import('../render/FormFields').default } formFields
924
+ /**
925
+ * @constructor
926
+ * @param { import('../core').FormFieldRegistry } formFieldRegistry
927
+ * @param { import('../render/FormFields').default } formFields
931
928
  */
932
929
  constructor(formFieldRegistry, formFields) {
933
930
  this._formFieldRegistry = formFieldRegistry;
934
931
  this._formFields = formFields;
935
932
  }
936
933
 
937
- /**
938
- * Import schema adding `id`, `_parent` and `_path`
939
- * information to each field and adding it to the
940
- * form field registry.
941
- *
942
- * @param {any} schema
943
- * @param {any} [data]
944
- *
945
- * @return { { warnings: Array<any>, schema: any, data: any } }
934
+ /**
935
+ * Import schema adding `id`, `_parent` and `_path`
936
+ * information to each field and adding it to the
937
+ * form field registry.
938
+ *
939
+ * @param {any} schema
940
+ * @param {any} [data]
941
+ *
942
+ * @return { { warnings: Array<any>, schema: any, data: any } }
946
943
  */
947
944
  importSchema(schema, data = {}) {
948
945
  // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
@@ -961,11 +958,11 @@ class Importer {
961
958
  }
962
959
  }
963
960
 
964
- /**
965
- * @param {any} formField
966
- * @param {string} [parentId]
967
- *
968
- * @return {any} importedField
961
+ /**
962
+ * @param {any} formField
963
+ * @param {string} [parentId]
964
+ *
965
+ * @return {any} importedField
969
966
  */
970
967
  importFormField(formField, parentId) {
971
968
  const {
@@ -1016,10 +1013,10 @@ class Importer {
1016
1013
  });
1017
1014
  }
1018
1015
 
1019
- /**
1020
- * @param {Object} data
1021
- *
1022
- * @return {Object} initializedData
1016
+ /**
1017
+ * @param {Object} data
1018
+ *
1019
+ * @return {Object} initializedData
1023
1020
  */
1024
1021
  initializeFieldValues(data) {
1025
1022
  return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
@@ -1069,11 +1066,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
1069
1066
 
1070
1067
  const FORM_ELEMENT = document.createElement('form');
1071
1068
 
1072
- /**
1073
- * Sanitize a HTML string and return the cleaned, safe version.
1074
- *
1075
- * @param {string} html
1076
- * @return {string}
1069
+ /**
1070
+ * Sanitize a HTML string and return the cleaned, safe version.
1071
+ *
1072
+ * @param {string} html
1073
+ * @return {string}
1077
1074
  */
1078
1075
  function sanitizeHTML(html) {
1079
1076
  const doc = new DOMParser().parseFromString(`<!DOCTYPE html>\n<html><body><div>${html}`, 'text/html');
@@ -1093,15 +1090,15 @@ function sanitizeImageSource(src) {
1093
1090
  return valid ? src : '';
1094
1091
  }
1095
1092
 
1096
- /**
1097
- * Recursively sanitize a HTML node, potentially
1098
- * removing it, its children or attributes.
1099
- *
1100
- * Inspired by https://github.com/developit/snarkdown/issues/70
1101
- * and https://github.com/cure53/DOMPurify. Simplified
1102
- * for our use-case.
1103
- *
1104
- * @param {Element} node
1093
+ /**
1094
+ * Recursively sanitize a HTML node, potentially
1095
+ * removing it, its children or attributes.
1096
+ *
1097
+ * Inspired by https://github.com/developit/snarkdown/issues/70
1098
+ * and https://github.com/cure53/DOMPurify. Simplified
1099
+ * for our use-case.
1100
+ *
1101
+ * @param {Element} node
1105
1102
  */
1106
1103
  function sanitizeNode(node) {
1107
1104
  // allow text nodes
@@ -1145,13 +1142,13 @@ function sanitizeNode(node) {
1145
1142
  }
1146
1143
  }
1147
1144
 
1148
- /**
1149
- * Validates attributes for validity.
1150
- *
1151
- * @param {string} lcTag
1152
- * @param {string} lcName
1153
- * @param {string} value
1154
- * @return {boolean}
1145
+ /**
1146
+ * Validates attributes for validity.
1147
+ *
1148
+ * @param {string} lcTag
1149
+ * @param {string} lcName
1150
+ * @param {string} value
1151
+ * @return {boolean}
1155
1152
  */
1156
1153
  function isValidAttribute(lcTag, lcName, value) {
1157
1154
  // disallow most attributes based on whitelist
@@ -1203,14 +1200,14 @@ function safeMarkdown(markdown) {
1203
1200
  return sanitizeHTML(html);
1204
1201
  }
1205
1202
 
1206
- /**
1207
- * Sanitizes an image source to ensure we only allow for data URI and links
1208
- * that start with http(s).
1209
- *
1210
- * Note: Most browsers anyway do not support script execution in <img> elements.
1211
- *
1212
- * @param {string} src
1213
- * @returns {string}
1203
+ /**
1204
+ * Sanitizes an image source to ensure we only allow for data URI and links
1205
+ * that start with http(s).
1206
+ *
1207
+ * Note: Most browsers anyway do not support script execution in <img> elements.
1208
+ *
1209
+ * @param {string} src
1210
+ * @returns {string}
1214
1211
  */
1215
1212
  function safeImageSource(src) {
1216
1213
  return sanitizeImageSource(src);
@@ -1235,15 +1232,14 @@ function Button(props) {
1235
1232
  })
1236
1233
  });
1237
1234
  }
1238
- Button.create = function (options = {}) {
1239
- return {
1240
- action: 'submit',
1241
- ...options
1242
- };
1243
- };
1235
+ Button.create = (options = {}) => ({
1236
+ action: 'submit',
1237
+ ...options
1238
+ });
1244
1239
  Button.type = type$b;
1245
1240
  Button.label = 'Button';
1246
1241
  Button.keyed = true;
1242
+ Button.group = 'action';
1247
1243
 
1248
1244
  const FormRenderContext = createContext({
1249
1245
  Empty: props => {
@@ -1256,18 +1252,20 @@ const FormRenderContext = createContext({
1256
1252
  return props.children;
1257
1253
  }
1258
1254
  });
1255
+ var FormRenderContext$1 = FormRenderContext;
1259
1256
 
1260
- /**
1261
- * @param {string} type
1262
- * @param {boolean} [strict]
1263
- *
1264
- * @returns {any}
1257
+ /**
1258
+ * @param {string} type
1259
+ * @param {boolean} [strict]
1260
+ *
1261
+ * @returns {any}
1265
1262
  */
1266
1263
  function getService(type, strict) {}
1267
1264
  const FormContext = createContext({
1268
1265
  getService,
1269
1266
  formId: null
1270
1267
  });
1268
+ var FormContext$1 = FormContext;
1271
1269
 
1272
1270
  function Description(props) {
1273
1271
  const {
@@ -1343,7 +1341,7 @@ function Checkbox(props) {
1343
1341
  };
1344
1342
  const {
1345
1343
  formId
1346
- } = useContext(FormContext);
1344
+ } = useContext(FormContext$1);
1347
1345
  return jsxs("div", {
1348
1346
  class: classNames(formFieldClasses(type$a, {
1349
1347
  errors,
@@ -1370,11 +1368,9 @@ function Checkbox(props) {
1370
1368
  })]
1371
1369
  });
1372
1370
  }
1373
- Checkbox.create = function (options = {}) {
1374
- return {
1375
- ...options
1376
- };
1377
- };
1371
+ Checkbox.create = (options = {}) => ({
1372
+ ...options
1373
+ });
1378
1374
  Checkbox.type = type$a;
1379
1375
  Checkbox.label = 'Checkbox';
1380
1376
  Checkbox.keyed = true;
@@ -1382,16 +1378,17 @@ Checkbox.emptyValue = false;
1382
1378
  Checkbox.sanitizeValue = ({
1383
1379
  value
1384
1380
  }) => value === true;
1381
+ Checkbox.group = 'selection';
1385
1382
 
1386
1383
  function useService (type, strict) {
1387
1384
  const {
1388
1385
  getService
1389
- } = useContext(FormContext);
1386
+ } = useContext(FormContext$1);
1390
1387
  return getService(type, strict);
1391
1388
  }
1392
1389
 
1393
- /**
1394
- * @enum { String }
1390
+ /**
1391
+ * @enum { String }
1395
1392
  */
1396
1393
  const LOAD_STATES = {
1397
1394
  LOADING: 'loading',
@@ -1399,17 +1396,17 @@ const LOAD_STATES = {
1399
1396
  ERROR: 'error'
1400
1397
  };
1401
1398
 
1402
- /**
1403
- * @typedef {Object} ValuesGetter
1404
- * @property {Object[]} values - The values data
1405
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
1399
+ /**
1400
+ * @typedef {Object} ValuesGetter
1401
+ * @property {Object[]} values - The values data
1402
+ * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
1406
1403
  */
1407
1404
 
1408
- /**
1409
- * A hook to load values for single and multiselect components.
1410
- *
1411
- * @param {Object} field - The form field to handle values for
1412
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
1405
+ /**
1406
+ * A hook to load values for single and multiselect components.
1407
+ *
1408
+ * @param {Object} field - The form field to handle values for
1409
+ * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
1413
1410
  */
1414
1411
  function useValuesAsync (field) {
1415
1412
  const {
@@ -1483,8 +1480,8 @@ function parseInputTime(stringTime) {
1483
1480
  if (is12h) {
1484
1481
  const isPM = workingString.includes('pm');
1485
1482
  const digits = workingString.match(/\d+/g);
1486
- const displayHour = parseInt(digits?.[0]);
1487
- const minute = parseInt(digits?.[1]) || 0;
1483
+ const displayHour = parseInt(digits && digits[0]);
1484
+ const minute = parseInt(digits && digits[1]) || 0;
1488
1485
  const isValidDisplayHour = isNumber(displayHour) && displayHour >= 1 && displayHour <= 12;
1489
1486
  const isValidMinute = minute >= 0 && minute <= 59;
1490
1487
  if (!isValidDisplayHour || !isValidMinute) return null;
@@ -1492,8 +1489,8 @@ function parseInputTime(stringTime) {
1492
1489
  return hour * 60 + minute;
1493
1490
  } else {
1494
1491
  const digits = workingString.match(/\d+/g);
1495
- const hour = parseInt(digits?.[0]);
1496
- const minute = parseInt(digits?.[1]);
1492
+ const hour = parseInt(digits && digits[0]);
1493
+ const minute = parseInt(digits && digits[1]);
1497
1494
  const isValidHour = isNumber(hour) && hour >= 0 && hour <= 23;
1498
1495
  const isValidMinute = isNumber(minute) && minute >= 0 && minute <= 59;
1499
1496
  if (!isValidHour || !isValidMinute) return null;
@@ -1696,7 +1693,7 @@ function Checklist(props) {
1696
1693
  } = useValuesAsync(field);
1697
1694
  const {
1698
1695
  formId
1699
- } = useContext(FormContext);
1696
+ } = useContext(FormContext$1);
1700
1697
  return jsxs("div", {
1701
1698
  class: classNames(formFieldClasses(type$9, {
1702
1699
  errors,
@@ -1728,13 +1725,18 @@ function Checklist(props) {
1728
1725
  })]
1729
1726
  });
1730
1727
  }
1731
- Checklist.create = function (options = {}) {
1732
- if (options.valuesKey) return options;
1733
- return {
1734
- values: [{
1728
+ Checklist.create = (options = {}) => {
1729
+ const defaults = {};
1730
+
1731
+ // provide default values if valuesKey isn't set
1732
+ if (!options.valuesKey) {
1733
+ defaults.values = [{
1735
1734
  label: 'Value',
1736
1735
  value: 'value'
1737
- }],
1736
+ }];
1737
+ }
1738
+ return {
1739
+ ...defaults,
1738
1740
  ...options
1739
1741
  };
1740
1742
  };
@@ -1743,14 +1745,15 @@ Checklist.label = 'Checklist';
1743
1745
  Checklist.keyed = true;
1744
1746
  Checklist.emptyValue = [];
1745
1747
  Checklist.sanitizeValue = sanitizeMultiSelectValue;
1748
+ Checklist.group = 'selection';
1746
1749
 
1747
- /**
1748
- * Check if condition is met with given variables.
1749
- *
1750
- * @param {string | undefined} condition
1751
- * @param {import('../../types').Data} data
1752
- *
1753
- * @returns {boolean} true if condition is met or no condition or condition checker exists
1750
+ /**
1751
+ * Check if condition is met with given variables.
1752
+ *
1753
+ * @param {string | undefined} condition
1754
+ * @param {import('../../types').Data} data
1755
+ *
1756
+ * @returns {boolean} true if condition is met or no condition or condition checker exists
1754
1757
  */
1755
1758
  function useCondition(condition, data) {
1756
1759
  const initialData = useService('form')._getState().initialData;
@@ -1786,7 +1789,7 @@ function FormField(props) {
1786
1789
  const {
1787
1790
  Element,
1788
1791
  Empty
1789
- } = useContext(FormRenderContext);
1792
+ } = useContext(FormRenderContext$1);
1790
1793
  const FormFieldComponent = formFields.get(field.type);
1791
1794
  if (!FormFieldComponent) {
1792
1795
  throw new Error(`cannot render field <${field.type}>`);
@@ -1818,7 +1821,7 @@ function Default(props) {
1818
1821
  const {
1819
1822
  Children,
1820
1823
  Empty
1821
- } = useContext(FormRenderContext);
1824
+ } = useContext(FormRenderContext$1);
1822
1825
  const {
1823
1826
  field
1824
1827
  } = props;
@@ -1837,20 +1840,20 @@ function Default(props) {
1837
1840
  }), components.length ? null : jsx(Empty, {})]
1838
1841
  });
1839
1842
  }
1840
- Default.create = function (options = {}) {
1841
- return {
1842
- components: [],
1843
- ...options
1844
- };
1845
- };
1843
+ Default.create = (options = {}) => ({
1844
+ components: [],
1845
+ ...options
1846
+ });
1846
1847
  Default.type = 'default';
1847
1848
  Default.keyed = false;
1849
+ Default.label = null;
1850
+ Default.group = null;
1848
1851
 
1849
- 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); }
1852
+ 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); }
1850
1853
  var CalendarIcon = (({
1851
1854
  styles = {},
1852
1855
  ...props
1853
- }) => /*#__PURE__*/React.createElement("svg", _extends$h({
1856
+ }) => /*#__PURE__*/React.createElement("svg", _extends$j({
1854
1857
  width: "14",
1855
1858
  height: "15",
1856
1859
  viewBox: "0 0 28 30",
@@ -1859,8 +1862,8 @@ var CalendarIcon = (({
1859
1862
  }, props), /*#__PURE__*/React.createElement("path", {
1860
1863
  fillRule: "evenodd",
1861
1864
  clipRule: "evenodd",
1862
- d: "M19 2H9V0H7v2H2a2 2 0 00-2 2v24a2 2 0 002 2h24a2 2 0 002-2V4a2 2 0 00-2-2h-5V0h-2v2zM7 7V4H2v5h24V4h-5v3h-2V4H9v3H7zm-5 4v17h24V11H2z",
1863
- fill: "#000"
1865
+ fill: "currentColor",
1866
+ d: "M19 2H9V0H7v2H2a2 2 0 00-2 2v24a2 2 0 002 2h24a2 2 0 002-2V4a2 2 0 00-2-2h-5V0h-2v2zM7 7V4H2v5h24V4h-5v3h-2V4H9v3H7zm-5 4v17h24V11H2z"
1864
1867
  })));
1865
1868
 
1866
1869
  function InputAdorner(props) {
@@ -1873,10 +1876,10 @@ function InputAdorner(props) {
1873
1876
  disabled,
1874
1877
  hasErrors
1875
1878
  } = props;
1876
- const onAdornmentClick = () => inputRef?.current?.focus();
1879
+ const onAdornmentClick = () => inputRef && inputRef.current && inputRef.current.focus();
1877
1880
  return jsxs("div", {
1878
1881
  class: classNames('fjs-input-group', {
1879
- 'disabled': disabled
1882
+ 'fjs-disabled': disabled
1880
1883
  }, {
1881
1884
  'hasErrors': hasErrors
1882
1885
  }),
@@ -1925,23 +1928,15 @@ function Datepicker(props) {
1925
1928
 
1926
1929
  // setup flatpickr instance
1927
1930
  useEffect(() => {
1928
- if (disabled) {
1929
- setFlatpickrInstance(null);
1930
- return;
1931
- }
1932
1931
  let config = {
1933
1932
  allowInput: true,
1934
1933
  dateFormat: 'm/d/Y',
1935
1934
  static: true,
1936
1935
  clickOpens: false,
1936
+ // TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
1937
+ minDate: disallowPassedDates ? 'today' : '01/01/1900',
1937
1938
  errorHandler: () => {/* do nothing, we expect the values to sometimes be erronous and we don't want warnings polluting the console */}
1938
1939
  };
1939
- if (disallowPassedDates) {
1940
- config = {
1941
- ...config,
1942
- minDate: 'today'
1943
- };
1944
- }
1945
1940
  const instance = flatpickr(dateInputRef.current, config);
1946
1941
  setFlatpickrInstance(instance);
1947
1942
  const onCalendarFocusOut = e => {
@@ -1961,7 +1956,7 @@ function Datepicker(props) {
1961
1956
  // flatpicker logic that was lost when setting allowInput to true
1962
1957
  instance.config.onOpen = [() => instance.calendarContainer.addEventListener('focusout', onCalendarFocusOut), () => instance.calendarContainer.addEventListener('mousedown', onCalendarMouseDown)];
1963
1958
  instance.config.onClose = [() => instance.calendarContainer.removeEventListener('focusout', onCalendarFocusOut), () => instance.calendarContainer.removeEventListener('mousedown', onCalendarMouseDown)];
1964
- }, [disabled, disallowPassedDates]);
1959
+ }, [disallowPassedDates]);
1965
1960
 
1966
1961
  // onChange is updated dynamically, so not to re-render the flatpicker every time it changes
1967
1962
  useEffect(() => {
@@ -2025,7 +2020,7 @@ function Datepicker(props) {
2025
2020
  class: 'fjs-input',
2026
2021
  disabled: disabled,
2027
2022
  placeholder: "mm/dd/yyyy",
2028
- autoComplete: "false",
2023
+ autoComplete: "off",
2029
2024
  onFocus: onInputFocus,
2030
2025
  onKeyDown: onInputKeyDown,
2031
2026
  onMouseDown: e => !flatpickrInstance.isOpen && flatpickrInstance.open(),
@@ -2038,24 +2033,24 @@ function Datepicker(props) {
2038
2033
  });
2039
2034
  }
2040
2035
 
2041
- 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); }
2036
+ 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); }
2042
2037
  var ClockIcon = (({
2043
2038
  styles = {},
2044
2039
  ...props
2045
- }) => /*#__PURE__*/React.createElement("svg", _extends$g({
2040
+ }) => /*#__PURE__*/React.createElement("svg", _extends$i({
2046
2041
  width: "16",
2047
2042
  height: "16",
2048
2043
  viewBox: "0 0 28 29",
2049
2044
  fill: "none",
2050
2045
  xmlns: "http://www.w3.org/2000/svg"
2051
2046
  }, props), /*#__PURE__*/React.createElement("path", {
2052
- d: "M13 14.41L18.59 20 20 18.59l-5-5.01V5h-2v9.41z",
2053
- fill: "#000"
2047
+ fill: "currentColor",
2048
+ d: "M13 14.41L18.59 20 20 18.59l-5-5.01V5h-2v9.41z"
2054
2049
  }), /*#__PURE__*/React.createElement("path", {
2055
2050
  fillRule: "evenodd",
2056
2051
  clipRule: "evenodd",
2057
- d: "M6.222 25.64A14 14 0 1021.778 2.36 14 14 0 006.222 25.64zM7.333 4.023a12 12 0 1113.334 19.955A12 12 0 017.333 4.022z",
2058
- fill: "#000"
2052
+ fill: "currentColor",
2053
+ d: "M6.222 25.64A14 14 0 1021.778 2.36 14 14 0 006.222 25.64zM7.333 4.023a12 12 0 1113.334 19.955A12 12 0 017.333 4.022z"
2059
2054
  })));
2060
2055
 
2061
2056
  function useKeyDownAction(targetKey, action, listenerElement = window) {
@@ -2078,7 +2073,7 @@ const DEFAULT_LABEL_GETTER = value => value;
2078
2073
  const NOOP = () => {};
2079
2074
  function DropdownList(props) {
2080
2075
  const {
2081
- keyEventsListener = window,
2076
+ listenerElement = window,
2082
2077
  values = [],
2083
2078
  getLabel = DEFAULT_LABEL_GETTER,
2084
2079
  onValueSelected = NOOP,
@@ -2108,18 +2103,18 @@ function DropdownList(props) {
2108
2103
  changeFocusedValueIndex(-1);
2109
2104
  setMouseControl(false);
2110
2105
  }
2111
- }, keyEventsListener);
2106
+ }, listenerElement);
2112
2107
  useKeyDownAction('ArrowDown', () => {
2113
2108
  if (values.length) {
2114
2109
  changeFocusedValueIndex(1);
2115
2110
  setMouseControl(false);
2116
2111
  }
2117
- }, keyEventsListener);
2112
+ }, listenerElement);
2118
2113
  useKeyDownAction('Enter', () => {
2119
2114
  if (focusedItem) {
2120
2115
  onValueSelected(focusedItem);
2121
2116
  }
2122
- }, keyEventsListener);
2117
+ }, listenerElement);
2123
2118
  useEffect(() => {
2124
2119
  const individualEntries = dropdownContainer.current.children;
2125
2120
  if (individualEntries.length && !mouseControl) {
@@ -2147,6 +2142,7 @@ function DropdownList(props) {
2147
2142
  ref: dropdownContainer,
2148
2143
  tabIndex: -1,
2149
2144
  class: "fjs-dropdownlist",
2145
+ onMouseDown: e => e.preventDefault(),
2150
2146
  style: {
2151
2147
  maxHeight: height,
2152
2148
  scrollBehavior: smoothScrolling ? 'smooth' : 'auto'
@@ -2158,10 +2154,7 @@ function DropdownList(props) {
2158
2154
  }),
2159
2155
  onMouseMove: mouseControl ? undefined : e => onMouseMovedInKeyboardMode(e, i),
2160
2156
  onMouseEnter: mouseControl ? () => setFocusedValueIndex(i) : undefined,
2161
- onMouseDown: e => {
2162
- e.preventDefault();
2163
- onValueSelected(v);
2164
- },
2157
+ onMouseDown: e => onValueSelected(v),
2165
2158
  children: getLabel(v)
2166
2159
  });
2167
2160
  }), !values.length && jsx("div", {
@@ -2294,7 +2287,7 @@ function Timepicker(props) {
2294
2287
  value: rawValue,
2295
2288
  disabled: disabled,
2296
2289
  placeholder: use24h ? 'hh:mm' : 'hh:mm ?m',
2297
- autoComplete: "false",
2290
+ autoComplete: "off",
2298
2291
  onFocus: () => useDropdown && setDropdownIsOpen(true),
2299
2292
  onClick: () => useDropdown && setDropdownIsOpen(true)
2300
2293
 
@@ -2345,7 +2338,7 @@ function Datetime(props) {
2345
2338
  } = validate;
2346
2339
  const {
2347
2340
  formId
2348
- } = useContext(FormContext);
2341
+ } = useContext(FormContext$1);
2349
2342
  const getNullDateTime = () => ({
2350
2343
  date: new Date(Date.parse(null)),
2351
2344
  time: null
@@ -2361,31 +2354,29 @@ function Datetime(props) {
2361
2354
  date,
2362
2355
  time
2363
2356
  } = getNullDateTime();
2364
- if (!disabled) {
2365
- switch (subtype) {
2366
- case DATETIME_SUBTYPES.DATE:
2367
- {
2368
- date = new Date(Date.parse(value));
2369
- break;
2370
- }
2371
- case DATETIME_SUBTYPES.TIME:
2372
- {
2373
- time = parseIsoTime(value);
2374
- break;
2375
- }
2376
- case DATETIME_SUBTYPES.DATETIME:
2377
- {
2378
- date = new Date(Date.parse(value));
2379
- time = isValidDate(date) ? 60 * date.getHours() + date.getMinutes() : null;
2380
- break;
2381
- }
2382
- }
2357
+ switch (subtype) {
2358
+ case DATETIME_SUBTYPES.DATE:
2359
+ {
2360
+ date = new Date(Date.parse(value));
2361
+ break;
2362
+ }
2363
+ case DATETIME_SUBTYPES.TIME:
2364
+ {
2365
+ time = parseIsoTime(value);
2366
+ break;
2367
+ }
2368
+ case DATETIME_SUBTYPES.DATETIME:
2369
+ {
2370
+ date = new Date(Date.parse(value));
2371
+ time = isValidDate(date) ? 60 * date.getHours() + date.getMinutes() : null;
2372
+ break;
2373
+ }
2383
2374
  }
2384
2375
  setDateTime({
2385
2376
  date,
2386
2377
  time
2387
2378
  });
2388
- }, [subtype, value, disabled]);
2379
+ }, [subtype, value]);
2389
2380
  const computeAndSetState = useCallback(({
2390
2381
  date,
2391
2382
  time
@@ -2488,12 +2479,12 @@ function Datetime(props) {
2488
2479
  })]
2489
2480
  });
2490
2481
  }
2491
- Datetime.create = function (options = {}) {
2492
- const newOptions = {};
2493
- set(newOptions, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES.DATE);
2494
- set(newOptions, DATE_LABEL_PATH, 'Date');
2482
+ Datetime.create = (options = {}) => {
2483
+ const defaults = {};
2484
+ set(defaults, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES.DATE);
2485
+ set(defaults, DATE_LABEL_PATH, 'Date');
2495
2486
  return {
2496
- ...newOptions,
2487
+ ...defaults,
2497
2488
  ...options
2498
2489
  };
2499
2490
  };
@@ -2501,11 +2492,13 @@ Datetime.type = type$8;
2501
2492
  Datetime.keyed = true;
2502
2493
  Datetime.emptyValue = null;
2503
2494
  Datetime.sanitizeValue = sanitizeDateTimePickerValue;
2495
+ Datetime.label = 'Date time';
2496
+ Datetime.group = 'basic-input';
2504
2497
 
2505
- /**
2506
- * This file must not be changed or exchanged.
2507
- *
2508
- * @see http://bpmn.io/license for more information.
2498
+ /**
2499
+ * This file must not be changed or exchanged.
2500
+ *
2501
+ * @see http://bpmn.io/license for more information.
2509
2502
  */
2510
2503
  function Logo() {
2511
2504
  return jsxs("svg", {
@@ -2622,10 +2615,10 @@ function FormComponent(props) {
2622
2615
  });
2623
2616
  }
2624
2617
 
2625
- /**
2626
- *
2627
- * @param {string | undefined} expression
2628
- * @param {import('../../types').Data} data
2618
+ /**
2619
+ *
2620
+ * @param {string | undefined} expression
2621
+ * @param {import('../../types').Data} data
2629
2622
  */
2630
2623
  function useEvaluation(expression, data) {
2631
2624
  const initialData = useService('form')._getState().initialData;
@@ -2642,9 +2635,9 @@ function useEvaluation(expression, data) {
2642
2635
  return conditionChecker.evaluate(expression, filteredData);
2643
2636
  }
2644
2637
 
2645
- /**
2646
- *
2647
- * @param {string} value
2638
+ /**
2639
+ *
2640
+ * @param {string} value
2648
2641
  */
2649
2642
  function useExpressionValue(value) {
2650
2643
  const formData = useService('form')._getState().data;
@@ -2664,11 +2657,11 @@ function isExpression(value) {
2664
2657
  return isString(value) && value.startsWith('=');
2665
2658
  }
2666
2659
 
2667
- 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); }
2660
+ 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); }
2668
2661
  var ImagePlaceholder = (({
2669
2662
  styles = {},
2670
2663
  ...props
2671
- }) => /*#__PURE__*/React.createElement("svg", _extends$f({
2664
+ }) => /*#__PURE__*/React.createElement("svg", _extends$h({
2672
2665
  width: "64",
2673
2666
  height: "64",
2674
2667
  viewBox: "0 0 1280 1280",
@@ -2708,7 +2701,7 @@ function Image(props) {
2708
2701
  const altText = useExpressionValue(alt);
2709
2702
  const {
2710
2703
  formId
2711
- } = useContext(FormContext);
2704
+ } = useContext(FormContext$1);
2712
2705
  return jsx("div", {
2713
2706
  class: formFieldClasses(type$7),
2714
2707
  children: jsxs("div", {
@@ -2727,13 +2720,47 @@ function Image(props) {
2727
2720
  })
2728
2721
  });
2729
2722
  }
2730
- Image.create = function (options = {}) {
2731
- return {
2732
- ...options
2733
- };
2734
- };
2723
+ Image.create = (options = {}) => ({
2724
+ ...options
2725
+ });
2735
2726
  Image.type = type$7;
2736
2727
  Image.keyed = false;
2728
+ Image.label = 'Image view';
2729
+ Image.group = 'presentation';
2730
+
2731
+ 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); }
2732
+ var AngelDownIcon = (({
2733
+ styles = {},
2734
+ ...props
2735
+ }) => /*#__PURE__*/React.createElement("svg", _extends$g({
2736
+ xmlns: "http://www.w3.org/2000/svg",
2737
+ width: "8",
2738
+ height: "8"
2739
+ }, props), /*#__PURE__*/React.createElement("path", {
2740
+ fillRule: "evenodd",
2741
+ clipRule: "evenodd",
2742
+ fill: "currentColor",
2743
+ stroke: "currentColor",
2744
+ strokeWidth: ".5",
2745
+ d: "M7.75 1.336L4 6.125.258 1.335 0 1.54l4 5.125L8 1.54zm0 0"
2746
+ })));
2747
+
2748
+ 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); }
2749
+ var AngelUpIcon = (({
2750
+ styles = {},
2751
+ ...props
2752
+ }) => /*#__PURE__*/React.createElement("svg", _extends$f({
2753
+ xmlns: "http://www.w3.org/2000/svg",
2754
+ width: "8",
2755
+ height: "8"
2756
+ }, props), /*#__PURE__*/React.createElement("path", {
2757
+ fillRule: "evenodd",
2758
+ clipRule: "evenodd",
2759
+ fill: "currentColor",
2760
+ stroke: "currentColor",
2761
+ strokeWidth: ".5",
2762
+ d: "M7.75 6.664L4 1.875.258 6.665 0 6.46l4-5.125L8 6.46zm0 0"
2763
+ })));
2737
2764
 
2738
2765
  const type$6 = 'number';
2739
2766
  function Numberfield(props) {
@@ -2775,6 +2802,7 @@ function Numberfield(props) {
2775
2802
  }), [stringValueCache, value, field]);
2776
2803
  const displayValue = useMemo(() => {
2777
2804
  if (value === 'NaN') return 'NaN';
2805
+ if (stringValueCache === '-') return '-';
2778
2806
  return cacheValueMatchesState ? stringValueCache : value || value === 0 ? Big(value).toFixed() : '';
2779
2807
  }, [stringValueCache, value, cacheValueMatchesState]);
2780
2808
  const arrowIncrementValue = useMemo(() => {
@@ -2794,6 +2822,10 @@ function Numberfield(props) {
2794
2822
 
2795
2823
  // treat commas as dots
2796
2824
  stringValue = stringValue.replaceAll(',', '.');
2825
+ if (stringValue === '-') {
2826
+ setStringValueCache('-');
2827
+ return;
2828
+ }
2797
2829
  if (isNaN(Number(stringValue))) {
2798
2830
  setStringValueCache('NaN');
2799
2831
  onChange({
@@ -2857,7 +2889,7 @@ function Numberfield(props) {
2857
2889
  };
2858
2890
  const {
2859
2891
  formId
2860
- } = useContext(FormContext);
2892
+ } = useContext(FormContext$1);
2861
2893
  return jsxs("div", {
2862
2894
  class: formFieldClasses(type$6, {
2863
2895
  errors,
@@ -2873,7 +2905,7 @@ function Numberfield(props) {
2873
2905
  post: suffixAdorner,
2874
2906
  children: jsxs("div", {
2875
2907
  class: classNames('fjs-vertical-group', {
2876
- 'disabled': disabled
2908
+ 'fjs-disabled': disabled
2877
2909
  }, {
2878
2910
  'hasErrors': errors.length
2879
2911
  }),
@@ -2894,22 +2926,24 @@ function Numberfield(props) {
2894
2926
  value: displayValue
2895
2927
  }), jsxs("div", {
2896
2928
  class: classNames('fjs-number-arrow-container', {
2897
- 'disabled': disabled
2929
+ 'fjs-disabled': disabled
2898
2930
  }),
2899
2931
  children: [jsx("button", {
2900
2932
  class: "fjs-number-arrow-up",
2901
2933
  type: "button",
2934
+ "aria-label": "Increment",
2902
2935
  onClick: () => increment(),
2903
2936
  tabIndex: -1,
2904
- children: "\u02C4"
2937
+ children: jsx(AngelUpIcon, {})
2905
2938
  }), jsx("div", {
2906
2939
  class: "fjs-number-arrow-separator"
2907
2940
  }), jsx("button", {
2908
2941
  class: "fjs-number-arrow-down",
2909
2942
  type: "button",
2943
+ "aria-label": "Decrement",
2910
2944
  onClick: () => decrement(),
2911
2945
  tabIndex: -1,
2912
- children: "\u02C5"
2946
+ children: jsx(AngelDownIcon, {})
2913
2947
  })]
2914
2948
  })]
2915
2949
  })
@@ -2920,7 +2954,9 @@ function Numberfield(props) {
2920
2954
  })]
2921
2955
  });
2922
2956
  }
2923
- Numberfield.create = (options = {}) => options;
2957
+ Numberfield.create = (options = {}) => ({
2958
+ ...options
2959
+ });
2924
2960
  Numberfield.sanitizeValue = ({
2925
2961
  value,
2926
2962
  formField
@@ -2938,6 +2974,7 @@ Numberfield.type = type$6;
2938
2974
  Numberfield.keyed = true;
2939
2975
  Numberfield.label = 'Number';
2940
2976
  Numberfield.emptyValue = null;
2977
+ Numberfield.group = 'basic-input';
2941
2978
 
2942
2979
  const type$5 = 'radio';
2943
2980
  function Radio(props) {
@@ -2968,7 +3005,7 @@ function Radio(props) {
2968
3005
  } = useValuesAsync(field);
2969
3006
  const {
2970
3007
  formId
2971
- } = useContext(FormContext);
3008
+ } = useContext(FormContext$1);
2972
3009
  return jsxs("div", {
2973
3010
  class: formFieldClasses(type$5, {
2974
3011
  errors,
@@ -3002,12 +3039,17 @@ function Radio(props) {
3002
3039
  });
3003
3040
  }
3004
3041
  Radio.create = function (options = {}) {
3005
- if (options.valuesKey) return options;
3006
- return {
3007
- values: [{
3042
+ const defaults = {};
3043
+
3044
+ // provide default values if valuesKey isn't set
3045
+ if (!options.valuesKey) {
3046
+ defaults.values = [{
3008
3047
  label: 'Value',
3009
3048
  value: 'value'
3010
- }],
3049
+ }];
3050
+ }
3051
+ return {
3052
+ ...defaults,
3011
3053
  ...options
3012
3054
  };
3013
3055
  };
@@ -3016,121 +3058,359 @@ Radio.label = 'Radio';
3016
3058
  Radio.keyed = true;
3017
3059
  Radio.emptyValue = null;
3018
3060
  Radio.sanitizeValue = sanitizeSingleSelectValue;
3019
-
3020
- const type$4 = 'select';
3021
- function Select(props) {
3022
- const {
3023
- disabled,
3024
- errors = [],
3025
- field,
3026
- value
3027
- } = props;
3028
- const {
3029
- description,
3030
- id,
3031
- label,
3032
- validate = {}
3033
- } = field;
3034
- const {
3035
- required
3036
- } = validate;
3037
- const onChange = ({
3038
- target
3039
- }) => {
3040
- props.onChange({
3041
- field,
3042
- value: target.value === '' ? null : target.value
3043
- });
3044
- };
3045
- const {
3046
- state: loadState,
3047
- values: options
3048
- } = useValuesAsync(field);
3049
- const {
3050
- formId
3051
- } = useContext(FormContext);
3052
- return jsxs("div", {
3053
- class: formFieldClasses(type$4, {
3054
- errors,
3055
- disabled
3056
- }),
3057
- children: [jsx(Label, {
3058
- id: prefixId(id, formId),
3059
- label: label,
3060
- required: required
3061
- }), jsxs("select", {
3062
- class: "fjs-select",
3063
- disabled: disabled,
3064
- id: prefixId(id, formId),
3065
- onChange: onChange,
3066
- value: value || '',
3067
- children: [jsx("option", {
3068
- value: ""
3069
- }), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
3070
- return jsx("option", {
3071
- value: option.value,
3072
- children: option.label
3073
- }, `${id}-${index}`);
3074
- })]
3075
- }), jsx(Description, {
3076
- description: description
3077
- }), jsx(Errors, {
3078
- errors: errors
3079
- })]
3080
- });
3081
- }
3082
- Select.create = function (options = {}) {
3083
- if (options.valuesKey) return options;
3084
- return {
3085
- values: [{
3086
- label: 'Value',
3087
- value: 'value'
3088
- }],
3089
- ...options
3090
- };
3091
- };
3092
- Select.type = type$4;
3093
- Select.label = 'Select';
3094
- Select.keyed = true;
3095
- Select.emptyValue = null;
3096
- Select.sanitizeValue = sanitizeSingleSelectValue;
3061
+ Radio.group = 'selection';
3097
3062
 
3098
3063
  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); }
3099
- var CloseIcon = (({
3064
+ var XMarkIcon = (({
3100
3065
  styles = {},
3101
3066
  ...props
3102
3067
  }) => /*#__PURE__*/React.createElement("svg", _extends$e({
3103
- width: "16",
3104
- height: "16",
3105
- fill: "none",
3106
- xmlns: "http://www.w3.org/2000/svg"
3068
+ xmlns: "http://www.w3.org/2000/svg",
3069
+ width: "8",
3070
+ height: "8"
3107
3071
  }, props), /*#__PURE__*/React.createElement("path", {
3108
3072
  fillRule: "evenodd",
3109
3073
  clipRule: "evenodd",
3110
- 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",
3111
- fill: "currentColor"
3074
+ fill: "currentColor",
3075
+ stroke: "currentColor",
3076
+ strokeWidth: ".5",
3077
+ d: "M4 3.766L7.43.336l.234.234L4.234 4l3.43 3.43-.234.234L4 4.234.57 7.664.336 7.43 3.766 4 .336.57.57.336zm0 0"
3112
3078
  })));
3113
3079
 
3114
- const type$3 = 'taglist';
3115
- function Taglist(props) {
3080
+ function SearchableSelect(props) {
3116
3081
  const {
3082
+ id,
3117
3083
  disabled,
3118
- errors = [],
3084
+ errors,
3119
3085
  field,
3120
- value: values = []
3086
+ value
3121
3087
  } = props;
3122
- const {
3123
- description,
3124
- id,
3125
- label
3126
- } = field;
3127
3088
  const {
3128
3089
  formId
3129
- } = useContext(FormContext);
3090
+ } = useContext(FormContext$1);
3130
3091
  const [filter, setFilter] = useState('');
3131
- const [filteredOptions, setFilteredOptions] = useState([]);
3132
3092
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3133
- const [hasOptionsLeft, setHasOptionsLeft] = useState(true);
3093
+ const [shouldApplyFilter, setShouldApplyFilter] = useState(true);
3094
+ const [isEscapeClosed, setIsEscapeClose] = useState(false);
3095
+ const searchbarRef = useRef();
3096
+ const {
3097
+ state: loadState,
3098
+ values: options
3099
+ } = useValuesAsync(field);
3100
+
3101
+ // 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
3102
+ const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3103
+ [o.value]: options[x]
3104
+ }))), [options]);
3105
+ const valueLabel = useMemo(() => value && valueToOptionMap[value] && valueToOptionMap[value].label || '', [value, valueToOptionMap]);
3106
+
3107
+ // whenever we change the underlying value, set the label to it
3108
+ useEffect(() => {
3109
+ setFilter(valueLabel);
3110
+ }, [valueLabel]);
3111
+ const filteredOptions = useMemo(() => {
3112
+ if (loadState === LOAD_STATES.LOADED) {
3113
+ return shouldApplyFilter ? options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase())) : options;
3114
+ }
3115
+ return [];
3116
+ }, [filter, loadState, options, shouldApplyFilter]);
3117
+ const onChange = ({
3118
+ target
3119
+ }) => {
3120
+ setIsEscapeClose(false);
3121
+ setIsDropdownExpanded(true);
3122
+ setShouldApplyFilter(true);
3123
+ setFilter(target.value || '');
3124
+ };
3125
+ const setValue = useCallback(option => {
3126
+ setFilter(option && option.label || '');
3127
+ props.onChange({
3128
+ value: option && option.value || null,
3129
+ field
3130
+ });
3131
+ }, [field, props]);
3132
+ const onInputKeyDown = useCallback(keyDownEvent => {
3133
+ switch (keyDownEvent.key) {
3134
+ case 'ArrowUp':
3135
+ keyDownEvent.preventDefault();
3136
+ break;
3137
+ case 'ArrowDown':
3138
+ {
3139
+ if (!isDropdownExpanded) {
3140
+ setIsDropdownExpanded(true);
3141
+ setShouldApplyFilter(false);
3142
+ }
3143
+ keyDownEvent.preventDefault();
3144
+ break;
3145
+ }
3146
+ case 'Escape':
3147
+ setIsEscapeClose(true);
3148
+ break;
3149
+ case 'Enter':
3150
+ if (isEscapeClosed) {
3151
+ setIsEscapeClose(false);
3152
+ }
3153
+ break;
3154
+ }
3155
+ }, [isDropdownExpanded, isEscapeClosed]);
3156
+ const displayState = useMemo(() => {
3157
+ const ds = {};
3158
+ ds.componentReady = !disabled && loadState === LOAD_STATES.LOADED;
3159
+ ds.displayCross = ds.componentReady && value !== null && value !== undefined;
3160
+ ds.displayDropdown = !disabled && isDropdownExpanded && !isEscapeClosed;
3161
+ return ds;
3162
+ }, [disabled, isDropdownExpanded, isEscapeClosed, loadState, value]);
3163
+ const onAngelMouseDown = useCallback(e => {
3164
+ setIsEscapeClose(false);
3165
+ setIsDropdownExpanded(!isDropdownExpanded);
3166
+ const searchbar = searchbarRef.current;
3167
+ isDropdownExpanded ? searchbar.blur() : searchbar.focus();
3168
+ e.preventDefault();
3169
+ }, [isDropdownExpanded]);
3170
+ return jsxs(Fragment$1, {
3171
+ children: [jsxs("div", {
3172
+ id: prefixId(`${id}`, formId),
3173
+ class: classNames('fjs-input-group', {
3174
+ 'disabled': disabled
3175
+ }, {
3176
+ 'hasErrors': errors.length
3177
+ }),
3178
+ children: [jsx("input", {
3179
+ disabled: disabled,
3180
+ class: "fjs-input",
3181
+ ref: searchbarRef,
3182
+ id: prefixId(`${id}-search`, formId),
3183
+ onChange: onChange,
3184
+ type: "text",
3185
+ value: filter,
3186
+ placeholder: 'Search',
3187
+ autoComplete: "off",
3188
+ onKeyDown: e => onInputKeyDown(e),
3189
+ onMouseDown: () => {
3190
+ setIsEscapeClose(false);
3191
+ setIsDropdownExpanded(true);
3192
+ setShouldApplyFilter(false);
3193
+ },
3194
+ onFocus: () => {
3195
+ setIsDropdownExpanded(true);
3196
+ setShouldApplyFilter(false);
3197
+ },
3198
+ onBlur: () => {
3199
+ setIsDropdownExpanded(false);
3200
+ setFilter(valueLabel);
3201
+ }
3202
+ }), displayState.displayCross && jsxs("span", {
3203
+ class: "fjs-select-cross",
3204
+ onMouseDown: e => {
3205
+ setValue(null);
3206
+ e.preventDefault();
3207
+ },
3208
+ children: [jsx(XMarkIcon, {}), " "]
3209
+ }), jsx("span", {
3210
+ class: "fjs-select-arrow",
3211
+ onMouseDown: e => onAngelMouseDown(e),
3212
+ children: displayState.displayDropdown ? jsx(AngelUpIcon, {}) : jsx(AngelDownIcon, {})
3213
+ })]
3214
+ }), jsx("div", {
3215
+ class: "fjs-select-anchor",
3216
+ children: displayState.displayDropdown && jsx(DropdownList, {
3217
+ values: filteredOptions,
3218
+ getLabel: o => o.label,
3219
+ onValueSelected: o => {
3220
+ setValue(o);
3221
+ setIsDropdownExpanded(false);
3222
+ },
3223
+ listenerElement: searchbarRef.current
3224
+ })
3225
+ })]
3226
+ });
3227
+ }
3228
+
3229
+ function SimpleSelect(props) {
3230
+ const {
3231
+ id,
3232
+ disabled,
3233
+ errors,
3234
+ field,
3235
+ value
3236
+ } = props;
3237
+ const {
3238
+ formId
3239
+ } = useContext(FormContext$1);
3240
+ const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3241
+ const selectRef = useRef();
3242
+ const {
3243
+ state: loadState,
3244
+ values: options
3245
+ } = useValuesAsync(field);
3246
+
3247
+ // 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
3248
+ const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3249
+ [o.value]: options[x]
3250
+ }))), [options]);
3251
+ const valueLabel = useMemo(() => value && valueToOptionMap[value] && valueToOptionMap[value].label || '', [value, valueToOptionMap]);
3252
+ const setValue = useCallback(option => {
3253
+ props.onChange({
3254
+ value: option && option.value || null,
3255
+ field
3256
+ });
3257
+ }, [field, props]);
3258
+ const displayState = useMemo(() => {
3259
+ const ds = {};
3260
+ ds.componentReady = !disabled && loadState === LOAD_STATES.LOADED;
3261
+ ds.displayCross = ds.componentReady && value !== null && value !== undefined;
3262
+ ds.displayDropdown = !disabled && isDropdownExpanded;
3263
+ return ds;
3264
+ }, [disabled, isDropdownExpanded, loadState, value]);
3265
+ const onMouseDown = useCallback(e => {
3266
+ const select = selectRef.current;
3267
+ setIsDropdownExpanded(!isDropdownExpanded);
3268
+ if (isDropdownExpanded) {
3269
+ select.blur();
3270
+ } else {
3271
+ select.focus();
3272
+ }
3273
+ e.preventDefault();
3274
+ }, [isDropdownExpanded]);
3275
+ const initialFocusIndex = useMemo(() => value && findIndex(options, o => o.value === value) || 0, [options, value]);
3276
+ return jsxs(Fragment$1, {
3277
+ children: [jsxs("div", {
3278
+ ref: selectRef,
3279
+ id: prefixId(`${id}`, formId),
3280
+ class: classNames('fjs-input-group', {
3281
+ 'disabled': disabled
3282
+ }, {
3283
+ 'hasErrors': errors.length
3284
+ }),
3285
+ onFocus: () => setIsDropdownExpanded(true),
3286
+ onBlur: () => setIsDropdownExpanded(false),
3287
+ onMouseDown: e => onMouseDown(e),
3288
+ tabIndex: disabled ? undefined : 0,
3289
+ children: [jsx("div", {
3290
+ class: classNames('fjs-select-display', {
3291
+ 'fjs-select-placeholder': !value
3292
+ }),
3293
+ id: prefixId(`${id}-display`, formId),
3294
+ children: valueLabel || 'Select'
3295
+ }), displayState.displayCross && jsx("span", {
3296
+ class: "fjs-select-cross",
3297
+ onMouseDown: e => {
3298
+ setValue(null);
3299
+ e.stopPropagation();
3300
+ },
3301
+ children: jsx(XMarkIcon, {})
3302
+ }), jsx("span", {
3303
+ class: "fjs-select-arrow",
3304
+ children: displayState.displayDropdown ? jsx(AngelUpIcon, {}) : jsx(AngelDownIcon, {})
3305
+ })]
3306
+ }), jsx("div", {
3307
+ class: "fjs-select-anchor",
3308
+ children: displayState.displayDropdown && jsx(DropdownList, {
3309
+ values: options,
3310
+ getLabel: o => o.label,
3311
+ initialFocusIndex: initialFocusIndex,
3312
+ onValueSelected: o => {
3313
+ setValue(o);
3314
+ setIsDropdownExpanded(false);
3315
+ },
3316
+ listenerElement: selectRef.current
3317
+ })
3318
+ })]
3319
+ });
3320
+ }
3321
+
3322
+ const type$4 = 'select';
3323
+ function Select(props) {
3324
+ const {
3325
+ disabled,
3326
+ errors = [],
3327
+ field,
3328
+ onChange,
3329
+ value
3330
+ } = props;
3331
+ const {
3332
+ description,
3333
+ id,
3334
+ label,
3335
+ searchable = false,
3336
+ validate = {}
3337
+ } = field;
3338
+ const {
3339
+ required
3340
+ } = validate;
3341
+ const {
3342
+ formId
3343
+ } = useContext(FormContext$1);
3344
+ const selectProps = useMemo(() => ({
3345
+ id,
3346
+ disabled,
3347
+ errors,
3348
+ field,
3349
+ value,
3350
+ onChange
3351
+ }), [disabled, errors, field, id, value, onChange]);
3352
+ return jsxs("div", {
3353
+ class: formFieldClasses(type$4, {
3354
+ errors,
3355
+ disabled
3356
+ }),
3357
+ children: [jsx(Label, {
3358
+ id: prefixId(id, formId),
3359
+ label: label,
3360
+ required: required
3361
+ }), searchable ? jsx(SearchableSelect, {
3362
+ ...selectProps
3363
+ }) : jsx(SimpleSelect, {
3364
+ ...selectProps
3365
+ }), jsx(Description, {
3366
+ description: description
3367
+ }), jsx(Errors, {
3368
+ errors: errors
3369
+ })]
3370
+ });
3371
+ }
3372
+ Select.create = (options = {}) => {
3373
+ const defaults = {};
3374
+
3375
+ // provide default values if valuesKey isn't set
3376
+ if (!options.valuesKey) {
3377
+ defaults.values = [{
3378
+ label: 'Value',
3379
+ value: 'value'
3380
+ }];
3381
+ }
3382
+ return {
3383
+ ...defaults,
3384
+ ...options
3385
+ };
3386
+ };
3387
+ Select.type = type$4;
3388
+ Select.label = 'Select';
3389
+ Select.keyed = true;
3390
+ Select.emptyValue = null;
3391
+ Select.sanitizeValue = sanitizeSingleSelectValue;
3392
+ Select.group = 'selection';
3393
+
3394
+ const type$3 = 'taglist';
3395
+ function Taglist(props) {
3396
+ const {
3397
+ disabled,
3398
+ errors = [],
3399
+ field,
3400
+ value: values = []
3401
+ } = props;
3402
+ const {
3403
+ description,
3404
+ id,
3405
+ label
3406
+ } = field;
3407
+ const {
3408
+ formId
3409
+ } = useContext(FormContext$1);
3410
+ const [filter, setFilter] = useState('');
3411
+ const [filteredOptions, setFilteredOptions] = useState([]);
3412
+ const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3413
+ const [hasOptionsLeft, setHasOptionsLeft] = useState(true);
3134
3414
  const [isEscapeClosed, setIsEscapeClose] = useState(false);
3135
3415
  const searchbarRef = useRef();
3136
3416
  const {
@@ -3214,6 +3494,7 @@ function Taglist(props) {
3214
3494
  searchbarRef.current.focus();
3215
3495
  }
3216
3496
  };
3497
+ const shouldDisplayDropdown = useMemo(() => !disabled && loadState === LOAD_STATES.LOADED && isDropdownExpanded && !isEscapeClosed, [disabled, isDropdownExpanded, isEscapeClosed, loadState]);
3217
3498
  return jsxs("div", {
3218
3499
  class: formFieldClasses(type$3, {
3219
3500
  errors,
@@ -3224,23 +3505,25 @@ function Taglist(props) {
3224
3505
  id: prefixId(`${id}-search`, formId)
3225
3506
  }), jsxs("div", {
3226
3507
  class: classNames('fjs-taglist', {
3227
- 'disabled': disabled
3508
+ 'fjs-disabled': disabled
3228
3509
  }),
3229
- children: [!disabled && loadState === LOAD_STATES.LOADED && jsx("div", {
3510
+ children: [loadState === LOAD_STATES.LOADED && jsx("div", {
3230
3511
  class: "fjs-taglist-tags",
3231
3512
  children: values.map(v => {
3232
3513
  return jsxs("div", {
3233
- class: "fjs-taglist-tag",
3514
+ class: classNames('fjs-taglist-tag', {
3515
+ 'fjs-disabled': disabled
3516
+ }),
3234
3517
  onMouseDown: e => e.preventDefault(),
3235
3518
  children: [jsx("span", {
3236
3519
  class: "fjs-taglist-tag-label",
3237
3520
  children: valueToOptionMap[v] ? valueToOptionMap[v].label : `unexpected value{${v}}`
3238
- }), jsx("button", {
3521
+ }), !disabled && jsx("button", {
3239
3522
  type: "button",
3240
3523
  title: "Remove tag",
3241
3524
  class: "fjs-taglist-tag-remove",
3242
3525
  onClick: event => onTagRemoveClick(event, v),
3243
- children: jsx(CloseIcon, {})
3526
+ children: jsx(XMarkIcon, {})
3244
3527
  })]
3245
3528
  });
3246
3529
  })
@@ -3252,7 +3535,7 @@ function Taglist(props) {
3252
3535
  onChange: onFilterChange,
3253
3536
  type: "text",
3254
3537
  value: filter,
3255
- placeholder: 'Search',
3538
+ placeholder: disabled ? '' : 'Search',
3256
3539
  autoComplete: "off",
3257
3540
  onKeyDown: e => onInputKeyDown(e),
3258
3541
  onMouseDown: () => setIsEscapeClose(false),
@@ -3264,7 +3547,7 @@ function Taglist(props) {
3264
3547
  })]
3265
3548
  }), jsx("div", {
3266
3549
  class: "fjs-taglist-anchor",
3267
- children: !disabled && loadState === LOAD_STATES.LOADED && isDropdownExpanded && !isEscapeClosed && jsx(DropdownList, {
3550
+ children: shouldDisplayDropdown && jsx(DropdownList, {
3268
3551
  values: filteredOptions,
3269
3552
  getLabel: o => o.label,
3270
3553
  onValueSelected: o => selectValue(o.value),
@@ -3278,21 +3561,222 @@ function Taglist(props) {
3278
3561
  })]
3279
3562
  });
3280
3563
  }
3281
- Taglist.create = function (options = {}) {
3282
- if (options.valuesKey) return options;
3283
- return {
3284
- values: [{
3285
- label: 'Value',
3286
- value: 'value'
3287
- }],
3288
- ...options
3289
- };
3290
- };
3291
- Taglist.type = type$3;
3292
- Taglist.label = 'Taglist';
3293
- Taglist.keyed = true;
3294
- Taglist.emptyValue = [];
3295
- Taglist.sanitizeValue = sanitizeMultiSelectValue;
3564
+ Taglist.create = (options = {}) => {
3565
+ const defaults = {};
3566
+
3567
+ // provide default values if valuesKey isn't set
3568
+ if (!options.valuesKey) {
3569
+ defaults.values = [{
3570
+ label: 'Value',
3571
+ value: 'value'
3572
+ }];
3573
+ }
3574
+ return {
3575
+ ...defaults,
3576
+ ...options
3577
+ };
3578
+ };
3579
+ Taglist.type = type$3;
3580
+ Taglist.label = 'Tag list';
3581
+ Taglist.keyed = true;
3582
+ Taglist.emptyValue = [];
3583
+ Taglist.sanitizeValue = sanitizeMultiSelectValue;
3584
+ Taglist.group = 'selection';
3585
+
3586
+ const type$2 = 'text';
3587
+ function Text(props) {
3588
+ const {
3589
+ field,
3590
+ disableLinks
3591
+ } = props;
3592
+ const {
3593
+ text = ''
3594
+ } = field;
3595
+ const textValue = useExpressionValue(text) || '';
3596
+ const componentOverrides = disableLinks ? {
3597
+ 'a': DisabledLink
3598
+ } : {};
3599
+ return jsx("div", {
3600
+ class: formFieldClasses(type$2),
3601
+ children: jsx(Markup, {
3602
+ markup: safeMarkdown(textValue),
3603
+ components: componentOverrides,
3604
+ trim: false
3605
+ })
3606
+ });
3607
+ }
3608
+ Text.create = (options = {}) => ({
3609
+ text: '# Text',
3610
+ ...options
3611
+ });
3612
+ Text.type = type$2;
3613
+ Text.keyed = false;
3614
+ Text.group = 'presentation';
3615
+ Text.label = 'Text view';
3616
+ function DisabledLink({
3617
+ href,
3618
+ children
3619
+ }) {
3620
+ return jsx("a", {
3621
+ class: "fjs-disabled-link",
3622
+ href: href,
3623
+ tabIndex: -1,
3624
+ children: children
3625
+ });
3626
+ }
3627
+
3628
+ const type$1 = 'textfield';
3629
+ function Textfield(props) {
3630
+ const {
3631
+ disabled,
3632
+ errors = [],
3633
+ field,
3634
+ value = ''
3635
+ } = props;
3636
+ const {
3637
+ description,
3638
+ id,
3639
+ label,
3640
+ appearance = {},
3641
+ validate = {}
3642
+ } = field;
3643
+ const {
3644
+ prefixAdorner,
3645
+ suffixAdorner
3646
+ } = appearance;
3647
+ const {
3648
+ required
3649
+ } = validate;
3650
+ const onChange = ({
3651
+ target
3652
+ }) => {
3653
+ props.onChange({
3654
+ field,
3655
+ value: target.value
3656
+ });
3657
+ };
3658
+ const {
3659
+ formId
3660
+ } = useContext(FormContext$1);
3661
+ return jsxs("div", {
3662
+ class: formFieldClasses(type$1, {
3663
+ errors,
3664
+ disabled
3665
+ }),
3666
+ children: [jsx(Label, {
3667
+ id: prefixId(id, formId),
3668
+ label: label,
3669
+ required: required
3670
+ }), jsx(InputAdorner, {
3671
+ disabled: disabled,
3672
+ pre: prefixAdorner,
3673
+ post: suffixAdorner,
3674
+ children: jsx("input", {
3675
+ class: "fjs-input",
3676
+ disabled: disabled,
3677
+ id: prefixId(id, formId),
3678
+ onInput: onChange,
3679
+ type: "text",
3680
+ value: value
3681
+ })
3682
+ }), jsx(Description, {
3683
+ description: description
3684
+ }), jsx(Errors, {
3685
+ errors: errors
3686
+ })]
3687
+ });
3688
+ }
3689
+ Textfield.create = (options = {}) => ({
3690
+ ...options
3691
+ });
3692
+ Textfield.type = type$1;
3693
+ Textfield.label = 'Text field';
3694
+ Textfield.keyed = true;
3695
+ Textfield.emptyValue = '';
3696
+ Textfield.sanitizeValue = ({
3697
+ value
3698
+ }) => isArray(value) || isObject(value) ? '' : String(value);
3699
+ Textfield.group = 'basic-input';
3700
+
3701
+ const type = 'textarea';
3702
+ function Textarea(props) {
3703
+ const {
3704
+ disabled,
3705
+ errors = [],
3706
+ field,
3707
+ value = ''
3708
+ } = props;
3709
+ const {
3710
+ description,
3711
+ id,
3712
+ label,
3713
+ validate = {}
3714
+ } = field;
3715
+ const {
3716
+ required
3717
+ } = validate;
3718
+ const textareaRef = useRef();
3719
+ const onInput = ({
3720
+ target
3721
+ }) => {
3722
+ props.onChange({
3723
+ field,
3724
+ value: target.value
3725
+ });
3726
+ };
3727
+ const autoSizeTextarea = useCallback(textarea => {
3728
+ // Ensures the textarea shrinks back, and improves resizing behavior consistency
3729
+ textarea.style.height = '0px';
3730
+ const computed = window.getComputedStyle(textarea);
3731
+ const calculatedHeight = parseInt(computed.getPropertyValue('border-top-width')) + parseInt(computed.getPropertyValue('padding-top')) + textarea.scrollHeight + parseInt(computed.getPropertyValue('padding-bottom')) + parseInt(computed.getPropertyValue('border-bottom-width'));
3732
+ const minHeight = 75;
3733
+ const maxHeight = 350;
3734
+ const displayHeight = Math.max(Math.min(calculatedHeight, maxHeight), minHeight);
3735
+ textarea.style.height = `${displayHeight}px`;
3736
+
3737
+ // Overflow is hidden by default to hide scrollbar flickering
3738
+ textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
3739
+ }, []);
3740
+ useEffect(() => {
3741
+ autoSizeTextarea(textareaRef.current);
3742
+ }, [autoSizeTextarea, value]);
3743
+ const {
3744
+ formId
3745
+ } = useContext(FormContext$1);
3746
+ return jsxs("div", {
3747
+ class: formFieldClasses(type, {
3748
+ errors,
3749
+ disabled
3750
+ }),
3751
+ children: [jsx(Label, {
3752
+ id: prefixId(id, formId),
3753
+ label: label,
3754
+ required: required
3755
+ }), jsx("textarea", {
3756
+ class: "fjs-textarea",
3757
+ disabled: disabled,
3758
+ id: prefixId(id, formId),
3759
+ onInput: onInput,
3760
+ value: value,
3761
+ ref: textareaRef
3762
+ }), jsx(Description, {
3763
+ description: description
3764
+ }), jsx(Errors, {
3765
+ errors: errors
3766
+ })]
3767
+ });
3768
+ }
3769
+ Textarea.create = (options = {}) => ({
3770
+ ...options
3771
+ });
3772
+ Textarea.type = type;
3773
+ Textarea.label = 'Text area';
3774
+ Textarea.keyed = true;
3775
+ Textarea.emptyValue = '';
3776
+ Textarea.sanitizeValue = ({
3777
+ value
3778
+ }) => isArray(value) || isObject(value) ? '' : String(value);
3779
+ Textarea.group = 'basic-input';
3296
3780
 
3297
3781
  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); }
3298
3782
  var ButtonIcon = (({
@@ -3324,18 +3808,18 @@ var ChecklistIcon = (({
3324
3808
  styles = {},
3325
3809
  ...props
3326
3810
  }) => /*#__PURE__*/React.createElement("svg", _extends$b({
3811
+ xmlns: "http://www.w3.org/2000/svg",
3327
3812
  width: "54",
3328
3813
  height: "54",
3329
- fill: "none",
3330
- xmlns: "http://www.w3.org/2000/svg"
3814
+ fill: "none"
3331
3815
  }, props), /*#__PURE__*/React.createElement("path", {
3332
3816
  fillRule: "evenodd",
3333
3817
  clipRule: "evenodd",
3334
- d: "M19 24h-6v6h6v-6zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2v-6a2 2 0 00-2-2h-6zm6 18h-6v6h6v-6zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2v-6a2 2 0 00-2-2h-6zm6-30h-6v6h6V8zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2V8a2 2 0 00-2-2h-6z",
3335
- fill: "#22242A"
3818
+ d: "M18 12h-6v6h6v-6zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2v-6a2 2 0 00-2-2h-6zM18 36h-6v6h6v-6zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2v-6a2 2 0 00-2-2h-6zM18 24h-6v6h6v-6zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2v-6a2 2 0 00-2-2h-6z",
3819
+ fill: "#161616"
3336
3820
  }), /*#__PURE__*/React.createElement("path", {
3337
- d: "M26 26a1 1 0 011-1h15a1 1 0 011 1v2a1 1 0 01-1 1H27a1 1 0 01-1-1v-2zm0 16a1 1 0 011-1h15a1 1 0 011 1v2a1 1 0 01-1 1H27a1 1 0 01-1-1v-2zm0-32a1 1 0 011-1h15a1 1 0 011 1v2a1 1 0 01-1 1H27a1 1 0 01-1-1v-2z",
3338
- fill: "#22242A"
3821
+ d: "M23 14.5a1 1 0 011-1h19a1 1 0 011 1v1a1 1 0 01-1 1H24a1 1 0 01-1-1v-1zM23 26.5a1 1 0 011-1h19a1 1 0 011 1v1a1 1 0 01-1 1H24a1 1 0 01-1-1v-1zM23 38.5a1 1 0 011-1h19a1 1 0 011 1v1a1 1 0 01-1 1H24a1 1 0 01-1-1v-1z",
3822
+ fill: "#161616"
3339
3823
  })));
3340
3824
 
3341
3825
  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); }
@@ -3536,226 +4020,13 @@ const iconsByType = {
3536
4020
  default: FormIcon
3537
4021
  };
3538
4022
 
3539
- const type$2 = 'text';
3540
- function Text(props) {
3541
- const {
3542
- field,
3543
- disabled
3544
- } = props;
3545
- const {
3546
- text = ''
3547
- } = field;
3548
- const textValue = useExpressionValue(text) || '';
3549
- return jsx("div", {
3550
- class: formFieldClasses(type$2),
3551
- children: renderText(field, textValue, disabled)
3552
- });
3553
- }
3554
- Text.create = function (options = {}) {
3555
- return {
3556
- text: '# Text',
3557
- ...options
3558
- };
3559
- };
3560
- Text.type = type$2;
3561
- Text.keyed = false;
3562
-
3563
- // helper //////////////
3564
-
3565
- function renderText(field, content, disabled) {
3566
- const {
3567
- text
3568
- } = field;
3569
- const Icon = iconsByType['text'];
3570
- if (disabled) {
3571
- if (!text) {
3572
- return jsxs("div", {
3573
- class: "fjs-form-field-placeholder",
3574
- children: [jsx(Icon, {
3575
- viewBox: "0 0 54 54"
3576
- }), "Text view is empty"]
3577
- });
3578
- }
3579
- if (isExpression$2(text)) {
3580
- return jsxs("div", {
3581
- class: "fjs-form-field-placeholder",
3582
- children: [jsx(Icon, {
3583
- viewBox: "0 0 54 54"
3584
- }), "Text view is populated by an expression"]
3585
- });
3586
- }
3587
- }
3588
- return jsx(Markup, {
3589
- markup: safeMarkdown(content),
3590
- trim: false
3591
- });
3592
- }
3593
-
3594
- const type$1 = 'textfield';
3595
- function Textfield(props) {
3596
- const {
3597
- disabled,
3598
- errors = [],
3599
- field,
3600
- value = ''
3601
- } = props;
3602
- const {
3603
- description,
3604
- id,
3605
- label,
3606
- appearance = {},
3607
- validate = {}
3608
- } = field;
3609
- const {
3610
- prefixAdorner,
3611
- suffixAdorner
3612
- } = appearance;
3613
- const {
3614
- required
3615
- } = validate;
3616
- const onChange = ({
3617
- target
3618
- }) => {
3619
- props.onChange({
3620
- field,
3621
- value: target.value
3622
- });
3623
- };
3624
- const {
3625
- formId
3626
- } = useContext(FormContext);
3627
- return jsxs("div", {
3628
- class: formFieldClasses(type$1, {
3629
- errors,
3630
- disabled
3631
- }),
3632
- children: [jsx(Label, {
3633
- id: prefixId(id, formId),
3634
- label: label,
3635
- required: required
3636
- }), jsx(InputAdorner, {
3637
- disabled: disabled,
3638
- pre: prefixAdorner,
3639
- post: suffixAdorner,
3640
- children: jsx("input", {
3641
- class: "fjs-input",
3642
- disabled: disabled,
3643
- id: prefixId(id, formId),
3644
- onInput: onChange,
3645
- type: "text",
3646
- value: value
3647
- })
3648
- }), jsx(Description, {
3649
- description: description
3650
- }), jsx(Errors, {
3651
- errors: errors
3652
- })]
3653
- });
3654
- }
3655
- Textfield.create = function (options = {}) {
3656
- return {
3657
- ...options
3658
- };
3659
- };
3660
- Textfield.type = type$1;
3661
- Textfield.label = 'Text field';
3662
- Textfield.keyed = true;
3663
- Textfield.emptyValue = '';
3664
- Textfield.sanitizeValue = ({
3665
- value
3666
- }) => isArray(value) || isObject(value) ? '' : String(value);
3667
-
3668
- const type = 'textarea';
3669
- function Textarea(props) {
3670
- const {
3671
- disabled,
3672
- errors = [],
3673
- field,
3674
- value = ''
3675
- } = props;
3676
- const {
3677
- description,
3678
- id,
3679
- label,
3680
- validate = {}
3681
- } = field;
3682
- const {
3683
- required
3684
- } = validate;
3685
- const textareaRef = useRef();
3686
- const onInput = ({
3687
- target
3688
- }) => {
3689
- props.onChange({
3690
- field,
3691
- value: target.value
3692
- });
3693
- };
3694
- const autoSizeTextarea = useCallback(textarea => {
3695
- // Ensures the textarea shrinks back, and improves resizing behavior consistency
3696
- textarea.style.height = '0px';
3697
- const computed = window.getComputedStyle(textarea);
3698
- const calculatedHeight = parseInt(computed.getPropertyValue('border-top-width')) + parseInt(computed.getPropertyValue('padding-top')) + textarea.scrollHeight + parseInt(computed.getPropertyValue('padding-bottom')) + parseInt(computed.getPropertyValue('border-bottom-width'));
3699
- const minHeight = 75;
3700
- const maxHeight = 350;
3701
- const displayHeight = Math.max(Math.min(calculatedHeight, maxHeight), minHeight);
3702
- textarea.style.height = `${displayHeight}px`;
3703
-
3704
- // Overflow is hidden by default to hide scrollbar flickering
3705
- textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
3706
- }, []);
3707
- useEffect(() => {
3708
- autoSizeTextarea(textareaRef.current);
3709
- }, [autoSizeTextarea, value]);
3710
- const {
3711
- formId
3712
- } = useContext(FormContext);
3713
- return jsxs("div", {
3714
- class: formFieldClasses(type, {
3715
- errors,
3716
- disabled
3717
- }),
3718
- children: [jsx(Label, {
3719
- id: prefixId(id, formId),
3720
- label: label,
3721
- required: required
3722
- }), jsx("textarea", {
3723
- class: "fjs-textarea",
3724
- disabled: disabled,
3725
- id: prefixId(id, formId),
3726
- onInput: onInput,
3727
- value: value,
3728
- ref: textareaRef
3729
- }), jsx(Description, {
3730
- description: description
3731
- }), jsx(Errors, {
3732
- errors: errors
3733
- })]
3734
- });
3735
- }
3736
- Textarea.create = function (options = {}) {
3737
- return {
3738
- ...options
3739
- };
3740
- };
3741
- Textarea.type = type;
3742
- Textarea.label = 'Text area';
3743
- Textarea.keyed = true;
3744
- Textarea.emptyValue = '';
3745
- Textarea.sanitizeValue = ({
3746
- value
3747
- }) => isArray(value) || isObject(value) ? '' : String(value);
3748
-
3749
4023
  const formFields = [Button, Checkbox, Checklist, Default, Image, Numberfield, Datetime, Radio, Select, Taglist, Text, Textfield, Textarea];
3750
4024
 
3751
4025
  class FormFields {
3752
4026
  constructor() {
3753
4027
  this._formFields = {};
3754
4028
  formFields.forEach(formField => {
3755
- const {
3756
- type
3757
- } = formField;
3758
- this.register(type, formField);
4029
+ this.register(formField.type, formField);
3759
4030
  });
3760
4031
  }
3761
4032
  register(type, formField) {
@@ -3797,7 +4068,7 @@ function Renderer(config, eventBus, form, injector) {
3797
4068
  if (!schema) {
3798
4069
  return null;
3799
4070
  }
3800
- return jsx(FormContext.Provider, {
4071
+ return jsx(FormContext$1.Provider, {
3801
4072
  value: formContext,
3802
4073
  children: jsx(FormComponent, {
3803
4074
  onChange: onChange,
@@ -4198,7 +4469,7 @@ class Form {
4198
4469
  }
4199
4470
  }
4200
4471
 
4201
- const schemaVersion = 6;
4472
+ const schemaVersion = 7;
4202
4473
 
4203
4474
  /**
4204
4475
  * @typedef { import('./types').CreateFormOptions } CreateFormOptions
@@ -4223,5 +4494,5 @@ function createForm(options) {
4223
4494
  });
4224
4495
  }
4225
4496
 
4226
- export { Button, Checkbox, Checklist, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, Default, Form, FormComponent, FormContext, FormFieldRegistry, FormFields, FormRenderContext, Image, MINUTES_IN_DAY, Numberfield, Radio, Select, 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, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, getSchemaVariables, getValuesSource, iconsByType, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
4497
+ export { Button, Checkbox, Checklist, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, Default, Form, FormComponent, FormContext$1 as FormContext, FormFieldRegistry, FormFields, FormRenderContext$1 as FormRenderContext, Image, MINUTES_IN_DAY, Numberfield, Radio, Select, 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, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, getExpressionVariableNames, getSchemaVariables, getValuesSource, getVariableNames, iconsByType, isExpression$1 as isExpression, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
4227
4498
  //# sourceMappingURL=index.es.js.map