@bpmn-io/properties-panel 1.5.0 → 1.6.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
@@ -176,19 +176,19 @@ const ErrorsContext = preact.createContext({
176
176
  errors: {}
177
177
  });
178
178
 
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
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
192
192
  */
193
193
  const EventContext = preact.createContext({
194
194
  eventBus: null
@@ -201,20 +201,20 @@ const LayoutContext = preact.createContext({
201
201
  setLayoutForKey: () => {}
202
202
  });
203
203
 
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}
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}
218
218
  */
219
219
  function useDescriptionContext(id, element) {
220
220
  const {
@@ -230,11 +230,11 @@ function useError(id) {
230
230
  return errors[id];
231
231
  }
232
232
 
233
- /**
234
- * Subscribe to an event immediately. Update subscription after inputs changed.
235
- *
236
- * @param {string} event
237
- * @param {Function} callback
233
+ /**
234
+ * Subscribe to an event immediately. Update subscription after inputs changed.
235
+ *
236
+ * @param {string} event
237
+ * @param {Function} callback
238
238
  */
239
239
  function useEvent(event, callback, eventBus) {
240
240
  const eventContext = hooks.useContext(EventContext);
@@ -266,24 +266,24 @@ function useEvent(event, callback, eventBus) {
266
266
 
267
267
  const KEY_LENGTH = 6;
268
268
 
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}
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}
287
287
  */
288
288
  function useKeyFactory(dependencies = []) {
289
289
  const map = hooks.useMemo(() => new Map(), dependencies);
@@ -298,20 +298,20 @@ function useKeyFactory(dependencies = []) {
298
298
  return getKey;
299
299
  }
300
300
 
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 ]}
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 ]}
315
315
  */
316
316
  function useLayoutState(path, defaultValue) {
317
317
  const {
@@ -319,17 +319,22 @@ function useLayoutState(path, defaultValue) {
319
319
  setLayoutForKey
320
320
  } = hooks.useContext(LayoutContext);
321
321
  const layoutForKey = getLayoutForKey(path, defaultValue);
322
- const setState = hooks.useCallback(newValue => {
322
+ const [value, set] = hooks.useState(layoutForKey);
323
+ const setState = newValue => {
324
+ // (1) set component state
325
+ set(newValue);
326
+
327
+ // (2) set context
323
328
  setLayoutForKey(path, newValue);
324
- }, [setLayoutForKey]);
325
- return [layoutForKey, setState];
329
+ };
330
+ return [value, setState];
326
331
  }
327
332
 
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
333
+ /**
334
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
335
+ * state on updates.
336
+ *
337
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
333
338
  */
334
339
 
335
340
  function usePrevious(value) {
@@ -340,12 +345,12 @@ function usePrevious(value) {
340
345
  return ref.current;
341
346
  }
342
347
 
343
- /**
344
- * Subscribe to `propertiesPanel.showEntry`.
345
- *
346
- * @param {string} id
347
- *
348
- * @returns {import('preact').Ref}
348
+ /**
349
+ * Subscribe to `propertiesPanel.showEntry`.
350
+ *
351
+ * @param {string} id
352
+ *
353
+ * @returns {import('preact').Ref}
349
354
  */
350
355
  function useShowEntryEvent(id) {
351
356
  const {
@@ -376,20 +381,20 @@ function useShowEntryEvent(id) {
376
381
  return ref;
377
382
  }
378
383
 
379
- /**
380
- * @callback setSticky
381
- * @param {boolean} value
384
+ /**
385
+ * @callback setSticky
386
+ * @param {boolean} value
382
387
  */
383
388
 
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
389
+ /**
390
+ * Use IntersectionObserver to identify when DOM element is in sticky mode.
391
+ * If sticky is observered setSticky(true) will be called.
392
+ * If sticky mode is left, setSticky(false) will be called.
393
+ *
394
+ *
395
+ * @param {Object} ref
396
+ * @param {string} scrollContainerSelector
397
+ * @param {setSticky} setSticky
393
398
  */
394
399
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
395
400
  hooks.useEffect(() => {
@@ -428,19 +433,19 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
428
433
  }, [ref, scrollContainerSelector, setSticky]);
429
434
  }
430
435
 
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
436
+ /**
437
+ * Creates a static function reference with changing body.
438
+ * This is necessary when external libraries require a callback function
439
+ * that has references to state variables.
440
+ *
441
+ * Usage:
442
+ * const callback = useStaticCallback((val) => {val === currentState});
443
+ *
444
+ * The `callback` reference is static and can be safely used in external
445
+ * libraries or as a prop that does not cause rerendering of children.
446
+ *
447
+ * @param {Function} callback function with changing reference
448
+ * @returns {Function} static function reference
444
449
  */
445
450
  function useStaticCallback(callback) {
446
451
  const callbackRef = hooks.useRef(callback);
@@ -533,13 +538,13 @@ function DataMarker() {
533
538
  });
534
539
  }
535
540
 
536
- /**
537
- * @typedef { {
538
- * text: (element: object) => string,
539
- * icon?: (element: Object) => import('preact').Component
540
- * } } PlaceholderDefinition
541
- *
542
- * @param { PlaceholderDefinition } props
541
+ /**
542
+ * @typedef { {
543
+ * text: (element: object) => string,
544
+ * icon?: (element: Object) => import('preact').Component
545
+ * } } PlaceholderDefinition
546
+ *
547
+ * @param { PlaceholderDefinition } props
543
548
  */
544
549
  function Placeholder(props) {
545
550
  const {
@@ -565,72 +570,72 @@ const DEFAULT_LAYOUT = {
565
570
  };
566
571
  const DEFAULT_DESCRIPTION = {};
567
572
 
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
- *
573
+ /**
574
+ * @typedef { {
575
+ * component: import('preact').Component,
576
+ * id: String,
577
+ * isEdited?: Function
578
+ * } } EntryDefinition
579
+ *
580
+ * @typedef { {
581
+ * autoFocusEntry: String,
582
+ * autoOpen?: Boolean,
583
+ * entries: Array<EntryDefinition>,
584
+ * id: String,
585
+ * label: String,
586
+ * remove: (event: MouseEvent) => void
587
+ * } } ListItemDefinition
588
+ *
589
+ * @typedef { {
590
+ * add: (event: MouseEvent) => void,
591
+ * component: import('preact').Component,
592
+ * element: Object,
593
+ * id: String,
594
+ * items: Array<ListItemDefinition>,
595
+ * label: String,
596
+ * shouldSort?: Boolean,
597
+ * shouldOpen?: Boolean
598
+ * } } ListGroupDefinition
599
+ *
600
+ * @typedef { {
601
+ * component?: import('preact').Component,
602
+ * entries: Array<EntryDefinition>,
603
+ * id: String,
604
+ * label: String,
605
+ * shouldOpen?: Boolean
606
+ * } } GroupDefinition
607
+ *
608
+ * @typedef { {
609
+ * [id: String]: GetDescriptionFunction
610
+ * } } DescriptionConfig
611
+ *
612
+ * @callback { {
613
+ * @param {string} id
614
+ * @param {Object} element
615
+ * @returns {string}
616
+ * } } GetDescriptionFunction
617
+ *
618
+ * @typedef { {
619
+ * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
620
+ * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
621
+ * } } PlaceholderProvider
622
+ *
618
623
  */
619
624
 
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]
625
+ /**
626
+ * A basic properties panel component. Describes *how* content will be rendered, accepts
627
+ * data from implementor to describe *what* will be rendered.
628
+ *
629
+ * @param {Object} props
630
+ * @param {Object|Array} props.element
631
+ * @param {import('./components/Header').HeaderProvider} props.headerProvider
632
+ * @param {PlaceholderProvider} [props.placeholderProvider]
633
+ * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
634
+ * @param {Object} [props.layoutConfig]
635
+ * @param {Function} [props.layoutChanged]
636
+ * @param {DescriptionConfig} [props.descriptionConfig]
637
+ * @param {Function} [props.descriptionLoaded]
638
+ * @param {Object} [props.eventBus]
634
639
  */
635
640
  function PropertiesPanel(props) {
636
641
  const {
@@ -647,12 +652,6 @@ function PropertiesPanel(props) {
647
652
 
648
653
  // set-up layout context
649
654
  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]);
656
655
  hooks.useEffect(() => {
657
656
  if (typeof layoutChanged === 'function') {
658
657
  layoutChanged(layout);
@@ -752,9 +751,9 @@ function PropertiesPanel(props) {
752
751
 
753
752
  // helpers //////////////////
754
753
 
755
- function createLayout(overrides, defaults = DEFAULT_LAYOUT) {
754
+ function createLayout(overrides) {
756
755
  return {
757
- ...defaults,
756
+ ...DEFAULT_LAYOUT,
758
757
  ...overrides
759
758
  };
760
759
  }
@@ -826,15 +825,15 @@ function MenuItem({
826
825
  });
827
826
  }
828
827
 
829
- /**
830
- *
831
- * @param {Array<null | Element>} ignoredElements
832
- * @param {Function} callback
828
+ /**
829
+ *
830
+ * @param {Array<null | Element>} ignoredElements
831
+ * @param {Function} callback
833
832
  */
834
833
  function useGlobalClick(ignoredElements, callback) {
835
834
  hooks.useEffect(() => {
836
- /**
837
- * @param {MouseEvent} event
835
+ /**
836
+ * @param {MouseEvent} event
838
837
  */
839
838
  function listener(event) {
840
839
  if (ignoredElements.some(element => element && element.contains(event.target))) {
@@ -965,8 +964,8 @@ function ListItem(props) {
965
964
 
966
965
  const noop$3 = () => {};
967
966
 
968
- /**
969
- * @param {import('../PropertiesPanel').ListGroupDefinition} props
967
+ /**
968
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
970
969
  */
971
970
  function ListGroup(props) {
972
971
  const {
@@ -1129,8 +1128,8 @@ function ListGroup(props) {
1129
1128
 
1130
1129
  // helpers ////////////////////
1131
1130
 
1132
- /**
1133
- * Sorts given items alphanumeric by label
1131
+ /**
1132
+ * Sorts given items alphanumeric by label
1134
1133
  */
1135
1134
  function sortItems(items) {
1136
1135
  return minDash.sortBy(items, i => i.label.toLowerCase());
@@ -1206,17 +1205,17 @@ function Checkbox(props) {
1206
1205
  });
1207
1206
  }
1208
1207
 
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]
1208
+ /**
1209
+ * @param {Object} props
1210
+ * @param {Object} props.element
1211
+ * @param {String} props.id
1212
+ * @param {String} props.description
1213
+ * @param {String} props.label
1214
+ * @param {Function} props.getValue
1215
+ * @param {Function} props.setValue
1216
+ * @param {Function} props.onFocus
1217
+ * @param {Function} props.onBlur
1218
+ * @param {boolean} [props.disabled]
1220
1219
  */
1221
1220
  function CheckboxEntry(props) {
1222
1221
  const {
@@ -1305,10 +1304,10 @@ const CodeEditor$1 = compat.forwardRef((props, ref) => {
1305
1304
  hooks.useEffect(() => {
1306
1305
  let editor;
1307
1306
 
1308
- /* Trigger FEEL toggle when
1309
- *
1310
- * - `backspace` is pressed
1311
- * - AND the cursor is at the beginning of the input
1307
+ /* Trigger FEEL toggle when
1308
+ *
1309
+ * - `backspace` is pressed
1310
+ * - AND the cursor is at the beginning of the input
1312
1311
  */
1313
1312
  const onKeyDown = e => {
1314
1313
  if (e.key !== 'Backspace' || !editor) {
@@ -1381,10 +1380,10 @@ function FeelIndicator(props) {
1381
1380
 
1382
1381
  const noop$2 = () => {};
1383
1382
 
1384
- /**
1385
- * @param {Object} props
1386
- * @param {Object} props.label
1387
- * @param {String} props.feel
1383
+ /**
1384
+ * @param {Object} props
1385
+ * @param {Object} props.label
1386
+ * @param {String} props.feel
1388
1387
  */
1389
1388
  function FeelIcon(props) {
1390
1389
  const {
@@ -1659,24 +1658,24 @@ const OptionalFeelTextArea = compat.forwardRef((props, ref) => {
1659
1658
  });
1660
1659
  });
1661
1660
 
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
1661
+ /**
1662
+ * @param {Object} props
1663
+ * @param {Object} props.element
1664
+ * @param {String} props.id
1665
+ * @param {String} props.description
1666
+ * @param {Boolean} props.debounce
1667
+ * @param {Boolean} props.disabled
1668
+ * @param {Boolean} props.feel
1669
+ * @param {String} props.label
1670
+ * @param {Function} props.getValue
1671
+ * @param {Function} props.setValue
1672
+ * @param {Function} props.tooltipContainer
1673
+ * @param {Function} props.validate
1674
+ * @param {Function} props.show
1675
+ * @param {Function} props.example
1676
+ * @param {Function} props.variables
1677
+ * @param {Function} props.onFocus
1678
+ * @param {Function} props.onBlur
1680
1679
  */
1681
1680
  function FeelEntry(props) {
1682
1681
  const {
@@ -1761,24 +1760,24 @@ function FeelEntry(props) {
1761
1760
  });
1762
1761
  }
1763
1762
 
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
1763
+ /**
1764
+ * @param {Object} props
1765
+ * @param {Object} props.element
1766
+ * @param {String} props.id
1767
+ * @param {String} props.description
1768
+ * @param {Boolean} props.debounce
1769
+ * @param {Boolean} props.disabled
1770
+ * @param {Boolean} props.feel
1771
+ * @param {String} props.label
1772
+ * @param {Function} props.getValue
1773
+ * @param {Function} props.setValue
1774
+ * @param {Function} props.tooltipContainer
1775
+ * @param {Function} props.validate
1776
+ * @param {Function} props.show
1777
+ * @param {Function} props.example
1778
+ * @param {Function} props.variables
1779
+ * @param {Function} props.onFocus
1780
+ * @param {Function} props.onBlur
1782
1781
  */
1783
1782
  function FeelTextArea(props) {
1784
1783
  return jsxRuntime.jsx(FeelEntry, {
@@ -1879,19 +1878,19 @@ const CodeEditor = compat.forwardRef((props, ref) => {
1879
1878
 
1880
1879
  const noop = () => {};
1881
1880
 
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
1881
+ /**
1882
+ * @param {Object} props
1883
+ * @param {Object} props.element
1884
+ * @param {String} props.id
1885
+ * @param {String} props.description
1886
+ * @param {Boolean} props.debounce
1887
+ * @param {Boolean} props.disabled
1888
+ * @param {String} props.label
1889
+ * @param {Function} props.getValue
1890
+ * @param {Function} props.setValue
1891
+ * @param {Function} props.tooltipContainer
1892
+ * @param {Function} props.validate
1893
+ * @param {Function} props.show
1895
1894
  */
1896
1895
  function FeelTemplatingEntry(props) {
1897
1896
  const {
@@ -2079,8 +2078,8 @@ function List(props) {
2079
2078
  }
2080
2079
  }, [open, hasItems]);
2081
2080
 
2082
- /**
2083
- * @param {MouseEvent} event
2081
+ /**
2082
+ * @param {MouseEvent} event
2084
2083
  */
2085
2084
  function addItem(event) {
2086
2085
  event.stopPropagation();
@@ -2192,14 +2191,14 @@ function ItemsList(props) {
2192
2191
  });
2193
2192
  }
2194
2193
 
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[]}
2194
+ /**
2195
+ * Place new items in the beginning of the list and sort the rest with provided function.
2196
+ *
2197
+ * @template Item
2198
+ * @param {Item[]} currentItems
2199
+ * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
2200
+ * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
2201
+ * @returns {Item[]}
2203
2202
  */
2204
2203
  function useSortedItems(currentItems, compareFn, shouldReset = false) {
2205
2204
  const itemsRef = hooks.useRef(currentItems.slice());
@@ -2295,22 +2294,22 @@ function NumberField(props) {
2295
2294
  });
2296
2295
  }
2297
2296
 
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
2297
+ /**
2298
+ * @param {Object} props
2299
+ * @param {Boolean} props.debounce
2300
+ * @param {String} props.description
2301
+ * @param {Boolean} props.disabled
2302
+ * @param {Object} props.element
2303
+ * @param {Function} props.getValue
2304
+ * @param {String} props.id
2305
+ * @param {String} props.label
2306
+ * @param {String} props.max
2307
+ * @param {String} props.min
2308
+ * @param {Function} props.setValue
2309
+ * @param {Function} props.onFocus
2310
+ * @param {Function} props.onBlur
2311
+ * @param {String} props.step
2312
+ * @param {Function} props.validate
2314
2313
  */
2315
2314
  function NumberFieldEntry(props) {
2316
2315
  const {
@@ -2456,18 +2455,19 @@ function Select(props) {
2456
2455
  });
2457
2456
  }
2458
2457
 
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]
2458
+ /**
2459
+ * @param {object} props
2460
+ * @param {object} props.element
2461
+ * @param {string} props.id
2462
+ * @param {string} [props.description]
2463
+ * @param {string} props.label
2464
+ * @param {Function} props.getValue
2465
+ * @param {Function} props.setValue
2466
+ * @param {Function} props.onFocus
2467
+ * @param {Function} props.onBlur
2468
+ * @param {Function} props.getOptions
2469
+ * @param {boolean} [props.disabled]
2470
+ * @param {Function} [props.validate]
2471
2471
  */
2472
2472
  function SelectEntry(props) {
2473
2473
  const {
@@ -2480,11 +2480,37 @@ function SelectEntry(props) {
2480
2480
  getOptions,
2481
2481
  disabled,
2482
2482
  onFocus,
2483
- onBlur
2483
+ onBlur,
2484
+ validate
2484
2485
  } = props;
2485
- const value = getValue(element);
2486
2486
  const options = getOptions(element);
2487
- const error = useError(id);
2487
+ const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
2488
+ const globalError = useError(id);
2489
+ const [localError, setLocalError] = hooks.useState(null);
2490
+ let value = getValue(element);
2491
+ const previousValue = usePrevious(value);
2492
+ hooks.useEffect(() => {
2493
+ if (minDash.isFunction(validate)) {
2494
+ const newValidationError = validate(value) || null;
2495
+ setLocalError(newValidationError);
2496
+ }
2497
+ }, [value]);
2498
+ const onChange = newValue => {
2499
+ let newValidationError = null;
2500
+ if (minDash.isFunction(validate)) {
2501
+ newValidationError = validate(newValue) || null;
2502
+ }
2503
+ if (newValidationError) {
2504
+ setCachedInvalidValue(newValue);
2505
+ } else {
2506
+ setValue(newValue);
2507
+ }
2508
+ setLocalError(newValidationError);
2509
+ };
2510
+ if (previousValue === value && localError) {
2511
+ value = cachedInvalidValue;
2512
+ }
2513
+ const error = globalError || localError;
2488
2514
  return jsxRuntime.jsxs("div", {
2489
2515
  class: classnames__default["default"]('bio-properties-panel-entry', error ? 'has-error' : ''),
2490
2516
  "data-entry-id": id,
@@ -2492,7 +2518,7 @@ function SelectEntry(props) {
2492
2518
  id: id,
2493
2519
  label: label,
2494
2520
  value: value,
2495
- onChange: setValue,
2521
+ onChange: onChange,
2496
2522
  onFocus: onFocus,
2497
2523
  onBlur: onBlur,
2498
2524
  options: options,
@@ -2638,20 +2664,20 @@ function TextArea(props) {
2638
2664
  });
2639
2665
  }
2640
2666
 
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]
2667
+ /**
2668
+ * @param {object} props
2669
+ * @param {object} props.element
2670
+ * @param {string} props.id
2671
+ * @param {string} props.description
2672
+ * @param {boolean} props.debounce
2673
+ * @param {string} props.label
2674
+ * @param {Function} props.getValue
2675
+ * @param {Function} props.setValue
2676
+ * @param {Function} props.onFocus
2677
+ * @param {Function} props.onBlur
2678
+ * @param {number} props.rows
2679
+ * @param {boolean} props.monospace
2680
+ * @param {boolean} [props.disabled]
2655
2681
  */
2656
2682
  function TextAreaEntry(props) {
2657
2683
  const {
@@ -2757,19 +2783,19 @@ function Textfield(props) {
2757
2783
  });
2758
2784
  }
2759
2785
 
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
2786
+ /**
2787
+ * @param {Object} props
2788
+ * @param {Object} props.element
2789
+ * @param {String} props.id
2790
+ * @param {String} props.description
2791
+ * @param {Boolean} props.debounce
2792
+ * @param {Boolean} props.disabled
2793
+ * @param {String} props.label
2794
+ * @param {Function} props.getValue
2795
+ * @param {Function} props.setValue
2796
+ * @param {Function} props.onFocus
2797
+ * @param {Function} props.onBlur
2798
+ * @param {Function} props.validate
2773
2799
  */
2774
2800
  function TextfieldEntry(props) {
2775
2801
  const {
@@ -2898,17 +2924,17 @@ function ToggleSwitch(props) {
2898
2924
  });
2899
2925
  }
2900
2926
 
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
2927
+ /**
2928
+ * @param {Object} props
2929
+ * @param {Object} props.element
2930
+ * @param {String} props.id
2931
+ * @param {String} props.description
2932
+ * @param {String} props.label
2933
+ * @param {String} props.switcherLabel
2934
+ * @param {Function} props.getValue
2935
+ * @param {Function} props.setValue
2936
+ * @param {Function} props.onFocus
2937
+ * @param {Function} props.onBlur
2912
2938
  */
2913
2939
  function ToggleSwitchEntry(props) {
2914
2940
  const {