@bpmn-io/form-js-editor 0.15.0-alpha.0 → 1.0.0-alpha.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.cjs CHANGED
@@ -1702,9 +1702,17 @@ function Element(props) {
1702
1702
  eventBus.on('selection.changed', scrollIntoView);
1703
1703
  return () => eventBus.off('selection.changed', scrollIntoView);
1704
1704
  }, [id]);
1705
+ hooks.useLayoutEffect(() => {
1706
+ if (selection.isSelected(field)) {
1707
+ ref.current.focus();
1708
+ }
1709
+ }, [selection, field]);
1705
1710
  function onClick(event) {
1706
1711
  event.stopPropagation();
1707
1712
  selection.toggle(field);
1713
+
1714
+ // properly focus on field
1715
+ ref.current.focus();
1708
1716
  }
1709
1717
  const classes = [];
1710
1718
  if (props.class) {
@@ -1719,11 +1727,19 @@ function Element(props) {
1719
1727
  const index = getFormFieldIndex(parentField, field);
1720
1728
  modeling.removeFormField(field, parentField, index);
1721
1729
  };
1730
+ const onKeyPress = event => {
1731
+ if (event.key === 'Enter') {
1732
+ event.stopPropagation();
1733
+ selection.toggle(field);
1734
+ }
1735
+ };
1722
1736
  return jsxRuntime.jsxs("div", {
1723
1737
  class: classes.join(' '),
1724
1738
  "data-id": id,
1725
1739
  "data-field-type": type,
1740
+ tabIndex: type === 'default' ? -1 : 0,
1726
1741
  onClick: onClick,
1742
+ onKeyPress: onKeyPress,
1727
1743
  ref: ref,
1728
1744
  children: [jsxRuntime.jsx(DebugColumns, {
1729
1745
  field: field
@@ -1916,7 +1932,7 @@ function FormEditor$1(props) {
1916
1932
  errors: {},
1917
1933
  properties: {
1918
1934
  ariaLabel,
1919
- readOnly: true
1935
+ disabled: true
1920
1936
  },
1921
1937
  schema
1922
1938
  };
@@ -4310,44 +4326,18 @@ ExternalLinkIcon.defaultProps = {
4310
4326
  fill: "none",
4311
4327
  xmlns: "http://www.w3.org/2000/svg"
4312
4328
  };
4313
- var FeelRequiredIcon = function FeelRequiredIcon(props) {
4314
- return jsxRuntime.jsxs("svg", {
4315
- ...props,
4316
- children: [jsxRuntime.jsx("path", {
4317
- d: "M5.8 7.06V5.95h4.307v1.11H5.8Zm0 3.071v-1.11h4.307v1.11H5.8Z",
4318
- fill: "currentColor"
4319
- }), jsxRuntime.jsx("path", {
4320
- fillRule: "evenodd",
4321
- clipRule: "evenodd",
4322
- d: "M8 3.268A4.732 4.732 0 1 0 12.732 8H14a6 6 0 1 1-6-6v1.268Z",
4323
- fill: "currentColor"
4324
- }), jsxRuntime.jsx("path", {
4325
- d: "m11.28 6.072-.832-.56 1.016-1.224L10 3.848l.312-.912 1.392.584L11.632 2h1.032l-.072 1.52 1.392-.584.312.912-1.464.44 1.008 1.224-.832.552-.864-1.296-.864 1.304Z",
4326
- fill: "currentColor"
4327
- })]
4328
- });
4329
- };
4330
- FeelRequiredIcon.defaultProps = {
4331
- viewBox: "0 0 16 16",
4332
- fill: "none",
4333
- xmlns: "http://www.w3.org/2000/svg"
4334
- };
4335
- var FeelOptionalIcon = function FeelOptionalIcon(props) {
4336
- return jsxRuntime.jsxs("svg", {
4329
+ var FeelIcon$1 = function FeelIcon(props) {
4330
+ return jsxRuntime.jsx("svg", {
4337
4331
  ...props,
4338
- children: [jsxRuntime.jsx("path", {
4339
- d: "M5.845 7.04V5.93h4.307v1.11H5.845Zm0 3.07V9h4.307v1.11H5.845Z",
4340
- fill: "currentColor"
4341
- }), jsxRuntime.jsx("path", {
4342
- fillRule: "evenodd",
4343
- clipRule: "evenodd",
4344
- d: "M3.286 8a4.714 4.714 0 1 0 9.428 0 4.714 4.714 0 0 0-9.428 0ZM8 2a6 6 0 1 0 0 12A6 6 0 0 0 8 2Z",
4345
- fill: "currentColor"
4346
- })]
4332
+ children: jsxRuntime.jsx("path", {
4333
+ d: "M3.617 11.99c-.137.684-.392 1.19-.765 1.518-.362.328-.882.492-1.558.492H0l.309-1.579h1.264l1.515-7.64h-.912l.309-1.579h.911l.236-1.191c.137-.685.387-1.192.75-1.52C4.753.164 5.277 0 5.953 0h1.294L6.94 1.579H5.675l-.323 1.623h1.264l-.309 1.579H5.043l-1.426 7.208ZM5.605 11.021l3.029-4.155L7.28 3.202h2.073l.706 2.547h.176l1.691-2.547H14l-3.014 4.051 1.338 3.768H10.25l-.706-2.606H9.37L7.678 11.02H5.605Z",
4334
+ fill: "currentcolor"
4335
+ })
4347
4336
  });
4348
4337
  };
4349
- FeelOptionalIcon.defaultProps = {
4350
- viewBox: "0 0 16 16",
4338
+ FeelIcon$1.defaultProps = {
4339
+ width: "14",
4340
+ height: "14",
4351
4341
  fill: "none",
4352
4342
  xmlns: "http://www.w3.org/2000/svg"
4353
4343
  };
@@ -4587,6 +4577,18 @@ function useShowEntryEvent(id) {
4587
4577
  * @param {setSticky} setSticky
4588
4578
  */
4589
4579
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
4580
+ const [scrollContainer, setScrollContainer] = hooks.useState(minDom.query(scrollContainerSelector));
4581
+ const updateScrollContainer = hooks.useCallback(() => {
4582
+ const newScrollContainer = minDom.query(scrollContainerSelector);
4583
+ if (newScrollContainer !== scrollContainer) {
4584
+ setScrollContainer(newScrollContainer);
4585
+ }
4586
+ }, [scrollContainerSelector, scrollContainer]);
4587
+ hooks.useEffect(() => {
4588
+ updateScrollContainer();
4589
+ }, [updateScrollContainer]);
4590
+ useEvent('propertiesPanel.attach', updateScrollContainer);
4591
+ useEvent('propertiesPanel.detach', updateScrollContainer);
4590
4592
  hooks.useEffect(() => {
4591
4593
  const Observer = IntersectionObserver;
4592
4594
 
@@ -4594,33 +4596,36 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
4594
4596
  if (!Observer) {
4595
4597
  return;
4596
4598
  }
4597
- let observer;
4598
- if (ref.current) {
4599
- const scrollContainer = minDom.query(scrollContainerSelector);
4600
- observer = new Observer(entries => {
4601
- entries.forEach(entry => {
4602
- if (entry.intersectionRatio < 1) {
4603
- setSticky(true);
4604
- } else if (entry.intersectionRatio === 1) {
4605
- setSticky(false);
4606
- }
4607
- });
4608
- }, {
4609
- root: scrollContainer,
4610
- rootMargin: '0px 0px 999999% 0px',
4611
- // Use bottom margin to avoid stickyness when scrolling out to bottom
4612
- threshold: [1]
4613
- });
4614
- observer.observe(ref.current);
4599
+
4600
+ // TODO(@barmac): test this
4601
+ if (!ref.current || !scrollContainer) {
4602
+ return;
4615
4603
  }
4604
+ const observer = new Observer(entries => {
4605
+ // scroll container is unmounted, do not update sticky state
4606
+ if (scrollContainer.scrollHeight === 0) {
4607
+ return;
4608
+ }
4609
+ entries.forEach(entry => {
4610
+ if (entry.intersectionRatio < 1) {
4611
+ setSticky(true);
4612
+ } else if (entry.intersectionRatio === 1) {
4613
+ setSticky(false);
4614
+ }
4615
+ });
4616
+ }, {
4617
+ root: scrollContainer,
4618
+ rootMargin: '0px 0px 999999% 0px',
4619
+ // Use bottom margin to avoid stickyness when scrolling out to bottom
4620
+ threshold: [1]
4621
+ });
4622
+ observer.observe(ref.current);
4616
4623
 
4617
4624
  // Unobserve if unmounted
4618
4625
  return () => {
4619
- if (ref.current && observer) {
4620
- observer.unobserve(ref.current);
4621
- }
4626
+ observer.unobserve(ref.current);
4622
4627
  };
4623
- }, [ref, scrollContainerSelector, setSticky]);
4628
+ }, [ref.current, scrollContainer, setSticky]);
4624
4629
  }
4625
4630
 
4626
4631
  /**
@@ -4753,9 +4758,7 @@ function Placeholder(props) {
4753
4758
  })
4754
4759
  });
4755
4760
  }
4756
- const DEFAULT_LAYOUT = {
4757
- open: true
4758
- };
4761
+ const DEFAULT_LAYOUT = {};
4759
4762
  const DEFAULT_DESCRIPTION = {};
4760
4763
 
4761
4764
  /**
@@ -4842,7 +4845,7 @@ function PropertiesPanel(props) {
4842
4845
  const [layout, setLayout] = hooks.useState(createLayout(layoutConfig));
4843
4846
 
4844
4847
  // react to external changes in the layout config
4845
- useUpdateEffect(() => {
4848
+ useUpdateLayoutEffect(() => {
4846
4849
  const newLayout = createLayout(layoutConfig);
4847
4850
  setLayout(newLayout);
4848
4851
  }, [layoutConfig]);
@@ -4919,7 +4922,7 @@ function PropertiesPanel(props) {
4919
4922
  children: jsxRuntime.jsx(EventContext.Provider, {
4920
4923
  value: eventContext,
4921
4924
  children: jsxRuntime.jsxs("div", {
4922
- class: classnames('bio-properties-panel', layout.open ? 'open' : ''),
4925
+ class: "bio-properties-panel",
4923
4926
  children: [jsxRuntime.jsx(Header, {
4924
4927
  element: element,
4925
4928
  headerProvider: headerProvider
@@ -4963,14 +4966,14 @@ function createDescriptionContext(overrides = {}) {
4963
4966
  // hooks //////////////////
4964
4967
 
4965
4968
  /**
4966
- * This hook behaves like useEffect, but does not trigger on the first render.
4969
+ * This hook behaves like useLayoutEffect, but does not trigger on the first render.
4967
4970
  *
4968
4971
  * @param {Function} effect
4969
4972
  * @param {Array} deps
4970
4973
  */
4971
- function useUpdateEffect(effect, deps) {
4974
+ function useUpdateLayoutEffect(effect, deps) {
4972
4975
  const isMounted = hooks.useRef(false);
4973
- hooks.useEffect(() => {
4976
+ hooks.useLayoutEffect(() => {
4974
4977
  if (isMounted.current) {
4975
4978
  return effect();
4976
4979
  } else {
@@ -5588,14 +5591,13 @@ const noop$2 = () => {};
5588
5591
  */
5589
5592
  function FeelIcon(props) {
5590
5593
  const {
5591
- label,
5592
5594
  feel = false,
5593
5595
  active,
5594
5596
  disabled = false,
5595
5597
  onClick = noop$2
5596
5598
  } = props;
5597
- const feelRequiredLabel = ' must be a FEEL expression';
5598
- const feelOptionalLabel = ' can optionally be a FEEL expression';
5599
+ const feelRequiredLabel = 'FEEL expression is mandatory';
5600
+ const feelOptionalLabel = `Click to ${active ? 'remove' : 'set a'} dynamic value with FEEL expression`;
5599
5601
  const handleClick = e => {
5600
5602
  onClick(e);
5601
5603
 
@@ -5608,10 +5610,124 @@ function FeelIcon(props) {
5608
5610
  class: classnames('bio-properties-panel-feel-icon', active ? 'active' : null, feel === 'required' ? 'required' : 'optional'),
5609
5611
  onClick: handleClick,
5610
5612
  disabled: feel === 'required' || disabled,
5611
- title: label + (feel === 'required' ? feelRequiredLabel : feelOptionalLabel),
5612
- children: feel === 'required' ? jsxRuntime.jsx(FeelRequiredIcon, {}) : jsxRuntime.jsx(FeelOptionalIcon, {})
5613
+ title: feel === 'required' ? feelRequiredLabel : feelOptionalLabel,
5614
+ children: jsxRuntime.jsx(FeelIcon$1, {})
5613
5615
  });
5614
5616
  }
5617
+ function ToggleSwitch(props) {
5618
+ const {
5619
+ id,
5620
+ label,
5621
+ onInput,
5622
+ value,
5623
+ switcherLabel,
5624
+ inline,
5625
+ onFocus,
5626
+ onBlur,
5627
+ inputRef
5628
+ } = props;
5629
+ const [localValue, setLocalValue] = hooks.useState(value);
5630
+ const handleInputCallback = async () => {
5631
+ onInput(!value);
5632
+ };
5633
+ const handleInput = e => {
5634
+ handleInputCallback();
5635
+ setLocalValue(e.target.value);
5636
+ };
5637
+ hooks.useEffect(() => {
5638
+ if (value === localValue) {
5639
+ return;
5640
+ }
5641
+ setLocalValue(value);
5642
+ }, [value]);
5643
+ return jsxRuntime.jsxs("div", {
5644
+ class: classnames('bio-properties-panel-toggle-switch', {
5645
+ inline
5646
+ }),
5647
+ children: [jsxRuntime.jsx("label", {
5648
+ class: "bio-properties-panel-label",
5649
+ for: prefixId$6(id),
5650
+ children: label
5651
+ }), jsxRuntime.jsxs("div", {
5652
+ class: "bio-properties-panel-field-wrapper",
5653
+ children: [jsxRuntime.jsxs("label", {
5654
+ class: "bio-properties-panel-toggle-switch__switcher",
5655
+ children: [jsxRuntime.jsx("input", {
5656
+ ref: inputRef,
5657
+ id: prefixId$6(id),
5658
+ class: "bio-properties-panel-input",
5659
+ type: "checkbox",
5660
+ onFocus: onFocus,
5661
+ onBlur: onBlur,
5662
+ name: id,
5663
+ onInput: handleInput,
5664
+ checked: !!localValue
5665
+ }), jsxRuntime.jsx("span", {
5666
+ class: "bio-properties-panel-toggle-switch__slider"
5667
+ })]
5668
+ }), switcherLabel && jsxRuntime.jsx("p", {
5669
+ class: "bio-properties-panel-toggle-switch__label",
5670
+ children: switcherLabel
5671
+ })]
5672
+ })]
5673
+ });
5674
+ }
5675
+
5676
+ /**
5677
+ * @param {Object} props
5678
+ * @param {Object} props.element
5679
+ * @param {String} props.id
5680
+ * @param {String} props.description
5681
+ * @param {String} props.label
5682
+ * @param {String} props.switcherLabel
5683
+ * @param {Boolean} props.inline
5684
+ * @param {Function} props.getValue
5685
+ * @param {Function} props.setValue
5686
+ * @param {Function} props.onFocus
5687
+ * @param {Function} props.onBlur
5688
+ */
5689
+ function ToggleSwitchEntry(props) {
5690
+ const {
5691
+ element,
5692
+ id,
5693
+ description,
5694
+ label,
5695
+ switcherLabel,
5696
+ inline,
5697
+ getValue,
5698
+ setValue,
5699
+ onFocus,
5700
+ onBlur
5701
+ } = props;
5702
+ const value = getValue(element);
5703
+ return jsxRuntime.jsxs("div", {
5704
+ class: "bio-properties-panel-entry bio-properties-panel-toggle-switch-entry",
5705
+ "data-entry-id": id,
5706
+ children: [jsxRuntime.jsx(ToggleSwitch, {
5707
+ id: id,
5708
+ label: label,
5709
+ value: value,
5710
+ onInput: setValue,
5711
+ onFocus: onFocus,
5712
+ onBlur: onBlur,
5713
+ switcherLabel: switcherLabel,
5714
+ inline: inline
5715
+ }), jsxRuntime.jsx(Description$1, {
5716
+ forId: id,
5717
+ element: element,
5718
+ value: description
5719
+ })]
5720
+ });
5721
+ }
5722
+ function isEdited$7(node) {
5723
+ return node && !!node.checked;
5724
+ }
5725
+
5726
+ // helpers /////////////////
5727
+
5728
+ function prefixId$6(id) {
5729
+ return `bio-properties-panel-${id}`;
5730
+ }
5615
5731
  const noop$1 = () => {};
5616
5732
  function FeelTextfield(props) {
5617
5733
  const {
@@ -5630,8 +5746,8 @@ function FeelTextfield(props) {
5630
5746
  const [localValue, _setLocalValue] = hooks.useState(value);
5631
5747
  const editorRef = useShowEntryEvent(id);
5632
5748
  const containerRef = hooks.useRef();
5633
- const feelActive = localValue.startsWith('=') || feel === 'required';
5634
- const feelOnlyValue = localValue.startsWith('=') ? localValue.substring(1) : localValue;
5749
+ const feelActive = minDash.isString(localValue) && localValue.startsWith('=') || feel === 'required';
5750
+ const feelOnlyValue = minDash.isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
5635
5751
  const [focus, _setFocus] = hooks.useState(undefined);
5636
5752
  const setFocus = (offset = 0) => {
5637
5753
  const hasFocus = containerRef.current.contains(document.activeElement);
@@ -5671,7 +5787,7 @@ function FeelTextfield(props) {
5671
5787
  return;
5672
5788
  }
5673
5789
  setLocalValue(newValue);
5674
- if (!feelActive && newValue.startsWith('=')) {
5790
+ if (!feelActive && minDash.isString(newValue) && newValue.startsWith('=')) {
5675
5791
  // focus is behind `=` sign that will be removed
5676
5792
  setFocus(-1);
5677
5793
  }
@@ -5734,9 +5850,11 @@ function FeelTextfield(props) {
5734
5850
  };
5735
5851
  }, [containerRef, feelActive, handleFeelToggle, setFocus]);
5736
5852
  return jsxRuntime.jsxs("div", {
5737
- class: "bio-properties-panel-feel-entry",
5853
+ class: classnames('bio-properties-panel-feel-entry', {
5854
+ 'feel-active': feelActive
5855
+ }),
5738
5856
  children: [jsxRuntime.jsxs("label", {
5739
- for: prefixId$6(id),
5857
+ for: prefixId$5(id),
5740
5858
  class: "bio-properties-panel-label",
5741
5859
  onClick: () => setFocus(),
5742
5860
  children: [label, jsxRuntime.jsx(FeelIcon, {
@@ -5753,7 +5871,7 @@ function FeelTextfield(props) {
5753
5871
  disabled: feel !== 'optional' || disabled,
5754
5872
  onClick: handleFeelToggle
5755
5873
  }), feelActive ? jsxRuntime.jsx(CodeEditor, {
5756
- id: prefixId$6(id),
5874
+ id: prefixId$5(id),
5757
5875
  name: id,
5758
5876
  onInput: handleLocalInput,
5759
5877
  disabled: disabled,
@@ -5770,7 +5888,8 @@ function FeelTextfield(props) {
5770
5888
  ...props,
5771
5889
  onInput: handleLocalInput,
5772
5890
  contentAttributes: {
5773
- 'id': prefixId$6(id)
5891
+ 'id': prefixId$5(id),
5892
+ 'aria-label': label
5774
5893
  },
5775
5894
  value: localValue,
5776
5895
  ref: editorRef
@@ -5807,7 +5926,7 @@ const OptionalFeelInput = React.forwardRef((props, ref) => {
5807
5926
  }
5808
5927
  };
5809
5928
  return jsxRuntime.jsx("input", {
5810
- id: prefixId$6(id),
5929
+ id: prefixId$5(id),
5811
5930
  type: "text",
5812
5931
  ref: inputRef,
5813
5932
  name: id,
@@ -5845,7 +5964,7 @@ React.forwardRef((props, ref) => {
5845
5964
  }
5846
5965
  };
5847
5966
  return jsxRuntime.jsx("textarea", {
5848
- id: prefixId$6(id),
5967
+ id: prefixId$5(id),
5849
5968
  type: "text",
5850
5969
  ref: inputRef,
5851
5970
  name: id,
@@ -5860,6 +5979,78 @@ React.forwardRef((props, ref) => {
5860
5979
  "data-gramm": "false"
5861
5980
  });
5862
5981
  });
5982
+ const OptionalFeelToggleSwitch = React.forwardRef((props, ref) => {
5983
+ const {
5984
+ id,
5985
+ onInput,
5986
+ value,
5987
+ onFocus,
5988
+ onBlur,
5989
+ switcherLabel
5990
+ } = props;
5991
+ const inputRef = hooks.useRef();
5992
+
5993
+ // To be consistent with the FEEL editor, set focus at start of input
5994
+ // this ensures clean editing experience when switching with the keyboard
5995
+ ref.current = {
5996
+ focus: () => {
5997
+ const input = inputRef.current;
5998
+ if (!input) {
5999
+ return;
6000
+ }
6001
+ input.focus();
6002
+ }
6003
+ };
6004
+ return jsxRuntime.jsx(ToggleSwitch, {
6005
+ id: id,
6006
+ value: value,
6007
+ inputRef: inputRef,
6008
+ onInput: onInput,
6009
+ onFocus: onFocus,
6010
+ onBlur: onBlur,
6011
+ switcherLabel: switcherLabel
6012
+ });
6013
+ });
6014
+ React.forwardRef((props, ref) => {
6015
+ const {
6016
+ id,
6017
+ disabled,
6018
+ onInput,
6019
+ value,
6020
+ onFocus,
6021
+ onBlur
6022
+ } = props;
6023
+ const inputRef = hooks.useRef();
6024
+ const handleChange = ({
6025
+ target
6026
+ }) => {
6027
+ onInput(target.checked);
6028
+ };
6029
+
6030
+ // To be consistent with the FEEL editor, set focus at start of input
6031
+ // this ensures clean editing experience when switching with the keyboard
6032
+ ref.current = {
6033
+ focus: () => {
6034
+ const input = inputRef.current;
6035
+ if (!input) {
6036
+ return;
6037
+ }
6038
+ input.focus();
6039
+ }
6040
+ };
6041
+ return jsxRuntime.jsx("input", {
6042
+ ref: inputRef,
6043
+ id: prefixId$5(id),
6044
+ name: id,
6045
+ onFocus: onFocus,
6046
+ onBlur: onBlur,
6047
+ type: "checkbox",
6048
+ class: "bio-properties-panel-input",
6049
+ onChange: handleChange,
6050
+ checked: value,
6051
+ disabled: disabled
6052
+ });
6053
+ });
5863
6054
 
5864
6055
  /**
5865
6056
  * @param {Object} props
@@ -5967,6 +6158,33 @@ function FeelEntry(props) {
5967
6158
  });
5968
6159
  }
5969
6160
 
6161
+ /**
6162
+ * @param {Object} props
6163
+ * @param {Object} props.element
6164
+ * @param {String} props.id
6165
+ * @param {String} props.description
6166
+ * @param {Boolean} props.debounce
6167
+ * @param {Boolean} props.disabled
6168
+ * @param {Boolean} props.feel
6169
+ * @param {String} props.label
6170
+ * @param {Function} props.getValue
6171
+ * @param {Function} props.setValue
6172
+ * @param {Function} props.tooltipContainer
6173
+ * @param {Function} props.validate
6174
+ * @param {Function} props.show
6175
+ * @param {Function} props.example
6176
+ * @param {Function} props.variables
6177
+ * @param {Function} props.onFocus
6178
+ * @param {Function} props.onBlur
6179
+ */
6180
+ function FeelToggleSwitchEntry(props) {
6181
+ return jsxRuntime.jsx(FeelEntry, {
6182
+ class: "bio-properties-panel-feel-toggle-switch",
6183
+ OptionalComponent: OptionalFeelToggleSwitch,
6184
+ ...props
6185
+ });
6186
+ }
6187
+
5970
6188
  /**
5971
6189
  * @param {Object} props
5972
6190
  * @param {Object} props.element
@@ -5995,13 +6213,19 @@ function FeelTemplatingEntry(props) {
5995
6213
  ...props
5996
6214
  });
5997
6215
  }
5998
- function isEdited$7(node) {
5999
- return node && (!!node.value || node.classList.contains('edited'));
6216
+ function isEdited$6(node) {
6217
+ if (!node) {
6218
+ return false;
6219
+ }
6220
+ if (node.type === 'checkbox') {
6221
+ return !!node.checked || node.classList.contains('edited');
6222
+ }
6223
+ return !!node.value || node.classList.contains('edited');
6000
6224
  }
6001
6225
 
6002
6226
  // helpers /////////////////
6003
6227
 
6004
- function prefixId$6(id) {
6228
+ function prefixId$5(id) {
6005
6229
  return `bio-properties-panel-${id}`;
6006
6230
  }
6007
6231
  function NumberField(props) {
@@ -6043,11 +6267,11 @@ function NumberField(props) {
6043
6267
  return jsxRuntime.jsxs("div", {
6044
6268
  class: "bio-properties-panel-numberfield",
6045
6269
  children: [jsxRuntime.jsx("label", {
6046
- for: prefixId$5(id),
6270
+ for: prefixId$4(id),
6047
6271
  class: "bio-properties-panel-label",
6048
6272
  children: label
6049
6273
  }), jsxRuntime.jsx("input", {
6050
- id: prefixId$5(id),
6274
+ id: prefixId$4(id),
6051
6275
  type: "number",
6052
6276
  name: id,
6053
6277
  spellCheck: "false",
@@ -6151,13 +6375,13 @@ function NumberFieldEntry(props) {
6151
6375
  })]
6152
6376
  });
6153
6377
  }
6154
- function isEdited$5(node) {
6378
+ function isEdited$4(node) {
6155
6379
  return node && !!node.value;
6156
6380
  }
6157
6381
 
6158
6382
  // helpers /////////////////
6159
6383
 
6160
- function prefixId$5(id) {
6384
+ function prefixId$4(id) {
6161
6385
  return `bio-properties-panel-${id}`;
6162
6386
  }
6163
6387
  function Select(props) {
@@ -6191,12 +6415,12 @@ function Select(props) {
6191
6415
  return jsxRuntime.jsxs("div", {
6192
6416
  class: "bio-properties-panel-select",
6193
6417
  children: [jsxRuntime.jsx("label", {
6194
- for: prefixId$4(id),
6418
+ for: prefixId$3(id),
6195
6419
  class: "bio-properties-panel-label",
6196
6420
  children: label
6197
6421
  }), jsxRuntime.jsx("select", {
6198
6422
  ref: ref,
6199
- id: prefixId$4(id),
6423
+ id: prefixId$3(id),
6200
6424
  name: id,
6201
6425
  class: "bio-properties-panel-input",
6202
6426
  onInput: handleChange,
@@ -6303,13 +6527,13 @@ function SelectEntry(props) {
6303
6527
  })]
6304
6528
  });
6305
6529
  }
6306
- function isEdited$4(node) {
6530
+ function isEdited$3(node) {
6307
6531
  return node && !!node.value;
6308
6532
  }
6309
6533
 
6310
6534
  // helpers /////////////////
6311
6535
 
6312
- function prefixId$4(id) {
6536
+ function prefixId$3(id) {
6313
6537
  return `bio-properties-panel-${id}`;
6314
6538
  }
6315
6539
  function resizeToContents(element) {
@@ -6357,12 +6581,12 @@ function TextArea(props) {
6357
6581
  return jsxRuntime.jsxs("div", {
6358
6582
  class: "bio-properties-panel-textarea",
6359
6583
  children: [jsxRuntime.jsx("label", {
6360
- for: prefixId$2(id),
6584
+ for: prefixId$1(id),
6361
6585
  class: "bio-properties-panel-label",
6362
6586
  children: label
6363
6587
  }), jsxRuntime.jsx("textarea", {
6364
6588
  ref: ref,
6365
- id: prefixId$2(id),
6589
+ id: prefixId$1(id),
6366
6590
  name: id,
6367
6591
  spellCheck: "false",
6368
6592
  class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : '', autoResize ? 'auto-resize' : ''),
@@ -6390,6 +6614,7 @@ function TextArea(props) {
6390
6614
  * @param {Function} props.onBlur
6391
6615
  * @param {number} props.rows
6392
6616
  * @param {boolean} props.monospace
6617
+ * @param {Function} [props.validate]
6393
6618
  * @param {boolean} [props.disabled]
6394
6619
  */
6395
6620
  function TextAreaEntry(props) {
@@ -6404,12 +6629,38 @@ function TextAreaEntry(props) {
6404
6629
  rows,
6405
6630
  monospace,
6406
6631
  disabled,
6632
+ validate,
6407
6633
  onFocus,
6408
6634
  onBlur,
6409
6635
  autoResize
6410
6636
  } = props;
6411
- const value = getValue(element);
6412
- const error = useError(id);
6637
+ const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
6638
+ const globalError = useError(id);
6639
+ const [localError, setLocalError] = hooks.useState(null);
6640
+ let value = getValue(element);
6641
+ const previousValue = usePrevious(value);
6642
+ hooks.useEffect(() => {
6643
+ if (minDash.isFunction(validate)) {
6644
+ const newValidationError = validate(value) || null;
6645
+ setLocalError(newValidationError);
6646
+ }
6647
+ }, [value]);
6648
+ const onInput = newValue => {
6649
+ let newValidationError = null;
6650
+ if (minDash.isFunction(validate)) {
6651
+ newValidationError = validate(newValue) || null;
6652
+ }
6653
+ if (newValidationError) {
6654
+ setCachedInvalidValue(newValue);
6655
+ } else {
6656
+ setValue(newValue);
6657
+ }
6658
+ setLocalError(newValidationError);
6659
+ };
6660
+ if (previousValue === value && localError) {
6661
+ value = cachedInvalidValue;
6662
+ }
6663
+ const error = globalError || localError;
6413
6664
  return jsxRuntime.jsxs("div", {
6414
6665
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6415
6666
  "data-entry-id": id,
@@ -6417,7 +6668,7 @@ function TextAreaEntry(props) {
6417
6668
  id: id,
6418
6669
  label: label,
6419
6670
  value: value,
6420
- onInput: setValue,
6671
+ onInput: onInput,
6421
6672
  onFocus: onFocus,
6422
6673
  onBlur: onBlur,
6423
6674
  rows: rows,
@@ -6435,13 +6686,13 @@ function TextAreaEntry(props) {
6435
6686
  })]
6436
6687
  });
6437
6688
  }
6438
- function isEdited$2(node) {
6689
+ function isEdited$1(node) {
6439
6690
  return node && !!node.value;
6440
6691
  }
6441
6692
 
6442
6693
  // helpers /////////////////
6443
6694
 
6444
- function prefixId$2(id) {
6695
+ function prefixId$1(id) {
6445
6696
  return `bio-properties-panel-${id}`;
6446
6697
  }
6447
6698
  function Textfield(props) {
@@ -6475,12 +6726,12 @@ function Textfield(props) {
6475
6726
  return jsxRuntime.jsxs("div", {
6476
6727
  class: "bio-properties-panel-textfield",
6477
6728
  children: [jsxRuntime.jsx("label", {
6478
- for: prefixId$1(id),
6729
+ for: prefixId(id),
6479
6730
  class: "bio-properties-panel-label",
6480
6731
  children: label
6481
6732
  }), jsxRuntime.jsx("input", {
6482
6733
  ref: ref,
6483
- id: prefixId$1(id),
6734
+ id: prefixId(id),
6484
6735
  type: "text",
6485
6736
  name: id,
6486
6737
  spellCheck: "false",
@@ -6572,13 +6823,13 @@ function TextfieldEntry(props) {
6572
6823
  })]
6573
6824
  });
6574
6825
  }
6575
- function isEdited$1(node) {
6826
+ function isEdited(node) {
6576
6827
  return node && !!node.value;
6577
6828
  }
6578
6829
 
6579
6830
  // helpers /////////////////
6580
6831
 
6581
- function prefixId$1(id) {
6832
+ function prefixId(id) {
6582
6833
  return `bio-properties-panel-${id}`;
6583
6834
  }
6584
6835
 
@@ -6706,7 +6957,7 @@ function ActionEntry(props) {
6706
6957
  component: Action,
6707
6958
  editField: editField,
6708
6959
  field: field,
6709
- isEdited: isEdited$4
6960
+ isEdited: isEdited$3
6710
6961
  });
6711
6962
  }
6712
6963
  return entries;
@@ -6774,7 +7025,7 @@ function AltTextEntry(props) {
6774
7025
  component: AltText,
6775
7026
  editField: editField,
6776
7027
  field: field,
6777
- isEdited: isEdited$7
7028
+ isEdited: isEdited$6
6778
7029
  });
6779
7030
  }
6780
7031
  return entries;
@@ -6819,7 +7070,7 @@ function ColumnsEntry(props) {
6819
7070
  component: Columns,
6820
7071
  field,
6821
7072
  editField,
6822
- isEdited: isEdited$4
7073
+ isEdited: isEdited$3
6823
7074
  }];
6824
7075
  return entries;
6825
7076
  }
@@ -6892,7 +7143,7 @@ function DescriptionEntry(props) {
6892
7143
  component: Description,
6893
7144
  editField: editField,
6894
7145
  field: field,
6895
- isEdited: isEdited$1
7146
+ isEdited: isEdited
6896
7147
  });
6897
7148
  }
6898
7149
  return entries;
@@ -6946,21 +7197,21 @@ function DefaultOptionEntry(props) {
6946
7197
  entries.push({
6947
7198
  ...defaultOptions,
6948
7199
  component: DefaultValueCheckbox,
6949
- isEdited: isEdited$4
7200
+ isEdited: isEdited$3
6950
7201
  });
6951
7202
  }
6952
7203
  if (type === 'number') {
6953
7204
  entries.push({
6954
7205
  ...defaultOptions,
6955
7206
  component: DefaultValueNumber,
6956
- isEdited: isEdited$1
7207
+ isEdited: isEdited
6957
7208
  });
6958
7209
  }
6959
7210
  if (type === 'radio' || type === 'select') {
6960
7211
  entries.push({
6961
7212
  ...defaultOptions,
6962
7213
  component: DefaultValueSingleSelect,
6963
- isEdited: isEdited$4
7214
+ isEdited: isEdited$3
6964
7215
  });
6965
7216
  }
6966
7217
 
@@ -6970,14 +7221,14 @@ function DefaultOptionEntry(props) {
6970
7221
  entries.push({
6971
7222
  ...defaultOptions,
6972
7223
  component: DefaultValueTextfield,
6973
- isEdited: isEdited$1
7224
+ isEdited: isEdited
6974
7225
  });
6975
7226
  }
6976
7227
  if (type === 'textarea') {
6977
7228
  entries.push({
6978
7229
  ...defaultOptions,
6979
7230
  component: DefaultValueTextarea,
6980
- isEdited: isEdited$2
7231
+ isEdited: isEdited$1
6981
7232
  });
6982
7233
  }
6983
7234
  return entries;
@@ -7171,7 +7422,7 @@ function DisabledEntry(props) {
7171
7422
  component: Disabled,
7172
7423
  editField: editField,
7173
7424
  field: field,
7174
- isEdited: isEdited$8
7425
+ isEdited: isEdited$7
7175
7426
  });
7176
7427
  }
7177
7428
  return entries;
@@ -7189,11 +7440,12 @@ function Disabled(props) {
7189
7440
  const setValue = value => {
7190
7441
  return editField(field, path, value);
7191
7442
  };
7192
- return CheckboxEntry({
7443
+ return ToggleSwitchEntry({
7193
7444
  element: field,
7194
7445
  getValue,
7195
7446
  id,
7196
7447
  label: 'Disabled',
7448
+ inline: true,
7197
7449
  setValue
7198
7450
  });
7199
7451
  }
@@ -7210,7 +7462,7 @@ function IdEntry(props) {
7210
7462
  component: Id,
7211
7463
  editField: editField,
7212
7464
  field: field,
7213
- isEdited: isEdited$1
7465
+ isEdited: isEdited
7214
7466
  });
7215
7467
  }
7216
7468
  return entries;
@@ -7290,7 +7542,7 @@ function KeyEntry(props) {
7290
7542
  component: Key$1,
7291
7543
  editField: editField,
7292
7544
  field: field,
7293
- isEdited: isEdited$1
7545
+ isEdited: isEdited
7294
7546
  });
7295
7547
  }
7296
7548
  return entries;
@@ -7353,7 +7605,7 @@ function simpleStringEntryFactory(options) {
7353
7605
  field,
7354
7606
  editField,
7355
7607
  component: SimpleStringComponent,
7356
- isEdited: isEdited$1
7608
+ isEdited: isEdited
7357
7609
  };
7358
7610
  }
7359
7611
  const SimpleStringComponent = props => {
@@ -7473,7 +7725,7 @@ function SourceEntry(props) {
7473
7725
  component: Source,
7474
7726
  editField: editField,
7475
7727
  field: field,
7476
- isEdited: isEdited$7
7728
+ isEdited: isEdited$6
7477
7729
  });
7478
7730
  }
7479
7731
  return entries;
@@ -7528,7 +7780,7 @@ function TextEntry(props) {
7528
7780
  component: Text,
7529
7781
  editField: editField,
7530
7782
  field: field,
7531
- isEdited: isEdited$7
7783
+ isEdited: isEdited$6
7532
7784
  }];
7533
7785
 
7534
7786
  // todo: skipped to make the release without too much risk
@@ -7597,14 +7849,14 @@ function NumberEntries(props) {
7597
7849
  entries.push({
7598
7850
  id: id + '-decimalDigits',
7599
7851
  component: NumberDecimalDigits,
7600
- isEdited: isEdited$5,
7852
+ isEdited: isEdited$4,
7601
7853
  editField,
7602
7854
  field
7603
7855
  });
7604
7856
  entries.push({
7605
7857
  id: id + '-step',
7606
7858
  component: NumberArrowStep,
7607
- isEdited: isEdited$1,
7859
+ isEdited: isEdited,
7608
7860
  editField,
7609
7861
  field
7610
7862
  });
@@ -7742,7 +7994,7 @@ function DateTimeEntry(props) {
7742
7994
  const entries = [{
7743
7995
  id: 'subtype',
7744
7996
  component: DateTimeSubtypeSelect,
7745
- isEdited: isEdited$4,
7997
+ isEdited: isEdited$3,
7746
7998
  editField,
7747
7999
  field
7748
8000
  }];
@@ -7854,7 +8106,7 @@ function DateTimeConstraintsEntry(props) {
7854
8106
  entries.push({
7855
8107
  id: id + '-timeInterval',
7856
8108
  component: TimeIntervalSelect,
7857
- isEdited: isEdited$4,
8109
+ isEdited: isEdited$3,
7858
8110
  editField,
7859
8111
  field
7860
8112
  });
@@ -7933,7 +8185,7 @@ function DateTimeFormatEntry(props) {
7933
8185
  entries.push({
7934
8186
  id: 'time-format',
7935
8187
  component: TimeFormatSelect,
7936
- isEdited: isEdited$4,
8188
+ isEdited: isEdited$3,
7937
8189
  editField,
7938
8190
  field
7939
8191
  });
@@ -8193,7 +8445,7 @@ function ValuesSourceSelectEntry(props) {
8193
8445
  return [{
8194
8446
  id: id + '-select',
8195
8447
  component: ValuesSourceSelect,
8196
- isEdited: isEdited$4,
8448
+ isEdited: isEdited$3,
8197
8449
  editField,
8198
8450
  field
8199
8451
  }];
@@ -8251,7 +8503,7 @@ function InputKeyValuesSourceEntry(props) {
8251
8503
  component: InputValuesKey,
8252
8504
  label: 'Input values key',
8253
8505
  description,
8254
- isEdited: isEdited$1,
8506
+ isEdited: isEdited,
8255
8507
  editField,
8256
8508
  field
8257
8509
  }];
@@ -8388,7 +8640,7 @@ function AdornerEntry(props) {
8388
8640
  entries.push({
8389
8641
  id: 'prefix-adorner',
8390
8642
  component: PrefixAdorner,
8391
- isEdited: isEdited$1,
8643
+ isEdited: isEdited,
8392
8644
  editField,
8393
8645
  field,
8394
8646
  onChange,
@@ -8397,7 +8649,7 @@ function AdornerEntry(props) {
8397
8649
  entries.push({
8398
8650
  id: 'suffix-adorner',
8399
8651
  component: SuffixAdorner,
8400
- isEdited: isEdited$1,
8652
+ isEdited: isEdited,
8401
8653
  editField,
8402
8654
  field,
8403
8655
  onChange,
@@ -8441,6 +8693,55 @@ function SuffixAdorner(props) {
8441
8693
  });
8442
8694
  }
8443
8695
 
8696
+ function ReadonlyEntry(props) {
8697
+ const {
8698
+ editField,
8699
+ field
8700
+ } = props;
8701
+ const {
8702
+ type
8703
+ } = field;
8704
+ const entries = [];
8705
+ if (INPUTS.includes(type)) {
8706
+ entries.push({
8707
+ id: 'readonly',
8708
+ component: Readonly,
8709
+ editField: editField,
8710
+ field: field,
8711
+ isEdited: isEdited$6
8712
+ });
8713
+ }
8714
+ return entries;
8715
+ }
8716
+ function Readonly(props) {
8717
+ const {
8718
+ editField,
8719
+ field,
8720
+ id
8721
+ } = props;
8722
+ const debounce = useService('debounce');
8723
+ const variables = useVariables().map(name => ({
8724
+ name
8725
+ }));
8726
+ const path = ['readonly'];
8727
+ const getValue = () => {
8728
+ return minDash.get(field, path, '');
8729
+ };
8730
+ const setValue = value => {
8731
+ return editField(field, path, value || false);
8732
+ };
8733
+ return FeelToggleSwitchEntry({
8734
+ debounce,
8735
+ element: field,
8736
+ feel: 'optional',
8737
+ getValue,
8738
+ id,
8739
+ label: 'Read only',
8740
+ setValue,
8741
+ variables
8742
+ });
8743
+ }
8744
+
8444
8745
  function ConditionEntry(props) {
8445
8746
  const {
8446
8747
  editField,
@@ -8451,7 +8752,7 @@ function ConditionEntry(props) {
8451
8752
  component: Condition,
8452
8753
  editField: editField,
8453
8754
  field: field,
8454
- isEdited: isEdited$7
8755
+ isEdited: isEdited$6
8455
8756
  }];
8456
8757
  }
8457
8758
  function Condition(props) {
@@ -8530,6 +8831,9 @@ function GeneralGroup(field, editField, getService) {
8530
8831
  }), ...DisabledEntry({
8531
8832
  field,
8532
8833
  editField
8834
+ }), ...ReadonlyEntry({
8835
+ field,
8836
+ editField
8533
8837
  })];
8534
8838
  return {
8535
8839
  id: 'general',
@@ -8620,7 +8924,7 @@ function ValidationGroup(field, editField) {
8620
8924
  getValue,
8621
8925
  field,
8622
8926
  editField,
8623
- isEdited: isEdited$1,
8927
+ isEdited: isEdited,
8624
8928
  onChange
8625
8929
  });
8626
8930
  }
@@ -8630,14 +8934,14 @@ function ValidationGroup(field, editField) {
8630
8934
  component: MinLength,
8631
8935
  getValue,
8632
8936
  field,
8633
- isEdited: isEdited$5,
8937
+ isEdited: isEdited$4,
8634
8938
  onChange
8635
8939
  }, {
8636
8940
  id: 'maxLength',
8637
8941
  component: MaxLength,
8638
8942
  getValue,
8639
8943
  field,
8640
- isEdited: isEdited$5,
8944
+ isEdited: isEdited$4,
8641
8945
  onChange
8642
8946
  });
8643
8947
  }
@@ -8647,7 +8951,7 @@ function ValidationGroup(field, editField) {
8647
8951
  component: Pattern,
8648
8952
  getValue,
8649
8953
  field,
8650
- isEdited: isEdited$1,
8954
+ isEdited: isEdited,
8651
8955
  onChange
8652
8956
  });
8653
8957
  }
@@ -8657,14 +8961,14 @@ function ValidationGroup(field, editField) {
8657
8961
  component: Min,
8658
8962
  getValue,
8659
8963
  field,
8660
- isEdited: isEdited$5,
8964
+ isEdited: isEdited$4,
8661
8965
  onChange
8662
8966
  }, {
8663
8967
  id: 'max',
8664
8968
  component: Max,
8665
8969
  getValue,
8666
8970
  field,
8667
- isEdited: isEdited$5,
8971
+ isEdited: isEdited$4,
8668
8972
  onChange
8669
8973
  });
8670
8974
  }