@bpmn-io/form-js-viewer 1.0.0 → 1.3.0-alpha.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.
Files changed (38) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +164 -164
  3. package/dist/assets/form-js-base.css +985 -917
  4. package/dist/assets/form-js.css +69 -1
  5. package/dist/index.cjs +1982 -859
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.es.js +1977 -860
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/types/Form.d.ts +12 -26
  10. package/dist/types/core/FieldFactory.d.ts +19 -0
  11. package/dist/types/core/FormFieldRegistry.d.ts +0 -1
  12. package/dist/types/core/Importer.d.ts +56 -0
  13. package/dist/types/core/PathRegistry.d.ts +71 -0
  14. package/dist/types/core/index.d.ts +9 -5
  15. package/dist/types/features/expression-language/ConditionChecker.d.ts +3 -2
  16. package/dist/types/features/expression-language/variableExtractionHelpers.d.ts +1 -1
  17. package/dist/types/features/index.d.ts +2 -0
  18. package/dist/types/features/viewerCommands/ViewerCommands.d.ts +14 -0
  19. package/dist/types/features/viewerCommands/cmd/UpdateFieldValidationHandler.d.ts +11 -0
  20. package/dist/types/features/viewerCommands/index.d.ts +8 -0
  21. package/dist/types/index.d.ts +2 -2
  22. package/dist/types/render/components/form-fields/Checklist.d.ts +2 -6
  23. package/dist/types/render/components/form-fields/Default.d.ts +3 -3
  24. package/dist/types/render/components/form-fields/Group.d.ts +14 -0
  25. package/dist/types/render/components/form-fields/Radio.d.ts +2 -6
  26. package/dist/types/render/components/form-fields/Select.d.ts +2 -6
  27. package/dist/types/render/components/form-fields/Spacer.d.ts +14 -0
  28. package/dist/types/render/components/form-fields/Taglist.d.ts +2 -6
  29. package/dist/types/render/components/form-fields/parts/Grid.d.ts +1 -0
  30. package/dist/types/render/components/index.d.ts +4 -2
  31. package/dist/types/render/components/util/localisationUtil.d.ts +24 -0
  32. package/dist/types/render/components/util/valuesUtil.d.ts +6 -0
  33. package/dist/types/render/context/FormRenderContext.d.ts +3 -0
  34. package/dist/types/types.d.ts +35 -35
  35. package/dist/types/util/index.d.ts +2 -5
  36. package/package.json +5 -4
  37. package/dist/types/import/Importer.d.ts +0 -45
  38. package/dist/types/import/index.d.ts +0 -5
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,6 +13,7 @@ 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
18
  function _interopNamespaceDefault(e) {
19
19
  var n = Object.create(null);
@@ -34,15 +34,15 @@ function _interopNamespaceDefault(e) {
34
34
 
35
35
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
36
36
 
37
- const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) => {
37
+ const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', options = {}) => {
38
38
  const {
39
39
  depth = 0,
40
40
  specialDepthAccessors = {}
41
41
  } = options;
42
42
  if (!['expression', 'unaryTest'].includes(feelFlavour)) return [];
43
- const tree = feelFlavour === 'expression' ? feelin.parseExpressions(feelString) : feelin.parseUnaryTests(feelString);
43
+ const tree = feelFlavour === 'expression' ? feelin.parseExpression(feelString) : feelin.parseUnaryTests(feelString);
44
44
  const simpleExpressionTree = _buildSimpleFeelStructureTree(tree, feelString);
45
- return function _unfoldVariables(node) {
45
+ const variables = function _unfoldVariables(node) {
46
46
  if (node.name === 'PathExpression') {
47
47
  if (Object.keys(specialDepthAccessors).length === 0) {
48
48
  return depth === 0 ? [_getVariableNameAtPathIndex(node, 0)] : [];
@@ -55,34 +55,38 @@ const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) =>
55
55
 
56
56
  // for any other kind of node, traverse its children and flatten the result
57
57
  if (node.children) {
58
- return node.children.reduce((acc, child) => {
58
+ const variables = node.children.reduce((acc, child) => {
59
59
  return acc.concat(_unfoldVariables(child));
60
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;
61
64
  }
62
65
  return [];
63
66
  }(simpleExpressionTree);
67
+ return [...new Set(variables)];
64
68
  };
65
69
 
66
- /**
67
- * Get the variable name at the specified index in a given path expression.
68
- *
69
- * @param {Object} root - The root node of the path expression tree.
70
- * @param {number} index - The index of the variable name to retrieve.
71
- * @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.
72
76
  */
73
77
  const _getVariableNameAtPathIndex = (root, index) => {
74
78
  const accessors = _deconstructPathExpression(root);
75
79
  return accessors[index] || null;
76
80
  };
77
81
 
78
- /**
79
- * Extracts the variables which are required of the external context for a given path expression.
80
- * This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
81
- *
82
- * @param {Object} node - The root node of the path expression tree.
83
- * @param {number} initialDepth - The depth at which the root node is located in the outer context.
84
- * @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
85
- * @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.
86
90
  */
87
91
  const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
88
92
  // depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
@@ -128,11 +132,11 @@ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) =
128
132
  return new Set(extractedVariables);
129
133
  };
130
134
 
131
- /**
132
- * Deconstructs a path expression tree into an array of components.
133
- *
134
- * @param {Object} root - The root node of the path expression tree.
135
- * @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.
136
140
  */
137
141
  const _deconstructPathExpression = root => {
138
142
  let node = root;
@@ -151,13 +155,13 @@ const _deconstructPathExpression = root => {
151
155
  return parts.reverse();
152
156
  };
153
157
 
154
- /**
155
- * Builds a simplified feel structure tree from the given parse tree and feel string.
156
- * The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
157
- *
158
- * @param {Object} parseTree - The parse tree generated by a parser.
159
- * @param {string} feelString - The feel string used for parsing.
160
- * @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.
161
165
  */
162
166
  const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
163
167
  const stack = [{
@@ -180,7 +184,45 @@ const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
180
184
  parent.children.push(result);
181
185
  }
182
186
  });
183
- 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;
184
226
  };
185
227
 
186
228
  class FeelExpressionLanguage {
@@ -188,25 +230,25 @@ class FeelExpressionLanguage {
188
230
  this._eventBus = eventBus;
189
231
  }
190
232
 
191
- /**
192
- * Determines if the given value is a FEEL expression.
193
- *
194
- * @param {any} value
195
- * @returns {boolean}
196
- *
233
+ /**
234
+ * Determines if the given value is a FEEL expression.
235
+ *
236
+ * @param {any} value
237
+ * @returns {boolean}
238
+ *
197
239
  */
198
240
  isExpression(value) {
199
241
  return minDash.isString(value) && value.startsWith('=');
200
242
  }
201
243
 
202
- /**
203
- * Retrieve variable names from a given FEEL expression.
204
- *
205
- * @param {string} expression
206
- * @param {object} [options]
207
- * @param {string} [options.type]
208
- *
209
- * @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[]}
210
252
  */
211
253
  getVariableNames(expression, options = {}) {
212
254
  const {
@@ -221,13 +263,13 @@ class FeelExpressionLanguage {
221
263
  return getFlavouredFeelVariableNames(expression, type);
222
264
  }
223
265
 
224
- /**
225
- * Evaluate an expression.
226
- *
227
- * @param {string} expression
228
- * @param {import('../../types').Data} [data]
229
- *
230
- * @returns {any}
266
+ /**
267
+ * Evaluate an expression.
268
+ *
269
+ * @param {string} expression
270
+ * @param {import('../../types').Data} [data]
271
+ *
272
+ * @returns {any}
231
273
  */
232
274
  evaluate(expression, data = {}) {
233
275
  if (!expression) {
@@ -252,23 +294,23 @@ FeelExpressionLanguage.$inject = ['eventBus'];
252
294
  class FeelersTemplating {
253
295
  constructor() {}
254
296
 
255
- /**
256
- * Determines if the given value is a feelers template.
257
- *
258
- * @param {any} value
259
- * @returns {boolean}
260
- *
297
+ /**
298
+ * Determines if the given value is a feelers template.
299
+ *
300
+ * @param {any} value
301
+ * @returns {boolean}
302
+ *
261
303
  */
262
304
  isTemplate(value) {
263
305
  return minDash.isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
264
306
  }
265
307
 
266
- /**
267
- * Retrieve variable names from a given feelers template.
268
- *
269
- * @param {string} template
270
- *
271
- * @returns {string[]}
308
+ /**
309
+ * Retrieve variable names from a given feelers template.
310
+ *
311
+ * @param {string} template
312
+ *
313
+ * @returns {string[]}
272
314
  */
273
315
  getVariableNames(template) {
274
316
  if (!this.isTemplate(template)) {
@@ -294,17 +336,17 @@ class FeelersTemplating {
294
336
  }, []);
295
337
  }
296
338
 
297
- /**
298
- * Evaluate a template.
299
- *
300
- * @param {string} template
301
- * @param {Object<string, any>} context
302
- * @param {Object} options
303
- * @param {boolean} [options.debug = false]
304
- * @param {boolean} [options.strict = false]
305
- * @param {Function} [options.buildDebugString]
306
- *
307
- * @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
308
350
  */
309
351
  evaluate(template, context = {}, options = {}) {
310
352
  const {
@@ -319,22 +361,22 @@ class FeelersTemplating {
319
361
  });
320
362
  }
321
363
 
322
- /**
323
- * @typedef {Object} ExpressionWithDepth
324
- * @property {number} depth - The depth of the expression in the syntax tree.
325
- * @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
326
368
  */
327
369
 
328
- /**
329
- * Extracts all feel expressions in the template along with their depth in the syntax tree.
330
- * The depth is incremented for child expressions of loops to account for context drilling.
331
- * @name extractExpressionsWithDepth
332
- * @param {string} template - A feelers template string.
333
- * @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
334
- *
335
- * @example
336
- * const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
337
- * 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);
338
380
  */
339
381
  _extractExpressionsWithDepth(template) {
340
382
  // build simplified feelers syntax tree
@@ -365,47 +407,271 @@ class FeelersTemplating {
365
407
  }
366
408
  FeelersTemplating.$inject = [];
367
409
 
368
- /**
369
- * @typedef {object} Condition
370
- * @property {string} [hide]
410
+ // config ///////////////////
411
+
412
+ const MINUTES_IN_DAY = 60 * 24;
413
+ const DATETIME_SUBTYPES = {
414
+ DATE: 'date',
415
+ TIME: 'time',
416
+ DATETIME: 'datetime'
417
+ };
418
+ const TIME_SERIALISING_FORMATS = {
419
+ UTC_OFFSET: 'utc_offset',
420
+ UTC_NORMALIZED: 'utc_normalized',
421
+ NO_TIMEZONE: 'no_timezone'
422
+ };
423
+ const DATETIME_SUBTYPES_LABELS = {
424
+ [DATETIME_SUBTYPES.DATE]: 'Date',
425
+ [DATETIME_SUBTYPES.TIME]: 'Time',
426
+ [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
427
+ };
428
+ const TIME_SERIALISINGFORMAT_LABELS = {
429
+ [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
430
+ [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
431
+ [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
432
+ };
433
+ const DATETIME_SUBTYPE_PATH = ['subtype'];
434
+ const DATE_LABEL_PATH = ['dateLabel'];
435
+ const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
436
+ const TIME_LABEL_PATH = ['timeLabel'];
437
+ const TIME_USE24H_PATH = ['use24h'];
438
+ const TIME_INTERVAL_PATH = ['timeInterval'];
439
+ const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
440
+
441
+ // config ///////////////////
442
+
443
+ const VALUES_SOURCES = {
444
+ STATIC: 'static',
445
+ INPUT: 'input',
446
+ EXPRESSION: 'expression'
447
+ };
448
+ const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
449
+ const VALUES_SOURCES_LABELS = {
450
+ [VALUES_SOURCES.STATIC]: 'Static',
451
+ [VALUES_SOURCES.INPUT]: 'Input data',
452
+ [VALUES_SOURCES.EXPRESSION]: 'Expression'
453
+ };
454
+ const VALUES_SOURCES_PATHS = {
455
+ [VALUES_SOURCES.STATIC]: ['values'],
456
+ [VALUES_SOURCES.INPUT]: ['valuesKey'],
457
+ [VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
458
+ };
459
+ const VALUES_SOURCES_DEFAULTS = {
460
+ [VALUES_SOURCES.STATIC]: [{
461
+ label: 'Value',
462
+ value: 'value'
463
+ }],
464
+ [VALUES_SOURCES.INPUT]: '',
465
+ [VALUES_SOURCES.EXPRESSION]: '='
466
+ };
467
+
468
+ // helpers ///////////////////
469
+
470
+ function getValuesSource(field) {
471
+ for (const source of Object.values(VALUES_SOURCES)) {
472
+ if (minDash.get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
473
+ return source;
474
+ }
475
+ }
476
+ return VALUES_SOURCE_DEFAULT;
477
+ }
478
+
479
+ function createInjector(bootstrapModules) {
480
+ const injector = new didi.Injector(bootstrapModules);
481
+ injector.init();
482
+ return injector;
483
+ }
484
+
485
+ /**
486
+ * @param {string?} prefix
487
+ *
488
+ * @returns Element
489
+ */
490
+ function createFormContainer(prefix = 'fjs') {
491
+ const container = document.createElement('div');
492
+ container.classList.add(`${prefix}-container`);
493
+ return container;
494
+ }
495
+
496
+ const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
497
+ const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
498
+ function isRequired(field) {
499
+ return field.required;
500
+ }
501
+ function pathParse(path) {
502
+ if (!path) {
503
+ return [];
504
+ }
505
+ return path.split('.').map(key => {
506
+ return isNaN(parseInt(key)) ? key : parseInt(key);
507
+ });
508
+ }
509
+ function pathsEqual(a, b) {
510
+ return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
511
+ }
512
+ const indices = {};
513
+ function generateIndexForType(type) {
514
+ if (type in indices) {
515
+ indices[type]++;
516
+ } else {
517
+ indices[type] = 1;
518
+ }
519
+ return indices[type];
520
+ }
521
+ function generateIdForType(type) {
522
+ return `${type}${generateIndexForType(type)}`;
523
+ }
524
+
525
+ /**
526
+ * @template T
527
+ * @param {T} data
528
+ * @param {(this: any, key: string, value: any) => any} [replacer]
529
+ * @return {T}
530
+ */
531
+ function clone(data, replacer) {
532
+ return JSON.parse(JSON.stringify(data, replacer));
533
+ }
534
+
535
+ /**
536
+ * Parse the schema for input variables a form might make use of
537
+ *
538
+ * @param {any} schema
539
+ *
540
+ * @return {string[]}
541
+ */
542
+ function getSchemaVariables(schema, options = {}) {
543
+ const {
544
+ expressionLanguage = new FeelExpressionLanguage(null),
545
+ templating = new FeelersTemplating(),
546
+ inputs = true,
547
+ outputs = true
548
+ } = options;
549
+ if (!schema.components) {
550
+ return [];
551
+ }
552
+ const getAllComponents = node => {
553
+ const components = [];
554
+ if (node.components) {
555
+ node.components.forEach(component => {
556
+ components.push(component);
557
+ components.push(...getAllComponents(component));
558
+ });
559
+ }
560
+ return components;
561
+ };
562
+ const variables = getAllComponents(schema).reduce((variables, component) => {
563
+ const {
564
+ valuesKey
565
+ } = component;
566
+
567
+ // collect input-only variables
568
+ if (inputs) {
569
+ if (valuesKey) {
570
+ variables = [...variables, valuesKey];
571
+ }
572
+ EXPRESSION_PROPERTIES.forEach(prop => {
573
+ const property = minDash.get(component, prop.split('.'));
574
+ if (property && expressionLanguage.isExpression(property)) {
575
+ const expressionVariables = expressionLanguage.getVariableNames(property, {
576
+ type: 'expression'
577
+ });
578
+ variables = [...variables, ...expressionVariables];
579
+ }
580
+ });
581
+ TEMPLATE_PROPERTIES.forEach(prop => {
582
+ const property = minDash.get(component, prop.split('.'));
583
+ if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
584
+ const templateVariables = templating.getVariableNames(property);
585
+ variables = [...variables, ...templateVariables];
586
+ }
587
+ });
588
+ }
589
+ return variables.filter(variable => variable !== undefined || variable !== null);
590
+ }, []);
591
+ const getBindingVariables = node => {
592
+ const bindingVariable = [];
593
+
594
+ // c.f. https://github.com/bpmn-io/form-js/issues/778 @Skaiir to remove?
595
+ if (node.type === 'button') {
596
+ return [];
597
+ } else if (node.key) {
598
+ return [node.key.split('.')[0]];
599
+ } else if (node.path) {
600
+ return [node.path.split('.')[0]];
601
+ } else if (node.components) {
602
+ node.components.forEach(component => {
603
+ bindingVariable.push(...getBindingVariables(component));
604
+ });
605
+ }
606
+ return bindingVariable;
607
+ };
608
+
609
+ // collect binding variables
610
+ if (inputs || outputs) {
611
+ variables.push(...getBindingVariables(schema));
612
+ }
613
+
614
+ // remove duplicates
615
+ return Array.from(new Set(variables));
616
+ }
617
+ function runRecursively(formField, fn) {
618
+ const components = formField.components || [];
619
+ components.forEach((component, index) => {
620
+ runRecursively(component, fn);
621
+ });
622
+ fn(formField);
623
+ }
624
+
625
+ /**
626
+ * @typedef {object} Condition
627
+ * @property {string} [hide]
371
628
  */
372
629
 
373
630
  class ConditionChecker {
374
- constructor(formFieldRegistry, eventBus) {
631
+ constructor(formFieldRegistry, pathRegistry, eventBus) {
375
632
  this._formFieldRegistry = formFieldRegistry;
633
+ this._pathRegistry = pathRegistry;
376
634
  this._eventBus = eventBus;
377
635
  }
378
636
 
379
- /**
380
- * For given data, remove properties based on condition.
381
- *
382
- * @param {Object<string, any>} properties
383
- * @param {Object<string, any>} data
637
+ /**
638
+ * For given data, remove properties based on condition.
639
+ *
640
+ * @param {Object<string, any>} properties
641
+ * @param {Object<string, any>} data
384
642
  */
385
643
  applyConditions(properties, data = {}) {
386
- const conditions = this._getConditions();
387
- const newProperties = {
388
- ...properties
389
- };
390
- for (const {
391
- key,
392
- condition
393
- } of conditions) {
394
- const shouldRemove = this._checkHideCondition(condition, data);
395
- if (shouldRemove) {
396
- delete newProperties[key];
397
- }
644
+ const newProperties = clone(properties);
645
+ const form = this._formFieldRegistry.getAll().find(field => field.type === 'default');
646
+ if (!form) {
647
+ throw new Error('form field registry has no form');
398
648
  }
649
+ this._pathRegistry.executeRecursivelyOnFields(form, ({
650
+ field,
651
+ isClosed,
652
+ context
653
+ }) => {
654
+ const {
655
+ conditional: condition
656
+ } = field;
657
+ context.isHidden = context.isHidden || condition && this._checkHideCondition(condition, data);
658
+
659
+ // only clear the leaf nodes, as groups may both point to the same path
660
+ if (context.isHidden && isClosed) {
661
+ const valuePath = this._pathRegistry.getValuePath(field);
662
+ this._clearObjectValueRecursively(valuePath, newProperties);
663
+ }
664
+ });
399
665
  return newProperties;
400
666
  }
401
667
 
402
- /**
403
- * Check if given condition is met. Returns null for invalid/missing conditions.
404
- *
405
- * @param {string} condition
406
- * @param {import('../../types').Data} [data]
407
- *
408
- * @returns {boolean|null}
668
+ /**
669
+ * Check if given condition is met. Returns null for invalid/missing conditions.
670
+ *
671
+ * @param {string} condition
672
+ * @param {import('../../types').Data} [data]
673
+ *
674
+ * @returns {boolean|null}
409
675
  */
410
676
  check(condition, data = {}) {
411
677
  if (!condition) {
@@ -426,12 +692,12 @@ class ConditionChecker {
426
692
  }
427
693
  }
428
694
 
429
- /**
430
- * Check if hide condition is met.
431
- *
432
- * @param {Condition} condition
433
- * @param {Object<string, any>} data
434
- * @returns {boolean}
695
+ /**
696
+ * Check if hide condition is met.
697
+ *
698
+ * @param {Condition} condition
699
+ * @param {Object<string, any>} data
700
+ * @returns {boolean}
435
701
  */
436
702
  _checkHideCondition(condition, data) {
437
703
  if (!condition.hide) {
@@ -440,24 +706,18 @@ class ConditionChecker {
440
706
  const result = this.check(condition.hide, data);
441
707
  return result === true;
442
708
  }
443
- _getConditions() {
444
- const formFields = this._formFieldRegistry.getAll();
445
- return formFields.reduce((conditions, formField) => {
446
- const {
447
- key,
448
- conditional: condition
449
- } = formField;
450
- if (key && condition) {
451
- return [...conditions, {
452
- key,
453
- condition
454
- }];
455
- }
456
- return conditions;
457
- }, []);
709
+ _clearObjectValueRecursively(valuePath, obj) {
710
+ const workingValuePath = [...valuePath];
711
+ let recurse = false;
712
+ do {
713
+ minDash.set(obj, workingValuePath, undefined);
714
+ workingValuePath.pop();
715
+ const parentObject = minDash.get(obj, workingValuePath);
716
+ recurse = minDash.isObject(parentObject) && !minDash.values(parentObject).length && !!workingValuePath.length;
717
+ } while (recurse);
458
718
  }
459
719
  }
460
- ConditionChecker.$inject = ['formFieldRegistry', 'eventBus'];
720
+ ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'eventBus'];
461
721
 
462
722
  var ExpressionLanguageModule = {
463
723
  __init__: ['expressionLanguage', 'templating', 'conditionChecker'],
@@ -473,12 +733,12 @@ class MarkdownRenderer {
473
733
  this._converter = new showdown.Converter();
474
734
  }
475
735
 
476
- /**
477
- * Render markdown to HTML.
478
- *
479
- * @param {string} markdown - The markdown to render
480
- *
481
- * @returns {string} HTML
736
+ /**
737
+ * Render markdown to HTML.
738
+ *
739
+ * @param {string} markdown - The markdown to render
740
+ *
741
+ * @returns {string} HTML
482
742
  */
483
743
  render(markdown) {
484
744
  return this._converter.makeHtml(markdown);
@@ -491,6 +751,538 @@ var MarkdownModule = {
491
751
  markdownRenderer: ['type', MarkdownRenderer]
492
752
  };
493
753
 
754
+ /**
755
+ * @typedef {import('didi').Injector} Injector
756
+ *
757
+ * @typedef {import('../core/Types').ElementLike} ElementLike
758
+ *
759
+ * @typedef {import('../core/EventBus').default} EventBus
760
+ * @typedef {import('./CommandHandler').default} CommandHandler
761
+ *
762
+ * @typedef { any } CommandContext
763
+ * @typedef { {
764
+ * new (...args: any[]) : CommandHandler
765
+ * } } CommandHandlerConstructor
766
+ * @typedef { {
767
+ * [key: string]: CommandHandler;
768
+ * } } CommandHandlerMap
769
+ * @typedef { {
770
+ * command: string;
771
+ * context: any;
772
+ * id?: any;
773
+ * } } CommandStackAction
774
+ * @typedef { {
775
+ * actions: CommandStackAction[];
776
+ * dirty: ElementLike[];
777
+ * trigger: 'execute' | 'undo' | 'redo' | 'clear' | null;
778
+ * atomic?: boolean;
779
+ * } } CurrentExecution
780
+ */
781
+
782
+ /**
783
+ * A service that offers un- and redoable execution of commands.
784
+ *
785
+ * The command stack is responsible for executing modeling actions
786
+ * in a un- and redoable manner. To do this it delegates the actual
787
+ * command execution to {@link CommandHandler}s.
788
+ *
789
+ * Command handlers provide {@link CommandHandler#execute(ctx)} and
790
+ * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
791
+ * identified by a command context.
792
+ *
793
+ *
794
+ * ## Life-Cycle events
795
+ *
796
+ * In the process the command stack fires a number of life-cycle events
797
+ * that other components to participate in the command execution.
798
+ *
799
+ * * preExecute
800
+ * * preExecuted
801
+ * * execute
802
+ * * executed
803
+ * * postExecute
804
+ * * postExecuted
805
+ * * revert
806
+ * * reverted
807
+ *
808
+ * A special event is used for validating, whether a command can be
809
+ * performed prior to its execution.
810
+ *
811
+ * * canExecute
812
+ *
813
+ * Each of the events is fired as `commandStack.{eventName}` and
814
+ * `commandStack.{commandName}.{eventName}`, respectively. This gives
815
+ * components fine grained control on where to hook into.
816
+ *
817
+ * The event object fired transports `command`, the name of the
818
+ * command and `context`, the command context.
819
+ *
820
+ *
821
+ * ## Creating Command Handlers
822
+ *
823
+ * Command handlers should provide the {@link CommandHandler#execute(ctx)}
824
+ * and {@link CommandHandler#revert(ctx)} methods to implement
825
+ * redoing and undoing of a command.
826
+ *
827
+ * A command handler _must_ ensure undo is performed properly in order
828
+ * not to break the undo chain. It must also return the shapes that
829
+ * got changed during the `execute` and `revert` operations.
830
+ *
831
+ * Command handlers may execute other modeling operations (and thus
832
+ * commands) in their `preExecute(d)` and `postExecute(d)` phases. The command
833
+ * stack will properly group all commands together into a logical unit
834
+ * that may be re- and undone atomically.
835
+ *
836
+ * Command handlers must not execute other commands from within their
837
+ * core implementation (`execute`, `revert`).
838
+ *
839
+ *
840
+ * ## Change Tracking
841
+ *
842
+ * During the execution of the CommandStack it will keep track of all
843
+ * elements that have been touched during the command's execution.
844
+ *
845
+ * At the end of the CommandStack execution it will notify interested
846
+ * components via an 'elements.changed' event with all the dirty
847
+ * elements.
848
+ *
849
+ * The event can be picked up by components that are interested in the fact
850
+ * that elements have been changed. One use case for this is updating
851
+ * their graphical representation after moving / resizing or deletion.
852
+ *
853
+ * @see CommandHandler
854
+ *
855
+ * @param {EventBus} eventBus
856
+ * @param {Injector} injector
857
+ */
858
+ function CommandStack(eventBus, injector) {
859
+ /**
860
+ * A map of all registered command handlers.
861
+ *
862
+ * @type {CommandHandlerMap}
863
+ */
864
+ this._handlerMap = {};
865
+
866
+ /**
867
+ * A stack containing all re/undoable actions on the diagram
868
+ *
869
+ * @type {CommandStackAction[]}
870
+ */
871
+ this._stack = [];
872
+
873
+ /**
874
+ * The current index on the stack
875
+ *
876
+ * @type {number}
877
+ */
878
+ this._stackIdx = -1;
879
+
880
+ /**
881
+ * Current active commandStack execution
882
+ *
883
+ * @type {CurrentExecution}
884
+ */
885
+ this._currentExecution = {
886
+ actions: [],
887
+ dirty: [],
888
+ trigger: null
889
+ };
890
+
891
+ /**
892
+ * @type {Injector}
893
+ */
894
+ this._injector = injector;
895
+
896
+ /**
897
+ * @type EventBus
898
+ */
899
+ this._eventBus = eventBus;
900
+
901
+ /**
902
+ * @type { number }
903
+ */
904
+ this._uid = 1;
905
+ eventBus.on(['diagram.destroy', 'diagram.clear'], function () {
906
+ this.clear(false);
907
+ }, this);
908
+ }
909
+ CommandStack.$inject = ['eventBus', 'injector'];
910
+
911
+ /**
912
+ * Execute a command.
913
+ *
914
+ * @param {string} command The command to execute.
915
+ * @param {CommandContext} context The context with which to execute the command.
916
+ */
917
+ CommandStack.prototype.execute = function (command, context) {
918
+ if (!command) {
919
+ throw new Error('command required');
920
+ }
921
+ this._currentExecution.trigger = 'execute';
922
+ const action = {
923
+ command: command,
924
+ context: context
925
+ };
926
+ this._pushAction(action);
927
+ this._internalExecute(action);
928
+ this._popAction();
929
+ };
930
+
931
+ /**
932
+ * Check whether a command can be executed.
933
+ *
934
+ * Implementors may hook into the mechanism on two ways:
935
+ *
936
+ * * in event listeners:
937
+ *
938
+ * Users may prevent the execution via an event listener.
939
+ * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
940
+ *
941
+ * * in command handlers:
942
+ *
943
+ * If the method {@link CommandHandler#canExecute} is implemented in a handler
944
+ * it will be called to figure out whether the execution is allowed.
945
+ *
946
+ * @param {string} command The command to execute.
947
+ * @param {CommandContext} context The context with which to execute the command.
948
+ *
949
+ * @return {boolean} Whether the command can be executed with the given context.
950
+ */
951
+ CommandStack.prototype.canExecute = function (command, context) {
952
+ const action = {
953
+ command: command,
954
+ context: context
955
+ };
956
+ const handler = this._getHandler(command);
957
+ let result = this._fire(command, 'canExecute', action);
958
+
959
+ // handler#canExecute will only be called if no listener
960
+ // decided on a result already
961
+ if (result === undefined) {
962
+ if (!handler) {
963
+ return false;
964
+ }
965
+ if (handler.canExecute) {
966
+ result = handler.canExecute(context);
967
+ }
968
+ }
969
+ return result;
970
+ };
971
+
972
+ /**
973
+ * Clear the command stack, erasing all undo / redo history.
974
+ *
975
+ * @param {boolean} [emit=true] Whether to fire an event. Defaults to `true`.
976
+ */
977
+ CommandStack.prototype.clear = function (emit) {
978
+ this._stack.length = 0;
979
+ this._stackIdx = -1;
980
+ if (emit !== false) {
981
+ this._fire('changed', {
982
+ trigger: 'clear'
983
+ });
984
+ }
985
+ };
986
+
987
+ /**
988
+ * Undo last command(s)
989
+ */
990
+ CommandStack.prototype.undo = function () {
991
+ let action = this._getUndoAction(),
992
+ next;
993
+ if (action) {
994
+ this._currentExecution.trigger = 'undo';
995
+ this._pushAction(action);
996
+ while (action) {
997
+ this._internalUndo(action);
998
+ next = this._getUndoAction();
999
+ if (!next || next.id !== action.id) {
1000
+ break;
1001
+ }
1002
+ action = next;
1003
+ }
1004
+ this._popAction();
1005
+ }
1006
+ };
1007
+
1008
+ /**
1009
+ * Redo last command(s)
1010
+ */
1011
+ CommandStack.prototype.redo = function () {
1012
+ let action = this._getRedoAction(),
1013
+ next;
1014
+ if (action) {
1015
+ this._currentExecution.trigger = 'redo';
1016
+ this._pushAction(action);
1017
+ while (action) {
1018
+ this._internalExecute(action, true);
1019
+ next = this._getRedoAction();
1020
+ if (!next || next.id !== action.id) {
1021
+ break;
1022
+ }
1023
+ action = next;
1024
+ }
1025
+ this._popAction();
1026
+ }
1027
+ };
1028
+
1029
+ /**
1030
+ * Register a handler instance with the command stack.
1031
+ *
1032
+ * @param {string} command Command to be executed.
1033
+ * @param {CommandHandler} handler Handler to execute the command.
1034
+ */
1035
+ CommandStack.prototype.register = function (command, handler) {
1036
+ this._setHandler(command, handler);
1037
+ };
1038
+
1039
+ /**
1040
+ * Register a handler type with the command stack by instantiating it and
1041
+ * injecting its dependencies.
1042
+ *
1043
+ * @param {string} command Command to be executed.
1044
+ * @param {CommandHandlerConstructor} handlerCls Constructor to instantiate a {@link CommandHandler}.
1045
+ */
1046
+ CommandStack.prototype.registerHandler = function (command, handlerCls) {
1047
+ if (!command || !handlerCls) {
1048
+ throw new Error('command and handlerCls must be defined');
1049
+ }
1050
+ const handler = this._injector.instantiate(handlerCls);
1051
+ this.register(command, handler);
1052
+ };
1053
+
1054
+ /**
1055
+ * @return {boolean}
1056
+ */
1057
+ CommandStack.prototype.canUndo = function () {
1058
+ return !!this._getUndoAction();
1059
+ };
1060
+
1061
+ /**
1062
+ * @return {boolean}
1063
+ */
1064
+ CommandStack.prototype.canRedo = function () {
1065
+ return !!this._getRedoAction();
1066
+ };
1067
+
1068
+ // stack access //////////////////////
1069
+
1070
+ CommandStack.prototype._getRedoAction = function () {
1071
+ return this._stack[this._stackIdx + 1];
1072
+ };
1073
+ CommandStack.prototype._getUndoAction = function () {
1074
+ return this._stack[this._stackIdx];
1075
+ };
1076
+
1077
+ // internal functionality //////////////////////
1078
+
1079
+ CommandStack.prototype._internalUndo = function (action) {
1080
+ const command = action.command,
1081
+ context = action.context;
1082
+ const handler = this._getHandler(command);
1083
+
1084
+ // guard against illegal nested command stack invocations
1085
+ this._atomicDo(() => {
1086
+ this._fire(command, 'revert', action);
1087
+ if (handler.revert) {
1088
+ this._markDirty(handler.revert(context));
1089
+ }
1090
+ this._revertedAction(action);
1091
+ this._fire(command, 'reverted', action);
1092
+ });
1093
+ };
1094
+ CommandStack.prototype._fire = function (command, qualifier, event) {
1095
+ if (arguments.length < 3) {
1096
+ event = qualifier;
1097
+ qualifier = null;
1098
+ }
1099
+ const names = qualifier ? [command + '.' + qualifier, qualifier] : [command];
1100
+ let result;
1101
+ event = this._eventBus.createEvent(event);
1102
+ for (const name of names) {
1103
+ result = this._eventBus.fire('commandStack.' + name, event);
1104
+ if (event.cancelBubble) {
1105
+ break;
1106
+ }
1107
+ }
1108
+ return result;
1109
+ };
1110
+ CommandStack.prototype._createId = function () {
1111
+ return this._uid++;
1112
+ };
1113
+ CommandStack.prototype._atomicDo = function (fn) {
1114
+ const execution = this._currentExecution;
1115
+ execution.atomic = true;
1116
+ try {
1117
+ fn();
1118
+ } finally {
1119
+ execution.atomic = false;
1120
+ }
1121
+ };
1122
+ CommandStack.prototype._internalExecute = function (action, redo) {
1123
+ const command = action.command,
1124
+ context = action.context;
1125
+ const handler = this._getHandler(command);
1126
+ if (!handler) {
1127
+ throw new Error('no command handler registered for <' + command + '>');
1128
+ }
1129
+ this._pushAction(action);
1130
+ if (!redo) {
1131
+ this._fire(command, 'preExecute', action);
1132
+ if (handler.preExecute) {
1133
+ handler.preExecute(context);
1134
+ }
1135
+ this._fire(command, 'preExecuted', action);
1136
+ }
1137
+
1138
+ // guard against illegal nested command stack invocations
1139
+ this._atomicDo(() => {
1140
+ this._fire(command, 'execute', action);
1141
+ if (handler.execute) {
1142
+ // actual execute + mark return results as dirty
1143
+ this._markDirty(handler.execute(context));
1144
+ }
1145
+
1146
+ // log to stack
1147
+ this._executedAction(action, redo);
1148
+ this._fire(command, 'executed', action);
1149
+ });
1150
+ if (!redo) {
1151
+ this._fire(command, 'postExecute', action);
1152
+ if (handler.postExecute) {
1153
+ handler.postExecute(context);
1154
+ }
1155
+ this._fire(command, 'postExecuted', action);
1156
+ }
1157
+ this._popAction();
1158
+ };
1159
+ CommandStack.prototype._pushAction = function (action) {
1160
+ const execution = this._currentExecution,
1161
+ actions = execution.actions;
1162
+ const baseAction = actions[0];
1163
+ if (execution.atomic) {
1164
+ throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
1165
+ }
1166
+ if (!action.id) {
1167
+ action.id = baseAction && baseAction.id || this._createId();
1168
+ }
1169
+ actions.push(action);
1170
+ };
1171
+ CommandStack.prototype._popAction = function () {
1172
+ const execution = this._currentExecution,
1173
+ trigger = execution.trigger,
1174
+ actions = execution.actions,
1175
+ dirty = execution.dirty;
1176
+ actions.pop();
1177
+ if (!actions.length) {
1178
+ this._eventBus.fire('elements.changed', {
1179
+ elements: minDash.uniqueBy('id', dirty.reverse())
1180
+ });
1181
+ dirty.length = 0;
1182
+ this._fire('changed', {
1183
+ trigger: trigger
1184
+ });
1185
+ execution.trigger = null;
1186
+ }
1187
+ };
1188
+ CommandStack.prototype._markDirty = function (elements) {
1189
+ const execution = this._currentExecution;
1190
+ if (!elements) {
1191
+ return;
1192
+ }
1193
+ elements = minDash.isArray(elements) ? elements : [elements];
1194
+ execution.dirty = execution.dirty.concat(elements);
1195
+ };
1196
+ CommandStack.prototype._executedAction = function (action, redo) {
1197
+ const stackIdx = ++this._stackIdx;
1198
+ if (!redo) {
1199
+ this._stack.splice(stackIdx, this._stack.length, action);
1200
+ }
1201
+ };
1202
+ CommandStack.prototype._revertedAction = function (action) {
1203
+ this._stackIdx--;
1204
+ };
1205
+ CommandStack.prototype._getHandler = function (command) {
1206
+ return this._handlerMap[command];
1207
+ };
1208
+ CommandStack.prototype._setHandler = function (command, handler) {
1209
+ if (!command || !handler) {
1210
+ throw new Error('command and handler required');
1211
+ }
1212
+ if (this._handlerMap[command]) {
1213
+ throw new Error('overriding handler for command <' + command + '>');
1214
+ }
1215
+ this._handlerMap[command] = handler;
1216
+ };
1217
+
1218
+ /**
1219
+ * @type { import('didi').ModuleDeclaration }
1220
+ */
1221
+ var commandModule = {
1222
+ commandStack: ['type', CommandStack]
1223
+ };
1224
+
1225
+ class UpdateFieldValidationHandler {
1226
+ constructor(form, validator) {
1227
+ this._form = form;
1228
+ this._validator = validator;
1229
+ }
1230
+ execute(context) {
1231
+ const {
1232
+ field,
1233
+ value
1234
+ } = context;
1235
+ const {
1236
+ errors
1237
+ } = this._form._getState();
1238
+ context.oldErrors = clone(errors);
1239
+ const fieldErrors = this._validator.validateField(field, value);
1240
+ const updatedErrors = minDash.set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
1241
+ this._form._setState({
1242
+ errors: updatedErrors
1243
+ });
1244
+ }
1245
+ revert(context) {
1246
+ this._form._setState({
1247
+ errors: context.oldErrors
1248
+ });
1249
+ }
1250
+ }
1251
+ UpdateFieldValidationHandler.$inject = ['form', 'validator'];
1252
+
1253
+ class ViewerCommands {
1254
+ constructor(commandStack, eventBus) {
1255
+ this._commandStack = commandStack;
1256
+ eventBus.on('form.init', () => {
1257
+ this.registerHandlers();
1258
+ });
1259
+ }
1260
+ registerHandlers() {
1261
+ Object.entries(this.getHandlers()).forEach(([id, handler]) => {
1262
+ this._commandStack.registerHandler(id, handler);
1263
+ });
1264
+ }
1265
+ getHandlers() {
1266
+ return {
1267
+ 'formField.validation.update': UpdateFieldValidationHandler
1268
+ };
1269
+ }
1270
+ updateFieldValidation(field, value) {
1271
+ const context = {
1272
+ field,
1273
+ value
1274
+ };
1275
+ this._commandStack.execute('formField.validation.update', context);
1276
+ }
1277
+ }
1278
+ ViewerCommands.$inject = ['commandStack', 'eventBus'];
1279
+
1280
+ var ViewerCommandsModule = {
1281
+ __depends__: [commandModule],
1282
+ __init__: ['viewerCommands'],
1283
+ viewerCommands: ['type', ViewerCommands]
1284
+ };
1285
+
494
1286
  var FN_REF = '__fn';
495
1287
  var DEFAULT_PRIORITY = 1000;
496
1288
  var slice = Array.prototype.slice;
@@ -1094,8 +1886,8 @@ Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
1094
1886
 
1095
1887
  // helpers //////////
1096
1888
 
1097
- /**
1098
- * Helper function to evaluate optional FEEL validation values.
1889
+ /**
1890
+ * Helper function to evaluate optional FEEL validation values.
1099
1891
  */
1100
1892
  function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
1101
1893
  const evaluatedValidate = {
@@ -1128,72 +1920,415 @@ function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form
1128
1920
  return evaluatedValidate;
1129
1921
  }
1130
1922
 
1131
- class FormFieldRegistry {
1132
- constructor(eventBus) {
1133
- this._eventBus = eventBus;
1134
- this._formFields = {};
1135
- eventBus.on('form.clear', () => this.clear());
1136
- this._ids = new Ids([32, 36, 1]);
1137
- this._keys = new Ids([32, 36, 1]);
1923
+ class Importer {
1924
+ /**
1925
+ * @constructor
1926
+ * @param { import('./FormFieldRegistry').default } formFieldRegistry
1927
+ * @param { import('./PathRegistry').default } pathRegistry
1928
+ * @param { import('./FieldFactory').default } fieldFactory
1929
+ * @param { import('./FormLayouter').default } formLayouter
1930
+ */
1931
+ constructor(formFieldRegistry, pathRegistry, fieldFactory, formLayouter) {
1932
+ this._formFieldRegistry = formFieldRegistry;
1933
+ this._pathRegistry = pathRegistry;
1934
+ this._fieldFactory = fieldFactory;
1935
+ this._formLayouter = formLayouter;
1138
1936
  }
1139
- add(formField) {
1140
- const {
1141
- id
1142
- } = formField;
1143
- if (this._formFields[id]) {
1144
- throw new Error(`form field with ID ${id} already exists`);
1937
+
1938
+ /**
1939
+ * Import schema creating rows, fields, attaching additional
1940
+ * information to each field and adding fields to the
1941
+ * field registry.
1942
+ *
1943
+ * Additional information attached:
1944
+ *
1945
+ * * `id` (unless present)
1946
+ * * `_parent`
1947
+ * * `_path`
1948
+ *
1949
+ * @param {any} schema
1950
+ *
1951
+ * @typedef {{ warnings: Error[], schema: any }} ImportResult
1952
+ * @returns {ImportResult}
1953
+ */
1954
+ importSchema(schema) {
1955
+ // TODO: Add warnings
1956
+ const warnings = [];
1957
+ try {
1958
+ this._cleanup();
1959
+ const importedSchema = this.importFormField(clone(schema));
1960
+ this._formLayouter.calculateLayout(clone(importedSchema));
1961
+ return {
1962
+ schema: importedSchema,
1963
+ warnings
1964
+ };
1965
+ } catch (err) {
1966
+ this._cleanup();
1967
+ err.warnings = warnings;
1968
+ throw err;
1145
1969
  }
1146
- this._eventBus.fire('formField.add', {
1147
- formField
1970
+ }
1971
+ _cleanup() {
1972
+ this._formLayouter.clear();
1973
+ this._formFieldRegistry.clear();
1974
+ this._pathRegistry.clear();
1975
+ }
1976
+
1977
+ /**
1978
+ * @param {{[x: string]: any}} fieldAttrs
1979
+ * @param {String} [parentId]
1980
+ * @param {number} [index]
1981
+ *
1982
+ * @return {any} field
1983
+ */
1984
+ importFormField(fieldAttrs, parentId, index) {
1985
+ const {
1986
+ components
1987
+ } = fieldAttrs;
1988
+ let parent, path;
1989
+ if (parentId) {
1990
+ parent = this._formFieldRegistry.get(parentId);
1991
+ }
1992
+
1993
+ // set form field path
1994
+ path = parent ? [...parent._path, 'components', index] : [];
1995
+ const field = this._fieldFactory.create({
1996
+ ...fieldAttrs,
1997
+ _path: path,
1998
+ _parent: parentId
1999
+ }, false);
2000
+ this._formFieldRegistry.add(field);
2001
+ if (components) {
2002
+ field.components = this.importFormFields(components, field.id);
2003
+ }
2004
+ return field;
2005
+ }
2006
+
2007
+ /**
2008
+ * @param {Array<any>} components
2009
+ * @param {string} parentId
2010
+ *
2011
+ * @return {Array<any>} imported components
2012
+ */
2013
+ importFormFields(components, parentId) {
2014
+ return components.map((component, index) => {
2015
+ return this.importFormField(component, parentId, index);
1148
2016
  });
1149
- this._formFields[id] = formField;
1150
2017
  }
1151
- remove(formField) {
2018
+ }
2019
+ Importer.$inject = ['formFieldRegistry', 'pathRegistry', 'fieldFactory', 'formLayouter'];
2020
+
2021
+ class FieldFactory {
2022
+ /**
2023
+ * @constructor
2024
+ *
2025
+ * @param formFieldRegistry
2026
+ * @param formFields
2027
+ */
2028
+ constructor(formFieldRegistry, pathRegistry, formFields) {
2029
+ this._formFieldRegistry = formFieldRegistry;
2030
+ this._pathRegistry = pathRegistry;
2031
+ this._formFields = formFields;
2032
+ }
2033
+ create(attrs, applyDefaults = true) {
1152
2034
  const {
1153
- id
1154
- } = formField;
1155
- if (!this._formFields[id]) {
1156
- return;
2035
+ id,
2036
+ type,
2037
+ key,
2038
+ path,
2039
+ _parent
2040
+ } = attrs;
2041
+ const fieldDefinition = this._formFields.get(type);
2042
+ if (!fieldDefinition) {
2043
+ throw new Error(`form field of type <${type}> not supported`);
1157
2044
  }
1158
- this._eventBus.fire('formField.remove', {
1159
- formField
2045
+ const {
2046
+ config
2047
+ } = fieldDefinition;
2048
+ if (!config) {
2049
+ throw new Error(`form field of type <${type}> has no config`);
2050
+ }
2051
+ if (id && this._formFieldRegistry._ids.assigned(id)) {
2052
+ throw new Error(`form field with id <${id}> already exists`);
2053
+ }
2054
+
2055
+ // ensure that we can claim the path
2056
+
2057
+ const parent = _parent && this._formFieldRegistry.get(_parent);
2058
+ const parentPath = parent && this._pathRegistry.getValuePath(parent) || [];
2059
+ if (config.keyed && key && !this._pathRegistry.canClaimPath([...parentPath, ...key.split('.')], true)) {
2060
+ throw new Error(`binding path '${[...parentPath, key].join('.')}' is already claimed`);
2061
+ }
2062
+ if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], false)) {
2063
+ throw new Error(`binding path '${[...parentPath, ...path.split('.')].join('.')}' is already claimed`);
2064
+ }
2065
+ const labelAttrs = applyDefaults && config.label ? {
2066
+ label: config.label
2067
+ } : {};
2068
+ const field = config.create({
2069
+ ...labelAttrs,
2070
+ ...attrs
1160
2071
  });
1161
- delete this._formFields[id];
2072
+ this._ensureId(field);
2073
+ if (config.keyed) {
2074
+ this._ensureKey(field);
2075
+ }
2076
+ if (config.pathed && path) {
2077
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), false);
2078
+ }
2079
+ return field;
1162
2080
  }
1163
- get(id) {
1164
- return this._formFields[id];
2081
+ _ensureId(field) {
2082
+ if (field.id) {
2083
+ this._formFieldRegistry._ids.claim(field.id, field);
2084
+ return;
2085
+ }
2086
+ let prefix = 'Field';
2087
+ if (field.type === 'default') {
2088
+ prefix = 'Form';
2089
+ }
2090
+ field.id = this._formFieldRegistry._ids.nextPrefixed(`${prefix}_`, field);
2091
+ }
2092
+ _ensureKey(field) {
2093
+ if (!field.key) {
2094
+ let random;
2095
+ const parent = this._formFieldRegistry.get(field._parent);
2096
+
2097
+ // ensure key uniqueness at level
2098
+ do {
2099
+ random = Math.random().toString(36).substring(7);
2100
+ } while (parent && parent.components.some(child => child.key === random));
2101
+ field.key = `${field.type}_${random}`;
2102
+ }
2103
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), true);
1165
2104
  }
1166
- getAll() {
1167
- return Object.values(this._formFields);
2105
+ }
2106
+ FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
2107
+
2108
+ /**
2109
+ * The PathRegistry class manages a hierarchical structure of paths associated with form fields.
2110
+ * It enables claiming, unclaiming, and validating paths within this structure.
2111
+ *
2112
+ * Example Tree Structure:
2113
+ *
2114
+ * [
2115
+ * {
2116
+ * segment: 'root',
2117
+ * claimCount: 1,
2118
+ * children: [
2119
+ * {
2120
+ * segment: 'child1',
2121
+ * claimCount: 2,
2122
+ * children: null // A leaf node (closed path)
2123
+ * },
2124
+ * {
2125
+ * segment: 'child2',
2126
+ * claimCount: 1,
2127
+ * children: [
2128
+ * {
2129
+ * segment: 'subChild1',
2130
+ * claimCount: 1,
2131
+ * children: [] // An open node (open path)
2132
+ * }
2133
+ * ]
2134
+ * }
2135
+ * ]
2136
+ * }
2137
+ * ]
2138
+ */
2139
+ class PathRegistry {
2140
+ constructor(formFieldRegistry, formFields) {
2141
+ this._formFieldRegistry = formFieldRegistry;
2142
+ this._formFields = formFields;
2143
+ this._dataPaths = [];
1168
2144
  }
1169
- forEach(callback) {
1170
- this.getAll().forEach(formField => callback(formField));
2145
+ canClaimPath(path, closed = false) {
2146
+ let node = {
2147
+ children: this._dataPaths
2148
+ };
2149
+ for (const segment of path) {
2150
+ node = _getNextSegment(node, segment);
2151
+
2152
+ // if no node at that path, we can claim it no matter what
2153
+ if (!node) {
2154
+ return true;
2155
+ }
2156
+
2157
+ // if we reach a leaf node, definitely not claimable
2158
+ if (node.children === null) {
2159
+ return false;
2160
+ }
2161
+ }
2162
+
2163
+ // if after all segments we reach a node with children, we can claim it only openly
2164
+ return !closed;
2165
+ }
2166
+ claimPath(path, closed = false) {
2167
+ if (!this.canClaimPath(path, closed)) {
2168
+ throw new Error(`cannot claim path '${path.join('.')}'`);
2169
+ }
2170
+ let node = {
2171
+ children: this._dataPaths
2172
+ };
2173
+ for (const segment of path) {
2174
+ let child = _getNextSegment(node, segment);
2175
+ if (!child) {
2176
+ child = {
2177
+ segment,
2178
+ claimCount: 1,
2179
+ children: []
2180
+ };
2181
+ node.children.push(child);
2182
+ } else {
2183
+ child.claimCount++;
2184
+ }
2185
+ node = child;
2186
+ }
2187
+ if (closed) {
2188
+ node.children = null;
2189
+ }
2190
+ }
2191
+ unclaimPath(path) {
2192
+ // verification Pass
2193
+ let node = {
2194
+ children: this._dataPaths
2195
+ };
2196
+ for (const segment of path) {
2197
+ const child = _getNextSegment(node, segment);
2198
+ if (!child) {
2199
+ throw new Error(`no open path found for '${path.join('.')}'`);
2200
+ }
2201
+ node = child;
2202
+ }
2203
+
2204
+ // mutation Pass
2205
+ node = {
2206
+ children: this._dataPaths
2207
+ };
2208
+ for (const segment of path) {
2209
+ const child = _getNextSegment(node, segment);
2210
+ child.claimCount--;
2211
+ if (child.claimCount === 0) {
2212
+ node.children.splice(node.children.indexOf(child), 1);
2213
+ break; // Abort early if claimCount reaches zero
2214
+ }
2215
+
2216
+ node = child;
2217
+ }
2218
+ }
2219
+
2220
+ /**
2221
+ * Applies a function (fn) recursively on a given field and its children.
2222
+ *
2223
+ * - `field`: Starting field object.
2224
+ * - `fn`: Function to apply.
2225
+ * - `context`: Optional object for passing data between calls.
2226
+ *
2227
+ * Stops early if `fn` returns `false`. Useful for traversing the form field tree.
2228
+ *
2229
+ * @returns {boolean} Success status based on function execution.
2230
+ */
2231
+ executeRecursivelyOnFields(field, fn, context = {}) {
2232
+ let result = true;
2233
+ const formFieldConfig = this._formFields.get(field.type).config;
2234
+ if (formFieldConfig.keyed) {
2235
+ const callResult = fn({
2236
+ field,
2237
+ isClosed: true,
2238
+ context
2239
+ });
2240
+ return result && callResult;
2241
+ } else if (formFieldConfig.pathed) {
2242
+ const callResult = fn({
2243
+ field,
2244
+ isClosed: false,
2245
+ context
2246
+ });
2247
+ result = result && callResult;
2248
+ }
2249
+ if (field.components) {
2250
+ for (const child of field.components) {
2251
+ const callResult = this.executeRecursivelyOnFields(child, fn, clone(context));
2252
+ result = result && callResult;
2253
+
2254
+ // only stop executing if false is specifically returned, not if undefined
2255
+ if (result === false) {
2256
+ return result;
2257
+ }
2258
+ }
2259
+ }
2260
+ return result;
2261
+ }
2262
+
2263
+ /**
2264
+ * Generates an array representing the binding path to an underlying data object for a form field.
2265
+ *
2266
+ * @param {Object} field - The field object with properties: `key`, `path`, `id`, and optionally `_parent`.
2267
+ * @param {Object} [options={}] - Configuration options.
2268
+ * @param {Object} [options.replacements={}] - A map of field IDs to alternative path arrays.
2269
+ * @param {Object} [options.cutoffNode] - The ID of the parent field at which to stop generating the path.
2270
+ *
2271
+ * @returns {(Array<string>|undefined)} An array of strings representing the binding path, or undefined if not determinable.
2272
+ */
2273
+ getValuePath(field, options = {}) {
2274
+ const {
2275
+ replacements = {},
2276
+ cutoffNode = null
2277
+ } = options;
2278
+ let localValuePath = [];
2279
+ const hasReplacement = Object.prototype.hasOwnProperty.call(replacements, field.id);
2280
+ const formFieldConfig = this._formFields.get(field.type).config;
2281
+ if (hasReplacement) {
2282
+ const replacement = replacements[field.id];
2283
+ if (replacement === null || replacement === undefined || replacement === '') {
2284
+ localValuePath = [];
2285
+ } else if (typeof replacement === 'string') {
2286
+ localValuePath = replacement.split('.');
2287
+ } else if (Array.isArray(replacement)) {
2288
+ localValuePath = replacement;
2289
+ } else {
2290
+ throw new Error(`replacements for field ${field.id} must be a string, array or null/undefined`);
2291
+ }
2292
+ } else if (formFieldConfig.keyed) {
2293
+ localValuePath = field.key.split('.');
2294
+ } else if (formFieldConfig.pathed && field.path) {
2295
+ localValuePath = field.path.split('.');
2296
+ }
2297
+ if (field._parent && field._parent !== cutoffNode) {
2298
+ const parent = this._formFieldRegistry.get(field._parent);
2299
+ return [...(this.getValuePath(parent, options) || []), ...localValuePath];
2300
+ }
2301
+ return localValuePath;
1171
2302
  }
1172
2303
  clear() {
1173
- this._formFields = {};
1174
- this._ids.clear();
1175
- this._keys.clear();
2304
+ this._dataPaths = [];
1176
2305
  }
1177
2306
  }
1178
- FormFieldRegistry.$inject = ['eventBus'];
2307
+ const _getNextSegment = (node, segment) => {
2308
+ if (minDash.isArray(node.children)) {
2309
+ return node.children.find(node => node.segment === segment) || null;
2310
+ }
2311
+ return null;
2312
+ };
2313
+ PathRegistry.$inject = ['formFieldRegistry', 'formFields'];
1179
2314
 
1180
- /**
1181
- * @typedef { { id: String, components: Array<String> } } FormRow
1182
- * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
2315
+ /**
2316
+ * @typedef { { id: String, components: Array<String> } } FormRow
2317
+ * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1183
2318
  */
1184
2319
 
1185
- /**
1186
- * Maintains the Form layout in a given structure, for example
1187
- *
1188
- * [
1189
- * {
1190
- * formFieldId: 'FormField_1',
1191
- * rows: [
1192
- * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1193
- * ]
1194
- * }
1195
- * ]
1196
- *
2320
+ /**
2321
+ * Maintains the Form layout in a given structure, for example
2322
+ *
2323
+ * [
2324
+ * {
2325
+ * formFieldId: 'FormField_1',
2326
+ * rows: [
2327
+ * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
2328
+ * ]
2329
+ * }
2330
+ * ]
2331
+ *
1197
2332
  */
1198
2333
  class FormLayouter {
1199
2334
  constructor(eventBus) {
@@ -1203,8 +2338,8 @@ class FormLayouter {
1203
2338
  this._eventBus = eventBus;
1204
2339
  }
1205
2340
 
1206
- /**
1207
- * @param {FormRow} row
2341
+ /**
2342
+ * @param {FormRow} row
1208
2343
  */
1209
2344
  addRow(formFieldId, row) {
1210
2345
  let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
@@ -1218,18 +2353,18 @@ class FormLayouter {
1218
2353
  rowsPerComponent.rows.push(row);
1219
2354
  }
1220
2355
 
1221
- /**
1222
- * @param {String} id
1223
- * @returns {FormRow}
2356
+ /**
2357
+ * @param {String} id
2358
+ * @returns {FormRow}
1224
2359
  */
1225
2360
  getRow(id) {
1226
2361
  const rows = allRows(this._rows);
1227
2362
  return rows.find(r => r.id === id);
1228
2363
  }
1229
2364
 
1230
- /**
1231
- * @param {any} formField
1232
- * @returns {FormRow}
2365
+ /**
2366
+ * @param {any} formField
2367
+ * @returns {FormRow}
1233
2368
  */
1234
2369
  getRowForField(formField) {
1235
2370
  return allRows(this._rows).find(r => {
@@ -1240,9 +2375,9 @@ class FormLayouter {
1240
2375
  });
1241
2376
  }
1242
2377
 
1243
- /**
1244
- * @param {String} formFieldId
1245
- * @returns { Array<FormRow> }
2378
+ /**
2379
+ * @param {String} formFieldId
2380
+ * @returns { Array<FormRow> }
1246
2381
  */
1247
2382
  getRows(formFieldId) {
1248
2383
  const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
@@ -1252,22 +2387,22 @@ class FormLayouter {
1252
2387
  return rowsForField.rows;
1253
2388
  }
1254
2389
 
1255
- /**
1256
- * @returns {string}
2390
+ /**
2391
+ * @returns {string}
1257
2392
  */
1258
2393
  nextRowId() {
1259
2394
  return this._ids.nextPrefixed('Row_');
1260
2395
  }
1261
2396
 
1262
- /**
1263
- * @param {any} formField
2397
+ /**
2398
+ * @param {any} formField
1264
2399
  */
1265
2400
  calculateLayout(formField) {
1266
2401
  const {
1267
2402
  type,
1268
2403
  components
1269
2404
  } = formField;
1270
- if (type !== 'default' || !components) {
2405
+ if (type !== 'default' && type !== 'group' || !components) {
1271
2406
  return;
1272
2407
  }
1273
2408
 
@@ -1289,363 +2424,85 @@ class FormLayouter {
1289
2424
  rows: this._rows
1290
2425
  });
1291
2426
  }
1292
- clear() {
1293
- this._rows = [];
1294
- this._ids.clear();
1295
-
1296
- // fire event to notify interested parties
1297
- this._eventBus.fire('form.layoutCleared');
1298
- }
1299
- }
1300
- FormLayouter.$inject = ['eventBus'];
1301
-
1302
- // helpers //////
1303
-
1304
- function groupByRow(components, ids) {
1305
- return minDash.groupBy(components, c => {
1306
- // mitigate missing row by creating new (handle legacy)
1307
- const {
1308
- layout
1309
- } = c;
1310
- if (!layout || !layout.row) {
1311
- return ids.nextPrefixed('Row_');
1312
- }
1313
- return layout.row;
1314
- });
1315
- }
1316
-
1317
- /**
1318
- * @param {Array<FormRows>} formRows
1319
- * @returns {Array<FormRow>}
1320
- */
1321
- function allRows(formRows) {
1322
- return minDash.flatten(formRows.map(c => c.rows));
1323
- }
1324
-
1325
- // config ///////////////////
1326
-
1327
- const MINUTES_IN_DAY = 60 * 24;
1328
- const DATETIME_SUBTYPES = {
1329
- DATE: 'date',
1330
- TIME: 'time',
1331
- DATETIME: 'datetime'
1332
- };
1333
- const TIME_SERIALISING_FORMATS = {
1334
- UTC_OFFSET: 'utc_offset',
1335
- UTC_NORMALIZED: 'utc_normalized',
1336
- NO_TIMEZONE: 'no_timezone'
1337
- };
1338
- const DATETIME_SUBTYPES_LABELS = {
1339
- [DATETIME_SUBTYPES.DATE]: 'Date',
1340
- [DATETIME_SUBTYPES.TIME]: 'Time',
1341
- [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
1342
- };
1343
- const TIME_SERIALISINGFORMAT_LABELS = {
1344
- [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
1345
- [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
1346
- [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
1347
- };
1348
- const DATETIME_SUBTYPE_PATH = ['subtype'];
1349
- const DATE_LABEL_PATH = ['dateLabel'];
1350
- const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
1351
- const TIME_LABEL_PATH = ['timeLabel'];
1352
- const TIME_USE24H_PATH = ['use24h'];
1353
- const TIME_INTERVAL_PATH = ['timeInterval'];
1354
- const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
1355
-
1356
- // config ///////////////////
1357
-
1358
- const VALUES_SOURCES = {
1359
- STATIC: 'static',
1360
- INPUT: 'input',
1361
- EXPRESSION: 'expression'
1362
- };
1363
- const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
1364
- const VALUES_SOURCES_LABELS = {
1365
- [VALUES_SOURCES.STATIC]: 'Static',
1366
- [VALUES_SOURCES.INPUT]: 'Input data',
1367
- [VALUES_SOURCES.EXPRESSION]: 'Expression'
1368
- };
1369
- const VALUES_SOURCES_PATHS = {
1370
- [VALUES_SOURCES.STATIC]: ['values'],
1371
- [VALUES_SOURCES.INPUT]: ['valuesKey'],
1372
- [VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
1373
- };
1374
- const VALUES_SOURCES_DEFAULTS = {
1375
- [VALUES_SOURCES.STATIC]: [{
1376
- label: 'Value',
1377
- value: 'value'
1378
- }],
1379
- [VALUES_SOURCES.INPUT]: '',
1380
- [VALUES_SOURCES.EXPRESSION]: '='
1381
- };
1382
-
1383
- // helpers ///////////////////
1384
-
1385
- function getValuesSource(field) {
1386
- for (const source of Object.values(VALUES_SOURCES)) {
1387
- if (minDash.get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
1388
- return source;
1389
- }
1390
- }
1391
- return VALUES_SOURCE_DEFAULT;
1392
- }
1393
-
1394
- function createInjector(bootstrapModules) {
1395
- const injector = new didi.Injector(bootstrapModules);
1396
- injector.init();
1397
- return injector;
1398
- }
1399
-
1400
- /**
1401
- * @param {string?} prefix
1402
- *
1403
- * @returns Element
1404
- */
1405
- function createFormContainer(prefix = 'fjs') {
1406
- const container = document.createElement('div');
1407
- container.classList.add(`${prefix}-container`);
1408
- return container;
1409
- }
1410
-
1411
- const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
1412
- const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
1413
- function findErrors(errors, path) {
1414
- return errors[pathStringify(path)];
1415
- }
1416
- function isRequired(field) {
1417
- return field.required;
1418
- }
1419
- function pathParse(path) {
1420
- if (!path) {
1421
- return [];
1422
- }
1423
- return path.split('.').map(key => {
1424
- return isNaN(parseInt(key)) ? key : parseInt(key);
1425
- });
1426
- }
1427
- function pathsEqual(a, b) {
1428
- return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
1429
- }
1430
- function pathStringify(path) {
1431
- if (!path) {
1432
- return '';
1433
- }
1434
- return path.join('.');
1435
- }
1436
- const indices = {};
1437
- function generateIndexForType(type) {
1438
- if (type in indices) {
1439
- indices[type]++;
1440
- } else {
1441
- indices[type] = 1;
1442
- }
1443
- return indices[type];
1444
- }
1445
- function generateIdForType(type) {
1446
- return `${type}${generateIndexForType(type)}`;
1447
- }
1448
-
1449
- /**
1450
- * @template T
1451
- * @param {T} data
1452
- * @param {(this: any, key: string, value: any) => any} [replacer]
1453
- * @return {T}
1454
- */
1455
- function clone(data, replacer) {
1456
- return JSON.parse(JSON.stringify(data, replacer));
1457
- }
1458
-
1459
- /**
1460
- * Parse the schema for input variables a form might make use of
1461
- *
1462
- * @param {any} schema
1463
- *
1464
- * @return {string[]}
1465
- */
1466
- function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null), templating = new FeelersTemplating()) {
1467
- if (!schema.components) {
1468
- return [];
1469
- }
1470
- const variables = schema.components.reduce((variables, component) => {
1471
- const {
1472
- key,
1473
- valuesKey,
1474
- type
1475
- } = component;
1476
- if (['button'].includes(type)) {
1477
- return variables;
1478
- }
1479
- if (key) {
1480
- variables = [...variables, key];
1481
- }
1482
- if (valuesKey) {
1483
- variables = [...variables, valuesKey];
1484
- }
1485
- EXPRESSION_PROPERTIES.forEach(prop => {
1486
- const property = minDash.get(component, prop.split('.'));
1487
- if (property && expressionLanguage.isExpression(property)) {
1488
- const expressionVariables = expressionLanguage.getVariableNames(property, {
1489
- type: 'expression'
1490
- });
1491
- variables = [...variables, ...expressionVariables];
1492
- }
1493
- });
1494
- TEMPLATE_PROPERTIES.forEach(prop => {
1495
- const property = minDash.get(component, prop.split('.'));
1496
- if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
1497
- const templateVariables = templating.getVariableNames(property);
1498
- variables = [...variables, ...templateVariables];
1499
- }
1500
- });
1501
- return variables;
1502
- }, []);
1503
-
1504
- // remove duplicates
1505
- return Array.from(new Set(variables));
1506
- }
1507
-
1508
- class Importer {
1509
- /**
1510
- * @constructor
1511
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
1512
- * @param { import('../render/FormFields').default } formFields
1513
- * @param { import('../core').FormLayouter } formLayouter
1514
- */
1515
- constructor(formFieldRegistry, formFields, formLayouter) {
1516
- this._formFieldRegistry = formFieldRegistry;
1517
- this._formFields = formFields;
1518
- this._formLayouter = formLayouter;
1519
- }
2427
+ clear() {
2428
+ this._rows = [];
2429
+ this._ids.clear();
1520
2430
 
1521
- /**
1522
- * Import schema adding `id`, `_parent` and `_path`
1523
- * information to each field and adding it to the
1524
- * form field registry.
1525
- *
1526
- * @param {any} schema
1527
- * @param {any} [data]
1528
- *
1529
- * @return { { warnings: Array<any>, schema: any, data: any } }
1530
- */
1531
- importSchema(schema, data = {}) {
1532
- // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
1533
- const warnings = [];
1534
- try {
1535
- this._formLayouter.clear();
1536
- const importedSchema = this.importFormField(clone(schema)),
1537
- initializedData = this.initializeFieldValues(clone(data));
1538
- this._formLayouter.calculateLayout(clone(importedSchema));
1539
- return {
1540
- warnings,
1541
- schema: importedSchema,
1542
- data: initializedData
1543
- };
1544
- } catch (err) {
1545
- err.warnings = warnings;
1546
- throw err;
1547
- }
2431
+ // fire event to notify interested parties
2432
+ this._eventBus.fire('form.layoutCleared');
1548
2433
  }
2434
+ }
2435
+ FormLayouter.$inject = ['eventBus'];
1549
2436
 
1550
- /**
1551
- * @param {any} formField
1552
- * @param {string} [parentId]
1553
- *
1554
- * @return {any} importedField
1555
- */
1556
- importFormField(formField, parentId) {
2437
+ // helpers //////
2438
+
2439
+ function groupByRow(components, ids) {
2440
+ return minDash.groupBy(components, c => {
2441
+ // mitigate missing row by creating new (handle legacy)
1557
2442
  const {
1558
- components,
1559
- key,
1560
- type,
1561
- id = generateIdForType(type)
1562
- } = formField;
1563
- if (parentId) {
1564
- // set form field parent
1565
- formField._parent = parentId;
1566
- }
1567
- if (!this._formFields.get(type)) {
1568
- throw new Error(`form field of type <${type}> not supported`);
2443
+ layout
2444
+ } = c;
2445
+ if (!layout || !layout.row) {
2446
+ return ids.nextPrefixed('Row_');
1569
2447
  }
1570
- if (key) {
1571
- // validate <key> uniqueness
1572
- if (this._formFieldRegistry._keys.assigned(key)) {
1573
- throw new Error(`form field with key <${key}> already exists`);
1574
- }
1575
- this._formFieldRegistry._keys.claim(key, formField);
2448
+ return layout.row;
2449
+ });
2450
+ }
1576
2451
 
1577
- // TODO: buttons should not have key
1578
- if (type !== 'button') {
1579
- // set form field path
1580
- formField._path = [key];
1581
- }
1582
- }
1583
- if (id) {
1584
- // validate <id> uniqueness
1585
- if (this._formFieldRegistry._ids.assigned(id)) {
1586
- throw new Error(`form field with id <${id}> already exists`);
1587
- }
1588
- this._formFieldRegistry._ids.claim(id, formField);
1589
- }
2452
+ /**
2453
+ * @param {Array<FormRows>} formRows
2454
+ * @returns {Array<FormRow>}
2455
+ */
2456
+ function allRows(formRows) {
2457
+ return minDash.flatten(formRows.map(c => c.rows));
2458
+ }
1590
2459
 
1591
- // set form field ID
1592
- formField.id = id;
1593
- this._formFieldRegistry.add(formField);
1594
- if (components) {
1595
- this.importFormFields(components, id);
2460
+ class FormFieldRegistry {
2461
+ constructor(eventBus) {
2462
+ this._eventBus = eventBus;
2463
+ this._formFields = {};
2464
+ eventBus.on('form.clear', () => this.clear());
2465
+ this._ids = new Ids([32, 36, 1]);
2466
+ }
2467
+ add(formField) {
2468
+ const {
2469
+ id
2470
+ } = formField;
2471
+ if (this._formFields[id]) {
2472
+ throw new Error(`form field with ID ${id} already exists`);
1596
2473
  }
1597
- return formField;
2474
+ this._eventBus.fire('formField.add', {
2475
+ formField
2476
+ });
2477
+ this._formFields[id] = formField;
1598
2478
  }
1599
- importFormFields(components, parentId) {
1600
- components.forEach(component => {
1601
- this.importFormField(component, parentId);
2479
+ remove(formField) {
2480
+ const {
2481
+ id
2482
+ } = formField;
2483
+ if (!this._formFields[id]) {
2484
+ return;
2485
+ }
2486
+ this._eventBus.fire('formField.remove', {
2487
+ formField
1602
2488
  });
2489
+ delete this._formFields[id];
1603
2490
  }
1604
-
1605
- /**
1606
- * @param {Object} data
1607
- *
1608
- * @return {Object} initializedData
1609
- */
1610
- initializeFieldValues(data) {
1611
- return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
1612
- const {
1613
- defaultValue,
1614
- _path,
1615
- type
1616
- } = formField;
1617
-
1618
- // try to get value from data
1619
- // if unavailable - try to get default value from form field
1620
- // if unavailable - get empty value from form field
1621
-
1622
- if (_path) {
1623
- const {
1624
- config: fieldConfig
1625
- } = this._formFields.get(type);
1626
- let valueData = minDash.get(data, _path);
1627
- if (!minDash.isUndefined(valueData) && fieldConfig.sanitizeValue) {
1628
- valueData = fieldConfig.sanitizeValue({
1629
- formField,
1630
- data,
1631
- value: valueData
1632
- });
1633
- }
1634
- const initializedFieldValue = !minDash.isUndefined(valueData) ? valueData : !minDash.isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
1635
- initializedData = {
1636
- ...initializedData,
1637
- [_path[0]]: initializedFieldValue
1638
- };
1639
- }
1640
- return initializedData;
1641
- }, data);
2491
+ get(id) {
2492
+ return this._formFields[id];
2493
+ }
2494
+ getAll() {
2495
+ return Object.values(this._formFields);
2496
+ }
2497
+ forEach(callback) {
2498
+ this.getAll().forEach(formField => callback(formField));
2499
+ }
2500
+ clear() {
2501
+ this._formFields = {};
2502
+ this._ids.clear();
1642
2503
  }
1643
2504
  }
1644
- Importer.$inject = ['formFieldRegistry', 'formFields', 'formLayouter'];
1645
-
1646
- var importModule = {
1647
- importer: ['type', Importer]
1648
- };
2505
+ FormFieldRegistry.$inject = ['eventBus'];
1649
2506
 
1650
2507
  function formFieldClasses(type, {
1651
2508
  errors = [],
@@ -1679,7 +2536,7 @@ function prefixId(id, formId) {
1679
2536
  return `fjs-form-${id}`;
1680
2537
  }
1681
2538
 
1682
- const type$b = 'button';
2539
+ const type$c = 'button';
1683
2540
  function Button(props) {
1684
2541
  const {
1685
2542
  disabled,
@@ -1689,7 +2546,7 @@ function Button(props) {
1689
2546
  action = 'submit'
1690
2547
  } = field;
1691
2548
  return jsxRuntime.jsx("div", {
1692
- class: formFieldClasses(type$b),
2549
+ class: formFieldClasses(type$c),
1693
2550
  children: jsxRuntime.jsx("button", {
1694
2551
  class: "fjs-button",
1695
2552
  type: action,
@@ -1699,8 +2556,8 @@ function Button(props) {
1699
2556
  });
1700
2557
  }
1701
2558
  Button.config = {
1702
- type: type$b,
1703
- keyed: true,
2559
+ type: type$c,
2560
+ keyed: false,
1704
2561
  label: 'Button',
1705
2562
  group: 'action',
1706
2563
  create: (options = {}) => ({
@@ -1710,6 +2567,9 @@ Button.config = {
1710
2567
  };
1711
2568
 
1712
2569
  const FormRenderContext = preact.createContext({
2570
+ EmptyRoot: props => {
2571
+ return null;
2572
+ },
1713
2573
  Empty: props => {
1714
2574
  return null;
1715
2575
  },
@@ -1739,15 +2599,19 @@ const FormRenderContext = preact.createContext({
1739
2599
  class: props.class,
1740
2600
  children: props.children
1741
2601
  });
2602
+ },
2603
+ hoveredId: [],
2604
+ setHoveredId: newValue => {
2605
+ console.log(`setHoveredId not defined, called with '${newValue}'`);
1742
2606
  }
1743
2607
  });
1744
2608
  var FormRenderContext$1 = FormRenderContext;
1745
2609
 
1746
- /**
1747
- * @param {string} type
1748
- * @param {boolean} [strict]
1749
- *
1750
- * @returns {any}
2610
+ /**
2611
+ * @param {string} type
2612
+ * @param {boolean} [strict]
2613
+ *
2614
+ * @returns {any}
1751
2615
  */
1752
2616
  function getService(type, strict) {}
1753
2617
  const FormContext = preact.createContext({
@@ -1763,10 +2627,10 @@ function useService(type, strict) {
1763
2627
  return getService(type, strict);
1764
2628
  }
1765
2629
 
1766
- /**
1767
- * Returns the conditionally filtered data of a form reactively.
1768
- * Memoised to minimize re-renders
1769
- *
2630
+ /**
2631
+ * Returns the conditionally filtered data of a form reactively.
2632
+ * Memoised to minimize re-renders
2633
+ *
1770
2634
  */
1771
2635
  function useFilteredFormData() {
1772
2636
  const {
@@ -1783,12 +2647,12 @@ function useFilteredFormData() {
1783
2647
  }, [conditionChecker, data, initialData]);
1784
2648
  }
1785
2649
 
1786
- /**
1787
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
1788
- *
1789
- * @param {string | undefined} condition
1790
- *
1791
- * @returns {boolean} true if condition is met or no condition or condition checker exists
2650
+ /**
2651
+ * Evaluate if condition is met reactively based on the conditionChecker and form data.
2652
+ *
2653
+ * @param {string | undefined} condition
2654
+ *
2655
+ * @returns {boolean} true if condition is met or no condition or condition checker exists
1792
2656
  */
1793
2657
  function useCondition(condition) {
1794
2658
  const conditionChecker = useService('conditionChecker', false);
@@ -1798,13 +2662,13 @@ function useCondition(condition) {
1798
2662
  }, [conditionChecker, condition, filteredData]);
1799
2663
  }
1800
2664
 
1801
- /**
1802
- * Evaluate a string reactively based on the expressionLanguage and form data.
1803
- * If the string is not an expression, it is returned as is.
1804
- * Memoised to minimize re-renders.
1805
- *
1806
- * @param {string} value
1807
- *
2665
+ /**
2666
+ * Evaluate a string reactively based on the expressionLanguage and form data.
2667
+ * If the string is not an expression, it is returned as is.
2668
+ * Memoised to minimize re-renders.
2669
+ *
2670
+ * @param {string} value
2671
+ *
1808
2672
  */
1809
2673
  function useExpressionEvaluation(value) {
1810
2674
  const formData = useFilteredFormData();
@@ -1833,16 +2697,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
1833
2697
  });
1834
2698
  }
1835
2699
 
1836
- /**
1837
- * Retrieve readonly value of a form field, given it can be an
1838
- * expression optionally or configured globally.
1839
- *
1840
- * @typedef { import('../../types').FormProperties } FormProperties
1841
- *
1842
- * @param {any} formField
1843
- * @param {FormProperties} properties
1844
- *
1845
- * @returns {boolean}
2700
+ /**
2701
+ * Retrieve readonly value of a form field, given it can be an
2702
+ * expression optionally or configured globally.
2703
+ *
2704
+ * @typedef { import('../../types').FormProperties } FormProperties
2705
+ *
2706
+ * @param {any} formField
2707
+ * @param {FormProperties} properties
2708
+ *
2709
+ * @returns {boolean}
1846
2710
  */
1847
2711
  function useReadonly(formField, properties = {}) {
1848
2712
  const expressionLanguage = useService('expressionLanguage');
@@ -1860,16 +2724,16 @@ function useReadonly(formField, properties = {}) {
1860
2724
  return readonly || false;
1861
2725
  }
1862
2726
 
1863
- /**
1864
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1865
- * Memoised to minimize re-renders
1866
- *
1867
- * @param {string} value
1868
- * @param {Object} options
1869
- * @param {boolean} [options.debug = false]
1870
- * @param {boolean} [options.strict = false]
1871
- * @param {Function} [options.buildDebugString]
1872
- *
2727
+ /**
2728
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2729
+ * Memoised to minimize re-renders
2730
+ *
2731
+ * @param {string} value
2732
+ * @param {Object} options
2733
+ * @param {boolean} [options.debug = false]
2734
+ * @param {boolean} [options.strict = false]
2735
+ * @param {Function} [options.buildDebugString]
2736
+ *
1873
2737
  */
1874
2738
  function useTemplateEvaluation(value, options) {
1875
2739
  const filteredData = useFilteredFormData();
@@ -1882,17 +2746,17 @@ function useTemplateEvaluation(value, options) {
1882
2746
  }, [filteredData, templating, value, options]);
1883
2747
  }
1884
2748
 
1885
- /**
1886
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1887
- * If the string contains multiple lines, only the first line is returned.
1888
- * Memoised to minimize re-renders
1889
- *
1890
- * @param {string} value
1891
- * @param {Object} [options]
1892
- * @param {boolean} [options.debug = false]
1893
- * @param {boolean} [options.strict = false]
1894
- * @param {Function} [options.buildDebugString]
1895
- *
2749
+ /**
2750
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2751
+ * If the string contains multiple lines, only the first line is returned.
2752
+ * Memoised to minimize re-renders
2753
+ *
2754
+ * @param {string} value
2755
+ * @param {Object} [options]
2756
+ * @param {boolean} [options.debug = false]
2757
+ * @param {boolean} [options.strict = false]
2758
+ * @param {Function} [options.buildDebugString]
2759
+ *
1896
2760
  */
1897
2761
  function useSingleLineTemplateEvaluation(value, options = {}) {
1898
2762
  const evaluatedTemplate = useTemplateEvaluation(value, options);
@@ -1959,11 +2823,12 @@ function Label(props) {
1959
2823
  });
1960
2824
  }
1961
2825
 
1962
- const type$a = 'checkbox';
2826
+ const type$b = 'checkbox';
1963
2827
  function Checkbox(props) {
1964
2828
  const {
1965
2829
  disabled,
1966
2830
  errors = [],
2831
+ onBlur,
1967
2832
  field,
1968
2833
  readonly,
1969
2834
  value = false
@@ -1990,7 +2855,7 @@ function Checkbox(props) {
1990
2855
  } = hooks.useContext(FormContext$1);
1991
2856
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
1992
2857
  return jsxRuntime.jsxs("div", {
1993
- class: classNames(formFieldClasses(type$a, {
2858
+ class: classNames(formFieldClasses(type$b, {
1994
2859
  errors,
1995
2860
  disabled,
1996
2861
  readonly
@@ -2009,6 +2874,7 @@ function Checkbox(props) {
2009
2874
  id: prefixId(id, formId),
2010
2875
  type: "checkbox",
2011
2876
  onChange: onChange,
2877
+ onBlur: onBlur,
2012
2878
  "aria-describedby": errorMessageId
2013
2879
  })
2014
2880
  }), jsxRuntime.jsx(Description, {
@@ -2020,7 +2886,7 @@ function Checkbox(props) {
2020
2886
  });
2021
2887
  }
2022
2888
  Checkbox.config = {
2023
- type: type$a,
2889
+ type: type$b,
2024
2890
  keyed: true,
2025
2891
  label: 'Checkbox',
2026
2892
  group: 'selection',
@@ -2078,9 +2944,24 @@ function _isReadableType(value) {
2078
2944
  function _isValueSomething(value) {
2079
2945
  return value || value === 0 || value === false;
2080
2946
  }
2947
+ function createEmptyOptions(options = {}) {
2948
+ const defaults = {};
2081
2949
 
2082
- /**
2083
- * @enum { String }
2950
+ // provide default values if valuesKey and valuesExpression are not set
2951
+ if (!options.valuesKey && !options.valuesExpression) {
2952
+ defaults.values = [{
2953
+ label: 'Value',
2954
+ value: 'value'
2955
+ }];
2956
+ }
2957
+ return {
2958
+ ...defaults,
2959
+ ...options
2960
+ };
2961
+ }
2962
+
2963
+ /**
2964
+ * @enum { String }
2084
2965
  */
2085
2966
  const LOAD_STATES = {
2086
2967
  LOADING: 'loading',
@@ -2088,17 +2969,17 @@ const LOAD_STATES = {
2088
2969
  ERROR: 'error'
2089
2970
  };
2090
2971
 
2091
- /**
2092
- * @typedef {Object} ValuesGetter
2093
- * @property {Object[]} values - The values data
2094
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2972
+ /**
2973
+ * @typedef {Object} ValuesGetter
2974
+ * @property {Object[]} values - The values data
2975
+ * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2095
2976
  */
2096
2977
 
2097
- /**
2098
- * A hook to load values for single and multiselect components.
2099
- *
2100
- * @param {Object} field - The form field to handle values for
2101
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2978
+ /**
2979
+ * A hook to load values for single and multiselect components.
2980
+ *
2981
+ * @param {Object} field - The form field to handle values for
2982
+ * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2102
2983
  */
2103
2984
  function useValuesAsync (field) {
2104
2985
  const {
@@ -2363,11 +3244,12 @@ function sanitizeMultiSelectValue(options) {
2363
3244
  }
2364
3245
  }
2365
3246
 
2366
- const type$9 = 'checklist';
3247
+ const type$a = 'checklist';
2367
3248
  function Checklist(props) {
2368
3249
  const {
2369
3250
  disabled,
2370
3251
  errors = [],
3252
+ onBlur,
2371
3253
  field,
2372
3254
  readonly,
2373
3255
  value = []
@@ -2378,6 +3260,7 @@ function Checklist(props) {
2378
3260
  label,
2379
3261
  validate = {}
2380
3262
  } = field;
3263
+ const outerDivRef = hooks.useRef();
2381
3264
  const {
2382
3265
  required
2383
3266
  } = validate;
@@ -2393,6 +3276,12 @@ function Checklist(props) {
2393
3276
  value: newValue
2394
3277
  });
2395
3278
  };
3279
+ const onCheckboxBlur = e => {
3280
+ if (outerDivRef.current.contains(e.relatedTarget)) {
3281
+ return;
3282
+ }
3283
+ onBlur();
3284
+ };
2396
3285
  const {
2397
3286
  state: loadState,
2398
3287
  values: options
@@ -2402,11 +3291,12 @@ function Checklist(props) {
2402
3291
  } = hooks.useContext(FormContext$1);
2403
3292
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
2404
3293
  return jsxRuntime.jsxs("div", {
2405
- class: classNames(formFieldClasses(type$9, {
3294
+ class: classNames(formFieldClasses(type$a, {
2406
3295
  errors,
2407
3296
  disabled,
2408
3297
  readonly
2409
3298
  })),
3299
+ ref: outerDivRef,
2410
3300
  children: [jsxRuntime.jsx(Label, {
2411
3301
  label: label,
2412
3302
  required: required
@@ -2426,6 +3316,7 @@ function Checklist(props) {
2426
3316
  id: prefixId(`${id}-${index}`, formId),
2427
3317
  type: "checkbox",
2428
3318
  onClick: () => toggleCheckbox(v.value),
3319
+ onBlur: onCheckboxBlur,
2429
3320
  "aria-describedby": errorMessageId
2430
3321
  })
2431
3322
  }, `${id}-${index}`);
@@ -2438,27 +3329,13 @@ function Checklist(props) {
2438
3329
  });
2439
3330
  }
2440
3331
  Checklist.config = {
2441
- type: type$9,
3332
+ type: type$a,
2442
3333
  keyed: true,
2443
3334
  label: 'Checklist',
2444
3335
  group: 'selection',
2445
3336
  emptyValue: [],
2446
3337
  sanitizeValue: sanitizeMultiSelectValue,
2447
- create: (options = {}) => {
2448
- const defaults = {};
2449
-
2450
- // provide default values if valuesKey isn't set
2451
- if (!options.valuesKey) {
2452
- defaults.values = [{
2453
- label: 'Value',
2454
- value: 'value'
2455
- }];
2456
- }
2457
- return {
2458
- ...defaults,
2459
- ...options
2460
- };
2461
- }
3338
+ create: createEmptyOptions
2462
3339
  };
2463
3340
 
2464
3341
  const noop$1 = () => false;
@@ -2468,8 +3345,11 @@ function FormField(props) {
2468
3345
  onChange
2469
3346
  } = props;
2470
3347
  const formFields = useService('formFields'),
3348
+ viewerCommands = useService('viewerCommands', false),
3349
+ pathRegistry = useService('pathRegistry'),
2471
3350
  form = useService('form');
2472
3351
  const {
3352
+ initialData,
2473
3353
  data,
2474
3354
  errors,
2475
3355
  properties
@@ -2483,12 +3363,23 @@ function FormField(props) {
2483
3363
  if (!FormFieldComponent) {
2484
3364
  throw new Error(`cannot render field <${field.type}>`);
2485
3365
  }
2486
- const value = minDash.get(data, field._path);
2487
- const fieldErrors = findErrors(errors, field._path);
3366
+ const valuePath = hooks.useMemo(() => pathRegistry.getValuePath(field), [field, pathRegistry]);
3367
+ const initialValue = hooks.useMemo(() => minDash.get(initialData, valuePath), [initialData, valuePath]);
2488
3368
  const readonly = useReadonly(field, properties);
3369
+ const value = minDash.get(data, valuePath);
2489
3370
 
2490
3371
  // add precedence: global readonly > form field disabled
2491
3372
  const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
3373
+ const onBlur = hooks.useCallback(() => {
3374
+ if (viewerCommands) {
3375
+ viewerCommands.updateFieldValidation(field, value);
3376
+ }
3377
+ }, [viewerCommands, field, value]);
3378
+ hooks.useEffect(() => {
3379
+ if (viewerCommands && initialValue) {
3380
+ viewerCommands.updateFieldValidation(field, initialValue);
3381
+ }
3382
+ }, [viewerCommands, field, initialValue]);
2492
3383
  const hidden = useCondition(field.conditional && field.conditional.hide || null);
2493
3384
  if (hidden) {
2494
3385
  return jsxRuntime.jsx(Empty, {});
@@ -2502,8 +3393,9 @@ function FormField(props) {
2502
3393
  children: jsxRuntime.jsx(FormFieldComponent, {
2503
3394
  ...props,
2504
3395
  disabled: disabled,
2505
- errors: fieldErrors,
3396
+ errors: errors[field.id],
2506
3397
  onChange: disabled || readonly ? noop$1 : onChange,
3398
+ onBlur: disabled || readonly ? noop$1 : onBlur,
2507
3399
  readonly: readonly,
2508
3400
  value: value
2509
3401
  })
@@ -2511,14 +3403,14 @@ function FormField(props) {
2511
3403
  });
2512
3404
  }
2513
3405
 
2514
- function Default(props) {
3406
+ function Grid(props) {
2515
3407
  const {
2516
3408
  Children,
2517
- Empty,
2518
3409
  Row
2519
3410
  } = hooks.useContext(FormRenderContext$1);
2520
3411
  const {
2521
- field
3412
+ field,
3413
+ Empty
2522
3414
  } = props;
2523
3415
  const {
2524
3416
  id,
@@ -2555,7 +3447,20 @@ function Default(props) {
2555
3447
  }), components.length ? null : jsxRuntime.jsx(Empty, {})]
2556
3448
  });
2557
3449
  }
2558
- Default.config = {
3450
+
3451
+ function FormComponent$1(props) {
3452
+ const {
3453
+ EmptyRoot
3454
+ } = hooks.useContext(FormRenderContext$1);
3455
+ const fullProps = {
3456
+ ...props,
3457
+ Empty: EmptyRoot
3458
+ };
3459
+ return jsxRuntime.jsx(Grid, {
3460
+ ...fullProps
3461
+ });
3462
+ }
3463
+ FormComponent$1.config = {
2559
3464
  type: 'default',
2560
3465
  keyed: false,
2561
3466
  label: null,
@@ -2566,16 +3471,16 @@ Default.config = {
2566
3471
  })
2567
3472
  };
2568
3473
 
2569
- var _path$g;
2570
- 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); }
3474
+ var _path$h;
3475
+ 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); }
2571
3476
  var SvgCalendar = function SvgCalendar(props) {
2572
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$j({
3477
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$k({
2573
3478
  xmlns: "http://www.w3.org/2000/svg",
2574
3479
  width: 14,
2575
3480
  height: 15,
2576
3481
  fill: "none",
2577
3482
  viewBox: "0 0 28 30"
2578
- }, props), _path$g || (_path$g = /*#__PURE__*/React__namespace.createElement("path", {
3483
+ }, props), _path$h || (_path$h = /*#__PURE__*/React__namespace.createElement("path", {
2579
3484
  fill: "currentColor",
2580
3485
  fillRule: "evenodd",
2581
3486
  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",
@@ -2584,6 +3489,74 @@ var SvgCalendar = function SvgCalendar(props) {
2584
3489
  };
2585
3490
  var CalendarIcon = SvgCalendar;
2586
3491
 
3492
+ /**
3493
+ * Returns date format for the provided locale.
3494
+ * If the locale is not provided, uses the browser's locale.
3495
+ *
3496
+ * @param {string} [locale] - The locale to get date format for.
3497
+ * @returns {string} The date format for the locale.
3498
+ */
3499
+ function getLocaleDateFormat(locale = 'default') {
3500
+ const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(Date.UTC(2020, 5, 5)));
3501
+ return parts.map(part => {
3502
+ const len = part.value.length;
3503
+ switch (part.type) {
3504
+ case 'day':
3505
+ return 'd'.repeat(len);
3506
+ case 'month':
3507
+ return 'M'.repeat(len);
3508
+ case 'year':
3509
+ return 'y'.repeat(len);
3510
+ default:
3511
+ return part.value;
3512
+ }
3513
+ }).join('');
3514
+ }
3515
+
3516
+ /**
3517
+ * Returns readable date format for the provided locale.
3518
+ * If the locale is not provided, uses the browser's locale.
3519
+ *
3520
+ * @param {string} [locale] - The locale to get readable date format for.
3521
+ * @returns {string} The readable date format for the locale.
3522
+ */
3523
+ function getLocaleReadableDateFormat(locale) {
3524
+ let format = getLocaleDateFormat(locale).toLowerCase();
3525
+
3526
+ // Ensure month is in 'mm' format
3527
+ if (!format.includes('mm')) {
3528
+ format = format.replace('m', 'mm');
3529
+ }
3530
+
3531
+ // Ensure day is in 'dd' format
3532
+ if (!format.includes('dd')) {
3533
+ format = format.replace('d', 'dd');
3534
+ }
3535
+ return format;
3536
+ }
3537
+
3538
+ /**
3539
+ * Returns flatpickr config for the provided locale.
3540
+ * If the locale is not provided, uses the browser's locale.
3541
+ *
3542
+ * @param {string} [locale] - The locale to get flatpickr config for.
3543
+ * @returns {object} The flatpickr config for the locale.
3544
+ */
3545
+ function getLocaleDateFlatpickrConfig(locale) {
3546
+ return flatpickerizeDateFormat(getLocaleDateFormat(locale));
3547
+ }
3548
+ function flatpickerizeDateFormat(dateFormat) {
3549
+ const useLeadingZero = {
3550
+ day: dateFormat.includes('dd'),
3551
+ month: dateFormat.includes('MM'),
3552
+ year: dateFormat.includes('yyyy')
3553
+ };
3554
+ dateFormat = useLeadingZero.day ? dateFormat.replace('dd', 'd') : dateFormat.replace('d', 'j');
3555
+ dateFormat = useLeadingZero.month ? dateFormat.replace('MM', 'm') : dateFormat.replace('M', 'n');
3556
+ dateFormat = useLeadingZero.year ? dateFormat.replace('yyyy', 'Y') : dateFormat.replace('yy', 'y');
3557
+ return dateFormat;
3558
+ }
3559
+
2587
3560
  function InputAdorner(props) {
2588
3561
  const {
2589
3562
  pre,
@@ -2627,6 +3600,7 @@ function Datepicker(props) {
2627
3600
  id,
2628
3601
  label,
2629
3602
  collapseLabelOnEmpty,
3603
+ onDateTimeBlur,
2630
3604
  formId,
2631
3605
  required,
2632
3606
  disabled,
@@ -2657,7 +3631,7 @@ function Datepicker(props) {
2657
3631
  hooks.useEffect(() => {
2658
3632
  let config = {
2659
3633
  allowInput: true,
2660
- dateFormat: 'm/d/Y',
3634
+ dateFormat: getLocaleDateFlatpickrConfig(),
2661
3635
  static: true,
2662
3636
  clickOpens: false,
2663
3637
  // TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
@@ -2721,7 +3695,8 @@ function Datepicker(props) {
2721
3695
  if (!isInputDirty || e.relatedTarget && e.relatedTarget.classList.contains('flatpickr-day')) return;
2722
3696
  dateInputRef.current.dispatchEvent(ENTER_KEYDOWN_EVENT);
2723
3697
  setIsInputDirty(false);
2724
- }, [isInputDirty]);
3698
+ onDateTimeBlur(e);
3699
+ }, [isInputDirty, onDateTimeBlur]);
2725
3700
  const fullId = `${prefixId(id, formId)}--date`;
2726
3701
  return jsxRuntime.jsxs("div", {
2727
3702
  class: "fjs-datetime-subsection",
@@ -2748,7 +3723,7 @@ function Datepicker(props) {
2748
3723
  class: "fjs-input",
2749
3724
  disabled: disabled,
2750
3725
  readOnly: readonly,
2751
- placeholder: "mm/dd/yyyy",
3726
+ placeholder: getLocaleReadableDateFormat(),
2752
3727
  autoComplete: "off",
2753
3728
  onFocus: onInputFocus,
2754
3729
  onKeyDown: onInputKeyDown,
@@ -2763,19 +3738,19 @@ function Datepicker(props) {
2763
3738
  });
2764
3739
  }
2765
3740
 
2766
- var _path$f, _path2$3;
2767
- 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); }
3741
+ var _path$g, _path2$4;
3742
+ 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); }
2768
3743
  var SvgClock = function SvgClock(props) {
2769
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$i({
3744
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$j({
2770
3745
  xmlns: "http://www.w3.org/2000/svg",
2771
3746
  width: 16,
2772
3747
  height: 16,
2773
3748
  fill: "none",
2774
3749
  viewBox: "0 0 28 29"
2775
- }, props), _path$f || (_path$f = /*#__PURE__*/React__namespace.createElement("path", {
3750
+ }, props), _path$g || (_path$g = /*#__PURE__*/React__namespace.createElement("path", {
2776
3751
  fill: "currentColor",
2777
3752
  d: "M13 14.41 18.59 20 20 18.59l-5-5.01V5h-2v9.41Z"
2778
- })), _path2$3 || (_path2$3 = /*#__PURE__*/React__namespace.createElement("path", {
3753
+ })), _path2$4 || (_path2$4 = /*#__PURE__*/React__namespace.createElement("path", {
2779
3754
  fill: "currentColor",
2780
3755
  fillRule: "evenodd",
2781
3756
  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",
@@ -2884,6 +3859,7 @@ function Timepicker(props) {
2884
3859
  id,
2885
3860
  label,
2886
3861
  collapseLabelOnEmpty,
3862
+ onDateTimeBlur,
2887
3863
  formId,
2888
3864
  required,
2889
3865
  disabled,
@@ -2983,6 +3959,7 @@ function Timepicker(props) {
2983
3959
  const onInputBlur = e => {
2984
3960
  setDropdownIsOpen(false);
2985
3961
  propagateRawToMinute();
3962
+ onDateTimeBlur(e);
2986
3963
  };
2987
3964
  const onDropdownValueSelected = value => {
2988
3965
  setDropdownIsOpen(false);
@@ -3038,11 +4015,12 @@ function Timepicker(props) {
3038
4015
  });
3039
4016
  }
3040
4017
 
3041
- const type$8 = 'datetime';
4018
+ const type$9 = 'datetime';
3042
4019
  function Datetime(props) {
3043
4020
  const {
3044
4021
  disabled,
3045
4022
  errors = [],
4023
+ onBlur,
3046
4024
  field,
3047
4025
  onChange,
3048
4026
  readonly,
@@ -3066,6 +4044,7 @@ function Datetime(props) {
3066
4044
  const {
3067
4045
  formId
3068
4046
  } = hooks.useContext(FormContext$1);
4047
+ const dateTimeGroupRef = hooks.useRef();
3069
4048
  const getNullDateTime = () => ({
3070
4049
  date: new Date(Date.parse(null)),
3071
4050
  time: null
@@ -3076,6 +4055,12 @@ function Datetime(props) {
3076
4055
  const isValidTime = time => !isNaN(parseInt(time));
3077
4056
  const useDatePicker = hooks.useMemo(() => subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
3078
4057
  const useTimePicker = hooks.useMemo(() => subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
4058
+ const onDateTimeBlur = hooks.useCallback(e => {
4059
+ if (e.relatedTarget && dateTimeGroupRef.current.contains(e.relatedTarget)) {
4060
+ return;
4061
+ }
4062
+ onBlur();
4063
+ }, [onBlur]);
3079
4064
  hooks.useEffect(() => {
3080
4065
  let {
3081
4066
  date,
@@ -3167,6 +4152,7 @@ function Datetime(props) {
3167
4152
  id,
3168
4153
  label: dateLabel,
3169
4154
  collapseLabelOnEmpty: !timeLabel,
4155
+ onDateTimeBlur,
3170
4156
  formId,
3171
4157
  required,
3172
4158
  disabled,
@@ -3180,6 +4166,7 @@ function Datetime(props) {
3180
4166
  id,
3181
4167
  label: timeLabel,
3182
4168
  collapseLabelOnEmpty: !dateLabel,
4169
+ onDateTimeBlur,
3183
4170
  formId,
3184
4171
  required,
3185
4172
  disabled,
@@ -3191,13 +4178,14 @@ function Datetime(props) {
3191
4178
  'aria-describedby': errorMessageId
3192
4179
  };
3193
4180
  return jsxRuntime.jsxs("div", {
3194
- class: formFieldClasses(type$8, {
4181
+ class: formFieldClasses(type$9, {
3195
4182
  errors: allErrors,
3196
4183
  disabled,
3197
4184
  readonly
3198
4185
  }),
3199
4186
  children: [jsxRuntime.jsxs("div", {
3200
4187
  class: classNames('fjs-vertical-group'),
4188
+ ref: dateTimeGroupRef,
3201
4189
  children: [useDatePicker && jsxRuntime.jsx(Datepicker, {
3202
4190
  ...datePickerProps
3203
4191
  }), useTimePicker && useDatePicker && jsxRuntime.jsx("div", {
@@ -3214,7 +4202,7 @@ function Datetime(props) {
3214
4202
  });
3215
4203
  }
3216
4204
  Datetime.config = {
3217
- type: type$8,
4205
+ type: type$9,
3218
4206
  keyed: true,
3219
4207
  label: 'Date time',
3220
4208
  group: 'basic-input',
@@ -3231,10 +4219,10 @@ Datetime.config = {
3231
4219
  }
3232
4220
  };
3233
4221
 
3234
- /**
3235
- * This file must not be changed or exchanged.
3236
- *
3237
- * @see http://bpmn.io/license for more information.
4222
+ /**
4223
+ * This file must not be changed or exchanged.
4224
+ *
4225
+ * @see http://bpmn.io/license for more information.
3238
4226
  */
3239
4227
  function Logo() {
3240
4228
  return jsxRuntime.jsxs("svg", {
@@ -3356,6 +4344,52 @@ function FormComponent(props) {
3356
4344
  });
3357
4345
  }
3358
4346
 
4347
+ function Group(props) {
4348
+ const {
4349
+ field
4350
+ } = props;
4351
+ const {
4352
+ label,
4353
+ id,
4354
+ type,
4355
+ showOutline
4356
+ } = field;
4357
+ const {
4358
+ formId
4359
+ } = hooks.useContext(FormContext$1);
4360
+ const {
4361
+ Empty
4362
+ } = hooks.useContext(FormRenderContext$1);
4363
+ const fullProps = {
4364
+ ...props,
4365
+ Empty
4366
+ };
4367
+ return jsxRuntime.jsxs("div", {
4368
+ className: classNames(formFieldClasses(type), {
4369
+ 'fjs-outlined': showOutline
4370
+ }),
4371
+ role: "group",
4372
+ "aria-labelledby": prefixId(id, formId),
4373
+ children: [jsxRuntime.jsx(Label, {
4374
+ id: prefixId(id, formId),
4375
+ label: label
4376
+ }), jsxRuntime.jsx(Grid, {
4377
+ ...fullProps
4378
+ })]
4379
+ });
4380
+ }
4381
+ Group.config = {
4382
+ type: 'group',
4383
+ pathed: true,
4384
+ label: 'Group',
4385
+ group: 'presentation',
4386
+ create: (options = {}) => ({
4387
+ components: [],
4388
+ showOutline: true,
4389
+ ...options
4390
+ })
4391
+ };
4392
+
3359
4393
  const NODE_TYPE_TEXT = 3,
3360
4394
  NODE_TYPE_ELEMENT = 1;
3361
4395
  const ALLOWED_NODES = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'em', 'a', 'p', 'div', 'ul', 'ol', 'li', 'hr', 'blockquote', 'img', 'pre', 'code', 'br', 'strong', 'table', 'thead', 'tbody', 'tr', 'th', 'td'];
@@ -3366,11 +4400,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
3366
4400
 
3367
4401
  const FORM_ELEMENT = document.createElement('form');
3368
4402
 
3369
- /**
3370
- * Sanitize a HTML string and return the cleaned, safe version.
3371
- *
3372
- * @param {string} html
3373
- * @return {string}
4403
+ /**
4404
+ * Sanitize a HTML string and return the cleaned, safe version.
4405
+ *
4406
+ * @param {string} html
4407
+ * @return {string}
3374
4408
  */
3375
4409
 
3376
4410
  // see https://github.com/developit/snarkdown/issues/70
@@ -3388,29 +4422,29 @@ function sanitizeHTML(html) {
3388
4422
  }
3389
4423
  }
3390
4424
 
3391
- /**
3392
- * Sanitizes an image source to ensure we only allow for data URI and links
3393
- * that start with http(s).
3394
- *
3395
- * Note: Most browsers anyway do not support script execution in <img> elements.
3396
- *
3397
- * @param {string} src
3398
- * @returns {string}
4425
+ /**
4426
+ * Sanitizes an image source to ensure we only allow for data URI and links
4427
+ * that start with http(s).
4428
+ *
4429
+ * Note: Most browsers anyway do not support script execution in <img> elements.
4430
+ *
4431
+ * @param {string} src
4432
+ * @returns {string}
3399
4433
  */
3400
4434
  function sanitizeImageSource(src) {
3401
4435
  const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
3402
4436
  return valid ? src : '';
3403
4437
  }
3404
4438
 
3405
- /**
3406
- * Recursively sanitize a HTML node, potentially
3407
- * removing it, its children or attributes.
3408
- *
3409
- * Inspired by https://github.com/developit/snarkdown/issues/70
3410
- * and https://github.com/cure53/DOMPurify. Simplified
3411
- * for our use-case.
3412
- *
3413
- * @param {Element} node
4439
+ /**
4440
+ * Recursively sanitize a HTML node, potentially
4441
+ * removing it, its children or attributes.
4442
+ *
4443
+ * Inspired by https://github.com/developit/snarkdown/issues/70
4444
+ * and https://github.com/cure53/DOMPurify. Simplified
4445
+ * for our use-case.
4446
+ *
4447
+ * @param {Element} node
3414
4448
  */
3415
4449
  function sanitizeNode(node) {
3416
4450
  // allow text nodes
@@ -3454,13 +4488,13 @@ function sanitizeNode(node) {
3454
4488
  }
3455
4489
  }
3456
4490
 
3457
- /**
3458
- * Validates attributes for validity.
3459
- *
3460
- * @param {string} lcTag
3461
- * @param {string} lcName
3462
- * @param {string} value
3463
- * @return {boolean}
4491
+ /**
4492
+ * Validates attributes for validity.
4493
+ *
4494
+ * @param {string} lcTag
4495
+ * @param {string} lcName
4496
+ * @param {string} value
4497
+ * @return {boolean}
3464
4498
  */
3465
4499
  function isValidAttribute(lcTag, lcName, value) {
3466
4500
  // disallow most attributes based on whitelist
@@ -3483,9 +4517,9 @@ function isValidAttribute(lcTag, lcName, value) {
3483
4517
  return true;
3484
4518
  }
3485
4519
 
3486
- 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); }
4520
+ 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); }
3487
4521
  var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
3488
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$h({
4522
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$i({
3489
4523
  xmlns: "http://www.w3.org/2000/svg",
3490
4524
  xmlSpace: "preserve",
3491
4525
  width: 64,
@@ -3524,7 +4558,7 @@ var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
3524
4558
  };
3525
4559
  var ImagePlaceholder = SvgImagePlaceholder;
3526
4560
 
3527
- const type$7 = 'image';
4561
+ const type$8 = 'image';
3528
4562
  function Image(props) {
3529
4563
  const {
3530
4564
  field
@@ -3545,7 +4579,7 @@ function Image(props) {
3545
4579
  formId
3546
4580
  } = hooks.useContext(FormContext$1);
3547
4581
  return jsxRuntime.jsx("div", {
3548
- class: formFieldClasses(type$7),
4582
+ class: formFieldClasses(type$8),
3549
4583
  children: jsxRuntime.jsxs("div", {
3550
4584
  class: "fjs-image-container",
3551
4585
  children: [safeSource && jsxRuntime.jsx("img", {
@@ -3563,7 +4597,7 @@ function Image(props) {
3563
4597
  });
3564
4598
  }
3565
4599
  Image.config = {
3566
- type: type$7,
4600
+ type: type$8,
3567
4601
  keyed: false,
3568
4602
  label: 'Image view',
3569
4603
  group: 'presentation',
@@ -3590,14 +4624,14 @@ function TemplatedInputAdorner(props) {
3590
4624
  });
3591
4625
  }
3592
4626
 
3593
- var _path$e;
3594
- 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); }
4627
+ var _path$f;
4628
+ 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); }
3595
4629
  var SvgAngelDown = function SvgAngelDown(props) {
3596
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$g({
4630
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$h({
3597
4631
  xmlns: "http://www.w3.org/2000/svg",
3598
4632
  width: 8,
3599
4633
  height: 8
3600
- }, props), _path$e || (_path$e = /*#__PURE__*/React__namespace.createElement("path", {
4634
+ }, props), _path$f || (_path$f = /*#__PURE__*/React__namespace.createElement("path", {
3601
4635
  fill: "currentColor",
3602
4636
  fillRule: "evenodd",
3603
4637
  stroke: "currentColor",
@@ -3608,14 +4642,14 @@ var SvgAngelDown = function SvgAngelDown(props) {
3608
4642
  };
3609
4643
  var AngelDownIcon = SvgAngelDown;
3610
4644
 
3611
- var _path$d;
3612
- 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); }
4645
+ var _path$e;
4646
+ 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); }
3613
4647
  var SvgAngelUp = function SvgAngelUp(props) {
3614
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$f({
4648
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$g({
3615
4649
  xmlns: "http://www.w3.org/2000/svg",
3616
4650
  width: 8,
3617
4651
  height: 8
3618
- }, props), _path$d || (_path$d = /*#__PURE__*/React__namespace.createElement("path", {
4652
+ }, props), _path$e || (_path$e = /*#__PURE__*/React__namespace.createElement("path", {
3619
4653
  fill: "currentColor",
3620
4654
  fillRule: "evenodd",
3621
4655
  stroke: "currentColor",
@@ -3626,11 +4660,12 @@ var SvgAngelUp = function SvgAngelUp(props) {
3626
4660
  };
3627
4661
  var AngelUpIcon = SvgAngelUp;
3628
4662
 
3629
- const type$6 = 'number';
4663
+ const type$7 = 'number';
3630
4664
  function Numberfield(props) {
3631
4665
  const {
3632
4666
  disabled,
3633
4667
  errors = [],
4668
+ onBlur,
3634
4669
  field,
3635
4670
  value,
3636
4671
  readonly,
@@ -3763,7 +4798,7 @@ function Numberfield(props) {
3763
4798
  } = hooks.useContext(FormContext$1);
3764
4799
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3765
4800
  return jsxRuntime.jsxs("div", {
3766
- class: formFieldClasses(type$6, {
4801
+ class: formFieldClasses(type$7, {
3767
4802
  errors,
3768
4803
  disabled,
3769
4804
  readonly
@@ -3791,7 +4826,8 @@ function Numberfield(props) {
3791
4826
  readOnly: readonly,
3792
4827
  id: prefixId(id, formId),
3793
4828
  onKeyDown: onKeyDown,
3794
- onKeyPress: onKeyPress
4829
+ onKeyPress: onKeyPress,
4830
+ onBlur: onBlur
3795
4831
 
3796
4832
  // @ts-ignore
3797
4833
  ,
@@ -3834,7 +4870,7 @@ function Numberfield(props) {
3834
4870
  });
3835
4871
  }
3836
4872
  Numberfield.config = {
3837
- type: type$6,
4873
+ type: type$7,
3838
4874
  keyed: true,
3839
4875
  label: 'Number',
3840
4876
  group: 'basic-input',
@@ -3857,11 +4893,12 @@ Numberfield.config = {
3857
4893
  })
3858
4894
  };
3859
4895
 
3860
- const type$5 = 'radio';
4896
+ const type$6 = 'radio';
3861
4897
  function Radio(props) {
3862
4898
  const {
3863
4899
  disabled,
3864
4900
  errors = [],
4901
+ onBlur,
3865
4902
  field,
3866
4903
  readonly,
3867
4904
  value
@@ -3872,6 +4909,7 @@ function Radio(props) {
3872
4909
  label,
3873
4910
  validate = {}
3874
4911
  } = field;
4912
+ const outerDivRef = hooks.useRef();
3875
4913
  const {
3876
4914
  required
3877
4915
  } = validate;
@@ -3881,6 +4919,12 @@ function Radio(props) {
3881
4919
  value: v
3882
4920
  });
3883
4921
  };
4922
+ const onRadioBlur = e => {
4923
+ if (outerDivRef.current.contains(e.relatedTarget)) {
4924
+ return;
4925
+ }
4926
+ onBlur();
4927
+ };
3884
4928
  const {
3885
4929
  state: loadState,
3886
4930
  values: options
@@ -3890,11 +4934,12 @@ function Radio(props) {
3890
4934
  } = hooks.useContext(FormContext$1);
3891
4935
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3892
4936
  return jsxRuntime.jsxs("div", {
3893
- class: formFieldClasses(type$5, {
4937
+ class: formFieldClasses(type$6, {
3894
4938
  errors,
3895
4939
  disabled,
3896
4940
  readonly
3897
4941
  }),
4942
+ ref: outerDivRef,
3898
4943
  children: [jsxRuntime.jsx(Label, {
3899
4944
  label: label,
3900
4945
  required: required
@@ -3914,6 +4959,7 @@ function Radio(props) {
3914
4959
  id: prefixId(`${id}-${index}`, formId),
3915
4960
  type: "radio",
3916
4961
  onClick: () => onChange(option.value),
4962
+ onBlur: onRadioBlur,
3917
4963
  "aria-describedby": errorMessageId
3918
4964
  })
3919
4965
  }, `${id}-${index}`);
@@ -3926,37 +4972,23 @@ function Radio(props) {
3926
4972
  });
3927
4973
  }
3928
4974
  Radio.config = {
3929
- type: type$5,
4975
+ type: type$6,
3930
4976
  keyed: true,
3931
4977
  label: 'Radio',
3932
4978
  group: 'selection',
3933
4979
  emptyValue: null,
3934
4980
  sanitizeValue: sanitizeSingleSelectValue,
3935
- create: (options = {}) => {
3936
- const defaults = {};
3937
-
3938
- // provide default values if valuesKey isn't set
3939
- if (!options.valuesKey) {
3940
- defaults.values = [{
3941
- label: 'Value',
3942
- value: 'value'
3943
- }];
3944
- }
3945
- return {
3946
- ...defaults,
3947
- ...options
3948
- };
3949
- }
4981
+ create: createEmptyOptions
3950
4982
  };
3951
4983
 
3952
- var _path$c;
3953
- 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); }
4984
+ var _path$d;
4985
+ 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); }
3954
4986
  var SvgXMark = function SvgXMark(props) {
3955
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$e({
4987
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$f({
3956
4988
  xmlns: "http://www.w3.org/2000/svg",
3957
4989
  width: 8,
3958
4990
  height: 8
3959
- }, props), _path$c || (_path$c = /*#__PURE__*/React__namespace.createElement("path", {
4991
+ }, props), _path$d || (_path$d = /*#__PURE__*/React__namespace.createElement("path", {
3960
4992
  fill: "currentColor",
3961
4993
  fillRule: "evenodd",
3962
4994
  stroke: "currentColor",
@@ -3972,6 +5004,7 @@ function SearchableSelect(props) {
3972
5004
  id,
3973
5005
  disabled,
3974
5006
  errors,
5007
+ onBlur,
3975
5008
  field,
3976
5009
  readonly,
3977
5010
  value
@@ -4091,6 +5124,7 @@ function SearchableSelect(props) {
4091
5124
  onBlur: () => {
4092
5125
  setIsDropdownExpanded(false);
4093
5126
  setFilter(valueLabel);
5127
+ onBlur();
4094
5128
  },
4095
5129
  "aria-describedby": props['aria-describedby']
4096
5130
  }), displayState.displayCross && jsxRuntime.jsxs("span", {
@@ -4125,6 +5159,7 @@ function SimpleSelect(props) {
4125
5159
  id,
4126
5160
  disabled,
4127
5161
  errors,
5162
+ onBlur,
4128
5163
  field,
4129
5164
  readonly,
4130
5165
  value
@@ -4179,7 +5214,10 @@ function SimpleSelect(props) {
4179
5214
  'hasErrors': errors.length
4180
5215
  }),
4181
5216
  onFocus: () => setIsDropdownExpanded(true),
4182
- onBlur: () => setIsDropdownExpanded(false),
5217
+ onBlur: () => {
5218
+ setIsDropdownExpanded(false);
5219
+ onBlur();
5220
+ },
4183
5221
  onMouseDown: onMouseDown,
4184
5222
  children: [jsxRuntime.jsx("div", {
4185
5223
  class: classNames('fjs-select-display', {
@@ -4221,11 +5259,12 @@ function SimpleSelect(props) {
4221
5259
  });
4222
5260
  }
4223
5261
 
4224
- const type$4 = 'select';
5262
+ const type$5 = 'select';
4225
5263
  function Select(props) {
4226
5264
  const {
4227
5265
  disabled,
4228
5266
  errors = [],
5267
+ onBlur,
4229
5268
  field,
4230
5269
  onChange,
4231
5270
  readonly,
@@ -4249,14 +5288,15 @@ function Select(props) {
4249
5288
  id,
4250
5289
  disabled,
4251
5290
  errors,
5291
+ onBlur,
4252
5292
  field,
4253
5293
  value,
4254
5294
  onChange,
4255
5295
  readonly,
4256
5296
  'aria-describedby': errorMessageId
4257
- }), [disabled, errors, field, id, value, onChange, readonly, errorMessageId]);
5297
+ }), [disabled, errors, field, id, value, onChange, onBlur, readonly, errorMessageId]);
4258
5298
  return jsxRuntime.jsxs("div", {
4259
- class: formFieldClasses(type$4, {
5299
+ class: formFieldClasses(type$5, {
4260
5300
  errors,
4261
5301
  disabled,
4262
5302
  readonly
@@ -4284,27 +5324,39 @@ function Select(props) {
4284
5324
  });
4285
5325
  }
4286
5326
  Select.config = {
4287
- type: type$4,
5327
+ type: type$5,
4288
5328
  keyed: true,
4289
5329
  label: 'Select',
4290
5330
  group: 'selection',
4291
5331
  emptyValue: null,
4292
5332
  sanitizeValue: sanitizeSingleSelectValue,
4293
- create: (options = {}) => {
4294
- const defaults = {};
5333
+ create: createEmptyOptions
5334
+ };
4295
5335
 
4296
- // provide default values if valuesKey isn't set
4297
- if (!options.valuesKey) {
4298
- defaults.values = [{
4299
- label: 'Value',
4300
- value: 'value'
4301
- }];
5336
+ const type$4 = 'spacer';
5337
+ function Spacer(props) {
5338
+ const {
5339
+ field
5340
+ } = props;
5341
+ const {
5342
+ height = 60
5343
+ } = field;
5344
+ return jsxRuntime.jsx("div", {
5345
+ class: formFieldClasses(type$4),
5346
+ style: {
5347
+ height: height
4302
5348
  }
4303
- return {
4304
- ...defaults,
4305
- ...options
4306
- };
4307
- }
5349
+ });
5350
+ }
5351
+ Spacer.config = {
5352
+ type: type$4,
5353
+ keyed: false,
5354
+ label: 'Spacer',
5355
+ group: 'presentation',
5356
+ create: (options = {}) => ({
5357
+ height: 60,
5358
+ ...options
5359
+ })
4308
5360
  };
4309
5361
 
4310
5362
  const type$3 = 'taglist';
@@ -4312,6 +5364,7 @@ function Taglist(props) {
4312
5364
  const {
4313
5365
  disabled,
4314
5366
  errors = [],
5367
+ onBlur,
4315
5368
  field,
4316
5369
  readonly,
4317
5370
  value: values = []
@@ -4404,9 +5457,10 @@ function Taglist(props) {
4404
5457
  break;
4405
5458
  }
4406
5459
  };
4407
- const onBlur = () => {
5460
+ const onComponentBlur = () => {
4408
5461
  setIsDropdownExpanded(false);
4409
5462
  setFilter('');
5463
+ onBlur();
4410
5464
  };
4411
5465
  const onTagRemoveClick = (event, value) => {
4412
5466
  const {
@@ -4477,9 +5531,7 @@ function Taglist(props) {
4477
5531
  onKeyDown: onInputKeyDown,
4478
5532
  onMouseDown: () => setIsEscapeClose(false),
4479
5533
  onFocus: () => !readonly && setIsDropdownExpanded(true),
4480
- onBlur: () => {
4481
- !readonly && onBlur();
4482
- },
5534
+ onBlur: () => !readonly && onComponentBlur(),
4483
5535
  "aria-describedby": errorMessageId
4484
5536
  })]
4485
5537
  }), jsxRuntime.jsx("div", {
@@ -4506,21 +5558,7 @@ Taglist.config = {
4506
5558
  group: 'selection',
4507
5559
  emptyValue: [],
4508
5560
  sanitizeValue: sanitizeMultiSelectValue,
4509
- create: (options = {}) => {
4510
- const defaults = {};
4511
-
4512
- // provide default values if valuesKey isn't set
4513
- if (!options.valuesKey) {
4514
- defaults.values = [{
4515
- label: 'Value',
4516
- value: 'value'
4517
- }];
4518
- }
4519
- return {
4520
- ...defaults,
4521
- ...options
4522
- };
4523
- }
5561
+ create: createEmptyOptions
4524
5562
  };
4525
5563
 
4526
5564
  const type$2 = 'text';
@@ -4612,6 +5650,7 @@ function Textfield(props) {
4612
5650
  const {
4613
5651
  disabled,
4614
5652
  errors = [],
5653
+ onBlur,
4615
5654
  field,
4616
5655
  readonly,
4617
5656
  value = ''
@@ -4663,6 +5702,7 @@ function Textfield(props) {
4663
5702
  readOnly: readonly,
4664
5703
  id: prefixId(id, formId),
4665
5704
  onInput: onChange,
5705
+ onBlur: onBlur,
4666
5706
  type: "text",
4667
5707
  value: value,
4668
5708
  "aria-describedby": errorMessageId
@@ -4704,6 +5744,7 @@ function Textarea(props) {
4704
5744
  const {
4705
5745
  disabled,
4706
5746
  errors = [],
5747
+ onBlur,
4707
5748
  field,
4708
5749
  readonly,
4709
5750
  value = ''
@@ -4726,22 +5767,12 @@ function Textarea(props) {
4726
5767
  value: target.value
4727
5768
  });
4728
5769
  };
4729
- const autoSizeTextarea = hooks.useCallback(textarea => {
4730
- // Ensures the textarea shrinks back, and improves resizing behavior consistency
4731
- textarea.style.height = '0px';
4732
- const computed = window.getComputedStyle(textarea);
4733
- 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'));
4734
- const minHeight = 75;
4735
- const maxHeight = 350;
4736
- const displayHeight = Math.max(Math.min(calculatedHeight, maxHeight), minHeight);
4737
- textarea.style.height = `${displayHeight}px`;
4738
-
4739
- // Overflow is hidden by default to hide scrollbar flickering
4740
- textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
4741
- }, []);
5770
+ hooks.useLayoutEffect(() => {
5771
+ autoSizeTextarea(textareaRef.current);
5772
+ }, [value]);
4742
5773
  hooks.useEffect(() => {
4743
5774
  autoSizeTextarea(textareaRef.current);
4744
- }, [autoSizeTextarea, value]);
5775
+ }, []);
4745
5776
  const {
4746
5777
  formId
4747
5778
  } = hooks.useContext(FormContext$1);
@@ -4762,6 +5793,7 @@ function Textarea(props) {
4762
5793
  readonly: readonly,
4763
5794
  id: prefixId(id, formId),
4764
5795
  onInput: onInput,
5796
+ onBlur: onBlur,
4765
5797
  value: value,
4766
5798
  ref: textareaRef,
4767
5799
  "aria-describedby": errorMessageId
@@ -4786,40 +5818,58 @@ Textarea.config = {
4786
5818
  ...options
4787
5819
  })
4788
5820
  };
5821
+ const autoSizeTextarea = textarea => {
5822
+ // Ensures the textarea shrinks back, and improves resizing behavior consistency
5823
+ textarea.style.height = '0px';
5824
+ const computed = window.getComputedStyle(textarea);
5825
+ const heightFromLines = () => {
5826
+ const lineHeight = parseInt(computed.getPropertyValue('line-height').replace('px', '')) || 0;
5827
+ const lines = textarea.value ? textarea.value.toString().split('\n').length : 0;
5828
+ return lines * lineHeight;
5829
+ };
5830
+ 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'));
5831
+ const minHeight = 75;
5832
+ const maxHeight = 350;
5833
+ const displayHeight = Math.max(Math.min(calculatedHeight || 0, maxHeight), minHeight);
5834
+ textarea.style.height = `${displayHeight}px`;
5835
+
5836
+ // Overflow is hidden by default to hide scrollbar flickering
5837
+ textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
5838
+ };
4789
5839
 
4790
- var _path$b;
4791
- 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); }
5840
+ var _path$c;
5841
+ 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); }
4792
5842
  var SvgButton = function SvgButton(props) {
4793
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$d({
5843
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$e({
4794
5844
  xmlns: "http://www.w3.org/2000/svg",
4795
5845
  width: 54,
4796
5846
  height: 54,
4797
5847
  fill: "currentcolor"
4798
- }, props), _path$b || (_path$b = /*#__PURE__*/React__namespace.createElement("path", {
5848
+ }, props), _path$c || (_path$c = /*#__PURE__*/React__namespace.createElement("path", {
4799
5849
  fillRule: "evenodd",
4800
5850
  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"
4801
5851
  })));
4802
5852
  };
4803
5853
  var ButtonIcon = SvgButton;
4804
5854
 
4805
- var _path$a;
4806
- 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); }
5855
+ var _path$b;
5856
+ 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); }
4807
5857
  var SvgCheckbox = function SvgCheckbox(props) {
4808
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$c({
5858
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$d({
4809
5859
  xmlns: "http://www.w3.org/2000/svg",
4810
5860
  width: 54,
4811
5861
  height: 54,
4812
5862
  fill: "currentcolor"
4813
- }, props), _path$a || (_path$a = /*#__PURE__*/React__namespace.createElement("path", {
5863
+ }, props), _path$b || (_path$b = /*#__PURE__*/React__namespace.createElement("path", {
4814
5864
  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"
4815
5865
  })));
4816
5866
  };
4817
5867
  var CheckboxIcon = SvgCheckbox;
4818
5868
 
4819
5869
  var _g, _use, _use2, _use3, _defs;
4820
- 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); }
5870
+ 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); }
4821
5871
  var SvgChecklist = function SvgChecklist(props) {
4822
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$b({
5872
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$c({
4823
5873
  xmlns: "http://www.w3.org/2000/svg",
4824
5874
  xmlnsXlink: "http://www.w3.org/1999/xlink",
4825
5875
  width: 54,
@@ -4853,18 +5903,18 @@ var SvgChecklist = function SvgChecklist(props) {
4853
5903
  };
4854
5904
  var ChecklistIcon = SvgChecklist;
4855
5905
 
4856
- var _path$9, _path2$2, _path3;
4857
- 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); }
5906
+ var _path$a, _path2$3, _path3;
5907
+ 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); }
4858
5908
  var SvgDatetime = function SvgDatetime(props) {
4859
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$a({
5909
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$b({
4860
5910
  xmlns: "http://www.w3.org/2000/svg",
4861
5911
  width: 54,
4862
5912
  height: 54,
4863
5913
  fill: "currentcolor"
4864
- }, props), _path$9 || (_path$9 = /*#__PURE__*/React__namespace.createElement("path", {
5914
+ }, props), _path$a || (_path$a = /*#__PURE__*/React__namespace.createElement("path", {
4865
5915
  fillRule: "evenodd",
4866
5916
  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"
4867
- })), _path2$2 || (_path2$2 = /*#__PURE__*/React__namespace.createElement("path", {
5917
+ })), _path2$3 || (_path2$3 = /*#__PURE__*/React__namespace.createElement("path", {
4868
5918
  d: "m35.13 37.603 1.237-1.237-3.468-3.475v-5.926h-1.754v6.654l3.984 3.984Z"
4869
5919
  })), _path3 || (_path3 = /*#__PURE__*/React__namespace.createElement("path", {
4870
5920
  fillRule: "evenodd",
@@ -4873,27 +5923,27 @@ var SvgDatetime = function SvgDatetime(props) {
4873
5923
  };
4874
5924
  var DatetimeIcon = SvgDatetime;
4875
5925
 
4876
- var _path$8, _path2$1;
4877
- 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); }
5926
+ var _path$9, _path2$2;
5927
+ 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); }
4878
5928
  var SvgTaglist = function SvgTaglist(props) {
4879
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$9({
5929
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$a({
4880
5930
  xmlns: "http://www.w3.org/2000/svg",
4881
5931
  width: 54,
4882
5932
  height: 54,
4883
5933
  fill: "currentcolor"
4884
- }, props), _path$8 || (_path$8 = /*#__PURE__*/React__namespace.createElement("path", {
5934
+ }, props), _path$9 || (_path$9 = /*#__PURE__*/React__namespace.createElement("path", {
4885
5935
  fillRule: "evenodd",
4886
5936
  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"
4887
- })), _path2$1 || (_path2$1 = /*#__PURE__*/React__namespace.createElement("path", {
5937
+ })), _path2$2 || (_path2$2 = /*#__PURE__*/React__namespace.createElement("path", {
4888
5938
  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"
4889
5939
  })));
4890
5940
  };
4891
5941
  var TaglistIcon = SvgTaglist;
4892
5942
 
4893
5943
  var _rect, _rect2, _rect3;
4894
- 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); }
5944
+ 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); }
4895
5945
  var SvgForm = function SvgForm(props) {
4896
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$8({
5946
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$9({
4897
5947
  xmlns: "http://www.w3.org/2000/svg",
4898
5948
  width: 54,
4899
5949
  height: 54
@@ -4919,63 +5969,87 @@ var SvgForm = function SvgForm(props) {
4919
5969
  };
4920
5970
  var FormIcon = SvgForm;
4921
5971
 
5972
+ var _path$8;
5973
+ 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); }
5974
+ var SvgGroup = function SvgGroup(props) {
5975
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$8({
5976
+ xmlns: "http://www.w3.org/2000/svg",
5977
+ width: 54,
5978
+ height: 54,
5979
+ fill: "currentcolor"
5980
+ }, props), _path$8 || (_path$8 = /*#__PURE__*/React__namespace.createElement("path", {
5981
+ fillRule: "evenodd",
5982
+ 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"
5983
+ })));
5984
+ };
5985
+ var GroupIcon = SvgGroup;
5986
+
4922
5987
  var _path$7;
4923
5988
  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); }
4924
- var SvgGroup = function SvgGroup(props) {
5989
+ var SvgNumber = function SvgNumber(props) {
4925
5990
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$7({
4926
5991
  xmlns: "http://www.w3.org/2000/svg",
4927
5992
  width: 54,
4928
- height: 54
5993
+ height: 54,
5994
+ fill: "currentcolor"
4929
5995
  }, props), _path$7 || (_path$7 = /*#__PURE__*/React__namespace.createElement("path", {
4930
5996
  fillRule: "evenodd",
4931
- 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"
5997
+ 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"
4932
5998
  })));
4933
5999
  };
4934
- var ColumnsIcon = SvgGroup;
6000
+ var NumberIcon = SvgNumber;
4935
6001
 
4936
6002
  var _path$6;
4937
6003
  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); }
4938
- var SvgNumber = function SvgNumber(props) {
6004
+ var SvgRadio = function SvgRadio(props) {
4939
6005
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$6({
4940
6006
  xmlns: "http://www.w3.org/2000/svg",
4941
6007
  width: 54,
4942
6008
  height: 54,
4943
6009
  fill: "currentcolor"
4944
6010
  }, props), _path$6 || (_path$6 = /*#__PURE__*/React__namespace.createElement("path", {
4945
- fillRule: "evenodd",
4946
- 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"
6011
+ 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"
4947
6012
  })));
4948
6013
  };
4949
- var NumberIcon = SvgNumber;
6014
+ var RadioIcon = SvgRadio;
4950
6015
 
4951
6016
  var _path$5;
4952
6017
  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); }
4953
- var SvgRadio = function SvgRadio(props) {
6018
+ var SvgSelect = function SvgSelect(props) {
4954
6019
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$5({
4955
6020
  xmlns: "http://www.w3.org/2000/svg",
4956
6021
  width: 54,
4957
6022
  height: 54,
4958
6023
  fill: "currentcolor"
4959
6024
  }, props), _path$5 || (_path$5 = /*#__PURE__*/React__namespace.createElement("path", {
4960
- 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"
6025
+ fillRule: "evenodd",
6026
+ 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"
4961
6027
  })));
4962
6028
  };
4963
- var RadioIcon = SvgRadio;
6029
+ var SelectIcon = SvgSelect;
4964
6030
 
4965
- var _path$4;
6031
+ var _path$4, _path2$1;
4966
6032
  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); }
4967
- var SvgSelect = function SvgSelect(props) {
6033
+ var SvgSpacer = function SvgSpacer(props) {
4968
6034
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$4({
4969
6035
  xmlns: "http://www.w3.org/2000/svg",
4970
6036
  width: 54,
4971
6037
  height: 54,
4972
6038
  fill: "currentcolor"
4973
6039
  }, props), _path$4 || (_path$4 = /*#__PURE__*/React__namespace.createElement("path", {
4974
- fillRule: "evenodd",
4975
- 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"
6040
+ stroke: "currentcolor",
6041
+ strokeLinecap: "square",
6042
+ strokeWidth: 2,
6043
+ d: "M9 23h36M9 31h36"
6044
+ })), _path2$1 || (_path2$1 = /*#__PURE__*/React__namespace.createElement("path", {
6045
+ stroke: "currentcolor",
6046
+ strokeLinecap: "round",
6047
+ strokeLinejoin: "round",
6048
+ strokeWidth: 2,
6049
+ d: "m23 17 4-4 4 4M31 37l-4 4-4-4"
4976
6050
  })));
4977
6051
  };
4978
- var SelectIcon = SvgSelect;
6052
+ var SpacerIcon = SvgSpacer;
4979
6053
 
4980
6054
  var _path$3;
4981
6055
  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); }
@@ -5046,12 +6120,14 @@ const iconsByType = type => {
5046
6120
  button: ButtonIcon,
5047
6121
  checkbox: CheckboxIcon,
5048
6122
  checklist: ChecklistIcon,
5049
- columns: ColumnsIcon,
6123
+ columns: GroupIcon,
5050
6124
  datetime: DatetimeIcon,
6125
+ group: GroupIcon,
5051
6126
  image: ImageIcon,
5052
6127
  number: NumberIcon,
5053
6128
  radio: RadioIcon,
5054
6129
  select: SelectIcon,
6130
+ spacer: SpacerIcon,
5055
6131
  taglist: TaglistIcon,
5056
6132
  text: TextIcon,
5057
6133
  textfield: TextfieldIcon,
@@ -5060,7 +6136,7 @@ const iconsByType = type => {
5060
6136
  }[type];
5061
6137
  };
5062
6138
 
5063
- const formFields = [Button, Checkbox, Checklist, Default, Image, Numberfield, Datetime, Radio, Select, Taglist, Text, Textfield, Textarea];
6139
+ const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, Image, Numberfield, Datetime, Radio, Select, Spacer, Taglist, Text, Textfield, Textarea];
5064
6140
 
5065
6141
  class FormFields {
5066
6142
  constructor() {
@@ -5136,9 +6212,12 @@ var renderModule = {
5136
6212
  };
5137
6213
 
5138
6214
  var core = {
5139
- __depends__: [importModule, renderModule],
6215
+ __depends__: [renderModule],
5140
6216
  eventBus: ['type', EventBus],
6217
+ importer: ['type', Importer],
6218
+ fieldFactory: ['type', FieldFactory],
5141
6219
  formFieldRegistry: ['type', FormFieldRegistry],
6220
+ pathRegistry: ['type', PathRegistry],
5142
6221
  formLayouter: ['type', FormLayouter],
5143
6222
  validator: ['type', Validator]
5144
6223
  };
@@ -5253,9 +6332,9 @@ class Form {
5253
6332
  this.clear();
5254
6333
  const {
5255
6334
  schema: importedSchema,
5256
- data: initializedData,
5257
6335
  warnings
5258
- } = this.get('importer').importSchema(schema, data);
6336
+ } = this.get('importer').importSchema(schema);
6337
+ const initializedData = this._initializeFieldData(clone(data));
5259
6338
  this._setState({
5260
6339
  data: initializedData,
5261
6340
  errors: {},
@@ -5313,21 +6392,21 @@ class Form {
5313
6392
  */
5314
6393
  validate() {
5315
6394
  const formFieldRegistry = this.get('formFieldRegistry'),
6395
+ pathRegistry = this.get('pathRegistry'),
5316
6396
  validator = this.get('validator');
5317
6397
  const {
5318
6398
  data
5319
6399
  } = this._getState();
5320
6400
  const errors = formFieldRegistry.getAll().reduce((errors, field) => {
5321
6401
  const {
5322
- disabled,
5323
- _path
6402
+ disabled
5324
6403
  } = field;
5325
6404
  if (disabled) {
5326
6405
  return errors;
5327
6406
  }
5328
- const value = minDash.get(data, _path);
6407
+ const value = minDash.get(data, pathRegistry.getValuePath(field));
5329
6408
  const fieldErrors = validator.validateField(field, value);
5330
- return minDash.set(errors, [pathStringify(_path)], fieldErrors.length ? fieldErrors : undefined);
6409
+ return minDash.set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
5331
6410
  }, /** @type {Errors} */{});
5332
6411
  this._setState({
5333
6412
  errors
@@ -5433,16 +6512,14 @@ class Form {
5433
6512
  value
5434
6513
  } = update;
5435
6514
  const {
5436
- _path
5437
- } = field;
5438
- let {
5439
6515
  data,
5440
6516
  errors
5441
6517
  } = this._getState();
5442
- const validator = this.get('validator');
6518
+ const validator = this.get('validator'),
6519
+ pathRegistry = this.get('pathRegistry');
5443
6520
  const fieldErrors = validator.validateField(field, value);
5444
- minDash.set(data, _path, value);
5445
- minDash.set(errors, [pathStringify(_path)], fieldErrors.length ? fieldErrors : undefined);
6521
+ minDash.set(data, pathRegistry.getValuePath(field), value);
6522
+ minDash.set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
5446
6523
  this._setState({
5447
6524
  data: clone(data),
5448
6525
  errors: clone(errors)
@@ -5471,7 +6548,7 @@ class Form {
5471
6548
  * @internal
5472
6549
  */
5473
6550
  _getModules() {
5474
- return [ExpressionLanguageModule, MarkdownModule];
6551
+ return [ExpressionLanguageModule, MarkdownModule, ViewerCommandsModule];
5475
6552
  }
5476
6553
 
5477
6554
  /**
@@ -5485,23 +6562,26 @@ class Form {
5485
6562
  * @internal
5486
6563
  */
5487
6564
  _getSubmitData() {
5488
- const formFieldRegistry = this.get('formFieldRegistry');
6565
+ const formFieldRegistry = this.get('formFieldRegistry'),
6566
+ pathRegistry = this.get('pathRegistry'),
6567
+ formFields = this.get('formFields');
5489
6568
  const formData = this._getState().data;
5490
6569
  const submitData = formFieldRegistry.getAll().reduce((previous, field) => {
5491
6570
  const {
5492
6571
  disabled,
5493
- _path
6572
+ type
5494
6573
  } = field;
6574
+ const {
6575
+ config: fieldConfig
6576
+ } = formFields.get(type);
5495
6577
 
5496
- // do not submit disabled form fields
5497
- if (disabled || !_path) {
6578
+ // do not submit disabled form fields or routing fields
6579
+ if (disabled || !fieldConfig.keyed) {
5498
6580
  return previous;
5499
6581
  }
5500
- const value = minDash.get(formData, _path);
5501
- return {
5502
- ...previous,
5503
- [_path[0]]: value
5504
- };
6582
+ const valuePath = pathRegistry.getValuePath(field);
6583
+ const value = minDash.get(formData, valuePath);
6584
+ return minDash.set(previous, valuePath, value);
5505
6585
  }, {});
5506
6586
  const filteredSubmitData = this._applyConditions(submitData, formData);
5507
6587
  return filteredSubmitData;
@@ -5514,9 +6594,46 @@ class Form {
5514
6594
  const conditionChecker = this.get('conditionChecker');
5515
6595
  return conditionChecker.applyConditions(toFilter, data);
5516
6596
  }
6597
+
6598
+ /**
6599
+ * @internal
6600
+ */
6601
+ _initializeFieldData(data) {
6602
+ const formFieldRegistry = this.get('formFieldRegistry'),
6603
+ formFields = this.get('formFields'),
6604
+ pathRegistry = this.get('pathRegistry');
6605
+ return formFieldRegistry.getAll().reduce((initializedData, formField) => {
6606
+ const {
6607
+ defaultValue,
6608
+ type
6609
+ } = formField;
6610
+
6611
+ // try to get value from data
6612
+ // if unavailable - try to get default value from form field
6613
+ // if unavailable - get empty value from form field
6614
+
6615
+ const valuePath = pathRegistry.getValuePath(formField);
6616
+ if (valuePath) {
6617
+ const {
6618
+ config: fieldConfig
6619
+ } = formFields.get(type);
6620
+ let valueData = minDash.get(data, valuePath);
6621
+ if (!minDash.isUndefined(valueData) && fieldConfig.sanitizeValue) {
6622
+ valueData = fieldConfig.sanitizeValue({
6623
+ formField,
6624
+ data,
6625
+ value: valueData
6626
+ });
6627
+ }
6628
+ const initializedFieldValue = !minDash.isUndefined(valueData) ? valueData : !minDash.isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
6629
+ return minDash.set(initializedData, valuePath, initializedFieldValue);
6630
+ }
6631
+ return initializedData;
6632
+ }, data);
6633
+ }
5517
6634
  }
5518
6635
 
5519
- const schemaVersion = 9;
6636
+ const schemaVersion = 11;
5520
6637
 
5521
6638
  /**
5522
6639
  * @typedef { import('./types').CreateFormOptions } CreateFormOptions
@@ -5551,10 +6668,11 @@ exports.DATETIME_SUBTYPE_PATH = DATETIME_SUBTYPE_PATH;
5551
6668
  exports.DATE_DISALLOW_PAST_PATH = DATE_DISALLOW_PAST_PATH;
5552
6669
  exports.DATE_LABEL_PATH = DATE_LABEL_PATH;
5553
6670
  exports.Datetime = Datetime;
5554
- exports.Default = Default;
6671
+ exports.Default = FormComponent$1;
5555
6672
  exports.ExpressionLanguageModule = ExpressionLanguageModule;
5556
6673
  exports.FeelExpressionLanguage = FeelExpressionLanguage;
5557
6674
  exports.FeelersTemplating = FeelersTemplating;
6675
+ exports.FieldFactory = FieldFactory;
5558
6676
  exports.Form = Form;
5559
6677
  exports.FormComponent = FormComponent;
5560
6678
  exports.FormContext = FormContext$1;
@@ -5562,13 +6680,17 @@ exports.FormFieldRegistry = FormFieldRegistry;
5562
6680
  exports.FormFields = FormFields;
5563
6681
  exports.FormLayouter = FormLayouter;
5564
6682
  exports.FormRenderContext = FormRenderContext$1;
6683
+ exports.Group = Group;
5565
6684
  exports.Image = Image;
6685
+ exports.Importer = Importer;
5566
6686
  exports.MINUTES_IN_DAY = MINUTES_IN_DAY;
5567
6687
  exports.MarkdownModule = MarkdownModule;
5568
6688
  exports.MarkdownRenderer = MarkdownRenderer;
5569
6689
  exports.Numberfield = Numberfield;
6690
+ exports.PathRegistry = PathRegistry;
5570
6691
  exports.Radio = Radio;
5571
6692
  exports.Select = Select;
6693
+ exports.Spacer = Spacer;
5572
6694
  exports.TIME_INTERVAL_PATH = TIME_INTERVAL_PATH;
5573
6695
  exports.TIME_LABEL_PATH = TIME_LABEL_PATH;
5574
6696
  exports.TIME_SERIALISINGFORMAT_LABELS = TIME_SERIALISINGFORMAT_LABELS;
@@ -5584,11 +6706,12 @@ exports.VALUES_SOURCES_DEFAULTS = VALUES_SOURCES_DEFAULTS;
5584
6706
  exports.VALUES_SOURCES_LABELS = VALUES_SOURCES_LABELS;
5585
6707
  exports.VALUES_SOURCES_PATHS = VALUES_SOURCES_PATHS;
5586
6708
  exports.VALUES_SOURCE_DEFAULT = VALUES_SOURCE_DEFAULT;
6709
+ exports.ViewerCommands = ViewerCommands;
6710
+ exports.ViewerCommandsModule = ViewerCommandsModule;
5587
6711
  exports.clone = clone;
5588
6712
  exports.createForm = createForm;
5589
6713
  exports.createFormContainer = createFormContainer;
5590
6714
  exports.createInjector = createInjector;
5591
- exports.findErrors = findErrors;
5592
6715
  exports.formFields = formFields;
5593
6716
  exports.generateIdForType = generateIdForType;
5594
6717
  exports.generateIndexForType = generateIndexForType;
@@ -5597,7 +6720,7 @@ exports.getValuesSource = getValuesSource;
5597
6720
  exports.iconsByType = iconsByType;
5598
6721
  exports.isRequired = isRequired;
5599
6722
  exports.pathParse = pathParse;
5600
- exports.pathStringify = pathStringify;
5601
6723
  exports.pathsEqual = pathsEqual;
6724
+ exports.runRecursively = runRecursively;
5602
6725
  exports.schemaVersion = schemaVersion;
5603
6726
  //# sourceMappingURL=index.cjs.map