@bpmn-io/properties-panel 1.4.0 → 1.5.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.js CHANGED
@@ -10,6 +10,7 @@ var jsxRuntime = require('../preact/jsx-runtime');
10
10
  var minDom = require('min-dom');
11
11
  var preact = require('../preact');
12
12
  var FeelEditor = require('@bpmn-io/feel-editor');
13
+ var feelers = require('feelers');
13
14
 
14
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
15
16
 
@@ -175,19 +176,19 @@ const ErrorsContext = preact.createContext({
175
176
  errors: {}
176
177
  });
177
178
 
178
- /**
179
- * @typedef {Function} <propertiesPanel.showEntry> callback
180
- *
181
- * @example
182
- *
183
- * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
184
- * // ...
185
- * });
186
- *
187
- * @param {Object} context
188
- * @param {boolean} [context.focus]
189
- *
190
- * @returns void
179
+ /**
180
+ * @typedef {Function} <propertiesPanel.showEntry> callback
181
+ *
182
+ * @example
183
+ *
184
+ * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
185
+ * // ...
186
+ * });
187
+ *
188
+ * @param {Object} context
189
+ * @param {boolean} [context.focus]
190
+ *
191
+ * @returns void
191
192
  */
192
193
  const EventContext = preact.createContext({
193
194
  eventBus: null
@@ -200,20 +201,20 @@ const LayoutContext = preact.createContext({
200
201
  setLayoutForKey: () => {}
201
202
  });
202
203
 
203
- /**
204
- * Accesses the global DescriptionContext and returns a description for a given id and element.
205
- *
206
- * @example
207
- * ```jsx
208
- * function TextField(props) {
209
- * const description = useDescriptionContext('input1', element);
210
- * }
211
- * ```
212
- *
213
- * @param {string} id
214
- * @param {object} element
215
- *
216
- * @returns {string}
204
+ /**
205
+ * Accesses the global DescriptionContext and returns a description for a given id and element.
206
+ *
207
+ * @example
208
+ * ```jsx
209
+ * function TextField(props) {
210
+ * const description = useDescriptionContext('input1', element);
211
+ * }
212
+ * ```
213
+ *
214
+ * @param {string} id
215
+ * @param {object} element
216
+ *
217
+ * @returns {string}
217
218
  */
218
219
  function useDescriptionContext(id, element) {
219
220
  const {
@@ -229,11 +230,11 @@ function useError(id) {
229
230
  return errors[id];
230
231
  }
231
232
 
232
- /**
233
- * Subscribe to an event immediately. Update subscription after inputs changed.
234
- *
235
- * @param {string} event
236
- * @param {Function} callback
233
+ /**
234
+ * Subscribe to an event immediately. Update subscription after inputs changed.
235
+ *
236
+ * @param {string} event
237
+ * @param {Function} callback
237
238
  */
238
239
  function useEvent(event, callback, eventBus) {
239
240
  const eventContext = hooks.useContext(EventContext);
@@ -265,24 +266,24 @@ function useEvent(event, callback, eventBus) {
265
266
 
266
267
  const KEY_LENGTH = 6;
267
268
 
268
- /**
269
- * Create a persistent key factory for plain objects without id.
270
- *
271
- * @example
272
- * ```jsx
273
- * function List({ objects }) {
274
- * const getKey = useKeyFactory();
275
- * return (<ol>{
276
- * objects.map(obj => {
277
- * const key = getKey(obj);
278
- * return <li key={key}>obj.name</li>
279
- * })
280
- * }</ol>);
281
- * }
282
- * ```
283
- *
284
- * @param {any[]} dependencies
285
- * @returns {(element: object) => string}
269
+ /**
270
+ * Create a persistent key factory for plain objects without id.
271
+ *
272
+ * @example
273
+ * ```jsx
274
+ * function List({ objects }) {
275
+ * const getKey = useKeyFactory();
276
+ * return (<ol>{
277
+ * objects.map(obj => {
278
+ * const key = getKey(obj);
279
+ * return <li key={key}>obj.name</li>
280
+ * })
281
+ * }</ol>);
282
+ * }
283
+ * ```
284
+ *
285
+ * @param {any[]} dependencies
286
+ * @returns {(element: object) => string}
286
287
  */
287
288
  function useKeyFactory(dependencies = []) {
288
289
  const map = hooks.useMemo(() => new Map(), dependencies);
@@ -297,20 +298,20 @@ function useKeyFactory(dependencies = []) {
297
298
  return getKey;
298
299
  }
299
300
 
300
- /**
301
- * Creates a state that persists in the global LayoutContext.
302
- *
303
- * @example
304
- * ```jsx
305
- * function Group(props) {
306
- * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
307
- * }
308
- * ```
309
- *
310
- * @param {(string|number)[]} path
311
- * @param {any} [defaultValue]
312
- *
313
- * @returns {[ any, Function ]}
301
+ /**
302
+ * Creates a state that persists in the global LayoutContext.
303
+ *
304
+ * @example
305
+ * ```jsx
306
+ * function Group(props) {
307
+ * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
308
+ * }
309
+ * ```
310
+ *
311
+ * @param {(string|number)[]} path
312
+ * @param {any} [defaultValue]
313
+ *
314
+ * @returns {[ any, Function ]}
314
315
  */
315
316
  function useLayoutState(path, defaultValue) {
316
317
  const {
@@ -318,22 +319,17 @@ function useLayoutState(path, defaultValue) {
318
319
  setLayoutForKey
319
320
  } = hooks.useContext(LayoutContext);
320
321
  const layoutForKey = getLayoutForKey(path, defaultValue);
321
- const [value, set] = hooks.useState(layoutForKey);
322
- const setState = newValue => {
323
- // (1) set component state
324
- set(newValue);
325
-
326
- // (2) set context
322
+ const setState = hooks.useCallback(newValue => {
327
323
  setLayoutForKey(path, newValue);
328
- };
329
- return [value, setState];
324
+ }, [setLayoutForKey]);
325
+ return [layoutForKey, setState];
330
326
  }
331
327
 
332
- /**
333
- * @pinussilvestrus: we need to introduce our own hook to persist the previous
334
- * state on updates.
335
- *
336
- * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
328
+ /**
329
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
330
+ * state on updates.
331
+ *
332
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
337
333
  */
338
334
 
339
335
  function usePrevious(value) {
@@ -344,12 +340,12 @@ function usePrevious(value) {
344
340
  return ref.current;
345
341
  }
346
342
 
347
- /**
348
- * Subscribe to `propertiesPanel.showEntry`.
349
- *
350
- * @param {string} id
351
- *
352
- * @returns {import('preact').Ref}
343
+ /**
344
+ * Subscribe to `propertiesPanel.showEntry`.
345
+ *
346
+ * @param {string} id
347
+ *
348
+ * @returns {import('preact').Ref}
353
349
  */
354
350
  function useShowEntryEvent(id) {
355
351
  const {
@@ -380,20 +376,20 @@ function useShowEntryEvent(id) {
380
376
  return ref;
381
377
  }
382
378
 
383
- /**
384
- * @callback setSticky
385
- * @param {boolean} value
379
+ /**
380
+ * @callback setSticky
381
+ * @param {boolean} value
386
382
  */
387
383
 
388
- /**
389
- * Use IntersectionObserver to identify when DOM element is in sticky mode.
390
- * If sticky is observered setSticky(true) will be called.
391
- * If sticky mode is left, setSticky(false) will be called.
392
- *
393
- *
394
- * @param {Object} ref
395
- * @param {string} scrollContainerSelector
396
- * @param {setSticky} setSticky
384
+ /**
385
+ * Use IntersectionObserver to identify when DOM element is in sticky mode.
386
+ * If sticky is observered setSticky(true) will be called.
387
+ * If sticky mode is left, setSticky(false) will be called.
388
+ *
389
+ *
390
+ * @param {Object} ref
391
+ * @param {string} scrollContainerSelector
392
+ * @param {setSticky} setSticky
397
393
  */
398
394
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
399
395
  hooks.useEffect(() => {
@@ -432,19 +428,19 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
432
428
  }, [ref, scrollContainerSelector, setSticky]);
433
429
  }
434
430
 
435
- /**
436
- * Creates a static function reference with changing body.
437
- * This is necessary when external libraries require a callback function
438
- * that has references to state variables.
439
- *
440
- * Usage:
441
- * const callback = useStaticCallback((val) => {val === currentState});
442
- *
443
- * The `callback` reference is static and can be safely used in external
444
- * libraries or as a prop that does not cause rerendering of children.
445
- *
446
- * @param {Function} callback function with changing reference
447
- * @returns {Function} static function reference
431
+ /**
432
+ * Creates a static function reference with changing body.
433
+ * This is necessary when external libraries require a callback function
434
+ * that has references to state variables.
435
+ *
436
+ * Usage:
437
+ * const callback = useStaticCallback((val) => {val === currentState});
438
+ *
439
+ * The `callback` reference is static and can be safely used in external
440
+ * libraries or as a prop that does not cause rerendering of children.
441
+ *
442
+ * @param {Function} callback function with changing reference
443
+ * @returns {Function} static function reference
448
444
  */
449
445
  function useStaticCallback(callback) {
450
446
  const callbackRef = hooks.useRef(callback);
@@ -537,13 +533,13 @@ function DataMarker() {
537
533
  });
538
534
  }
539
535
 
540
- /**
541
- * @typedef { {
542
- * text: (element: object) => string,
543
- * icon?: (element: Object) => import('preact').Component
544
- * } } PlaceholderDefinition
545
- *
546
- * @param { PlaceholderDefinition } props
536
+ /**
537
+ * @typedef { {
538
+ * text: (element: object) => string,
539
+ * icon?: (element: Object) => import('preact').Component
540
+ * } } PlaceholderDefinition
541
+ *
542
+ * @param { PlaceholderDefinition } props
547
543
  */
548
544
  function Placeholder(props) {
549
545
  const {
@@ -569,72 +565,72 @@ const DEFAULT_LAYOUT = {
569
565
  };
570
566
  const DEFAULT_DESCRIPTION = {};
571
567
 
572
- /**
573
- * @typedef { {
574
- * component: import('preact').Component,
575
- * id: String,
576
- * isEdited?: Function
577
- * } } EntryDefinition
578
- *
579
- * @typedef { {
580
- * autoFocusEntry: String,
581
- * autoOpen?: Boolean,
582
- * entries: Array<EntryDefinition>,
583
- * id: String,
584
- * label: String,
585
- * remove: (event: MouseEvent) => void
586
- * } } ListItemDefinition
587
- *
588
- * @typedef { {
589
- * add: (event: MouseEvent) => void,
590
- * component: import('preact').Component,
591
- * element: Object,
592
- * id: String,
593
- * items: Array<ListItemDefinition>,
594
- * label: String,
595
- * shouldSort?: Boolean,
596
- * shouldOpen?: Boolean
597
- * } } ListGroupDefinition
598
- *
599
- * @typedef { {
600
- * component?: import('preact').Component,
601
- * entries: Array<EntryDefinition>,
602
- * id: String,
603
- * label: String,
604
- * shouldOpen?: Boolean
605
- * } } GroupDefinition
606
- *
607
- * @typedef { {
608
- * [id: String]: GetDescriptionFunction
609
- * } } DescriptionConfig
610
- *
611
- * @callback { {
612
- * @param {string} id
613
- * @param {Object} element
614
- * @returns {string}
615
- * } } GetDescriptionFunction
616
- *
617
- * @typedef { {
618
- * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
619
- * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
620
- * } } PlaceholderProvider
621
- *
568
+ /**
569
+ * @typedef { {
570
+ * component: import('preact').Component,
571
+ * id: String,
572
+ * isEdited?: Function
573
+ * } } EntryDefinition
574
+ *
575
+ * @typedef { {
576
+ * autoFocusEntry: String,
577
+ * autoOpen?: Boolean,
578
+ * entries: Array<EntryDefinition>,
579
+ * id: String,
580
+ * label: String,
581
+ * remove: (event: MouseEvent) => void
582
+ * } } ListItemDefinition
583
+ *
584
+ * @typedef { {
585
+ * add: (event: MouseEvent) => void,
586
+ * component: import('preact').Component,
587
+ * element: Object,
588
+ * id: String,
589
+ * items: Array<ListItemDefinition>,
590
+ * label: String,
591
+ * shouldSort?: Boolean,
592
+ * shouldOpen?: Boolean
593
+ * } } ListGroupDefinition
594
+ *
595
+ * @typedef { {
596
+ * component?: import('preact').Component,
597
+ * entries: Array<EntryDefinition>,
598
+ * id: String,
599
+ * label: String,
600
+ * shouldOpen?: Boolean
601
+ * } } GroupDefinition
602
+ *
603
+ * @typedef { {
604
+ * [id: String]: GetDescriptionFunction
605
+ * } } DescriptionConfig
606
+ *
607
+ * @callback { {
608
+ * @param {string} id
609
+ * @param {Object} element
610
+ * @returns {string}
611
+ * } } GetDescriptionFunction
612
+ *
613
+ * @typedef { {
614
+ * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
615
+ * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
616
+ * } } PlaceholderProvider
617
+ *
622
618
  */
623
619
 
624
- /**
625
- * A basic properties panel component. Describes *how* content will be rendered, accepts
626
- * data from implementor to describe *what* will be rendered.
627
- *
628
- * @param {Object} props
629
- * @param {Object|Array} props.element
630
- * @param {import('./components/Header').HeaderProvider} props.headerProvider
631
- * @param {PlaceholderProvider} [props.placeholderProvider]
632
- * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
633
- * @param {Object} [props.layoutConfig]
634
- * @param {Function} [props.layoutChanged]
635
- * @param {DescriptionConfig} [props.descriptionConfig]
636
- * @param {Function} [props.descriptionLoaded]
637
- * @param {Object} [props.eventBus]
620
+ /**
621
+ * A basic properties panel component. Describes *how* content will be rendered, accepts
622
+ * data from implementor to describe *what* will be rendered.
623
+ *
624
+ * @param {Object} props
625
+ * @param {Object|Array} props.element
626
+ * @param {import('./components/Header').HeaderProvider} props.headerProvider
627
+ * @param {PlaceholderProvider} [props.placeholderProvider]
628
+ * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
629
+ * @param {Object} [props.layoutConfig]
630
+ * @param {Function} [props.layoutChanged]
631
+ * @param {DescriptionConfig} [props.descriptionConfig]
632
+ * @param {Function} [props.descriptionLoaded]
633
+ * @param {Object} [props.eventBus]
638
634
  */
639
635
  function PropertiesPanel(props) {
640
636
  const {
@@ -651,6 +647,12 @@ function PropertiesPanel(props) {
651
647
 
652
648
  // set-up layout context
653
649
  const [layout, setLayout] = hooks.useState(createLayout(layoutConfig));
650
+
651
+ // react to external changes in the layout config
652
+ hooks.useEffect(() => {
653
+ const newLayout = createLayout(layoutConfig);
654
+ setLayout(newLayout);
655
+ }, [layoutConfig]);
654
656
  hooks.useEffect(() => {
655
657
  if (typeof layoutChanged === 'function') {
656
658
  layoutChanged(layout);
@@ -750,9 +752,9 @@ function PropertiesPanel(props) {
750
752
 
751
753
  // helpers //////////////////
752
754
 
753
- function createLayout(overrides) {
755
+ function createLayout(overrides, defaults = DEFAULT_LAYOUT) {
754
756
  return {
755
- ...DEFAULT_LAYOUT,
757
+ ...defaults,
756
758
  ...overrides
757
759
  };
758
760
  }
@@ -824,15 +826,15 @@ function MenuItem({
824
826
  });
825
827
  }
826
828
 
827
- /**
828
- *
829
- * @param {Array<null | Element>} ignoredElements
830
- * @param {Function} callback
829
+ /**
830
+ *
831
+ * @param {Array<null | Element>} ignoredElements
832
+ * @param {Function} callback
831
833
  */
832
834
  function useGlobalClick(ignoredElements, callback) {
833
835
  hooks.useEffect(() => {
834
- /**
835
- * @param {MouseEvent} event
836
+ /**
837
+ * @param {MouseEvent} event
836
838
  */
837
839
  function listener(event) {
838
840
  if (ignoredElements.some(element => element && element.contains(event.target))) {
@@ -961,10 +963,10 @@ function ListItem(props) {
961
963
  });
962
964
  }
963
965
 
964
- const noop$2 = () => {};
966
+ const noop$3 = () => {};
965
967
 
966
- /**
967
- * @param {import('../PropertiesPanel').ListGroupDefinition} props
968
+ /**
969
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
968
970
  */
969
971
  function ListGroup(props) {
970
972
  const {
@@ -1070,7 +1072,7 @@ function ListGroup(props) {
1070
1072
  ref: groupRef,
1071
1073
  children: [jsxRuntime.jsxs("div", {
1072
1074
  class: classnames__default["default"]('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
1073
- onClick: hasItems ? toggleOpen : noop$2,
1075
+ onClick: hasItems ? toggleOpen : noop$3,
1074
1076
  children: [jsxRuntime.jsx("div", {
1075
1077
  title: label,
1076
1078
  class: "bio-properties-panel-group-header-title",
@@ -1127,8 +1129,8 @@ function ListGroup(props) {
1127
1129
 
1128
1130
  // helpers ////////////////////
1129
1131
 
1130
- /**
1131
- * Sorts given items alphanumeric by label
1132
+ /**
1133
+ * Sorts given items alphanumeric by label
1132
1134
  */
1133
1135
  function sortItems(items) {
1134
1136
  return minDash.sortBy(items, i => i.label.toLowerCase());
@@ -1204,17 +1206,17 @@ function Checkbox(props) {
1204
1206
  });
1205
1207
  }
1206
1208
 
1207
- /**
1208
- * @param {Object} props
1209
- * @param {Object} props.element
1210
- * @param {String} props.id
1211
- * @param {String} props.description
1212
- * @param {String} props.label
1213
- * @param {Function} props.getValue
1214
- * @param {Function} props.setValue
1215
- * @param {Function} props.onFocus
1216
- * @param {Function} props.onBlur
1217
- * @param {boolean} [props.disabled]
1209
+ /**
1210
+ * @param {Object} props
1211
+ * @param {Object} props.element
1212
+ * @param {String} props.id
1213
+ * @param {String} props.description
1214
+ * @param {String} props.label
1215
+ * @param {Function} props.getValue
1216
+ * @param {Function} props.setValue
1217
+ * @param {Function} props.onFocus
1218
+ * @param {Function} props.onBlur
1219
+ * @param {boolean} [props.disabled]
1218
1220
  */
1219
1221
  function CheckboxEntry(props) {
1220
1222
  const {
@@ -1251,7 +1253,7 @@ function CheckboxEntry(props) {
1251
1253
  })]
1252
1254
  });
1253
1255
  }
1254
- function isEdited$7(node) {
1256
+ function isEdited$8(node) {
1255
1257
  return node && !!node.checked;
1256
1258
  }
1257
1259
 
@@ -1261,7 +1263,7 @@ function prefixId$7(id) {
1261
1263
  return `bio-properties-panel-${id}`;
1262
1264
  }
1263
1265
 
1264
- const useBufferedFocus = function (editor, ref) {
1266
+ const useBufferedFocus$1 = function (editor, ref) {
1265
1267
  const [buffer, setBuffer] = hooks.useState(undefined);
1266
1268
  ref.current = hooks.useMemo(() => ({
1267
1269
  focus: offset => {
@@ -1282,7 +1284,7 @@ const useBufferedFocus = function (editor, ref) {
1282
1284
  }
1283
1285
  }, [editor, buffer]);
1284
1286
  };
1285
- const CodeEditor = compat.forwardRef((props, ref) => {
1287
+ const CodeEditor$1 = compat.forwardRef((props, ref) => {
1286
1288
  const {
1287
1289
  value,
1288
1290
  onInput,
@@ -1295,7 +1297,7 @@ const CodeEditor = compat.forwardRef((props, ref) => {
1295
1297
  const inputRef = hooks.useRef();
1296
1298
  const [editor, setEditor] = hooks.useState();
1297
1299
  const [localValue, setLocalValue] = hooks.useState(value || '');
1298
- useBufferedFocus(editor, ref);
1300
+ useBufferedFocus$1(editor, ref);
1299
1301
  const handleInput = useStaticCallback(newValue => {
1300
1302
  onInput(newValue);
1301
1303
  setLocalValue(newValue);
@@ -1303,10 +1305,10 @@ const CodeEditor = compat.forwardRef((props, ref) => {
1303
1305
  hooks.useEffect(() => {
1304
1306
  let editor;
1305
1307
 
1306
- /* Trigger FEEL toggle when
1307
- *
1308
- * - `backspace` is pressed
1309
- * - AND the cursor is at the beginning of the input
1308
+ /* Trigger FEEL toggle when
1309
+ *
1310
+ * - `backspace` is pressed
1311
+ * - AND the cursor is at the beginning of the input
1310
1312
  */
1311
1313
  const onKeyDown = e => {
1312
1314
  if (e.key !== 'Backspace' || !editor) {
@@ -1377,12 +1379,12 @@ function FeelIndicator(props) {
1377
1379
  });
1378
1380
  }
1379
1381
 
1380
- const noop$1 = () => {};
1382
+ const noop$2 = () => {};
1381
1383
 
1382
- /**
1383
- * @param {Object} props
1384
- * @param {Object} props.label
1385
- * @param {String} props.feel
1384
+ /**
1385
+ * @param {Object} props
1386
+ * @param {Object} props.label
1387
+ * @param {String} props.feel
1386
1388
  */
1387
1389
  function FeelIcon(props) {
1388
1390
  const {
@@ -1390,7 +1392,7 @@ function FeelIcon(props) {
1390
1392
  feel = false,
1391
1393
  active,
1392
1394
  disabled = false,
1393
- onClick = noop$1
1395
+ onClick = noop$2
1394
1396
  } = props;
1395
1397
  const feelRequiredLabel = ' must be a FEEL expression';
1396
1398
  const feelOptionalLabel = ' can optionally be a FEEL expression';
@@ -1411,7 +1413,7 @@ function FeelIcon(props) {
1411
1413
  });
1412
1414
  }
1413
1415
 
1414
- const noop = () => {};
1416
+ const noop$1 = () => {};
1415
1417
  function FeelTextfield(props) {
1416
1418
  const {
1417
1419
  debounce,
@@ -1551,7 +1553,7 @@ function FeelTextfield(props) {
1551
1553
  active: feelActive,
1552
1554
  disabled: feel !== 'optional' || disabled,
1553
1555
  onClick: handleFeelToggle
1554
- }), feelActive ? jsxRuntime.jsx(CodeEditor, {
1556
+ }), feelActive ? jsxRuntime.jsx(CodeEditor$1, {
1555
1557
  id: prefixId$6(id),
1556
1558
  name: id,
1557
1559
  onInput: handleLocalInput,
@@ -1657,17 +1659,24 @@ const OptionalFeelTextArea = compat.forwardRef((props, ref) => {
1657
1659
  });
1658
1660
  });
1659
1661
 
1660
- /**
1661
- * @param {Object} props
1662
- * @param {Object} props.element
1663
- * @param {String} props.id
1664
- * @param {String} props.description
1665
- * @param {Boolean} props.debounce
1666
- * @param {Boolean} props.disabled
1667
- * @param {String} props.label
1668
- * @param {Function} props.getValue
1669
- * @param {Function} props.setValue
1670
- * @param {Function} props.validate
1662
+ /**
1663
+ * @param {Object} props
1664
+ * @param {Object} props.element
1665
+ * @param {String} props.id
1666
+ * @param {String} props.description
1667
+ * @param {Boolean} props.debounce
1668
+ * @param {Boolean} props.disabled
1669
+ * @param {Boolean} props.feel
1670
+ * @param {String} props.label
1671
+ * @param {Function} props.getValue
1672
+ * @param {Function} props.setValue
1673
+ * @param {Function} props.tooltipContainer
1674
+ * @param {Function} props.validate
1675
+ * @param {Function} props.show
1676
+ * @param {Function} props.example
1677
+ * @param {Function} props.variables
1678
+ * @param {Function} props.onFocus
1679
+ * @param {Function} props.onBlur
1671
1680
  */
1672
1681
  function FeelEntry(props) {
1673
1682
  const {
@@ -1682,7 +1691,7 @@ function FeelEntry(props) {
1682
1691
  setValue,
1683
1692
  tooltipContainer,
1684
1693
  validate,
1685
- show = noop,
1694
+ show = noop$1,
1686
1695
  example,
1687
1696
  variables,
1688
1697
  onFocus,
@@ -1752,19 +1761,24 @@ function FeelEntry(props) {
1752
1761
  });
1753
1762
  }
1754
1763
 
1755
- /**
1756
- * @param {Object} props
1757
- * @param {Object} props.element
1758
- * @param {String} props.id
1759
- * @param {String} props.description
1760
- * @param {Boolean} props.debounce
1761
- * @param {Boolean} props.disabled
1762
- * @param {String} props.label
1763
- * @param {Function} props.getValue
1764
- * @param {Function} props.setValue
1765
- * @param {Function} props.onFocus
1766
- * @param {Function} props.onBlur
1767
- * @param {Function} props.validate
1764
+ /**
1765
+ * @param {Object} props
1766
+ * @param {Object} props.element
1767
+ * @param {String} props.id
1768
+ * @param {String} props.description
1769
+ * @param {Boolean} props.debounce
1770
+ * @param {Boolean} props.disabled
1771
+ * @param {Boolean} props.feel
1772
+ * @param {String} props.label
1773
+ * @param {Function} props.getValue
1774
+ * @param {Function} props.setValue
1775
+ * @param {Function} props.tooltipContainer
1776
+ * @param {Function} props.validate
1777
+ * @param {Function} props.show
1778
+ * @param {Function} props.example
1779
+ * @param {Function} props.variables
1780
+ * @param {Function} props.onFocus
1781
+ * @param {Function} props.onBlur
1768
1782
  */
1769
1783
  function FeelTextArea(props) {
1770
1784
  return jsxRuntime.jsx(FeelEntry, {
@@ -1773,7 +1787,7 @@ function FeelTextArea(props) {
1773
1787
  ...props
1774
1788
  });
1775
1789
  }
1776
- function isEdited$6(node) {
1790
+ function isEdited$7(node) {
1777
1791
  return node && (!!node.value || node.classList.contains('edited'));
1778
1792
  }
1779
1793
 
@@ -1783,6 +1797,260 @@ function prefixId$6(id) {
1783
1797
  return `bio-properties-panel-${id}`;
1784
1798
  }
1785
1799
 
1800
+ const useBufferedFocus = function (editor, ref) {
1801
+ const [buffer, setBuffer] = hooks.useState(undefined);
1802
+ ref.current = hooks.useMemo(() => ({
1803
+ focus: offset => {
1804
+ if (editor) {
1805
+ editor.focus(offset);
1806
+ } else {
1807
+ if (typeof offset === 'undefined') {
1808
+ offset = Infinity;
1809
+ }
1810
+ setBuffer(offset);
1811
+ }
1812
+ }
1813
+ }), [editor]);
1814
+ hooks.useEffect(() => {
1815
+ if (typeof buffer !== 'undefined' && editor) {
1816
+ editor.focus(buffer);
1817
+ setBuffer(false);
1818
+ }
1819
+ }, [editor, buffer]);
1820
+ };
1821
+ const CodeEditor = compat.forwardRef((props, ref) => {
1822
+ const {
1823
+ value,
1824
+ onInput,
1825
+ onLint = () => {},
1826
+ contentAttributes = {},
1827
+ disabled,
1828
+ tooltipContainer,
1829
+ useGutters = false,
1830
+ darkMode = false
1831
+ } = props;
1832
+ const inputRef = hooks.useRef();
1833
+ const [editor, setEditor] = hooks.useState();
1834
+ const [localValue, setLocalValue] = hooks.useState(value || '');
1835
+ useBufferedFocus(editor, ref);
1836
+ const handleInput = useStaticCallback(newValue => {
1837
+ onInput(newValue);
1838
+ setLocalValue(newValue);
1839
+ });
1840
+ hooks.useEffect(() => {
1841
+ let editor;
1842
+ editor = new feelers.FeelersEditor({
1843
+ container: inputRef.current,
1844
+ onChange: handleInput,
1845
+ onLint: onLint,
1846
+ contentAttributes: contentAttributes,
1847
+ tooltipContainer: tooltipContainer,
1848
+ value: localValue,
1849
+ darkMode: darkMode,
1850
+ enableGutters: useGutters
1851
+ });
1852
+ setEditor(editor);
1853
+ return () => {
1854
+ onLint([]);
1855
+ inputRef.current.innerHTML = '';
1856
+ setEditor(null);
1857
+ };
1858
+ }, []);
1859
+ hooks.useEffect(() => {
1860
+ if (!editor) {
1861
+ return;
1862
+ }
1863
+ if (value === localValue) {
1864
+ return;
1865
+ }
1866
+ editor.setValue(value);
1867
+ setLocalValue(value);
1868
+ }, [value]);
1869
+ const handleClick = () => {
1870
+ ref.current.focus();
1871
+ };
1872
+ return jsxRuntime.jsx("div", {
1873
+ name: props.name,
1874
+ class: classnames__default["default"]('bio-properties-panel-feelers-editor bio-properties-panel-input', localValue ? 'edited' : null, disabled ? 'disabled' : null),
1875
+ ref: inputRef,
1876
+ onClick: handleClick
1877
+ });
1878
+ });
1879
+
1880
+ const noop = () => {};
1881
+
1882
+ /**
1883
+ * @param {Object} props
1884
+ * @param {Object} props.element
1885
+ * @param {String} props.id
1886
+ * @param {String} props.description
1887
+ * @param {Boolean} props.debounce
1888
+ * @param {Boolean} props.disabled
1889
+ * @param {String} props.label
1890
+ * @param {Function} props.getValue
1891
+ * @param {Function} props.setValue
1892
+ * @param {Function} props.tooltipContainer
1893
+ * @param {Function} props.validate
1894
+ * @param {Function} props.show
1895
+ */
1896
+ function FeelTemplatingEntry(props) {
1897
+ const {
1898
+ element,
1899
+ id,
1900
+ description,
1901
+ debounce,
1902
+ disabled,
1903
+ label,
1904
+ getValue,
1905
+ setValue,
1906
+ tooltipContainer,
1907
+ validate,
1908
+ show = noop
1909
+ } = props;
1910
+ const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
1911
+ const [validationError, setValidationError] = hooks.useState(null);
1912
+ const [localError, setLocalError] = hooks.useState(null);
1913
+ let value = getValue(element);
1914
+ const previousValue = usePrevious(value);
1915
+ hooks.useEffect(() => {
1916
+ if (minDash.isFunction(validate)) {
1917
+ const newValidationError = validate(value) || null;
1918
+ setValidationError(newValidationError);
1919
+ }
1920
+ }, [value]);
1921
+ const onInput = useStaticCallback(newValue => {
1922
+ let newValidationError = null;
1923
+ if (minDash.isFunction(validate)) {
1924
+ newValidationError = validate(newValue) || null;
1925
+ }
1926
+ if (newValidationError) {
1927
+ setCachedInvalidValue(newValue);
1928
+ } else {
1929
+ // don't create multiple commandStack entries for the same value
1930
+ if (newValue !== value) {
1931
+ setValue(newValue);
1932
+ }
1933
+ }
1934
+ setValidationError(newValidationError);
1935
+ });
1936
+ const onError = hooks.useCallback(err => {
1937
+ setLocalError(err);
1938
+ }, []);
1939
+ if (previousValue === value && validationError) {
1940
+ value = cachedInvalidValue;
1941
+ }
1942
+ const temporaryError = useError(id);
1943
+ const error = localError || temporaryError || validationError;
1944
+ return jsxRuntime.jsxs("div", {
1945
+ class: classnames__default["default"]('bio-properties-panel-entry', error ? 'has-error' : ''),
1946
+ "data-entry-id": id,
1947
+ children: [jsxRuntime.jsx(FeelTemplating, {
1948
+ debounce: debounce,
1949
+ disabled: disabled,
1950
+ id: id,
1951
+ label: label,
1952
+ onInput: onInput,
1953
+ onError: onError,
1954
+ show: show,
1955
+ value: value,
1956
+ tooltipContainer: tooltipContainer
1957
+ }, element), error && jsxRuntime.jsx("div", {
1958
+ class: "bio-properties-panel-error",
1959
+ children: error
1960
+ }), jsxRuntime.jsx(Description, {
1961
+ forId: id,
1962
+ element: element,
1963
+ value: description
1964
+ })]
1965
+ });
1966
+ }
1967
+ function FeelTemplating(props) {
1968
+ const {
1969
+ debounce,
1970
+ id,
1971
+ label,
1972
+ onInput,
1973
+ onError,
1974
+ value = '',
1975
+ disabled = false,
1976
+ tooltipContainer
1977
+ } = props;
1978
+ const [localValue, setLocalValue] = hooks.useState(value);
1979
+ const editorRef = useShowEntryEvent(id);
1980
+ const containerRef = hooks.useRef();
1981
+ const [focus, _setFocus] = hooks.useState(undefined);
1982
+ const setFocus = (offset = 0) => {
1983
+ const hasFocus = containerRef.current.contains(document.activeElement);
1984
+
1985
+ // Keep caret position if it is already focused, otherwise focus at the end
1986
+ const position = hasFocus ? document.activeElement.selectionStart : Infinity;
1987
+ _setFocus(position + offset);
1988
+ };
1989
+ const handleInputCallback = hooks.useMemo(() => {
1990
+ return debounce(newValue => onInput(newValue.length ? newValue : undefined));
1991
+ }, [onInput, debounce]);
1992
+ const handleInput = newValue => {
1993
+ handleInputCallback(newValue);
1994
+ setLocalValue(newValue);
1995
+ };
1996
+ const handleLint = useStaticCallback(lint => {
1997
+ const errors = lint && lint.length && lint.filter(e => e.severity === 'error') || [];
1998
+ if (!errors.length) {
1999
+ onError(undefined);
2000
+ return;
2001
+ }
2002
+ const error = lint[0];
2003
+ const message = `${error.source}: ${error.message}`;
2004
+ onError(message);
2005
+ });
2006
+ hooks.useEffect(() => {
2007
+ if (typeof focus !== 'undefined') {
2008
+ editorRef.current.focus(focus);
2009
+ _setFocus(undefined);
2010
+ }
2011
+ }, [focus]);
2012
+ hooks.useEffect(() => {
2013
+ if (value === localValue) {
2014
+ return;
2015
+ }
2016
+ setLocalValue(value ? value : '');
2017
+ }, [value]);
2018
+ return jsxRuntime.jsxs("div", {
2019
+ class: "bio-properties-panel-feelers",
2020
+ children: [jsxRuntime.jsx("label", {
2021
+ id: prefixIdLabel(id),
2022
+ class: "bio-properties-panel-label",
2023
+ onClick: () => setFocus(),
2024
+ children: label
2025
+ }), jsxRuntime.jsx("div", {
2026
+ class: "bio-properties-panel-feelers-input",
2027
+ ref: containerRef,
2028
+ children: jsxRuntime.jsx(CodeEditor, {
2029
+ name: id,
2030
+ onInput: handleInput,
2031
+ contentAttributes: {
2032
+ 'aria-labelledby': prefixIdLabel(id)
2033
+ },
2034
+ disabled: disabled,
2035
+ onLint: handleLint,
2036
+ value: localValue,
2037
+ useGutters: false,
2038
+ ref: editorRef,
2039
+ tooltipContainer: tooltipContainer
2040
+ })
2041
+ })]
2042
+ });
2043
+ }
2044
+ function isEdited$6(node) {
2045
+ return node && (!!node.value || node.classList.contains('edited'));
2046
+ }
2047
+
2048
+ // helpers /////////////////
2049
+
2050
+ function prefixIdLabel(id) {
2051
+ return `bio-properties-panel-feelers-${id}-label`;
2052
+ }
2053
+
1786
2054
  function List(props) {
1787
2055
  const {
1788
2056
  id,
@@ -1811,8 +2079,8 @@ function List(props) {
1811
2079
  }
1812
2080
  }, [open, hasItems]);
1813
2081
 
1814
- /**
1815
- * @param {MouseEvent} event
2082
+ /**
2083
+ * @param {MouseEvent} event
1816
2084
  */
1817
2085
  function addItem(event) {
1818
2086
  event.stopPropagation();
@@ -1924,14 +2192,14 @@ function ItemsList(props) {
1924
2192
  });
1925
2193
  }
1926
2194
 
1927
- /**
1928
- * Place new items in the beginning of the list and sort the rest with provided function.
1929
- *
1930
- * @template Item
1931
- * @param {Item[]} currentItems
1932
- * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
1933
- * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
1934
- * @returns {Item[]}
2195
+ /**
2196
+ * Place new items in the beginning of the list and sort the rest with provided function.
2197
+ *
2198
+ * @template Item
2199
+ * @param {Item[]} currentItems
2200
+ * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
2201
+ * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
2202
+ * @returns {Item[]}
1935
2203
  */
1936
2204
  function useSortedItems(currentItems, compareFn, shouldReset = false) {
1937
2205
  const itemsRef = hooks.useRef(currentItems.slice());
@@ -2027,22 +2295,22 @@ function NumberField(props) {
2027
2295
  });
2028
2296
  }
2029
2297
 
2030
- /**
2031
- * @param {Object} props
2032
- * @param {Boolean} props.debounce
2033
- * @param {String} props.description
2034
- * @param {Boolean} props.disabled
2035
- * @param {Object} props.element
2036
- * @param {Function} props.getValue
2037
- * @param {String} props.id
2038
- * @param {String} props.label
2039
- * @param {String} props.max
2040
- * @param {String} props.min
2041
- * @param {Function} props.setValue
2042
- * @param {Function} props.onFocus
2043
- * @param {Function} props.onBlur
2044
- * @param {String} props.step
2045
- * @param {Function} props.validate
2298
+ /**
2299
+ * @param {Object} props
2300
+ * @param {Boolean} props.debounce
2301
+ * @param {String} props.description
2302
+ * @param {Boolean} props.disabled
2303
+ * @param {Object} props.element
2304
+ * @param {Function} props.getValue
2305
+ * @param {String} props.id
2306
+ * @param {String} props.label
2307
+ * @param {String} props.max
2308
+ * @param {String} props.min
2309
+ * @param {Function} props.setValue
2310
+ * @param {Function} props.onFocus
2311
+ * @param {Function} props.onBlur
2312
+ * @param {String} props.step
2313
+ * @param {Function} props.validate
2046
2314
  */
2047
2315
  function NumberFieldEntry(props) {
2048
2316
  const {
@@ -2188,18 +2456,18 @@ function Select(props) {
2188
2456
  });
2189
2457
  }
2190
2458
 
2191
- /**
2192
- * @param {object} props
2193
- * @param {object} props.element
2194
- * @param {string} props.id
2195
- * @param {string} [props.description]
2196
- * @param {string} props.label
2197
- * @param {Function} props.getValue
2198
- * @param {Function} props.setValue
2199
- * @param {Function} props.onFocus
2200
- * @param {Function} props.onBlur
2201
- * @param {Function} props.getOptions
2202
- * @param {boolean} [props.disabled]
2459
+ /**
2460
+ * @param {object} props
2461
+ * @param {object} props.element
2462
+ * @param {string} props.id
2463
+ * @param {string} [props.description]
2464
+ * @param {string} props.label
2465
+ * @param {Function} props.getValue
2466
+ * @param {Function} props.setValue
2467
+ * @param {Function} props.onFocus
2468
+ * @param {Function} props.onBlur
2469
+ * @param {Function} props.getOptions
2470
+ * @param {boolean} [props.disabled]
2203
2471
  */
2204
2472
  function SelectEntry(props) {
2205
2473
  const {
@@ -2370,20 +2638,20 @@ function TextArea(props) {
2370
2638
  });
2371
2639
  }
2372
2640
 
2373
- /**
2374
- * @param {object} props
2375
- * @param {object} props.element
2376
- * @param {string} props.id
2377
- * @param {string} props.description
2378
- * @param {boolean} props.debounce
2379
- * @param {string} props.label
2380
- * @param {Function} props.getValue
2381
- * @param {Function} props.setValue
2382
- * @param {Function} props.onFocus
2383
- * @param {Function} props.onBlur
2384
- * @param {number} props.rows
2385
- * @param {boolean} props.monospace
2386
- * @param {boolean} [props.disabled]
2641
+ /**
2642
+ * @param {object} props
2643
+ * @param {object} props.element
2644
+ * @param {string} props.id
2645
+ * @param {string} props.description
2646
+ * @param {boolean} props.debounce
2647
+ * @param {string} props.label
2648
+ * @param {Function} props.getValue
2649
+ * @param {Function} props.setValue
2650
+ * @param {Function} props.onFocus
2651
+ * @param {Function} props.onBlur
2652
+ * @param {number} props.rows
2653
+ * @param {boolean} props.monospace
2654
+ * @param {boolean} [props.disabled]
2387
2655
  */
2388
2656
  function TextAreaEntry(props) {
2389
2657
  const {
@@ -2489,19 +2757,19 @@ function Textfield(props) {
2489
2757
  });
2490
2758
  }
2491
2759
 
2492
- /**
2493
- * @param {Object} props
2494
- * @param {Object} props.element
2495
- * @param {String} props.id
2496
- * @param {String} props.description
2497
- * @param {Boolean} props.debounce
2498
- * @param {Boolean} props.disabled
2499
- * @param {String} props.label
2500
- * @param {Function} props.getValue
2501
- * @param {Function} props.setValue
2502
- * @param {Function} props.onFocus
2503
- * @param {Function} props.onBlur
2504
- * @param {Function} props.validate
2760
+ /**
2761
+ * @param {Object} props
2762
+ * @param {Object} props.element
2763
+ * @param {String} props.id
2764
+ * @param {String} props.description
2765
+ * @param {Boolean} props.debounce
2766
+ * @param {Boolean} props.disabled
2767
+ * @param {String} props.label
2768
+ * @param {Function} props.getValue
2769
+ * @param {Function} props.setValue
2770
+ * @param {Function} props.onFocus
2771
+ * @param {Function} props.onBlur
2772
+ * @param {Function} props.validate
2505
2773
  */
2506
2774
  function TextfieldEntry(props) {
2507
2775
  const {
@@ -2630,17 +2898,17 @@ function ToggleSwitch(props) {
2630
2898
  });
2631
2899
  }
2632
2900
 
2633
- /**
2634
- * @param {Object} props
2635
- * @param {Object} props.element
2636
- * @param {String} props.id
2637
- * @param {String} props.description
2638
- * @param {String} props.label
2639
- * @param {String} props.switcherLabel
2640
- * @param {Function} props.getValue
2641
- * @param {Function} props.setValue
2642
- * @param {Function} props.onFocus
2643
- * @param {Function} props.onBlur
2901
+ /**
2902
+ * @param {Object} props
2903
+ * @param {Object} props.element
2904
+ * @param {String} props.id
2905
+ * @param {String} props.description
2906
+ * @param {String} props.label
2907
+ * @param {String} props.switcherLabel
2908
+ * @param {Function} props.getValue
2909
+ * @param {Function} props.setValue
2910
+ * @param {Function} props.onFocus
2911
+ * @param {Function} props.onBlur
2644
2912
  */
2645
2913
  function ToggleSwitchEntry(props) {
2646
2914
  const {
@@ -2715,6 +2983,7 @@ exports.ExternalLinkIcon = ExternalLinkIcon;
2715
2983
  exports.FeelEntry = FeelEntry;
2716
2984
  exports.FeelOptionalIcon = FeelOptionalIcon;
2717
2985
  exports.FeelRequiredIcon = FeelRequiredIcon;
2986
+ exports.FeelTemplatingEntry = FeelTemplatingEntry;
2718
2987
  exports.FeelTextAreaEntry = FeelTextArea;
2719
2988
  exports.Group = Group;
2720
2989
  exports.Header = Header;
@@ -2732,8 +3001,9 @@ exports.SimpleEntry = Simple;
2732
3001
  exports.TextAreaEntry = TextAreaEntry;
2733
3002
  exports.TextFieldEntry = TextfieldEntry;
2734
3003
  exports.ToggleSwitchEntry = ToggleSwitchEntry;
2735
- exports.isCheckboxEntryEdited = isEdited$7;
2736
- exports.isFeelEntryEdited = isEdited$6;
3004
+ exports.isCheckboxEntryEdited = isEdited$8;
3005
+ exports.isFeelEntryEdited = isEdited$7;
3006
+ exports.isFeelTemplatingEntryEdited = isEdited$6;
2737
3007
  exports.isNumberFieldEntryEdited = isEdited$5;
2738
3008
  exports.isSelectEntryEdited = isEdited$4;
2739
3009
  exports.isSimpleEntryEdited = isEdited$3;