@bpmn-io/properties-panel 0.12.0 → 0.13.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,32 +1,131 @@
1
- import { useRef, useEffect, useMemo, useContext, useState } from '../preact/hooks';
2
- import { isFunction, get, assign, set, sortBy, find, isNumber, debounce } from 'min-dash';
1
+ import { useContext, useEffect, useRef, useMemo, useState, useCallback } from '../preact/hooks';
2
+ import { isArray, isFunction, isNumber, bind, assign, get, set, sortBy, find, debounce } from 'min-dash';
3
3
  import classnames from 'classnames';
4
- import { jsxs, jsx } from '../preact/jsx-runtime';
4
+ import '../preact/compat';
5
+ import { jsx, jsxs } from '../preact/jsx-runtime';
5
6
  import { query } from 'min-dom';
6
7
  import { createContext, createElement } from '../preact';
7
- import '../preact/compat';
8
8
 
9
- /**
10
- * @typedef { { getElementLabel: Function, getTypeLabel: Function, getElementIcon: Function } } HeaderProvider
11
- */
9
+ var ArrowIcon = function ArrowIcon(props) {
10
+ return jsx("svg", { ...props,
11
+ children: jsx("path", {
12
+ fillRule: "evenodd",
13
+ d: "m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8z"
14
+ })
15
+ });
16
+ };
17
+
18
+ ArrowIcon.defaultProps = {
19
+ xmlns: "http://www.w3.org/2000/svg",
20
+ width: "16",
21
+ height: "16"
22
+ };
23
+
24
+ var CreateIcon = function CreateIcon(props) {
25
+ return jsx("svg", { ...props,
26
+ children: jsx("path", {
27
+ fillRule: "evenodd",
28
+ d: "M9 13V9h4a1 1 0 0 0 0-2H9V3a1 1 0 1 0-2 0v4H3a1 1 0 1 0 0 2h4v4a1 1 0 0 0 2 0z"
29
+ })
30
+ });
31
+ };
32
+
33
+ CreateIcon.defaultProps = {
34
+ xmlns: "http://www.w3.org/2000/svg",
35
+ width: "16",
36
+ height: "16"
37
+ };
38
+
39
+ var DeleteIcon = function DeleteIcon(props) {
40
+ return jsx("svg", { ...props,
41
+ children: jsx("path", {
42
+ fillRule: "evenodd",
43
+ d: "M12 6v7c0 1.1-.4 1.55-1.5 1.55h-5C4.4 14.55 4 14.1 4 13V6h8zm-1.5 1.5h-5v4.3c0 .66.5 1.2 1.111 1.2H9.39c.611 0 1.111-.54 1.111-1.2V7.5zM13 3h-2l-1-1H6L5 3H3v1.5h10V3z"
44
+ })
45
+ });
46
+ };
47
+
48
+ DeleteIcon.defaultProps = {
49
+ xmlns: "http://www.w3.org/2000/svg",
50
+ width: "16",
51
+ height: "16"
52
+ };
53
+
54
+ var ExternalLinkIcon = function ExternalLinkIcon(props) {
55
+ return jsx("svg", { ...props,
56
+ children: jsx("path", {
57
+ fillRule: "evenodd",
58
+ clipRule: "evenodd",
59
+ d: "M12.637 12.637v-4.72h1.362v4.721c0 .36-.137.676-.411.95-.275.275-.591.412-.95.412H3.362c-.38 0-.703-.132-.967-.396A1.315 1.315 0 0 1 2 12.638V3.362c0-.38.132-.703.396-.967S2.982 2 3.363 2h4.553v1.363H3.363v9.274h9.274zM14 2H9.28l-.001 1.362h2.408L5.065 9.984l.95.95 6.622-6.622v2.409H14V2z",
60
+ fill: "#818798"
61
+ })
62
+ });
63
+ };
64
+
65
+ ExternalLinkIcon.defaultProps = {
66
+ width: "16",
67
+ height: "16",
68
+ fill: "none",
69
+ xmlns: "http://www.w3.org/2000/svg"
70
+ };
71
+
72
+ var FeelRequiredIcon = function FeelRequiredIcon(props) {
73
+ return jsxs("svg", { ...props,
74
+ children: [jsx("path", {
75
+ d: "M5.8 7.06V5.95h4.307v1.11H5.8zm0 3.071v-1.11h4.307v1.11H5.8z",
76
+ fill: "#505562"
77
+ }), jsx("path", {
78
+ fillRule: "evenodd",
79
+ clipRule: "evenodd",
80
+ d: "M8 3.268A4.732 4.732 0 1 0 12.732 8H14a6 6 0 1 1-6-6v1.268z",
81
+ fill: "#505562"
82
+ }), jsx("path", {
83
+ d: "m11.28 6.072-.832-.56 1.016-1.224L10 3.848l.312-.912 1.392.584L11.632 2h1.032l-.072 1.52 1.392-.584.312.912-1.464.44 1.008 1.224-.832.552-.864-1.296-.864 1.304z",
84
+ fill: "#505562"
85
+ })]
86
+ });
87
+ };
88
+
89
+ FeelRequiredIcon.defaultProps = {
90
+ viewBox: "0 0 16 16",
91
+ fill: "none",
92
+ xmlns: "http://www.w3.org/2000/svg"
93
+ };
94
+
95
+ var FeelOptionalIcon = function FeelOptionalIcon(props) {
96
+ return jsxs("svg", { ...props,
97
+ children: [jsx("path", {
98
+ d: "M5.845 7.04V5.93h4.307v1.11H5.845zm0 3.07V9h4.307v1.11H5.845z",
99
+ fill: "#505562"
100
+ }), jsx("path", {
101
+ fillRule: "evenodd",
102
+ clipRule: "evenodd",
103
+ d: "M3.286 8a4.714 4.714 0 1 0 9.428 0 4.714 4.714 0 0 0-9.428 0zM8 2a6 6 0 1 0 0 12A6 6 0 0 0 8 2z",
104
+ fill: "#505562"
105
+ })]
106
+ });
107
+ };
108
+
109
+ FeelOptionalIcon.defaultProps = {
110
+ viewBox: "0 0 16 16",
111
+ fill: "none",
112
+ xmlns: "http://www.w3.org/2000/svg"
113
+ };
12
114
 
13
- /**
14
- * @param {Object} props
15
- * @param {Object} props.element,
16
- * @param {HeaderProvider} props.headerProvider
17
- */
18
115
  function Header(props) {
19
116
  const {
20
117
  element,
21
118
  headerProvider
22
119
  } = props;
23
120
  const {
121
+ getElementIcon,
122
+ getDocumentationRef,
24
123
  getElementLabel,
25
- getTypeLabel,
26
- getElementIcon
124
+ getTypeLabel
27
125
  } = headerProvider;
28
126
  const label = getElementLabel(element);
29
127
  const type = getTypeLabel(element);
128
+ const documentationRef = getDocumentationRef && getDocumentationRef(element);
30
129
  const ElementIcon = getElementIcon(element);
31
130
  return jsxs("div", {
32
131
  class: "bio-properties-panel-header",
@@ -43,28 +142,624 @@ function Header(props) {
43
142
  title: type,
44
143
  class: "bio-properties-panel-header-type",
45
144
  children: type
46
- }), getElementLabel(element) ? jsx("div", {
145
+ }), label ? jsx("div", {
47
146
  title: label,
48
147
  class: "bio-properties-panel-header-label",
49
148
  children: label
50
149
  }) : null]
150
+ }), jsx("div", {
151
+ class: "bio-properties-panel-header-actions",
152
+ children: documentationRef ? jsx("a", {
153
+ rel: "noopener",
154
+ class: "bio-properties-panel-header-link",
155
+ href: documentationRef,
156
+ target: "_blank",
157
+ children: jsx(ExternalLinkIcon, {})
158
+ }) : null
51
159
  })]
52
160
  });
53
161
  }
54
162
 
163
+ const DescriptionContext = createContext({
164
+ description: {},
165
+ getDescriptionForId: () => {}
166
+ });
167
+
168
+ var FN_REF = '__fn';
169
+ var DEFAULT_PRIORITY$1 = 1000;
170
+ var slice = Array.prototype.slice;
171
+ /**
172
+ * A general purpose event bus.
173
+ *
174
+ * This component is used to communicate across a diagram instance.
175
+ * Other parts of a diagram can use it to listen to and broadcast events.
176
+ *
177
+ *
178
+ * ## Registering for Events
179
+ *
180
+ * The event bus provides the {@link EventBus#on} and {@link EventBus#once}
181
+ * methods to register for events. {@link EventBus#off} can be used to
182
+ * remove event registrations. Listeners receive an instance of {@link Event}
183
+ * as the first argument. It allows them to hook into the event execution.
184
+ *
185
+ * ```javascript
186
+ *
187
+ * // listen for event
188
+ * eventBus.on('foo', function(event) {
189
+ *
190
+ * // access event type
191
+ * event.type; // 'foo'
192
+ *
193
+ * // stop propagation to other listeners
194
+ * event.stopPropagation();
195
+ *
196
+ * // prevent event default
197
+ * event.preventDefault();
198
+ * });
199
+ *
200
+ * // listen for event with custom payload
201
+ * eventBus.on('bar', function(event, payload) {
202
+ * console.log(payload);
203
+ * });
204
+ *
205
+ * // listen for event returning value
206
+ * eventBus.on('foobar', function(event) {
207
+ *
208
+ * // stop event propagation + prevent default
209
+ * return false;
210
+ *
211
+ * // stop event propagation + return custom result
212
+ * return {
213
+ * complex: 'listening result'
214
+ * };
215
+ * });
216
+ *
217
+ *
218
+ * // listen with custom priority (default=1000, higher is better)
219
+ * eventBus.on('priorityfoo', 1500, function(event) {
220
+ * console.log('invoked first!');
221
+ * });
222
+ *
223
+ *
224
+ * // listen for event and pass the context (`this`)
225
+ * eventBus.on('foobar', function(event) {
226
+ * this.foo();
227
+ * }, this);
228
+ * ```
229
+ *
230
+ *
231
+ * ## Emitting Events
232
+ *
233
+ * Events can be emitted via the event bus using {@link EventBus#fire}.
234
+ *
235
+ * ```javascript
236
+ *
237
+ * // false indicates that the default action
238
+ * // was prevented by listeners
239
+ * if (eventBus.fire('foo') === false) {
240
+ * console.log('default has been prevented!');
241
+ * };
242
+ *
243
+ *
244
+ * // custom args + return value listener
245
+ * eventBus.on('sum', function(event, a, b) {
246
+ * return a + b;
247
+ * });
248
+ *
249
+ * // you can pass custom arguments + retrieve result values.
250
+ * var sum = eventBus.fire('sum', 1, 2);
251
+ * console.log(sum); // 3
252
+ * ```
253
+ */
254
+
255
+ function EventBus() {
256
+ this._listeners = {}; // cleanup on destroy on lowest priority to allow
257
+ // message passing until the bitter end
258
+
259
+ this.on('diagram.destroy', 1, this._destroy, this);
260
+ }
261
+ /**
262
+ * Register an event listener for events with the given name.
263
+ *
264
+ * The callback will be invoked with `event, ...additionalArguments`
265
+ * that have been passed to {@link EventBus#fire}.
266
+ *
267
+ * Returning false from a listener will prevent the events default action
268
+ * (if any is specified). To stop an event from being processed further in
269
+ * other listeners execute {@link Event#stopPropagation}.
270
+ *
271
+ * Returning anything but `undefined` from a listener will stop the listener propagation.
272
+ *
273
+ * @param {string|Array<string>} events
274
+ * @param {number} [priority=1000] the priority in which this listener is called, larger is higher
275
+ * @param {Function} callback
276
+ * @param {Object} [that] Pass context (`this`) to the callback
277
+ */
278
+
279
+ EventBus.prototype.on = function (events, priority, callback, that) {
280
+ events = isArray(events) ? events : [events];
281
+
282
+ if (isFunction(priority)) {
283
+ that = callback;
284
+ callback = priority;
285
+ priority = DEFAULT_PRIORITY$1;
286
+ }
287
+
288
+ if (!isNumber(priority)) {
289
+ throw new Error('priority must be a number');
290
+ }
291
+
292
+ var actualCallback = callback;
293
+
294
+ if (that) {
295
+ actualCallback = bind(callback, that); // make sure we remember and are able to remove
296
+ // bound callbacks via {@link #off} using the original
297
+ // callback
298
+
299
+ actualCallback[FN_REF] = callback[FN_REF] || callback;
300
+ }
301
+
302
+ var self = this;
303
+ events.forEach(function (e) {
304
+ self._addListener(e, {
305
+ priority: priority,
306
+ callback: actualCallback,
307
+ next: null
308
+ });
309
+ });
310
+ };
311
+ /**
312
+ * Register an event listener that is executed only once.
313
+ *
314
+ * @param {string} event the event name to register for
315
+ * @param {number} [priority=1000] the priority in which this listener is called, larger is higher
316
+ * @param {Function} callback the callback to execute
317
+ * @param {Object} [that] Pass context (`this`) to the callback
318
+ */
319
+
320
+
321
+ EventBus.prototype.once = function (event, priority, callback, that) {
322
+ var self = this;
323
+
324
+ if (isFunction(priority)) {
325
+ that = callback;
326
+ callback = priority;
327
+ priority = DEFAULT_PRIORITY$1;
328
+ }
329
+
330
+ if (!isNumber(priority)) {
331
+ throw new Error('priority must be a number');
332
+ }
333
+
334
+ function wrappedCallback() {
335
+ wrappedCallback.__isTomb = true;
336
+ var result = callback.apply(that, arguments);
337
+ self.off(event, wrappedCallback);
338
+ return result;
339
+ } // make sure we remember and are able to remove
340
+ // bound callbacks via {@link #off} using the original
341
+ // callback
342
+
343
+
344
+ wrappedCallback[FN_REF] = callback;
345
+ this.on(event, priority, wrappedCallback);
346
+ };
347
+ /**
348
+ * Removes event listeners by event and callback.
349
+ *
350
+ * If no callback is given, all listeners for a given event name are being removed.
351
+ *
352
+ * @param {string|Array<string>} events
353
+ * @param {Function} [callback]
354
+ */
355
+
356
+
357
+ EventBus.prototype.off = function (events, callback) {
358
+ events = isArray(events) ? events : [events];
359
+ var self = this;
360
+ events.forEach(function (event) {
361
+ self._removeListener(event, callback);
362
+ });
363
+ };
364
+ /**
365
+ * Create an EventBus event.
366
+ *
367
+ * @param {Object} data
368
+ *
369
+ * @return {Object} event, recognized by the eventBus
370
+ */
371
+
372
+
373
+ EventBus.prototype.createEvent = function (data) {
374
+ var event = new InternalEvent();
375
+ event.init(data);
376
+ return event;
377
+ };
378
+ /**
379
+ * Fires a named event.
380
+ *
381
+ * @example
382
+ *
383
+ * // fire event by name
384
+ * events.fire('foo');
385
+ *
386
+ * // fire event object with nested type
387
+ * var event = { type: 'foo' };
388
+ * events.fire(event);
389
+ *
390
+ * // fire event with explicit type
391
+ * var event = { x: 10, y: 20 };
392
+ * events.fire('element.moved', event);
393
+ *
394
+ * // pass additional arguments to the event
395
+ * events.on('foo', function(event, bar) {
396
+ * alert(bar);
397
+ * });
398
+ *
399
+ * events.fire({ type: 'foo' }, 'I am bar!');
400
+ *
401
+ * @param {string} [name] the optional event name
402
+ * @param {Object} [event] the event object
403
+ * @param {...Object} additional arguments to be passed to the callback functions
404
+ *
405
+ * @return {boolean} the events return value, if specified or false if the
406
+ * default action was prevented by listeners
407
+ */
408
+
409
+
410
+ EventBus.prototype.fire = function (type, data) {
411
+ var event, firstListener, returnValue, args;
412
+ args = slice.call(arguments);
413
+
414
+ if (typeof type === 'object') {
415
+ data = type;
416
+ type = data.type;
417
+ }
418
+
419
+ if (!type) {
420
+ throw new Error('no event type specified');
421
+ }
422
+
423
+ firstListener = this._listeners[type];
424
+
425
+ if (!firstListener) {
426
+ return;
427
+ } // we make sure we fire instances of our home made
428
+ // events here. We wrap them only once, though
429
+
430
+
431
+ if (data instanceof InternalEvent) {
432
+ // we are fine, we alread have an event
433
+ event = data;
434
+ } else {
435
+ event = this.createEvent(data);
436
+ } // ensure we pass the event as the first parameter
437
+
438
+
439
+ args[0] = event; // original event type (in case we delegate)
440
+
441
+ var originalType = event.type; // update event type before delegation
442
+
443
+ if (type !== originalType) {
444
+ event.type = type;
445
+ }
446
+
447
+ try {
448
+ returnValue = this._invokeListeners(event, args, firstListener);
449
+ } finally {
450
+ // reset event type after delegation
451
+ if (type !== originalType) {
452
+ event.type = originalType;
453
+ }
454
+ } // set the return value to false if the event default
455
+ // got prevented and no other return value exists
456
+
457
+
458
+ if (returnValue === undefined && event.defaultPrevented) {
459
+ returnValue = false;
460
+ }
461
+
462
+ return returnValue;
463
+ };
464
+
465
+ EventBus.prototype.handleError = function (error) {
466
+ return this.fire('error', {
467
+ error: error
468
+ }) === false;
469
+ };
470
+
471
+ EventBus.prototype._destroy = function () {
472
+ this._listeners = {};
473
+ };
474
+
475
+ EventBus.prototype._invokeListeners = function (event, args, listener) {
476
+ var returnValue;
477
+
478
+ while (listener) {
479
+ // handle stopped propagation
480
+ if (event.cancelBubble) {
481
+ break;
482
+ }
483
+
484
+ returnValue = this._invokeListener(event, args, listener);
485
+ listener = listener.next;
486
+ }
487
+
488
+ return returnValue;
489
+ };
490
+
491
+ EventBus.prototype._invokeListener = function (event, args, listener) {
492
+ var returnValue;
493
+
494
+ if (listener.callback.__isTomb) {
495
+ return returnValue;
496
+ }
497
+
498
+ try {
499
+ // returning false prevents the default action
500
+ returnValue = invokeFunction(listener.callback, args); // stop propagation on return value
501
+
502
+ if (returnValue !== undefined) {
503
+ event.returnValue = returnValue;
504
+ event.stopPropagation();
505
+ } // prevent default on return false
506
+
507
+
508
+ if (returnValue === false) {
509
+ event.preventDefault();
510
+ }
511
+ } catch (error) {
512
+ if (!this.handleError(error)) {
513
+ console.error('unhandled error in event listener', error);
514
+ throw error;
515
+ }
516
+ }
517
+
518
+ return returnValue;
519
+ };
520
+ /*
521
+ * Add new listener with a certain priority to the list
522
+ * of listeners (for the given event).
523
+ *
524
+ * The semantics of listener registration / listener execution are
525
+ * first register, first serve: New listeners will always be inserted
526
+ * after existing listeners with the same priority.
527
+ *
528
+ * Example: Inserting two listeners with priority 1000 and 1300
529
+ *
530
+ * * before: [ 1500, 1500, 1000, 1000 ]
531
+ * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
532
+ *
533
+ * @param {string} event
534
+ * @param {Object} listener { priority, callback }
535
+ */
536
+
537
+
538
+ EventBus.prototype._addListener = function (event, newListener) {
539
+ var listener = this._getListeners(event),
540
+ previousListener; // no prior listeners
541
+
542
+
543
+ if (!listener) {
544
+ this._setListeners(event, newListener);
545
+
546
+ return;
547
+ } // ensure we order listeners by priority from
548
+ // 0 (high) to n > 0 (low)
549
+
550
+
551
+ while (listener) {
552
+ if (listener.priority < newListener.priority) {
553
+ newListener.next = listener;
554
+
555
+ if (previousListener) {
556
+ previousListener.next = newListener;
557
+ } else {
558
+ this._setListeners(event, newListener);
559
+ }
560
+
561
+ return;
562
+ }
563
+
564
+ previousListener = listener;
565
+ listener = listener.next;
566
+ } // add new listener to back
567
+
568
+
569
+ previousListener.next = newListener;
570
+ };
571
+
572
+ EventBus.prototype._getListeners = function (name) {
573
+ return this._listeners[name];
574
+ };
575
+
576
+ EventBus.prototype._setListeners = function (name, listener) {
577
+ this._listeners[name] = listener;
578
+ };
579
+
580
+ EventBus.prototype._removeListener = function (event, callback) {
581
+ var listener = this._getListeners(event),
582
+ nextListener,
583
+ previousListener,
584
+ listenerCallback;
585
+
586
+ if (!callback) {
587
+ // clear listeners
588
+ this._setListeners(event, null);
589
+
590
+ return;
591
+ }
592
+
593
+ while (listener) {
594
+ nextListener = listener.next;
595
+ listenerCallback = listener.callback;
596
+
597
+ if (listenerCallback === callback || listenerCallback[FN_REF] === callback) {
598
+ if (previousListener) {
599
+ previousListener.next = nextListener;
600
+ } else {
601
+ // new first listener
602
+ this._setListeners(event, nextListener);
603
+ }
604
+ }
605
+
606
+ previousListener = listener;
607
+ listener = nextListener;
608
+ }
609
+ };
610
+ /**
611
+ * A event that is emitted via the event bus.
612
+ */
613
+
614
+
615
+ function InternalEvent() {}
616
+
617
+ InternalEvent.prototype.stopPropagation = function () {
618
+ this.cancelBubble = true;
619
+ };
620
+
621
+ InternalEvent.prototype.preventDefault = function () {
622
+ this.defaultPrevented = true;
623
+ };
624
+
625
+ InternalEvent.prototype.init = function (data) {
626
+ assign(this, data || {});
627
+ };
628
+ /**
629
+ * Invoke function. Be fast...
630
+ *
631
+ * @param {Function} fn
632
+ * @param {Array<Object>} args
633
+ *
634
+ * @return {Any}
635
+ */
636
+
637
+
638
+ function invokeFunction(fn, args) {
639
+ return fn.apply(null, args);
640
+ }
641
+
642
+ /**
643
+ * @typedef {Function} <propertiesPanel.showEntry> callback
644
+ *
645
+ * @example
646
+ *
647
+ * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
648
+ * // ...
649
+ * });
650
+ *
651
+ * @param {Object} context
652
+ * @param {boolean} [context.focus]
653
+ *
654
+ * @returns void
655
+ */
656
+ const eventBus = new EventBus();
657
+ const EventContext = createContext({
658
+ eventBus
659
+ });
660
+
661
+ const LayoutContext = createContext({
662
+ layout: {},
663
+ setLayout: () => {},
664
+ getLayoutForKey: () => {},
665
+ setLayoutForKey: () => {}
666
+ });
667
+
668
+ /**
669
+ * Accesses the global DescriptionContext and returns a description for a given id and element.
670
+ *
671
+ * @example
672
+ * ```jsx
673
+ * function TextField(props) {
674
+ * const description = useDescriptionContext('input1', element);
675
+ * }
676
+ * ```
677
+ *
678
+ * @param {string} id
679
+ * @param {djs.model.Base} element
680
+ *
681
+ * @returns {string}
682
+ */
683
+
684
+ function useDescriptionContext(id, element) {
685
+ const {
686
+ getDescriptionForId
687
+ } = useContext(DescriptionContext);
688
+ return getDescriptionForId(id, element);
689
+ }
690
+
691
+ const DEFAULT_PRIORITY = 1000;
692
+ /**
693
+ * Subscribe to an event.
694
+ *
695
+ * @param {string} event
696
+ * @param {Function} callback
697
+ * @param {number} [priority]
698
+ *
699
+ * @returns {import('preact').Ref}
700
+ */
701
+
702
+ function useEvent(event, callback, priority = DEFAULT_PRIORITY) {
703
+ const {
704
+ eventBus
705
+ } = useContext(EventContext);
706
+ useEffect(() => {
707
+ eventBus.on(event, priority, callback);
708
+ return () => eventBus.off(event, callback);
709
+ }, [event, eventBus, callback, priority]);
710
+ }
711
+
712
+ const HIGH_PRIORITY = 10000;
55
713
  /**
56
- * @pinussilvestrus: we need to introduce our own hook to persist the previous
57
- * state on updates.
714
+ * Buffer events and re-fire during passive effect phase.
58
715
  *
59
- * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
716
+ * @param {string[]} bufferedEvents
717
+ * @param {Object} [eventBus]
60
718
  */
61
719
 
62
- function usePrevious(value) {
63
- const ref = useRef();
720
+ function useEventBuffer(bufferedEvents, eventBus) {
721
+ const buffer = useRef([]),
722
+ buffering = useRef(true);
723
+
724
+ const createCallback = event => data => {
725
+ if (buffering.current === true) {
726
+ buffer.current.unshift([event, data]);
727
+ }
728
+ }; // (1) buffer events
729
+
730
+
64
731
  useEffect(() => {
65
- ref.current = value;
732
+ if (!eventBus) {
733
+ return;
734
+ }
735
+
736
+ const listeners = bufferedEvents.map(event => {
737
+ return [event, createCallback(event)];
738
+ });
739
+ listeners.forEach(([event, callback]) => {
740
+ eventBus.on(event, HIGH_PRIORITY, callback);
741
+ });
742
+ return () => {
743
+ listeners.forEach(([event, callback]) => {
744
+ eventBus.off(event, callback);
745
+ });
746
+ };
747
+ }, [bufferedEvents, eventBus]); // (2) re-fire events
748
+
749
+ useEffect(() => {
750
+ if (!eventBus) {
751
+ return;
752
+ }
753
+
754
+ buffering.current = false;
755
+
756
+ while (buffer.current.length) {
757
+ const [event, data] = buffer.current.pop();
758
+ eventBus.fire(event, data);
759
+ }
760
+
761
+ buffering.current = true;
66
762
  });
67
- return ref.current;
68
763
  }
69
764
 
70
765
  const KEY_LENGTH = 6;
@@ -105,18 +800,6 @@ function useKeyFactory(dependencies = []) {
105
800
  return getKey;
106
801
  }
107
802
 
108
- const DescriptionContext = createContext({
109
- description: {},
110
- getDescriptionForId: () => {}
111
- });
112
-
113
- const LayoutContext = createContext({
114
- layout: {},
115
- setLayout: () => {},
116
- getLayoutForKey: () => {},
117
- setLayoutForKey: () => {}
118
- });
119
-
120
803
  /**
121
804
  * Creates a state that persists in the global LayoutContext.
122
805
  *
@@ -152,124 +835,101 @@ function useLayoutState(path, defaultValue) {
152
835
  }
153
836
 
154
837
  /**
155
- * Accesses the global DescriptionContext and returns a description for a given id and element.
156
- *
157
- * @example
158
- * ```jsx
159
- * function TextField(props) {
160
- * const description = useDescriptionContext('input1', element);
161
- * }
162
- * ```
163
- *
164
- * @param {string} id
165
- * @param {djs.model.Base} element
838
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
839
+ * state on updates.
166
840
  *
167
- * @returns {string}
841
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
168
842
  */
169
843
 
170
- function useDescriptionContext(id, element) {
171
- const {
172
- getDescriptionForId
173
- } = useContext(DescriptionContext);
174
- return getDescriptionForId(id, element);
175
- }
176
-
177
- var ArrowIcon = function ArrowIcon(props) {
178
- return jsx("svg", { ...props,
179
- children: jsx("path", {
180
- fillRule: "evenodd",
181
- d: "m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8z"
182
- })
183
- });
184
- };
185
-
186
- ArrowIcon.defaultProps = {
187
- xmlns: "http://www.w3.org/2000/svg",
188
- width: "16",
189
- height: "16"
190
- };
191
-
192
- var CreateIcon = function CreateIcon(props) {
193
- return jsx("svg", { ...props,
194
- children: jsx("path", {
195
- fillRule: "evenodd",
196
- d: "M9 13V9h4a1 1 0 0 0 0-2H9V3a1 1 0 1 0-2 0v4H3a1 1 0 1 0 0 2h4v4a1 1 0 0 0 2 0z"
197
- })
844
+ function usePrevious(value) {
845
+ const ref = useRef();
846
+ useEffect(() => {
847
+ ref.current = value;
198
848
  });
199
- };
849
+ return ref.current;
850
+ }
200
851
 
201
- CreateIcon.defaultProps = {
202
- xmlns: "http://www.w3.org/2000/svg",
203
- width: "16",
204
- height: "16"
205
- };
852
+ /**
853
+ * Subscribe to `propertiesPanel.showEntry`.
854
+ *
855
+ * @param {Function} show
856
+ *
857
+ * @returns {import('preact').Ref}
858
+ */
206
859
 
207
- var DeleteIcon = function DeleteIcon(props) {
208
- return jsx("svg", { ...props,
209
- children: jsx("path", {
210
- fillRule: "evenodd",
211
- d: "M12 6v7c0 1.1-.4 1.55-1.5 1.55h-5C4.4 14.55 4 14.1 4 13V6h8zm-1.5 1.5h-5v4.3c0 .66.5 1.2 1.111 1.2H9.39c.611 0 1.111-.54 1.111-1.2V7.5zM13 3h-2l-1-1H6L5 3H3v1.5h10V3z"
212
- })
213
- });
214
- };
860
+ function useShowEntryEvent(show) {
861
+ const {
862
+ onShow
863
+ } = useContext(LayoutContext);
864
+ const ref = useRef();
865
+ const [focus, setFocus] = useState(false);
866
+ const onShowEntry = useCallback(event => {
867
+ if (show(event)) {
868
+ if (isFunction(onShow)) {
869
+ onShow();
870
+ }
215
871
 
216
- DeleteIcon.defaultProps = {
217
- xmlns: "http://www.w3.org/2000/svg",
218
- width: "16",
219
- height: "16"
220
- };
872
+ if (event.focus && !focus) {
873
+ setFocus(true);
874
+ }
875
+ }
876
+ }, [show]);
877
+ useEffect(() => {
878
+ if (focus && ref.current) {
879
+ if (isFunction(ref.current.focus)) {
880
+ ref.current.focus();
881
+ }
221
882
 
222
- var FeelRequiredIcon = function FeelRequiredIcon(props) {
223
- return jsxs("svg", { ...props,
224
- children: [jsx("path", {
225
- d: "M5.8 7.06V5.95h4.307v1.11H5.8zm0 3.071v-1.11h4.307v1.11H5.8z",
226
- fill: "#505562"
227
- }), jsx("path", {
228
- fillRule: "evenodd",
229
- clipRule: "evenodd",
230
- d: "M8 3.268A4.732 4.732 0 1 0 12.732 8H14a6 6 0 1 1-6-6v1.268z",
231
- fill: "#505562"
232
- }), jsx("path", {
233
- d: "m11.28 6.072-.832-.56 1.016-1.224L10 3.848l.312-.912 1.392.584L11.632 2h1.032l-.072 1.52 1.392-.584.312.912-1.464.44 1.008 1.224-.832.552-.864-1.296-.864 1.304z",
234
- fill: "#505562"
235
- })]
236
- });
237
- };
883
+ if (isFunction(ref.current.select)) {
884
+ ref.current.select();
885
+ }
238
886
 
239
- FeelRequiredIcon.defaultProps = {
240
- viewBox: "0 0 16 16",
241
- fill: "none",
242
- xmlns: "http://www.w3.org/2000/svg"
243
- };
887
+ setFocus(false);
888
+ }
889
+ }, [focus]);
890
+ useEvent('propertiesPanel.showEntry', onShowEntry);
891
+ return ref;
892
+ }
244
893
 
245
- var FeelOptionalIcon = function FeelOptionalIcon(props) {
246
- return jsxs("svg", { ...props,
247
- children: [jsx("path", {
248
- d: "M5.845 7.04V5.93h4.307v1.11H5.845zm0 3.07V9h4.307v1.11H5.845z",
249
- fill: "#505562"
250
- }), jsx("path", {
251
- fillRule: "evenodd",
252
- clipRule: "evenodd",
253
- d: "M3.286 8a4.714 4.714 0 1 0 9.428 0 4.714 4.714 0 0 0-9.428 0zM8 2a6 6 0 1 0 0 12A6 6 0 0 0 8 2z",
254
- fill: "#505562"
255
- })]
256
- });
257
- };
894
+ /**
895
+ * Subscribe to `propertiesPanel.showError`. On `propertiesPanel.showError` set
896
+ * temporary error. Fire `propertiesPanel.showEntry` for temporary error to be
897
+ * visible. Unset error on `propertiesPanel.updated`.
898
+ *
899
+ * @param {Function} show
900
+ *
901
+ * @returns {import('preact').Ref}
902
+ */
258
903
 
259
- FeelOptionalIcon.defaultProps = {
260
- viewBox: "0 0 16 16",
261
- fill: "none",
262
- xmlns: "http://www.w3.org/2000/svg"
263
- };
904
+ function useShowErrorEvent(show) {
905
+ const {
906
+ eventBus
907
+ } = useContext(EventContext);
908
+ const [temporaryError, setTemporaryError] = useState(null);
909
+ const onPropertiesPanelUpdated = useCallback(() => setTemporaryError(null), []);
910
+ useEvent('propertiesPanel.updated', onPropertiesPanelUpdated);
911
+ const onShowError = useCallback(event => {
912
+ setTemporaryError(null);
913
+
914
+ if (show(event)) {
915
+ eventBus.fire('propertiesPanel.showEntry', event);
916
+ setTemporaryError(event.message);
917
+ }
918
+ }, [show]);
919
+ useEvent('propertiesPanel.showError', onShowError);
920
+ return temporaryError;
921
+ }
264
922
 
265
923
  function Group(props) {
266
924
  const {
267
925
  element,
268
926
  entries = [],
269
927
  id,
270
- label
928
+ label,
929
+ shouldOpen = false
271
930
  } = props;
272
- const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
931
+ const [open, setOpen] = useLayoutState(['groups', id, 'open'], shouldOpen);
932
+ const onShow = useCallback(() => setOpen(true), [setOpen]);
273
933
 
274
934
  const toggleOpen = () => setOpen(!open);
275
935
 
@@ -292,6 +952,9 @@ function Group(props) {
292
952
  });
293
953
  setEdited(hasOneEditedEntry);
294
954
  }, [entries]);
955
+ const propertiesPanelContext = { ...useContext(LayoutContext),
956
+ onShow
957
+ };
295
958
  return jsxs("div", {
296
959
  class: "bio-properties-panel-group",
297
960
  "data-group-id": 'group-' + id,
@@ -314,15 +977,18 @@ function Group(props) {
314
977
  })]
315
978
  }), jsx("div", {
316
979
  class: classnames('bio-properties-panel-group-entries', open ? 'open' : ''),
317
- children: entries.map(entry => {
318
- const {
319
- component: Component,
320
- id
321
- } = entry;
322
- return createElement(Component, { ...entry,
323
- key: id,
324
- element: element
325
- });
980
+ children: jsx(LayoutContext.Provider, {
981
+ value: propertiesPanelContext,
982
+ children: entries.map(entry => {
983
+ const {
984
+ component: Component,
985
+ id
986
+ } = entry;
987
+ return createElement(Component, { ...entry,
988
+ element: element,
989
+ key: id
990
+ });
991
+ })
326
992
  })
327
993
  })]
328
994
  });
@@ -339,6 +1005,7 @@ const DEFAULT_LAYOUT = {
339
1005
  open: true
340
1006
  };
341
1007
  const DEFAULT_DESCRIPTION = {};
1008
+ const bufferedEvents = ['propertiesPanel.showEntry', 'propertiesPanel.showError'];
342
1009
  /**
343
1010
  * @typedef { {
344
1011
  * component: import('preact').Component,
@@ -370,7 +1037,8 @@ const DEFAULT_DESCRIPTION = {};
370
1037
  * component?: import('preact').Component,
371
1038
  * entries: Array<EntryDefinition>,
372
1039
  * id: String,
373
- * label: String
1040
+ * label: String,
1041
+ * shouldOpen?: Boolean
374
1042
  * } } GroupDefinition
375
1043
  *
376
1044
  * @typedef { {
@@ -407,7 +1075,8 @@ function PropertiesPanel(props) {
407
1075
  layoutConfig = {},
408
1076
  layoutChanged,
409
1077
  descriptionConfig = {},
410
- descriptionLoaded
1078
+ descriptionLoaded,
1079
+ eventBus
411
1080
  } = props; // set-up layout context
412
1081
 
413
1082
  const [layout, setLayout] = useState(createLayout(layoutConfig));
@@ -448,6 +1117,13 @@ function PropertiesPanel(props) {
448
1117
  description,
449
1118
  getDescriptionForId
450
1119
  };
1120
+ useEventBuffer(bufferedEvents, eventBus);
1121
+ const eventContext = {
1122
+ eventBus
1123
+ };
1124
+ const propertiesPanelContext = {
1125
+ element
1126
+ };
451
1127
 
452
1128
  if (!element) {
453
1129
  return jsx("div", {
@@ -456,28 +1132,34 @@ function PropertiesPanel(props) {
456
1132
  });
457
1133
  }
458
1134
 
459
- return jsx(DescriptionContext.Provider, {
460
- value: descriptionContext,
461
- children: jsx(LayoutContext.Provider, {
462
- value: layoutContext,
463
- children: jsxs("div", {
464
- class: classnames('bio-properties-panel', layout.open ? 'open' : ''),
465
- children: [jsx(Header, {
466
- element: element,
467
- headerProvider: headerProvider
468
- }), jsx("div", {
469
- class: "bio-properties-panel-scroll-container",
470
- children: groups.map(group => {
471
- const {
472
- component: Component = Group,
473
- id
474
- } = group;
475
- return createElement(Component, { ...group,
476
- key: id,
477
- element: element
478
- });
1135
+ return jsx(LayoutContext.Provider, {
1136
+ value: propertiesPanelContext,
1137
+ children: jsx(DescriptionContext.Provider, {
1138
+ value: descriptionContext,
1139
+ children: jsx(LayoutContext.Provider, {
1140
+ value: layoutContext,
1141
+ children: jsx(EventContext.Provider, {
1142
+ value: eventContext,
1143
+ children: jsxs("div", {
1144
+ class: classnames('bio-properties-panel', layout.open ? 'open' : ''),
1145
+ children: [jsx(Header, {
1146
+ element: element,
1147
+ headerProvider: headerProvider
1148
+ }), jsx("div", {
1149
+ class: "bio-properties-panel-scroll-container",
1150
+ children: groups.map(group => {
1151
+ const {
1152
+ component: Component = Group,
1153
+ id
1154
+ } = group;
1155
+ return createElement(Component, { ...group,
1156
+ key: id,
1157
+ element: element
1158
+ });
1159
+ })
1160
+ })]
479
1161
  })
480
- })]
1162
+ })
481
1163
  })
482
1164
  })
483
1165
  });
@@ -617,8 +1299,20 @@ function CollapsibleEntry(props) {
617
1299
  } = props;
618
1300
  const [open, setOpen] = useState(shouldOpen);
619
1301
 
620
- const toggleOpen = () => setOpen(!open); // todo(pinussilvestrus): translate once we have a translate mechanism for the core
1302
+ const toggleOpen = () => setOpen(!open);
1303
+
1304
+ const {
1305
+ onShow
1306
+ } = useContext(LayoutContext);
1307
+ const propertiesPanelContext = { ...useContext(LayoutContext),
1308
+ onShow: useCallback(() => {
1309
+ setOpen(true);
621
1310
 
1311
+ if (isFunction(onShow)) {
1312
+ onShow();
1313
+ }
1314
+ }, [onShow, setOpen])
1315
+ }; // todo(pinussilvestrus): translate once we have a translate mechanism for the core
622
1316
 
623
1317
  const placeholderLabel = '<empty>';
624
1318
  return jsxs("div", {
@@ -645,15 +1339,18 @@ function CollapsibleEntry(props) {
645
1339
  }) : null]
646
1340
  }), jsx("div", {
647
1341
  class: classnames('bio-properties-panel-collapsible-entry-entries', open ? 'open' : ''),
648
- children: entries.map(entry => {
649
- const {
650
- component: Component,
651
- id
652
- } = entry;
653
- return createElement(Component, { ...entry,
654
- key: id,
655
- element: element
656
- });
1342
+ children: jsx(LayoutContext.Provider, {
1343
+ value: propertiesPanelContext,
1344
+ children: entries.map(entry => {
1345
+ const {
1346
+ component: Component,
1347
+ id
1348
+ } = entry;
1349
+ return createElement(Component, { ...entry,
1350
+ element: element,
1351
+ key: id
1352
+ });
1353
+ })
657
1354
  })
658
1355
  })]
659
1356
  });
@@ -687,7 +1384,7 @@ function ListItem(props) {
687
1384
  });
688
1385
  }
689
1386
 
690
- const noop = () => {};
1387
+ const noop$3 = () => {};
691
1388
  /**
692
1389
  * @param {import('../PropertiesPanel').ListGroupDefinition} props
693
1390
  */
@@ -704,6 +1401,7 @@ function ListGroup(props) {
704
1401
  shouldSort = true
705
1402
  } = props;
706
1403
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
1404
+ const onShow = useCallback(() => setOpen(true), [setOpen]);
707
1405
  const [ordering, setOrdering] = useState([]);
708
1406
  const [newItemAdded, setNewItemAdded] = useState(false);
709
1407
  const prevItems = usePrevious(items);
@@ -778,12 +1476,15 @@ function ListGroup(props) {
778
1476
  const toggleOpen = () => setOpen(!open);
779
1477
 
780
1478
  const hasItems = !!items.length;
1479
+ const propertiesPanelContext = { ...useContext(LayoutContext),
1480
+ onShow
1481
+ };
781
1482
  return jsxs("div", {
782
1483
  class: "bio-properties-panel-group",
783
1484
  "data-group-id": 'group-' + id,
784
1485
  children: [jsxs("div", {
785
1486
  class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : ''),
786
- onClick: hasItems ? toggleOpen : noop,
1487
+ onClick: hasItems ? toggleOpen : noop$3,
787
1488
  children: [jsx("div", {
788
1489
  title: label,
789
1490
  class: "bio-properties-panel-group-header-title",
@@ -812,23 +1513,27 @@ function ListGroup(props) {
812
1513
  })]
813
1514
  }), jsx("div", {
814
1515
  class: classnames('bio-properties-panel-list', open && hasItems ? 'open' : ''),
815
- children: ordering.map((o, index) => {
816
- const item = getItem(items, o);
817
-
818
- if (!item) {
819
- return;
820
- }
821
-
822
- const {
823
- id
824
- } = item;
825
- return createElement(ListItem, { ...item,
826
- element: element,
827
- index: index,
828
- key: id // if item was added, open first or last item based on ordering
829
- ,
830
- autoOpen: newItemAdded && (shouldSort ? index === 0 : index === ordering.length - 1)
831
- });
1516
+ children: jsx(LayoutContext.Provider, {
1517
+ value: propertiesPanelContext,
1518
+ children: ordering.map((o, index) => {
1519
+ const item = getItem(items, o);
1520
+
1521
+ if (!item) {
1522
+ return;
1523
+ }
1524
+
1525
+ const {
1526
+ id
1527
+ } = item; // if item was added, open first or last item based on ordering
1528
+
1529
+ const autoOpen = newItemAdded && (shouldSort ? index === 0 : index === ordering.length - 1);
1530
+ return createElement(ListItem, { ...item,
1531
+ autoOpen: autoOpen,
1532
+ element: element,
1533
+ index: index,
1534
+ key: id
1535
+ });
1536
+ })
832
1537
  })
833
1538
  })]
834
1539
  });
@@ -867,13 +1572,16 @@ function Description(props) {
867
1572
  }
868
1573
  }
869
1574
 
1575
+ const noop$2 = () => {};
1576
+
870
1577
  function Checkbox(props) {
871
1578
  const {
872
1579
  id,
873
1580
  label,
874
1581
  onChange,
875
1582
  disabled,
876
- value = false
1583
+ value = false,
1584
+ show = noop$2
877
1585
  } = props;
878
1586
 
879
1587
  const handleChange = ({
@@ -882,9 +1590,11 @@ function Checkbox(props) {
882
1590
  onChange(target.checked);
883
1591
  };
884
1592
 
1593
+ const ref = useShowEntryEvent(show);
885
1594
  return jsxs("div", {
886
1595
  class: "bio-properties-panel-checkbox",
887
1596
  children: [jsx("input", {
1597
+ ref: ref,
888
1598
  id: prefixId$6(id),
889
1599
  name: id,
890
1600
  type: "checkbox",
@@ -919,18 +1629,24 @@ function CheckboxEntry(props) {
919
1629
  label,
920
1630
  getValue,
921
1631
  setValue,
922
- disabled
1632
+ disabled,
1633
+ show = noop$2
923
1634
  } = props;
924
1635
  const value = getValue(element);
1636
+ const error = useShowErrorEvent(show);
925
1637
  return jsxs("div", {
926
1638
  class: "bio-properties-panel-entry bio-properties-panel-checkbox-entry",
927
1639
  "data-entry-id": id,
928
1640
  children: [jsx(Checkbox, {
1641
+ disabled: disabled,
929
1642
  id: id,
930
1643
  label: label,
931
1644
  onChange: setValue,
932
- value: value,
933
- disabled: disabled
1645
+ show: show,
1646
+ value: value
1647
+ }), error && jsx("div", {
1648
+ class: "bio-properties-panel-error",
1649
+ children: error
934
1650
  }), jsx(Description, {
935
1651
  forId: id,
936
1652
  element: element,
@@ -1237,6 +1953,25 @@ function prefixId$5(id) {
1237
1953
  return `bio-properties-panel-${id}`;
1238
1954
  }
1239
1955
 
1956
+ const noop$1 = () => {};
1957
+ /**
1958
+ * @typedef { { value: string, label: string, disabled: boolean } } Option
1959
+ */
1960
+
1961
+ /**
1962
+ * Provides basic select input.
1963
+ *
1964
+ * @param {object} props
1965
+ * @param {string} props.id
1966
+ * @param {string[]} props.path
1967
+ * @param {string} props.label
1968
+ * @param {Function} props.onChange
1969
+ * @param {Array<Option>} [props.options]
1970
+ * @param {string} props.value
1971
+ * @param {boolean} [props.disabled]
1972
+ */
1973
+
1974
+
1240
1975
  function Select(props) {
1241
1976
  const {
1242
1977
  id,
@@ -1244,8 +1979,10 @@ function Select(props) {
1244
1979
  onChange,
1245
1980
  options = [],
1246
1981
  value,
1247
- disabled
1982
+ disabled,
1983
+ show = noop$1
1248
1984
  } = props;
1985
+ const ref = useShowEntryEvent(show);
1249
1986
 
1250
1987
  const handleChange = ({
1251
1988
  target
@@ -1260,6 +1997,7 @@ function Select(props) {
1260
1997
  class: "bio-properties-panel-label",
1261
1998
  children: label
1262
1999
  }), jsx("select", {
2000
+ ref: ref,
1263
2001
  id: prefixId$4(id),
1264
2002
  name: id,
1265
2003
  class: "bio-properties-panel-input",
@@ -1298,12 +2036,14 @@ function SelectEntry(props) {
1298
2036
  getValue,
1299
2037
  setValue,
1300
2038
  getOptions,
1301
- disabled
2039
+ disabled,
2040
+ show = noop$1
1302
2041
  } = props;
1303
2042
  const value = getValue(element);
1304
2043
  const options = getOptions(element);
2044
+ const error = useShowErrorEvent(show);
1305
2045
  return jsxs("div", {
1306
- class: "bio-properties-panel-entry",
2046
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
1307
2047
  "data-entry-id": id,
1308
2048
  children: [jsx(Select, {
1309
2049
  id: id,
@@ -1311,7 +2051,11 @@ function SelectEntry(props) {
1311
2051
  value: value,
1312
2052
  onChange: setValue,
1313
2053
  options: options,
1314
- disabled: disabled
2054
+ disabled: disabled,
2055
+ show: show
2056
+ }), error && jsx("div", {
2057
+ class: "bio-properties-panel-error",
2058
+ children: error
1315
2059
  }), jsx(Description, {
1316
2060
  forId: id,
1317
2061
  element: element,
@@ -1482,6 +2226,8 @@ function prefixId$2(id) {
1482
2226
  return `bio-properties-panel-${id}`;
1483
2227
  }
1484
2228
 
2229
+ const noop = () => {};
2230
+
1485
2231
  function Textfield(props) {
1486
2232
  const {
1487
2233
  debounce,
@@ -1490,8 +2236,10 @@ function Textfield(props) {
1490
2236
  label,
1491
2237
  onInput,
1492
2238
  feel = false,
1493
- value = ''
2239
+ value = '',
2240
+ show = noop
1494
2241
  } = props;
2242
+ const ref = useShowEntryEvent(show);
1495
2243
  const handleInput = useMemo(() => {
1496
2244
  return debounce(({
1497
2245
  target
@@ -1507,6 +2255,7 @@ function Textfield(props) {
1507
2255
  label: label
1508
2256
  })]
1509
2257
  }), jsx("input", {
2258
+ ref: ref,
1510
2259
  id: prefixId$1(id),
1511
2260
  type: "text",
1512
2261
  name: id,
@@ -1546,53 +2295,61 @@ function TextfieldEntry(props) {
1546
2295
  label,
1547
2296
  getValue,
1548
2297
  setValue,
1549
- validate
2298
+ validate,
2299
+ show = noop
1550
2300
  } = props;
1551
- const [error, setError] = useState(null);
1552
- const [invalidValueCache, setInvalidValueCache] = useState(null);
2301
+ const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
2302
+ const [validationError, setValidationError] = useState(null);
1553
2303
  let value = getValue(element);
1554
- const prevValue = usePrevious(value); // validate again when value prop changed
1555
-
2304
+ const previousValue = usePrevious(value);
1556
2305
  useEffect(() => {
1557
- const err = validate ? validate(value) : null;
1558
- setError(err);
1559
- }, [value]); // validate on change
2306
+ if (isFunction(validate)) {
2307
+ const newValidationError = validate(value) || null;
2308
+ setValidationError(newValidationError);
2309
+ }
2310
+ }, [value]);
1560
2311
 
1561
- const handleChange = newValue => {
1562
- const err = validate ? validate(newValue) : null;
2312
+ const onInput = newValue => {
2313
+ let newValidationError = null;
2314
+
2315
+ if (isFunction(validate)) {
2316
+ newValidationError = validate(newValue) || null;
2317
+ }
1563
2318
 
1564
- if (err) {
1565
- setInvalidValueCache(newValue);
2319
+ if (newValidationError) {
2320
+ setCachedInvalidValue(newValue);
1566
2321
  } else {
1567
2322
  setValue(newValue);
1568
2323
  }
1569
2324
 
1570
- setError(err);
1571
- }; // keep showing invalid value on errors, although it was not set
1572
-
2325
+ setValidationError(newValidationError);
2326
+ };
1573
2327
 
1574
- if (prevValue === value && error) {
1575
- value = invalidValueCache;
2328
+ if (previousValue === value && validationError) {
2329
+ value = cachedInvalidValue;
1576
2330
  }
1577
2331
 
2332
+ const temporaryError = useShowErrorEvent(show);
2333
+ const error = temporaryError || validationError;
1578
2334
  return jsxs("div", {
1579
2335
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
1580
2336
  "data-entry-id": id,
1581
2337
  children: [jsx(Textfield, {
1582
- id: id,
1583
- label: label,
1584
- value: value,
1585
- onInput: handleChange,
1586
2338
  debounce: debounce,
1587
2339
  disabled: disabled,
1588
- feel: feel
2340
+ feel: feel,
2341
+ id: id,
2342
+ label: label,
2343
+ onInput: onInput,
2344
+ show: show,
2345
+ value: value
2346
+ }), error && jsx("div", {
2347
+ class: "bio-properties-panel-error",
2348
+ children: error
1589
2349
  }), jsx(Description, {
1590
2350
  forId: id,
1591
2351
  element: element,
1592
2352
  value: description
1593
- }), error && jsx("div", {
1594
- class: "bio-properties-panel-error",
1595
- children: error
1596
2353
  })]
1597
2354
  });
1598
2355
  }
@@ -1708,5 +2465,5 @@ var index = {
1708
2465
  debounceInput: ['factory', debounceInput]
1709
2466
  };
1710
2467
 
1711
- export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, FeelOptionalIcon, FeelRequiredIcon, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, PropertiesPanel, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$6 as isCheckboxEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useKeyFactory, useLayoutState, usePrevious };
2468
+ export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, EventContext, ExternalLinkIcon, FeelOptionalIcon, FeelRequiredIcon, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$6 as isCheckboxEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useEvent, useEventBuffer, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useShowErrorEvent };
1712
2469
  //# sourceMappingURL=index.esm.js.map