@bpmn-io/properties-panel 1.3.1 → 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.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useContext, useRef, useEffect, useMemo, useState, useCallback, useLayoutEffect } from '../preact/hooks';
1
+ import { useContext, useRef, useEffect, useMemo, useCallback, useState, useLayoutEffect } from '../preact/hooks';
2
2
  import { isFunction, isArray, get, assign, set, sortBy, find, isNumber, debounce } from 'min-dash';
3
3
  import classnames from 'classnames';
4
4
  import { forwardRef } from '../preact/compat';
@@ -6,6 +6,7 @@ import { jsx, jsxs } from '../preact/jsx-runtime';
6
6
  import { query } from 'min-dom';
7
7
  import { createContext, createElement } from '../preact';
8
8
  import FeelEditor from '@bpmn-io/feel-editor';
9
+ import { FeelersEditor } from 'feelers';
9
10
 
10
11
  var ArrowIcon = function ArrowIcon(props) {
11
12
  return jsx("svg", {
@@ -166,19 +167,19 @@ const ErrorsContext = createContext({
166
167
  errors: {}
167
168
  });
168
169
 
169
- /**
170
- * @typedef {Function} <propertiesPanel.showEntry> callback
171
- *
172
- * @example
173
- *
174
- * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
175
- * // ...
176
- * });
177
- *
178
- * @param {Object} context
179
- * @param {boolean} [context.focus]
180
- *
181
- * @returns void
170
+ /**
171
+ * @typedef {Function} <propertiesPanel.showEntry> callback
172
+ *
173
+ * @example
174
+ *
175
+ * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
176
+ * // ...
177
+ * });
178
+ *
179
+ * @param {Object} context
180
+ * @param {boolean} [context.focus]
181
+ *
182
+ * @returns void
182
183
  */
183
184
  const EventContext = createContext({
184
185
  eventBus: null
@@ -191,20 +192,20 @@ const LayoutContext = createContext({
191
192
  setLayoutForKey: () => {}
192
193
  });
193
194
 
194
- /**
195
- * Accesses the global DescriptionContext and returns a description for a given id and element.
196
- *
197
- * @example
198
- * ```jsx
199
- * function TextField(props) {
200
- * const description = useDescriptionContext('input1', element);
201
- * }
202
- * ```
203
- *
204
- * @param {string} id
205
- * @param {object} element
206
- *
207
- * @returns {string}
195
+ /**
196
+ * Accesses the global DescriptionContext and returns a description for a given id and element.
197
+ *
198
+ * @example
199
+ * ```jsx
200
+ * function TextField(props) {
201
+ * const description = useDescriptionContext('input1', element);
202
+ * }
203
+ * ```
204
+ *
205
+ * @param {string} id
206
+ * @param {object} element
207
+ *
208
+ * @returns {string}
208
209
  */
209
210
  function useDescriptionContext(id, element) {
210
211
  const {
@@ -220,11 +221,11 @@ function useError(id) {
220
221
  return errors[id];
221
222
  }
222
223
 
223
- /**
224
- * Subscribe to an event immediately. Update subscription after inputs changed.
225
- *
226
- * @param {string} event
227
- * @param {Function} callback
224
+ /**
225
+ * Subscribe to an event immediately. Update subscription after inputs changed.
226
+ *
227
+ * @param {string} event
228
+ * @param {Function} callback
228
229
  */
229
230
  function useEvent(event, callback, eventBus) {
230
231
  const eventContext = useContext(EventContext);
@@ -256,24 +257,24 @@ function useEvent(event, callback, eventBus) {
256
257
 
257
258
  const KEY_LENGTH = 6;
258
259
 
259
- /**
260
- * Create a persistent key factory for plain objects without id.
261
- *
262
- * @example
263
- * ```jsx
264
- * function List({ objects }) {
265
- * const getKey = useKeyFactory();
266
- * return (<ol>{
267
- * objects.map(obj => {
268
- * const key = getKey(obj);
269
- * return <li key={key}>obj.name</li>
270
- * })
271
- * }</ol>);
272
- * }
273
- * ```
274
- *
275
- * @param {any[]} dependencies
276
- * @returns {(element: object) => string}
260
+ /**
261
+ * Create a persistent key factory for plain objects without id.
262
+ *
263
+ * @example
264
+ * ```jsx
265
+ * function List({ objects }) {
266
+ * const getKey = useKeyFactory();
267
+ * return (<ol>{
268
+ * objects.map(obj => {
269
+ * const key = getKey(obj);
270
+ * return <li key={key}>obj.name</li>
271
+ * })
272
+ * }</ol>);
273
+ * }
274
+ * ```
275
+ *
276
+ * @param {any[]} dependencies
277
+ * @returns {(element: object) => string}
277
278
  */
278
279
  function useKeyFactory(dependencies = []) {
279
280
  const map = useMemo(() => new Map(), dependencies);
@@ -288,20 +289,20 @@ function useKeyFactory(dependencies = []) {
288
289
  return getKey;
289
290
  }
290
291
 
291
- /**
292
- * Creates a state that persists in the global LayoutContext.
293
- *
294
- * @example
295
- * ```jsx
296
- * function Group(props) {
297
- * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
298
- * }
299
- * ```
300
- *
301
- * @param {(string|number)[]} path
302
- * @param {any} [defaultValue]
303
- *
304
- * @returns {[ any, Function ]}
292
+ /**
293
+ * Creates a state that persists in the global LayoutContext.
294
+ *
295
+ * @example
296
+ * ```jsx
297
+ * function Group(props) {
298
+ * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
299
+ * }
300
+ * ```
301
+ *
302
+ * @param {(string|number)[]} path
303
+ * @param {any} [defaultValue]
304
+ *
305
+ * @returns {[ any, Function ]}
305
306
  */
306
307
  function useLayoutState(path, defaultValue) {
307
308
  const {
@@ -309,22 +310,17 @@ function useLayoutState(path, defaultValue) {
309
310
  setLayoutForKey
310
311
  } = useContext(LayoutContext);
311
312
  const layoutForKey = getLayoutForKey(path, defaultValue);
312
- const [value, set] = useState(layoutForKey);
313
- const setState = newValue => {
314
- // (1) set component state
315
- set(newValue);
316
-
317
- // (2) set context
313
+ const setState = useCallback(newValue => {
318
314
  setLayoutForKey(path, newValue);
319
- };
320
- return [value, setState];
315
+ }, [setLayoutForKey]);
316
+ return [layoutForKey, setState];
321
317
  }
322
318
 
323
- /**
324
- * @pinussilvestrus: we need to introduce our own hook to persist the previous
325
- * state on updates.
326
- *
327
- * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
319
+ /**
320
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
321
+ * state on updates.
322
+ *
323
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
328
324
  */
329
325
 
330
326
  function usePrevious(value) {
@@ -335,12 +331,12 @@ function usePrevious(value) {
335
331
  return ref.current;
336
332
  }
337
333
 
338
- /**
339
- * Subscribe to `propertiesPanel.showEntry`.
340
- *
341
- * @param {string} id
342
- *
343
- * @returns {import('preact').Ref}
334
+ /**
335
+ * Subscribe to `propertiesPanel.showEntry`.
336
+ *
337
+ * @param {string} id
338
+ *
339
+ * @returns {import('preact').Ref}
344
340
  */
345
341
  function useShowEntryEvent(id) {
346
342
  const {
@@ -371,20 +367,20 @@ function useShowEntryEvent(id) {
371
367
  return ref;
372
368
  }
373
369
 
374
- /**
375
- * @callback setSticky
376
- * @param {boolean} value
370
+ /**
371
+ * @callback setSticky
372
+ * @param {boolean} value
377
373
  */
378
374
 
379
- /**
380
- * Use IntersectionObserver to identify when DOM element is in sticky mode.
381
- * If sticky is observered setSticky(true) will be called.
382
- * If sticky mode is left, setSticky(false) will be called.
383
- *
384
- *
385
- * @param {Object} ref
386
- * @param {string} scrollContainerSelector
387
- * @param {setSticky} setSticky
375
+ /**
376
+ * Use IntersectionObserver to identify when DOM element is in sticky mode.
377
+ * If sticky is observered setSticky(true) will be called.
378
+ * If sticky mode is left, setSticky(false) will be called.
379
+ *
380
+ *
381
+ * @param {Object} ref
382
+ * @param {string} scrollContainerSelector
383
+ * @param {setSticky} setSticky
388
384
  */
389
385
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
390
386
  useEffect(() => {
@@ -423,19 +419,19 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
423
419
  }, [ref, scrollContainerSelector, setSticky]);
424
420
  }
425
421
 
426
- /**
427
- * Creates a static function reference with changing body.
428
- * This is necessary when external libraries require a callback function
429
- * that has references to state variables.
430
- *
431
- * Usage:
432
- * const callback = useStaticCallback((val) => {val === currentState});
433
- *
434
- * The `callback` reference is static and can be safely used in external
435
- * libraries or as a prop that does not cause rerendering of children.
436
- *
437
- * @param {Function} callback function with changing reference
438
- * @returns {Function} static function reference
422
+ /**
423
+ * Creates a static function reference with changing body.
424
+ * This is necessary when external libraries require a callback function
425
+ * that has references to state variables.
426
+ *
427
+ * Usage:
428
+ * const callback = useStaticCallback((val) => {val === currentState});
429
+ *
430
+ * The `callback` reference is static and can be safely used in external
431
+ * libraries or as a prop that does not cause rerendering of children.
432
+ *
433
+ * @param {Function} callback function with changing reference
434
+ * @returns {Function} static function reference
439
435
  */
440
436
  function useStaticCallback(callback) {
441
437
  const callbackRef = useRef(callback);
@@ -528,13 +524,13 @@ function DataMarker() {
528
524
  });
529
525
  }
530
526
 
531
- /**
532
- * @typedef { {
533
- * text: (element: object) => string,
534
- * icon?: (element: Object) => import('preact').Component
535
- * } } PlaceholderDefinition
536
- *
537
- * @param { PlaceholderDefinition } props
527
+ /**
528
+ * @typedef { {
529
+ * text: (element: object) => string,
530
+ * icon?: (element: Object) => import('preact').Component
531
+ * } } PlaceholderDefinition
532
+ *
533
+ * @param { PlaceholderDefinition } props
538
534
  */
539
535
  function Placeholder(props) {
540
536
  const {
@@ -560,72 +556,72 @@ const DEFAULT_LAYOUT = {
560
556
  };
561
557
  const DEFAULT_DESCRIPTION = {};
562
558
 
563
- /**
564
- * @typedef { {
565
- * component: import('preact').Component,
566
- * id: String,
567
- * isEdited?: Function
568
- * } } EntryDefinition
569
- *
570
- * @typedef { {
571
- * autoFocusEntry: String,
572
- * autoOpen?: Boolean,
573
- * entries: Array<EntryDefinition>,
574
- * id: String,
575
- * label: String,
576
- * remove: (event: MouseEvent) => void
577
- * } } ListItemDefinition
578
- *
579
- * @typedef { {
580
- * add: (event: MouseEvent) => void,
581
- * component: import('preact').Component,
582
- * element: Object,
583
- * id: String,
584
- * items: Array<ListItemDefinition>,
585
- * label: String,
586
- * shouldSort?: Boolean,
587
- * shouldOpen?: Boolean
588
- * } } ListGroupDefinition
589
- *
590
- * @typedef { {
591
- * component?: import('preact').Component,
592
- * entries: Array<EntryDefinition>,
593
- * id: String,
594
- * label: String,
595
- * shouldOpen?: Boolean
596
- * } } GroupDefinition
597
- *
598
- * @typedef { {
599
- * [id: String]: GetDescriptionFunction
600
- * } } DescriptionConfig
601
- *
602
- * @callback { {
603
- * @param {string} id
604
- * @param {Object} element
605
- * @returns {string}
606
- * } } GetDescriptionFunction
607
- *
608
- * @typedef { {
609
- * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
610
- * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
611
- * } } PlaceholderProvider
612
- *
559
+ /**
560
+ * @typedef { {
561
+ * component: import('preact').Component,
562
+ * id: String,
563
+ * isEdited?: Function
564
+ * } } EntryDefinition
565
+ *
566
+ * @typedef { {
567
+ * autoFocusEntry: String,
568
+ * autoOpen?: Boolean,
569
+ * entries: Array<EntryDefinition>,
570
+ * id: String,
571
+ * label: String,
572
+ * remove: (event: MouseEvent) => void
573
+ * } } ListItemDefinition
574
+ *
575
+ * @typedef { {
576
+ * add: (event: MouseEvent) => void,
577
+ * component: import('preact').Component,
578
+ * element: Object,
579
+ * id: String,
580
+ * items: Array<ListItemDefinition>,
581
+ * label: String,
582
+ * shouldSort?: Boolean,
583
+ * shouldOpen?: Boolean
584
+ * } } ListGroupDefinition
585
+ *
586
+ * @typedef { {
587
+ * component?: import('preact').Component,
588
+ * entries: Array<EntryDefinition>,
589
+ * id: String,
590
+ * label: String,
591
+ * shouldOpen?: Boolean
592
+ * } } GroupDefinition
593
+ *
594
+ * @typedef { {
595
+ * [id: String]: GetDescriptionFunction
596
+ * } } DescriptionConfig
597
+ *
598
+ * @callback { {
599
+ * @param {string} id
600
+ * @param {Object} element
601
+ * @returns {string}
602
+ * } } GetDescriptionFunction
603
+ *
604
+ * @typedef { {
605
+ * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
606
+ * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
607
+ * } } PlaceholderProvider
608
+ *
613
609
  */
614
610
 
615
- /**
616
- * A basic properties panel component. Describes *how* content will be rendered, accepts
617
- * data from implementor to describe *what* will be rendered.
618
- *
619
- * @param {Object} props
620
- * @param {Object|Array} props.element
621
- * @param {import('./components/Header').HeaderProvider} props.headerProvider
622
- * @param {PlaceholderProvider} [props.placeholderProvider]
623
- * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
624
- * @param {Object} [props.layoutConfig]
625
- * @param {Function} [props.layoutChanged]
626
- * @param {DescriptionConfig} [props.descriptionConfig]
627
- * @param {Function} [props.descriptionLoaded]
628
- * @param {Object} [props.eventBus]
611
+ /**
612
+ * A basic properties panel component. Describes *how* content will be rendered, accepts
613
+ * data from implementor to describe *what* will be rendered.
614
+ *
615
+ * @param {Object} props
616
+ * @param {Object|Array} props.element
617
+ * @param {import('./components/Header').HeaderProvider} props.headerProvider
618
+ * @param {PlaceholderProvider} [props.placeholderProvider]
619
+ * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
620
+ * @param {Object} [props.layoutConfig]
621
+ * @param {Function} [props.layoutChanged]
622
+ * @param {DescriptionConfig} [props.descriptionConfig]
623
+ * @param {Function} [props.descriptionLoaded]
624
+ * @param {Object} [props.eventBus]
629
625
  */
630
626
  function PropertiesPanel(props) {
631
627
  const {
@@ -642,6 +638,12 @@ function PropertiesPanel(props) {
642
638
 
643
639
  // set-up layout context
644
640
  const [layout, setLayout] = useState(createLayout(layoutConfig));
641
+
642
+ // react to external changes in the layout config
643
+ useEffect(() => {
644
+ const newLayout = createLayout(layoutConfig);
645
+ setLayout(newLayout);
646
+ }, [layoutConfig]);
645
647
  useEffect(() => {
646
648
  if (typeof layoutChanged === 'function') {
647
649
  layoutChanged(layout);
@@ -741,9 +743,9 @@ function PropertiesPanel(props) {
741
743
 
742
744
  // helpers //////////////////
743
745
 
744
- function createLayout(overrides) {
746
+ function createLayout(overrides, defaults = DEFAULT_LAYOUT) {
745
747
  return {
746
- ...DEFAULT_LAYOUT,
748
+ ...defaults,
747
749
  ...overrides
748
750
  };
749
751
  }
@@ -815,15 +817,15 @@ function MenuItem({
815
817
  });
816
818
  }
817
819
 
818
- /**
819
- *
820
- * @param {Array<null | Element>} ignoredElements
821
- * @param {Function} callback
820
+ /**
821
+ *
822
+ * @param {Array<null | Element>} ignoredElements
823
+ * @param {Function} callback
822
824
  */
823
825
  function useGlobalClick(ignoredElements, callback) {
824
826
  useEffect(() => {
825
- /**
826
- * @param {MouseEvent} event
827
+ /**
828
+ * @param {MouseEvent} event
827
829
  */
828
830
  function listener(event) {
829
831
  if (ignoredElements.some(element => element && element.contains(event.target))) {
@@ -952,10 +954,10 @@ function ListItem(props) {
952
954
  });
953
955
  }
954
956
 
955
- const noop$2 = () => {};
957
+ const noop$3 = () => {};
956
958
 
957
- /**
958
- * @param {import('../PropertiesPanel').ListGroupDefinition} props
959
+ /**
960
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
959
961
  */
960
962
  function ListGroup(props) {
961
963
  const {
@@ -1061,7 +1063,7 @@ function ListGroup(props) {
1061
1063
  ref: groupRef,
1062
1064
  children: [jsxs("div", {
1063
1065
  class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
1064
- onClick: hasItems ? toggleOpen : noop$2,
1066
+ onClick: hasItems ? toggleOpen : noop$3,
1065
1067
  children: [jsx("div", {
1066
1068
  title: label,
1067
1069
  class: "bio-properties-panel-group-header-title",
@@ -1118,8 +1120,8 @@ function ListGroup(props) {
1118
1120
 
1119
1121
  // helpers ////////////////////
1120
1122
 
1121
- /**
1122
- * Sorts given items alphanumeric by label
1123
+ /**
1124
+ * Sorts given items alphanumeric by label
1123
1125
  */
1124
1126
  function sortItems(items) {
1125
1127
  return sortBy(items, i => i.label.toLowerCase());
@@ -1195,17 +1197,17 @@ function Checkbox(props) {
1195
1197
  });
1196
1198
  }
1197
1199
 
1198
- /**
1199
- * @param {Object} props
1200
- * @param {Object} props.element
1201
- * @param {String} props.id
1202
- * @param {String} props.description
1203
- * @param {String} props.label
1204
- * @param {Function} props.getValue
1205
- * @param {Function} props.setValue
1206
- * @param {Function} props.onFocus
1207
- * @param {Function} props.onBlur
1208
- * @param {boolean} [props.disabled]
1200
+ /**
1201
+ * @param {Object} props
1202
+ * @param {Object} props.element
1203
+ * @param {String} props.id
1204
+ * @param {String} props.description
1205
+ * @param {String} props.label
1206
+ * @param {Function} props.getValue
1207
+ * @param {Function} props.setValue
1208
+ * @param {Function} props.onFocus
1209
+ * @param {Function} props.onBlur
1210
+ * @param {boolean} [props.disabled]
1209
1211
  */
1210
1212
  function CheckboxEntry(props) {
1211
1213
  const {
@@ -1242,7 +1244,7 @@ function CheckboxEntry(props) {
1242
1244
  })]
1243
1245
  });
1244
1246
  }
1245
- function isEdited$7(node) {
1247
+ function isEdited$8(node) {
1246
1248
  return node && !!node.checked;
1247
1249
  }
1248
1250
 
@@ -1252,7 +1254,7 @@ function prefixId$7(id) {
1252
1254
  return `bio-properties-panel-${id}`;
1253
1255
  }
1254
1256
 
1255
- const useBufferedFocus = function (editor, ref) {
1257
+ const useBufferedFocus$1 = function (editor, ref) {
1256
1258
  const [buffer, setBuffer] = useState(undefined);
1257
1259
  ref.current = useMemo(() => ({
1258
1260
  focus: offset => {
@@ -1273,7 +1275,7 @@ const useBufferedFocus = function (editor, ref) {
1273
1275
  }
1274
1276
  }, [editor, buffer]);
1275
1277
  };
1276
- const CodeEditor = forwardRef((props, ref) => {
1278
+ const CodeEditor$1 = forwardRef((props, ref) => {
1277
1279
  const {
1278
1280
  value,
1279
1281
  onInput,
@@ -1286,7 +1288,7 @@ const CodeEditor = forwardRef((props, ref) => {
1286
1288
  const inputRef = useRef();
1287
1289
  const [editor, setEditor] = useState();
1288
1290
  const [localValue, setLocalValue] = useState(value || '');
1289
- useBufferedFocus(editor, ref);
1291
+ useBufferedFocus$1(editor, ref);
1290
1292
  const handleInput = useStaticCallback(newValue => {
1291
1293
  onInput(newValue);
1292
1294
  setLocalValue(newValue);
@@ -1294,10 +1296,10 @@ const CodeEditor = forwardRef((props, ref) => {
1294
1296
  useEffect(() => {
1295
1297
  let editor;
1296
1298
 
1297
- /* Trigger FEEL toggle when
1298
- *
1299
- * - `backspace` is pressed
1300
- * - AND the cursor is at the beginning of the input
1299
+ /* Trigger FEEL toggle when
1300
+ *
1301
+ * - `backspace` is pressed
1302
+ * - AND the cursor is at the beginning of the input
1301
1303
  */
1302
1304
  const onKeyDown = e => {
1303
1305
  if (e.key !== 'Backspace' || !editor) {
@@ -1368,12 +1370,12 @@ function FeelIndicator(props) {
1368
1370
  });
1369
1371
  }
1370
1372
 
1371
- const noop$1 = () => {};
1373
+ const noop$2 = () => {};
1372
1374
 
1373
- /**
1374
- * @param {Object} props
1375
- * @param {Object} props.label
1376
- * @param {String} props.feel
1375
+ /**
1376
+ * @param {Object} props
1377
+ * @param {Object} props.label
1378
+ * @param {String} props.feel
1377
1379
  */
1378
1380
  function FeelIcon(props) {
1379
1381
  const {
@@ -1381,7 +1383,7 @@ function FeelIcon(props) {
1381
1383
  feel = false,
1382
1384
  active,
1383
1385
  disabled = false,
1384
- onClick = noop$1
1386
+ onClick = noop$2
1385
1387
  } = props;
1386
1388
  const feelRequiredLabel = ' must be a FEEL expression';
1387
1389
  const feelOptionalLabel = ' can optionally be a FEEL expression';
@@ -1402,7 +1404,7 @@ function FeelIcon(props) {
1402
1404
  });
1403
1405
  }
1404
1406
 
1405
- const noop = () => {};
1407
+ const noop$1 = () => {};
1406
1408
  function FeelTextfield(props) {
1407
1409
  const {
1408
1410
  debounce,
@@ -1542,7 +1544,7 @@ function FeelTextfield(props) {
1542
1544
  active: feelActive,
1543
1545
  disabled: feel !== 'optional' || disabled,
1544
1546
  onClick: handleFeelToggle
1545
- }), feelActive ? jsx(CodeEditor, {
1547
+ }), feelActive ? jsx(CodeEditor$1, {
1546
1548
  id: prefixId$6(id),
1547
1549
  name: id,
1548
1550
  onInput: handleLocalInput,
@@ -1648,17 +1650,24 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
1648
1650
  });
1649
1651
  });
1650
1652
 
1651
- /**
1652
- * @param {Object} props
1653
- * @param {Object} props.element
1654
- * @param {String} props.id
1655
- * @param {String} props.description
1656
- * @param {Boolean} props.debounce
1657
- * @param {Boolean} props.disabled
1658
- * @param {String} props.label
1659
- * @param {Function} props.getValue
1660
- * @param {Function} props.setValue
1661
- * @param {Function} props.validate
1653
+ /**
1654
+ * @param {Object} props
1655
+ * @param {Object} props.element
1656
+ * @param {String} props.id
1657
+ * @param {String} props.description
1658
+ * @param {Boolean} props.debounce
1659
+ * @param {Boolean} props.disabled
1660
+ * @param {Boolean} props.feel
1661
+ * @param {String} props.label
1662
+ * @param {Function} props.getValue
1663
+ * @param {Function} props.setValue
1664
+ * @param {Function} props.tooltipContainer
1665
+ * @param {Function} props.validate
1666
+ * @param {Function} props.show
1667
+ * @param {Function} props.example
1668
+ * @param {Function} props.variables
1669
+ * @param {Function} props.onFocus
1670
+ * @param {Function} props.onBlur
1662
1671
  */
1663
1672
  function FeelEntry(props) {
1664
1673
  const {
@@ -1673,7 +1682,7 @@ function FeelEntry(props) {
1673
1682
  setValue,
1674
1683
  tooltipContainer,
1675
1684
  validate,
1676
- show = noop,
1685
+ show = noop$1,
1677
1686
  example,
1678
1687
  variables,
1679
1688
  onFocus,
@@ -1743,19 +1752,24 @@ function FeelEntry(props) {
1743
1752
  });
1744
1753
  }
1745
1754
 
1746
- /**
1747
- * @param {Object} props
1748
- * @param {Object} props.element
1749
- * @param {String} props.id
1750
- * @param {String} props.description
1751
- * @param {Boolean} props.debounce
1752
- * @param {Boolean} props.disabled
1753
- * @param {String} props.label
1754
- * @param {Function} props.getValue
1755
- * @param {Function} props.setValue
1756
- * @param {Function} props.onFocus
1757
- * @param {Function} props.onBlur
1758
- * @param {Function} props.validate
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 {Boolean} props.feel
1763
+ * @param {String} props.label
1764
+ * @param {Function} props.getValue
1765
+ * @param {Function} props.setValue
1766
+ * @param {Function} props.tooltipContainer
1767
+ * @param {Function} props.validate
1768
+ * @param {Function} props.show
1769
+ * @param {Function} props.example
1770
+ * @param {Function} props.variables
1771
+ * @param {Function} props.onFocus
1772
+ * @param {Function} props.onBlur
1759
1773
  */
1760
1774
  function FeelTextArea(props) {
1761
1775
  return jsx(FeelEntry, {
@@ -1764,7 +1778,7 @@ function FeelTextArea(props) {
1764
1778
  ...props
1765
1779
  });
1766
1780
  }
1767
- function isEdited$6(node) {
1781
+ function isEdited$7(node) {
1768
1782
  return node && (!!node.value || node.classList.contains('edited'));
1769
1783
  }
1770
1784
 
@@ -1774,6 +1788,260 @@ function prefixId$6(id) {
1774
1788
  return `bio-properties-panel-${id}`;
1775
1789
  }
1776
1790
 
1791
+ const useBufferedFocus = function (editor, ref) {
1792
+ const [buffer, setBuffer] = useState(undefined);
1793
+ ref.current = useMemo(() => ({
1794
+ focus: offset => {
1795
+ if (editor) {
1796
+ editor.focus(offset);
1797
+ } else {
1798
+ if (typeof offset === 'undefined') {
1799
+ offset = Infinity;
1800
+ }
1801
+ setBuffer(offset);
1802
+ }
1803
+ }
1804
+ }), [editor]);
1805
+ useEffect(() => {
1806
+ if (typeof buffer !== 'undefined' && editor) {
1807
+ editor.focus(buffer);
1808
+ setBuffer(false);
1809
+ }
1810
+ }, [editor, buffer]);
1811
+ };
1812
+ const CodeEditor = forwardRef((props, ref) => {
1813
+ const {
1814
+ value,
1815
+ onInput,
1816
+ onLint = () => {},
1817
+ contentAttributes = {},
1818
+ disabled,
1819
+ tooltipContainer,
1820
+ useGutters = false,
1821
+ darkMode = false
1822
+ } = props;
1823
+ const inputRef = useRef();
1824
+ const [editor, setEditor] = useState();
1825
+ const [localValue, setLocalValue] = useState(value || '');
1826
+ useBufferedFocus(editor, ref);
1827
+ const handleInput = useStaticCallback(newValue => {
1828
+ onInput(newValue);
1829
+ setLocalValue(newValue);
1830
+ });
1831
+ useEffect(() => {
1832
+ let editor;
1833
+ editor = new FeelersEditor({
1834
+ container: inputRef.current,
1835
+ onChange: handleInput,
1836
+ onLint: onLint,
1837
+ contentAttributes: contentAttributes,
1838
+ tooltipContainer: tooltipContainer,
1839
+ value: localValue,
1840
+ darkMode: darkMode,
1841
+ enableGutters: useGutters
1842
+ });
1843
+ setEditor(editor);
1844
+ return () => {
1845
+ onLint([]);
1846
+ inputRef.current.innerHTML = '';
1847
+ setEditor(null);
1848
+ };
1849
+ }, []);
1850
+ useEffect(() => {
1851
+ if (!editor) {
1852
+ return;
1853
+ }
1854
+ if (value === localValue) {
1855
+ return;
1856
+ }
1857
+ editor.setValue(value);
1858
+ setLocalValue(value);
1859
+ }, [value]);
1860
+ const handleClick = () => {
1861
+ ref.current.focus();
1862
+ };
1863
+ return jsx("div", {
1864
+ name: props.name,
1865
+ class: classnames('bio-properties-panel-feelers-editor bio-properties-panel-input', localValue ? 'edited' : null, disabled ? 'disabled' : null),
1866
+ ref: inputRef,
1867
+ onClick: handleClick
1868
+ });
1869
+ });
1870
+
1871
+ const noop = () => {};
1872
+
1873
+ /**
1874
+ * @param {Object} props
1875
+ * @param {Object} props.element
1876
+ * @param {String} props.id
1877
+ * @param {String} props.description
1878
+ * @param {Boolean} props.debounce
1879
+ * @param {Boolean} props.disabled
1880
+ * @param {String} props.label
1881
+ * @param {Function} props.getValue
1882
+ * @param {Function} props.setValue
1883
+ * @param {Function} props.tooltipContainer
1884
+ * @param {Function} props.validate
1885
+ * @param {Function} props.show
1886
+ */
1887
+ function FeelTemplatingEntry(props) {
1888
+ const {
1889
+ element,
1890
+ id,
1891
+ description,
1892
+ debounce,
1893
+ disabled,
1894
+ label,
1895
+ getValue,
1896
+ setValue,
1897
+ tooltipContainer,
1898
+ validate,
1899
+ show = noop
1900
+ } = props;
1901
+ const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
1902
+ const [validationError, setValidationError] = useState(null);
1903
+ const [localError, setLocalError] = useState(null);
1904
+ let value = getValue(element);
1905
+ const previousValue = usePrevious(value);
1906
+ useEffect(() => {
1907
+ if (isFunction(validate)) {
1908
+ const newValidationError = validate(value) || null;
1909
+ setValidationError(newValidationError);
1910
+ }
1911
+ }, [value]);
1912
+ const onInput = useStaticCallback(newValue => {
1913
+ let newValidationError = null;
1914
+ if (isFunction(validate)) {
1915
+ newValidationError = validate(newValue) || null;
1916
+ }
1917
+ if (newValidationError) {
1918
+ setCachedInvalidValue(newValue);
1919
+ } else {
1920
+ // don't create multiple commandStack entries for the same value
1921
+ if (newValue !== value) {
1922
+ setValue(newValue);
1923
+ }
1924
+ }
1925
+ setValidationError(newValidationError);
1926
+ });
1927
+ const onError = useCallback(err => {
1928
+ setLocalError(err);
1929
+ }, []);
1930
+ if (previousValue === value && validationError) {
1931
+ value = cachedInvalidValue;
1932
+ }
1933
+ const temporaryError = useError(id);
1934
+ const error = localError || temporaryError || validationError;
1935
+ return jsxs("div", {
1936
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
1937
+ "data-entry-id": id,
1938
+ children: [jsx(FeelTemplating, {
1939
+ debounce: debounce,
1940
+ disabled: disabled,
1941
+ id: id,
1942
+ label: label,
1943
+ onInput: onInput,
1944
+ onError: onError,
1945
+ show: show,
1946
+ value: value,
1947
+ tooltipContainer: tooltipContainer
1948
+ }, element), error && jsx("div", {
1949
+ class: "bio-properties-panel-error",
1950
+ children: error
1951
+ }), jsx(Description, {
1952
+ forId: id,
1953
+ element: element,
1954
+ value: description
1955
+ })]
1956
+ });
1957
+ }
1958
+ function FeelTemplating(props) {
1959
+ const {
1960
+ debounce,
1961
+ id,
1962
+ label,
1963
+ onInput,
1964
+ onError,
1965
+ value = '',
1966
+ disabled = false,
1967
+ tooltipContainer
1968
+ } = props;
1969
+ const [localValue, setLocalValue] = useState(value);
1970
+ const editorRef = useShowEntryEvent(id);
1971
+ const containerRef = useRef();
1972
+ const [focus, _setFocus] = useState(undefined);
1973
+ const setFocus = (offset = 0) => {
1974
+ const hasFocus = containerRef.current.contains(document.activeElement);
1975
+
1976
+ // Keep caret position if it is already focused, otherwise focus at the end
1977
+ const position = hasFocus ? document.activeElement.selectionStart : Infinity;
1978
+ _setFocus(position + offset);
1979
+ };
1980
+ const handleInputCallback = useMemo(() => {
1981
+ return debounce(newValue => onInput(newValue.length ? newValue : undefined));
1982
+ }, [onInput, debounce]);
1983
+ const handleInput = newValue => {
1984
+ handleInputCallback(newValue);
1985
+ setLocalValue(newValue);
1986
+ };
1987
+ const handleLint = useStaticCallback(lint => {
1988
+ const errors = lint && lint.length && lint.filter(e => e.severity === 'error') || [];
1989
+ if (!errors.length) {
1990
+ onError(undefined);
1991
+ return;
1992
+ }
1993
+ const error = lint[0];
1994
+ const message = `${error.source}: ${error.message}`;
1995
+ onError(message);
1996
+ });
1997
+ useEffect(() => {
1998
+ if (typeof focus !== 'undefined') {
1999
+ editorRef.current.focus(focus);
2000
+ _setFocus(undefined);
2001
+ }
2002
+ }, [focus]);
2003
+ useEffect(() => {
2004
+ if (value === localValue) {
2005
+ return;
2006
+ }
2007
+ setLocalValue(value ? value : '');
2008
+ }, [value]);
2009
+ return jsxs("div", {
2010
+ class: "bio-properties-panel-feelers",
2011
+ children: [jsx("label", {
2012
+ id: prefixIdLabel(id),
2013
+ class: "bio-properties-panel-label",
2014
+ onClick: () => setFocus(),
2015
+ children: label
2016
+ }), jsx("div", {
2017
+ class: "bio-properties-panel-feelers-input",
2018
+ ref: containerRef,
2019
+ children: jsx(CodeEditor, {
2020
+ name: id,
2021
+ onInput: handleInput,
2022
+ contentAttributes: {
2023
+ 'aria-labelledby': prefixIdLabel(id)
2024
+ },
2025
+ disabled: disabled,
2026
+ onLint: handleLint,
2027
+ value: localValue,
2028
+ useGutters: false,
2029
+ ref: editorRef,
2030
+ tooltipContainer: tooltipContainer
2031
+ })
2032
+ })]
2033
+ });
2034
+ }
2035
+ function isEdited$6(node) {
2036
+ return node && (!!node.value || node.classList.contains('edited'));
2037
+ }
2038
+
2039
+ // helpers /////////////////
2040
+
2041
+ function prefixIdLabel(id) {
2042
+ return `bio-properties-panel-feelers-${id}-label`;
2043
+ }
2044
+
1777
2045
  function List(props) {
1778
2046
  const {
1779
2047
  id,
@@ -1802,8 +2070,8 @@ function List(props) {
1802
2070
  }
1803
2071
  }, [open, hasItems]);
1804
2072
 
1805
- /**
1806
- * @param {MouseEvent} event
2073
+ /**
2074
+ * @param {MouseEvent} event
1807
2075
  */
1808
2076
  function addItem(event) {
1809
2077
  event.stopPropagation();
@@ -1915,14 +2183,14 @@ function ItemsList(props) {
1915
2183
  });
1916
2184
  }
1917
2185
 
1918
- /**
1919
- * Place new items in the beginning of the list and sort the rest with provided function.
1920
- *
1921
- * @template Item
1922
- * @param {Item[]} currentItems
1923
- * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
1924
- * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
1925
- * @returns {Item[]}
2186
+ /**
2187
+ * Place new items in the beginning of the list and sort the rest with provided function.
2188
+ *
2189
+ * @template Item
2190
+ * @param {Item[]} currentItems
2191
+ * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
2192
+ * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
2193
+ * @returns {Item[]}
1926
2194
  */
1927
2195
  function useSortedItems(currentItems, compareFn, shouldReset = false) {
1928
2196
  const itemsRef = useRef(currentItems.slice());
@@ -2018,22 +2286,22 @@ function NumberField(props) {
2018
2286
  });
2019
2287
  }
2020
2288
 
2021
- /**
2022
- * @param {Object} props
2023
- * @param {Boolean} props.debounce
2024
- * @param {String} props.description
2025
- * @param {Boolean} props.disabled
2026
- * @param {Object} props.element
2027
- * @param {Function} props.getValue
2028
- * @param {String} props.id
2029
- * @param {String} props.label
2030
- * @param {String} props.max
2031
- * @param {String} props.min
2032
- * @param {Function} props.setValue
2033
- * @param {Function} props.onFocus
2034
- * @param {Function} props.onBlur
2035
- * @param {String} props.step
2036
- * @param {Function} props.validate
2289
+ /**
2290
+ * @param {Object} props
2291
+ * @param {Boolean} props.debounce
2292
+ * @param {String} props.description
2293
+ * @param {Boolean} props.disabled
2294
+ * @param {Object} props.element
2295
+ * @param {Function} props.getValue
2296
+ * @param {String} props.id
2297
+ * @param {String} props.label
2298
+ * @param {String} props.max
2299
+ * @param {String} props.min
2300
+ * @param {Function} props.setValue
2301
+ * @param {Function} props.onFocus
2302
+ * @param {Function} props.onBlur
2303
+ * @param {String} props.step
2304
+ * @param {Function} props.validate
2037
2305
  */
2038
2306
  function NumberFieldEntry(props) {
2039
2307
  const {
@@ -2159,6 +2427,16 @@ function Select(props) {
2159
2427
  value: localValue,
2160
2428
  disabled: disabled,
2161
2429
  children: options.map((option, idx) => {
2430
+ if (option.children) {
2431
+ return jsx("optgroup", {
2432
+ label: option.label,
2433
+ children: option.children.map((child, idx) => jsx("option", {
2434
+ value: child.value,
2435
+ disabled: child.disabled,
2436
+ children: child.label
2437
+ }, idx))
2438
+ }, idx);
2439
+ }
2162
2440
  return jsx("option", {
2163
2441
  value: option.value,
2164
2442
  disabled: option.disabled,
@@ -2169,18 +2447,18 @@ function Select(props) {
2169
2447
  });
2170
2448
  }
2171
2449
 
2172
- /**
2173
- * @param {object} props
2174
- * @param {object} props.element
2175
- * @param {string} props.id
2176
- * @param {string} [props.description]
2177
- * @param {string} props.label
2178
- * @param {Function} props.getValue
2179
- * @param {Function} props.setValue
2180
- * @param {Function} props.onFocus
2181
- * @param {Function} props.onBlur
2182
- * @param {Function} props.getOptions
2183
- * @param {boolean} [props.disabled]
2450
+ /**
2451
+ * @param {object} props
2452
+ * @param {object} props.element
2453
+ * @param {string} props.id
2454
+ * @param {string} [props.description]
2455
+ * @param {string} props.label
2456
+ * @param {Function} props.getValue
2457
+ * @param {Function} props.setValue
2458
+ * @param {Function} props.onFocus
2459
+ * @param {Function} props.onBlur
2460
+ * @param {Function} props.getOptions
2461
+ * @param {boolean} [props.disabled]
2184
2462
  */
2185
2463
  function SelectEntry(props) {
2186
2464
  const {
@@ -2351,20 +2629,20 @@ function TextArea(props) {
2351
2629
  });
2352
2630
  }
2353
2631
 
2354
- /**
2355
- * @param {object} props
2356
- * @param {object} props.element
2357
- * @param {string} props.id
2358
- * @param {string} props.description
2359
- * @param {boolean} props.debounce
2360
- * @param {string} props.label
2361
- * @param {Function} props.getValue
2362
- * @param {Function} props.setValue
2363
- * @param {Function} props.onFocus
2364
- * @param {Function} props.onBlur
2365
- * @param {number} props.rows
2366
- * @param {boolean} props.monospace
2367
- * @param {boolean} [props.disabled]
2632
+ /**
2633
+ * @param {object} props
2634
+ * @param {object} props.element
2635
+ * @param {string} props.id
2636
+ * @param {string} props.description
2637
+ * @param {boolean} props.debounce
2638
+ * @param {string} props.label
2639
+ * @param {Function} props.getValue
2640
+ * @param {Function} props.setValue
2641
+ * @param {Function} props.onFocus
2642
+ * @param {Function} props.onBlur
2643
+ * @param {number} props.rows
2644
+ * @param {boolean} props.monospace
2645
+ * @param {boolean} [props.disabled]
2368
2646
  */
2369
2647
  function TextAreaEntry(props) {
2370
2648
  const {
@@ -2470,19 +2748,19 @@ function Textfield(props) {
2470
2748
  });
2471
2749
  }
2472
2750
 
2473
- /**
2474
- * @param {Object} props
2475
- * @param {Object} props.element
2476
- * @param {String} props.id
2477
- * @param {String} props.description
2478
- * @param {Boolean} props.debounce
2479
- * @param {Boolean} props.disabled
2480
- * @param {String} props.label
2481
- * @param {Function} props.getValue
2482
- * @param {Function} props.setValue
2483
- * @param {Function} props.onFocus
2484
- * @param {Function} props.onBlur
2485
- * @param {Function} props.validate
2751
+ /**
2752
+ * @param {Object} props
2753
+ * @param {Object} props.element
2754
+ * @param {String} props.id
2755
+ * @param {String} props.description
2756
+ * @param {Boolean} props.debounce
2757
+ * @param {Boolean} props.disabled
2758
+ * @param {String} props.label
2759
+ * @param {Function} props.getValue
2760
+ * @param {Function} props.setValue
2761
+ * @param {Function} props.onFocus
2762
+ * @param {Function} props.onBlur
2763
+ * @param {Function} props.validate
2486
2764
  */
2487
2765
  function TextfieldEntry(props) {
2488
2766
  const {
@@ -2611,17 +2889,17 @@ function ToggleSwitch(props) {
2611
2889
  });
2612
2890
  }
2613
2891
 
2614
- /**
2615
- * @param {Object} props
2616
- * @param {Object} props.element
2617
- * @param {String} props.id
2618
- * @param {String} props.description
2619
- * @param {String} props.label
2620
- * @param {String} props.switcherLabel
2621
- * @param {Function} props.getValue
2622
- * @param {Function} props.setValue
2623
- * @param {Function} props.onFocus
2624
- * @param {Function} props.onBlur
2892
+ /**
2893
+ * @param {Object} props
2894
+ * @param {Object} props.element
2895
+ * @param {String} props.id
2896
+ * @param {String} props.description
2897
+ * @param {String} props.label
2898
+ * @param {String} props.switcherLabel
2899
+ * @param {Function} props.getValue
2900
+ * @param {Function} props.setValue
2901
+ * @param {Function} props.onFocus
2902
+ * @param {Function} props.onBlur
2625
2903
  */
2626
2904
  function ToggleSwitchEntry(props) {
2627
2905
  const {
@@ -2681,5 +2959,5 @@ var index = {
2681
2959
  debounceInput: ['factory', debounceInput]
2682
2960
  };
2683
2961
 
2684
- export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelEntry, FeelOptionalIcon, FeelRequiredIcon, FeelTextArea as FeelTextAreaEntry, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, Placeholder, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$7 as isCheckboxEntryEdited, isEdited$6 as isFeelEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useError, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver };
2962
+ export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelEntry, FeelOptionalIcon, FeelRequiredIcon, FeelTemplatingEntry, FeelTextArea as FeelTextAreaEntry, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, Placeholder, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$8 as isCheckboxEntryEdited, isEdited$7 as isFeelEntryEdited, isEdited$6 as isFeelTemplatingEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useError, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver };
2685
2963
  //# sourceMappingURL=index.esm.js.map