@bpmn-io/properties-panel 3.5.0 → 3.7.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.esm.js CHANGED
@@ -1132,6 +1132,7 @@ const noop$3 = () => {};
1132
1132
  * @param {Object} props
1133
1133
  * @param {HTMLElement} [props.container]
1134
1134
  * @param {string} [props.className]
1135
+ * @param {boolean} [props.delayInitialFocus]
1135
1136
  * @param {{x: number, y: number}} [props.position]
1136
1137
  * @param {number} [props.width]
1137
1138
  * @param {number} [props.height]
@@ -1139,12 +1140,14 @@ const noop$3 = () => {};
1139
1140
  * @param {Function} [props.onPostActivate]
1140
1141
  * @param {Function} [props.onPostDeactivate]
1141
1142
  * @param {boolean} [props.returnFocus]
1143
+ * @param {boolean} [props.closeOnEscape]
1142
1144
  * @param {string} props.title
1143
1145
  */
1144
1146
  function Popup(props) {
1145
1147
  const {
1146
1148
  container,
1147
1149
  className,
1150
+ delayInitialFocus,
1148
1151
  position,
1149
1152
  width,
1150
1153
  height,
@@ -1152,12 +1155,15 @@ function Popup(props) {
1152
1155
  onPostActivate = noop$3,
1153
1156
  onPostDeactivate = noop$3,
1154
1157
  returnFocus = true,
1158
+ closeOnEscape = true,
1155
1159
  title
1156
1160
  } = props;
1157
1161
  const focusTrapRef = useRef(null);
1158
1162
  const popupRef = useRef(null);
1159
- const handleKeyPress = event => {
1160
- if (event.key === 'Escape') {
1163
+ const handleKeydown = event => {
1164
+ // do not allow keyboard events to bubble
1165
+ event.stopPropagation();
1166
+ if (closeOnEscape && event.key === 'Escape') {
1161
1167
  onClose();
1162
1168
  }
1163
1169
  };
@@ -1182,14 +1188,6 @@ function Popup(props) {
1182
1188
  if (height) {
1183
1189
  style.height = height + 'px';
1184
1190
  }
1185
- useEffect(() => {
1186
- if (popupRef.current) {
1187
- popupRef.current.addEventListener('keydown', handleKeyPress);
1188
- }
1189
- return () => {
1190
- popupRef.current.removeEventListener('keydown', handleKeyPress);
1191
- };
1192
- }, [popupRef]);
1193
1191
  useEffect(() => {
1194
1192
  if (popupRef.current) {
1195
1193
  popupRef.current.addEventListener('focusin', handleFocus);
@@ -1202,6 +1200,7 @@ function Popup(props) {
1202
1200
  if (popupRef.current) {
1203
1201
  focusTrapRef.current = focusTrap.createFocusTrap(popupRef.current, {
1204
1202
  clickOutsideDeactivates: true,
1203
+ delayInitialFocus,
1205
1204
  fallbackFocus: popupRef.current,
1206
1205
  onPostActivate,
1207
1206
  onPostDeactivate,
@@ -1215,6 +1214,7 @@ function Popup(props) {
1215
1214
  "aria-label": title,
1216
1215
  tabIndex: -1,
1217
1216
  ref: popupRef,
1217
+ onKeyDown: handleKeydown,
1218
1218
  role: "dialog",
1219
1219
  class: classnames('bio-properties-panel-popup', className),
1220
1220
  style: style,
@@ -1329,6 +1329,11 @@ const FEEL_POPUP_HEIGHT = 250;
1329
1329
  function FEELPopupRoot(props) {
1330
1330
  const {
1331
1331
  element,
1332
+ eventBus = {
1333
+ fire() {},
1334
+ on() {},
1335
+ off() {}
1336
+ },
1332
1337
  popupContainer
1333
1338
  } = props;
1334
1339
  const prevElement = usePrevious(element);
@@ -1336,15 +1341,23 @@ function FEELPopupRoot(props) {
1336
1341
  const [open, setOpen] = useState(false);
1337
1342
  const [source, setSource] = useState(null);
1338
1343
  const [sourceElement, setSourceElement] = useState(null);
1339
- const handleOpen = (key, config, _sourceElement) => {
1340
- setSource(key);
1344
+ const emit = (type, context) => {
1345
+ eventBus.fire('feelPopup.' + type, context);
1346
+ };
1347
+ const isOpen = useCallback(() => {
1348
+ return !!open;
1349
+ }, [open]);
1350
+ const handleOpen = (entryId, config, _sourceElement) => {
1351
+ setSource(entryId);
1341
1352
  setPopupConfig(config);
1342
1353
  setOpen(true);
1343
1354
  setSourceElement(_sourceElement);
1355
+ emit('opened');
1344
1356
  };
1345
1357
  const handleClose = () => {
1346
1358
  setOpen(false);
1347
1359
  setSource(null);
1360
+ emit('closed');
1348
1361
  };
1349
1362
  const feelPopupContext = {
1350
1363
  open: handleOpen,
@@ -1354,10 +1367,33 @@ function FEELPopupRoot(props) {
1354
1367
 
1355
1368
  // close popup on element change, cf. https://github.com/bpmn-io/properties-panel/issues/270
1356
1369
  useEffect(() => {
1357
- if (element && element !== prevElement) {
1370
+ if (element && prevElement && element !== prevElement) {
1358
1371
  handleClose();
1359
1372
  }
1360
1373
  }, [element]);
1374
+
1375
+ // allow close and open via events
1376
+ useEffect(() => {
1377
+ const handlePopupOpen = context => {
1378
+ const {
1379
+ entryId,
1380
+ popupConfig,
1381
+ sourceElement
1382
+ } = context;
1383
+ handleOpen(entryId, popupConfig, sourceElement);
1384
+ };
1385
+ const handleIsOpen = () => {
1386
+ return isOpen();
1387
+ };
1388
+ eventBus.on('feelPopup._close', handleClose);
1389
+ eventBus.on('feelPopup._open', handlePopupOpen);
1390
+ eventBus.on('feelPopup._isOpen', handleIsOpen);
1391
+ return () => {
1392
+ eventBus.off('feelPopup._close', handleClose);
1393
+ eventBus.off('feelPopup._open', handleOpen);
1394
+ eventBus.off('feelPopup._isOpen', handleIsOpen);
1395
+ };
1396
+ }, [eventBus, isOpen]);
1361
1397
  return jsxs(FeelPopupContext.Provider, {
1362
1398
  value: feelPopupContext,
1363
1399
  children: [open && jsx(FeelPopupComponent, {
@@ -1385,15 +1421,28 @@ function FeelPopupComponent(props) {
1385
1421
  variables
1386
1422
  } = props;
1387
1423
  const editorRef = useRef();
1424
+ const isAutoCompletionOpen = useRef(false);
1388
1425
  const handleSetReturnFocus = () => {
1389
1426
  sourceElement && sourceElement.focus();
1390
1427
  };
1391
- useEffect(() => {
1392
- const editor = editorRef.current;
1393
- if (editor) {
1394
- editor.focus();
1428
+ const onKeyDownCapture = event => {
1429
+ // we use capture here to make sure we handle the event before the editor does
1430
+ if (event.key === 'Escape') {
1431
+ isAutoCompletionOpen.current = autoCompletionOpen(event.target);
1432
+ }
1433
+ };
1434
+ const onKeyDown = event => {
1435
+ if (event.key === 'Escape') {
1436
+ // close popup only if auto completion is not open
1437
+ // we need to do check this because the editor is not
1438
+ // stop propagating the keydown event
1439
+ // cf. https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322/5
1440
+ if (!isAutoCompletionOpen.current) {
1441
+ onClose();
1442
+ isAutoCompletionOpen.current = false;
1443
+ }
1395
1444
  }
1396
- }, [editorRef, id]);
1445
+ };
1397
1446
  return jsxs(Popup, {
1398
1447
  container: container,
1399
1448
  className: "bio-properties-panel-feel-popup",
@@ -1404,6 +1453,8 @@ function FeelPopupComponent(props) {
1404
1453
  // handle focus manually on deactivate
1405
1454
  ,
1406
1455
  returnFocus: false,
1456
+ closeOnEscape: false,
1457
+ delayInitialFocus: false,
1407
1458
  onPostDeactivate: handleSetReturnFocus,
1408
1459
  height: FEEL_POPUP_HEIGHT,
1409
1460
  width: FEEL_POPUP_WIDTH,
@@ -1412,6 +1463,8 @@ function FeelPopupComponent(props) {
1412
1463
  draggable: true
1413
1464
  }), jsx(Popup.Body, {
1414
1465
  children: jsxs("div", {
1466
+ onKeyDownCapture: onKeyDownCapture,
1467
+ onKeyDown: onKeyDown,
1415
1468
  class: "bio-properties-panel-feel-popup__body",
1416
1469
  children: [type === 'feel' && jsx(CodeEditor, {
1417
1470
  enableGutters: true,
@@ -1453,6 +1506,9 @@ function FeelPopupComponent(props) {
1453
1506
  function prefixId$8(id) {
1454
1507
  return `bio-properties-panel-${id}`;
1455
1508
  }
1509
+ function autoCompletionOpen(element) {
1510
+ return element.closest('.cm-editor').querySelector('.cm-tooltip-autocomplete');
1511
+ }
1456
1512
 
1457
1513
  function ToggleSwitch(props) {
1458
1514
  const {
@@ -2202,7 +2258,7 @@ function FeelEntry(props) {
2202
2258
  setLocalError(err);
2203
2259
  }, []);
2204
2260
  const temporaryError = useError(id);
2205
- const error = localError || temporaryError || validationError;
2261
+ const error = temporaryError || localError || validationError;
2206
2262
  return jsxs("div", {
2207
2263
  class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
2208
2264
  "data-entry-id": id,
@@ -2611,6 +2667,7 @@ function PropertiesPanel(props) {
2611
2667
  value: eventContext,
2612
2668
  children: jsx(FEELPopupRoot, {
2613
2669
  element: element,
2670
+ eventBus: eventBus,
2614
2671
  popupContainer: feelPopupContainer,
2615
2672
  children: jsxs("div", {
2616
2673
  class: "bio-properties-panel",
@@ -4076,9 +4133,50 @@ function debounceInput(debounceDelay) {
4076
4133
  }
4077
4134
  debounceInput.$inject = ['config.debounceInput'];
4078
4135
 
4079
- var index = {
4136
+ var index$1 = {
4080
4137
  debounceInput: ['factory', debounceInput]
4081
4138
  };
4082
4139
 
4083
- export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DragIcon, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelCheckboxEntry, FeelEntry, FeelIcon$1 as FeelIcon, FeelNumberEntry, FeelTemplatingEntry, FeelTextAreaEntry, FeelToggleSwitchEntry, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, Placeholder, Popup, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TemplatingEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, TooltipContext, isEdited$5 as isCheckboxEntryEdited, isEdited$6 as isFeelEntryEdited, isEdited$7 as isNumberFieldEntryEdited, isEdited$3 as isSelectEntryEdited, isEdited$2 as isSimpleEntryEdited, isEdited$4 as isTemplatingEntryEdited, isEdited$1 as isTextAreaEntryEdited, isEdited as isTextFieldEntryEdited, isEdited$8 as isToggleSwitchEntryEdited, useDescriptionContext, useError, useErrors, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver, useTooltipContext };
4140
+ class FeelPopupModule {
4141
+ constructor(eventBus) {
4142
+ this._eventBus = eventBus;
4143
+ }
4144
+
4145
+ /**
4146
+ * Check if the FEEL popup is open.
4147
+ * @return {Boolean}
4148
+ */
4149
+ isOpen() {
4150
+ return this._eventBus.fire('feelPopup._isOpen');
4151
+ }
4152
+
4153
+ /**
4154
+ * Open the FEEL popup.
4155
+ *
4156
+ * @param {String} entryId
4157
+ * @param {Object} popupConfig
4158
+ * @param {HTMLElement} sourceElement
4159
+ */
4160
+ open(entryId, popupConfig, sourceElement) {
4161
+ return this._eventBus.fire('feelPopup._open', {
4162
+ entryId,
4163
+ popupConfig,
4164
+ sourceElement
4165
+ });
4166
+ }
4167
+
4168
+ /**
4169
+ * Close the FEEL popup.
4170
+ */
4171
+ close() {
4172
+ return this._eventBus.fire('feelPopup._close');
4173
+ }
4174
+ }
4175
+ FeelPopupModule.$inject = ['eventBus'];
4176
+
4177
+ var index = {
4178
+ feelPopup: ['type', FeelPopupModule]
4179
+ };
4180
+
4181
+ export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index$1 as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DragIcon, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelCheckboxEntry, FeelEntry, FeelIcon$1 as FeelIcon, FeelNumberEntry, index as FeelPopupModule, FeelTemplatingEntry, FeelTextAreaEntry, FeelToggleSwitchEntry, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, Placeholder, Popup, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TemplatingEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, TooltipContext, isEdited$5 as isCheckboxEntryEdited, isEdited$6 as isFeelEntryEdited, isEdited$7 as isNumberFieldEntryEdited, isEdited$3 as isSelectEntryEdited, isEdited$2 as isSimpleEntryEdited, isEdited$4 as isTemplatingEntryEdited, isEdited$1 as isTextAreaEntryEdited, isEdited as isTextFieldEntryEdited, isEdited$8 as isToggleSwitchEntryEdited, useDescriptionContext, useError, useErrors, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver, useTooltipContext };
4084
4182
  //# sourceMappingURL=index.esm.js.map