@bpmn-io/form-js-viewer 1.0.0-alpha.9 → 1.1.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.cjs CHANGED
@@ -2,10 +2,9 @@
2
2
 
3
3
  var Ids = require('ids');
4
4
  var minDash = require('min-dash');
5
+ var Big = require('big.js');
5
6
  var feelin = require('feelin');
6
7
  var feelers = require('feelers');
7
- var showdown = require('showdown');
8
- var Big = require('big.js');
9
8
  var classNames = require('classnames');
10
9
  var jsxRuntime = require('preact/jsx-runtime');
11
10
  var hooks = require('preact/hooks');
@@ -14,16 +13,36 @@ var React = require('preact/compat');
14
13
  var flatpickr = require('flatpickr');
15
14
  var Markup = require('preact-markup');
16
15
  var didi = require('didi');
16
+ var showdown = require('showdown');
17
17
 
18
- const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) => {
18
+ function _interopNamespaceDefault(e) {
19
+ var n = Object.create(null);
20
+ if (e) {
21
+ Object.keys(e).forEach(function (k) {
22
+ if (k !== 'default') {
23
+ var d = Object.getOwnPropertyDescriptor(e, k);
24
+ Object.defineProperty(n, k, d.get ? d : {
25
+ enumerable: true,
26
+ get: function () { return e[k]; }
27
+ });
28
+ }
29
+ });
30
+ }
31
+ n.default = e;
32
+ return Object.freeze(n);
33
+ }
34
+
35
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
36
+
37
+ const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', options = {}) => {
19
38
  const {
20
39
  depth = 0,
21
40
  specialDepthAccessors = {}
22
41
  } = options;
23
42
  if (!['expression', 'unaryTest'].includes(feelFlavour)) return [];
24
- const tree = feelFlavour === 'expression' ? feelin.parseExpressions(feelString) : feelin.parseUnaryTests(feelString);
43
+ const tree = feelFlavour === 'expression' ? feelin.parseExpression(feelString) : feelin.parseUnaryTests(feelString);
25
44
  const simpleExpressionTree = _buildSimpleFeelStructureTree(tree, feelString);
26
- return function _unfoldVariables(node) {
45
+ const variables = function _unfoldVariables(node) {
27
46
  if (node.name === 'PathExpression') {
28
47
  if (Object.keys(specialDepthAccessors).length === 0) {
29
48
  return depth === 0 ? [_getVariableNameAtPathIndex(node, 0)] : [];
@@ -36,34 +55,38 @@ const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) =>
36
55
 
37
56
  // for any other kind of node, traverse its children and flatten the result
38
57
  if (node.children) {
39
- return node.children.reduce((acc, child) => {
58
+ const variables = node.children.reduce((acc, child) => {
40
59
  return acc.concat(_unfoldVariables(child));
41
60
  }, []);
61
+
62
+ // if we are within a filter context, we need to remove the item variable as it is used for iteration there
63
+ return node.name === 'FilterContext' ? variables.filter(name => name !== 'item') : variables;
42
64
  }
43
65
  return [];
44
66
  }(simpleExpressionTree);
67
+ return [...new Set(variables)];
45
68
  };
46
69
 
47
- /**
48
- * Get the variable name at the specified index in a given path expression.
49
- *
50
- * @param {Object} root - The root node of the path expression tree.
51
- * @param {number} index - The index of the variable name to retrieve.
52
- * @returns {string|null} The variable name at the specified index or null if index is out of bounds.
70
+ /**
71
+ * Get the variable name at the specified index in a given path expression.
72
+ *
73
+ * @param {Object} root - The root node of the path expression tree.
74
+ * @param {number} index - The index of the variable name to retrieve.
75
+ * @returns {string|null} The variable name at the specified index or null if index is out of bounds.
53
76
  */
54
77
  const _getVariableNameAtPathIndex = (root, index) => {
55
78
  const accessors = _deconstructPathExpression(root);
56
79
  return accessors[index] || null;
57
80
  };
58
81
 
59
- /**
60
- * Extracts the variables which are required of the external context for a given path expression.
61
- * This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
62
- *
63
- * @param {Object} node - The root node of the path expression tree.
64
- * @param {number} initialDepth - The depth at which the root node is located in the outer context.
65
- * @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
66
- * @returns {Set} - A set containing the extracted variable names.
82
+ /**
83
+ * Extracts the variables which are required of the external context for a given path expression.
84
+ * This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
85
+ *
86
+ * @param {Object} node - The root node of the path expression tree.
87
+ * @param {number} initialDepth - The depth at which the root node is located in the outer context.
88
+ * @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
89
+ * @returns {Set} - A set containing the extracted variable names.
67
90
  */
68
91
  const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
69
92
  // depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
@@ -109,11 +132,11 @@ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) =
109
132
  return new Set(extractedVariables);
110
133
  };
111
134
 
112
- /**
113
- * Deconstructs a path expression tree into an array of components.
114
- *
115
- * @param {Object} root - The root node of the path expression tree.
116
- * @returns {Array<string>} An array of components in the path expression, in the correct order.
135
+ /**
136
+ * Deconstructs a path expression tree into an array of components.
137
+ *
138
+ * @param {Object} root - The root node of the path expression tree.
139
+ * @returns {Array<string>} An array of components in the path expression, in the correct order.
117
140
  */
118
141
  const _deconstructPathExpression = root => {
119
142
  let node = root;
@@ -132,13 +155,13 @@ const _deconstructPathExpression = root => {
132
155
  return parts.reverse();
133
156
  };
134
157
 
135
- /**
136
- * Builds a simplified feel structure tree from the given parse tree and feel string.
137
- * The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
138
- *
139
- * @param {Object} parseTree - The parse tree generated by a parser.
140
- * @param {string} feelString - The feel string used for parsing.
141
- * @returns {Object} The simplified feel structure tree.
158
+ /**
159
+ * Builds a simplified feel structure tree from the given parse tree and feel string.
160
+ * The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
161
+ *
162
+ * @param {Object} parseTree - The parse tree generated by a parser.
163
+ * @param {string} feelString - The feel string used for parsing.
164
+ * @returns {Object} The simplified feel structure tree.
142
165
  */
143
166
  const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
144
167
  const stack = [{
@@ -161,7 +184,45 @@ const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
161
184
  parent.children.push(result);
162
185
  }
163
186
  });
164
- return stack[0].children[0];
187
+ return _extractFilterExpressions(stack[0].children[0]);
188
+ };
189
+
190
+ /**
191
+ * Restructure the tree in such a way to bring filters (which create new contexts) to the root of the tree.
192
+ * This is done to simplify the extraction of variables and match the context hierarchy.
193
+ */
194
+ const _extractFilterExpressions = tree => {
195
+ const flattenedExpressionTree = {
196
+ name: 'Root',
197
+ children: [tree]
198
+ };
199
+ const iterate = node => {
200
+ if (node.children) {
201
+ for (let x = 0; x < node.children.length; x++) {
202
+ if (node.children[x].name === 'FilterExpression') {
203
+ const filterTarget = node.children[x].children[0];
204
+ const filterExpression = node.children[x].children[2];
205
+
206
+ // bypass the filter expression
207
+ node.children[x] = filterTarget;
208
+ const taggedFilterExpression = {
209
+ name: 'FilterContext',
210
+ children: [filterExpression]
211
+ };
212
+
213
+ // append the filter expression to the root
214
+ flattenedExpressionTree.children.push(taggedFilterExpression);
215
+
216
+ // recursively iterate the expression
217
+ iterate(filterExpression);
218
+ } else {
219
+ iterate(node.children[x]);
220
+ }
221
+ }
222
+ }
223
+ };
224
+ iterate(tree);
225
+ return flattenedExpressionTree;
165
226
  };
166
227
 
167
228
  class FeelExpressionLanguage {
@@ -169,25 +230,25 @@ class FeelExpressionLanguage {
169
230
  this._eventBus = eventBus;
170
231
  }
171
232
 
172
- /**
173
- * Determines if the given value is a FEEL expression.
174
- *
175
- * @param {any} value
176
- * @returns {boolean}
177
- *
233
+ /**
234
+ * Determines if the given value is a FEEL expression.
235
+ *
236
+ * @param {any} value
237
+ * @returns {boolean}
238
+ *
178
239
  */
179
240
  isExpression(value) {
180
241
  return minDash.isString(value) && value.startsWith('=');
181
242
  }
182
243
 
183
- /**
184
- * Retrieve variable names from a given FEEL expression.
185
- *
186
- * @param {string} expression
187
- * @param {object} [options]
188
- * @param {string} [options.type]
189
- *
190
- * @returns {string[]}
244
+ /**
245
+ * Retrieve variable names from a given FEEL expression.
246
+ *
247
+ * @param {string} expression
248
+ * @param {object} [options]
249
+ * @param {string} [options.type]
250
+ *
251
+ * @returns {string[]}
191
252
  */
192
253
  getVariableNames(expression, options = {}) {
193
254
  const {
@@ -202,13 +263,13 @@ class FeelExpressionLanguage {
202
263
  return getFlavouredFeelVariableNames(expression, type);
203
264
  }
204
265
 
205
- /**
206
- * Evaluate an expression.
207
- *
208
- * @param {string} expression
209
- * @param {import('../../types').Data} [data]
210
- *
211
- * @returns {any}
266
+ /**
267
+ * Evaluate an expression.
268
+ *
269
+ * @param {string} expression
270
+ * @param {import('../../types').Data} [data]
271
+ *
272
+ * @returns {any}
212
273
  */
213
274
  evaluate(expression, data = {}) {
214
275
  if (!expression) {
@@ -233,23 +294,23 @@ FeelExpressionLanguage.$inject = ['eventBus'];
233
294
  class FeelersTemplating {
234
295
  constructor() {}
235
296
 
236
- /**
237
- * Determines if the given value is a feelers template.
238
- *
239
- * @param {any} value
240
- * @returns {boolean}
241
- *
297
+ /**
298
+ * Determines if the given value is a feelers template.
299
+ *
300
+ * @param {any} value
301
+ * @returns {boolean}
302
+ *
242
303
  */
243
304
  isTemplate(value) {
244
305
  return minDash.isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
245
306
  }
246
307
 
247
- /**
248
- * Retrieve variable names from a given feelers template.
249
- *
250
- * @param {string} template
251
- *
252
- * @returns {string[]}
308
+ /**
309
+ * Retrieve variable names from a given feelers template.
310
+ *
311
+ * @param {string} template
312
+ *
313
+ * @returns {string[]}
253
314
  */
254
315
  getVariableNames(template) {
255
316
  if (!this.isTemplate(template)) {
@@ -275,17 +336,17 @@ class FeelersTemplating {
275
336
  }, []);
276
337
  }
277
338
 
278
- /**
279
- * Evaluate a template.
280
- *
281
- * @param {string} template
282
- * @param {Object<string, any>} context
283
- * @param {Object} options
284
- * @param {boolean} [options.debug = false]
285
- * @param {boolean} [options.strict = false]
286
- * @param {Function} [options.buildDebugString]
287
- *
288
- * @returns
339
+ /**
340
+ * Evaluate a template.
341
+ *
342
+ * @param {string} template
343
+ * @param {Object<string, any>} context
344
+ * @param {Object} options
345
+ * @param {boolean} [options.debug = false]
346
+ * @param {boolean} [options.strict = false]
347
+ * @param {Function} [options.buildDebugString]
348
+ *
349
+ * @returns
289
350
  */
290
351
  evaluate(template, context = {}, options = {}) {
291
352
  const {
@@ -300,22 +361,22 @@ class FeelersTemplating {
300
361
  });
301
362
  }
302
363
 
303
- /**
304
- * @typedef {Object} ExpressionWithDepth
305
- * @property {number} depth - The depth of the expression in the syntax tree.
306
- * @property {string} expression - The extracted expression
364
+ /**
365
+ * @typedef {Object} ExpressionWithDepth
366
+ * @property {number} depth - The depth of the expression in the syntax tree.
367
+ * @property {string} expression - The extracted expression
307
368
  */
308
369
 
309
- /**
310
- * Extracts all feel expressions in the template along with their depth in the syntax tree.
311
- * The depth is incremented for child expressions of loops to account for context drilling.
312
- * @name extractExpressionsWithDepth
313
- * @param {string} template - A feelers template string.
314
- * @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
315
- *
316
- * @example
317
- * const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
318
- * const extractedExpressions = _extractExpressionsWithDepth(template);
370
+ /**
371
+ * Extracts all feel expressions in the template along with their depth in the syntax tree.
372
+ * The depth is incremented for child expressions of loops to account for context drilling.
373
+ * @name extractExpressionsWithDepth
374
+ * @param {string} template - A feelers template string.
375
+ * @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
376
+ *
377
+ * @example
378
+ * const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
379
+ * const extractedExpressions = _extractExpressionsWithDepth(template);
319
380
  */
320
381
  _extractExpressionsWithDepth(template) {
321
382
  // build simplified feelers syntax tree
@@ -346,9 +407,9 @@ class FeelersTemplating {
346
407
  }
347
408
  FeelersTemplating.$inject = [];
348
409
 
349
- /**
350
- * @typedef {object} Condition
351
- * @property {string} [hide]
410
+ /**
411
+ * @typedef {object} Condition
412
+ * @property {string} [hide]
352
413
  */
353
414
 
354
415
  class ConditionChecker {
@@ -357,11 +418,11 @@ class ConditionChecker {
357
418
  this._eventBus = eventBus;
358
419
  }
359
420
 
360
- /**
361
- * For given data, remove properties based on condition.
362
- *
363
- * @param {Object<string, any>} properties
364
- * @param {Object<string, any>} data
421
+ /**
422
+ * For given data, remove properties based on condition.
423
+ *
424
+ * @param {Object<string, any>} properties
425
+ * @param {Object<string, any>} data
365
426
  */
366
427
  applyConditions(properties, data = {}) {
367
428
  const conditions = this._getConditions();
@@ -380,13 +441,13 @@ class ConditionChecker {
380
441
  return newProperties;
381
442
  }
382
443
 
383
- /**
384
- * Check if given condition is met. Returns null for invalid/missing conditions.
385
- *
386
- * @param {string} condition
387
- * @param {import('../../types').Data} [data]
388
- *
389
- * @returns {boolean|null}
444
+ /**
445
+ * Check if given condition is met. Returns null for invalid/missing conditions.
446
+ *
447
+ * @param {string} condition
448
+ * @param {import('../../types').Data} [data]
449
+ *
450
+ * @returns {boolean|null}
390
451
  */
391
452
  check(condition, data = {}) {
392
453
  if (!condition) {
@@ -407,12 +468,12 @@ class ConditionChecker {
407
468
  }
408
469
  }
409
470
 
410
- /**
411
- * Check if hide condition is met.
412
- *
413
- * @param {Condition} condition
414
- * @param {Object<string, any>} data
415
- * @returns {boolean}
471
+ /**
472
+ * Check if hide condition is met.
473
+ *
474
+ * @param {Condition} condition
475
+ * @param {Object<string, any>} data
476
+ * @returns {boolean}
416
477
  */
417
478
  _checkHideCondition(condition, data) {
418
479
  if (!condition.hide) {
@@ -421,55 +482,787 @@ class ConditionChecker {
421
482
  const result = this.check(condition.hide, data);
422
483
  return result === true;
423
484
  }
424
- _getConditions() {
425
- const formFields = this._formFieldRegistry.getAll();
426
- return formFields.reduce((conditions, formField) => {
427
- const {
428
- key,
429
- conditional: condition
430
- } = formField;
431
- if (key && condition) {
432
- return [...conditions, {
433
- key,
434
- condition
435
- }];
436
- }
437
- return conditions;
438
- }, []);
485
+ _getConditions() {
486
+ const formFields = this._formFieldRegistry.getAll();
487
+ return formFields.reduce((conditions, formField) => {
488
+ const {
489
+ key,
490
+ conditional: condition
491
+ } = formField;
492
+ if (key && condition) {
493
+ return [...conditions, {
494
+ key,
495
+ condition
496
+ }];
497
+ }
498
+ return conditions;
499
+ }, []);
500
+ }
501
+ }
502
+ ConditionChecker.$inject = ['formFieldRegistry', 'eventBus'];
503
+
504
+ var ExpressionLanguageModule = {
505
+ __init__: ['expressionLanguage', 'templating', 'conditionChecker'],
506
+ expressionLanguage: ['type', FeelExpressionLanguage],
507
+ templating: ['type', FeelersTemplating],
508
+ conditionChecker: ['type', ConditionChecker]
509
+ };
510
+
511
+ // bootstrap showdown to support github flavored markdown
512
+ showdown.setFlavor('github');
513
+ class MarkdownRenderer {
514
+ constructor() {
515
+ this._converter = new showdown.Converter();
516
+ }
517
+
518
+ /**
519
+ * Render markdown to HTML.
520
+ *
521
+ * @param {string} markdown - The markdown to render
522
+ *
523
+ * @returns {string} HTML
524
+ */
525
+ render(markdown) {
526
+ return this._converter.makeHtml(markdown);
527
+ }
528
+ }
529
+ MarkdownRenderer.$inject = [];
530
+
531
+ var MarkdownModule = {
532
+ __init__: ['markdownRenderer'],
533
+ markdownRenderer: ['type', MarkdownRenderer]
534
+ };
535
+
536
+ /**
537
+ * @typedef {import('didi').Injector} Injector
538
+ *
539
+ * @typedef {import('../core/Types').ElementLike} ElementLike
540
+ *
541
+ * @typedef {import('../core/EventBus').default} EventBus
542
+ * @typedef {import('./CommandHandler').default} CommandHandler
543
+ *
544
+ * @typedef { any } CommandContext
545
+ * @typedef { {
546
+ * new (...args: any[]) : CommandHandler
547
+ * } } CommandHandlerConstructor
548
+ * @typedef { {
549
+ * [key: string]: CommandHandler;
550
+ * } } CommandHandlerMap
551
+ * @typedef { {
552
+ * command: string;
553
+ * context: any;
554
+ * id?: any;
555
+ * } } CommandStackAction
556
+ * @typedef { {
557
+ * actions: CommandStackAction[];
558
+ * dirty: ElementLike[];
559
+ * trigger: 'execute' | 'undo' | 'redo' | 'clear' | null;
560
+ * atomic?: boolean;
561
+ * } } CurrentExecution
562
+ */
563
+
564
+ /**
565
+ * A service that offers un- and redoable execution of commands.
566
+ *
567
+ * The command stack is responsible for executing modeling actions
568
+ * in a un- and redoable manner. To do this it delegates the actual
569
+ * command execution to {@link CommandHandler}s.
570
+ *
571
+ * Command handlers provide {@link CommandHandler#execute(ctx)} and
572
+ * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
573
+ * identified by a command context.
574
+ *
575
+ *
576
+ * ## Life-Cycle events
577
+ *
578
+ * In the process the command stack fires a number of life-cycle events
579
+ * that other components to participate in the command execution.
580
+ *
581
+ * * preExecute
582
+ * * preExecuted
583
+ * * execute
584
+ * * executed
585
+ * * postExecute
586
+ * * postExecuted
587
+ * * revert
588
+ * * reverted
589
+ *
590
+ * A special event is used for validating, whether a command can be
591
+ * performed prior to its execution.
592
+ *
593
+ * * canExecute
594
+ *
595
+ * Each of the events is fired as `commandStack.{eventName}` and
596
+ * `commandStack.{commandName}.{eventName}`, respectively. This gives
597
+ * components fine grained control on where to hook into.
598
+ *
599
+ * The event object fired transports `command`, the name of the
600
+ * command and `context`, the command context.
601
+ *
602
+ *
603
+ * ## Creating Command Handlers
604
+ *
605
+ * Command handlers should provide the {@link CommandHandler#execute(ctx)}
606
+ * and {@link CommandHandler#revert(ctx)} methods to implement
607
+ * redoing and undoing of a command.
608
+ *
609
+ * A command handler _must_ ensure undo is performed properly in order
610
+ * not to break the undo chain. It must also return the shapes that
611
+ * got changed during the `execute` and `revert` operations.
612
+ *
613
+ * Command handlers may execute other modeling operations (and thus
614
+ * commands) in their `preExecute(d)` and `postExecute(d)` phases. The command
615
+ * stack will properly group all commands together into a logical unit
616
+ * that may be re- and undone atomically.
617
+ *
618
+ * Command handlers must not execute other commands from within their
619
+ * core implementation (`execute`, `revert`).
620
+ *
621
+ *
622
+ * ## Change Tracking
623
+ *
624
+ * During the execution of the CommandStack it will keep track of all
625
+ * elements that have been touched during the command's execution.
626
+ *
627
+ * At the end of the CommandStack execution it will notify interested
628
+ * components via an 'elements.changed' event with all the dirty
629
+ * elements.
630
+ *
631
+ * The event can be picked up by components that are interested in the fact
632
+ * that elements have been changed. One use case for this is updating
633
+ * their graphical representation after moving / resizing or deletion.
634
+ *
635
+ * @see CommandHandler
636
+ *
637
+ * @param {EventBus} eventBus
638
+ * @param {Injector} injector
639
+ */
640
+ function CommandStack(eventBus, injector) {
641
+ /**
642
+ * A map of all registered command handlers.
643
+ *
644
+ * @type {CommandHandlerMap}
645
+ */
646
+ this._handlerMap = {};
647
+
648
+ /**
649
+ * A stack containing all re/undoable actions on the diagram
650
+ *
651
+ * @type {CommandStackAction[]}
652
+ */
653
+ this._stack = [];
654
+
655
+ /**
656
+ * The current index on the stack
657
+ *
658
+ * @type {number}
659
+ */
660
+ this._stackIdx = -1;
661
+
662
+ /**
663
+ * Current active commandStack execution
664
+ *
665
+ * @type {CurrentExecution}
666
+ */
667
+ this._currentExecution = {
668
+ actions: [],
669
+ dirty: [],
670
+ trigger: null
671
+ };
672
+
673
+ /**
674
+ * @type {Injector}
675
+ */
676
+ this._injector = injector;
677
+
678
+ /**
679
+ * @type EventBus
680
+ */
681
+ this._eventBus = eventBus;
682
+
683
+ /**
684
+ * @type { number }
685
+ */
686
+ this._uid = 1;
687
+ eventBus.on(['diagram.destroy', 'diagram.clear'], function () {
688
+ this.clear(false);
689
+ }, this);
690
+ }
691
+ CommandStack.$inject = ['eventBus', 'injector'];
692
+
693
+ /**
694
+ * Execute a command.
695
+ *
696
+ * @param {string} command The command to execute.
697
+ * @param {CommandContext} context The context with which to execute the command.
698
+ */
699
+ CommandStack.prototype.execute = function (command, context) {
700
+ if (!command) {
701
+ throw new Error('command required');
702
+ }
703
+ this._currentExecution.trigger = 'execute';
704
+ const action = {
705
+ command: command,
706
+ context: context
707
+ };
708
+ this._pushAction(action);
709
+ this._internalExecute(action);
710
+ this._popAction();
711
+ };
712
+
713
+ /**
714
+ * Check whether a command can be executed.
715
+ *
716
+ * Implementors may hook into the mechanism on two ways:
717
+ *
718
+ * * in event listeners:
719
+ *
720
+ * Users may prevent the execution via an event listener.
721
+ * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
722
+ *
723
+ * * in command handlers:
724
+ *
725
+ * If the method {@link CommandHandler#canExecute} is implemented in a handler
726
+ * it will be called to figure out whether the execution is allowed.
727
+ *
728
+ * @param {string} command The command to execute.
729
+ * @param {CommandContext} context The context with which to execute the command.
730
+ *
731
+ * @return {boolean} Whether the command can be executed with the given context.
732
+ */
733
+ CommandStack.prototype.canExecute = function (command, context) {
734
+ const action = {
735
+ command: command,
736
+ context: context
737
+ };
738
+ const handler = this._getHandler(command);
739
+ let result = this._fire(command, 'canExecute', action);
740
+
741
+ // handler#canExecute will only be called if no listener
742
+ // decided on a result already
743
+ if (result === undefined) {
744
+ if (!handler) {
745
+ return false;
746
+ }
747
+ if (handler.canExecute) {
748
+ result = handler.canExecute(context);
749
+ }
750
+ }
751
+ return result;
752
+ };
753
+
754
+ /**
755
+ * Clear the command stack, erasing all undo / redo history.
756
+ *
757
+ * @param {boolean} [emit=true] Whether to fire an event. Defaults to `true`.
758
+ */
759
+ CommandStack.prototype.clear = function (emit) {
760
+ this._stack.length = 0;
761
+ this._stackIdx = -1;
762
+ if (emit !== false) {
763
+ this._fire('changed', {
764
+ trigger: 'clear'
765
+ });
766
+ }
767
+ };
768
+
769
+ /**
770
+ * Undo last command(s)
771
+ */
772
+ CommandStack.prototype.undo = function () {
773
+ let action = this._getUndoAction(),
774
+ next;
775
+ if (action) {
776
+ this._currentExecution.trigger = 'undo';
777
+ this._pushAction(action);
778
+ while (action) {
779
+ this._internalUndo(action);
780
+ next = this._getUndoAction();
781
+ if (!next || next.id !== action.id) {
782
+ break;
783
+ }
784
+ action = next;
785
+ }
786
+ this._popAction();
787
+ }
788
+ };
789
+
790
+ /**
791
+ * Redo last command(s)
792
+ */
793
+ CommandStack.prototype.redo = function () {
794
+ let action = this._getRedoAction(),
795
+ next;
796
+ if (action) {
797
+ this._currentExecution.trigger = 'redo';
798
+ this._pushAction(action);
799
+ while (action) {
800
+ this._internalExecute(action, true);
801
+ next = this._getRedoAction();
802
+ if (!next || next.id !== action.id) {
803
+ break;
804
+ }
805
+ action = next;
806
+ }
807
+ this._popAction();
808
+ }
809
+ };
810
+
811
+ /**
812
+ * Register a handler instance with the command stack.
813
+ *
814
+ * @param {string} command Command to be executed.
815
+ * @param {CommandHandler} handler Handler to execute the command.
816
+ */
817
+ CommandStack.prototype.register = function (command, handler) {
818
+ this._setHandler(command, handler);
819
+ };
820
+
821
+ /**
822
+ * Register a handler type with the command stack by instantiating it and
823
+ * injecting its dependencies.
824
+ *
825
+ * @param {string} command Command to be executed.
826
+ * @param {CommandHandlerConstructor} handlerCls Constructor to instantiate a {@link CommandHandler}.
827
+ */
828
+ CommandStack.prototype.registerHandler = function (command, handlerCls) {
829
+ if (!command || !handlerCls) {
830
+ throw new Error('command and handlerCls must be defined');
831
+ }
832
+ const handler = this._injector.instantiate(handlerCls);
833
+ this.register(command, handler);
834
+ };
835
+
836
+ /**
837
+ * @return {boolean}
838
+ */
839
+ CommandStack.prototype.canUndo = function () {
840
+ return !!this._getUndoAction();
841
+ };
842
+
843
+ /**
844
+ * @return {boolean}
845
+ */
846
+ CommandStack.prototype.canRedo = function () {
847
+ return !!this._getRedoAction();
848
+ };
849
+
850
+ // stack access //////////////////////
851
+
852
+ CommandStack.prototype._getRedoAction = function () {
853
+ return this._stack[this._stackIdx + 1];
854
+ };
855
+ CommandStack.prototype._getUndoAction = function () {
856
+ return this._stack[this._stackIdx];
857
+ };
858
+
859
+ // internal functionality //////////////////////
860
+
861
+ CommandStack.prototype._internalUndo = function (action) {
862
+ const command = action.command,
863
+ context = action.context;
864
+ const handler = this._getHandler(command);
865
+
866
+ // guard against illegal nested command stack invocations
867
+ this._atomicDo(() => {
868
+ this._fire(command, 'revert', action);
869
+ if (handler.revert) {
870
+ this._markDirty(handler.revert(context));
871
+ }
872
+ this._revertedAction(action);
873
+ this._fire(command, 'reverted', action);
874
+ });
875
+ };
876
+ CommandStack.prototype._fire = function (command, qualifier, event) {
877
+ if (arguments.length < 3) {
878
+ event = qualifier;
879
+ qualifier = null;
880
+ }
881
+ const names = qualifier ? [command + '.' + qualifier, qualifier] : [command];
882
+ let result;
883
+ event = this._eventBus.createEvent(event);
884
+ for (const name of names) {
885
+ result = this._eventBus.fire('commandStack.' + name, event);
886
+ if (event.cancelBubble) {
887
+ break;
888
+ }
889
+ }
890
+ return result;
891
+ };
892
+ CommandStack.prototype._createId = function () {
893
+ return this._uid++;
894
+ };
895
+ CommandStack.prototype._atomicDo = function (fn) {
896
+ const execution = this._currentExecution;
897
+ execution.atomic = true;
898
+ try {
899
+ fn();
900
+ } finally {
901
+ execution.atomic = false;
902
+ }
903
+ };
904
+ CommandStack.prototype._internalExecute = function (action, redo) {
905
+ const command = action.command,
906
+ context = action.context;
907
+ const handler = this._getHandler(command);
908
+ if (!handler) {
909
+ throw new Error('no command handler registered for <' + command + '>');
910
+ }
911
+ this._pushAction(action);
912
+ if (!redo) {
913
+ this._fire(command, 'preExecute', action);
914
+ if (handler.preExecute) {
915
+ handler.preExecute(context);
916
+ }
917
+ this._fire(command, 'preExecuted', action);
918
+ }
919
+
920
+ // guard against illegal nested command stack invocations
921
+ this._atomicDo(() => {
922
+ this._fire(command, 'execute', action);
923
+ if (handler.execute) {
924
+ // actual execute + mark return results as dirty
925
+ this._markDirty(handler.execute(context));
926
+ }
927
+
928
+ // log to stack
929
+ this._executedAction(action, redo);
930
+ this._fire(command, 'executed', action);
931
+ });
932
+ if (!redo) {
933
+ this._fire(command, 'postExecute', action);
934
+ if (handler.postExecute) {
935
+ handler.postExecute(context);
936
+ }
937
+ this._fire(command, 'postExecuted', action);
938
+ }
939
+ this._popAction();
940
+ };
941
+ CommandStack.prototype._pushAction = function (action) {
942
+ const execution = this._currentExecution,
943
+ actions = execution.actions;
944
+ const baseAction = actions[0];
945
+ if (execution.atomic) {
946
+ throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
947
+ }
948
+ if (!action.id) {
949
+ action.id = baseAction && baseAction.id || this._createId();
950
+ }
951
+ actions.push(action);
952
+ };
953
+ CommandStack.prototype._popAction = function () {
954
+ const execution = this._currentExecution,
955
+ trigger = execution.trigger,
956
+ actions = execution.actions,
957
+ dirty = execution.dirty;
958
+ actions.pop();
959
+ if (!actions.length) {
960
+ this._eventBus.fire('elements.changed', {
961
+ elements: minDash.uniqueBy('id', dirty.reverse())
962
+ });
963
+ dirty.length = 0;
964
+ this._fire('changed', {
965
+ trigger: trigger
966
+ });
967
+ execution.trigger = null;
968
+ }
969
+ };
970
+ CommandStack.prototype._markDirty = function (elements) {
971
+ const execution = this._currentExecution;
972
+ if (!elements) {
973
+ return;
974
+ }
975
+ elements = minDash.isArray(elements) ? elements : [elements];
976
+ execution.dirty = execution.dirty.concat(elements);
977
+ };
978
+ CommandStack.prototype._executedAction = function (action, redo) {
979
+ const stackIdx = ++this._stackIdx;
980
+ if (!redo) {
981
+ this._stack.splice(stackIdx, this._stack.length, action);
982
+ }
983
+ };
984
+ CommandStack.prototype._revertedAction = function (action) {
985
+ this._stackIdx--;
986
+ };
987
+ CommandStack.prototype._getHandler = function (command) {
988
+ return this._handlerMap[command];
989
+ };
990
+ CommandStack.prototype._setHandler = function (command, handler) {
991
+ if (!command || !handler) {
992
+ throw new Error('command and handler required');
993
+ }
994
+ if (this._handlerMap[command]) {
995
+ throw new Error('overriding handler for command <' + command + '>');
996
+ }
997
+ this._handlerMap[command] = handler;
998
+ };
999
+
1000
+ /**
1001
+ * @type { import('didi').ModuleDeclaration }
1002
+ */
1003
+ var commandModule = {
1004
+ commandStack: ['type', CommandStack]
1005
+ };
1006
+
1007
+ // config ///////////////////
1008
+
1009
+ const MINUTES_IN_DAY = 60 * 24;
1010
+ const DATETIME_SUBTYPES = {
1011
+ DATE: 'date',
1012
+ TIME: 'time',
1013
+ DATETIME: 'datetime'
1014
+ };
1015
+ const TIME_SERIALISING_FORMATS = {
1016
+ UTC_OFFSET: 'utc_offset',
1017
+ UTC_NORMALIZED: 'utc_normalized',
1018
+ NO_TIMEZONE: 'no_timezone'
1019
+ };
1020
+ const DATETIME_SUBTYPES_LABELS = {
1021
+ [DATETIME_SUBTYPES.DATE]: 'Date',
1022
+ [DATETIME_SUBTYPES.TIME]: 'Time',
1023
+ [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
1024
+ };
1025
+ const TIME_SERIALISINGFORMAT_LABELS = {
1026
+ [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
1027
+ [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
1028
+ [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
1029
+ };
1030
+ const DATETIME_SUBTYPE_PATH = ['subtype'];
1031
+ const DATE_LABEL_PATH = ['dateLabel'];
1032
+ const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
1033
+ const TIME_LABEL_PATH = ['timeLabel'];
1034
+ const TIME_USE24H_PATH = ['use24h'];
1035
+ const TIME_INTERVAL_PATH = ['timeInterval'];
1036
+ const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
1037
+
1038
+ // config ///////////////////
1039
+
1040
+ const VALUES_SOURCES = {
1041
+ STATIC: 'static',
1042
+ INPUT: 'input',
1043
+ EXPRESSION: 'expression'
1044
+ };
1045
+ const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
1046
+ const VALUES_SOURCES_LABELS = {
1047
+ [VALUES_SOURCES.STATIC]: 'Static',
1048
+ [VALUES_SOURCES.INPUT]: 'Input data',
1049
+ [VALUES_SOURCES.EXPRESSION]: 'Expression'
1050
+ };
1051
+ const VALUES_SOURCES_PATHS = {
1052
+ [VALUES_SOURCES.STATIC]: ['values'],
1053
+ [VALUES_SOURCES.INPUT]: ['valuesKey'],
1054
+ [VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
1055
+ };
1056
+ const VALUES_SOURCES_DEFAULTS = {
1057
+ [VALUES_SOURCES.STATIC]: [{
1058
+ label: 'Value',
1059
+ value: 'value'
1060
+ }],
1061
+ [VALUES_SOURCES.INPUT]: '',
1062
+ [VALUES_SOURCES.EXPRESSION]: '='
1063
+ };
1064
+
1065
+ // helpers ///////////////////
1066
+
1067
+ function getValuesSource(field) {
1068
+ for (const source of Object.values(VALUES_SOURCES)) {
1069
+ if (minDash.get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
1070
+ return source;
1071
+ }
1072
+ }
1073
+ return VALUES_SOURCE_DEFAULT;
1074
+ }
1075
+
1076
+ function createInjector(bootstrapModules) {
1077
+ const injector = new didi.Injector(bootstrapModules);
1078
+ injector.init();
1079
+ return injector;
1080
+ }
1081
+
1082
+ /**
1083
+ * @param {string?} prefix
1084
+ *
1085
+ * @returns Element
1086
+ */
1087
+ function createFormContainer(prefix = 'fjs') {
1088
+ const container = document.createElement('div');
1089
+ container.classList.add(`${prefix}-container`);
1090
+ return container;
1091
+ }
1092
+
1093
+ const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
1094
+ const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
1095
+ function findErrors(errors, path) {
1096
+ return errors[pathStringify(path)];
1097
+ }
1098
+ function isRequired(field) {
1099
+ return field.required;
1100
+ }
1101
+ function pathParse(path) {
1102
+ if (!path) {
1103
+ return [];
1104
+ }
1105
+ return path.split('.').map(key => {
1106
+ return isNaN(parseInt(key)) ? key : parseInt(key);
1107
+ });
1108
+ }
1109
+ function pathsEqual(a, b) {
1110
+ return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
1111
+ }
1112
+ function pathStringify(path) {
1113
+ if (!path) {
1114
+ return '';
1115
+ }
1116
+ return path.join('.');
1117
+ }
1118
+ const indices = {};
1119
+ function generateIndexForType(type) {
1120
+ if (type in indices) {
1121
+ indices[type]++;
1122
+ } else {
1123
+ indices[type] = 1;
1124
+ }
1125
+ return indices[type];
1126
+ }
1127
+ function generateIdForType(type) {
1128
+ return `${type}${generateIndexForType(type)}`;
1129
+ }
1130
+
1131
+ /**
1132
+ * @template T
1133
+ * @param {T} data
1134
+ * @param {(this: any, key: string, value: any) => any} [replacer]
1135
+ * @return {T}
1136
+ */
1137
+ function clone(data, replacer) {
1138
+ return JSON.parse(JSON.stringify(data, replacer));
1139
+ }
1140
+
1141
+ /**
1142
+ * Parse the schema for input variables a form might make use of
1143
+ *
1144
+ * @param {any} schema
1145
+ *
1146
+ * @return {string[]}
1147
+ */
1148
+ function getSchemaVariables(schema, options = {}) {
1149
+ const {
1150
+ expressionLanguage = new FeelExpressionLanguage(null),
1151
+ templating = new FeelersTemplating(),
1152
+ inputs = true,
1153
+ outputs = true
1154
+ } = options;
1155
+ if (!schema.components) {
1156
+ return [];
1157
+ }
1158
+ const variables = schema.components.reduce((variables, component) => {
1159
+ const {
1160
+ key,
1161
+ valuesKey,
1162
+ type
1163
+ } = component;
1164
+ if (['button'].includes(type)) {
1165
+ return variables;
1166
+ }
1167
+
1168
+ // collect bi-directional variables
1169
+ if (inputs || outputs) {
1170
+ if (key) {
1171
+ variables = [...variables, key];
1172
+ }
1173
+ }
1174
+
1175
+ // collect input-only variables
1176
+ if (inputs) {
1177
+ if (valuesKey) {
1178
+ variables = [...variables, valuesKey];
1179
+ }
1180
+ EXPRESSION_PROPERTIES.forEach(prop => {
1181
+ const property = minDash.get(component, prop.split('.'));
1182
+ if (property && expressionLanguage.isExpression(property)) {
1183
+ const expressionVariables = expressionLanguage.getVariableNames(property, {
1184
+ type: 'expression'
1185
+ });
1186
+ variables = [...variables, ...expressionVariables];
1187
+ }
1188
+ });
1189
+ TEMPLATE_PROPERTIES.forEach(prop => {
1190
+ const property = minDash.get(component, prop.split('.'));
1191
+ if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
1192
+ const templateVariables = templating.getVariableNames(property);
1193
+ variables = [...variables, ...templateVariables];
1194
+ }
1195
+ });
1196
+ }
1197
+ return variables.filter(variable => variable !== undefined || variable !== null);
1198
+ }, []);
1199
+
1200
+ // remove duplicates
1201
+ return Array.from(new Set(variables));
1202
+ }
1203
+
1204
+ class UpdateFieldValidationHandler {
1205
+ constructor(form, validator) {
1206
+ this._form = form;
1207
+ this._validator = validator;
1208
+ }
1209
+ execute(context) {
1210
+ const {
1211
+ field,
1212
+ value
1213
+ } = context;
1214
+ const {
1215
+ _path
1216
+ } = field;
1217
+ const {
1218
+ errors
1219
+ } = this._form._getState();
1220
+ context.oldErrors = clone(errors);
1221
+ const fieldErrors = this._validator.validateField(field, value);
1222
+ const updatedErrors = minDash.set(errors, [pathStringify(_path)], fieldErrors.length ? fieldErrors : undefined);
1223
+ this._form._setState({
1224
+ errors: updatedErrors
1225
+ });
1226
+ }
1227
+ revert(context) {
1228
+ this._form._setState({
1229
+ errors: context.oldErrors
1230
+ });
439
1231
  }
440
1232
  }
441
- ConditionChecker.$inject = ['formFieldRegistry', 'eventBus'];
1233
+ UpdateFieldValidationHandler.$inject = ['form', 'validator'];
442
1234
 
443
- var ExpressionLanguageModule = {
444
- __init__: ['expressionLanguage', 'templating', 'conditionChecker'],
445
- expressionLanguage: ['type', FeelExpressionLanguage],
446
- templating: ['type', FeelersTemplating],
447
- conditionChecker: ['type', ConditionChecker]
448
- };
449
-
450
- // bootstrap showdown to support github flavored markdown
451
- showdown.setFlavor('github');
452
- class MarkdownRenderer {
453
- constructor() {
454
- this._converter = new showdown.Converter();
1235
+ class ViewerCommands {
1236
+ constructor(commandStack, eventBus) {
1237
+ this._commandStack = commandStack;
1238
+ eventBus.on('form.init', () => {
1239
+ this.registerHandlers();
1240
+ });
455
1241
  }
456
-
457
- /**
458
- * Render markdown to HTML.
459
- *
460
- * @param {string} markdown - The markdown to render
461
- *
462
- * @returns {string} HTML
463
- */
464
- render(markdown) {
465
- return this._converter.makeHtml(markdown);
1242
+ registerHandlers() {
1243
+ Object.entries(this.getHandlers()).forEach(([id, handler]) => {
1244
+ this._commandStack.registerHandler(id, handler);
1245
+ });
1246
+ }
1247
+ getHandlers() {
1248
+ return {
1249
+ 'formField.validation.update': UpdateFieldValidationHandler
1250
+ };
1251
+ }
1252
+ updateFieldValidation(field, value) {
1253
+ const context = {
1254
+ field,
1255
+ value
1256
+ };
1257
+ this._commandStack.execute('formField.validation.update', context);
466
1258
  }
467
1259
  }
468
- MarkdownRenderer.$inject = [];
1260
+ ViewerCommands.$inject = ['commandStack', 'eventBus'];
469
1261
 
470
- var MarkdownModule = {
471
- __init__: ['markdownRenderer'],
472
- markdownRenderer: ['type', MarkdownRenderer]
1262
+ var ViewerCommandsModule = {
1263
+ __depends__: [commandModule],
1264
+ __init__: ['viewerCommands'],
1265
+ viewerCommands: ['type', ViewerCommands]
473
1266
  };
474
1267
 
475
1268
  var FN_REF = '__fn';
@@ -999,7 +1792,13 @@ function isNullEquivalentValue(value) {
999
1792
 
1000
1793
  const EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
1001
1794
  const PHONE_PATTERN = /(\+|00)(297|93|244|1264|358|355|376|971|54|374|1684|1268|61|43|994|257|32|229|226|880|359|973|1242|387|590|375|501|1441|591|55|1246|673|975|267|236|1|61|41|56|86|225|237|243|242|682|57|269|238|506|53|5999|61|1345|357|420|49|253|1767|45|1809|1829|1849|213|593|20|291|212|34|372|251|358|679|500|33|298|691|241|44|995|44|233|350|224|590|220|245|240|30|1473|299|502|594|1671|592|852|504|385|509|36|62|44|91|246|353|98|964|354|972|39|1876|44|962|81|76|77|254|996|855|686|1869|82|383|965|856|961|231|218|1758|423|94|266|370|352|371|853|590|212|377|373|261|960|52|692|389|223|356|95|382|976|1670|258|222|1664|596|230|265|60|262|264|687|227|672|234|505|683|31|47|977|674|64|968|92|507|64|51|63|680|675|48|1787|1939|850|351|595|970|689|974|262|40|7|250|966|249|221|65|500|4779|677|232|503|378|252|508|381|211|239|597|421|386|46|268|1721|248|963|1649|235|228|66|992|690|993|670|676|1868|216|90|688|886|255|256|380|598|1|998|3906698|379|1784|58|1284|1340|84|678|681|685|967|27|260|263)(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{4,20}$/;
1795
+ const VALIDATE_FEEL_PROPERTIES = ['min', 'max', 'minLength', 'maxLength'];
1002
1796
  class Validator {
1797
+ constructor(expressionLanguage, conditionChecker, form) {
1798
+ this._expressionLanguage = expressionLanguage;
1799
+ this._conditionChecker = conditionChecker;
1800
+ this._form = form;
1801
+ }
1003
1802
  validateField(field, value) {
1004
1803
  const {
1005
1804
  type,
@@ -1032,10 +1831,11 @@ class Validator {
1032
1831
  if (!validate) {
1033
1832
  return errors;
1034
1833
  }
1035
- if (validate.pattern && value && !new RegExp(validate.pattern).test(value)) {
1036
- errors = [...errors, `Field must match pattern ${validate.pattern}.`];
1834
+ const evaluatedValidation = evaluateFEELValues(validate, this._expressionLanguage, this._conditionChecker, this._form);
1835
+ if (evaluatedValidation.pattern && value && !new RegExp(evaluatedValidation.pattern).test(value)) {
1836
+ errors = [...errors, `Field must match pattern ${evaluatedValidation.pattern}.`];
1037
1837
  }
1038
- if (validate.required) {
1838
+ if (evaluatedValidation.required) {
1039
1839
  const isUncheckedCheckbox = type === 'checkbox' && value === false;
1040
1840
  const isUnsetValue = minDash.isNil(value) || value === '';
1041
1841
  const isEmptyMultiselect = Array.isArray(value) && value.length === 0;
@@ -1043,28 +1843,64 @@ class Validator {
1043
1843
  errors = [...errors, 'Field is required.'];
1044
1844
  }
1045
1845
  }
1046
- if ('min' in validate && (value || value === 0) && value < validate.min) {
1047
- errors = [...errors, `Field must have minimum value of ${validate.min}.`];
1846
+ if ('min' in evaluatedValidation && (value || value === 0) && value < evaluatedValidation.min) {
1847
+ errors = [...errors, `Field must have minimum value of ${evaluatedValidation.min}.`];
1048
1848
  }
1049
- if ('max' in validate && (value || value === 0) && value > validate.max) {
1050
- errors = [...errors, `Field must have maximum value of ${validate.max}.`];
1849
+ if ('max' in evaluatedValidation && (value || value === 0) && value > evaluatedValidation.max) {
1850
+ errors = [...errors, `Field must have maximum value of ${evaluatedValidation.max}.`];
1051
1851
  }
1052
- if ('minLength' in validate && value && value.trim().length < validate.minLength) {
1053
- errors = [...errors, `Field must have minimum length of ${validate.minLength}.`];
1852
+ if ('minLength' in evaluatedValidation && value && value.trim().length < evaluatedValidation.minLength) {
1853
+ errors = [...errors, `Field must have minimum length of ${evaluatedValidation.minLength}.`];
1054
1854
  }
1055
- if ('maxLength' in validate && value && value.trim().length > validate.maxLength) {
1056
- errors = [...errors, `Field must have maximum length of ${validate.maxLength}.`];
1855
+ if ('maxLength' in evaluatedValidation && value && value.trim().length > evaluatedValidation.maxLength) {
1856
+ errors = [...errors, `Field must have maximum length of ${evaluatedValidation.maxLength}.`];
1057
1857
  }
1058
- if ('validationType' in validate && value && validate.validationType === 'phone' && !PHONE_PATTERN.test(value)) {
1858
+ if ('validationType' in evaluatedValidation && value && evaluatedValidation.validationType === 'phone' && !PHONE_PATTERN.test(value)) {
1059
1859
  errors = [...errors, 'Field must be a valid international phone number. (e.g. +4930664040900)'];
1060
1860
  }
1061
- if ('validationType' in validate && value && validate.validationType === 'email' && !EMAIL_PATTERN.test(value)) {
1861
+ if ('validationType' in evaluatedValidation && value && evaluatedValidation.validationType === 'email' && !EMAIL_PATTERN.test(value)) {
1062
1862
  errors = [...errors, 'Field must be a valid email.'];
1063
1863
  }
1064
1864
  return errors;
1065
1865
  }
1066
1866
  }
1067
- Validator.$inject = [];
1867
+ Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
1868
+
1869
+ // helpers //////////
1870
+
1871
+ /**
1872
+ * Helper function to evaluate optional FEEL validation values.
1873
+ */
1874
+ function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
1875
+ const evaluatedValidate = {
1876
+ ...validate
1877
+ };
1878
+ VALIDATE_FEEL_PROPERTIES.forEach(property => {
1879
+ const path = property.split('.');
1880
+ const value = minDash.get(evaluatedValidate, path);
1881
+
1882
+ // mirroring FEEL evaluation of our hooks
1883
+ if (!expressionLanguage || !expressionLanguage.isExpression(value)) {
1884
+ return value;
1885
+ }
1886
+ const {
1887
+ initialData,
1888
+ data
1889
+ } = form._getState();
1890
+ const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
1891
+ const filteredData = {
1892
+ ...initialData,
1893
+ ...newData
1894
+ };
1895
+ const evaluatedValue = expressionLanguage.evaluate(value, filteredData);
1896
+
1897
+ // replace validate property with evaluated value
1898
+ if (evaluatedValue) {
1899
+ minDash.set(evaluatedValidate, path, evaluatedValue);
1900
+ }
1901
+ });
1902
+ return evaluatedValidate;
1903
+ }
1068
1904
 
1069
1905
  class FormFieldRegistry {
1070
1906
  constructor(eventBus) {
@@ -1115,23 +1951,23 @@ class FormFieldRegistry {
1115
1951
  }
1116
1952
  FormFieldRegistry.$inject = ['eventBus'];
1117
1953
 
1118
- /**
1119
- * @typedef { { id: String, components: Array<String> } } FormRow
1120
- * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1954
+ /**
1955
+ * @typedef { { id: String, components: Array<String> } } FormRow
1956
+ * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1121
1957
  */
1122
1958
 
1123
- /**
1124
- * Maintains the Form layout in a given structure, for example
1125
- *
1126
- * [
1127
- * {
1128
- * formFieldId: 'FormField_1',
1129
- * rows: [
1130
- * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1131
- * ]
1132
- * }
1133
- * ]
1134
- *
1959
+ /**
1960
+ * Maintains the Form layout in a given structure, for example
1961
+ *
1962
+ * [
1963
+ * {
1964
+ * formFieldId: 'FormField_1',
1965
+ * rows: [
1966
+ * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1967
+ * ]
1968
+ * }
1969
+ * ]
1970
+ *
1135
1971
  */
1136
1972
  class FormLayouter {
1137
1973
  constructor(eventBus) {
@@ -1141,8 +1977,8 @@ class FormLayouter {
1141
1977
  this._eventBus = eventBus;
1142
1978
  }
1143
1979
 
1144
- /**
1145
- * @param {FormRow} row
1980
+ /**
1981
+ * @param {FormRow} row
1146
1982
  */
1147
1983
  addRow(formFieldId, row) {
1148
1984
  let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
@@ -1156,18 +1992,18 @@ class FormLayouter {
1156
1992
  rowsPerComponent.rows.push(row);
1157
1993
  }
1158
1994
 
1159
- /**
1160
- * @param {String} id
1161
- * @returns {FormRow}
1995
+ /**
1996
+ * @param {String} id
1997
+ * @returns {FormRow}
1162
1998
  */
1163
1999
  getRow(id) {
1164
2000
  const rows = allRows(this._rows);
1165
2001
  return rows.find(r => r.id === id);
1166
2002
  }
1167
2003
 
1168
- /**
1169
- * @param {any} formField
1170
- * @returns {FormRow}
2004
+ /**
2005
+ * @param {any} formField
2006
+ * @returns {FormRow}
1171
2007
  */
1172
2008
  getRowForField(formField) {
1173
2009
  return allRows(this._rows).find(r => {
@@ -1178,9 +2014,9 @@ class FormLayouter {
1178
2014
  });
1179
2015
  }
1180
2016
 
1181
- /**
1182
- * @param {String} formFieldId
1183
- * @returns { Array<FormRow> }
2017
+ /**
2018
+ * @param {String} formFieldId
2019
+ * @returns { Array<FormRow> }
1184
2020
  */
1185
2021
  getRows(formFieldId) {
1186
2022
  const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
@@ -1190,15 +2026,15 @@ class FormLayouter {
1190
2026
  return rowsForField.rows;
1191
2027
  }
1192
2028
 
1193
- /**
1194
- * @returns {string}
2029
+ /**
2030
+ * @returns {string}
1195
2031
  */
1196
2032
  nextRowId() {
1197
2033
  return this._ids.nextPrefixed('Row_');
1198
2034
  }
1199
2035
 
1200
- /**
1201
- * @param {any} formField
2036
+ /**
2037
+ * @param {any} formField
1202
2038
  */
1203
2039
  calculateLayout(formField) {
1204
2040
  const {
@@ -1234,224 +2070,38 @@ class FormLayouter {
1234
2070
  // fire event to notify interested parties
1235
2071
  this._eventBus.fire('form.layoutCleared');
1236
2072
  }
1237
- }
1238
- FormLayouter.$inject = ['eventBus'];
1239
-
1240
- // helpers //////
1241
-
1242
- function groupByRow(components, ids) {
1243
- return minDash.groupBy(components, c => {
1244
- // mitigate missing row by creating new (handle legacy)
1245
- const {
1246
- layout
1247
- } = c;
1248
- if (!layout || !layout.row) {
1249
- return ids.nextPrefixed('Row_');
1250
- }
1251
- return layout.row;
1252
- });
1253
- }
1254
-
1255
- /**
1256
- * @param {Array<FormRows>} formRows
1257
- * @returns {Array<FormRow>}
1258
- */
1259
- function allRows(formRows) {
1260
- return minDash.flatten(formRows.map(c => c.rows));
1261
- }
1262
-
1263
- // config ///////////////////
1264
-
1265
- const MINUTES_IN_DAY = 60 * 24;
1266
- const DATETIME_SUBTYPES = {
1267
- DATE: 'date',
1268
- TIME: 'time',
1269
- DATETIME: 'datetime'
1270
- };
1271
- const TIME_SERIALISING_FORMATS = {
1272
- UTC_OFFSET: 'utc_offset',
1273
- UTC_NORMALIZED: 'utc_normalized',
1274
- NO_TIMEZONE: 'no_timezone'
1275
- };
1276
- const DATETIME_SUBTYPES_LABELS = {
1277
- [DATETIME_SUBTYPES.DATE]: 'Date',
1278
- [DATETIME_SUBTYPES.TIME]: 'Time',
1279
- [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
1280
- };
1281
- const TIME_SERIALISINGFORMAT_LABELS = {
1282
- [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
1283
- [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
1284
- [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
1285
- };
1286
- const DATETIME_SUBTYPE_PATH = ['subtype'];
1287
- const DATE_LABEL_PATH = ['dateLabel'];
1288
- const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
1289
- const TIME_LABEL_PATH = ['timeLabel'];
1290
- const TIME_USE24H_PATH = ['use24h'];
1291
- const TIME_INTERVAL_PATH = ['timeInterval'];
1292
- const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
1293
-
1294
- // config ///////////////////
1295
-
1296
- const VALUES_SOURCES = {
1297
- STATIC: 'static',
1298
- INPUT: 'input'
1299
- };
1300
- const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
1301
- const VALUES_SOURCES_LABELS = {
1302
- [VALUES_SOURCES.STATIC]: 'Static',
1303
- [VALUES_SOURCES.INPUT]: 'Input data'
1304
- };
1305
- const VALUES_SOURCES_PATHS = {
1306
- [VALUES_SOURCES.STATIC]: ['values'],
1307
- [VALUES_SOURCES.INPUT]: ['valuesKey']
1308
- };
1309
- const VALUES_SOURCES_DEFAULTS = {
1310
- [VALUES_SOURCES.STATIC]: [{
1311
- label: 'Value',
1312
- value: 'value'
1313
- }],
1314
- [VALUES_SOURCES.INPUT]: ''
1315
- };
1316
-
1317
- // helpers ///////////////////
1318
-
1319
- function getValuesSource(field) {
1320
- for (const source of Object.values(VALUES_SOURCES)) {
1321
- if (minDash.get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
1322
- return source;
1323
- }
1324
- }
1325
- return VALUES_SOURCE_DEFAULT;
1326
- }
1327
-
1328
- function createInjector(bootstrapModules) {
1329
- const injector = new didi.Injector(bootstrapModules);
1330
- injector.init();
1331
- return injector;
1332
- }
1333
-
1334
- /**
1335
- * @param {string?} prefix
1336
- *
1337
- * @returns Element
1338
- */
1339
- function createFormContainer(prefix = 'fjs') {
1340
- const container = document.createElement('div');
1341
- container.classList.add(`${prefix}-container`);
1342
- return container;
1343
- }
1344
-
1345
- const EXPRESSION_PROPERTIES = ['alt', 'description', 'label', 'source', 'readonly', 'text'];
1346
- const TEMPLATE_PROPERTIES = ['description', 'label', 'text'];
1347
- function findErrors(errors, path) {
1348
- return errors[pathStringify(path)];
1349
- }
1350
- function isRequired(field) {
1351
- return field.required;
1352
- }
1353
- function pathParse(path) {
1354
- if (!path) {
1355
- return [];
1356
- }
1357
- return path.split('.').map(key => {
1358
- return isNaN(parseInt(key)) ? key : parseInt(key);
1359
- });
1360
- }
1361
- function pathsEqual(a, b) {
1362
- return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
1363
- }
1364
- function pathStringify(path) {
1365
- if (!path) {
1366
- return '';
1367
- }
1368
- return path.join('.');
1369
- }
1370
- const indices = {};
1371
- function generateIndexForType(type) {
1372
- if (type in indices) {
1373
- indices[type]++;
1374
- } else {
1375
- indices[type] = 1;
1376
- }
1377
- return indices[type];
1378
- }
1379
- function generateIdForType(type) {
1380
- return `${type}${generateIndexForType(type)}`;
1381
- }
1382
-
1383
- /**
1384
- * @template T
1385
- * @param {T} data
1386
- * @param {(this: any, key: string, value: any) => any} [replacer]
1387
- * @return {T}
1388
- */
1389
- function clone(data, replacer) {
1390
- return JSON.parse(JSON.stringify(data, replacer));
1391
- }
1392
-
1393
- /**
1394
- * Parse the schema for input variables a form might make use of
1395
- *
1396
- * @param {any} schema
1397
- *
1398
- * @return {string[]}
1399
- */
1400
- function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null), templating = new FeelersTemplating()) {
1401
- if (!schema.components) {
1402
- return [];
1403
- }
1404
- const variables = schema.components.reduce((variables, component) => {
1405
- const {
1406
- key,
1407
- valuesKey,
1408
- type,
1409
- conditional
1410
- } = component;
1411
- if (['button'].includes(type)) {
1412
- return variables;
1413
- }
1414
- if (key) {
1415
- variables = [...variables, key];
1416
- }
1417
- if (valuesKey) {
1418
- variables = [...variables, valuesKey];
1419
- }
1420
- if (conditional && conditional.hide) {
1421
- const conditionVariables = expressionLanguage.getVariableNames(conditional.hide, {
1422
- type: 'unaryTest'
1423
- });
1424
- variables = [...variables, ...conditionVariables];
2073
+ }
2074
+ FormLayouter.$inject = ['eventBus'];
2075
+
2076
+ // helpers //////
2077
+
2078
+ function groupByRow(components, ids) {
2079
+ return minDash.groupBy(components, c => {
2080
+ // mitigate missing row by creating new (handle legacy)
2081
+ const {
2082
+ layout
2083
+ } = c;
2084
+ if (!layout || !layout.row) {
2085
+ return ids.nextPrefixed('Row_');
1425
2086
  }
1426
- EXPRESSION_PROPERTIES.forEach(prop => {
1427
- const property = component[prop];
1428
- if (property && expressionLanguage.isExpression(property)) {
1429
- const expressionVariables = expressionLanguage.getVariableNames(property, {
1430
- type: 'expression'
1431
- });
1432
- variables = [...variables, ...expressionVariables];
1433
- }
1434
- });
1435
- TEMPLATE_PROPERTIES.forEach(prop => {
1436
- const property = component[prop];
1437
- if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
1438
- const templateVariables = templating.getVariableNames(property);
1439
- variables = [...variables, ...templateVariables];
1440
- }
1441
- });
1442
- return variables;
1443
- }, []);
2087
+ return layout.row;
2088
+ });
2089
+ }
1444
2090
 
1445
- // remove duplicates
1446
- return Array.from(new Set(variables));
2091
+ /**
2092
+ * @param {Array<FormRows>} formRows
2093
+ * @returns {Array<FormRow>}
2094
+ */
2095
+ function allRows(formRows) {
2096
+ return minDash.flatten(formRows.map(c => c.rows));
1447
2097
  }
1448
2098
 
1449
2099
  class Importer {
1450
- /**
1451
- * @constructor
1452
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
1453
- * @param { import('../render/FormFields').default } formFields
1454
- * @param { import('../core').FormLayouter } formLayouter
2100
+ /**
2101
+ * @constructor
2102
+ * @param { import('../core').FormFieldRegistry } formFieldRegistry
2103
+ * @param { import('../render/FormFields').default } formFields
2104
+ * @param { import('../core').FormLayouter } formLayouter
1455
2105
  */
1456
2106
  constructor(formFieldRegistry, formFields, formLayouter) {
1457
2107
  this._formFieldRegistry = formFieldRegistry;
@@ -1459,15 +2109,15 @@ class Importer {
1459
2109
  this._formLayouter = formLayouter;
1460
2110
  }
1461
2111
 
1462
- /**
1463
- * Import schema adding `id`, `_parent` and `_path`
1464
- * information to each field and adding it to the
1465
- * form field registry.
1466
- *
1467
- * @param {any} schema
1468
- * @param {any} [data]
1469
- *
1470
- * @return { { warnings: Array<any>, schema: any, data: any } }
2112
+ /**
2113
+ * Import schema adding `id`, `_parent` and `_path`
2114
+ * information to each field and adding it to the
2115
+ * form field registry.
2116
+ *
2117
+ * @param {any} schema
2118
+ * @param {any} [data]
2119
+ *
2120
+ * @return { { warnings: Array<any>, schema: any, data: any } }
1471
2121
  */
1472
2122
  importSchema(schema, data = {}) {
1473
2123
  // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
@@ -1488,11 +2138,11 @@ class Importer {
1488
2138
  }
1489
2139
  }
1490
2140
 
1491
- /**
1492
- * @param {any} formField
1493
- * @param {string} [parentId]
1494
- *
1495
- * @return {any} importedField
2141
+ /**
2142
+ * @param {any} formField
2143
+ * @param {string} [parentId]
2144
+ *
2145
+ * @return {any} importedField
1496
2146
  */
1497
2147
  importFormField(formField, parentId) {
1498
2148
  const {
@@ -1543,10 +2193,10 @@ class Importer {
1543
2193
  });
1544
2194
  }
1545
2195
 
1546
- /**
1547
- * @param {Object} data
1548
- *
1549
- * @return {Object} initializedData
2196
+ /**
2197
+ * @param {Object} data
2198
+ *
2199
+ * @return {Object} initializedData
1550
2200
  */
1551
2201
  initializeFieldValues(data) {
1552
2202
  return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
@@ -1620,7 +2270,7 @@ function prefixId(id, formId) {
1620
2270
  return `fjs-form-${id}`;
1621
2271
  }
1622
2272
 
1623
- const type$b = 'button';
2273
+ const type$c = 'button';
1624
2274
  function Button(props) {
1625
2275
  const {
1626
2276
  disabled,
@@ -1630,7 +2280,7 @@ function Button(props) {
1630
2280
  action = 'submit'
1631
2281
  } = field;
1632
2282
  return jsxRuntime.jsx("div", {
1633
- class: formFieldClasses(type$b),
2283
+ class: formFieldClasses(type$c),
1634
2284
  children: jsxRuntime.jsx("button", {
1635
2285
  class: "fjs-button",
1636
2286
  type: action,
@@ -1640,7 +2290,7 @@ function Button(props) {
1640
2290
  });
1641
2291
  }
1642
2292
  Button.config = {
1643
- type: type$b,
2293
+ type: type$c,
1644
2294
  keyed: true,
1645
2295
  label: 'Button',
1646
2296
  group: 'action',
@@ -1684,11 +2334,11 @@ const FormRenderContext = preact.createContext({
1684
2334
  });
1685
2335
  var FormRenderContext$1 = FormRenderContext;
1686
2336
 
1687
- /**
1688
- * @param {string} type
1689
- * @param {boolean} [strict]
1690
- *
1691
- * @returns {any}
2337
+ /**
2338
+ * @param {string} type
2339
+ * @param {boolean} [strict]
2340
+ *
2341
+ * @returns {any}
1692
2342
  */
1693
2343
  function getService(type, strict) {}
1694
2344
  const FormContext = preact.createContext({
@@ -1704,10 +2354,10 @@ function useService(type, strict) {
1704
2354
  return getService(type, strict);
1705
2355
  }
1706
2356
 
1707
- /**
1708
- * Returns the conditionally filtered data of a form reactively.
1709
- * Memoised to minimize re-renders
1710
- *
2357
+ /**
2358
+ * Returns the conditionally filtered data of a form reactively.
2359
+ * Memoised to minimize re-renders
2360
+ *
1711
2361
  */
1712
2362
  function useFilteredFormData() {
1713
2363
  const {
@@ -1724,12 +2374,12 @@ function useFilteredFormData() {
1724
2374
  }, [conditionChecker, data, initialData]);
1725
2375
  }
1726
2376
 
1727
- /**
1728
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
1729
- *
1730
- * @param {string | undefined} condition
1731
- *
1732
- * @returns {boolean} true if condition is met or no condition or condition checker exists
2377
+ /**
2378
+ * Evaluate if condition is met reactively based on the conditionChecker and form data.
2379
+ *
2380
+ * @param {string | undefined} condition
2381
+ *
2382
+ * @returns {boolean} true if condition is met or no condition or condition checker exists
1733
2383
  */
1734
2384
  function useCondition(condition) {
1735
2385
  const conditionChecker = useService('conditionChecker', false);
@@ -1739,13 +2389,13 @@ function useCondition(condition) {
1739
2389
  }, [conditionChecker, condition, filteredData]);
1740
2390
  }
1741
2391
 
1742
- /**
1743
- * Evaluate a string reactively based on the expressionLanguage and form data.
1744
- * If the string is not an expression, it is returned as is.
1745
- * Memoised to minimize re-renders.
1746
- *
1747
- * @param {string} value
1748
- *
2392
+ /**
2393
+ * Evaluate a string reactively based on the expressionLanguage and form data.
2394
+ * If the string is not an expression, it is returned as is.
2395
+ * Memoised to minimize re-renders.
2396
+ *
2397
+ * @param {string} value
2398
+ *
1749
2399
  */
1750
2400
  function useExpressionEvaluation(value) {
1751
2401
  const formData = useFilteredFormData();
@@ -1774,16 +2424,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
1774
2424
  });
1775
2425
  }
1776
2426
 
1777
- /**
1778
- * Retrieve readonly value of a form field, given it can be an
1779
- * expression optionally or configured globally.
1780
- *
1781
- * @typedef { import('../../types').FormProperties } FormProperties
1782
- *
1783
- * @param {any} formField
1784
- * @param {FormProperties} properties
1785
- *
1786
- * @returns {boolean}
2427
+ /**
2428
+ * Retrieve readonly value of a form field, given it can be an
2429
+ * expression optionally or configured globally.
2430
+ *
2431
+ * @typedef { import('../../types').FormProperties } FormProperties
2432
+ *
2433
+ * @param {any} formField
2434
+ * @param {FormProperties} properties
2435
+ *
2436
+ * @returns {boolean}
1787
2437
  */
1788
2438
  function useReadonly(formField, properties = {}) {
1789
2439
  const expressionLanguage = useService('expressionLanguage');
@@ -1801,16 +2451,16 @@ function useReadonly(formField, properties = {}) {
1801
2451
  return readonly || false;
1802
2452
  }
1803
2453
 
1804
- /**
1805
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1806
- * Memoised to minimize re-renders
1807
- *
1808
- * @param {string} value
1809
- * @param {Object} options
1810
- * @param {boolean} [options.debug = false]
1811
- * @param {boolean} [options.strict = false]
1812
- * @param {Function} [options.buildDebugString]
1813
- *
2454
+ /**
2455
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2456
+ * Memoised to minimize re-renders
2457
+ *
2458
+ * @param {string} value
2459
+ * @param {Object} options
2460
+ * @param {boolean} [options.debug = false]
2461
+ * @param {boolean} [options.strict = false]
2462
+ * @param {Function} [options.buildDebugString]
2463
+ *
1814
2464
  */
1815
2465
  function useTemplateEvaluation(value, options) {
1816
2466
  const filteredData = useFilteredFormData();
@@ -1823,17 +2473,17 @@ function useTemplateEvaluation(value, options) {
1823
2473
  }, [filteredData, templating, value, options]);
1824
2474
  }
1825
2475
 
1826
- /**
1827
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1828
- * If the string contains multiple lines, only the first line is returned.
1829
- * Memoised to minimize re-renders
1830
- *
1831
- * @param {string} value
1832
- * @param {Object} [options]
1833
- * @param {boolean} [options.debug = false]
1834
- * @param {boolean} [options.strict = false]
1835
- * @param {Function} [options.buildDebugString]
1836
- *
2476
+ /**
2477
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2478
+ * If the string contains multiple lines, only the first line is returned.
2479
+ * Memoised to minimize re-renders
2480
+ *
2481
+ * @param {string} value
2482
+ * @param {Object} [options]
2483
+ * @param {boolean} [options.debug = false]
2484
+ * @param {boolean} [options.strict = false]
2485
+ * @param {Function} [options.buildDebugString]
2486
+ *
1837
2487
  */
1838
2488
  function useSingleLineTemplateEvaluation(value, options = {}) {
1839
2489
  const evaluatedTemplate = useTemplateEvaluation(value, options);
@@ -1900,11 +2550,12 @@ function Label(props) {
1900
2550
  });
1901
2551
  }
1902
2552
 
1903
- const type$a = 'checkbox';
2553
+ const type$b = 'checkbox';
1904
2554
  function Checkbox(props) {
1905
2555
  const {
1906
2556
  disabled,
1907
2557
  errors = [],
2558
+ onBlur,
1908
2559
  field,
1909
2560
  readonly,
1910
2561
  value = false
@@ -1931,7 +2582,7 @@ function Checkbox(props) {
1931
2582
  } = hooks.useContext(FormContext$1);
1932
2583
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
1933
2584
  return jsxRuntime.jsxs("div", {
1934
- class: classNames(formFieldClasses(type$a, {
2585
+ class: classNames(formFieldClasses(type$b, {
1935
2586
  errors,
1936
2587
  disabled,
1937
2588
  readonly
@@ -1950,6 +2601,7 @@ function Checkbox(props) {
1950
2601
  id: prefixId(id, formId),
1951
2602
  type: "checkbox",
1952
2603
  onChange: onChange,
2604
+ onBlur: onBlur,
1953
2605
  "aria-describedby": errorMessageId
1954
2606
  })
1955
2607
  }), jsxRuntime.jsx(Description, {
@@ -1961,7 +2613,7 @@ function Checkbox(props) {
1961
2613
  });
1962
2614
  }
1963
2615
  Checkbox.config = {
1964
- type: type$a,
2616
+ type: type$b,
1965
2617
  keyed: true,
1966
2618
  label: 'Checkbox',
1967
2619
  group: 'selection',
@@ -2020,8 +2672,8 @@ function _isValueSomething(value) {
2020
2672
  return value || value === 0 || value === false;
2021
2673
  }
2022
2674
 
2023
- /**
2024
- * @enum { String }
2675
+ /**
2676
+ * @enum { String }
2025
2677
  */
2026
2678
  const LOAD_STATES = {
2027
2679
  LOADING: 'loading',
@@ -2029,20 +2681,21 @@ const LOAD_STATES = {
2029
2681
  ERROR: 'error'
2030
2682
  };
2031
2683
 
2032
- /**
2033
- * @typedef {Object} ValuesGetter
2034
- * @property {Object[]} values - The values data
2035
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2684
+ /**
2685
+ * @typedef {Object} ValuesGetter
2686
+ * @property {Object[]} values - The values data
2687
+ * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2036
2688
  */
2037
2689
 
2038
- /**
2039
- * A hook to load values for single and multiselect components.
2040
- *
2041
- * @param {Object} field - The form field to handle values for
2042
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2690
+ /**
2691
+ * A hook to load values for single and multiselect components.
2692
+ *
2693
+ * @param {Object} field - The form field to handle values for
2694
+ * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2043
2695
  */
2044
2696
  function useValuesAsync (field) {
2045
2697
  const {
2698
+ valuesExpression,
2046
2699
  valuesKey,
2047
2700
  values: staticValues
2048
2701
  } = field;
@@ -2052,6 +2705,11 @@ function useValuesAsync (field) {
2052
2705
  state: LOAD_STATES.LOADING
2053
2706
  });
2054
2707
  const initialData = useService('form')._getState().initialData;
2708
+ const evaluatedValues = hooks.useMemo(() => {
2709
+ if (valuesExpression) {
2710
+ return useExpressionEvaluation(valuesExpression);
2711
+ }
2712
+ }, [valuesExpression]);
2055
2713
  hooks.useEffect(() => {
2056
2714
  let values = [];
2057
2715
 
@@ -2061,11 +2719,14 @@ function useValuesAsync (field) {
2061
2719
  if (keyedValues && Array.isArray(keyedValues)) {
2062
2720
  values = keyedValues;
2063
2721
  }
2064
- }
2065
2722
 
2066
- // static values
2067
- else if (staticValues !== undefined) {
2723
+ // static values
2724
+ } else if (staticValues !== undefined) {
2068
2725
  values = Array.isArray(staticValues) ? staticValues : [];
2726
+
2727
+ // expression
2728
+ } else if (evaluatedValues && Array.isArray(evaluatedValues)) {
2729
+ values = evaluatedValues;
2069
2730
  } else {
2070
2731
  setValuesGetter(buildErrorState('No values source defined in the form definition'));
2071
2732
  return;
@@ -2295,11 +2956,12 @@ function sanitizeMultiSelectValue(options) {
2295
2956
  }
2296
2957
  }
2297
2958
 
2298
- const type$9 = 'checklist';
2959
+ const type$a = 'checklist';
2299
2960
  function Checklist(props) {
2300
2961
  const {
2301
2962
  disabled,
2302
2963
  errors = [],
2964
+ onBlur,
2303
2965
  field,
2304
2966
  readonly,
2305
2967
  value = []
@@ -2310,6 +2972,7 @@ function Checklist(props) {
2310
2972
  label,
2311
2973
  validate = {}
2312
2974
  } = field;
2975
+ const outerDivRef = hooks.useRef();
2313
2976
  const {
2314
2977
  required
2315
2978
  } = validate;
@@ -2325,6 +2988,12 @@ function Checklist(props) {
2325
2988
  value: newValue
2326
2989
  });
2327
2990
  };
2991
+ const onCheckboxBlur = e => {
2992
+ if (outerDivRef.current.contains(e.relatedTarget)) {
2993
+ return;
2994
+ }
2995
+ onBlur();
2996
+ };
2328
2997
  const {
2329
2998
  state: loadState,
2330
2999
  values: options
@@ -2334,11 +3003,12 @@ function Checklist(props) {
2334
3003
  } = hooks.useContext(FormContext$1);
2335
3004
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
2336
3005
  return jsxRuntime.jsxs("div", {
2337
- class: classNames(formFieldClasses(type$9, {
3006
+ class: classNames(formFieldClasses(type$a, {
2338
3007
  errors,
2339
3008
  disabled,
2340
3009
  readonly
2341
3010
  })),
3011
+ ref: outerDivRef,
2342
3012
  children: [jsxRuntime.jsx(Label, {
2343
3013
  label: label,
2344
3014
  required: required
@@ -2358,6 +3028,7 @@ function Checklist(props) {
2358
3028
  id: prefixId(`${id}-${index}`, formId),
2359
3029
  type: "checkbox",
2360
3030
  onClick: () => toggleCheckbox(v.value),
3031
+ onBlur: onCheckboxBlur,
2361
3032
  "aria-describedby": errorMessageId
2362
3033
  })
2363
3034
  }, `${id}-${index}`);
@@ -2370,7 +3041,7 @@ function Checklist(props) {
2370
3041
  });
2371
3042
  }
2372
3043
  Checklist.config = {
2373
- type: type$9,
3044
+ type: type$a,
2374
3045
  keyed: true,
2375
3046
  label: 'Checklist',
2376
3047
  group: 'selection',
@@ -2400,8 +3071,10 @@ function FormField(props) {
2400
3071
  onChange
2401
3072
  } = props;
2402
3073
  const formFields = useService('formFields'),
3074
+ viewerCommands = useService('viewerCommands', false),
2403
3075
  form = useService('form');
2404
3076
  const {
3077
+ initialData,
2405
3078
  data,
2406
3079
  errors,
2407
3080
  properties
@@ -2415,12 +3088,23 @@ function FormField(props) {
2415
3088
  if (!FormFieldComponent) {
2416
3089
  throw new Error(`cannot render field <${field.type}>`);
2417
3090
  }
3091
+ const initialValue = hooks.useMemo(() => minDash.get(initialData, field._path), [initialData, field._path]);
2418
3092
  const value = minDash.get(data, field._path);
2419
3093
  const fieldErrors = findErrors(errors, field._path);
2420
3094
  const readonly = useReadonly(field, properties);
2421
3095
 
2422
3096
  // add precedence: global readonly > form field disabled
2423
3097
  const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
3098
+ const onBlur = hooks.useCallback(() => {
3099
+ if (viewerCommands) {
3100
+ viewerCommands.updateFieldValidation(field, value);
3101
+ }
3102
+ }, [viewerCommands, field, value]);
3103
+ hooks.useEffect(() => {
3104
+ if (viewerCommands && initialValue) {
3105
+ viewerCommands.updateFieldValidation(field, initialValue);
3106
+ }
3107
+ }, [viewerCommands, field, initialValue]);
2424
3108
  const hidden = useCondition(field.conditional && field.conditional.hide || null);
2425
3109
  if (hidden) {
2426
3110
  return jsxRuntime.jsx(Empty, {});
@@ -2436,6 +3120,7 @@ function FormField(props) {
2436
3120
  disabled: disabled,
2437
3121
  errors: fieldErrors,
2438
3122
  onChange: disabled || readonly ? noop$1 : onChange,
3123
+ onBlur: disabled || readonly ? noop$1 : onBlur,
2439
3124
  readonly: readonly,
2440
3125
  value: value
2441
3126
  })
@@ -2498,22 +3183,23 @@ Default.config = {
2498
3183
  })
2499
3184
  };
2500
3185
 
2501
- function _extends$j() { _extends$j = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$j.apply(this, arguments); }
2502
- var CalendarIcon = (({
2503
- styles = {},
2504
- ...props
2505
- }) => /*#__PURE__*/React.createElement("svg", _extends$j({
2506
- width: "14",
2507
- height: "15",
2508
- viewBox: "0 0 28 30",
2509
- fill: "none",
2510
- xmlns: "http://www.w3.org/2000/svg"
2511
- }, props), /*#__PURE__*/React.createElement("path", {
2512
- fillRule: "evenodd",
2513
- clipRule: "evenodd",
2514
- fill: "currentColor",
2515
- d: "M19 2H9V0H7v2H2a2 2 0 00-2 2v24a2 2 0 002 2h24a2 2 0 002-2V4a2 2 0 00-2-2h-5V0h-2v2zM7 7V4H2v5h24V4h-5v3h-2V4H9v3H7zm-5 4v17h24V11H2z"
2516
- })));
3186
+ var _path$h;
3187
+ function _extends$k() { _extends$k = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$k.apply(this, arguments); }
3188
+ var SvgCalendar = function SvgCalendar(props) {
3189
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$k({
3190
+ xmlns: "http://www.w3.org/2000/svg",
3191
+ width: 14,
3192
+ height: 15,
3193
+ fill: "none",
3194
+ viewBox: "0 0 28 30"
3195
+ }, props), _path$h || (_path$h = /*#__PURE__*/React__namespace.createElement("path", {
3196
+ fill: "currentColor",
3197
+ fillRule: "evenodd",
3198
+ d: "M19 2H9V0H7v2H2a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-5V0h-2v2ZM7 7V4H2v5h24V4h-5v3h-2V4H9v3H7Zm-5 4v17h24V11H2Z",
3199
+ clipRule: "evenodd"
3200
+ })));
3201
+ };
3202
+ var CalendarIcon = SvgCalendar;
2517
3203
 
2518
3204
  function InputAdorner(props) {
2519
3205
  const {
@@ -2558,6 +3244,7 @@ function Datepicker(props) {
2558
3244
  id,
2559
3245
  label,
2560
3246
  collapseLabelOnEmpty,
3247
+ onDateTimeBlur,
2561
3248
  formId,
2562
3249
  required,
2563
3250
  disabled,
@@ -2652,7 +3339,8 @@ function Datepicker(props) {
2652
3339
  if (!isInputDirty || e.relatedTarget && e.relatedTarget.classList.contains('flatpickr-day')) return;
2653
3340
  dateInputRef.current.dispatchEvent(ENTER_KEYDOWN_EVENT);
2654
3341
  setIsInputDirty(false);
2655
- }, [isInputDirty]);
3342
+ onDateTimeBlur(e);
3343
+ }, [isInputDirty, onDateTimeBlur]);
2656
3344
  const fullId = `${prefixId(id, formId)}--date`;
2657
3345
  return jsxRuntime.jsxs("div", {
2658
3346
  class: "fjs-datetime-subsection",
@@ -2694,25 +3382,26 @@ function Datepicker(props) {
2694
3382
  });
2695
3383
  }
2696
3384
 
2697
- function _extends$i() { _extends$i = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$i.apply(this, arguments); }
2698
- var ClockIcon = (({
2699
- styles = {},
2700
- ...props
2701
- }) => /*#__PURE__*/React.createElement("svg", _extends$i({
2702
- width: "16",
2703
- height: "16",
2704
- viewBox: "0 0 28 29",
2705
- fill: "none",
2706
- xmlns: "http://www.w3.org/2000/svg"
2707
- }, props), /*#__PURE__*/React.createElement("path", {
2708
- fill: "currentColor",
2709
- d: "M13 14.41L18.59 20 20 18.59l-5-5.01V5h-2v9.41z"
2710
- }), /*#__PURE__*/React.createElement("path", {
2711
- fillRule: "evenodd",
2712
- clipRule: "evenodd",
2713
- fill: "currentColor",
2714
- d: "M6.222 25.64A14 14 0 1021.778 2.36 14 14 0 006.222 25.64zM7.333 4.023a12 12 0 1113.334 19.955A12 12 0 017.333 4.022z"
2715
- })));
3385
+ var _path$g, _path2$4;
3386
+ function _extends$j() { _extends$j = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$j.apply(this, arguments); }
3387
+ var SvgClock = function SvgClock(props) {
3388
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$j({
3389
+ xmlns: "http://www.w3.org/2000/svg",
3390
+ width: 16,
3391
+ height: 16,
3392
+ fill: "none",
3393
+ viewBox: "0 0 28 29"
3394
+ }, props), _path$g || (_path$g = /*#__PURE__*/React__namespace.createElement("path", {
3395
+ fill: "currentColor",
3396
+ d: "M13 14.41 18.59 20 20 18.59l-5-5.01V5h-2v9.41Z"
3397
+ })), _path2$4 || (_path2$4 = /*#__PURE__*/React__namespace.createElement("path", {
3398
+ fill: "currentColor",
3399
+ fillRule: "evenodd",
3400
+ d: "M6.222 25.64A14 14 0 1 0 21.778 2.36 14 14 0 0 0 6.222 25.64ZM7.333 4.023a12 12 0 1 1 13.334 19.955A12 12 0 0 1 7.333 4.022Z",
3401
+ clipRule: "evenodd"
3402
+ })));
3403
+ };
3404
+ var ClockIcon = SvgClock;
2716
3405
 
2717
3406
  const DEFAULT_LABEL_GETTER = value => value;
2718
3407
  const NOOP = () => {};
@@ -2814,6 +3503,7 @@ function Timepicker(props) {
2814
3503
  id,
2815
3504
  label,
2816
3505
  collapseLabelOnEmpty,
3506
+ onDateTimeBlur,
2817
3507
  formId,
2818
3508
  required,
2819
3509
  disabled,
@@ -2913,6 +3603,7 @@ function Timepicker(props) {
2913
3603
  const onInputBlur = e => {
2914
3604
  setDropdownIsOpen(false);
2915
3605
  propagateRawToMinute();
3606
+ onDateTimeBlur(e);
2916
3607
  };
2917
3608
  const onDropdownValueSelected = value => {
2918
3609
  setDropdownIsOpen(false);
@@ -2968,11 +3659,12 @@ function Timepicker(props) {
2968
3659
  });
2969
3660
  }
2970
3661
 
2971
- const type$8 = 'datetime';
3662
+ const type$9 = 'datetime';
2972
3663
  function Datetime(props) {
2973
3664
  const {
2974
3665
  disabled,
2975
3666
  errors = [],
3667
+ onBlur,
2976
3668
  field,
2977
3669
  onChange,
2978
3670
  readonly,
@@ -2996,6 +3688,7 @@ function Datetime(props) {
2996
3688
  const {
2997
3689
  formId
2998
3690
  } = hooks.useContext(FormContext$1);
3691
+ const dateTimeGroupRef = hooks.useRef();
2999
3692
  const getNullDateTime = () => ({
3000
3693
  date: new Date(Date.parse(null)),
3001
3694
  time: null
@@ -3006,6 +3699,12 @@ function Datetime(props) {
3006
3699
  const isValidTime = time => !isNaN(parseInt(time));
3007
3700
  const useDatePicker = hooks.useMemo(() => subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
3008
3701
  const useTimePicker = hooks.useMemo(() => subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
3702
+ const onDateTimeBlur = hooks.useCallback(e => {
3703
+ if (e.relatedTarget && dateTimeGroupRef.current.contains(e.relatedTarget)) {
3704
+ return;
3705
+ }
3706
+ onBlur();
3707
+ }, [onBlur]);
3009
3708
  hooks.useEffect(() => {
3010
3709
  let {
3011
3710
  date,
@@ -3097,6 +3796,7 @@ function Datetime(props) {
3097
3796
  id,
3098
3797
  label: dateLabel,
3099
3798
  collapseLabelOnEmpty: !timeLabel,
3799
+ onDateTimeBlur,
3100
3800
  formId,
3101
3801
  required,
3102
3802
  disabled,
@@ -3110,6 +3810,7 @@ function Datetime(props) {
3110
3810
  id,
3111
3811
  label: timeLabel,
3112
3812
  collapseLabelOnEmpty: !dateLabel,
3813
+ onDateTimeBlur,
3113
3814
  formId,
3114
3815
  required,
3115
3816
  disabled,
@@ -3121,13 +3822,14 @@ function Datetime(props) {
3121
3822
  'aria-describedby': errorMessageId
3122
3823
  };
3123
3824
  return jsxRuntime.jsxs("div", {
3124
- class: formFieldClasses(type$8, {
3825
+ class: formFieldClasses(type$9, {
3125
3826
  errors: allErrors,
3126
3827
  disabled,
3127
3828
  readonly
3128
3829
  }),
3129
3830
  children: [jsxRuntime.jsxs("div", {
3130
3831
  class: classNames('fjs-vertical-group'),
3832
+ ref: dateTimeGroupRef,
3131
3833
  children: [useDatePicker && jsxRuntime.jsx(Datepicker, {
3132
3834
  ...datePickerProps
3133
3835
  }), useTimePicker && useDatePicker && jsxRuntime.jsx("div", {
@@ -3144,7 +3846,7 @@ function Datetime(props) {
3144
3846
  });
3145
3847
  }
3146
3848
  Datetime.config = {
3147
- type: type$8,
3849
+ type: type$9,
3148
3850
  keyed: true,
3149
3851
  label: 'Date time',
3150
3852
  group: 'basic-input',
@@ -3161,10 +3863,10 @@ Datetime.config = {
3161
3863
  }
3162
3864
  };
3163
3865
 
3164
- /**
3165
- * This file must not be changed or exchanged.
3166
- *
3167
- * @see http://bpmn.io/license for more information.
3866
+ /**
3867
+ * This file must not be changed or exchanged.
3868
+ *
3869
+ * @see http://bpmn.io/license for more information.
3168
3870
  */
3169
3871
  function Logo() {
3170
3872
  return jsxRuntime.jsxs("svg", {
@@ -3296,11 +3998,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
3296
3998
 
3297
3999
  const FORM_ELEMENT = document.createElement('form');
3298
4000
 
3299
- /**
3300
- * Sanitize a HTML string and return the cleaned, safe version.
3301
- *
3302
- * @param {string} html
3303
- * @return {string}
4001
+ /**
4002
+ * Sanitize a HTML string and return the cleaned, safe version.
4003
+ *
4004
+ * @param {string} html
4005
+ * @return {string}
3304
4006
  */
3305
4007
 
3306
4008
  // see https://github.com/developit/snarkdown/issues/70
@@ -3318,29 +4020,29 @@ function sanitizeHTML(html) {
3318
4020
  }
3319
4021
  }
3320
4022
 
3321
- /**
3322
- * Sanitizes an image source to ensure we only allow for data URI and links
3323
- * that start with http(s).
3324
- *
3325
- * Note: Most browsers anyway do not support script execution in <img> elements.
3326
- *
3327
- * @param {string} src
3328
- * @returns {string}
4023
+ /**
4024
+ * Sanitizes an image source to ensure we only allow for data URI and links
4025
+ * that start with http(s).
4026
+ *
4027
+ * Note: Most browsers anyway do not support script execution in <img> elements.
4028
+ *
4029
+ * @param {string} src
4030
+ * @returns {string}
3329
4031
  */
3330
4032
  function sanitizeImageSource(src) {
3331
4033
  const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
3332
4034
  return valid ? src : '';
3333
4035
  }
3334
4036
 
3335
- /**
3336
- * Recursively sanitize a HTML node, potentially
3337
- * removing it, its children or attributes.
3338
- *
3339
- * Inspired by https://github.com/developit/snarkdown/issues/70
3340
- * and https://github.com/cure53/DOMPurify. Simplified
3341
- * for our use-case.
3342
- *
3343
- * @param {Element} node
4037
+ /**
4038
+ * Recursively sanitize a HTML node, potentially
4039
+ * removing it, its children or attributes.
4040
+ *
4041
+ * Inspired by https://github.com/developit/snarkdown/issues/70
4042
+ * and https://github.com/cure53/DOMPurify. Simplified
4043
+ * for our use-case.
4044
+ *
4045
+ * @param {Element} node
3344
4046
  */
3345
4047
  function sanitizeNode(node) {
3346
4048
  // allow text nodes
@@ -3384,13 +4086,13 @@ function sanitizeNode(node) {
3384
4086
  }
3385
4087
  }
3386
4088
 
3387
- /**
3388
- * Validates attributes for validity.
3389
- *
3390
- * @param {string} lcTag
3391
- * @param {string} lcName
3392
- * @param {string} value
3393
- * @return {boolean}
4089
+ /**
4090
+ * Validates attributes for validity.
4091
+ *
4092
+ * @param {string} lcTag
4093
+ * @param {string} lcName
4094
+ * @param {string} value
4095
+ * @return {boolean}
3394
4096
  */
3395
4097
  function isValidAttribute(lcTag, lcName, value) {
3396
4098
  // disallow most attributes based on whitelist
@@ -3413,37 +4115,48 @@ function isValidAttribute(lcTag, lcName, value) {
3413
4115
  return true;
3414
4116
  }
3415
4117
 
3416
- function _extends$h() { _extends$h = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$h.apply(this, arguments); }
3417
- var ImagePlaceholder = (({
3418
- styles = {},
3419
- ...props
3420
- }) => /*#__PURE__*/React.createElement("svg", _extends$h({
3421
- width: "64",
3422
- height: "64",
3423
- viewBox: "0 0 1280 1280",
3424
- xmlns: "http://www.w3.org/2000/svg",
3425
- fillRule: "evenodd",
3426
- clipRule: "evenodd",
3427
- strokeLinejoin: "round",
3428
- strokeMiterlimit: "2"
3429
- }, props), /*#__PURE__*/React.createElement("path", {
3430
- fill: "#e5e9ed",
3431
- d: "M0 0h1280v1280H0z"
3432
- }), /*#__PURE__*/React.createElement("path", {
3433
- d: "M910 410H370v470h540V410zm-57.333 57.333v355.334H427.333V467.333h425.334z",
3434
- fill: "#cad3db"
3435
- }), /*#__PURE__*/React.createElement("path", {
3436
- d: "M810 770H480v-60l100-170 130 170 100-65v125z",
3437
- fill: "#cad3db"
3438
- }), /*#__PURE__*/React.createElement("circle", {
3439
- cx: "750",
3440
- cy: "550",
3441
- r: "50",
3442
- fill: "#cad3db",
3443
- transform: "translate(10 10)"
3444
- })));
3445
-
3446
- const type$7 = 'image';
4118
+ function _extends$i() { _extends$i = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$i.apply(this, arguments); }
4119
+ var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
4120
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$i({
4121
+ xmlns: "http://www.w3.org/2000/svg",
4122
+ xmlSpace: "preserve",
4123
+ width: 64,
4124
+ height: 64,
4125
+ style: {
4126
+ fillRule: "evenodd",
4127
+ clipRule: "evenodd",
4128
+ strokeLinejoin: "round",
4129
+ strokeMiterlimit: 2
4130
+ },
4131
+ viewBox: "0 0 1280 1280"
4132
+ }, props), /*#__PURE__*/React__namespace.createElement("path", {
4133
+ d: "M0 0h1280v1280H0z",
4134
+ style: {
4135
+ fill: "#e5e9ed"
4136
+ }
4137
+ }), /*#__PURE__*/React__namespace.createElement("path", {
4138
+ d: "M910 410H370v470h540V410Zm-57.333 57.333v355.334H427.333V467.333h425.334Z",
4139
+ style: {
4140
+ fill: "#cad3db"
4141
+ }
4142
+ }), /*#__PURE__*/React__namespace.createElement("path", {
4143
+ d: "M810 770H480v-60l100-170 130 170 100-65v125Z",
4144
+ style: {
4145
+ fill: "#cad3db"
4146
+ }
4147
+ }), /*#__PURE__*/React__namespace.createElement("circle", {
4148
+ cx: 750,
4149
+ cy: 550,
4150
+ r: 50,
4151
+ style: {
4152
+ fill: "#cad3db"
4153
+ },
4154
+ transform: "translate(10 10)"
4155
+ }));
4156
+ };
4157
+ var ImagePlaceholder = SvgImagePlaceholder;
4158
+
4159
+ const type$8 = 'image';
3447
4160
  function Image(props) {
3448
4161
  const {
3449
4162
  field
@@ -3453,14 +4166,18 @@ function Image(props) {
3453
4166
  id,
3454
4167
  source
3455
4168
  } = field;
3456
- const evaluatedImageSource = useExpressionEvaluation(source);
4169
+ const evaluatedImageSource = useSingleLineTemplateEvaluation(source, {
4170
+ debug: true
4171
+ });
3457
4172
  const safeSource = hooks.useMemo(() => sanitizeImageSource(evaluatedImageSource), [evaluatedImageSource]);
3458
- const altText = useExpressionEvaluation(alt);
4173
+ const altText = useSingleLineTemplateEvaluation(alt, {
4174
+ debug: true
4175
+ });
3459
4176
  const {
3460
4177
  formId
3461
4178
  } = hooks.useContext(FormContext$1);
3462
4179
  return jsxRuntime.jsx("div", {
3463
- class: formFieldClasses(type$7),
4180
+ class: formFieldClasses(type$8),
3464
4181
  children: jsxRuntime.jsxs("div", {
3465
4182
  class: "fjs-image-container",
3466
4183
  children: [safeSource && jsxRuntime.jsx("img", {
@@ -3478,7 +4195,7 @@ function Image(props) {
3478
4195
  });
3479
4196
  }
3480
4197
  Image.config = {
3481
- type: type$7,
4198
+ type: type$8,
3482
4199
  keyed: false,
3483
4200
  label: 'Image view',
3484
4201
  group: 'presentation',
@@ -3487,45 +4204,66 @@ Image.config = {
3487
4204
  })
3488
4205
  };
3489
4206
 
4207
+ function TemplatedInputAdorner(props) {
4208
+ const {
4209
+ pre,
4210
+ post
4211
+ } = props;
4212
+ const evaluatedPre = useSingleLineTemplateEvaluation(pre, {
4213
+ debug: true
4214
+ });
4215
+ const evaluatedPost = useSingleLineTemplateEvaluation(post, {
4216
+ debug: true
4217
+ });
4218
+ return jsxRuntime.jsx(InputAdorner, {
4219
+ ...props,
4220
+ pre: evaluatedPre,
4221
+ post: evaluatedPost
4222
+ });
4223
+ }
4224
+
4225
+ var _path$f;
4226
+ function _extends$h() { _extends$h = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$h.apply(this, arguments); }
4227
+ var SvgAngelDown = function SvgAngelDown(props) {
4228
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$h({
4229
+ xmlns: "http://www.w3.org/2000/svg",
4230
+ width: 8,
4231
+ height: 8
4232
+ }, props), _path$f || (_path$f = /*#__PURE__*/React__namespace.createElement("path", {
4233
+ fill: "currentColor",
4234
+ fillRule: "evenodd",
4235
+ stroke: "currentColor",
4236
+ strokeWidth: 0.5,
4237
+ d: "M7.75 1.336 4 6.125.258 1.335 0 1.54l4 5.125L8 1.54Zm0 0",
4238
+ clipRule: "evenodd"
4239
+ })));
4240
+ };
4241
+ var AngelDownIcon = SvgAngelDown;
4242
+
4243
+ var _path$e;
3490
4244
  function _extends$g() { _extends$g = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$g.apply(this, arguments); }
3491
- var AngelDownIcon = (({
3492
- styles = {},
3493
- ...props
3494
- }) => /*#__PURE__*/React.createElement("svg", _extends$g({
3495
- xmlns: "http://www.w3.org/2000/svg",
3496
- width: "8",
3497
- height: "8"
3498
- }, props), /*#__PURE__*/React.createElement("path", {
3499
- fillRule: "evenodd",
3500
- clipRule: "evenodd",
3501
- fill: "currentColor",
3502
- stroke: "currentColor",
3503
- strokeWidth: ".5",
3504
- d: "M7.75 1.336L4 6.125.258 1.335 0 1.54l4 5.125L8 1.54zm0 0"
3505
- })));
4245
+ var SvgAngelUp = function SvgAngelUp(props) {
4246
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$g({
4247
+ xmlns: "http://www.w3.org/2000/svg",
4248
+ width: 8,
4249
+ height: 8
4250
+ }, props), _path$e || (_path$e = /*#__PURE__*/React__namespace.createElement("path", {
4251
+ fill: "currentColor",
4252
+ fillRule: "evenodd",
4253
+ stroke: "currentColor",
4254
+ strokeWidth: 0.5,
4255
+ d: "M7.75 6.664 4 1.875.258 6.665 0 6.46l4-5.125L8 6.46Zm0 0",
4256
+ clipRule: "evenodd"
4257
+ })));
4258
+ };
4259
+ var AngelUpIcon = SvgAngelUp;
3506
4260
 
3507
- function _extends$f() { _extends$f = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$f.apply(this, arguments); }
3508
- var AngelUpIcon = (({
3509
- styles = {},
3510
- ...props
3511
- }) => /*#__PURE__*/React.createElement("svg", _extends$f({
3512
- xmlns: "http://www.w3.org/2000/svg",
3513
- width: "8",
3514
- height: "8"
3515
- }, props), /*#__PURE__*/React.createElement("path", {
3516
- fillRule: "evenodd",
3517
- clipRule: "evenodd",
3518
- fill: "currentColor",
3519
- stroke: "currentColor",
3520
- strokeWidth: ".5",
3521
- d: "M7.75 6.664L4 1.875.258 6.665 0 6.46l4-5.125L8 6.46zm0 0"
3522
- })));
3523
-
3524
- const type$6 = 'number';
4261
+ const type$7 = 'number';
3525
4262
  function Numberfield(props) {
3526
4263
  const {
3527
4264
  disabled,
3528
4265
  errors = [],
4266
+ onBlur,
3529
4267
  field,
3530
4268
  value,
3531
4269
  readonly,
@@ -3658,7 +4396,7 @@ function Numberfield(props) {
3658
4396
  } = hooks.useContext(FormContext$1);
3659
4397
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3660
4398
  return jsxRuntime.jsxs("div", {
3661
- class: formFieldClasses(type$6, {
4399
+ class: formFieldClasses(type$7, {
3662
4400
  errors,
3663
4401
  disabled,
3664
4402
  readonly
@@ -3667,7 +4405,7 @@ function Numberfield(props) {
3667
4405
  id: prefixId(id, formId),
3668
4406
  label: label,
3669
4407
  required: required
3670
- }), jsxRuntime.jsx(InputAdorner, {
4408
+ }), jsxRuntime.jsx(TemplatedInputAdorner, {
3671
4409
  disabled: disabled,
3672
4410
  readonly: readonly,
3673
4411
  pre: prefixAdorner,
@@ -3686,7 +4424,8 @@ function Numberfield(props) {
3686
4424
  readOnly: readonly,
3687
4425
  id: prefixId(id, formId),
3688
4426
  onKeyDown: onKeyDown,
3689
- onKeyPress: onKeyPress
4427
+ onKeyPress: onKeyPress,
4428
+ onBlur: onBlur
3690
4429
 
3691
4430
  // @ts-ignore
3692
4431
  ,
@@ -3729,7 +4468,7 @@ function Numberfield(props) {
3729
4468
  });
3730
4469
  }
3731
4470
  Numberfield.config = {
3732
- type: type$6,
4471
+ type: type$7,
3733
4472
  keyed: true,
3734
4473
  label: 'Number',
3735
4474
  group: 'basic-input',
@@ -3752,11 +4491,12 @@ Numberfield.config = {
3752
4491
  })
3753
4492
  };
3754
4493
 
3755
- const type$5 = 'radio';
4494
+ const type$6 = 'radio';
3756
4495
  function Radio(props) {
3757
4496
  const {
3758
4497
  disabled,
3759
4498
  errors = [],
4499
+ onBlur,
3760
4500
  field,
3761
4501
  readonly,
3762
4502
  value
@@ -3767,6 +4507,7 @@ function Radio(props) {
3767
4507
  label,
3768
4508
  validate = {}
3769
4509
  } = field;
4510
+ const outerDivRef = hooks.useRef();
3770
4511
  const {
3771
4512
  required
3772
4513
  } = validate;
@@ -3776,6 +4517,12 @@ function Radio(props) {
3776
4517
  value: v
3777
4518
  });
3778
4519
  };
4520
+ const onRadioBlur = e => {
4521
+ if (outerDivRef.current.contains(e.relatedTarget)) {
4522
+ return;
4523
+ }
4524
+ onBlur();
4525
+ };
3779
4526
  const {
3780
4527
  state: loadState,
3781
4528
  values: options
@@ -3785,11 +4532,12 @@ function Radio(props) {
3785
4532
  } = hooks.useContext(FormContext$1);
3786
4533
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3787
4534
  return jsxRuntime.jsxs("div", {
3788
- class: formFieldClasses(type$5, {
4535
+ class: formFieldClasses(type$6, {
3789
4536
  errors,
3790
4537
  disabled,
3791
4538
  readonly
3792
4539
  }),
4540
+ ref: outerDivRef,
3793
4541
  children: [jsxRuntime.jsx(Label, {
3794
4542
  label: label,
3795
4543
  required: required
@@ -3809,6 +4557,7 @@ function Radio(props) {
3809
4557
  id: prefixId(`${id}-${index}`, formId),
3810
4558
  type: "radio",
3811
4559
  onClick: () => onChange(option.value),
4560
+ onBlur: onRadioBlur,
3812
4561
  "aria-describedby": errorMessageId
3813
4562
  })
3814
4563
  }, `${id}-${index}`);
@@ -3821,7 +4570,7 @@ function Radio(props) {
3821
4570
  });
3822
4571
  }
3823
4572
  Radio.config = {
3824
- type: type$5,
4573
+ type: type$6,
3825
4574
  keyed: true,
3826
4575
  label: 'Radio',
3827
4576
  group: 'selection',
@@ -3844,28 +4593,30 @@ Radio.config = {
3844
4593
  }
3845
4594
  };
3846
4595
 
3847
- function _extends$e() { _extends$e = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$e.apply(this, arguments); }
3848
- var XMarkIcon = (({
3849
- styles = {},
3850
- ...props
3851
- }) => /*#__PURE__*/React.createElement("svg", _extends$e({
3852
- xmlns: "http://www.w3.org/2000/svg",
3853
- width: "8",
3854
- height: "8"
3855
- }, props), /*#__PURE__*/React.createElement("path", {
3856
- fillRule: "evenodd",
3857
- clipRule: "evenodd",
3858
- fill: "currentColor",
3859
- stroke: "currentColor",
3860
- strokeWidth: ".5",
3861
- d: "M4 3.766L7.43.336l.234.234L4.234 4l3.43 3.43-.234.234L4 4.234.57 7.664.336 7.43 3.766 4 .336.57.57.336zm0 0"
3862
- })));
4596
+ var _path$d;
4597
+ function _extends$f() { _extends$f = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$f.apply(this, arguments); }
4598
+ var SvgXMark = function SvgXMark(props) {
4599
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$f({
4600
+ xmlns: "http://www.w3.org/2000/svg",
4601
+ width: 8,
4602
+ height: 8
4603
+ }, props), _path$d || (_path$d = /*#__PURE__*/React__namespace.createElement("path", {
4604
+ fill: "currentColor",
4605
+ fillRule: "evenodd",
4606
+ stroke: "currentColor",
4607
+ strokeWidth: 0.5,
4608
+ d: "M4 3.766 7.43.336l.234.234L4.234 4l3.43 3.43-.234.234L4 4.234.57 7.664.336 7.43 3.766 4 .336.57.57.336Zm0 0",
4609
+ clipRule: "evenodd"
4610
+ })));
4611
+ };
4612
+ var XMarkIcon = SvgXMark;
3863
4613
 
3864
4614
  function SearchableSelect(props) {
3865
4615
  const {
3866
4616
  id,
3867
4617
  disabled,
3868
4618
  errors,
4619
+ onBlur,
3869
4620
  field,
3870
4621
  readonly,
3871
4622
  value
@@ -3985,6 +4736,7 @@ function SearchableSelect(props) {
3985
4736
  onBlur: () => {
3986
4737
  setIsDropdownExpanded(false);
3987
4738
  setFilter(valueLabel);
4739
+ onBlur();
3988
4740
  },
3989
4741
  "aria-describedby": props['aria-describedby']
3990
4742
  }), displayState.displayCross && jsxRuntime.jsxs("span", {
@@ -4019,6 +4771,7 @@ function SimpleSelect(props) {
4019
4771
  id,
4020
4772
  disabled,
4021
4773
  errors,
4774
+ onBlur,
4022
4775
  field,
4023
4776
  readonly,
4024
4777
  value
@@ -4073,7 +4826,10 @@ function SimpleSelect(props) {
4073
4826
  'hasErrors': errors.length
4074
4827
  }),
4075
4828
  onFocus: () => setIsDropdownExpanded(true),
4076
- onBlur: () => setIsDropdownExpanded(false),
4829
+ onBlur: () => {
4830
+ setIsDropdownExpanded(false);
4831
+ onBlur();
4832
+ },
4077
4833
  onMouseDown: onMouseDown,
4078
4834
  children: [jsxRuntime.jsx("div", {
4079
4835
  class: classNames('fjs-select-display', {
@@ -4115,11 +4871,12 @@ function SimpleSelect(props) {
4115
4871
  });
4116
4872
  }
4117
4873
 
4118
- const type$4 = 'select';
4874
+ const type$5 = 'select';
4119
4875
  function Select(props) {
4120
4876
  const {
4121
4877
  disabled,
4122
4878
  errors = [],
4879
+ onBlur,
4123
4880
  field,
4124
4881
  onChange,
4125
4882
  readonly,
@@ -4143,14 +4900,15 @@ function Select(props) {
4143
4900
  id,
4144
4901
  disabled,
4145
4902
  errors,
4903
+ onBlur,
4146
4904
  field,
4147
4905
  value,
4148
4906
  onChange,
4149
4907
  readonly,
4150
4908
  'aria-describedby': errorMessageId
4151
- }), [disabled, errors, field, id, value, onChange, readonly, errorMessageId]);
4909
+ }), [disabled, errors, field, id, value, onChange, onBlur, readonly, errorMessageId]);
4152
4910
  return jsxRuntime.jsxs("div", {
4153
- class: formFieldClasses(type$4, {
4911
+ class: formFieldClasses(type$5, {
4154
4912
  errors,
4155
4913
  disabled,
4156
4914
  readonly
@@ -4178,7 +4936,7 @@ function Select(props) {
4178
4936
  });
4179
4937
  }
4180
4938
  Select.config = {
4181
- type: type$4,
4939
+ type: type$5,
4182
4940
  keyed: true,
4183
4941
  label: 'Select',
4184
4942
  group: 'selection',
@@ -4201,11 +4959,38 @@ Select.config = {
4201
4959
  }
4202
4960
  };
4203
4961
 
4962
+ const type$4 = 'spacer';
4963
+ function Spacer(props) {
4964
+ const {
4965
+ field
4966
+ } = props;
4967
+ const {
4968
+ height = 60
4969
+ } = field;
4970
+ return jsxRuntime.jsx("div", {
4971
+ class: formFieldClasses(type$4),
4972
+ style: {
4973
+ height: height
4974
+ }
4975
+ });
4976
+ }
4977
+ Spacer.config = {
4978
+ type: type$4,
4979
+ keyed: false,
4980
+ label: 'Spacer',
4981
+ group: 'presentation',
4982
+ create: (options = {}) => ({
4983
+ height: 60,
4984
+ ...options
4985
+ })
4986
+ };
4987
+
4204
4988
  const type$3 = 'taglist';
4205
4989
  function Taglist(props) {
4206
4990
  const {
4207
4991
  disabled,
4208
4992
  errors = [],
4993
+ onBlur,
4209
4994
  field,
4210
4995
  readonly,
4211
4996
  value: values = []
@@ -4298,9 +5083,10 @@ function Taglist(props) {
4298
5083
  break;
4299
5084
  }
4300
5085
  };
4301
- const onBlur = () => {
5086
+ const onComponentBlur = () => {
4302
5087
  setIsDropdownExpanded(false);
4303
5088
  setFilter('');
5089
+ onBlur();
4304
5090
  };
4305
5091
  const onTagRemoveClick = (event, value) => {
4306
5092
  const {
@@ -4371,9 +5157,7 @@ function Taglist(props) {
4371
5157
  onKeyDown: onInputKeyDown,
4372
5158
  onMouseDown: () => setIsEscapeClose(false),
4373
5159
  onFocus: () => !readonly && setIsDropdownExpanded(true),
4374
- onBlur: () => {
4375
- !readonly && onBlur();
4376
- },
5160
+ onBlur: () => !readonly && onComponentBlur(),
4377
5161
  "aria-describedby": errorMessageId
4378
5162
  })]
4379
5163
  }), jsxRuntime.jsx("div", {
@@ -4506,6 +5290,7 @@ function Textfield(props) {
4506
5290
  const {
4507
5291
  disabled,
4508
5292
  errors = [],
5293
+ onBlur,
4509
5294
  field,
4510
5295
  readonly,
4511
5296
  value = ''
@@ -4546,7 +5331,7 @@ function Textfield(props) {
4546
5331
  id: prefixId(id, formId),
4547
5332
  label: label,
4548
5333
  required: required
4549
- }), jsxRuntime.jsx(InputAdorner, {
5334
+ }), jsxRuntime.jsx(TemplatedInputAdorner, {
4550
5335
  disabled: disabled,
4551
5336
  readonly: readonly,
4552
5337
  pre: prefixAdorner,
@@ -4557,6 +5342,7 @@ function Textfield(props) {
4557
5342
  readOnly: readonly,
4558
5343
  id: prefixId(id, formId),
4559
5344
  onInput: onChange,
5345
+ onBlur: onBlur,
4560
5346
  type: "text",
4561
5347
  value: value,
4562
5348
  "aria-describedby": errorMessageId
@@ -4598,6 +5384,7 @@ function Textarea(props) {
4598
5384
  const {
4599
5385
  disabled,
4600
5386
  errors = [],
5387
+ onBlur,
4601
5388
  field,
4602
5389
  readonly,
4603
5390
  value = ''
@@ -4620,22 +5407,12 @@ function Textarea(props) {
4620
5407
  value: target.value
4621
5408
  });
4622
5409
  };
4623
- const autoSizeTextarea = hooks.useCallback(textarea => {
4624
- // Ensures the textarea shrinks back, and improves resizing behavior consistency
4625
- textarea.style.height = '0px';
4626
- const computed = window.getComputedStyle(textarea);
4627
- const calculatedHeight = parseInt(computed.getPropertyValue('border-top-width')) + parseInt(computed.getPropertyValue('padding-top')) + textarea.scrollHeight + parseInt(computed.getPropertyValue('padding-bottom')) + parseInt(computed.getPropertyValue('border-bottom-width'));
4628
- const minHeight = 75;
4629
- const maxHeight = 350;
4630
- const displayHeight = Math.max(Math.min(calculatedHeight, maxHeight), minHeight);
4631
- textarea.style.height = `${displayHeight}px`;
4632
-
4633
- // Overflow is hidden by default to hide scrollbar flickering
4634
- textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
4635
- }, []);
5410
+ hooks.useLayoutEffect(() => {
5411
+ autoSizeTextarea(textareaRef.current);
5412
+ }, [value]);
4636
5413
  hooks.useEffect(() => {
4637
5414
  autoSizeTextarea(textareaRef.current);
4638
- }, [autoSizeTextarea, value]);
5415
+ }, []);
4639
5416
  const {
4640
5417
  formId
4641
5418
  } = hooks.useContext(FormContext$1);
@@ -4656,6 +5433,7 @@ function Textarea(props) {
4656
5433
  readonly: readonly,
4657
5434
  id: prefixId(id, formId),
4658
5435
  onInput: onInput,
5436
+ onBlur: onBlur,
4659
5437
  value: value,
4660
5438
  ref: textareaRef,
4661
5439
  "aria-describedby": errorMessageId
@@ -4680,246 +5458,301 @@ Textarea.config = {
4680
5458
  ...options
4681
5459
  })
4682
5460
  };
5461
+ const autoSizeTextarea = textarea => {
5462
+ // Ensures the textarea shrinks back, and improves resizing behavior consistency
5463
+ textarea.style.height = '0px';
5464
+ const computed = window.getComputedStyle(textarea);
5465
+ const heightFromLines = () => {
5466
+ const lineHeight = parseInt(computed.getPropertyValue('line-height').replace('px', '')) || 0;
5467
+ const lines = textarea.value ? textarea.value.toString().split('\n').length : 0;
5468
+ return lines * lineHeight;
5469
+ };
5470
+ const calculatedHeight = parseInt(computed.getPropertyValue('border-top-width')) + parseInt(computed.getPropertyValue('padding-top')) + (textarea.scrollHeight || heightFromLines()) + parseInt(computed.getPropertyValue('padding-bottom')) + parseInt(computed.getPropertyValue('border-bottom-width'));
5471
+ const minHeight = 75;
5472
+ const maxHeight = 350;
5473
+ const displayHeight = Math.max(Math.min(calculatedHeight || 0, maxHeight), minHeight);
5474
+ textarea.style.height = `${displayHeight}px`;
5475
+
5476
+ // Overflow is hidden by default to hide scrollbar flickering
5477
+ textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
5478
+ };
4683
5479
 
5480
+ var _path$c;
5481
+ function _extends$e() { _extends$e = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$e.apply(this, arguments); }
5482
+ var SvgButton = function SvgButton(props) {
5483
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$e({
5484
+ xmlns: "http://www.w3.org/2000/svg",
5485
+ width: 54,
5486
+ height: 54,
5487
+ fill: "currentcolor"
5488
+ }, props), _path$c || (_path$c = /*#__PURE__*/React__namespace.createElement("path", {
5489
+ fillRule: "evenodd",
5490
+ d: "M45 17a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V20a3 3 0 0 1 3-3h36zm-9 8.889H18v2.222h18v-2.222z"
5491
+ })));
5492
+ };
5493
+ var ButtonIcon = SvgButton;
5494
+
5495
+ var _path$b;
4684
5496
  function _extends$d() { _extends$d = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$d.apply(this, arguments); }
4685
- var ButtonIcon = (({
4686
- styles = {},
4687
- ...props
4688
- }) => /*#__PURE__*/React.createElement("svg", _extends$d({
4689
- xmlns: "http://www.w3.org/2000/svg",
4690
- width: "54",
4691
- height: "54",
4692
- fill: "currentcolor"
4693
- }, props), /*#__PURE__*/React.createElement("path", {
4694
- fillRule: "evenodd",
4695
- d: "M45 17a3 3 0 013 3v14a3 3 0 01-3 3H9a3 3 0 01-3-3V20a3 3 0 013-3h36zm-9 8.889H18v2.222h18v-2.222z"
4696
- })));
5497
+ var SvgCheckbox = function SvgCheckbox(props) {
5498
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$d({
5499
+ xmlns: "http://www.w3.org/2000/svg",
5500
+ width: 54,
5501
+ height: 54,
5502
+ fill: "currentcolor"
5503
+ }, props), _path$b || (_path$b = /*#__PURE__*/React__namespace.createElement("path", {
5504
+ d: "M34 18H20a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V20a2 2 0 0 0-2-2zm-9 14-5-5 1.41-1.41L25 29.17l7.59-7.59L34 23l-9 9z"
5505
+ })));
5506
+ };
5507
+ var CheckboxIcon = SvgCheckbox;
4697
5508
 
5509
+ var _g, _use, _use2, _use3, _defs;
4698
5510
  function _extends$c() { _extends$c = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$c.apply(this, arguments); }
4699
- var CheckboxIcon = (({
4700
- styles = {},
4701
- ...props
4702
- }) => /*#__PURE__*/React.createElement("svg", _extends$c({
4703
- xmlns: "http://www.w3.org/2000/svg",
4704
- width: "54",
4705
- height: "54",
4706
- fill: "currentcolor"
4707
- }, props), /*#__PURE__*/React.createElement("path", {
4708
- d: "M34 18H20a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V20a2 2 0 00-2-2zm-9 14l-5-5 1.41-1.41L25 29.17l7.59-7.59L34 23l-9 9z"
4709
- })));
5511
+ var SvgChecklist = function SvgChecklist(props) {
5512
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$c({
5513
+ xmlns: "http://www.w3.org/2000/svg",
5514
+ xmlnsXlink: "http://www.w3.org/1999/xlink",
5515
+ width: 54,
5516
+ height: 54,
5517
+ fill: "currentcolor"
5518
+ }, props), _g || (_g = /*#__PURE__*/React__namespace.createElement("g", {
5519
+ fillRule: "evenodd"
5520
+ }, /*#__PURE__*/React__namespace.createElement("use", {
5521
+ xlinkHref: "#Checklist_svg__a"
5522
+ }), /*#__PURE__*/React__namespace.createElement("use", {
5523
+ xlinkHref: "#Checklist_svg__a",
5524
+ y: 24
5525
+ }), /*#__PURE__*/React__namespace.createElement("use", {
5526
+ xlinkHref: "#Checklist_svg__a",
5527
+ y: 12
5528
+ }))), _use || (_use = /*#__PURE__*/React__namespace.createElement("use", {
5529
+ xlinkHref: "#Checklist_svg__b"
5530
+ })), _use2 || (_use2 = /*#__PURE__*/React__namespace.createElement("use", {
5531
+ xlinkHref: "#Checklist_svg__b",
5532
+ y: 12
5533
+ })), _use3 || (_use3 = /*#__PURE__*/React__namespace.createElement("use", {
5534
+ xlinkHref: "#Checklist_svg__b",
5535
+ y: 24
5536
+ })), _defs || (_defs = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("path", {
5537
+ id: "Checklist_svg__a",
5538
+ d: "M18 12h-6v6h6v-6zm-6-2a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2h-6z"
5539
+ }), /*#__PURE__*/React__namespace.createElement("path", {
5540
+ id: "Checklist_svg__b",
5541
+ d: "M23 14.5a1 1 0 0 1 1-1h19a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1v-1z"
5542
+ }))));
5543
+ };
5544
+ var ChecklistIcon = SvgChecklist;
4710
5545
 
5546
+ var _path$a, _path2$3, _path3;
4711
5547
  function _extends$b() { _extends$b = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$b.apply(this, arguments); }
4712
- var ChecklistIcon = (({
4713
- styles = {},
4714
- ...props
4715
- }) => /*#__PURE__*/React.createElement("svg", _extends$b({
4716
- xmlns: "http://www.w3.org/2000/svg",
4717
- xmlnsXlink: "http://www.w3.org/1999/xlink",
4718
- width: "54",
4719
- height: "54",
4720
- fill: "currentcolor"
4721
- }, props), /*#__PURE__*/React.createElement("g", {
4722
- fillRule: "evenodd"
4723
- }, /*#__PURE__*/React.createElement("use", {
4724
- xlinkHref: "#a"
4725
- }), /*#__PURE__*/React.createElement("use", {
4726
- xlinkHref: "#a",
4727
- y: "24"
4728
- }), /*#__PURE__*/React.createElement("use", {
4729
- xlinkHref: "#a",
4730
- y: "12"
4731
- })), /*#__PURE__*/React.createElement("use", {
4732
- xlinkHref: "#b"
4733
- }), /*#__PURE__*/React.createElement("use", {
4734
- xlinkHref: "#b",
4735
- y: "12"
4736
- }), /*#__PURE__*/React.createElement("use", {
4737
- xlinkHref: "#b",
4738
- y: "24"
4739
- }), /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
4740
- id: "a",
4741
- d: "M18 12h-6v6h6v-6zm-6-2a2 2 0 00-2 2v6a2 2 0 002 2h6a2 2 0 002-2v-6a2 2 0 00-2-2h-6z"
4742
- }), /*#__PURE__*/React.createElement("path", {
4743
- id: "b",
4744
- d: "M23 14.5a1 1 0 011-1h19a1 1 0 011 1v1a1 1 0 01-1 1H24a1 1 0 01-1-1v-1z"
4745
- }))));
5548
+ var SvgDatetime = function SvgDatetime(props) {
5549
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$b({
5550
+ xmlns: "http://www.w3.org/2000/svg",
5551
+ width: 54,
5552
+ height: 54,
5553
+ fill: "currentcolor"
5554
+ }, props), _path$a || (_path$a = /*#__PURE__*/React__namespace.createElement("path", {
5555
+ fillRule: "evenodd",
5556
+ d: "M37.908 13.418h-5.004v-2.354h-1.766v2.354H21.13v-2.354h-1.766v2.354H14.36a2.07 2.07 0 0 0-2.06 2.06v23.549a2.07 2.07 0 0 0 2.06 2.06h6.77v-1.766h-6.358a.707.707 0 0 1-.706-.706V15.89c0-.39.316-.707.706-.707h4.592v2.355h1.766v-2.355h10.008v2.355h1.766v-2.355h4.592a.71.71 0 0 1 .707.707v6.358h1.765v-6.77c0-1.133-.927-2.06-2.06-2.06z"
5557
+ })), _path2$3 || (_path2$3 = /*#__PURE__*/React__namespace.createElement("path", {
5558
+ d: "m35.13 37.603 1.237-1.237-3.468-3.475v-5.926h-1.754v6.654l3.984 3.984Z"
5559
+ })), _path3 || (_path3 = /*#__PURE__*/React__namespace.createElement("path", {
5560
+ fillRule: "evenodd",
5561
+ d: "M23.08 36.962a9.678 9.678 0 1 0 17.883-7.408 9.678 9.678 0 0 0-17.882 7.408Zm4.54-10.292a7.924 7.924 0 1 1 8.805 13.177A7.924 7.924 0 0 1 27.62 26.67Z"
5562
+ })));
5563
+ };
5564
+ var DatetimeIcon = SvgDatetime;
4746
5565
 
5566
+ var _path$9, _path2$2;
4747
5567
  function _extends$a() { _extends$a = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$a.apply(this, arguments); }
4748
- var DatetimeIcon = (({
4749
- styles = {},
4750
- ...props
4751
- }) => /*#__PURE__*/React.createElement("svg", _extends$a({
4752
- xmlns: "http://www.w3.org/2000/svg",
4753
- width: "54",
4754
- height: "54",
4755
- fill: "currentcolor"
4756
- }, props), /*#__PURE__*/React.createElement("path", {
4757
- fillRule: "evenodd",
4758
- d: "M37.908 13.418h-5.004v-2.354h-1.766v2.354H21.13v-2.354h-1.766v2.354H14.36a2.07 2.07 0 00-2.06 2.06v23.549a2.07 2.07 0 002.06 2.06h6.77v-1.766h-6.358a.707.707 0 01-.706-.706V15.89c0-.39.316-.707.706-.707h4.592v2.355h1.766v-2.355h10.008v2.355h1.766v-2.355h4.592a.71.71 0 01.707.707v6.358h1.765v-6.77c0-1.133-.927-2.06-2.06-2.06z"
4759
- }), /*#__PURE__*/React.createElement("path", {
4760
- d: "M35.13 37.603l1.237-1.237-3.468-3.475v-5.926h-1.754v6.654l3.984 3.984z"
4761
- }), /*#__PURE__*/React.createElement("path", {
4762
- fillRule: "evenodd",
4763
- d: "M23.08 36.962a9.678 9.678 0 1017.883-7.408 9.678 9.678 0 00-17.882 7.408zm4.54-10.292a7.924 7.924 0 118.805 13.177A7.924 7.924 0 0127.62 26.67z"
4764
- })));
5568
+ var SvgTaglist = function SvgTaglist(props) {
5569
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$a({
5570
+ xmlns: "http://www.w3.org/2000/svg",
5571
+ width: 54,
5572
+ height: 54,
5573
+ fill: "currentcolor"
5574
+ }, props), _path$9 || (_path$9 = /*#__PURE__*/React__namespace.createElement("path", {
5575
+ fillRule: "evenodd",
5576
+ d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36Zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1Z"
5577
+ })), _path2$2 || (_path2$2 = /*#__PURE__*/React__namespace.createElement("path", {
5578
+ d: "M11 22a1 1 0 0 1 1-1h19a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H12a1 1 0 0 1-1-1V22Z"
5579
+ })));
5580
+ };
5581
+ var TaglistIcon = SvgTaglist;
4765
5582
 
5583
+ var _rect, _rect2, _rect3;
4766
5584
  function _extends$9() { _extends$9 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$9.apply(this, arguments); }
4767
- var TaglistIcon = (({
4768
- styles = {},
4769
- ...props
4770
- }) => /*#__PURE__*/React.createElement("svg", _extends$9({
4771
- xmlns: "http://www.w3.org/2000/svg",
4772
- width: "54",
4773
- height: "54",
4774
- fill: "currentcolor"
4775
- }, props), /*#__PURE__*/React.createElement("path", {
4776
- fillRule: "evenodd",
4777
- d: "M45 16a3 3 0 013 3v16a3 3 0 01-3 3H9a3 3 0 01-3-3V19a3 3 0 013-3h36zm0 2H9a1 1 0 00-1 1v16a1 1 0 001 1h36a1 1 0 001-1V19a1 1 0 00-1-1z"
4778
- }), /*#__PURE__*/React.createElement("path", {
4779
- d: "M11 22a1 1 0 011-1h19a1 1 0 011 1v10a1 1 0 01-1 1H12a1 1 0 01-1-1V22z"
4780
- })));
5585
+ var SvgForm = function SvgForm(props) {
5586
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$9({
5587
+ xmlns: "http://www.w3.org/2000/svg",
5588
+ width: 54,
5589
+ height: 54
5590
+ }, props), _rect || (_rect = /*#__PURE__*/React__namespace.createElement("rect", {
5591
+ width: 24,
5592
+ height: 4,
5593
+ x: 15,
5594
+ y: 17,
5595
+ rx: 1
5596
+ })), _rect2 || (_rect2 = /*#__PURE__*/React__namespace.createElement("rect", {
5597
+ width: 24,
5598
+ height: 4,
5599
+ x: 15,
5600
+ y: 25,
5601
+ rx: 1
5602
+ })), _rect3 || (_rect3 = /*#__PURE__*/React__namespace.createElement("rect", {
5603
+ width: 13,
5604
+ height: 4,
5605
+ x: 15,
5606
+ y: 33,
5607
+ rx: 1
5608
+ })));
5609
+ };
5610
+ var FormIcon = SvgForm;
4781
5611
 
5612
+ var _path$8;
4782
5613
  function _extends$8() { _extends$8 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$8.apply(this, arguments); }
4783
- var FormIcon = (({
4784
- styles = {},
4785
- ...props
4786
- }) => /*#__PURE__*/React.createElement("svg", _extends$8({
4787
- xmlns: "http://www.w3.org/2000/svg",
4788
- width: "54",
4789
- height: "54"
4790
- }, props), /*#__PURE__*/React.createElement("rect", {
4791
- width: "24",
4792
- height: "4",
4793
- x: "15",
4794
- y: "17",
4795
- rx: "1"
4796
- }), /*#__PURE__*/React.createElement("rect", {
4797
- width: "24",
4798
- height: "4",
4799
- x: "15",
4800
- y: "25",
4801
- rx: "1"
4802
- }), /*#__PURE__*/React.createElement("rect", {
4803
- width: "13",
4804
- height: "4",
4805
- x: "15",
4806
- y: "33",
4807
- rx: "1"
4808
- })));
5614
+ var SvgGroup = function SvgGroup(props) {
5615
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$8({
5616
+ xmlns: "http://www.w3.org/2000/svg",
5617
+ width: 54,
5618
+ height: 54
5619
+ }, props), _path$8 || (_path$8 = /*#__PURE__*/React__namespace.createElement("path", {
5620
+ fillRule: "evenodd",
5621
+ d: "M8 33v5a1 1 0 0 0 1 1h4v2H9a3 3 0 0 1-3-3v-5h2Zm18 6v2H15v-2h11Zm13 0v2H28v-2h11Zm9-6v5a3 3 0 0 1-3 3h-4v-2h4a1 1 0 0 0 .993-.883L46 38v-5h2ZM8 22v9H6v-9h2Zm40 0v9h-2v-9h2Zm-35-9v2H9a1 1 0 0 0-.993.883L8 16v4H6v-4a3 3 0 0 1 3-3h4Zm32 0a3 3 0 0 1 3 3v4h-2v-4a1 1 0 0 0-.883-.993L45 15h-4v-2h4Zm-6 0v2H28v-2h11Zm-13 0v2H15v-2h11Z"
5622
+ })));
5623
+ };
5624
+ var ColumnsIcon = SvgGroup;
4809
5625
 
5626
+ var _path$7;
4810
5627
  function _extends$7() { _extends$7 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$7.apply(this, arguments); }
4811
- var ColumnsIcon = (({
4812
- styles = {},
4813
- ...props
4814
- }) => /*#__PURE__*/React.createElement("svg", _extends$7({
4815
- xmlns: "http://www.w3.org/2000/svg",
4816
- width: "54",
4817
- height: "54"
4818
- }, props), /*#__PURE__*/React.createElement("path", {
4819
- fillRule: "evenodd",
4820
- d: "M8 33v5a1 1 0 001 1h4v2H9a3 3 0 01-3-3v-5h2zm18 6v2H15v-2h11zm13 0v2H28v-2h11zm9-6v5a3 3 0 01-3 3h-4v-2h4a1 1 0 00.993-.883L46 38v-5h2zM8 22v9H6v-9h2zm40 0v9h-2v-9h2zm-35-9v2H9a1 1 0 00-.993.883L8 16v4H6v-4a3 3 0 013-3h4zm32 0a3 3 0 013 3v4h-2v-4a1 1 0 00-.883-.993L45 15h-4v-2h4zm-6 0v2H28v-2h11zm-13 0v2H15v-2h11z"
4821
- })));
5628
+ var SvgNumber = function SvgNumber(props) {
5629
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$7({
5630
+ xmlns: "http://www.w3.org/2000/svg",
5631
+ width: 54,
5632
+ height: 54,
5633
+ fill: "currentcolor"
5634
+ }, props), _path$7 || (_path$7 = /*#__PURE__*/React__namespace.createElement("path", {
5635
+ fillRule: "evenodd",
5636
+ d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1zM35 28.444h7l-3.5 4-3.5-4zM35 26h7l-3.5-4-3.5 4z"
5637
+ })));
5638
+ };
5639
+ var NumberIcon = SvgNumber;
4822
5640
 
5641
+ var _path$6;
4823
5642
  function _extends$6() { _extends$6 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$6.apply(this, arguments); }
4824
- var NumberIcon = (({
4825
- styles = {},
4826
- ...props
4827
- }) => /*#__PURE__*/React.createElement("svg", _extends$6({
4828
- xmlns: "http://www.w3.org/2000/svg",
4829
- width: "54",
4830
- height: "54",
4831
- fill: "currentcolor"
4832
- }, props), /*#__PURE__*/React.createElement("path", {
4833
- fillRule: "evenodd",
4834
- d: "M45 16a3 3 0 013 3v16a3 3 0 01-3 3H9a3 3 0 01-3-3V19a3 3 0 013-3h36zm0 2H9a1 1 0 00-1 1v16a1 1 0 001 1h36a1 1 0 001-1V19a1 1 0 00-1-1zM35 28.444h7l-3.5 4-3.5-4zM35 26h7l-3.5-4-3.5 4z"
4835
- })));
5643
+ var SvgRadio = function SvgRadio(props) {
5644
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$6({
5645
+ xmlns: "http://www.w3.org/2000/svg",
5646
+ width: 54,
5647
+ height: 54,
5648
+ fill: "currentcolor"
5649
+ }, props), _path$6 || (_path$6 = /*#__PURE__*/React__namespace.createElement("path", {
5650
+ d: "M27 22c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10zm0 18a8 8 0 1 1 0-16 8 8 0 1 1 0 16z"
5651
+ })));
5652
+ };
5653
+ var RadioIcon = SvgRadio;
4836
5654
 
5655
+ var _path$5;
4837
5656
  function _extends$5() { _extends$5 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$5.apply(this, arguments); }
4838
- var RadioIcon = (({
4839
- styles = {},
4840
- ...props
4841
- }) => /*#__PURE__*/React.createElement("svg", _extends$5({
4842
- xmlns: "http://www.w3.org/2000/svg",
4843
- width: "54",
4844
- height: "54",
4845
- fill: "currentcolor"
4846
- }, props), /*#__PURE__*/React.createElement("path", {
4847
- d: "M27 22c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5c-5.52 0-10 4.48-10 10s4.48 10 10 10 10-4.48 10-10-4.48-10-10-10zm0 18a8 8 0 110-16 8 8 0 110 16z"
4848
- })));
5657
+ var SvgSelect = function SvgSelect(props) {
5658
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$5({
5659
+ xmlns: "http://www.w3.org/2000/svg",
5660
+ width: 54,
5661
+ height: 54,
5662
+ fill: "currentcolor"
5663
+ }, props), _path$5 || (_path$5 = /*#__PURE__*/React__namespace.createElement("path", {
5664
+ fillRule: "evenodd",
5665
+ d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1zm-12 7h9l-4.5 6-4.5-6z"
5666
+ })));
5667
+ };
5668
+ var SelectIcon = SvgSelect;
4849
5669
 
5670
+ var _path$4, _path2$1;
4850
5671
  function _extends$4() { _extends$4 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$4.apply(this, arguments); }
4851
- var SelectIcon = (({
4852
- styles = {},
4853
- ...props
4854
- }) => /*#__PURE__*/React.createElement("svg", _extends$4({
4855
- xmlns: "http://www.w3.org/2000/svg",
4856
- width: "54",
4857
- height: "54",
4858
- fill: "currentcolor"
4859
- }, props), /*#__PURE__*/React.createElement("path", {
4860
- fillRule: "evenodd",
4861
- d: "M45 16a3 3 0 013 3v16a3 3 0 01-3 3H9a3 3 0 01-3-3V19a3 3 0 013-3h36zm0 2H9a1 1 0 00-1 1v16a1 1 0 001 1h36a1 1 0 001-1V19a1 1 0 00-1-1zm-12 7h9l-4.5 6-4.5-6z"
4862
- })));
5672
+ var SvgSpacer = function SvgSpacer(props) {
5673
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$4({
5674
+ xmlns: "http://www.w3.org/2000/svg",
5675
+ width: 54,
5676
+ height: 54,
5677
+ fill: "none"
5678
+ }, props), _path$4 || (_path$4 = /*#__PURE__*/React__namespace.createElement("path", {
5679
+ stroke: "#000",
5680
+ strokeLinecap: "square",
5681
+ strokeWidth: 2,
5682
+ d: "M9 23h36M9 31h36"
5683
+ })), _path2$1 || (_path2$1 = /*#__PURE__*/React__namespace.createElement("path", {
5684
+ stroke: "#000",
5685
+ strokeLinecap: "round",
5686
+ strokeLinejoin: "round",
5687
+ strokeWidth: 2,
5688
+ d: "m23 17 4-4 4 4M31 37l-4 4-4-4"
5689
+ })));
5690
+ };
5691
+ var SpacerIcon = SvgSpacer;
4863
5692
 
5693
+ var _path$3;
4864
5694
  function _extends$3() { _extends$3 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$3.apply(this, arguments); }
4865
- var TextIcon = (({
4866
- styles = {},
4867
- ...props
4868
- }) => /*#__PURE__*/React.createElement("svg", _extends$3({
4869
- xmlns: "http://www.w3.org/2000/svg",
4870
- width: "54",
4871
- height: "54",
4872
- fill: "currentcolor"
4873
- }, props), /*#__PURE__*/React.createElement("path", {
4874
- d: "M20.58 33.77h-3l-1.18-3.08H11l-1.1 3.08H7l5.27-13.54h2.89zm-5-5.36l-1.86-5-1.83 5zM22 20.23h5.41a15.47 15.47 0 012.4.14 3.42 3.42 0 011.41.55 3.47 3.47 0 011 1.14 3 3 0 01.42 1.58 3.26 3.26 0 01-1.91 2.94 3.63 3.63 0 011.91 1.22 3.28 3.28 0 01.66 2 4 4 0 01-.43 1.8 3.63 3.63 0 01-1.09 1.4 3.89 3.89 0 01-1.83.65q-.69.07-3.3.09H22zm2.73 2.25v3.13h3.8a1.79 1.79 0 001.1-.49 1.41 1.41 0 00.41-1 1.49 1.49 0 00-.35-1 1.54 1.54 0 00-1-.48c-.27 0-1.05-.05-2.34-.05zm0 5.39v3.62h2.57a11.52 11.52 0 001.88-.09 1.65 1.65 0 001-.54 1.6 1.6 0 00.38-1.14 1.75 1.75 0 00-.29-1 1.69 1.69 0 00-.86-.62 9.28 9.28 0 00-2.41-.23zm19.62.92l2.65.84a5.94 5.94 0 01-2 3.29A5.74 5.74 0 0141.38 34a5.87 5.87 0 01-4.44-1.84 7.09 7.09 0 01-1.73-5A7.43 7.43 0 0137 21.87 6 6 0 0141.54 20a5.64 5.64 0 014 1.47A5.33 5.33 0 0147 24l-2.7.65a2.8 2.8 0 00-2.86-2.27A3.09 3.09 0 0039 23.42a5.31 5.31 0 00-.93 3.5 5.62 5.62 0 00.93 3.65 3 3 0 002.4 1.09 2.72 2.72 0 001.82-.66 4 4 0 001.13-2.21z"
4875
- })));
5695
+ var SvgText = function SvgText(props) {
5696
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$3({
5697
+ xmlns: "http://www.w3.org/2000/svg",
5698
+ width: 54,
5699
+ height: 54,
5700
+ fill: "currentcolor"
5701
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React__namespace.createElement("path", {
5702
+ d: "M20.58 33.77h-3l-1.18-3.08H11l-1.1 3.08H7l5.27-13.54h2.89zm-5-5.36-1.86-5-1.83 5zM22 20.23h5.41a15.47 15.47 0 0 1 2.4.14 3.42 3.42 0 0 1 1.41.55 3.47 3.47 0 0 1 1 1.14 3 3 0 0 1 .42 1.58 3.26 3.26 0 0 1-1.91 2.94 3.63 3.63 0 0 1 1.91 1.22 3.28 3.28 0 0 1 .66 2 4 4 0 0 1-.43 1.8 3.63 3.63 0 0 1-1.09 1.4 3.89 3.89 0 0 1-1.83.65q-.69.07-3.3.09H22zm2.73 2.25v3.13h3.8a1.79 1.79 0 0 0 1.1-.49 1.41 1.41 0 0 0 .41-1 1.49 1.49 0 0 0-.35-1 1.54 1.54 0 0 0-1-.48c-.27 0-1.05-.05-2.34-.05zm0 5.39v3.62h2.57a11.52 11.52 0 0 0 1.88-.09 1.65 1.65 0 0 0 1-.54 1.6 1.6 0 0 0 .38-1.14 1.75 1.75 0 0 0-.29-1 1.69 1.69 0 0 0-.86-.62 9.28 9.28 0 0 0-2.41-.23zm19.62.92 2.65.84a5.94 5.94 0 0 1-2 3.29A5.74 5.74 0 0 1 41.38 34a5.87 5.87 0 0 1-4.44-1.84 7.09 7.09 0 0 1-1.73-5A7.43 7.43 0 0 1 37 21.87 6 6 0 0 1 41.54 20a5.64 5.64 0 0 1 4 1.47A5.33 5.33 0 0 1 47 24l-2.7.65a2.8 2.8 0 0 0-2.86-2.27A3.09 3.09 0 0 0 39 23.42a5.31 5.31 0 0 0-.93 3.5 5.62 5.62 0 0 0 .93 3.65 3 3 0 0 0 2.4 1.09 2.72 2.72 0 0 0 1.82-.66 4 4 0 0 0 1.13-2.21z"
5703
+ })));
5704
+ };
5705
+ var TextIcon = SvgText;
4876
5706
 
5707
+ var _path$2;
4877
5708
  function _extends$2() { _extends$2 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$2.apply(this, arguments); }
4878
- var TextfieldIcon = (({
4879
- styles = {},
4880
- ...props
4881
- }) => /*#__PURE__*/React.createElement("svg", _extends$2({
4882
- xmlns: "http://www.w3.org/2000/svg",
4883
- width: "54",
4884
- height: "54",
4885
- fill: "currentcolor"
4886
- }, props), /*#__PURE__*/React.createElement("path", {
4887
- fillRule: "evenodd",
4888
- d: "M45 16a3 3 0 013 3v16a3 3 0 01-3 3H9a3 3 0 01-3-3V19a3 3 0 013-3h36zm0 2H9a1 1 0 00-1 1v16a1 1 0 001 1h36a1 1 0 001-1V19a1 1 0 00-1-1zm-32 4v10h-2V22h2z"
4889
- })));
5709
+ var SvgTextfield = function SvgTextfield(props) {
5710
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$2({
5711
+ xmlns: "http://www.w3.org/2000/svg",
5712
+ width: 54,
5713
+ height: 54,
5714
+ fill: "currentcolor"
5715
+ }, props), _path$2 || (_path$2 = /*#__PURE__*/React__namespace.createElement("path", {
5716
+ fillRule: "evenodd",
5717
+ d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1zm-32 4v10h-2V22h2z"
5718
+ })));
5719
+ };
5720
+ var TextfieldIcon = SvgTextfield;
4890
5721
 
5722
+ var _path$1;
4891
5723
  function _extends$1() { _extends$1 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$1.apply(this, arguments); }
4892
- var TextareaIcon = (({
4893
- styles = {},
4894
- ...props
4895
- }) => /*#__PURE__*/React.createElement("svg", _extends$1({
4896
- xmlns: "http://www.w3.org/2000/svg",
4897
- width: "54",
4898
- height: "54",
4899
- fill: "currentcolor"
4900
- }, props), /*#__PURE__*/React.createElement("path", {
4901
- fillRule: "evenodd",
4902
- d: "M45 13a3 3 0 013 3v22a3 3 0 01-3 3H9a3 3 0 01-3-3V16a3 3 0 013-3h36zm0 2H9a1 1 0 00-1 1v22a1 1 0 001 1h36a1 1 0 001-1V16a1 1 0 00-1-1zm-1.136 15.5l.849.849-6.364 6.364-.849-.849 6.364-6.364zm.264 3.5l.849.849-2.828 2.828-.849-.849L44.128 34zM13 19v10h-2V19h2z"
4903
- })));
5724
+ var SvgTextarea = function SvgTextarea(props) {
5725
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$1({
5726
+ xmlns: "http://www.w3.org/2000/svg",
5727
+ width: 54,
5728
+ height: 54,
5729
+ fill: "currentcolor"
5730
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React__namespace.createElement("path", {
5731
+ fillRule: "evenodd",
5732
+ d: "M45 13a3 3 0 0 1 3 3v22a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V16a3 3 0 0 1 3-3h36zm0 2H9a1 1 0 0 0-1 1v22a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V16a1 1 0 0 0-1-1zm-1.136 15.5.849.849-6.364 6.364-.849-.849 6.364-6.364zm.264 3.5.849.849-2.828 2.828-.849-.849L44.128 34zM13 19v10h-2V19h2z"
5733
+ })));
5734
+ };
5735
+ var TextareaIcon = SvgTextarea;
4904
5736
 
5737
+ var _path, _path2;
4905
5738
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
4906
- var ImageIcon = (({
4907
- styles = {},
4908
- ...props
4909
- }) => /*#__PURE__*/React.createElement("svg", _extends({
4910
- xmlns: "http://www.w3.org/2000/svg",
4911
- width: "54",
4912
- height: "54",
4913
- fill: "currentcolor"
4914
- }, props), /*#__PURE__*/React.createElement("path", {
4915
- fillRule: "evenodd",
4916
- d: "M34.636 21.91A3.818 3.818 0 1127 21.908a3.818 3.818 0 017.636 0zm-2 0A1.818 1.818 0 1129 21.908a1.818 1.818 0 013.636 0z",
4917
- clipRule: "evenodd"
4918
- }), /*#__PURE__*/React.createElement("path", {
4919
- fillRule: "evenodd",
4920
- d: "M15 13a2 2 0 00-2 2v24a2 2 0 002 2h24a2 2 0 002-2V15a2 2 0 00-2-2H15zm24 2H15v12.45l4.71-4.709a1.91 1.91 0 012.702 0l6.695 6.695 2.656-1.77a1.91 1.91 0 012.411.239L39 32.73V15zM15 39v-8.754a.975.975 0 00.168-.135l5.893-5.893 6.684 6.685a1.911 1.911 0 002.41.238l2.657-1.77 6.02 6.02c.052.051.108.097.168.135V39H15z",
4921
- clipRule: "evenodd"
4922
- })));
5739
+ var SvgImage = function SvgImage(props) {
5740
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends({
5741
+ xmlns: "http://www.w3.org/2000/svg",
5742
+ width: 54,
5743
+ height: 54,
5744
+ fill: "currentcolor"
5745
+ }, props), _path || (_path = /*#__PURE__*/React__namespace.createElement("path", {
5746
+ fillRule: "evenodd",
5747
+ d: "M34.636 21.91A3.818 3.818 0 1 1 27 21.908a3.818 3.818 0 0 1 7.636 0Zm-2 0A1.818 1.818 0 1 1 29 21.908a1.818 1.818 0 0 1 3.636 0Z",
5748
+ clipRule: "evenodd"
5749
+ })), _path2 || (_path2 = /*#__PURE__*/React__namespace.createElement("path", {
5750
+ fillRule: "evenodd",
5751
+ d: "M15 13a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V15a2 2 0 0 0-2-2H15Zm24 2H15v12.45l4.71-4.709a1.91 1.91 0 0 1 2.702 0l6.695 6.695 2.656-1.77a1.91 1.91 0 0 1 2.411.239L39 32.73V15ZM15 39v-8.754a.975.975 0 0 0 .168-.135l5.893-5.893 6.684 6.685a1.911 1.911 0 0 0 2.41.238l2.657-1.77 6.02 6.02c.052.051.108.097.168.135V39H15Z",
5752
+ clipRule: "evenodd"
5753
+ })));
5754
+ };
5755
+ var ImageIcon = SvgImage;
4923
5756
 
4924
5757
  const iconsByType = type => {
4925
5758
  return {
@@ -4932,6 +5765,7 @@ const iconsByType = type => {
4932
5765
  number: NumberIcon,
4933
5766
  radio: RadioIcon,
4934
5767
  select: SelectIcon,
5768
+ spacer: SpacerIcon,
4935
5769
  taglist: TaglistIcon,
4936
5770
  text: TextIcon,
4937
5771
  textfield: TextfieldIcon,
@@ -4940,7 +5774,7 @@ const iconsByType = type => {
4940
5774
  }[type];
4941
5775
  };
4942
5776
 
4943
- const formFields = [Button, Checkbox, Checklist, Default, Image, Numberfield, Datetime, Radio, Select, Taglist, Text, Textfield, Textarea];
5777
+ const formFields = [Button, Checkbox, Checklist, Default, Image, Numberfield, Datetime, Radio, Select, Spacer, Taglist, Text, Textfield, Textarea];
4944
5778
 
4945
5779
  class FormFields {
4946
5780
  constructor() {
@@ -5351,7 +6185,7 @@ class Form {
5351
6185
  * @internal
5352
6186
  */
5353
6187
  _getModules() {
5354
- return [ExpressionLanguageModule, MarkdownModule];
6188
+ return [ExpressionLanguageModule, MarkdownModule, ViewerCommandsModule];
5355
6189
  }
5356
6190
 
5357
6191
  /**
@@ -5396,7 +6230,7 @@ class Form {
5396
6230
  }
5397
6231
  }
5398
6232
 
5399
- const schemaVersion = 9;
6233
+ const schemaVersion = 10;
5400
6234
 
5401
6235
  /**
5402
6236
  * @typedef { import('./types').CreateFormOptions } CreateFormOptions
@@ -5449,6 +6283,7 @@ exports.MarkdownRenderer = MarkdownRenderer;
5449
6283
  exports.Numberfield = Numberfield;
5450
6284
  exports.Radio = Radio;
5451
6285
  exports.Select = Select;
6286
+ exports.Spacer = Spacer;
5452
6287
  exports.TIME_INTERVAL_PATH = TIME_INTERVAL_PATH;
5453
6288
  exports.TIME_LABEL_PATH = TIME_LABEL_PATH;
5454
6289
  exports.TIME_SERIALISINGFORMAT_LABELS = TIME_SERIALISINGFORMAT_LABELS;
@@ -5464,6 +6299,8 @@ exports.VALUES_SOURCES_DEFAULTS = VALUES_SOURCES_DEFAULTS;
5464
6299
  exports.VALUES_SOURCES_LABELS = VALUES_SOURCES_LABELS;
5465
6300
  exports.VALUES_SOURCES_PATHS = VALUES_SOURCES_PATHS;
5466
6301
  exports.VALUES_SOURCE_DEFAULT = VALUES_SOURCE_DEFAULT;
6302
+ exports.ViewerCommands = ViewerCommands;
6303
+ exports.ViewerCommandsModule = ViewerCommandsModule;
5467
6304
  exports.clone = clone;
5468
6305
  exports.createForm = createForm;
5469
6306
  exports.createFormContainer = createFormContainer;