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