@bpmn-io/properties-panel 1.1.0 → 1.2.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
@@ -175,19 +175,19 @@ const ErrorsContext = preact.createContext({
175
175
  errors: {}
176
176
  });
177
177
 
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
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
191
191
  */
192
192
  const EventContext = preact.createContext({
193
193
  eventBus: null
@@ -200,20 +200,20 @@ const LayoutContext = preact.createContext({
200
200
  setLayoutForKey: () => {}
201
201
  });
202
202
 
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}
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}
217
217
  */
218
218
  function useDescriptionContext(id, element) {
219
219
  const {
@@ -229,11 +229,11 @@ function useError(id) {
229
229
  return errors[id];
230
230
  }
231
231
 
232
- /**
233
- * Subscribe to an event immediately. Update subscription after inputs changed.
234
- *
235
- * @param {string} event
236
- * @param {Function} callback
232
+ /**
233
+ * Subscribe to an event immediately. Update subscription after inputs changed.
234
+ *
235
+ * @param {string} event
236
+ * @param {Function} callback
237
237
  */
238
238
  function useEvent(event, callback, eventBus) {
239
239
  const eventContext = hooks.useContext(EventContext);
@@ -265,24 +265,24 @@ function useEvent(event, callback, eventBus) {
265
265
 
266
266
  const KEY_LENGTH = 6;
267
267
 
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}
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}
286
286
  */
287
287
  function useKeyFactory(dependencies = []) {
288
288
  const map = hooks.useMemo(() => new Map(), dependencies);
@@ -297,20 +297,20 @@ function useKeyFactory(dependencies = []) {
297
297
  return getKey;
298
298
  }
299
299
 
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 ]}
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 ]}
314
314
  */
315
315
  function useLayoutState(path, defaultValue) {
316
316
  const {
@@ -329,11 +329,11 @@ function useLayoutState(path, defaultValue) {
329
329
  return [value, setState];
330
330
  }
331
331
 
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
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
337
337
  */
338
338
 
339
339
  function usePrevious(value) {
@@ -344,12 +344,12 @@ function usePrevious(value) {
344
344
  return ref.current;
345
345
  }
346
346
 
347
- /**
348
- * Subscribe to `propertiesPanel.showEntry`.
349
- *
350
- * @param {string} id
351
- *
352
- * @returns {import('preact').Ref}
347
+ /**
348
+ * Subscribe to `propertiesPanel.showEntry`.
349
+ *
350
+ * @param {string} id
351
+ *
352
+ * @returns {import('preact').Ref}
353
353
  */
354
354
  function useShowEntryEvent(id) {
355
355
  const {
@@ -380,20 +380,20 @@ function useShowEntryEvent(id) {
380
380
  return ref;
381
381
  }
382
382
 
383
- /**
384
- * @callback setSticky
385
- * @param {boolean} value
383
+ /**
384
+ * @callback setSticky
385
+ * @param {boolean} value
386
386
  */
387
387
 
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
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
397
397
  */
398
398
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
399
399
  hooks.useEffect(() => {
@@ -432,19 +432,19 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
432
432
  }, [ref, scrollContainerSelector, setSticky]);
433
433
  }
434
434
 
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
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
448
448
  */
449
449
  function useStaticCallback(callback) {
450
450
  const callbackRef = hooks.useRef(callback);
@@ -537,13 +537,13 @@ function DataMarker() {
537
537
  });
538
538
  }
539
539
 
540
- /**
541
- * @typedef { {
542
- * text: (element: object) => string,
543
- * icon?: (element: Object) => import('preact').Component
544
- * } } PlaceholderDefinition
545
- *
546
- * @param { PlaceholderDefinition } props
540
+ /**
541
+ * @typedef { {
542
+ * text: (element: object) => string,
543
+ * icon?: (element: Object) => import('preact').Component
544
+ * } } PlaceholderDefinition
545
+ *
546
+ * @param { PlaceholderDefinition } props
547
547
  */
548
548
  function Placeholder(props) {
549
549
  const {
@@ -569,72 +569,72 @@ const DEFAULT_LAYOUT = {
569
569
  };
570
570
  const DEFAULT_DESCRIPTION = {};
571
571
 
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
- *
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
+ *
622
622
  */
623
623
 
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]
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]
638
638
  */
639
639
  function PropertiesPanel(props) {
640
640
  const {
@@ -824,15 +824,15 @@ function MenuItem({
824
824
  });
825
825
  }
826
826
 
827
- /**
828
- *
829
- * @param {Array<null | Element>} ignoredElements
830
- * @param {Function} callback
827
+ /**
828
+ *
829
+ * @param {Array<null | Element>} ignoredElements
830
+ * @param {Function} callback
831
831
  */
832
832
  function useGlobalClick(ignoredElements, callback) {
833
833
  hooks.useEffect(() => {
834
- /**
835
- * @param {MouseEvent} event
834
+ /**
835
+ * @param {MouseEvent} event
836
836
  */
837
837
  function listener(event) {
838
838
  if (ignoredElements.some(element => element && element.contains(event.target))) {
@@ -963,8 +963,8 @@ function ListItem(props) {
963
963
 
964
964
  const noop$2 = () => {};
965
965
 
966
- /**
967
- * @param {import('../PropertiesPanel').ListGroupDefinition} props
966
+ /**
967
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
968
968
  */
969
969
  function ListGroup(props) {
970
970
  const {
@@ -1127,8 +1127,8 @@ function ListGroup(props) {
1127
1127
 
1128
1128
  // helpers ////////////////////
1129
1129
 
1130
- /**
1131
- * Sorts given items alphanumeric by label
1130
+ /**
1131
+ * Sorts given items alphanumeric by label
1132
1132
  */
1133
1133
  function sortItems(items) {
1134
1134
  return minDash.sortBy(items, i => i.label.toLowerCase());
@@ -1204,17 +1204,17 @@ function Checkbox(props) {
1204
1204
  });
1205
1205
  }
1206
1206
 
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]
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]
1218
1218
  */
1219
1219
  function CheckboxEntry(props) {
1220
1220
  const {
@@ -1303,10 +1303,10 @@ const CodeEditor = compat.forwardRef((props, ref) => {
1303
1303
  hooks.useEffect(() => {
1304
1304
  let editor;
1305
1305
 
1306
- /* Trigger FEEL toggle when
1307
- *
1308
- * - `backspace` is pressed
1309
- * - AND the cursor is at the beginning of the input
1306
+ /* Trigger FEEL toggle when
1307
+ *
1308
+ * - `backspace` is pressed
1309
+ * - AND the cursor is at the beginning of the input
1310
1310
  */
1311
1311
  const onKeyDown = e => {
1312
1312
  if (e.key !== 'Backspace' || !editor) {
@@ -1379,10 +1379,10 @@ function FeelIndicator(props) {
1379
1379
 
1380
1380
  const noop$1 = () => {};
1381
1381
 
1382
- /**
1383
- * @param {Object} props
1384
- * @param {Object} props.label
1385
- * @param {String} props.feel
1382
+ /**
1383
+ * @param {Object} props
1384
+ * @param {Object} props.label
1385
+ * @param {String} props.feel
1386
1386
  */
1387
1387
  function FeelIcon(props) {
1388
1388
  const {
@@ -1657,17 +1657,17 @@ const OptionalFeelTextArea = compat.forwardRef((props, ref) => {
1657
1657
  });
1658
1658
  });
1659
1659
 
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
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
1671
1671
  */
1672
1672
  function FeelEntry(props) {
1673
1673
  const {
@@ -1752,19 +1752,19 @@ function FeelEntry(props) {
1752
1752
  });
1753
1753
  }
1754
1754
 
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
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
1768
1768
  */
1769
1769
  function FeelTextArea(props) {
1770
1770
  return jsxRuntime.jsx(FeelEntry, {
@@ -1811,8 +1811,8 @@ function List(props) {
1811
1811
  }
1812
1812
  }, [open, hasItems]);
1813
1813
 
1814
- /**
1815
- * @param {MouseEvent} event
1814
+ /**
1815
+ * @param {MouseEvent} event
1816
1816
  */
1817
1817
  function addItem(event) {
1818
1818
  event.stopPropagation();
@@ -1924,14 +1924,14 @@ function ItemsList(props) {
1924
1924
  });
1925
1925
  }
1926
1926
 
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[]}
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[]}
1935
1935
  */
1936
1936
  function useSortedItems(currentItems, compareFn, shouldReset = false) {
1937
1937
  const itemsRef = hooks.useRef(currentItems.slice());
@@ -2027,21 +2027,22 @@ function NumberField(props) {
2027
2027
  });
2028
2028
  }
2029
2029
 
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
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
2045
2046
  */
2046
2047
  function NumberFieldEntry(props) {
2047
2048
  const {
@@ -2057,25 +2058,55 @@ function NumberFieldEntry(props) {
2057
2058
  setValue,
2058
2059
  step,
2059
2060
  onFocus,
2060
- onBlur
2061
+ onBlur,
2062
+ validate
2061
2063
  } = props;
2062
- const value = getValue(element);
2064
+ const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
2065
+ const globalError = useError(id);
2066
+ const [localError, setLocalError] = hooks.useState(null);
2067
+ let value = getValue(element);
2068
+ const previousValue = usePrevious(value);
2069
+ hooks.useEffect(() => {
2070
+ if (minDash.isFunction(validate)) {
2071
+ const newValidationError = validate(value) || null;
2072
+ setLocalError(newValidationError);
2073
+ }
2074
+ }, [value]);
2075
+ const onInput = newValue => {
2076
+ let newValidationError = null;
2077
+ if (minDash.isFunction(validate)) {
2078
+ newValidationError = validate(newValue) || null;
2079
+ }
2080
+ if (newValidationError) {
2081
+ setCachedInvalidValue(newValue);
2082
+ } else {
2083
+ setValue(newValue);
2084
+ }
2085
+ setLocalError(newValidationError);
2086
+ };
2087
+ if (previousValue === value && localError) {
2088
+ value = cachedInvalidValue;
2089
+ }
2090
+ const error = globalError || localError;
2063
2091
  return jsxRuntime.jsxs("div", {
2064
- class: "bio-properties-panel-entry",
2092
+ class: classnames__default["default"]('bio-properties-panel-entry', error ? 'has-error' : ''),
2065
2093
  "data-entry-id": id,
2066
2094
  children: [jsxRuntime.jsx(NumberField, {
2067
2095
  debounce: debounce,
2068
2096
  disabled: disabled,
2069
2097
  id: id,
2070
2098
  label: label,
2071
- onInput: setValue,
2072
2099
  onFocus: onFocus,
2073
2100
  onBlur: onBlur,
2101
+ onInput: onInput,
2074
2102
  max: max,
2075
2103
  min: min,
2076
2104
  step: step,
2077
2105
  value: value
2078
- }, element), jsxRuntime.jsx(Description, {
2106
+ }, element), error && jsxRuntime.jsx("div", {
2107
+ class: "bio-properties-panel-error",
2108
+ children: error
2109
+ }), jsxRuntime.jsx(Description, {
2079
2110
  forId: id,
2080
2111
  element: element,
2081
2112
  value: description
@@ -2098,7 +2129,7 @@ function Select(props) {
2098
2129
  label,
2099
2130
  onChange,
2100
2131
  options = [],
2101
- value,
2132
+ value = '',
2102
2133
  disabled,
2103
2134
  onFocus,
2104
2135
  onBlur
@@ -2147,18 +2178,18 @@ function Select(props) {
2147
2178
  });
2148
2179
  }
2149
2180
 
2150
- /**
2151
- * @param {object} props
2152
- * @param {object} props.element
2153
- * @param {string} props.id
2154
- * @param {string} [props.description]
2155
- * @param {string} props.label
2156
- * @param {Function} props.getValue
2157
- * @param {Function} props.setValue
2158
- * @param {Function} props.onFocus
2159
- * @param {Function} props.onBlur
2160
- * @param {Function} props.getOptions
2161
- * @param {boolean} [props.disabled]
2181
+ /**
2182
+ * @param {object} props
2183
+ * @param {object} props.element
2184
+ * @param {string} props.id
2185
+ * @param {string} [props.description]
2186
+ * @param {string} props.label
2187
+ * @param {Function} props.getValue
2188
+ * @param {Function} props.setValue
2189
+ * @param {Function} props.onFocus
2190
+ * @param {Function} props.onBlur
2191
+ * @param {Function} props.getOptions
2192
+ * @param {boolean} [props.disabled]
2162
2193
  */
2163
2194
  function SelectEntry(props) {
2164
2195
  const {
@@ -2317,20 +2348,20 @@ function TextArea(props) {
2317
2348
  });
2318
2349
  }
2319
2350
 
2320
- /**
2321
- * @param {object} props
2322
- * @param {object} props.element
2323
- * @param {string} props.id
2324
- * @param {string} props.description
2325
- * @param {boolean} props.debounce
2326
- * @param {string} props.label
2327
- * @param {Function} props.getValue
2328
- * @param {Function} props.setValue
2329
- * @param {Function} props.onFocus
2330
- * @param {Function} props.onBlur
2331
- * @param {number} props.rows
2332
- * @param {boolean} props.monospace
2333
- * @param {boolean} [props.disabled]
2351
+ /**
2352
+ * @param {object} props
2353
+ * @param {object} props.element
2354
+ * @param {string} props.id
2355
+ * @param {string} props.description
2356
+ * @param {boolean} props.debounce
2357
+ * @param {string} props.label
2358
+ * @param {Function} props.getValue
2359
+ * @param {Function} props.setValue
2360
+ * @param {Function} props.onFocus
2361
+ * @param {Function} props.onBlur
2362
+ * @param {number} props.rows
2363
+ * @param {boolean} props.monospace
2364
+ * @param {boolean} [props.disabled]
2334
2365
  */
2335
2366
  function TextAreaEntry(props) {
2336
2367
  const {
@@ -2434,19 +2465,19 @@ function Textfield(props) {
2434
2465
  });
2435
2466
  }
2436
2467
 
2437
- /**
2438
- * @param {Object} props
2439
- * @param {Object} props.element
2440
- * @param {String} props.id
2441
- * @param {String} props.description
2442
- * @param {Boolean} props.debounce
2443
- * @param {Boolean} props.disabled
2444
- * @param {String} props.label
2445
- * @param {Function} props.getValue
2446
- * @param {Function} props.setValue
2447
- * @param {Function} props.onFocus
2448
- * @param {Function} props.onBlur
2449
- * @param {Function} props.validate
2468
+ /**
2469
+ * @param {Object} props
2470
+ * @param {Object} props.element
2471
+ * @param {String} props.id
2472
+ * @param {String} props.description
2473
+ * @param {Boolean} props.debounce
2474
+ * @param {Boolean} props.disabled
2475
+ * @param {String} props.label
2476
+ * @param {Function} props.getValue
2477
+ * @param {Function} props.setValue
2478
+ * @param {Function} props.onFocus
2479
+ * @param {Function} props.onBlur
2480
+ * @param {Function} props.validate
2450
2481
  */
2451
2482
  function TextfieldEntry(props) {
2452
2483
  const {
@@ -2575,17 +2606,17 @@ function ToggleSwitch(props) {
2575
2606
  });
2576
2607
  }
2577
2608
 
2578
- /**
2579
- * @param {Object} props
2580
- * @param {Object} props.element
2581
- * @param {String} props.id
2582
- * @param {String} props.description
2583
- * @param {String} props.label
2584
- * @param {String} props.switcherLabel
2585
- * @param {Function} props.getValue
2586
- * @param {Function} props.setValue
2587
- * @param {Function} props.onFocus
2588
- * @param {Function} props.onBlur
2609
+ /**
2610
+ * @param {Object} props
2611
+ * @param {Object} props.element
2612
+ * @param {String} props.id
2613
+ * @param {String} props.description
2614
+ * @param {String} props.label
2615
+ * @param {String} props.switcherLabel
2616
+ * @param {Function} props.getValue
2617
+ * @param {Function} props.setValue
2618
+ * @param {Function} props.onFocus
2619
+ * @param {Function} props.onBlur
2589
2620
  */
2590
2621
  function ToggleSwitchEntry(props) {
2591
2622
  const {