@bpmn-io/form-js-editor 1.3.0 → 1.3.1

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,7 +1,7 @@
1
1
  import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, Text as Text$1, FormFields, formFields, FormContext, FormRenderContext, FormComponent, Importer, PathRegistry, FormLayouter, FieldFactory, runRecursively, clone, 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, 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, throttle as throttle$1, set as set$1, isString, isUndefined, without, has } 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 as Fragment$1 } from 'preact/jsx-runtime';
7
7
  import { useContext, useState, useMemo, useEffect, useCallback, useRef as useRef$1, useLayoutEffect } from 'preact/hooks';
@@ -5243,20 +5243,24 @@ function Group(props) {
5243
5243
 
5244
5244
  // set edited state depending on all entries
5245
5245
  useEffect(() => {
5246
- const hasOneEditedEntry = entries.find(entry => {
5247
- const {
5248
- id,
5249
- isEdited
5250
- } = entry;
5251
- const entryNode = query(`[data-entry-id="${id}"]`);
5252
- if (!isFunction(isEdited) || !entryNode) {
5253
- return false;
5254
- }
5255
- const inputNode = query('.bio-properties-panel-input', entryNode);
5256
- return isEdited(inputNode);
5246
+ // TODO(@barmac): replace with CSS when `:has()` is supported in all major browsers, or rewrite as in https://github.com/camunda/camunda-modeler/issues/3815#issuecomment-1733038161
5247
+ const scheduled = requestAnimationFrame(() => {
5248
+ const hasOneEditedEntry = entries.find(entry => {
5249
+ const {
5250
+ id,
5251
+ isEdited
5252
+ } = entry;
5253
+ const entryNode = query(`[data-entry-id="${id}"]`);
5254
+ if (!isFunction(isEdited) || !entryNode) {
5255
+ return false;
5256
+ }
5257
+ const inputNode = query('.bio-properties-panel-input', entryNode);
5258
+ return isEdited(inputNode);
5259
+ });
5260
+ setEdited(hasOneEditedEntry);
5257
5261
  });
5258
- setEdited(hasOneEditedEntry);
5259
- }, [entries]);
5262
+ return () => cancelAnimationFrame(scheduled);
5263
+ }, [entries, setEdited]);
5260
5264
 
5261
5265
  // set error state depending on all entries
5262
5266
  const allErrors = useErrors();
@@ -5684,7 +5688,11 @@ function createDragger(fn, dragPreview) {
5684
5688
  // (2) setup drag listeners
5685
5689
 
5686
5690
  // attach drag + cleanup event
5687
- document.addEventListener('dragover', onDrag);
5691
+ // we need to do this to make sure we track cursor
5692
+ // movements before we reach other drag event handlers,
5693
+ // e.g. in child containers.
5694
+ document.addEventListener('dragover', onDrag, true);
5695
+ document.addEventListener('dragenter', preventDefault, true);
5688
5696
  document.addEventListener('dragend', onEnd);
5689
5697
  document.addEventListener('drop', preventDefault);
5690
5698
  }
@@ -5698,7 +5706,8 @@ function createDragger(fn, dragPreview) {
5698
5706
  return fn.call(self, event, delta);
5699
5707
  }
5700
5708
  function onEnd() {
5701
- document.removeEventListener('dragover', onDrag);
5709
+ document.removeEventListener('dragover', onDrag, true);
5710
+ document.removeEventListener('dragenter', preventDefault, true);
5702
5711
  document.removeEventListener('dragend', onEnd);
5703
5712
  document.removeEventListener('drop', preventDefault);
5704
5713
  }
@@ -5729,8 +5738,9 @@ const noop$3 = () => {};
5729
5738
  * @param {boolean} [props.returnFocus]
5730
5739
  * @param {boolean} [props.closeOnEscape]
5731
5740
  * @param {string} props.title
5741
+ * @param {Ref} [ref]
5732
5742
  */
5733
- function Popup(props) {
5743
+ function PopupComponent(props, globalRef) {
5734
5744
  const {
5735
5745
  container,
5736
5746
  className,
@@ -5746,7 +5756,8 @@ function Popup(props) {
5746
5756
  title
5747
5757
  } = props;
5748
5758
  const focusTrapRef = useRef$1(null);
5749
- const popupRef = useRef$1(null);
5759
+ const localRef = useRef$1(null);
5760
+ const popupRef = globalRef || localRef;
5750
5761
  const handleKeydown = event => {
5751
5762
  // do not allow keyboard events to bubble
5752
5763
  event.stopPropagation();
@@ -5808,6 +5819,7 @@ function Popup(props) {
5808
5819
  children: props.children
5809
5820
  }), container || document.body);
5810
5821
  }
5822
+ const Popup = forwardRef(PopupComponent);
5811
5823
  Popup.Title = Title;
5812
5824
  Popup.Body = Body;
5813
5825
  Popup.Footer = Footer;
@@ -5816,6 +5828,7 @@ function Title(props) {
5816
5828
  children,
5817
5829
  className,
5818
5830
  draggable,
5831
+ emit = () => {},
5819
5832
  title,
5820
5833
  ...rest
5821
5834
  } = props;
@@ -5828,7 +5841,8 @@ function Title(props) {
5828
5841
  });
5829
5842
  const dragPreviewRef = useRef$1();
5830
5843
  const titleRef = useRef$1();
5831
- const onMove = throttle$1((_, delta) => {
5844
+ const onMove = (event, delta) => {
5845
+ cancel(event);
5832
5846
  const {
5833
5847
  x: dx,
5834
5848
  y: dy
@@ -5840,20 +5854,33 @@ function Title(props) {
5840
5854
  const popupParent = getPopupParent(titleRef.current);
5841
5855
  popupParent.style.top = newPosition.y + 'px';
5842
5856
  popupParent.style.left = newPosition.x + 'px';
5843
- });
5857
+
5858
+ // notify interested parties
5859
+ emit('dragover', {
5860
+ newPosition,
5861
+ delta
5862
+ });
5863
+ };
5844
5864
  const onMoveStart = event => {
5845
5865
  // initialize drag handler
5846
5866
  const onDragStart = createDragger(onMove, dragPreviewRef.current);
5847
5867
  onDragStart(event);
5868
+ event.stopPropagation();
5848
5869
  const popupParent = getPopupParent(titleRef.current);
5849
5870
  const bounds = popupParent.getBoundingClientRect();
5850
5871
  context.current.startPosition = {
5851
5872
  x: bounds.left,
5852
5873
  y: bounds.top
5853
5874
  };
5875
+
5876
+ // notify interested parties
5877
+ emit('dragstart');
5854
5878
  };
5855
5879
  const onMoveEnd = () => {
5856
5880
  context.current.newPosition = null;
5881
+
5882
+ // notify interested parties
5883
+ emit('dragend');
5857
5884
  };
5858
5885
  return jsxs("div", {
5859
5886
  class: classnames('bio-properties-panel-popup__header', draggable && 'draggable', className),
@@ -5906,11 +5933,19 @@ function Footer(props) {
5906
5933
  function getPopupParent(node) {
5907
5934
  return node.closest('.bio-properties-panel-popup');
5908
5935
  }
5936
+ function cancel(event) {
5937
+ event.preventDefault();
5938
+ event.stopPropagation();
5939
+ }
5909
5940
  const FEEL_POPUP_WIDTH = 700;
5910
5941
  const FEEL_POPUP_HEIGHT = 250;
5911
5942
 
5912
5943
  /**
5913
- * FEEL popup component, built as a singleton.
5944
+ * FEEL popup component, built as a singleton. Emits lifecycle events as follows:
5945
+ * - `feelPopup.open` - fired before the popup is mounted
5946
+ * - `feelPopup.opened` - fired after the popup is mounted. Event context contains the DOM node of the popup
5947
+ * - `feelPopup.close` - fired before the popup is unmounted. Event context contains the DOM node of the popup
5948
+ * - `feelPopup.closed` - fired after the popup is unmounted
5914
5949
  */
5915
5950
  function FEELPopupRoot(props) {
5916
5951
  const {
@@ -5933,17 +5968,21 @@ function FEELPopupRoot(props) {
5933
5968
  const isOpen = useCallback(() => {
5934
5969
  return !!open;
5935
5970
  }, [open]);
5971
+ useUpdateEffect(() => {
5972
+ if (!open) {
5973
+ emit('closed');
5974
+ }
5975
+ }, [open]);
5936
5976
  const handleOpen = (entryId, config, _sourceElement) => {
5937
5977
  setSource(entryId);
5938
5978
  setPopupConfig(config);
5939
5979
  setOpen(true);
5940
5980
  setSourceElement(_sourceElement);
5941
- emit('opened');
5981
+ emit('open');
5942
5982
  };
5943
5983
  const handleClose = () => {
5944
5984
  setOpen(false);
5945
5985
  setSource(null);
5946
- emit('closed');
5947
5986
  };
5948
5987
  const feelPopupContext = {
5949
5988
  open: handleOpen,
@@ -5986,6 +6025,7 @@ function FEELPopupRoot(props) {
5986
6025
  onClose: handleClose,
5987
6026
  container: popupContainer,
5988
6027
  sourceElement: sourceElement,
6028
+ emit: emit,
5989
6029
  ...popupConfig
5990
6030
  }), props.children]
5991
6031
  });
@@ -6004,9 +6044,11 @@ function FeelPopupComponent(props) {
6004
6044
  tooltipContainer,
6005
6045
  type,
6006
6046
  value,
6007
- variables
6047
+ variables,
6048
+ emit
6008
6049
  } = props;
6009
6050
  const editorRef = useRef$1();
6051
+ const popupRef = useRef$1();
6010
6052
  const isAutoCompletionOpen = useRef$1(false);
6011
6053
  const handleSetReturnFocus = () => {
6012
6054
  sourceElement && sourceElement.focus();
@@ -6029,9 +6071,18 @@ function FeelPopupComponent(props) {
6029
6071
  }
6030
6072
  }
6031
6073
  };
6074
+ useEffect(() => {
6075
+ emit('opened', {
6076
+ domNode: popupRef.current
6077
+ });
6078
+ return () => emit('close', {
6079
+ domNode: popupRef.current
6080
+ });
6081
+ }, []);
6032
6082
  return jsxs(Popup, {
6033
6083
  container: container,
6034
6084
  className: "bio-properties-panel-feel-popup",
6085
+ emit: emit,
6035
6086
  position: position,
6036
6087
  title: title,
6037
6088
  onClose: onClose
@@ -6045,8 +6096,10 @@ function FeelPopupComponent(props) {
6045
6096
  onPostDeactivate: handleSetReturnFocus,
6046
6097
  height: FEEL_POPUP_HEIGHT,
6047
6098
  width: FEEL_POPUP_WIDTH,
6099
+ ref: popupRef,
6048
6100
  children: [jsx(Popup.Title, {
6049
6101
  title: title,
6102
+ emit: emit,
6050
6103
  draggable: true
6051
6104
  }), jsx(Popup.Body, {
6052
6105
  children: jsxs("div", {
@@ -6096,6 +6149,23 @@ function prefixId$8(id) {
6096
6149
  function autoCompletionOpen(element) {
6097
6150
  return element.closest('.cm-editor').querySelector('.cm-tooltip-autocomplete');
6098
6151
  }
6152
+
6153
+ /**
6154
+ * This hook behaves like useEffect, but does not trigger on the first render.
6155
+ *
6156
+ * @param {Function} effect
6157
+ * @param {Array} deps
6158
+ */
6159
+ function useUpdateEffect(effect, deps) {
6160
+ const isMounted = useRef$1(false);
6161
+ useEffect(() => {
6162
+ if (isMounted.current) {
6163
+ return effect();
6164
+ } else {
6165
+ isMounted.current = true;
6166
+ }
6167
+ }, deps);
6168
+ }
6099
6169
  function ToggleSwitch(props) {
6100
6170
  const {
6101
6171
  id,
@@ -6991,7 +7061,7 @@ function calculatePopupPosition(element) {
6991
7061
 
6992
7062
  // todo(pinussilvestrus): make this configurable in the future
6993
7063
  function getPopupTitle(element, label) {
6994
- let popupTitle;
7064
+ let popupTitle = '';
6995
7065
  if (element && element.type) {
6996
7066
  popupTitle = `${element.type} / `;
6997
7067
  }