@bpmn-io/form-js-editor 1.0.0-alpha.8 → 1.0.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
@@ -6,7 +6,8 @@ import classnames from 'classnames';
6
6
  import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
7
7
  import { useContext, useState, useMemo, useEffect, useCallback, useRef as useRef$1, useLayoutEffect } from 'preact/hooks';
8
8
  import { createContext, Fragment, render, createElement } from 'preact';
9
- import React, { createPortal, useRef, useContext as useContext$1, useEffect as useEffect$1, forwardRef } from 'preact/compat';
9
+ import * as React from 'preact/compat';
10
+ import { createPortal, useRef, useContext as useContext$1, useEffect as useEffect$1, forwardRef } from 'preact/compat';
10
11
  import dragula from 'dragula';
11
12
  import { classes, query, closest, event, matches, domify } from 'min-dom';
12
13
  import { mutate } from 'array-move';
@@ -933,83 +934,91 @@ function useService$1 (type, strict) {
933
934
  return getService(type, strict);
934
935
  }
935
936
 
937
+ var _path$3;
936
938
  function _extends$3() { _extends$3 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$3.apply(this, arguments); }
937
- var CloseIcon = (({
938
- styles = {},
939
- ...props
940
- }) => /*#__PURE__*/React.createElement("svg", _extends$3({
941
- width: "16",
942
- height: "16",
943
- fill: "currentColor",
944
- xmlns: "http://www.w3.org/2000/svg"
945
- }, props), /*#__PURE__*/React.createElement("path", {
946
- fillRule: "evenodd",
947
- clipRule: "evenodd",
948
- 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"
949
- })));
939
+ var SvgClose = function SvgClose(props) {
940
+ return /*#__PURE__*/React.createElement("svg", _extends$3({
941
+ xmlns: "http://www.w3.org/2000/svg",
942
+ width: 16,
943
+ height: 16,
944
+ fill: "currentColor"
945
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
946
+ fillRule: "evenodd",
947
+ d: "m12 4.7-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7Z",
948
+ clipRule: "evenodd"
949
+ })));
950
+ };
951
+ var CloseIcon = SvgClose;
950
952
 
953
+ var _path$2, _path2;
951
954
  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); }
952
- var DeleteIcon$1 = (({
953
- styles = {},
954
- ...props
955
- }) => /*#__PURE__*/React.createElement("svg", _extends$2({
956
- xmlns: "http://www.w3.org/2000/svg",
957
- width: "16",
958
- height: "16",
959
- fill: "none"
960
- }, props), /*#__PURE__*/React.createElement("rect", {
961
- width: "16",
962
- height: "16",
963
- x: ".536",
964
- fill: "#fff",
965
- rx: "3",
966
- style: {
967
- mixBlendMode: "multiply"
968
- }
969
- }), /*#__PURE__*/React.createElement("path", {
970
- fill: "#fff",
971
- d: "M.536 0h16v16h-16z",
972
- style: {
973
- mixBlendMode: "multiply"
974
- }
975
- }), /*#__PURE__*/React.createElement("path", {
976
- fill: "currentcolor",
977
- d: "M7.536 6h-1v6h1V6zm3 0h-1v6h1V6z"
978
- }), /*#__PURE__*/React.createElement("path", {
979
- fill: "currentcolor",
980
- d: "M2.536 3v1h1v10a1 1 0 001 1h8a1 1 0 001-1V4h1V3h-12zm2 11V4h8v10h-8zm6-13h-4v1h4V1z"
981
- })));
955
+ var SvgDelete = function SvgDelete(props) {
956
+ return /*#__PURE__*/React.createElement("svg", _extends$2({
957
+ xmlns: "http://www.w3.org/2000/svg",
958
+ width: 16,
959
+ height: 16,
960
+ fill: "none"
961
+ }, props), /*#__PURE__*/React.createElement("rect", {
962
+ width: 16,
963
+ height: 16,
964
+ x: 0.536,
965
+ fill: "#fff",
966
+ rx: 3,
967
+ style: {
968
+ mixBlendMode: "multiply"
969
+ }
970
+ }), /*#__PURE__*/React.createElement("path", {
971
+ fill: "#fff",
972
+ d: "M0 0h16v16H0z",
973
+ style: {
974
+ mixBlendMode: "multiply"
975
+ },
976
+ transform: "translate(.536)"
977
+ }), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
978
+ fill: "currentcolor",
979
+ d: "M7.536 6h-1v6h1V6Zm3 0h-1v6h1V6Z"
980
+ })), _path2 || (_path2 = /*#__PURE__*/React.createElement("path", {
981
+ fill: "currentcolor",
982
+ d: "M2.536 3v1h1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4h1V3h-12Zm2 11V4h8v10h-8Zm6-13h-4v1h4V1Z"
983
+ })));
984
+ };
985
+ var DeleteIcon$1 = SvgDelete;
982
986
 
987
+ var _path$1;
983
988
  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); }
984
- var DraggableIcon = (({
985
- styles = {},
986
- ...props
987
- }) => /*#__PURE__*/React.createElement("svg", _extends$1({
988
- xmlns: "http://www.w3.org/2000/svg",
989
- width: "16",
990
- height: "16",
991
- fill: "currentcolor",
992
- viewBox: "0 0 32 32"
993
- }, props), /*#__PURE__*/React.createElement("path", {
994
- d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
995
- }), /*#__PURE__*/React.createElement("path", {
996
- d: "M0 0h32v32H0z",
997
- fill: "none"
998
- })));
989
+ var SvgDraggable = function SvgDraggable(props) {
990
+ return /*#__PURE__*/React.createElement("svg", _extends$1({
991
+ xmlns: "http://www.w3.org/2000/svg",
992
+ xmlSpace: "preserve",
993
+ width: 16,
994
+ height: 16,
995
+ fill: "currentcolor",
996
+ viewBox: "0 0 32 32"
997
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
998
+ d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
999
+ })), /*#__PURE__*/React.createElement("path", {
1000
+ d: "M0 0h32v32H0z",
1001
+ style: {
1002
+ fill: "none"
1003
+ }
1004
+ }));
1005
+ };
1006
+ var DraggableIcon = SvgDraggable;
999
1007
 
1008
+ var _path;
1000
1009
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
1001
- var SearchIcon = (({
1002
- styles = {},
1003
- ...props
1004
- }) => /*#__PURE__*/React.createElement("svg", _extends({
1005
- width: "15",
1006
- height: "15",
1007
- fill: "none",
1008
- xmlns: "http://www.w3.org/2000/svg"
1009
- }, props), /*#__PURE__*/React.createElement("path", {
1010
- 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",
1011
- fill: "currentColor"
1012
- })));
1010
+ var SvgSearch = function SvgSearch(props) {
1011
+ return /*#__PURE__*/React.createElement("svg", _extends({
1012
+ xmlns: "http://www.w3.org/2000/svg",
1013
+ width: 15,
1014
+ height: 15,
1015
+ fill: "none"
1016
+ }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
1017
+ fill: "currentColor",
1018
+ d: "m14.5 13.793-3.776-3.776a5.508 5.508 0 1 0-.707.707l3.776 3.776.707-.707ZM2 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0Z"
1019
+ })));
1020
+ };
1021
+ var SearchIcon = SvgSearch;
1013
1022
 
1014
1023
  function EditorText(props) {
1015
1024
  const {
@@ -5982,6 +5991,165 @@ function isEdited$7(node) {
5982
5991
  function prefixId$6(id) {
5983
5992
  return `bio-properties-panel-${id}`;
5984
5993
  }
5994
+ function NumberField(props) {
5995
+ const {
5996
+ debounce,
5997
+ disabled,
5998
+ displayLabel = true,
5999
+ id,
6000
+ inputRef,
6001
+ label,
6002
+ max,
6003
+ min,
6004
+ onInput,
6005
+ step,
6006
+ value = '',
6007
+ onFocus,
6008
+ onBlur
6009
+ } = props;
6010
+ const [localValue, setLocalValue] = useState(value);
6011
+ const handleInputCallback = useMemo(() => {
6012
+ return debounce(event => {
6013
+ const {
6014
+ validity,
6015
+ value
6016
+ } = event.target;
6017
+ if (validity.valid) {
6018
+ onInput(value ? parseFloat(value) : undefined);
6019
+ }
6020
+ });
6021
+ }, [onInput, debounce]);
6022
+ const handleInput = e => {
6023
+ handleInputCallback(e);
6024
+ setLocalValue(e.target.value);
6025
+ };
6026
+ useEffect(() => {
6027
+ if (value === localValue) {
6028
+ return;
6029
+ }
6030
+ setLocalValue(value);
6031
+ }, [value]);
6032
+ return jsxs("div", {
6033
+ class: "bio-properties-panel-numberfield",
6034
+ children: [displayLabel && jsx("label", {
6035
+ for: prefixId$5(id),
6036
+ class: "bio-properties-panel-label",
6037
+ children: label
6038
+ }), jsx("input", {
6039
+ id: prefixId$5(id),
6040
+ ref: inputRef,
6041
+ type: "number",
6042
+ name: id,
6043
+ spellCheck: "false",
6044
+ autoComplete: "off",
6045
+ disabled: disabled,
6046
+ class: "bio-properties-panel-input",
6047
+ max: max,
6048
+ min: min,
6049
+ onInput: handleInput,
6050
+ onFocus: onFocus,
6051
+ onBlur: onBlur,
6052
+ step: step,
6053
+ value: localValue
6054
+ })]
6055
+ });
6056
+ }
6057
+
6058
+ /**
6059
+ * @param {Object} props
6060
+ * @param {Boolean} props.debounce
6061
+ * @param {String} props.description
6062
+ * @param {Boolean} props.disabled
6063
+ * @param {Object} props.element
6064
+ * @param {Function} props.getValue
6065
+ * @param {String} props.id
6066
+ * @param {String} props.label
6067
+ * @param {String} props.max
6068
+ * @param {String} props.min
6069
+ * @param {Function} props.setValue
6070
+ * @param {Function} props.onFocus
6071
+ * @param {Function} props.onBlur
6072
+ * @param {String} props.step
6073
+ * @param {Function} props.validate
6074
+ */
6075
+ function NumberFieldEntry(props) {
6076
+ const {
6077
+ debounce,
6078
+ description,
6079
+ disabled,
6080
+ element,
6081
+ getValue,
6082
+ id,
6083
+ label,
6084
+ max,
6085
+ min,
6086
+ setValue,
6087
+ step,
6088
+ onFocus,
6089
+ onBlur,
6090
+ validate
6091
+ } = props;
6092
+ const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
6093
+ const globalError = useError(id);
6094
+ const [localError, setLocalError] = useState(null);
6095
+ let value = getValue(element);
6096
+ const previousValue = usePrevious(value);
6097
+ useEffect(() => {
6098
+ if (isFunction(validate)) {
6099
+ const newValidationError = validate(value) || null;
6100
+ setLocalError(newValidationError);
6101
+ }
6102
+ }, [value]);
6103
+ const onInput = newValue => {
6104
+ let newValidationError = null;
6105
+ if (isFunction(validate)) {
6106
+ newValidationError = validate(newValue) || null;
6107
+ }
6108
+ if (newValidationError) {
6109
+ setCachedInvalidValue(newValue);
6110
+ } else {
6111
+ setValue(newValue);
6112
+ }
6113
+ setLocalError(newValidationError);
6114
+ };
6115
+ if (previousValue === value && localError) {
6116
+ value = cachedInvalidValue;
6117
+ }
6118
+ const error = globalError || localError;
6119
+ return jsxs("div", {
6120
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6121
+ "data-entry-id": id,
6122
+ children: [jsx(NumberField, {
6123
+ debounce: debounce,
6124
+ disabled: disabled,
6125
+ id: id,
6126
+ label: label,
6127
+ onFocus: onFocus,
6128
+ onBlur: onBlur,
6129
+ onInput: onInput,
6130
+ max: max,
6131
+ min: min,
6132
+ step: step,
6133
+ value: value
6134
+ }, element), error && jsx("div", {
6135
+ class: "bio-properties-panel-error",
6136
+ children: error
6137
+ }), jsx(Description$1, {
6138
+ forId: id,
6139
+ element: element,
6140
+ value: description
6141
+ })]
6142
+ });
6143
+ }
6144
+ function isEdited$6(node) {
6145
+ return node && !!node.value;
6146
+ }
6147
+
6148
+ // helpers /////////////////
6149
+
6150
+ function prefixId$5(id) {
6151
+ return `bio-properties-panel-${id}`;
6152
+ }
5985
6153
  const noop$1 = () => {};
5986
6154
  function FeelTextfield(props) {
5987
6155
  const {
@@ -6108,7 +6276,7 @@ function FeelTextfield(props) {
6108
6276
  'feel-active': feelActive
6109
6277
  }),
6110
6278
  children: [jsxs("label", {
6111
- for: prefixId$5(id),
6279
+ for: prefixId$4(id),
6112
6280
  class: "bio-properties-panel-label",
6113
6281
  onClick: () => setFocus(),
6114
6282
  children: [label, jsx(FeelIcon, {
@@ -6125,7 +6293,7 @@ function FeelTextfield(props) {
6125
6293
  disabled: feel !== 'optional' || disabled,
6126
6294
  onClick: handleFeelToggle
6127
6295
  }), feelActive ? jsx(CodeEditor, {
6128
- id: prefixId$5(id),
6296
+ id: prefixId$4(id),
6129
6297
  name: id,
6130
6298
  onInput: handleLocalInput,
6131
6299
  disabled: disabled,
@@ -6142,7 +6310,7 @@ function FeelTextfield(props) {
6142
6310
  ...props,
6143
6311
  onInput: handleLocalInput,
6144
6312
  contentAttributes: {
6145
- 'id': prefixId$5(id),
6313
+ 'id': prefixId$4(id),
6146
6314
  'aria-label': label
6147
6315
  },
6148
6316
  value: localValue,
@@ -6180,7 +6348,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
6180
6348
  }
6181
6349
  };
6182
6350
  return jsx("input", {
6183
- id: prefixId$5(id),
6351
+ id: prefixId$4(id),
6184
6352
  type: "text",
6185
6353
  ref: inputRef,
6186
6354
  name: id,
@@ -6194,6 +6362,53 @@ const OptionalFeelInput = forwardRef((props, ref) => {
6194
6362
  value: value || ''
6195
6363
  });
6196
6364
  });
6365
+ const OptionalFeelNumberField = forwardRef((props, ref) => {
6366
+ const {
6367
+ id,
6368
+ debounce,
6369
+ disabled,
6370
+ onInput,
6371
+ value,
6372
+ min,
6373
+ max,
6374
+ step,
6375
+ onFocus,
6376
+ onBlur
6377
+ } = props;
6378
+ const inputRef = useRef$1();
6379
+
6380
+ // To be consistent with the FEEL editor, set focus at start of input
6381
+ // this ensures clean editing experience when switching with the keyboard
6382
+ ref.current = {
6383
+ focus: position => {
6384
+ const input = inputRef.current;
6385
+ if (!input) {
6386
+ return;
6387
+ }
6388
+ input.focus();
6389
+ if (typeof position === 'number' && position !== Infinity) {
6390
+ if (position > value.length) {
6391
+ position = value.length;
6392
+ }
6393
+ input.setSelectionRange(position, position);
6394
+ }
6395
+ }
6396
+ };
6397
+ return jsx(NumberField, {
6398
+ id: id,
6399
+ debounce: debounce,
6400
+ disabled: disabled,
6401
+ displayLabel: false,
6402
+ inputRef: inputRef,
6403
+ max: max,
6404
+ min: min,
6405
+ onInput: onInput,
6406
+ step: step,
6407
+ value: value,
6408
+ onFocus: onFocus,
6409
+ onBlur: onBlur
6410
+ });
6411
+ });
6197
6412
  forwardRef((props, ref) => {
6198
6413
  const {
6199
6414
  id,
@@ -6218,7 +6433,7 @@ forwardRef((props, ref) => {
6218
6433
  }
6219
6434
  };
6220
6435
  return jsx("textarea", {
6221
- id: prefixId$5(id),
6436
+ id: prefixId$4(id),
6222
6437
  type: "text",
6223
6438
  ref: inputRef,
6224
6439
  name: id,
@@ -6294,7 +6509,7 @@ forwardRef((props, ref) => {
6294
6509
  };
6295
6510
  return jsx("input", {
6296
6511
  ref: inputRef,
6297
- id: prefixId$5(id),
6512
+ id: prefixId$4(id),
6298
6513
  name: id,
6299
6514
  onFocus: onFocus,
6300
6515
  onBlur: onBlur,
@@ -6383,11 +6598,13 @@ function FeelEntry(props) {
6383
6598
  return jsxs("div", {
6384
6599
  class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6385
6600
  "data-entry-id": id,
6386
- children: [jsx(FeelTextfield, {
6601
+ children: [createElement(FeelTextfield, {
6602
+ ...props,
6387
6603
  debounce: debounce,
6388
6604
  disabled: disabled,
6389
6605
  feel: feel,
6390
6606
  id: id,
6607
+ key: element,
6391
6608
  label: label,
6392
6609
  onInput: onInput,
6393
6610
  onError: onError,
@@ -6401,7 +6618,7 @@ function FeelEntry(props) {
6401
6618
  variables: variables,
6402
6619
  tooltipContainer: tooltipContainer,
6403
6620
  OptionalComponent: props.OptionalComponent
6404
- }, element), error && jsx("div", {
6621
+ }), error && jsx("div", {
6405
6622
  class: "bio-properties-panel-error",
6406
6623
  children: error
6407
6624
  }), jsx(Description$1, {
@@ -6412,6 +6629,36 @@ function FeelEntry(props) {
6412
6629
  });
6413
6630
  }
6414
6631
 
6632
+ /**
6633
+ * @param {Object} props
6634
+ * @param {Object} props.element
6635
+ * @param {String} props.id
6636
+ * @param {String} props.description
6637
+ * @param {Boolean} props.debounce
6638
+ * @param {Boolean} props.disabled
6639
+ * @param {String} props.max
6640
+ * @param {String} props.min
6641
+ * @param {String} props.step
6642
+ * @param {Boolean} props.feel
6643
+ * @param {String} props.label
6644
+ * @param {Function} props.getValue
6645
+ * @param {Function} props.setValue
6646
+ * @param {Function} props.tooltipContainer
6647
+ * @param {Function} props.validate
6648
+ * @param {Function} props.show
6649
+ * @param {Function} props.example
6650
+ * @param {Function} props.variables
6651
+ * @param {Function} props.onFocus
6652
+ * @param {Function} props.onBlur
6653
+ */
6654
+ function FeelNumberEntry(props) {
6655
+ return jsx(FeelEntry, {
6656
+ class: "bio-properties-panel-feel-number",
6657
+ OptionalComponent: OptionalFeelNumberField,
6658
+ ...props
6659
+ });
6660
+ }
6661
+
6415
6662
  /**
6416
6663
  * @param {Object} props
6417
6664
  * @param {Object} props.element
@@ -6467,7 +6714,7 @@ function FeelTemplatingEntry(props) {
6467
6714
  ...props
6468
6715
  });
6469
6716
  }
6470
- function isEdited$6(node) {
6717
+ function isEdited$5(node) {
6471
6718
  if (!node) {
6472
6719
  return false;
6473
6720
  }
@@ -6479,166 +6726,10 @@ function isEdited$6(node) {
6479
6726
 
6480
6727
  // helpers /////////////////
6481
6728
 
6482
- function prefixId$5(id) {
6729
+ function prefixId$4(id) {
6483
6730
  return `bio-properties-panel-${id}`;
6484
6731
  }
6485
- function NumberField(props) {
6486
- const {
6487
- debounce,
6488
- disabled,
6489
- id,
6490
- label,
6491
- max,
6492
- min,
6493
- onInput,
6494
- step,
6495
- value = '',
6496
- onFocus,
6497
- onBlur
6498
- } = props;
6499
- const [localValue, setLocalValue] = useState(value);
6500
- const handleInputCallback = useMemo(() => {
6501
- return debounce(event => {
6502
- const {
6503
- validity,
6504
- value
6505
- } = event.target;
6506
- if (validity.valid) {
6507
- onInput(value ? parseFloat(value) : undefined);
6508
- }
6509
- });
6510
- }, [onInput, debounce]);
6511
- const handleInput = e => {
6512
- handleInputCallback(e);
6513
- setLocalValue(e.target.value);
6514
- };
6515
- useEffect(() => {
6516
- if (value === localValue) {
6517
- return;
6518
- }
6519
- setLocalValue(value);
6520
- }, [value]);
6521
- return jsxs("div", {
6522
- class: "bio-properties-panel-numberfield",
6523
- children: [jsx("label", {
6524
- for: prefixId$4(id),
6525
- class: "bio-properties-panel-label",
6526
- children: label
6527
- }), jsx("input", {
6528
- id: prefixId$4(id),
6529
- type: "number",
6530
- name: id,
6531
- spellCheck: "false",
6532
- autoComplete: "off",
6533
- disabled: disabled,
6534
- class: "bio-properties-panel-input",
6535
- max: max,
6536
- min: min,
6537
- onInput: handleInput,
6538
- onFocus: onFocus,
6539
- onBlur: onBlur,
6540
- step: step,
6541
- value: localValue
6542
- })]
6543
- });
6544
- }
6545
-
6546
- /**
6547
- * @param {Object} props
6548
- * @param {Boolean} props.debounce
6549
- * @param {String} props.description
6550
- * @param {Boolean} props.disabled
6551
- * @param {Object} props.element
6552
- * @param {Function} props.getValue
6553
- * @param {String} props.id
6554
- * @param {String} props.label
6555
- * @param {String} props.max
6556
- * @param {String} props.min
6557
- * @param {Function} props.setValue
6558
- * @param {Function} props.onFocus
6559
- * @param {Function} props.onBlur
6560
- * @param {String} props.step
6561
- * @param {Function} props.validate
6562
- */
6563
- function NumberFieldEntry(props) {
6564
- const {
6565
- debounce,
6566
- description,
6567
- disabled,
6568
- element,
6569
- getValue,
6570
- id,
6571
- label,
6572
- max,
6573
- min,
6574
- setValue,
6575
- step,
6576
- onFocus,
6577
- onBlur,
6578
- validate
6579
- } = props;
6580
- const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
6581
- const globalError = useError(id);
6582
- const [localError, setLocalError] = useState(null);
6583
- let value = getValue(element);
6584
- const previousValue = usePrevious(value);
6585
- useEffect(() => {
6586
- if (isFunction(validate)) {
6587
- const newValidationError = validate(value) || null;
6588
- setLocalError(newValidationError);
6589
- }
6590
- }, [value]);
6591
- const onInput = newValue => {
6592
- let newValidationError = null;
6593
- if (isFunction(validate)) {
6594
- newValidationError = validate(newValue) || null;
6595
- }
6596
- if (newValidationError) {
6597
- setCachedInvalidValue(newValue);
6598
- } else {
6599
- setValue(newValue);
6600
- }
6601
- setLocalError(newValidationError);
6602
- };
6603
- if (previousValue === value && localError) {
6604
- value = cachedInvalidValue;
6605
- }
6606
- const error = globalError || localError;
6607
- return jsxs("div", {
6608
- class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6609
- "data-entry-id": id,
6610
- children: [jsx(NumberField, {
6611
- debounce: debounce,
6612
- disabled: disabled,
6613
- id: id,
6614
- label: label,
6615
- onFocus: onFocus,
6616
- onBlur: onBlur,
6617
- onInput: onInput,
6618
- max: max,
6619
- min: min,
6620
- step: step,
6621
- value: value
6622
- }, element), error && jsx("div", {
6623
- class: "bio-properties-panel-error",
6624
- children: error
6625
- }), jsx(Description$1, {
6626
- forId: id,
6627
- element: element,
6628
- value: description
6629
- })]
6630
- });
6631
- }
6632
- function isEdited$4(node) {
6633
- return node && !!node.value;
6634
- }
6635
-
6636
- // helpers /////////////////
6637
-
6638
- function prefixId$4(id) {
6639
- return `bio-properties-panel-${id}`;
6640
- }
6641
- function Select(props) {
6732
+ function Select(props) {
6642
6733
  const {
6643
6734
  id,
6644
6735
  label,
@@ -7279,7 +7370,7 @@ function AltTextEntry(props) {
7279
7370
  component: AltText,
7280
7371
  editField: editField,
7281
7372
  field: field,
7282
- isEdited: isEdited$6
7373
+ isEdited: isEdited$5
7283
7374
  });
7284
7375
  }
7285
7376
  return entries;
@@ -7301,7 +7392,7 @@ function AltText(props) {
7301
7392
  const setValue = value => {
7302
7393
  return editField(field, path, value);
7303
7394
  };
7304
- return FeelEntry({
7395
+ return FeelTemplatingEntry({
7305
7396
  debounce,
7306
7397
  element: field,
7307
7398
  feel: 'optional',
@@ -7309,6 +7400,7 @@ function AltText(props) {
7309
7400
  id,
7310
7401
  label: 'Alternative text',
7311
7402
  setValue,
7403
+ singleLine: true,
7312
7404
  variables
7313
7405
  });
7314
7406
  }
@@ -7397,7 +7489,7 @@ function DescriptionEntry(props) {
7397
7489
  component: Description,
7398
7490
  editField: editField,
7399
7491
  field: field,
7400
- isEdited: isEdited$6
7492
+ isEdited: isEdited$5
7401
7493
  });
7402
7494
  }
7403
7495
  return entries;
@@ -7863,7 +7955,7 @@ function LabelEntry(props) {
7863
7955
  component: DateLabel,
7864
7956
  editField,
7865
7957
  field,
7866
- isEdited: isEdited$6
7958
+ isEdited: isEdited$5
7867
7959
  });
7868
7960
  }
7869
7961
  if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
@@ -7872,7 +7964,7 @@ function LabelEntry(props) {
7872
7964
  component: TimeLabel,
7873
7965
  editField,
7874
7966
  field,
7875
- isEdited: isEdited$6
7967
+ isEdited: isEdited$5
7876
7968
  });
7877
7969
  }
7878
7970
  } else if (INPUTS.includes(type) || type === 'button') {
@@ -7881,7 +7973,7 @@ function LabelEntry(props) {
7881
7973
  component: Label$1,
7882
7974
  editField,
7883
7975
  field,
7884
- isEdited: isEdited$6
7976
+ isEdited: isEdited$5
7885
7977
  });
7886
7978
  }
7887
7979
  return entries;
@@ -7986,7 +8078,7 @@ function SourceEntry(props) {
7986
8078
  component: Source,
7987
8079
  editField: editField,
7988
8080
  field: field,
7989
- isEdited: isEdited$6
8081
+ isEdited: isEdited$5
7990
8082
  });
7991
8083
  }
7992
8084
  return entries;
@@ -8008,7 +8100,7 @@ function Source(props) {
8008
8100
  const setValue = value => {
8009
8101
  return editField(field, path, value);
8010
8102
  };
8011
- return FeelEntry({
8103
+ return FeelTemplatingEntry({
8012
8104
  debounce,
8013
8105
  description: 'Expression or static value (link/data URI)',
8014
8106
  element: field,
@@ -8017,6 +8109,7 @@ function Source(props) {
8017
8109
  id,
8018
8110
  label: 'Image source',
8019
8111
  setValue,
8112
+ singleLine: true,
8020
8113
  variables
8021
8114
  });
8022
8115
  }
@@ -8041,7 +8134,7 @@ function TextEntry(props) {
8041
8134
  component: Text,
8042
8135
  editField: editField,
8043
8136
  field: field,
8044
- isEdited: isEdited$6
8137
+ isEdited: isEdited$5
8045
8138
  }];
8046
8139
 
8047
8140
  // todo: skipped to make the release without too much risk
@@ -8110,7 +8203,7 @@ function NumberEntries(props) {
8110
8203
  entries.push({
8111
8204
  id: id + '-decimalDigits',
8112
8205
  component: NumberDecimalDigits,
8113
- isEdited: isEdited$4,
8206
+ isEdited: isEdited$6,
8114
8207
  editField,
8115
8208
  field
8116
8209
  });
@@ -8741,6 +8834,34 @@ function updateKey(properties, oldKey, newKey) {
8741
8834
  }, {});
8742
8835
  }
8743
8836
 
8837
+ function AutoFocusSelectEntry(props) {
8838
+ const {
8839
+ autoFocusEntry,
8840
+ element,
8841
+ getValue
8842
+ } = props;
8843
+ const value = getValue(element);
8844
+ const prevValue = usePrevious(value);
8845
+ const eventBus = useService('eventBus');
8846
+
8847
+ // auto focus specifc other entry when selected value changed
8848
+ useEffect(() => {
8849
+ if (autoFocusEntry && prevValue && value !== prevValue) {
8850
+ // @Note(pinussilvestrus): There is an issue in the properties
8851
+ // panel so we have to wait a bit before showing the entry.
8852
+ // Cf. https://github.com/camunda/linting/blob/4f5328e2722f73ae60ae584c5f576eaec3999cb2/lib/modeler/Linting.js#L37
8853
+ setTimeout(() => {
8854
+ eventBus.fire('propertiesPanel.showEntry', {
8855
+ id: autoFocusEntry
8856
+ });
8857
+ });
8858
+ }
8859
+ }, [value, autoFocusEntry, prevValue, eventBus]);
8860
+ return jsx(SelectEntry, {
8861
+ ...props
8862
+ });
8863
+ }
8864
+
8744
8865
  function ValuesSourceSelectEntry(props) {
8745
8866
  const {
8746
8867
  editField,
@@ -8779,7 +8900,8 @@ function ValuesSourceSelect(props) {
8779
8900
  value: valueSource
8780
8901
  }));
8781
8902
  };
8782
- return SelectEntry({
8903
+ return AutoFocusSelectEntry({
8904
+ autoFocusEntry: getAutoFocusEntryId(field),
8783
8905
  label: 'Type',
8784
8906
  element: field,
8785
8907
  getOptions: getValuesSourceOptions,
@@ -8789,6 +8911,20 @@ function ValuesSourceSelect(props) {
8789
8911
  });
8790
8912
  }
8791
8913
 
8914
+ // helpers //////////
8915
+
8916
+ function getAutoFocusEntryId(field) {
8917
+ const valuesSource = getValuesSource(field);
8918
+ if (valuesSource === VALUES_SOURCES.EXPRESSION) {
8919
+ return `${field.id}-valuesExpression-expression`;
8920
+ } else if (valuesSource === VALUES_SOURCES.INPUT) {
8921
+ return `${field.id}-dynamicValues-key`;
8922
+ } else if (valuesSource === VALUES_SOURCES.STATIC) {
8923
+ return `${field.id}-staticValues-0-label`;
8924
+ }
8925
+ return null;
8926
+ }
8927
+
8792
8928
  function InputKeyValuesSourceEntry(props) {
8793
8929
  const {
8794
8930
  editField,
@@ -8945,7 +9081,7 @@ function AdornerEntry(props) {
8945
9081
  entries.push({
8946
9082
  id: 'prefix-adorner',
8947
9083
  component: PrefixAdorner,
8948
- isEdited: isEdited,
9084
+ isEdited: isEdited$5,
8949
9085
  editField,
8950
9086
  field,
8951
9087
  onChange,
@@ -8954,7 +9090,7 @@ function AdornerEntry(props) {
8954
9090
  entries.push({
8955
9091
  id: 'suffix-adorner',
8956
9092
  component: SuffixAdorner,
8957
- isEdited: isEdited,
9093
+ isEdited: isEdited$5,
8958
9094
  editField,
8959
9095
  field,
8960
9096
  onChange,
@@ -8971,13 +9107,19 @@ function PrefixAdorner(props) {
8971
9107
  getValue
8972
9108
  } = props;
8973
9109
  const debounce = useService('debounce');
8974
- return TextfieldEntry({
9110
+ const variables = useVariables().map(name => ({
9111
+ name
9112
+ }));
9113
+ return FeelTemplatingEntry({
8975
9114
  debounce,
8976
9115
  element: field,
9116
+ feel: 'optional',
8977
9117
  getValue: getValue('prefixAdorner'),
8978
9118
  id,
8979
9119
  label: 'Prefix',
8980
- setValue: onChange('prefixAdorner')
9120
+ setValue: onChange('prefixAdorner'),
9121
+ singleLine: true,
9122
+ variables
8981
9123
  });
8982
9124
  }
8983
9125
  function SuffixAdorner(props) {
@@ -8988,13 +9130,18 @@ function SuffixAdorner(props) {
8988
9130
  getValue
8989
9131
  } = props;
8990
9132
  const debounce = useService('debounce');
8991
- return TextfieldEntry({
9133
+ const variables = useVariables().map(name => ({
9134
+ name
9135
+ }));
9136
+ return FeelTemplatingEntry({
8992
9137
  debounce,
8993
9138
  element: field,
8994
9139
  getValue: getValue('suffixAdorner'),
8995
9140
  id,
8996
9141
  label: 'Suffix',
8997
- setValue: onChange('suffixAdorner')
9142
+ setValue: onChange('suffixAdorner'),
9143
+ singleLine: true,
9144
+ variables
8998
9145
  });
8999
9146
  }
9000
9147
 
@@ -9013,7 +9160,7 @@ function ReadonlyEntry(props) {
9013
9160
  component: Readonly,
9014
9161
  editField: editField,
9015
9162
  field: field,
9016
- isEdited: isEdited$6
9163
+ isEdited: isEdited$5
9017
9164
  });
9018
9165
  }
9019
9166
  return entries;
@@ -9057,7 +9204,7 @@ function ConditionEntry(props) {
9057
9204
  component: Condition,
9058
9205
  editField: editField,
9059
9206
  field: field,
9060
- isEdited: isEdited$6
9207
+ isEdited: isEdited$5
9061
9208
  }];
9062
9209
  }
9063
9210
  function Condition(props) {
@@ -9095,6 +9242,55 @@ function Condition(props) {
9095
9242
  });
9096
9243
  }
9097
9244
 
9245
+ function ValuesExpressionEntry(props) {
9246
+ const {
9247
+ editField,
9248
+ field,
9249
+ id
9250
+ } = props;
9251
+ return [{
9252
+ id: id + '-expression',
9253
+ component: ValuesExpression,
9254
+ label: 'Values expression',
9255
+ isEdited: isEdited$5,
9256
+ editField,
9257
+ field
9258
+ }];
9259
+ }
9260
+ function ValuesExpression(props) {
9261
+ const {
9262
+ editField,
9263
+ field,
9264
+ id
9265
+ } = props;
9266
+ const debounce = useService('debounce');
9267
+ const variables = useVariables().map(name => ({
9268
+ name
9269
+ }));
9270
+ const path = VALUES_SOURCES_PATHS[VALUES_SOURCES.EXPRESSION];
9271
+ const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
9272
+ const description = jsxs("div", {
9273
+ children: ["Define an expression to populate the options from.", jsx("br", {}), jsx("br", {}), "The expression may result in an array of simple values or alternatively follow this schema:", jsx("pre", {
9274
+ children: jsx("code", {
9275
+ children: schema
9276
+ })
9277
+ })]
9278
+ });
9279
+ const getValue = () => get(field, path, '');
9280
+ const setValue = value => editField(field, path, value || '');
9281
+ return FeelEntry({
9282
+ debounce,
9283
+ description,
9284
+ element: field,
9285
+ feel: 'required',
9286
+ getValue,
9287
+ id,
9288
+ label: 'Options expression',
9289
+ setValue,
9290
+ variables
9291
+ });
9292
+ }
9293
+
9098
9294
  function GeneralGroup(field, editField, getService) {
9099
9295
  const entries = [...IdEntry({
9100
9296
  field,
@@ -9239,14 +9435,14 @@ function ValidationGroup(field, editField) {
9239
9435
  component: MinLength,
9240
9436
  getValue,
9241
9437
  field,
9242
- isEdited: isEdited$4,
9438
+ isEdited: isEdited$5,
9243
9439
  onChange
9244
9440
  }, {
9245
9441
  id: 'maxLength',
9246
9442
  component: MaxLength,
9247
9443
  getValue,
9248
9444
  field,
9249
- isEdited: isEdited$4,
9445
+ isEdited: isEdited$5,
9250
9446
  onChange
9251
9447
  });
9252
9448
  }
@@ -9266,14 +9462,14 @@ function ValidationGroup(field, editField) {
9266
9462
  component: Min,
9267
9463
  getValue,
9268
9464
  field,
9269
- isEdited: isEdited$4,
9465
+ isEdited: isEdited$5,
9270
9466
  onChange
9271
9467
  }, {
9272
9468
  id: 'max',
9273
9469
  component: Max,
9274
9470
  getValue,
9275
9471
  field,
9276
- isEdited: isEdited$4,
9472
+ isEdited: isEdited$5,
9277
9473
  onChange
9278
9474
  });
9279
9475
  }
@@ -9306,14 +9502,19 @@ function MinLength(props) {
9306
9502
  onChange
9307
9503
  } = props;
9308
9504
  const debounce = useService('debounce');
9309
- return NumberFieldEntry({
9505
+ const variables = useVariables().map(name => ({
9506
+ name
9507
+ }));
9508
+ return FeelNumberEntry({
9310
9509
  debounce,
9311
9510
  element: field,
9511
+ feel: 'optional',
9312
9512
  getValue: getValue('minLength'),
9313
9513
  id,
9314
9514
  label: 'Minimum length',
9315
9515
  min: 0,
9316
- setValue: onChange('minLength')
9516
+ setValue: onChange('minLength'),
9517
+ variables
9317
9518
  });
9318
9519
  }
9319
9520
  function MaxLength(props) {
@@ -9324,14 +9525,19 @@ function MaxLength(props) {
9324
9525
  onChange
9325
9526
  } = props;
9326
9527
  const debounce = useService('debounce');
9327
- return NumberFieldEntry({
9528
+ const variables = useVariables().map(name => ({
9529
+ name
9530
+ }));
9531
+ return FeelNumberEntry({
9328
9532
  debounce,
9329
9533
  element: field,
9534
+ feel: 'optional',
9330
9535
  getValue: getValue('maxLength'),
9331
9536
  id,
9332
9537
  label: 'Maximum length',
9333
9538
  min: 0,
9334
- setValue: onChange('maxLength')
9539
+ setValue: onChange('maxLength'),
9540
+ variables
9335
9541
  });
9336
9542
  }
9337
9543
  function Pattern(props) {
@@ -9359,14 +9565,19 @@ function Min(props) {
9359
9565
  onChange
9360
9566
  } = props;
9361
9567
  const debounce = useService('debounce');
9362
- return NumberFieldEntry({
9568
+ const variables = useVariables().map(name => ({
9569
+ name
9570
+ }));
9571
+ return FeelNumberEntry({
9363
9572
  debounce,
9364
9573
  element: field,
9574
+ feel: 'optional',
9365
9575
  id,
9366
9576
  label: 'Minimum',
9367
9577
  step: 'any',
9368
9578
  getValue: getValue('min'),
9369
- setValue: onChange('min')
9579
+ setValue: onChange('min'),
9580
+ variables
9370
9581
  });
9371
9582
  }
9372
9583
  function Max(props) {
@@ -9377,14 +9588,19 @@ function Max(props) {
9377
9588
  onChange
9378
9589
  } = props;
9379
9590
  const debounce = useService('debounce');
9380
- return NumberFieldEntry({
9591
+ const variables = useVariables().map(name => ({
9592
+ name
9593
+ }));
9594
+ return FeelNumberEntry({
9381
9595
  debounce,
9382
9596
  element: field,
9597
+ feel: 'optional',
9383
9598
  id,
9384
9599
  label: 'Maximum',
9385
9600
  step: 'any',
9386
9601
  getValue: getValue('max'),
9387
- setValue: onChange('max')
9602
+ setValue: onChange('max'),
9603
+ variables
9388
9604
  });
9389
9605
  }
9390
9606
  function ValidationType(props) {
@@ -9458,6 +9674,17 @@ function ValuesGroups(field, editField) {
9458
9674
  id: staticValuesId
9459
9675
  })
9460
9676
  });
9677
+ } else if (valuesSource === VALUES_SOURCES.EXPRESSION) {
9678
+ const valuesExpressionId = `${fieldId}-valuesExpression`;
9679
+ groups.push({
9680
+ id: valuesExpressionId,
9681
+ label: 'Options expression',
9682
+ component: Group,
9683
+ entries: ValuesExpressionEntry({
9684
+ ...context,
9685
+ id: valuesExpressionId
9686
+ })
9687
+ });
9461
9688
  }
9462
9689
  return groups;
9463
9690
  }
@@ -9617,47 +9844,37 @@ function FormPropertiesPanel(props) {
9617
9844
  } = props;
9618
9845
  const formEditor = injector.get('formEditor');
9619
9846
  const modeling = injector.get('modeling');
9620
- const selection = injector.get('selection');
9621
- const {
9622
- schema
9623
- } = formEditor._getState();
9847
+ const selectionModule = injector.get('selection');
9624
9848
  const [state, setState] = useState({
9625
- selectedFormField: selection.get() || schema
9849
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
9626
9850
  });
9627
- const _update = field => {
9851
+ const selectedFormField = state.selectedFormField;
9852
+ const refresh = useCallback(field => {
9853
+ // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
9854
+ // https://github.com/bpmn-io/form-js/issues/686
9628
9855
  setState({
9629
- ...state,
9630
- selectedFormField: field
9856
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
9631
9857
  });
9632
9858
 
9633
9859
  // notify interested parties on property panel updates
9634
9860
  eventBus.fire('propertiesPanel.updated', {
9635
9861
  formField: field
9636
9862
  });
9637
- };
9863
+ }, [eventBus, formEditor, selectionModule]);
9638
9864
  useLayoutEffect(() => {
9639
- function onSelectionChange(event) {
9640
- _update(event.selection || schema);
9641
- }
9642
- eventBus.on('selection.changed', onSelectionChange);
9643
- return () => {
9644
- eventBus.off('selection.changed', onSelectionChange);
9645
- };
9646
- }, []);
9647
- useLayoutEffect(() => {
9648
- const onFieldChanged = () => {
9649
- /**
9650
- * TODO(pinussilvestrus): update with actual updated element,
9651
- * once we have a proper updater/change support
9652
- */
9653
- _update(selection.get() || schema);
9654
- };
9655
- eventBus.on('changed', onFieldChanged);
9865
+ /**
9866
+ * TODO(pinussilvestrus): update with actual updated element,
9867
+ * once we have a proper updater/change support
9868
+ */
9869
+ eventBus.on('changed', refresh);
9870
+ eventBus.on('import.done', refresh);
9871
+ eventBus.on('selection.changed', refresh);
9656
9872
  return () => {
9657
- eventBus.off('changed', onFieldChanged);
9873
+ eventBus.off('changed', refresh);
9874
+ eventBus.off('import.done', refresh);
9875
+ eventBus.off('selection.changed', refresh);
9658
9876
  };
9659
- }, []);
9660
- const selectedFormField = state.selectedFormField;
9877
+ }, [eventBus, refresh]);
9661
9878
  const getService = (type, strict = true) => injector.get(type, strict);
9662
9879
  const propertiesPanelContext = {
9663
9880
  getService