@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.es.js CHANGED
@@ -1,28 +1,28 @@
1
1
  import Ids from 'ids';
2
- import { isString, isArray, isFunction, isNumber, bind, assign, isNil, get, set, groupBy, flatten, isUndefined, findIndex, isObject } from 'min-dash';
3
- import { parseExpressions, parseUnaryTests, evaluate, unaryTest } from 'feelin';
4
- import { evaluate as evaluate$1, parser, buildSimpleTree } from 'feelers';
5
- import showdown from 'showdown';
2
+ import { isString, get, set, isObject, values, uniqueBy, isArray, isFunction, isNumber, bind, assign, isNil, groupBy, flatten, findIndex, isUndefined } from 'min-dash';
6
3
  import Big from 'big.js';
4
+ import { parseExpression, parseUnaryTests, evaluate, unaryTest } from 'feelin';
5
+ import { evaluate as evaluate$1, parser, buildSimpleTree } from 'feelers';
7
6
  import classNames from 'classnames';
8
7
  import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
9
- import { useContext, useMemo, useEffect, useState, useRef, useCallback } from 'preact/hooks';
8
+ import { useContext, useMemo, useEffect, useState, useRef, useCallback, useLayoutEffect } from 'preact/hooks';
10
9
  import { createContext, createElement, Fragment, render } from 'preact';
11
10
  import * as React from 'preact/compat';
12
11
  import { createPortal } from 'preact/compat';
13
12
  import flatpickr from 'flatpickr';
14
13
  import Markup from 'preact-markup';
15
14
  import { Injector } from 'didi';
15
+ import showdown from 'showdown';
16
16
 
17
- const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) => {
17
+ const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', options = {}) => {
18
18
  const {
19
19
  depth = 0,
20
20
  specialDepthAccessors = {}
21
21
  } = options;
22
22
  if (!['expression', 'unaryTest'].includes(feelFlavour)) return [];
23
- const tree = feelFlavour === 'expression' ? parseExpressions(feelString) : parseUnaryTests(feelString);
23
+ const tree = feelFlavour === 'expression' ? parseExpression(feelString) : parseUnaryTests(feelString);
24
24
  const simpleExpressionTree = _buildSimpleFeelStructureTree(tree, feelString);
25
- return function _unfoldVariables(node) {
25
+ const variables = function _unfoldVariables(node) {
26
26
  if (node.name === 'PathExpression') {
27
27
  if (Object.keys(specialDepthAccessors).length === 0) {
28
28
  return depth === 0 ? [_getVariableNameAtPathIndex(node, 0)] : [];
@@ -35,34 +35,38 @@ const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) =>
35
35
 
36
36
  // for any other kind of node, traverse its children and flatten the result
37
37
  if (node.children) {
38
- return node.children.reduce((acc, child) => {
38
+ const variables = node.children.reduce((acc, child) => {
39
39
  return acc.concat(_unfoldVariables(child));
40
40
  }, []);
41
+
42
+ // if we are within a filter context, we need to remove the item variable as it is used for iteration there
43
+ return node.name === 'FilterContext' ? variables.filter(name => name !== 'item') : variables;
41
44
  }
42
45
  return [];
43
46
  }(simpleExpressionTree);
47
+ return [...new Set(variables)];
44
48
  };
45
49
 
46
- /**
47
- * Get the variable name at the specified index in a given path expression.
48
- *
49
- * @param {Object} root - The root node of the path expression tree.
50
- * @param {number} index - The index of the variable name to retrieve.
51
- * @returns {string|null} The variable name at the specified index or null if index is out of bounds.
50
+ /**
51
+ * Get the variable name at the specified index in a given path expression.
52
+ *
53
+ * @param {Object} root - The root node of the path expression tree.
54
+ * @param {number} index - The index of the variable name to retrieve.
55
+ * @returns {string|null} The variable name at the specified index or null if index is out of bounds.
52
56
  */
53
57
  const _getVariableNameAtPathIndex = (root, index) => {
54
58
  const accessors = _deconstructPathExpression(root);
55
59
  return accessors[index] || null;
56
60
  };
57
61
 
58
- /**
59
- * Extracts the variables which are required of the external context for a given path expression.
60
- * This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
61
- *
62
- * @param {Object} node - The root node of the path expression tree.
63
- * @param {number} initialDepth - The depth at which the root node is located in the outer context.
64
- * @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
65
- * @returns {Set} - A set containing the extracted variable names.
62
+ /**
63
+ * Extracts the variables which are required of the external context for a given path expression.
64
+ * This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
65
+ *
66
+ * @param {Object} node - The root node of the path expression tree.
67
+ * @param {number} initialDepth - The depth at which the root node is located in the outer context.
68
+ * @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
69
+ * @returns {Set} - A set containing the extracted variable names.
66
70
  */
67
71
  const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
68
72
  // depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
@@ -108,11 +112,11 @@ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) =
108
112
  return new Set(extractedVariables);
109
113
  };
110
114
 
111
- /**
112
- * Deconstructs a path expression tree into an array of components.
113
- *
114
- * @param {Object} root - The root node of the path expression tree.
115
- * @returns {Array<string>} An array of components in the path expression, in the correct order.
115
+ /**
116
+ * Deconstructs a path expression tree into an array of components.
117
+ *
118
+ * @param {Object} root - The root node of the path expression tree.
119
+ * @returns {Array<string>} An array of components in the path expression, in the correct order.
116
120
  */
117
121
  const _deconstructPathExpression = root => {
118
122
  let node = root;
@@ -131,13 +135,13 @@ const _deconstructPathExpression = root => {
131
135
  return parts.reverse();
132
136
  };
133
137
 
134
- /**
135
- * Builds a simplified feel structure tree from the given parse tree and feel string.
136
- * The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
137
- *
138
- * @param {Object} parseTree - The parse tree generated by a parser.
139
- * @param {string} feelString - The feel string used for parsing.
140
- * @returns {Object} The simplified feel structure tree.
138
+ /**
139
+ * Builds a simplified feel structure tree from the given parse tree and feel string.
140
+ * The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
141
+ *
142
+ * @param {Object} parseTree - The parse tree generated by a parser.
143
+ * @param {string} feelString - The feel string used for parsing.
144
+ * @returns {Object} The simplified feel structure tree.
141
145
  */
142
146
  const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
143
147
  const stack = [{
@@ -160,7 +164,45 @@ const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
160
164
  parent.children.push(result);
161
165
  }
162
166
  });
163
- return stack[0].children[0];
167
+ return _extractFilterExpressions(stack[0].children[0]);
168
+ };
169
+
170
+ /**
171
+ * Restructure the tree in such a way to bring filters (which create new contexts) to the root of the tree.
172
+ * This is done to simplify the extraction of variables and match the context hierarchy.
173
+ */
174
+ const _extractFilterExpressions = tree => {
175
+ const flattenedExpressionTree = {
176
+ name: 'Root',
177
+ children: [tree]
178
+ };
179
+ const iterate = node => {
180
+ if (node.children) {
181
+ for (let x = 0; x < node.children.length; x++) {
182
+ if (node.children[x].name === 'FilterExpression') {
183
+ const filterTarget = node.children[x].children[0];
184
+ const filterExpression = node.children[x].children[2];
185
+
186
+ // bypass the filter expression
187
+ node.children[x] = filterTarget;
188
+ const taggedFilterExpression = {
189
+ name: 'FilterContext',
190
+ children: [filterExpression]
191
+ };
192
+
193
+ // append the filter expression to the root
194
+ flattenedExpressionTree.children.push(taggedFilterExpression);
195
+
196
+ // recursively iterate the expression
197
+ iterate(filterExpression);
198
+ } else {
199
+ iterate(node.children[x]);
200
+ }
201
+ }
202
+ }
203
+ };
204
+ iterate(tree);
205
+ return flattenedExpressionTree;
164
206
  };
165
207
 
166
208
  class FeelExpressionLanguage {
@@ -168,25 +210,25 @@ class FeelExpressionLanguage {
168
210
  this._eventBus = eventBus;
169
211
  }
170
212
 
171
- /**
172
- * Determines if the given value is a FEEL expression.
173
- *
174
- * @param {any} value
175
- * @returns {boolean}
176
- *
213
+ /**
214
+ * Determines if the given value is a FEEL expression.
215
+ *
216
+ * @param {any} value
217
+ * @returns {boolean}
218
+ *
177
219
  */
178
220
  isExpression(value) {
179
221
  return isString(value) && value.startsWith('=');
180
222
  }
181
223
 
182
- /**
183
- * Retrieve variable names from a given FEEL expression.
184
- *
185
- * @param {string} expression
186
- * @param {object} [options]
187
- * @param {string} [options.type]
188
- *
189
- * @returns {string[]}
224
+ /**
225
+ * Retrieve variable names from a given FEEL expression.
226
+ *
227
+ * @param {string} expression
228
+ * @param {object} [options]
229
+ * @param {string} [options.type]
230
+ *
231
+ * @returns {string[]}
190
232
  */
191
233
  getVariableNames(expression, options = {}) {
192
234
  const {
@@ -201,13 +243,13 @@ class FeelExpressionLanguage {
201
243
  return getFlavouredFeelVariableNames(expression, type);
202
244
  }
203
245
 
204
- /**
205
- * Evaluate an expression.
206
- *
207
- * @param {string} expression
208
- * @param {import('../../types').Data} [data]
209
- *
210
- * @returns {any}
246
+ /**
247
+ * Evaluate an expression.
248
+ *
249
+ * @param {string} expression
250
+ * @param {import('../../types').Data} [data]
251
+ *
252
+ * @returns {any}
211
253
  */
212
254
  evaluate(expression, data = {}) {
213
255
  if (!expression) {
@@ -232,23 +274,23 @@ FeelExpressionLanguage.$inject = ['eventBus'];
232
274
  class FeelersTemplating {
233
275
  constructor() {}
234
276
 
235
- /**
236
- * Determines if the given value is a feelers template.
237
- *
238
- * @param {any} value
239
- * @returns {boolean}
240
- *
277
+ /**
278
+ * Determines if the given value is a feelers template.
279
+ *
280
+ * @param {any} value
281
+ * @returns {boolean}
282
+ *
241
283
  */
242
284
  isTemplate(value) {
243
285
  return isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
244
286
  }
245
287
 
246
- /**
247
- * Retrieve variable names from a given feelers template.
248
- *
249
- * @param {string} template
250
- *
251
- * @returns {string[]}
288
+ /**
289
+ * Retrieve variable names from a given feelers template.
290
+ *
291
+ * @param {string} template
292
+ *
293
+ * @returns {string[]}
252
294
  */
253
295
  getVariableNames(template) {
254
296
  if (!this.isTemplate(template)) {
@@ -274,17 +316,17 @@ class FeelersTemplating {
274
316
  }, []);
275
317
  }
276
318
 
277
- /**
278
- * Evaluate a template.
279
- *
280
- * @param {string} template
281
- * @param {Object<string, any>} context
282
- * @param {Object} options
283
- * @param {boolean} [options.debug = false]
284
- * @param {boolean} [options.strict = false]
285
- * @param {Function} [options.buildDebugString]
286
- *
287
- * @returns
319
+ /**
320
+ * Evaluate a template.
321
+ *
322
+ * @param {string} template
323
+ * @param {Object<string, any>} context
324
+ * @param {Object} options
325
+ * @param {boolean} [options.debug = false]
326
+ * @param {boolean} [options.strict = false]
327
+ * @param {Function} [options.buildDebugString]
328
+ *
329
+ * @returns
288
330
  */
289
331
  evaluate(template, context = {}, options = {}) {
290
332
  const {
@@ -299,22 +341,22 @@ class FeelersTemplating {
299
341
  });
300
342
  }
301
343
 
302
- /**
303
- * @typedef {Object} ExpressionWithDepth
304
- * @property {number} depth - The depth of the expression in the syntax tree.
305
- * @property {string} expression - The extracted expression
344
+ /**
345
+ * @typedef {Object} ExpressionWithDepth
346
+ * @property {number} depth - The depth of the expression in the syntax tree.
347
+ * @property {string} expression - The extracted expression
306
348
  */
307
349
 
308
- /**
309
- * Extracts all feel expressions in the template along with their depth in the syntax tree.
310
- * The depth is incremented for child expressions of loops to account for context drilling.
311
- * @name extractExpressionsWithDepth
312
- * @param {string} template - A feelers template string.
313
- * @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
314
- *
315
- * @example
316
- * const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
317
- * const extractedExpressions = _extractExpressionsWithDepth(template);
350
+ /**
351
+ * Extracts all feel expressions in the template along with their depth in the syntax tree.
352
+ * The depth is incremented for child expressions of loops to account for context drilling.
353
+ * @name extractExpressionsWithDepth
354
+ * @param {string} template - A feelers template string.
355
+ * @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
356
+ *
357
+ * @example
358
+ * const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
359
+ * const extractedExpressions = _extractExpressionsWithDepth(template);
318
360
  */
319
361
  _extractExpressionsWithDepth(template) {
320
362
  // build simplified feelers syntax tree
@@ -345,47 +387,271 @@ class FeelersTemplating {
345
387
  }
346
388
  FeelersTemplating.$inject = [];
347
389
 
348
- /**
349
- * @typedef {object} Condition
350
- * @property {string} [hide]
390
+ // config ///////////////////
391
+
392
+ const MINUTES_IN_DAY = 60 * 24;
393
+ const DATETIME_SUBTYPES = {
394
+ DATE: 'date',
395
+ TIME: 'time',
396
+ DATETIME: 'datetime'
397
+ };
398
+ const TIME_SERIALISING_FORMATS = {
399
+ UTC_OFFSET: 'utc_offset',
400
+ UTC_NORMALIZED: 'utc_normalized',
401
+ NO_TIMEZONE: 'no_timezone'
402
+ };
403
+ const DATETIME_SUBTYPES_LABELS = {
404
+ [DATETIME_SUBTYPES.DATE]: 'Date',
405
+ [DATETIME_SUBTYPES.TIME]: 'Time',
406
+ [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
407
+ };
408
+ const TIME_SERIALISINGFORMAT_LABELS = {
409
+ [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
410
+ [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
411
+ [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
412
+ };
413
+ const DATETIME_SUBTYPE_PATH = ['subtype'];
414
+ const DATE_LABEL_PATH = ['dateLabel'];
415
+ const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
416
+ const TIME_LABEL_PATH = ['timeLabel'];
417
+ const TIME_USE24H_PATH = ['use24h'];
418
+ const TIME_INTERVAL_PATH = ['timeInterval'];
419
+ const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
420
+
421
+ // config ///////////////////
422
+
423
+ const VALUES_SOURCES = {
424
+ STATIC: 'static',
425
+ INPUT: 'input',
426
+ EXPRESSION: 'expression'
427
+ };
428
+ const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
429
+ const VALUES_SOURCES_LABELS = {
430
+ [VALUES_SOURCES.STATIC]: 'Static',
431
+ [VALUES_SOURCES.INPUT]: 'Input data',
432
+ [VALUES_SOURCES.EXPRESSION]: 'Expression'
433
+ };
434
+ const VALUES_SOURCES_PATHS = {
435
+ [VALUES_SOURCES.STATIC]: ['values'],
436
+ [VALUES_SOURCES.INPUT]: ['valuesKey'],
437
+ [VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
438
+ };
439
+ const VALUES_SOURCES_DEFAULTS = {
440
+ [VALUES_SOURCES.STATIC]: [{
441
+ label: 'Value',
442
+ value: 'value'
443
+ }],
444
+ [VALUES_SOURCES.INPUT]: '',
445
+ [VALUES_SOURCES.EXPRESSION]: '='
446
+ };
447
+
448
+ // helpers ///////////////////
449
+
450
+ function getValuesSource(field) {
451
+ for (const source of Object.values(VALUES_SOURCES)) {
452
+ if (get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
453
+ return source;
454
+ }
455
+ }
456
+ return VALUES_SOURCE_DEFAULT;
457
+ }
458
+
459
+ function createInjector(bootstrapModules) {
460
+ const injector = new Injector(bootstrapModules);
461
+ injector.init();
462
+ return injector;
463
+ }
464
+
465
+ /**
466
+ * @param {string?} prefix
467
+ *
468
+ * @returns Element
469
+ */
470
+ function createFormContainer(prefix = 'fjs') {
471
+ const container = document.createElement('div');
472
+ container.classList.add(`${prefix}-container`);
473
+ return container;
474
+ }
475
+
476
+ const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
477
+ const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
478
+ function isRequired(field) {
479
+ return field.required;
480
+ }
481
+ function pathParse(path) {
482
+ if (!path) {
483
+ return [];
484
+ }
485
+ return path.split('.').map(key => {
486
+ return isNaN(parseInt(key)) ? key : parseInt(key);
487
+ });
488
+ }
489
+ function pathsEqual(a, b) {
490
+ return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
491
+ }
492
+ const indices = {};
493
+ function generateIndexForType(type) {
494
+ if (type in indices) {
495
+ indices[type]++;
496
+ } else {
497
+ indices[type] = 1;
498
+ }
499
+ return indices[type];
500
+ }
501
+ function generateIdForType(type) {
502
+ return `${type}${generateIndexForType(type)}`;
503
+ }
504
+
505
+ /**
506
+ * @template T
507
+ * @param {T} data
508
+ * @param {(this: any, key: string, value: any) => any} [replacer]
509
+ * @return {T}
510
+ */
511
+ function clone(data, replacer) {
512
+ return JSON.parse(JSON.stringify(data, replacer));
513
+ }
514
+
515
+ /**
516
+ * Parse the schema for input variables a form might make use of
517
+ *
518
+ * @param {any} schema
519
+ *
520
+ * @return {string[]}
521
+ */
522
+ function getSchemaVariables(schema, options = {}) {
523
+ const {
524
+ expressionLanguage = new FeelExpressionLanguage(null),
525
+ templating = new FeelersTemplating(),
526
+ inputs = true,
527
+ outputs = true
528
+ } = options;
529
+ if (!schema.components) {
530
+ return [];
531
+ }
532
+ const getAllComponents = node => {
533
+ const components = [];
534
+ if (node.components) {
535
+ node.components.forEach(component => {
536
+ components.push(component);
537
+ components.push(...getAllComponents(component));
538
+ });
539
+ }
540
+ return components;
541
+ };
542
+ const variables = getAllComponents(schema).reduce((variables, component) => {
543
+ const {
544
+ valuesKey
545
+ } = component;
546
+
547
+ // collect input-only variables
548
+ if (inputs) {
549
+ if (valuesKey) {
550
+ variables = [...variables, valuesKey];
551
+ }
552
+ EXPRESSION_PROPERTIES.forEach(prop => {
553
+ const property = get(component, prop.split('.'));
554
+ if (property && expressionLanguage.isExpression(property)) {
555
+ const expressionVariables = expressionLanguage.getVariableNames(property, {
556
+ type: 'expression'
557
+ });
558
+ variables = [...variables, ...expressionVariables];
559
+ }
560
+ });
561
+ TEMPLATE_PROPERTIES.forEach(prop => {
562
+ const property = get(component, prop.split('.'));
563
+ if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
564
+ const templateVariables = templating.getVariableNames(property);
565
+ variables = [...variables, ...templateVariables];
566
+ }
567
+ });
568
+ }
569
+ return variables.filter(variable => variable !== undefined || variable !== null);
570
+ }, []);
571
+ const getBindingVariables = node => {
572
+ const bindingVariable = [];
573
+
574
+ // c.f. https://github.com/bpmn-io/form-js/issues/778 @Skaiir to remove?
575
+ if (node.type === 'button') {
576
+ return [];
577
+ } else if (node.key) {
578
+ return [node.key.split('.')[0]];
579
+ } else if (node.path) {
580
+ return [node.path.split('.')[0]];
581
+ } else if (node.components) {
582
+ node.components.forEach(component => {
583
+ bindingVariable.push(...getBindingVariables(component));
584
+ });
585
+ }
586
+ return bindingVariable;
587
+ };
588
+
589
+ // collect binding variables
590
+ if (inputs || outputs) {
591
+ variables.push(...getBindingVariables(schema));
592
+ }
593
+
594
+ // remove duplicates
595
+ return Array.from(new Set(variables));
596
+ }
597
+ function runRecursively(formField, fn) {
598
+ const components = formField.components || [];
599
+ components.forEach((component, index) => {
600
+ runRecursively(component, fn);
601
+ });
602
+ fn(formField);
603
+ }
604
+
605
+ /**
606
+ * @typedef {object} Condition
607
+ * @property {string} [hide]
351
608
  */
352
609
 
353
610
  class ConditionChecker {
354
- constructor(formFieldRegistry, eventBus) {
611
+ constructor(formFieldRegistry, pathRegistry, eventBus) {
355
612
  this._formFieldRegistry = formFieldRegistry;
613
+ this._pathRegistry = pathRegistry;
356
614
  this._eventBus = eventBus;
357
615
  }
358
616
 
359
- /**
360
- * For given data, remove properties based on condition.
361
- *
362
- * @param {Object<string, any>} properties
363
- * @param {Object<string, any>} data
617
+ /**
618
+ * For given data, remove properties based on condition.
619
+ *
620
+ * @param {Object<string, any>} properties
621
+ * @param {Object<string, any>} data
364
622
  */
365
623
  applyConditions(properties, data = {}) {
366
- const conditions = this._getConditions();
367
- const newProperties = {
368
- ...properties
369
- };
370
- for (const {
371
- key,
372
- condition
373
- } of conditions) {
374
- const shouldRemove = this._checkHideCondition(condition, data);
375
- if (shouldRemove) {
376
- delete newProperties[key];
377
- }
624
+ const newProperties = clone(properties);
625
+ const form = this._formFieldRegistry.getAll().find(field => field.type === 'default');
626
+ if (!form) {
627
+ throw new Error('form field registry has no form');
378
628
  }
629
+ this._pathRegistry.executeRecursivelyOnFields(form, ({
630
+ field,
631
+ isClosed,
632
+ context
633
+ }) => {
634
+ const {
635
+ conditional: condition
636
+ } = field;
637
+ context.isHidden = context.isHidden || condition && this._checkHideCondition(condition, data);
638
+
639
+ // only clear the leaf nodes, as groups may both point to the same path
640
+ if (context.isHidden && isClosed) {
641
+ const valuePath = this._pathRegistry.getValuePath(field);
642
+ this._clearObjectValueRecursively(valuePath, newProperties);
643
+ }
644
+ });
379
645
  return newProperties;
380
646
  }
381
647
 
382
- /**
383
- * Check if given condition is met. Returns null for invalid/missing conditions.
384
- *
385
- * @param {string} condition
386
- * @param {import('../../types').Data} [data]
387
- *
388
- * @returns {boolean|null}
648
+ /**
649
+ * Check if given condition is met. Returns null for invalid/missing conditions.
650
+ *
651
+ * @param {string} condition
652
+ * @param {import('../../types').Data} [data]
653
+ *
654
+ * @returns {boolean|null}
389
655
  */
390
656
  check(condition, data = {}) {
391
657
  if (!condition) {
@@ -406,12 +672,12 @@ class ConditionChecker {
406
672
  }
407
673
  }
408
674
 
409
- /**
410
- * Check if hide condition is met.
411
- *
412
- * @param {Condition} condition
413
- * @param {Object<string, any>} data
414
- * @returns {boolean}
675
+ /**
676
+ * Check if hide condition is met.
677
+ *
678
+ * @param {Condition} condition
679
+ * @param {Object<string, any>} data
680
+ * @returns {boolean}
415
681
  */
416
682
  _checkHideCondition(condition, data) {
417
683
  if (!condition.hide) {
@@ -420,24 +686,18 @@ class ConditionChecker {
420
686
  const result = this.check(condition.hide, data);
421
687
  return result === true;
422
688
  }
423
- _getConditions() {
424
- const formFields = this._formFieldRegistry.getAll();
425
- return formFields.reduce((conditions, formField) => {
426
- const {
427
- key,
428
- conditional: condition
429
- } = formField;
430
- if (key && condition) {
431
- return [...conditions, {
432
- key,
433
- condition
434
- }];
435
- }
436
- return conditions;
437
- }, []);
689
+ _clearObjectValueRecursively(valuePath, obj) {
690
+ const workingValuePath = [...valuePath];
691
+ let recurse = false;
692
+ do {
693
+ set(obj, workingValuePath, undefined);
694
+ workingValuePath.pop();
695
+ const parentObject = get(obj, workingValuePath);
696
+ recurse = isObject(parentObject) && !values(parentObject).length && !!workingValuePath.length;
697
+ } while (recurse);
438
698
  }
439
699
  }
440
- ConditionChecker.$inject = ['formFieldRegistry', 'eventBus'];
700
+ ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'eventBus'];
441
701
 
442
702
  var ExpressionLanguageModule = {
443
703
  __init__: ['expressionLanguage', 'templating', 'conditionChecker'],
@@ -453,12 +713,12 @@ class MarkdownRenderer {
453
713
  this._converter = new showdown.Converter();
454
714
  }
455
715
 
456
- /**
457
- * Render markdown to HTML.
458
- *
459
- * @param {string} markdown - The markdown to render
460
- *
461
- * @returns {string} HTML
716
+ /**
717
+ * Render markdown to HTML.
718
+ *
719
+ * @param {string} markdown - The markdown to render
720
+ *
721
+ * @returns {string} HTML
462
722
  */
463
723
  render(markdown) {
464
724
  return this._converter.makeHtml(markdown);
@@ -471,6 +731,538 @@ var MarkdownModule = {
471
731
  markdownRenderer: ['type', MarkdownRenderer]
472
732
  };
473
733
 
734
+ /**
735
+ * @typedef {import('didi').Injector} Injector
736
+ *
737
+ * @typedef {import('../core/Types').ElementLike} ElementLike
738
+ *
739
+ * @typedef {import('../core/EventBus').default} EventBus
740
+ * @typedef {import('./CommandHandler').default} CommandHandler
741
+ *
742
+ * @typedef { any } CommandContext
743
+ * @typedef { {
744
+ * new (...args: any[]) : CommandHandler
745
+ * } } CommandHandlerConstructor
746
+ * @typedef { {
747
+ * [key: string]: CommandHandler;
748
+ * } } CommandHandlerMap
749
+ * @typedef { {
750
+ * command: string;
751
+ * context: any;
752
+ * id?: any;
753
+ * } } CommandStackAction
754
+ * @typedef { {
755
+ * actions: CommandStackAction[];
756
+ * dirty: ElementLike[];
757
+ * trigger: 'execute' | 'undo' | 'redo' | 'clear' | null;
758
+ * atomic?: boolean;
759
+ * } } CurrentExecution
760
+ */
761
+
762
+ /**
763
+ * A service that offers un- and redoable execution of commands.
764
+ *
765
+ * The command stack is responsible for executing modeling actions
766
+ * in a un- and redoable manner. To do this it delegates the actual
767
+ * command execution to {@link CommandHandler}s.
768
+ *
769
+ * Command handlers provide {@link CommandHandler#execute(ctx)} and
770
+ * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
771
+ * identified by a command context.
772
+ *
773
+ *
774
+ * ## Life-Cycle events
775
+ *
776
+ * In the process the command stack fires a number of life-cycle events
777
+ * that other components to participate in the command execution.
778
+ *
779
+ * * preExecute
780
+ * * preExecuted
781
+ * * execute
782
+ * * executed
783
+ * * postExecute
784
+ * * postExecuted
785
+ * * revert
786
+ * * reverted
787
+ *
788
+ * A special event is used for validating, whether a command can be
789
+ * performed prior to its execution.
790
+ *
791
+ * * canExecute
792
+ *
793
+ * Each of the events is fired as `commandStack.{eventName}` and
794
+ * `commandStack.{commandName}.{eventName}`, respectively. This gives
795
+ * components fine grained control on where to hook into.
796
+ *
797
+ * The event object fired transports `command`, the name of the
798
+ * command and `context`, the command context.
799
+ *
800
+ *
801
+ * ## Creating Command Handlers
802
+ *
803
+ * Command handlers should provide the {@link CommandHandler#execute(ctx)}
804
+ * and {@link CommandHandler#revert(ctx)} methods to implement
805
+ * redoing and undoing of a command.
806
+ *
807
+ * A command handler _must_ ensure undo is performed properly in order
808
+ * not to break the undo chain. It must also return the shapes that
809
+ * got changed during the `execute` and `revert` operations.
810
+ *
811
+ * Command handlers may execute other modeling operations (and thus
812
+ * commands) in their `preExecute(d)` and `postExecute(d)` phases. The command
813
+ * stack will properly group all commands together into a logical unit
814
+ * that may be re- and undone atomically.
815
+ *
816
+ * Command handlers must not execute other commands from within their
817
+ * core implementation (`execute`, `revert`).
818
+ *
819
+ *
820
+ * ## Change Tracking
821
+ *
822
+ * During the execution of the CommandStack it will keep track of all
823
+ * elements that have been touched during the command's execution.
824
+ *
825
+ * At the end of the CommandStack execution it will notify interested
826
+ * components via an 'elements.changed' event with all the dirty
827
+ * elements.
828
+ *
829
+ * The event can be picked up by components that are interested in the fact
830
+ * that elements have been changed. One use case for this is updating
831
+ * their graphical representation after moving / resizing or deletion.
832
+ *
833
+ * @see CommandHandler
834
+ *
835
+ * @param {EventBus} eventBus
836
+ * @param {Injector} injector
837
+ */
838
+ function CommandStack(eventBus, injector) {
839
+ /**
840
+ * A map of all registered command handlers.
841
+ *
842
+ * @type {CommandHandlerMap}
843
+ */
844
+ this._handlerMap = {};
845
+
846
+ /**
847
+ * A stack containing all re/undoable actions on the diagram
848
+ *
849
+ * @type {CommandStackAction[]}
850
+ */
851
+ this._stack = [];
852
+
853
+ /**
854
+ * The current index on the stack
855
+ *
856
+ * @type {number}
857
+ */
858
+ this._stackIdx = -1;
859
+
860
+ /**
861
+ * Current active commandStack execution
862
+ *
863
+ * @type {CurrentExecution}
864
+ */
865
+ this._currentExecution = {
866
+ actions: [],
867
+ dirty: [],
868
+ trigger: null
869
+ };
870
+
871
+ /**
872
+ * @type {Injector}
873
+ */
874
+ this._injector = injector;
875
+
876
+ /**
877
+ * @type EventBus
878
+ */
879
+ this._eventBus = eventBus;
880
+
881
+ /**
882
+ * @type { number }
883
+ */
884
+ this._uid = 1;
885
+ eventBus.on(['diagram.destroy', 'diagram.clear'], function () {
886
+ this.clear(false);
887
+ }, this);
888
+ }
889
+ CommandStack.$inject = ['eventBus', 'injector'];
890
+
891
+ /**
892
+ * Execute a command.
893
+ *
894
+ * @param {string} command The command to execute.
895
+ * @param {CommandContext} context The context with which to execute the command.
896
+ */
897
+ CommandStack.prototype.execute = function (command, context) {
898
+ if (!command) {
899
+ throw new Error('command required');
900
+ }
901
+ this._currentExecution.trigger = 'execute';
902
+ const action = {
903
+ command: command,
904
+ context: context
905
+ };
906
+ this._pushAction(action);
907
+ this._internalExecute(action);
908
+ this._popAction();
909
+ };
910
+
911
+ /**
912
+ * Check whether a command can be executed.
913
+ *
914
+ * Implementors may hook into the mechanism on two ways:
915
+ *
916
+ * * in event listeners:
917
+ *
918
+ * Users may prevent the execution via an event listener.
919
+ * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
920
+ *
921
+ * * in command handlers:
922
+ *
923
+ * If the method {@link CommandHandler#canExecute} is implemented in a handler
924
+ * it will be called to figure out whether the execution is allowed.
925
+ *
926
+ * @param {string} command The command to execute.
927
+ * @param {CommandContext} context The context with which to execute the command.
928
+ *
929
+ * @return {boolean} Whether the command can be executed with the given context.
930
+ */
931
+ CommandStack.prototype.canExecute = function (command, context) {
932
+ const action = {
933
+ command: command,
934
+ context: context
935
+ };
936
+ const handler = this._getHandler(command);
937
+ let result = this._fire(command, 'canExecute', action);
938
+
939
+ // handler#canExecute will only be called if no listener
940
+ // decided on a result already
941
+ if (result === undefined) {
942
+ if (!handler) {
943
+ return false;
944
+ }
945
+ if (handler.canExecute) {
946
+ result = handler.canExecute(context);
947
+ }
948
+ }
949
+ return result;
950
+ };
951
+
952
+ /**
953
+ * Clear the command stack, erasing all undo / redo history.
954
+ *
955
+ * @param {boolean} [emit=true] Whether to fire an event. Defaults to `true`.
956
+ */
957
+ CommandStack.prototype.clear = function (emit) {
958
+ this._stack.length = 0;
959
+ this._stackIdx = -1;
960
+ if (emit !== false) {
961
+ this._fire('changed', {
962
+ trigger: 'clear'
963
+ });
964
+ }
965
+ };
966
+
967
+ /**
968
+ * Undo last command(s)
969
+ */
970
+ CommandStack.prototype.undo = function () {
971
+ let action = this._getUndoAction(),
972
+ next;
973
+ if (action) {
974
+ this._currentExecution.trigger = 'undo';
975
+ this._pushAction(action);
976
+ while (action) {
977
+ this._internalUndo(action);
978
+ next = this._getUndoAction();
979
+ if (!next || next.id !== action.id) {
980
+ break;
981
+ }
982
+ action = next;
983
+ }
984
+ this._popAction();
985
+ }
986
+ };
987
+
988
+ /**
989
+ * Redo last command(s)
990
+ */
991
+ CommandStack.prototype.redo = function () {
992
+ let action = this._getRedoAction(),
993
+ next;
994
+ if (action) {
995
+ this._currentExecution.trigger = 'redo';
996
+ this._pushAction(action);
997
+ while (action) {
998
+ this._internalExecute(action, true);
999
+ next = this._getRedoAction();
1000
+ if (!next || next.id !== action.id) {
1001
+ break;
1002
+ }
1003
+ action = next;
1004
+ }
1005
+ this._popAction();
1006
+ }
1007
+ };
1008
+
1009
+ /**
1010
+ * Register a handler instance with the command stack.
1011
+ *
1012
+ * @param {string} command Command to be executed.
1013
+ * @param {CommandHandler} handler Handler to execute the command.
1014
+ */
1015
+ CommandStack.prototype.register = function (command, handler) {
1016
+ this._setHandler(command, handler);
1017
+ };
1018
+
1019
+ /**
1020
+ * Register a handler type with the command stack by instantiating it and
1021
+ * injecting its dependencies.
1022
+ *
1023
+ * @param {string} command Command to be executed.
1024
+ * @param {CommandHandlerConstructor} handlerCls Constructor to instantiate a {@link CommandHandler}.
1025
+ */
1026
+ CommandStack.prototype.registerHandler = function (command, handlerCls) {
1027
+ if (!command || !handlerCls) {
1028
+ throw new Error('command and handlerCls must be defined');
1029
+ }
1030
+ const handler = this._injector.instantiate(handlerCls);
1031
+ this.register(command, handler);
1032
+ };
1033
+
1034
+ /**
1035
+ * @return {boolean}
1036
+ */
1037
+ CommandStack.prototype.canUndo = function () {
1038
+ return !!this._getUndoAction();
1039
+ };
1040
+
1041
+ /**
1042
+ * @return {boolean}
1043
+ */
1044
+ CommandStack.prototype.canRedo = function () {
1045
+ return !!this._getRedoAction();
1046
+ };
1047
+
1048
+ // stack access //////////////////////
1049
+
1050
+ CommandStack.prototype._getRedoAction = function () {
1051
+ return this._stack[this._stackIdx + 1];
1052
+ };
1053
+ CommandStack.prototype._getUndoAction = function () {
1054
+ return this._stack[this._stackIdx];
1055
+ };
1056
+
1057
+ // internal functionality //////////////////////
1058
+
1059
+ CommandStack.prototype._internalUndo = function (action) {
1060
+ const command = action.command,
1061
+ context = action.context;
1062
+ const handler = this._getHandler(command);
1063
+
1064
+ // guard against illegal nested command stack invocations
1065
+ this._atomicDo(() => {
1066
+ this._fire(command, 'revert', action);
1067
+ if (handler.revert) {
1068
+ this._markDirty(handler.revert(context));
1069
+ }
1070
+ this._revertedAction(action);
1071
+ this._fire(command, 'reverted', action);
1072
+ });
1073
+ };
1074
+ CommandStack.prototype._fire = function (command, qualifier, event) {
1075
+ if (arguments.length < 3) {
1076
+ event = qualifier;
1077
+ qualifier = null;
1078
+ }
1079
+ const names = qualifier ? [command + '.' + qualifier, qualifier] : [command];
1080
+ let result;
1081
+ event = this._eventBus.createEvent(event);
1082
+ for (const name of names) {
1083
+ result = this._eventBus.fire('commandStack.' + name, event);
1084
+ if (event.cancelBubble) {
1085
+ break;
1086
+ }
1087
+ }
1088
+ return result;
1089
+ };
1090
+ CommandStack.prototype._createId = function () {
1091
+ return this._uid++;
1092
+ };
1093
+ CommandStack.prototype._atomicDo = function (fn) {
1094
+ const execution = this._currentExecution;
1095
+ execution.atomic = true;
1096
+ try {
1097
+ fn();
1098
+ } finally {
1099
+ execution.atomic = false;
1100
+ }
1101
+ };
1102
+ CommandStack.prototype._internalExecute = function (action, redo) {
1103
+ const command = action.command,
1104
+ context = action.context;
1105
+ const handler = this._getHandler(command);
1106
+ if (!handler) {
1107
+ throw new Error('no command handler registered for <' + command + '>');
1108
+ }
1109
+ this._pushAction(action);
1110
+ if (!redo) {
1111
+ this._fire(command, 'preExecute', action);
1112
+ if (handler.preExecute) {
1113
+ handler.preExecute(context);
1114
+ }
1115
+ this._fire(command, 'preExecuted', action);
1116
+ }
1117
+
1118
+ // guard against illegal nested command stack invocations
1119
+ this._atomicDo(() => {
1120
+ this._fire(command, 'execute', action);
1121
+ if (handler.execute) {
1122
+ // actual execute + mark return results as dirty
1123
+ this._markDirty(handler.execute(context));
1124
+ }
1125
+
1126
+ // log to stack
1127
+ this._executedAction(action, redo);
1128
+ this._fire(command, 'executed', action);
1129
+ });
1130
+ if (!redo) {
1131
+ this._fire(command, 'postExecute', action);
1132
+ if (handler.postExecute) {
1133
+ handler.postExecute(context);
1134
+ }
1135
+ this._fire(command, 'postExecuted', action);
1136
+ }
1137
+ this._popAction();
1138
+ };
1139
+ CommandStack.prototype._pushAction = function (action) {
1140
+ const execution = this._currentExecution,
1141
+ actions = execution.actions;
1142
+ const baseAction = actions[0];
1143
+ if (execution.atomic) {
1144
+ throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
1145
+ }
1146
+ if (!action.id) {
1147
+ action.id = baseAction && baseAction.id || this._createId();
1148
+ }
1149
+ actions.push(action);
1150
+ };
1151
+ CommandStack.prototype._popAction = function () {
1152
+ const execution = this._currentExecution,
1153
+ trigger = execution.trigger,
1154
+ actions = execution.actions,
1155
+ dirty = execution.dirty;
1156
+ actions.pop();
1157
+ if (!actions.length) {
1158
+ this._eventBus.fire('elements.changed', {
1159
+ elements: uniqueBy('id', dirty.reverse())
1160
+ });
1161
+ dirty.length = 0;
1162
+ this._fire('changed', {
1163
+ trigger: trigger
1164
+ });
1165
+ execution.trigger = null;
1166
+ }
1167
+ };
1168
+ CommandStack.prototype._markDirty = function (elements) {
1169
+ const execution = this._currentExecution;
1170
+ if (!elements) {
1171
+ return;
1172
+ }
1173
+ elements = isArray(elements) ? elements : [elements];
1174
+ execution.dirty = execution.dirty.concat(elements);
1175
+ };
1176
+ CommandStack.prototype._executedAction = function (action, redo) {
1177
+ const stackIdx = ++this._stackIdx;
1178
+ if (!redo) {
1179
+ this._stack.splice(stackIdx, this._stack.length, action);
1180
+ }
1181
+ };
1182
+ CommandStack.prototype._revertedAction = function (action) {
1183
+ this._stackIdx--;
1184
+ };
1185
+ CommandStack.prototype._getHandler = function (command) {
1186
+ return this._handlerMap[command];
1187
+ };
1188
+ CommandStack.prototype._setHandler = function (command, handler) {
1189
+ if (!command || !handler) {
1190
+ throw new Error('command and handler required');
1191
+ }
1192
+ if (this._handlerMap[command]) {
1193
+ throw new Error('overriding handler for command <' + command + '>');
1194
+ }
1195
+ this._handlerMap[command] = handler;
1196
+ };
1197
+
1198
+ /**
1199
+ * @type { import('didi').ModuleDeclaration }
1200
+ */
1201
+ var commandModule = {
1202
+ commandStack: ['type', CommandStack]
1203
+ };
1204
+
1205
+ class UpdateFieldValidationHandler {
1206
+ constructor(form, validator) {
1207
+ this._form = form;
1208
+ this._validator = validator;
1209
+ }
1210
+ execute(context) {
1211
+ const {
1212
+ field,
1213
+ value
1214
+ } = context;
1215
+ const {
1216
+ errors
1217
+ } = this._form._getState();
1218
+ context.oldErrors = clone(errors);
1219
+ const fieldErrors = this._validator.validateField(field, value);
1220
+ const updatedErrors = set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
1221
+ this._form._setState({
1222
+ errors: updatedErrors
1223
+ });
1224
+ }
1225
+ revert(context) {
1226
+ this._form._setState({
1227
+ errors: context.oldErrors
1228
+ });
1229
+ }
1230
+ }
1231
+ UpdateFieldValidationHandler.$inject = ['form', 'validator'];
1232
+
1233
+ class ViewerCommands {
1234
+ constructor(commandStack, eventBus) {
1235
+ this._commandStack = commandStack;
1236
+ eventBus.on('form.init', () => {
1237
+ this.registerHandlers();
1238
+ });
1239
+ }
1240
+ registerHandlers() {
1241
+ Object.entries(this.getHandlers()).forEach(([id, handler]) => {
1242
+ this._commandStack.registerHandler(id, handler);
1243
+ });
1244
+ }
1245
+ getHandlers() {
1246
+ return {
1247
+ 'formField.validation.update': UpdateFieldValidationHandler
1248
+ };
1249
+ }
1250
+ updateFieldValidation(field, value) {
1251
+ const context = {
1252
+ field,
1253
+ value
1254
+ };
1255
+ this._commandStack.execute('formField.validation.update', context);
1256
+ }
1257
+ }
1258
+ ViewerCommands.$inject = ['commandStack', 'eventBus'];
1259
+
1260
+ var ViewerCommandsModule = {
1261
+ __depends__: [commandModule],
1262
+ __init__: ['viewerCommands'],
1263
+ viewerCommands: ['type', ViewerCommands]
1264
+ };
1265
+
474
1266
  var FN_REF = '__fn';
475
1267
  var DEFAULT_PRIORITY = 1000;
476
1268
  var slice = Array.prototype.slice;
@@ -1074,8 +1866,8 @@ Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
1074
1866
 
1075
1867
  // helpers //////////
1076
1868
 
1077
- /**
1078
- * Helper function to evaluate optional FEEL validation values.
1869
+ /**
1870
+ * Helper function to evaluate optional FEEL validation values.
1079
1871
  */
1080
1872
  function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
1081
1873
  const evaluatedValidate = {
@@ -1108,72 +1900,415 @@ function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form
1108
1900
  return evaluatedValidate;
1109
1901
  }
1110
1902
 
1111
- class FormFieldRegistry {
1112
- constructor(eventBus) {
1113
- this._eventBus = eventBus;
1114
- this._formFields = {};
1115
- eventBus.on('form.clear', () => this.clear());
1116
- this._ids = new Ids([32, 36, 1]);
1117
- this._keys = new Ids([32, 36, 1]);
1903
+ class Importer {
1904
+ /**
1905
+ * @constructor
1906
+ * @param { import('./FormFieldRegistry').default } formFieldRegistry
1907
+ * @param { import('./PathRegistry').default } pathRegistry
1908
+ * @param { import('./FieldFactory').default } fieldFactory
1909
+ * @param { import('./FormLayouter').default } formLayouter
1910
+ */
1911
+ constructor(formFieldRegistry, pathRegistry, fieldFactory, formLayouter) {
1912
+ this._formFieldRegistry = formFieldRegistry;
1913
+ this._pathRegistry = pathRegistry;
1914
+ this._fieldFactory = fieldFactory;
1915
+ this._formLayouter = formLayouter;
1118
1916
  }
1119
- add(formField) {
1120
- const {
1121
- id
1122
- } = formField;
1123
- if (this._formFields[id]) {
1124
- throw new Error(`form field with ID ${id} already exists`);
1917
+
1918
+ /**
1919
+ * Import schema creating rows, fields, attaching additional
1920
+ * information to each field and adding fields to the
1921
+ * field registry.
1922
+ *
1923
+ * Additional information attached:
1924
+ *
1925
+ * * `id` (unless present)
1926
+ * * `_parent`
1927
+ * * `_path`
1928
+ *
1929
+ * @param {any} schema
1930
+ *
1931
+ * @typedef {{ warnings: Error[], schema: any }} ImportResult
1932
+ * @returns {ImportResult}
1933
+ */
1934
+ importSchema(schema) {
1935
+ // TODO: Add warnings
1936
+ const warnings = [];
1937
+ try {
1938
+ this._cleanup();
1939
+ const importedSchema = this.importFormField(clone(schema));
1940
+ this._formLayouter.calculateLayout(clone(importedSchema));
1941
+ return {
1942
+ schema: importedSchema,
1943
+ warnings
1944
+ };
1945
+ } catch (err) {
1946
+ this._cleanup();
1947
+ err.warnings = warnings;
1948
+ throw err;
1125
1949
  }
1126
- this._eventBus.fire('formField.add', {
1127
- formField
1950
+ }
1951
+ _cleanup() {
1952
+ this._formLayouter.clear();
1953
+ this._formFieldRegistry.clear();
1954
+ this._pathRegistry.clear();
1955
+ }
1956
+
1957
+ /**
1958
+ * @param {{[x: string]: any}} fieldAttrs
1959
+ * @param {String} [parentId]
1960
+ * @param {number} [index]
1961
+ *
1962
+ * @return {any} field
1963
+ */
1964
+ importFormField(fieldAttrs, parentId, index) {
1965
+ const {
1966
+ components
1967
+ } = fieldAttrs;
1968
+ let parent, path;
1969
+ if (parentId) {
1970
+ parent = this._formFieldRegistry.get(parentId);
1971
+ }
1972
+
1973
+ // set form field path
1974
+ path = parent ? [...parent._path, 'components', index] : [];
1975
+ const field = this._fieldFactory.create({
1976
+ ...fieldAttrs,
1977
+ _path: path,
1978
+ _parent: parentId
1979
+ }, false);
1980
+ this._formFieldRegistry.add(field);
1981
+ if (components) {
1982
+ field.components = this.importFormFields(components, field.id);
1983
+ }
1984
+ return field;
1985
+ }
1986
+
1987
+ /**
1988
+ * @param {Array<any>} components
1989
+ * @param {string} parentId
1990
+ *
1991
+ * @return {Array<any>} imported components
1992
+ */
1993
+ importFormFields(components, parentId) {
1994
+ return components.map((component, index) => {
1995
+ return this.importFormField(component, parentId, index);
1128
1996
  });
1129
- this._formFields[id] = formField;
1130
1997
  }
1131
- remove(formField) {
1998
+ }
1999
+ Importer.$inject = ['formFieldRegistry', 'pathRegistry', 'fieldFactory', 'formLayouter'];
2000
+
2001
+ class FieldFactory {
2002
+ /**
2003
+ * @constructor
2004
+ *
2005
+ * @param formFieldRegistry
2006
+ * @param formFields
2007
+ */
2008
+ constructor(formFieldRegistry, pathRegistry, formFields) {
2009
+ this._formFieldRegistry = formFieldRegistry;
2010
+ this._pathRegistry = pathRegistry;
2011
+ this._formFields = formFields;
2012
+ }
2013
+ create(attrs, applyDefaults = true) {
1132
2014
  const {
1133
- id
1134
- } = formField;
1135
- if (!this._formFields[id]) {
1136
- return;
2015
+ id,
2016
+ type,
2017
+ key,
2018
+ path,
2019
+ _parent
2020
+ } = attrs;
2021
+ const fieldDefinition = this._formFields.get(type);
2022
+ if (!fieldDefinition) {
2023
+ throw new Error(`form field of type <${type}> not supported`);
1137
2024
  }
1138
- this._eventBus.fire('formField.remove', {
1139
- formField
2025
+ const {
2026
+ config
2027
+ } = fieldDefinition;
2028
+ if (!config) {
2029
+ throw new Error(`form field of type <${type}> has no config`);
2030
+ }
2031
+ if (id && this._formFieldRegistry._ids.assigned(id)) {
2032
+ throw new Error(`form field with id <${id}> already exists`);
2033
+ }
2034
+
2035
+ // ensure that we can claim the path
2036
+
2037
+ const parent = _parent && this._formFieldRegistry.get(_parent);
2038
+ const parentPath = parent && this._pathRegistry.getValuePath(parent) || [];
2039
+ if (config.keyed && key && !this._pathRegistry.canClaimPath([...parentPath, ...key.split('.')], true)) {
2040
+ throw new Error(`binding path '${[...parentPath, key].join('.')}' is already claimed`);
2041
+ }
2042
+ if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], false)) {
2043
+ throw new Error(`binding path '${[...parentPath, ...path.split('.')].join('.')}' is already claimed`);
2044
+ }
2045
+ const labelAttrs = applyDefaults && config.label ? {
2046
+ label: config.label
2047
+ } : {};
2048
+ const field = config.create({
2049
+ ...labelAttrs,
2050
+ ...attrs
1140
2051
  });
1141
- delete this._formFields[id];
2052
+ this._ensureId(field);
2053
+ if (config.keyed) {
2054
+ this._ensureKey(field);
2055
+ }
2056
+ if (config.pathed && path) {
2057
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), false);
2058
+ }
2059
+ return field;
1142
2060
  }
1143
- get(id) {
1144
- return this._formFields[id];
2061
+ _ensureId(field) {
2062
+ if (field.id) {
2063
+ this._formFieldRegistry._ids.claim(field.id, field);
2064
+ return;
2065
+ }
2066
+ let prefix = 'Field';
2067
+ if (field.type === 'default') {
2068
+ prefix = 'Form';
2069
+ }
2070
+ field.id = this._formFieldRegistry._ids.nextPrefixed(`${prefix}_`, field);
2071
+ }
2072
+ _ensureKey(field) {
2073
+ if (!field.key) {
2074
+ let random;
2075
+ const parent = this._formFieldRegistry.get(field._parent);
2076
+
2077
+ // ensure key uniqueness at level
2078
+ do {
2079
+ random = Math.random().toString(36).substring(7);
2080
+ } while (parent && parent.components.some(child => child.key === random));
2081
+ field.key = `${field.type}_${random}`;
2082
+ }
2083
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), true);
1145
2084
  }
1146
- getAll() {
1147
- return Object.values(this._formFields);
2085
+ }
2086
+ FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
2087
+
2088
+ /**
2089
+ * The PathRegistry class manages a hierarchical structure of paths associated with form fields.
2090
+ * It enables claiming, unclaiming, and validating paths within this structure.
2091
+ *
2092
+ * Example Tree Structure:
2093
+ *
2094
+ * [
2095
+ * {
2096
+ * segment: 'root',
2097
+ * claimCount: 1,
2098
+ * children: [
2099
+ * {
2100
+ * segment: 'child1',
2101
+ * claimCount: 2,
2102
+ * children: null // A leaf node (closed path)
2103
+ * },
2104
+ * {
2105
+ * segment: 'child2',
2106
+ * claimCount: 1,
2107
+ * children: [
2108
+ * {
2109
+ * segment: 'subChild1',
2110
+ * claimCount: 1,
2111
+ * children: [] // An open node (open path)
2112
+ * }
2113
+ * ]
2114
+ * }
2115
+ * ]
2116
+ * }
2117
+ * ]
2118
+ */
2119
+ class PathRegistry {
2120
+ constructor(formFieldRegistry, formFields) {
2121
+ this._formFieldRegistry = formFieldRegistry;
2122
+ this._formFields = formFields;
2123
+ this._dataPaths = [];
1148
2124
  }
1149
- forEach(callback) {
1150
- this.getAll().forEach(formField => callback(formField));
2125
+ canClaimPath(path, closed = false) {
2126
+ let node = {
2127
+ children: this._dataPaths
2128
+ };
2129
+ for (const segment of path) {
2130
+ node = _getNextSegment(node, segment);
2131
+
2132
+ // if no node at that path, we can claim it no matter what
2133
+ if (!node) {
2134
+ return true;
2135
+ }
2136
+
2137
+ // if we reach a leaf node, definitely not claimable
2138
+ if (node.children === null) {
2139
+ return false;
2140
+ }
2141
+ }
2142
+
2143
+ // if after all segments we reach a node with children, we can claim it only openly
2144
+ return !closed;
2145
+ }
2146
+ claimPath(path, closed = false) {
2147
+ if (!this.canClaimPath(path, closed)) {
2148
+ throw new Error(`cannot claim path '${path.join('.')}'`);
2149
+ }
2150
+ let node = {
2151
+ children: this._dataPaths
2152
+ };
2153
+ for (const segment of path) {
2154
+ let child = _getNextSegment(node, segment);
2155
+ if (!child) {
2156
+ child = {
2157
+ segment,
2158
+ claimCount: 1,
2159
+ children: []
2160
+ };
2161
+ node.children.push(child);
2162
+ } else {
2163
+ child.claimCount++;
2164
+ }
2165
+ node = child;
2166
+ }
2167
+ if (closed) {
2168
+ node.children = null;
2169
+ }
2170
+ }
2171
+ unclaimPath(path) {
2172
+ // verification Pass
2173
+ let node = {
2174
+ children: this._dataPaths
2175
+ };
2176
+ for (const segment of path) {
2177
+ const child = _getNextSegment(node, segment);
2178
+ if (!child) {
2179
+ throw new Error(`no open path found for '${path.join('.')}'`);
2180
+ }
2181
+ node = child;
2182
+ }
2183
+
2184
+ // mutation Pass
2185
+ node = {
2186
+ children: this._dataPaths
2187
+ };
2188
+ for (const segment of path) {
2189
+ const child = _getNextSegment(node, segment);
2190
+ child.claimCount--;
2191
+ if (child.claimCount === 0) {
2192
+ node.children.splice(node.children.indexOf(child), 1);
2193
+ break; // Abort early if claimCount reaches zero
2194
+ }
2195
+
2196
+ node = child;
2197
+ }
2198
+ }
2199
+
2200
+ /**
2201
+ * Applies a function (fn) recursively on a given field and its children.
2202
+ *
2203
+ * - `field`: Starting field object.
2204
+ * - `fn`: Function to apply.
2205
+ * - `context`: Optional object for passing data between calls.
2206
+ *
2207
+ * Stops early if `fn` returns `false`. Useful for traversing the form field tree.
2208
+ *
2209
+ * @returns {boolean} Success status based on function execution.
2210
+ */
2211
+ executeRecursivelyOnFields(field, fn, context = {}) {
2212
+ let result = true;
2213
+ const formFieldConfig = this._formFields.get(field.type).config;
2214
+ if (formFieldConfig.keyed) {
2215
+ const callResult = fn({
2216
+ field,
2217
+ isClosed: true,
2218
+ context
2219
+ });
2220
+ return result && callResult;
2221
+ } else if (formFieldConfig.pathed) {
2222
+ const callResult = fn({
2223
+ field,
2224
+ isClosed: false,
2225
+ context
2226
+ });
2227
+ result = result && callResult;
2228
+ }
2229
+ if (field.components) {
2230
+ for (const child of field.components) {
2231
+ const callResult = this.executeRecursivelyOnFields(child, fn, clone(context));
2232
+ result = result && callResult;
2233
+
2234
+ // only stop executing if false is specifically returned, not if undefined
2235
+ if (result === false) {
2236
+ return result;
2237
+ }
2238
+ }
2239
+ }
2240
+ return result;
2241
+ }
2242
+
2243
+ /**
2244
+ * Generates an array representing the binding path to an underlying data object for a form field.
2245
+ *
2246
+ * @param {Object} field - The field object with properties: `key`, `path`, `id`, and optionally `_parent`.
2247
+ * @param {Object} [options={}] - Configuration options.
2248
+ * @param {Object} [options.replacements={}] - A map of field IDs to alternative path arrays.
2249
+ * @param {Object} [options.cutoffNode] - The ID of the parent field at which to stop generating the path.
2250
+ *
2251
+ * @returns {(Array<string>|undefined)} An array of strings representing the binding path, or undefined if not determinable.
2252
+ */
2253
+ getValuePath(field, options = {}) {
2254
+ const {
2255
+ replacements = {},
2256
+ cutoffNode = null
2257
+ } = options;
2258
+ let localValuePath = [];
2259
+ const hasReplacement = Object.prototype.hasOwnProperty.call(replacements, field.id);
2260
+ const formFieldConfig = this._formFields.get(field.type).config;
2261
+ if (hasReplacement) {
2262
+ const replacement = replacements[field.id];
2263
+ if (replacement === null || replacement === undefined || replacement === '') {
2264
+ localValuePath = [];
2265
+ } else if (typeof replacement === 'string') {
2266
+ localValuePath = replacement.split('.');
2267
+ } else if (Array.isArray(replacement)) {
2268
+ localValuePath = replacement;
2269
+ } else {
2270
+ throw new Error(`replacements for field ${field.id} must be a string, array or null/undefined`);
2271
+ }
2272
+ } else if (formFieldConfig.keyed) {
2273
+ localValuePath = field.key.split('.');
2274
+ } else if (formFieldConfig.pathed && field.path) {
2275
+ localValuePath = field.path.split('.');
2276
+ }
2277
+ if (field._parent && field._parent !== cutoffNode) {
2278
+ const parent = this._formFieldRegistry.get(field._parent);
2279
+ return [...(this.getValuePath(parent, options) || []), ...localValuePath];
2280
+ }
2281
+ return localValuePath;
1151
2282
  }
1152
2283
  clear() {
1153
- this._formFields = {};
1154
- this._ids.clear();
1155
- this._keys.clear();
2284
+ this._dataPaths = [];
1156
2285
  }
1157
2286
  }
1158
- FormFieldRegistry.$inject = ['eventBus'];
2287
+ const _getNextSegment = (node, segment) => {
2288
+ if (isArray(node.children)) {
2289
+ return node.children.find(node => node.segment === segment) || null;
2290
+ }
2291
+ return null;
2292
+ };
2293
+ PathRegistry.$inject = ['formFieldRegistry', 'formFields'];
1159
2294
 
1160
- /**
1161
- * @typedef { { id: String, components: Array<String> } } FormRow
1162
- * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
2295
+ /**
2296
+ * @typedef { { id: String, components: Array<String> } } FormRow
2297
+ * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1163
2298
  */
1164
2299
 
1165
- /**
1166
- * Maintains the Form layout in a given structure, for example
1167
- *
1168
- * [
1169
- * {
1170
- * formFieldId: 'FormField_1',
1171
- * rows: [
1172
- * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1173
- * ]
1174
- * }
1175
- * ]
1176
- *
2300
+ /**
2301
+ * Maintains the Form layout in a given structure, for example
2302
+ *
2303
+ * [
2304
+ * {
2305
+ * formFieldId: 'FormField_1',
2306
+ * rows: [
2307
+ * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
2308
+ * ]
2309
+ * }
2310
+ * ]
2311
+ *
1177
2312
  */
1178
2313
  class FormLayouter {
1179
2314
  constructor(eventBus) {
@@ -1183,8 +2318,8 @@ class FormLayouter {
1183
2318
  this._eventBus = eventBus;
1184
2319
  }
1185
2320
 
1186
- /**
1187
- * @param {FormRow} row
2321
+ /**
2322
+ * @param {FormRow} row
1188
2323
  */
1189
2324
  addRow(formFieldId, row) {
1190
2325
  let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
@@ -1198,18 +2333,18 @@ class FormLayouter {
1198
2333
  rowsPerComponent.rows.push(row);
1199
2334
  }
1200
2335
 
1201
- /**
1202
- * @param {String} id
1203
- * @returns {FormRow}
2336
+ /**
2337
+ * @param {String} id
2338
+ * @returns {FormRow}
1204
2339
  */
1205
2340
  getRow(id) {
1206
2341
  const rows = allRows(this._rows);
1207
2342
  return rows.find(r => r.id === id);
1208
2343
  }
1209
2344
 
1210
- /**
1211
- * @param {any} formField
1212
- * @returns {FormRow}
2345
+ /**
2346
+ * @param {any} formField
2347
+ * @returns {FormRow}
1213
2348
  */
1214
2349
  getRowForField(formField) {
1215
2350
  return allRows(this._rows).find(r => {
@@ -1220,9 +2355,9 @@ class FormLayouter {
1220
2355
  });
1221
2356
  }
1222
2357
 
1223
- /**
1224
- * @param {String} formFieldId
1225
- * @returns { Array<FormRow> }
2358
+ /**
2359
+ * @param {String} formFieldId
2360
+ * @returns { Array<FormRow> }
1226
2361
  */
1227
2362
  getRows(formFieldId) {
1228
2363
  const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
@@ -1232,22 +2367,22 @@ class FormLayouter {
1232
2367
  return rowsForField.rows;
1233
2368
  }
1234
2369
 
1235
- /**
1236
- * @returns {string}
2370
+ /**
2371
+ * @returns {string}
1237
2372
  */
1238
2373
  nextRowId() {
1239
2374
  return this._ids.nextPrefixed('Row_');
1240
2375
  }
1241
2376
 
1242
- /**
1243
- * @param {any} formField
2377
+ /**
2378
+ * @param {any} formField
1244
2379
  */
1245
2380
  calculateLayout(formField) {
1246
2381
  const {
1247
2382
  type,
1248
2383
  components
1249
2384
  } = formField;
1250
- if (type !== 'default' || !components) {
2385
+ if (type !== 'default' && type !== 'group' || !components) {
1251
2386
  return;
1252
2387
  }
1253
2388
 
@@ -1269,363 +2404,85 @@ class FormLayouter {
1269
2404
  rows: this._rows
1270
2405
  });
1271
2406
  }
1272
- clear() {
1273
- this._rows = [];
1274
- this._ids.clear();
1275
-
1276
- // fire event to notify interested parties
1277
- this._eventBus.fire('form.layoutCleared');
1278
- }
1279
- }
1280
- FormLayouter.$inject = ['eventBus'];
1281
-
1282
- // helpers //////
1283
-
1284
- function groupByRow(components, ids) {
1285
- return groupBy(components, c => {
1286
- // mitigate missing row by creating new (handle legacy)
1287
- const {
1288
- layout
1289
- } = c;
1290
- if (!layout || !layout.row) {
1291
- return ids.nextPrefixed('Row_');
1292
- }
1293
- return layout.row;
1294
- });
1295
- }
1296
-
1297
- /**
1298
- * @param {Array<FormRows>} formRows
1299
- * @returns {Array<FormRow>}
1300
- */
1301
- function allRows(formRows) {
1302
- return flatten(formRows.map(c => c.rows));
1303
- }
1304
-
1305
- // config ///////////////////
1306
-
1307
- const MINUTES_IN_DAY = 60 * 24;
1308
- const DATETIME_SUBTYPES = {
1309
- DATE: 'date',
1310
- TIME: 'time',
1311
- DATETIME: 'datetime'
1312
- };
1313
- const TIME_SERIALISING_FORMATS = {
1314
- UTC_OFFSET: 'utc_offset',
1315
- UTC_NORMALIZED: 'utc_normalized',
1316
- NO_TIMEZONE: 'no_timezone'
1317
- };
1318
- const DATETIME_SUBTYPES_LABELS = {
1319
- [DATETIME_SUBTYPES.DATE]: 'Date',
1320
- [DATETIME_SUBTYPES.TIME]: 'Time',
1321
- [DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
1322
- };
1323
- const TIME_SERIALISINGFORMAT_LABELS = {
1324
- [TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
1325
- [TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
1326
- [TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
1327
- };
1328
- const DATETIME_SUBTYPE_PATH = ['subtype'];
1329
- const DATE_LABEL_PATH = ['dateLabel'];
1330
- const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
1331
- const TIME_LABEL_PATH = ['timeLabel'];
1332
- const TIME_USE24H_PATH = ['use24h'];
1333
- const TIME_INTERVAL_PATH = ['timeInterval'];
1334
- const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
1335
-
1336
- // config ///////////////////
1337
-
1338
- const VALUES_SOURCES = {
1339
- STATIC: 'static',
1340
- INPUT: 'input',
1341
- EXPRESSION: 'expression'
1342
- };
1343
- const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
1344
- const VALUES_SOURCES_LABELS = {
1345
- [VALUES_SOURCES.STATIC]: 'Static',
1346
- [VALUES_SOURCES.INPUT]: 'Input data',
1347
- [VALUES_SOURCES.EXPRESSION]: 'Expression'
1348
- };
1349
- const VALUES_SOURCES_PATHS = {
1350
- [VALUES_SOURCES.STATIC]: ['values'],
1351
- [VALUES_SOURCES.INPUT]: ['valuesKey'],
1352
- [VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
1353
- };
1354
- const VALUES_SOURCES_DEFAULTS = {
1355
- [VALUES_SOURCES.STATIC]: [{
1356
- label: 'Value',
1357
- value: 'value'
1358
- }],
1359
- [VALUES_SOURCES.INPUT]: '',
1360
- [VALUES_SOURCES.EXPRESSION]: '='
1361
- };
1362
-
1363
- // helpers ///////////////////
1364
-
1365
- function getValuesSource(field) {
1366
- for (const source of Object.values(VALUES_SOURCES)) {
1367
- if (get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
1368
- return source;
1369
- }
1370
- }
1371
- return VALUES_SOURCE_DEFAULT;
1372
- }
1373
-
1374
- function createInjector(bootstrapModules) {
1375
- const injector = new Injector(bootstrapModules);
1376
- injector.init();
1377
- return injector;
1378
- }
1379
-
1380
- /**
1381
- * @param {string?} prefix
1382
- *
1383
- * @returns Element
1384
- */
1385
- function createFormContainer(prefix = 'fjs') {
1386
- const container = document.createElement('div');
1387
- container.classList.add(`${prefix}-container`);
1388
- return container;
1389
- }
1390
-
1391
- const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
1392
- const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
1393
- function findErrors(errors, path) {
1394
- return errors[pathStringify(path)];
1395
- }
1396
- function isRequired(field) {
1397
- return field.required;
1398
- }
1399
- function pathParse(path) {
1400
- if (!path) {
1401
- return [];
1402
- }
1403
- return path.split('.').map(key => {
1404
- return isNaN(parseInt(key)) ? key : parseInt(key);
1405
- });
1406
- }
1407
- function pathsEqual(a, b) {
1408
- return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
1409
- }
1410
- function pathStringify(path) {
1411
- if (!path) {
1412
- return '';
1413
- }
1414
- return path.join('.');
1415
- }
1416
- const indices = {};
1417
- function generateIndexForType(type) {
1418
- if (type in indices) {
1419
- indices[type]++;
1420
- } else {
1421
- indices[type] = 1;
1422
- }
1423
- return indices[type];
1424
- }
1425
- function generateIdForType(type) {
1426
- return `${type}${generateIndexForType(type)}`;
1427
- }
1428
-
1429
- /**
1430
- * @template T
1431
- * @param {T} data
1432
- * @param {(this: any, key: string, value: any) => any} [replacer]
1433
- * @return {T}
1434
- */
1435
- function clone(data, replacer) {
1436
- return JSON.parse(JSON.stringify(data, replacer));
1437
- }
1438
-
1439
- /**
1440
- * Parse the schema for input variables a form might make use of
1441
- *
1442
- * @param {any} schema
1443
- *
1444
- * @return {string[]}
1445
- */
1446
- function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null), templating = new FeelersTemplating()) {
1447
- if (!schema.components) {
1448
- return [];
1449
- }
1450
- const variables = schema.components.reduce((variables, component) => {
1451
- const {
1452
- key,
1453
- valuesKey,
1454
- type
1455
- } = component;
1456
- if (['button'].includes(type)) {
1457
- return variables;
1458
- }
1459
- if (key) {
1460
- variables = [...variables, key];
1461
- }
1462
- if (valuesKey) {
1463
- variables = [...variables, valuesKey];
1464
- }
1465
- EXPRESSION_PROPERTIES.forEach(prop => {
1466
- const property = get(component, prop.split('.'));
1467
- if (property && expressionLanguage.isExpression(property)) {
1468
- const expressionVariables = expressionLanguage.getVariableNames(property, {
1469
- type: 'expression'
1470
- });
1471
- variables = [...variables, ...expressionVariables];
1472
- }
1473
- });
1474
- TEMPLATE_PROPERTIES.forEach(prop => {
1475
- const property = get(component, prop.split('.'));
1476
- if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
1477
- const templateVariables = templating.getVariableNames(property);
1478
- variables = [...variables, ...templateVariables];
1479
- }
1480
- });
1481
- return variables;
1482
- }, []);
1483
-
1484
- // remove duplicates
1485
- return Array.from(new Set(variables));
1486
- }
1487
-
1488
- class Importer {
1489
- /**
1490
- * @constructor
1491
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
1492
- * @param { import('../render/FormFields').default } formFields
1493
- * @param { import('../core').FormLayouter } formLayouter
1494
- */
1495
- constructor(formFieldRegistry, formFields, formLayouter) {
1496
- this._formFieldRegistry = formFieldRegistry;
1497
- this._formFields = formFields;
1498
- this._formLayouter = formLayouter;
1499
- }
2407
+ clear() {
2408
+ this._rows = [];
2409
+ this._ids.clear();
1500
2410
 
1501
- /**
1502
- * Import schema adding `id`, `_parent` and `_path`
1503
- * information to each field and adding it to the
1504
- * form field registry.
1505
- *
1506
- * @param {any} schema
1507
- * @param {any} [data]
1508
- *
1509
- * @return { { warnings: Array<any>, schema: any, data: any } }
1510
- */
1511
- importSchema(schema, data = {}) {
1512
- // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
1513
- const warnings = [];
1514
- try {
1515
- this._formLayouter.clear();
1516
- const importedSchema = this.importFormField(clone(schema)),
1517
- initializedData = this.initializeFieldValues(clone(data));
1518
- this._formLayouter.calculateLayout(clone(importedSchema));
1519
- return {
1520
- warnings,
1521
- schema: importedSchema,
1522
- data: initializedData
1523
- };
1524
- } catch (err) {
1525
- err.warnings = warnings;
1526
- throw err;
1527
- }
2411
+ // fire event to notify interested parties
2412
+ this._eventBus.fire('form.layoutCleared');
1528
2413
  }
2414
+ }
2415
+ FormLayouter.$inject = ['eventBus'];
1529
2416
 
1530
- /**
1531
- * @param {any} formField
1532
- * @param {string} [parentId]
1533
- *
1534
- * @return {any} importedField
1535
- */
1536
- importFormField(formField, parentId) {
2417
+ // helpers //////
2418
+
2419
+ function groupByRow(components, ids) {
2420
+ return groupBy(components, c => {
2421
+ // mitigate missing row by creating new (handle legacy)
1537
2422
  const {
1538
- components,
1539
- key,
1540
- type,
1541
- id = generateIdForType(type)
1542
- } = formField;
1543
- if (parentId) {
1544
- // set form field parent
1545
- formField._parent = parentId;
1546
- }
1547
- if (!this._formFields.get(type)) {
1548
- throw new Error(`form field of type <${type}> not supported`);
2423
+ layout
2424
+ } = c;
2425
+ if (!layout || !layout.row) {
2426
+ return ids.nextPrefixed('Row_');
1549
2427
  }
1550
- if (key) {
1551
- // validate <key> uniqueness
1552
- if (this._formFieldRegistry._keys.assigned(key)) {
1553
- throw new Error(`form field with key <${key}> already exists`);
1554
- }
1555
- this._formFieldRegistry._keys.claim(key, formField);
2428
+ return layout.row;
2429
+ });
2430
+ }
1556
2431
 
1557
- // TODO: buttons should not have key
1558
- if (type !== 'button') {
1559
- // set form field path
1560
- formField._path = [key];
1561
- }
1562
- }
1563
- if (id) {
1564
- // validate <id> uniqueness
1565
- if (this._formFieldRegistry._ids.assigned(id)) {
1566
- throw new Error(`form field with id <${id}> already exists`);
1567
- }
1568
- this._formFieldRegistry._ids.claim(id, formField);
1569
- }
2432
+ /**
2433
+ * @param {Array<FormRows>} formRows
2434
+ * @returns {Array<FormRow>}
2435
+ */
2436
+ function allRows(formRows) {
2437
+ return flatten(formRows.map(c => c.rows));
2438
+ }
1570
2439
 
1571
- // set form field ID
1572
- formField.id = id;
1573
- this._formFieldRegistry.add(formField);
1574
- if (components) {
1575
- this.importFormFields(components, id);
2440
+ class FormFieldRegistry {
2441
+ constructor(eventBus) {
2442
+ this._eventBus = eventBus;
2443
+ this._formFields = {};
2444
+ eventBus.on('form.clear', () => this.clear());
2445
+ this._ids = new Ids([32, 36, 1]);
2446
+ }
2447
+ add(formField) {
2448
+ const {
2449
+ id
2450
+ } = formField;
2451
+ if (this._formFields[id]) {
2452
+ throw new Error(`form field with ID ${id} already exists`);
1576
2453
  }
1577
- return formField;
2454
+ this._eventBus.fire('formField.add', {
2455
+ formField
2456
+ });
2457
+ this._formFields[id] = formField;
1578
2458
  }
1579
- importFormFields(components, parentId) {
1580
- components.forEach(component => {
1581
- this.importFormField(component, parentId);
2459
+ remove(formField) {
2460
+ const {
2461
+ id
2462
+ } = formField;
2463
+ if (!this._formFields[id]) {
2464
+ return;
2465
+ }
2466
+ this._eventBus.fire('formField.remove', {
2467
+ formField
1582
2468
  });
2469
+ delete this._formFields[id];
1583
2470
  }
1584
-
1585
- /**
1586
- * @param {Object} data
1587
- *
1588
- * @return {Object} initializedData
1589
- */
1590
- initializeFieldValues(data) {
1591
- return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
1592
- const {
1593
- defaultValue,
1594
- _path,
1595
- type
1596
- } = formField;
1597
-
1598
- // try to get value from data
1599
- // if unavailable - try to get default value from form field
1600
- // if unavailable - get empty value from form field
1601
-
1602
- if (_path) {
1603
- const {
1604
- config: fieldConfig
1605
- } = this._formFields.get(type);
1606
- let valueData = get(data, _path);
1607
- if (!isUndefined(valueData) && fieldConfig.sanitizeValue) {
1608
- valueData = fieldConfig.sanitizeValue({
1609
- formField,
1610
- data,
1611
- value: valueData
1612
- });
1613
- }
1614
- const initializedFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
1615
- initializedData = {
1616
- ...initializedData,
1617
- [_path[0]]: initializedFieldValue
1618
- };
1619
- }
1620
- return initializedData;
1621
- }, data);
2471
+ get(id) {
2472
+ return this._formFields[id];
2473
+ }
2474
+ getAll() {
2475
+ return Object.values(this._formFields);
2476
+ }
2477
+ forEach(callback) {
2478
+ this.getAll().forEach(formField => callback(formField));
2479
+ }
2480
+ clear() {
2481
+ this._formFields = {};
2482
+ this._ids.clear();
1622
2483
  }
1623
2484
  }
1624
- Importer.$inject = ['formFieldRegistry', 'formFields', 'formLayouter'];
1625
-
1626
- var importModule = {
1627
- importer: ['type', Importer]
1628
- };
2485
+ FormFieldRegistry.$inject = ['eventBus'];
1629
2486
 
1630
2487
  function formFieldClasses(type, {
1631
2488
  errors = [],
@@ -1659,7 +2516,7 @@ function prefixId(id, formId) {
1659
2516
  return `fjs-form-${id}`;
1660
2517
  }
1661
2518
 
1662
- const type$b = 'button';
2519
+ const type$c = 'button';
1663
2520
  function Button(props) {
1664
2521
  const {
1665
2522
  disabled,
@@ -1669,7 +2526,7 @@ function Button(props) {
1669
2526
  action = 'submit'
1670
2527
  } = field;
1671
2528
  return jsx("div", {
1672
- class: formFieldClasses(type$b),
2529
+ class: formFieldClasses(type$c),
1673
2530
  children: jsx("button", {
1674
2531
  class: "fjs-button",
1675
2532
  type: action,
@@ -1679,8 +2536,8 @@ function Button(props) {
1679
2536
  });
1680
2537
  }
1681
2538
  Button.config = {
1682
- type: type$b,
1683
- keyed: true,
2539
+ type: type$c,
2540
+ keyed: false,
1684
2541
  label: 'Button',
1685
2542
  group: 'action',
1686
2543
  create: (options = {}) => ({
@@ -1690,6 +2547,9 @@ Button.config = {
1690
2547
  };
1691
2548
 
1692
2549
  const FormRenderContext = createContext({
2550
+ EmptyRoot: props => {
2551
+ return null;
2552
+ },
1693
2553
  Empty: props => {
1694
2554
  return null;
1695
2555
  },
@@ -1719,15 +2579,19 @@ const FormRenderContext = createContext({
1719
2579
  class: props.class,
1720
2580
  children: props.children
1721
2581
  });
2582
+ },
2583
+ hoveredId: [],
2584
+ setHoveredId: newValue => {
2585
+ console.log(`setHoveredId not defined, called with '${newValue}'`);
1722
2586
  }
1723
2587
  });
1724
2588
  var FormRenderContext$1 = FormRenderContext;
1725
2589
 
1726
- /**
1727
- * @param {string} type
1728
- * @param {boolean} [strict]
1729
- *
1730
- * @returns {any}
2590
+ /**
2591
+ * @param {string} type
2592
+ * @param {boolean} [strict]
2593
+ *
2594
+ * @returns {any}
1731
2595
  */
1732
2596
  function getService(type, strict) {}
1733
2597
  const FormContext = createContext({
@@ -1743,10 +2607,10 @@ function useService(type, strict) {
1743
2607
  return getService(type, strict);
1744
2608
  }
1745
2609
 
1746
- /**
1747
- * Returns the conditionally filtered data of a form reactively.
1748
- * Memoised to minimize re-renders
1749
- *
2610
+ /**
2611
+ * Returns the conditionally filtered data of a form reactively.
2612
+ * Memoised to minimize re-renders
2613
+ *
1750
2614
  */
1751
2615
  function useFilteredFormData() {
1752
2616
  const {
@@ -1763,12 +2627,12 @@ function useFilteredFormData() {
1763
2627
  }, [conditionChecker, data, initialData]);
1764
2628
  }
1765
2629
 
1766
- /**
1767
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
1768
- *
1769
- * @param {string | undefined} condition
1770
- *
1771
- * @returns {boolean} true if condition is met or no condition or condition checker exists
2630
+ /**
2631
+ * Evaluate if condition is met reactively based on the conditionChecker and form data.
2632
+ *
2633
+ * @param {string | undefined} condition
2634
+ *
2635
+ * @returns {boolean} true if condition is met or no condition or condition checker exists
1772
2636
  */
1773
2637
  function useCondition(condition) {
1774
2638
  const conditionChecker = useService('conditionChecker', false);
@@ -1778,13 +2642,13 @@ function useCondition(condition) {
1778
2642
  }, [conditionChecker, condition, filteredData]);
1779
2643
  }
1780
2644
 
1781
- /**
1782
- * Evaluate a string reactively based on the expressionLanguage and form data.
1783
- * If the string is not an expression, it is returned as is.
1784
- * Memoised to minimize re-renders.
1785
- *
1786
- * @param {string} value
1787
- *
2645
+ /**
2646
+ * Evaluate a string reactively based on the expressionLanguage and form data.
2647
+ * If the string is not an expression, it is returned as is.
2648
+ * Memoised to minimize re-renders.
2649
+ *
2650
+ * @param {string} value
2651
+ *
1788
2652
  */
1789
2653
  function useExpressionEvaluation(value) {
1790
2654
  const formData = useFilteredFormData();
@@ -1813,16 +2677,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
1813
2677
  });
1814
2678
  }
1815
2679
 
1816
- /**
1817
- * Retrieve readonly value of a form field, given it can be an
1818
- * expression optionally or configured globally.
1819
- *
1820
- * @typedef { import('../../types').FormProperties } FormProperties
1821
- *
1822
- * @param {any} formField
1823
- * @param {FormProperties} properties
1824
- *
1825
- * @returns {boolean}
2680
+ /**
2681
+ * Retrieve readonly value of a form field, given it can be an
2682
+ * expression optionally or configured globally.
2683
+ *
2684
+ * @typedef { import('../../types').FormProperties } FormProperties
2685
+ *
2686
+ * @param {any} formField
2687
+ * @param {FormProperties} properties
2688
+ *
2689
+ * @returns {boolean}
1826
2690
  */
1827
2691
  function useReadonly(formField, properties = {}) {
1828
2692
  const expressionLanguage = useService('expressionLanguage');
@@ -1840,16 +2704,16 @@ function useReadonly(formField, properties = {}) {
1840
2704
  return readonly || false;
1841
2705
  }
1842
2706
 
1843
- /**
1844
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1845
- * Memoised to minimize re-renders
1846
- *
1847
- * @param {string} value
1848
- * @param {Object} options
1849
- * @param {boolean} [options.debug = false]
1850
- * @param {boolean} [options.strict = false]
1851
- * @param {Function} [options.buildDebugString]
1852
- *
2707
+ /**
2708
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2709
+ * Memoised to minimize re-renders
2710
+ *
2711
+ * @param {string} value
2712
+ * @param {Object} options
2713
+ * @param {boolean} [options.debug = false]
2714
+ * @param {boolean} [options.strict = false]
2715
+ * @param {Function} [options.buildDebugString]
2716
+ *
1853
2717
  */
1854
2718
  function useTemplateEvaluation(value, options) {
1855
2719
  const filteredData = useFilteredFormData();
@@ -1862,17 +2726,17 @@ function useTemplateEvaluation(value, options) {
1862
2726
  }, [filteredData, templating, value, options]);
1863
2727
  }
1864
2728
 
1865
- /**
1866
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1867
- * If the string contains multiple lines, only the first line is returned.
1868
- * Memoised to minimize re-renders
1869
- *
1870
- * @param {string} value
1871
- * @param {Object} [options]
1872
- * @param {boolean} [options.debug = false]
1873
- * @param {boolean} [options.strict = false]
1874
- * @param {Function} [options.buildDebugString]
1875
- *
2729
+ /**
2730
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2731
+ * If the string contains multiple lines, only the first line is returned.
2732
+ * Memoised to minimize re-renders
2733
+ *
2734
+ * @param {string} value
2735
+ * @param {Object} [options]
2736
+ * @param {boolean} [options.debug = false]
2737
+ * @param {boolean} [options.strict = false]
2738
+ * @param {Function} [options.buildDebugString]
2739
+ *
1876
2740
  */
1877
2741
  function useSingleLineTemplateEvaluation(value, options = {}) {
1878
2742
  const evaluatedTemplate = useTemplateEvaluation(value, options);
@@ -1939,11 +2803,12 @@ function Label(props) {
1939
2803
  });
1940
2804
  }
1941
2805
 
1942
- const type$a = 'checkbox';
2806
+ const type$b = 'checkbox';
1943
2807
  function Checkbox(props) {
1944
2808
  const {
1945
2809
  disabled,
1946
2810
  errors = [],
2811
+ onBlur,
1947
2812
  field,
1948
2813
  readonly,
1949
2814
  value = false
@@ -1970,7 +2835,7 @@ function Checkbox(props) {
1970
2835
  } = useContext(FormContext$1);
1971
2836
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
1972
2837
  return jsxs("div", {
1973
- class: classNames(formFieldClasses(type$a, {
2838
+ class: classNames(formFieldClasses(type$b, {
1974
2839
  errors,
1975
2840
  disabled,
1976
2841
  readonly
@@ -1989,6 +2854,7 @@ function Checkbox(props) {
1989
2854
  id: prefixId(id, formId),
1990
2855
  type: "checkbox",
1991
2856
  onChange: onChange,
2857
+ onBlur: onBlur,
1992
2858
  "aria-describedby": errorMessageId
1993
2859
  })
1994
2860
  }), jsx(Description, {
@@ -2000,7 +2866,7 @@ function Checkbox(props) {
2000
2866
  });
2001
2867
  }
2002
2868
  Checkbox.config = {
2003
- type: type$a,
2869
+ type: type$b,
2004
2870
  keyed: true,
2005
2871
  label: 'Checkbox',
2006
2872
  group: 'selection',
@@ -2058,9 +2924,24 @@ function _isReadableType(value) {
2058
2924
  function _isValueSomething(value) {
2059
2925
  return value || value === 0 || value === false;
2060
2926
  }
2927
+ function createEmptyOptions(options = {}) {
2928
+ const defaults = {};
2061
2929
 
2062
- /**
2063
- * @enum { String }
2930
+ // provide default values if valuesKey and valuesExpression are not set
2931
+ if (!options.valuesKey && !options.valuesExpression) {
2932
+ defaults.values = [{
2933
+ label: 'Value',
2934
+ value: 'value'
2935
+ }];
2936
+ }
2937
+ return {
2938
+ ...defaults,
2939
+ ...options
2940
+ };
2941
+ }
2942
+
2943
+ /**
2944
+ * @enum { String }
2064
2945
  */
2065
2946
  const LOAD_STATES = {
2066
2947
  LOADING: 'loading',
@@ -2068,17 +2949,17 @@ const LOAD_STATES = {
2068
2949
  ERROR: 'error'
2069
2950
  };
2070
2951
 
2071
- /**
2072
- * @typedef {Object} ValuesGetter
2073
- * @property {Object[]} values - The values data
2074
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2952
+ /**
2953
+ * @typedef {Object} ValuesGetter
2954
+ * @property {Object[]} values - The values data
2955
+ * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2075
2956
  */
2076
2957
 
2077
- /**
2078
- * A hook to load values for single and multiselect components.
2079
- *
2080
- * @param {Object} field - The form field to handle values for
2081
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2958
+ /**
2959
+ * A hook to load values for single and multiselect components.
2960
+ *
2961
+ * @param {Object} field - The form field to handle values for
2962
+ * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2082
2963
  */
2083
2964
  function useValuesAsync (field) {
2084
2965
  const {
@@ -2343,11 +3224,12 @@ function sanitizeMultiSelectValue(options) {
2343
3224
  }
2344
3225
  }
2345
3226
 
2346
- const type$9 = 'checklist';
3227
+ const type$a = 'checklist';
2347
3228
  function Checklist(props) {
2348
3229
  const {
2349
3230
  disabled,
2350
3231
  errors = [],
3232
+ onBlur,
2351
3233
  field,
2352
3234
  readonly,
2353
3235
  value = []
@@ -2358,6 +3240,7 @@ function Checklist(props) {
2358
3240
  label,
2359
3241
  validate = {}
2360
3242
  } = field;
3243
+ const outerDivRef = useRef();
2361
3244
  const {
2362
3245
  required
2363
3246
  } = validate;
@@ -2373,6 +3256,12 @@ function Checklist(props) {
2373
3256
  value: newValue
2374
3257
  });
2375
3258
  };
3259
+ const onCheckboxBlur = e => {
3260
+ if (outerDivRef.current.contains(e.relatedTarget)) {
3261
+ return;
3262
+ }
3263
+ onBlur();
3264
+ };
2376
3265
  const {
2377
3266
  state: loadState,
2378
3267
  values: options
@@ -2382,11 +3271,12 @@ function Checklist(props) {
2382
3271
  } = useContext(FormContext$1);
2383
3272
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
2384
3273
  return jsxs("div", {
2385
- class: classNames(formFieldClasses(type$9, {
3274
+ class: classNames(formFieldClasses(type$a, {
2386
3275
  errors,
2387
3276
  disabled,
2388
3277
  readonly
2389
3278
  })),
3279
+ ref: outerDivRef,
2390
3280
  children: [jsx(Label, {
2391
3281
  label: label,
2392
3282
  required: required
@@ -2406,6 +3296,7 @@ function Checklist(props) {
2406
3296
  id: prefixId(`${id}-${index}`, formId),
2407
3297
  type: "checkbox",
2408
3298
  onClick: () => toggleCheckbox(v.value),
3299
+ onBlur: onCheckboxBlur,
2409
3300
  "aria-describedby": errorMessageId
2410
3301
  })
2411
3302
  }, `${id}-${index}`);
@@ -2418,27 +3309,13 @@ function Checklist(props) {
2418
3309
  });
2419
3310
  }
2420
3311
  Checklist.config = {
2421
- type: type$9,
3312
+ type: type$a,
2422
3313
  keyed: true,
2423
3314
  label: 'Checklist',
2424
3315
  group: 'selection',
2425
3316
  emptyValue: [],
2426
3317
  sanitizeValue: sanitizeMultiSelectValue,
2427
- create: (options = {}) => {
2428
- const defaults = {};
2429
-
2430
- // provide default values if valuesKey isn't set
2431
- if (!options.valuesKey) {
2432
- defaults.values = [{
2433
- label: 'Value',
2434
- value: 'value'
2435
- }];
2436
- }
2437
- return {
2438
- ...defaults,
2439
- ...options
2440
- };
2441
- }
3318
+ create: createEmptyOptions
2442
3319
  };
2443
3320
 
2444
3321
  const noop$1 = () => false;
@@ -2448,8 +3325,11 @@ function FormField(props) {
2448
3325
  onChange
2449
3326
  } = props;
2450
3327
  const formFields = useService('formFields'),
3328
+ viewerCommands = useService('viewerCommands', false),
3329
+ pathRegistry = useService('pathRegistry'),
2451
3330
  form = useService('form');
2452
3331
  const {
3332
+ initialData,
2453
3333
  data,
2454
3334
  errors,
2455
3335
  properties
@@ -2463,12 +3343,23 @@ function FormField(props) {
2463
3343
  if (!FormFieldComponent) {
2464
3344
  throw new Error(`cannot render field <${field.type}>`);
2465
3345
  }
2466
- const value = get(data, field._path);
2467
- const fieldErrors = findErrors(errors, field._path);
3346
+ const valuePath = useMemo(() => pathRegistry.getValuePath(field), [field, pathRegistry]);
3347
+ const initialValue = useMemo(() => get(initialData, valuePath), [initialData, valuePath]);
2468
3348
  const readonly = useReadonly(field, properties);
3349
+ const value = get(data, valuePath);
2469
3350
 
2470
3351
  // add precedence: global readonly > form field disabled
2471
3352
  const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
3353
+ const onBlur = useCallback(() => {
3354
+ if (viewerCommands) {
3355
+ viewerCommands.updateFieldValidation(field, value);
3356
+ }
3357
+ }, [viewerCommands, field, value]);
3358
+ useEffect(() => {
3359
+ if (viewerCommands && initialValue) {
3360
+ viewerCommands.updateFieldValidation(field, initialValue);
3361
+ }
3362
+ }, [viewerCommands, field, initialValue]);
2472
3363
  const hidden = useCondition(field.conditional && field.conditional.hide || null);
2473
3364
  if (hidden) {
2474
3365
  return jsx(Empty, {});
@@ -2482,8 +3373,9 @@ function FormField(props) {
2482
3373
  children: jsx(FormFieldComponent, {
2483
3374
  ...props,
2484
3375
  disabled: disabled,
2485
- errors: fieldErrors,
3376
+ errors: errors[field.id],
2486
3377
  onChange: disabled || readonly ? noop$1 : onChange,
3378
+ onBlur: disabled || readonly ? noop$1 : onBlur,
2487
3379
  readonly: readonly,
2488
3380
  value: value
2489
3381
  })
@@ -2491,14 +3383,14 @@ function FormField(props) {
2491
3383
  });
2492
3384
  }
2493
3385
 
2494
- function Default(props) {
3386
+ function Grid(props) {
2495
3387
  const {
2496
3388
  Children,
2497
- Empty,
2498
3389
  Row
2499
3390
  } = useContext(FormRenderContext$1);
2500
3391
  const {
2501
- field
3392
+ field,
3393
+ Empty
2502
3394
  } = props;
2503
3395
  const {
2504
3396
  id,
@@ -2535,7 +3427,20 @@ function Default(props) {
2535
3427
  }), components.length ? null : jsx(Empty, {})]
2536
3428
  });
2537
3429
  }
2538
- Default.config = {
3430
+
3431
+ function FormComponent$1(props) {
3432
+ const {
3433
+ EmptyRoot
3434
+ } = useContext(FormRenderContext$1);
3435
+ const fullProps = {
3436
+ ...props,
3437
+ Empty: EmptyRoot
3438
+ };
3439
+ return jsx(Grid, {
3440
+ ...fullProps
3441
+ });
3442
+ }
3443
+ FormComponent$1.config = {
2539
3444
  type: 'default',
2540
3445
  keyed: false,
2541
3446
  label: null,
@@ -2546,16 +3451,16 @@ Default.config = {
2546
3451
  })
2547
3452
  };
2548
3453
 
2549
- var _path$g;
2550
- 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); }
3454
+ var _path$h;
3455
+ 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); }
2551
3456
  var SvgCalendar = function SvgCalendar(props) {
2552
- return /*#__PURE__*/React.createElement("svg", _extends$j({
3457
+ return /*#__PURE__*/React.createElement("svg", _extends$k({
2553
3458
  xmlns: "http://www.w3.org/2000/svg",
2554
3459
  width: 14,
2555
3460
  height: 15,
2556
3461
  fill: "none",
2557
3462
  viewBox: "0 0 28 30"
2558
- }, props), _path$g || (_path$g = /*#__PURE__*/React.createElement("path", {
3463
+ }, props), _path$h || (_path$h = /*#__PURE__*/React.createElement("path", {
2559
3464
  fill: "currentColor",
2560
3465
  fillRule: "evenodd",
2561
3466
  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",
@@ -2564,6 +3469,74 @@ var SvgCalendar = function SvgCalendar(props) {
2564
3469
  };
2565
3470
  var CalendarIcon = SvgCalendar;
2566
3471
 
3472
+ /**
3473
+ * Returns date format for the provided locale.
3474
+ * If the locale is not provided, uses the browser's locale.
3475
+ *
3476
+ * @param {string} [locale] - The locale to get date format for.
3477
+ * @returns {string} The date format for the locale.
3478
+ */
3479
+ function getLocaleDateFormat(locale = 'default') {
3480
+ const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(Date.UTC(2020, 5, 5)));
3481
+ return parts.map(part => {
3482
+ const len = part.value.length;
3483
+ switch (part.type) {
3484
+ case 'day':
3485
+ return 'd'.repeat(len);
3486
+ case 'month':
3487
+ return 'M'.repeat(len);
3488
+ case 'year':
3489
+ return 'y'.repeat(len);
3490
+ default:
3491
+ return part.value;
3492
+ }
3493
+ }).join('');
3494
+ }
3495
+
3496
+ /**
3497
+ * Returns readable date format for the provided locale.
3498
+ * If the locale is not provided, uses the browser's locale.
3499
+ *
3500
+ * @param {string} [locale] - The locale to get readable date format for.
3501
+ * @returns {string} The readable date format for the locale.
3502
+ */
3503
+ function getLocaleReadableDateFormat(locale) {
3504
+ let format = getLocaleDateFormat(locale).toLowerCase();
3505
+
3506
+ // Ensure month is in 'mm' format
3507
+ if (!format.includes('mm')) {
3508
+ format = format.replace('m', 'mm');
3509
+ }
3510
+
3511
+ // Ensure day is in 'dd' format
3512
+ if (!format.includes('dd')) {
3513
+ format = format.replace('d', 'dd');
3514
+ }
3515
+ return format;
3516
+ }
3517
+
3518
+ /**
3519
+ * Returns flatpickr config for the provided locale.
3520
+ * If the locale is not provided, uses the browser's locale.
3521
+ *
3522
+ * @param {string} [locale] - The locale to get flatpickr config for.
3523
+ * @returns {object} The flatpickr config for the locale.
3524
+ */
3525
+ function getLocaleDateFlatpickrConfig(locale) {
3526
+ return flatpickerizeDateFormat(getLocaleDateFormat(locale));
3527
+ }
3528
+ function flatpickerizeDateFormat(dateFormat) {
3529
+ const useLeadingZero = {
3530
+ day: dateFormat.includes('dd'),
3531
+ month: dateFormat.includes('MM'),
3532
+ year: dateFormat.includes('yyyy')
3533
+ };
3534
+ dateFormat = useLeadingZero.day ? dateFormat.replace('dd', 'd') : dateFormat.replace('d', 'j');
3535
+ dateFormat = useLeadingZero.month ? dateFormat.replace('MM', 'm') : dateFormat.replace('M', 'n');
3536
+ dateFormat = useLeadingZero.year ? dateFormat.replace('yyyy', 'Y') : dateFormat.replace('yy', 'y');
3537
+ return dateFormat;
3538
+ }
3539
+
2567
3540
  function InputAdorner(props) {
2568
3541
  const {
2569
3542
  pre,
@@ -2607,6 +3580,7 @@ function Datepicker(props) {
2607
3580
  id,
2608
3581
  label,
2609
3582
  collapseLabelOnEmpty,
3583
+ onDateTimeBlur,
2610
3584
  formId,
2611
3585
  required,
2612
3586
  disabled,
@@ -2637,7 +3611,7 @@ function Datepicker(props) {
2637
3611
  useEffect(() => {
2638
3612
  let config = {
2639
3613
  allowInput: true,
2640
- dateFormat: 'm/d/Y',
3614
+ dateFormat: getLocaleDateFlatpickrConfig(),
2641
3615
  static: true,
2642
3616
  clickOpens: false,
2643
3617
  // TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
@@ -2701,7 +3675,8 @@ function Datepicker(props) {
2701
3675
  if (!isInputDirty || e.relatedTarget && e.relatedTarget.classList.contains('flatpickr-day')) return;
2702
3676
  dateInputRef.current.dispatchEvent(ENTER_KEYDOWN_EVENT);
2703
3677
  setIsInputDirty(false);
2704
- }, [isInputDirty]);
3678
+ onDateTimeBlur(e);
3679
+ }, [isInputDirty, onDateTimeBlur]);
2705
3680
  const fullId = `${prefixId(id, formId)}--date`;
2706
3681
  return jsxs("div", {
2707
3682
  class: "fjs-datetime-subsection",
@@ -2728,7 +3703,7 @@ function Datepicker(props) {
2728
3703
  class: "fjs-input",
2729
3704
  disabled: disabled,
2730
3705
  readOnly: readonly,
2731
- placeholder: "mm/dd/yyyy",
3706
+ placeholder: getLocaleReadableDateFormat(),
2732
3707
  autoComplete: "off",
2733
3708
  onFocus: onInputFocus,
2734
3709
  onKeyDown: onInputKeyDown,
@@ -2743,19 +3718,19 @@ function Datepicker(props) {
2743
3718
  });
2744
3719
  }
2745
3720
 
2746
- var _path$f, _path2$3;
2747
- 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); }
3721
+ var _path$g, _path2$4;
3722
+ 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); }
2748
3723
  var SvgClock = function SvgClock(props) {
2749
- return /*#__PURE__*/React.createElement("svg", _extends$i({
3724
+ return /*#__PURE__*/React.createElement("svg", _extends$j({
2750
3725
  xmlns: "http://www.w3.org/2000/svg",
2751
3726
  width: 16,
2752
3727
  height: 16,
2753
3728
  fill: "none",
2754
3729
  viewBox: "0 0 28 29"
2755
- }, props), _path$f || (_path$f = /*#__PURE__*/React.createElement("path", {
3730
+ }, props), _path$g || (_path$g = /*#__PURE__*/React.createElement("path", {
2756
3731
  fill: "currentColor",
2757
3732
  d: "M13 14.41 18.59 20 20 18.59l-5-5.01V5h-2v9.41Z"
2758
- })), _path2$3 || (_path2$3 = /*#__PURE__*/React.createElement("path", {
3733
+ })), _path2$4 || (_path2$4 = /*#__PURE__*/React.createElement("path", {
2759
3734
  fill: "currentColor",
2760
3735
  fillRule: "evenodd",
2761
3736
  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",
@@ -2864,6 +3839,7 @@ function Timepicker(props) {
2864
3839
  id,
2865
3840
  label,
2866
3841
  collapseLabelOnEmpty,
3842
+ onDateTimeBlur,
2867
3843
  formId,
2868
3844
  required,
2869
3845
  disabled,
@@ -2963,6 +3939,7 @@ function Timepicker(props) {
2963
3939
  const onInputBlur = e => {
2964
3940
  setDropdownIsOpen(false);
2965
3941
  propagateRawToMinute();
3942
+ onDateTimeBlur(e);
2966
3943
  };
2967
3944
  const onDropdownValueSelected = value => {
2968
3945
  setDropdownIsOpen(false);
@@ -3018,11 +3995,12 @@ function Timepicker(props) {
3018
3995
  });
3019
3996
  }
3020
3997
 
3021
- const type$8 = 'datetime';
3998
+ const type$9 = 'datetime';
3022
3999
  function Datetime(props) {
3023
4000
  const {
3024
4001
  disabled,
3025
4002
  errors = [],
4003
+ onBlur,
3026
4004
  field,
3027
4005
  onChange,
3028
4006
  readonly,
@@ -3046,6 +4024,7 @@ function Datetime(props) {
3046
4024
  const {
3047
4025
  formId
3048
4026
  } = useContext(FormContext$1);
4027
+ const dateTimeGroupRef = useRef();
3049
4028
  const getNullDateTime = () => ({
3050
4029
  date: new Date(Date.parse(null)),
3051
4030
  time: null
@@ -3056,6 +4035,12 @@ function Datetime(props) {
3056
4035
  const isValidTime = time => !isNaN(parseInt(time));
3057
4036
  const useDatePicker = useMemo(() => subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
3058
4037
  const useTimePicker = useMemo(() => subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
4038
+ const onDateTimeBlur = useCallback(e => {
4039
+ if (e.relatedTarget && dateTimeGroupRef.current.contains(e.relatedTarget)) {
4040
+ return;
4041
+ }
4042
+ onBlur();
4043
+ }, [onBlur]);
3059
4044
  useEffect(() => {
3060
4045
  let {
3061
4046
  date,
@@ -3147,6 +4132,7 @@ function Datetime(props) {
3147
4132
  id,
3148
4133
  label: dateLabel,
3149
4134
  collapseLabelOnEmpty: !timeLabel,
4135
+ onDateTimeBlur,
3150
4136
  formId,
3151
4137
  required,
3152
4138
  disabled,
@@ -3160,6 +4146,7 @@ function Datetime(props) {
3160
4146
  id,
3161
4147
  label: timeLabel,
3162
4148
  collapseLabelOnEmpty: !dateLabel,
4149
+ onDateTimeBlur,
3163
4150
  formId,
3164
4151
  required,
3165
4152
  disabled,
@@ -3171,13 +4158,14 @@ function Datetime(props) {
3171
4158
  'aria-describedby': errorMessageId
3172
4159
  };
3173
4160
  return jsxs("div", {
3174
- class: formFieldClasses(type$8, {
4161
+ class: formFieldClasses(type$9, {
3175
4162
  errors: allErrors,
3176
4163
  disabled,
3177
4164
  readonly
3178
4165
  }),
3179
4166
  children: [jsxs("div", {
3180
4167
  class: classNames('fjs-vertical-group'),
4168
+ ref: dateTimeGroupRef,
3181
4169
  children: [useDatePicker && jsx(Datepicker, {
3182
4170
  ...datePickerProps
3183
4171
  }), useTimePicker && useDatePicker && jsx("div", {
@@ -3194,7 +4182,7 @@ function Datetime(props) {
3194
4182
  });
3195
4183
  }
3196
4184
  Datetime.config = {
3197
- type: type$8,
4185
+ type: type$9,
3198
4186
  keyed: true,
3199
4187
  label: 'Date time',
3200
4188
  group: 'basic-input',
@@ -3211,10 +4199,10 @@ Datetime.config = {
3211
4199
  }
3212
4200
  };
3213
4201
 
3214
- /**
3215
- * This file must not be changed or exchanged.
3216
- *
3217
- * @see http://bpmn.io/license for more information.
4202
+ /**
4203
+ * This file must not be changed or exchanged.
4204
+ *
4205
+ * @see http://bpmn.io/license for more information.
3218
4206
  */
3219
4207
  function Logo() {
3220
4208
  return jsxs("svg", {
@@ -3336,6 +4324,52 @@ function FormComponent(props) {
3336
4324
  });
3337
4325
  }
3338
4326
 
4327
+ function Group(props) {
4328
+ const {
4329
+ field
4330
+ } = props;
4331
+ const {
4332
+ label,
4333
+ id,
4334
+ type,
4335
+ showOutline
4336
+ } = field;
4337
+ const {
4338
+ formId
4339
+ } = useContext(FormContext$1);
4340
+ const {
4341
+ Empty
4342
+ } = useContext(FormRenderContext$1);
4343
+ const fullProps = {
4344
+ ...props,
4345
+ Empty
4346
+ };
4347
+ return jsxs("div", {
4348
+ className: classNames(formFieldClasses(type), {
4349
+ 'fjs-outlined': showOutline
4350
+ }),
4351
+ role: "group",
4352
+ "aria-labelledby": prefixId(id, formId),
4353
+ children: [jsx(Label, {
4354
+ id: prefixId(id, formId),
4355
+ label: label
4356
+ }), jsx(Grid, {
4357
+ ...fullProps
4358
+ })]
4359
+ });
4360
+ }
4361
+ Group.config = {
4362
+ type: 'group',
4363
+ pathed: true,
4364
+ label: 'Group',
4365
+ group: 'presentation',
4366
+ create: (options = {}) => ({
4367
+ components: [],
4368
+ showOutline: true,
4369
+ ...options
4370
+ })
4371
+ };
4372
+
3339
4373
  const NODE_TYPE_TEXT = 3,
3340
4374
  NODE_TYPE_ELEMENT = 1;
3341
4375
  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'];
@@ -3346,11 +4380,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
3346
4380
 
3347
4381
  const FORM_ELEMENT = document.createElement('form');
3348
4382
 
3349
- /**
3350
- * Sanitize a HTML string and return the cleaned, safe version.
3351
- *
3352
- * @param {string} html
3353
- * @return {string}
4383
+ /**
4384
+ * Sanitize a HTML string and return the cleaned, safe version.
4385
+ *
4386
+ * @param {string} html
4387
+ * @return {string}
3354
4388
  */
3355
4389
 
3356
4390
  // see https://github.com/developit/snarkdown/issues/70
@@ -3368,29 +4402,29 @@ function sanitizeHTML(html) {
3368
4402
  }
3369
4403
  }
3370
4404
 
3371
- /**
3372
- * Sanitizes an image source to ensure we only allow for data URI and links
3373
- * that start with http(s).
3374
- *
3375
- * Note: Most browsers anyway do not support script execution in <img> elements.
3376
- *
3377
- * @param {string} src
3378
- * @returns {string}
4405
+ /**
4406
+ * Sanitizes an image source to ensure we only allow for data URI and links
4407
+ * that start with http(s).
4408
+ *
4409
+ * Note: Most browsers anyway do not support script execution in <img> elements.
4410
+ *
4411
+ * @param {string} src
4412
+ * @returns {string}
3379
4413
  */
3380
4414
  function sanitizeImageSource(src) {
3381
4415
  const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
3382
4416
  return valid ? src : '';
3383
4417
  }
3384
4418
 
3385
- /**
3386
- * Recursively sanitize a HTML node, potentially
3387
- * removing it, its children or attributes.
3388
- *
3389
- * Inspired by https://github.com/developit/snarkdown/issues/70
3390
- * and https://github.com/cure53/DOMPurify. Simplified
3391
- * for our use-case.
3392
- *
3393
- * @param {Element} node
4419
+ /**
4420
+ * Recursively sanitize a HTML node, potentially
4421
+ * removing it, its children or attributes.
4422
+ *
4423
+ * Inspired by https://github.com/developit/snarkdown/issues/70
4424
+ * and https://github.com/cure53/DOMPurify. Simplified
4425
+ * for our use-case.
4426
+ *
4427
+ * @param {Element} node
3394
4428
  */
3395
4429
  function sanitizeNode(node) {
3396
4430
  // allow text nodes
@@ -3434,13 +4468,13 @@ function sanitizeNode(node) {
3434
4468
  }
3435
4469
  }
3436
4470
 
3437
- /**
3438
- * Validates attributes for validity.
3439
- *
3440
- * @param {string} lcTag
3441
- * @param {string} lcName
3442
- * @param {string} value
3443
- * @return {boolean}
4471
+ /**
4472
+ * Validates attributes for validity.
4473
+ *
4474
+ * @param {string} lcTag
4475
+ * @param {string} lcName
4476
+ * @param {string} value
4477
+ * @return {boolean}
3444
4478
  */
3445
4479
  function isValidAttribute(lcTag, lcName, value) {
3446
4480
  // disallow most attributes based on whitelist
@@ -3463,9 +4497,9 @@ function isValidAttribute(lcTag, lcName, value) {
3463
4497
  return true;
3464
4498
  }
3465
4499
 
3466
- 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); }
4500
+ 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); }
3467
4501
  var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
3468
- return /*#__PURE__*/React.createElement("svg", _extends$h({
4502
+ return /*#__PURE__*/React.createElement("svg", _extends$i({
3469
4503
  xmlns: "http://www.w3.org/2000/svg",
3470
4504
  xmlSpace: "preserve",
3471
4505
  width: 64,
@@ -3504,7 +4538,7 @@ var SvgImagePlaceholder = function SvgImagePlaceholder(props) {
3504
4538
  };
3505
4539
  var ImagePlaceholder = SvgImagePlaceholder;
3506
4540
 
3507
- const type$7 = 'image';
4541
+ const type$8 = 'image';
3508
4542
  function Image(props) {
3509
4543
  const {
3510
4544
  field
@@ -3525,7 +4559,7 @@ function Image(props) {
3525
4559
  formId
3526
4560
  } = useContext(FormContext$1);
3527
4561
  return jsx("div", {
3528
- class: formFieldClasses(type$7),
4562
+ class: formFieldClasses(type$8),
3529
4563
  children: jsxs("div", {
3530
4564
  class: "fjs-image-container",
3531
4565
  children: [safeSource && jsx("img", {
@@ -3543,7 +4577,7 @@ function Image(props) {
3543
4577
  });
3544
4578
  }
3545
4579
  Image.config = {
3546
- type: type$7,
4580
+ type: type$8,
3547
4581
  keyed: false,
3548
4582
  label: 'Image view',
3549
4583
  group: 'presentation',
@@ -3570,14 +4604,14 @@ function TemplatedInputAdorner(props) {
3570
4604
  });
3571
4605
  }
3572
4606
 
3573
- var _path$e;
3574
- 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); }
4607
+ var _path$f;
4608
+ 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); }
3575
4609
  var SvgAngelDown = function SvgAngelDown(props) {
3576
- return /*#__PURE__*/React.createElement("svg", _extends$g({
4610
+ return /*#__PURE__*/React.createElement("svg", _extends$h({
3577
4611
  xmlns: "http://www.w3.org/2000/svg",
3578
4612
  width: 8,
3579
4613
  height: 8
3580
- }, props), _path$e || (_path$e = /*#__PURE__*/React.createElement("path", {
4614
+ }, props), _path$f || (_path$f = /*#__PURE__*/React.createElement("path", {
3581
4615
  fill: "currentColor",
3582
4616
  fillRule: "evenodd",
3583
4617
  stroke: "currentColor",
@@ -3588,14 +4622,14 @@ var SvgAngelDown = function SvgAngelDown(props) {
3588
4622
  };
3589
4623
  var AngelDownIcon = SvgAngelDown;
3590
4624
 
3591
- var _path$d;
3592
- 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); }
4625
+ var _path$e;
4626
+ 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); }
3593
4627
  var SvgAngelUp = function SvgAngelUp(props) {
3594
- return /*#__PURE__*/React.createElement("svg", _extends$f({
4628
+ return /*#__PURE__*/React.createElement("svg", _extends$g({
3595
4629
  xmlns: "http://www.w3.org/2000/svg",
3596
4630
  width: 8,
3597
4631
  height: 8
3598
- }, props), _path$d || (_path$d = /*#__PURE__*/React.createElement("path", {
4632
+ }, props), _path$e || (_path$e = /*#__PURE__*/React.createElement("path", {
3599
4633
  fill: "currentColor",
3600
4634
  fillRule: "evenodd",
3601
4635
  stroke: "currentColor",
@@ -3606,11 +4640,12 @@ var SvgAngelUp = function SvgAngelUp(props) {
3606
4640
  };
3607
4641
  var AngelUpIcon = SvgAngelUp;
3608
4642
 
3609
- const type$6 = 'number';
4643
+ const type$7 = 'number';
3610
4644
  function Numberfield(props) {
3611
4645
  const {
3612
4646
  disabled,
3613
4647
  errors = [],
4648
+ onBlur,
3614
4649
  field,
3615
4650
  value,
3616
4651
  readonly,
@@ -3743,7 +4778,7 @@ function Numberfield(props) {
3743
4778
  } = useContext(FormContext$1);
3744
4779
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3745
4780
  return jsxs("div", {
3746
- class: formFieldClasses(type$6, {
4781
+ class: formFieldClasses(type$7, {
3747
4782
  errors,
3748
4783
  disabled,
3749
4784
  readonly
@@ -3771,7 +4806,8 @@ function Numberfield(props) {
3771
4806
  readOnly: readonly,
3772
4807
  id: prefixId(id, formId),
3773
4808
  onKeyDown: onKeyDown,
3774
- onKeyPress: onKeyPress
4809
+ onKeyPress: onKeyPress,
4810
+ onBlur: onBlur
3775
4811
 
3776
4812
  // @ts-ignore
3777
4813
  ,
@@ -3814,7 +4850,7 @@ function Numberfield(props) {
3814
4850
  });
3815
4851
  }
3816
4852
  Numberfield.config = {
3817
- type: type$6,
4853
+ type: type$7,
3818
4854
  keyed: true,
3819
4855
  label: 'Number',
3820
4856
  group: 'basic-input',
@@ -3837,11 +4873,12 @@ Numberfield.config = {
3837
4873
  })
3838
4874
  };
3839
4875
 
3840
- const type$5 = 'radio';
4876
+ const type$6 = 'radio';
3841
4877
  function Radio(props) {
3842
4878
  const {
3843
4879
  disabled,
3844
4880
  errors = [],
4881
+ onBlur,
3845
4882
  field,
3846
4883
  readonly,
3847
4884
  value
@@ -3852,6 +4889,7 @@ function Radio(props) {
3852
4889
  label,
3853
4890
  validate = {}
3854
4891
  } = field;
4892
+ const outerDivRef = useRef();
3855
4893
  const {
3856
4894
  required
3857
4895
  } = validate;
@@ -3861,6 +4899,12 @@ function Radio(props) {
3861
4899
  value: v
3862
4900
  });
3863
4901
  };
4902
+ const onRadioBlur = e => {
4903
+ if (outerDivRef.current.contains(e.relatedTarget)) {
4904
+ return;
4905
+ }
4906
+ onBlur();
4907
+ };
3864
4908
  const {
3865
4909
  state: loadState,
3866
4910
  values: options
@@ -3870,11 +4914,12 @@ function Radio(props) {
3870
4914
  } = useContext(FormContext$1);
3871
4915
  const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;
3872
4916
  return jsxs("div", {
3873
- class: formFieldClasses(type$5, {
4917
+ class: formFieldClasses(type$6, {
3874
4918
  errors,
3875
4919
  disabled,
3876
4920
  readonly
3877
4921
  }),
4922
+ ref: outerDivRef,
3878
4923
  children: [jsx(Label, {
3879
4924
  label: label,
3880
4925
  required: required
@@ -3894,6 +4939,7 @@ function Radio(props) {
3894
4939
  id: prefixId(`${id}-${index}`, formId),
3895
4940
  type: "radio",
3896
4941
  onClick: () => onChange(option.value),
4942
+ onBlur: onRadioBlur,
3897
4943
  "aria-describedby": errorMessageId
3898
4944
  })
3899
4945
  }, `${id}-${index}`);
@@ -3906,37 +4952,23 @@ function Radio(props) {
3906
4952
  });
3907
4953
  }
3908
4954
  Radio.config = {
3909
- type: type$5,
4955
+ type: type$6,
3910
4956
  keyed: true,
3911
4957
  label: 'Radio',
3912
4958
  group: 'selection',
3913
4959
  emptyValue: null,
3914
4960
  sanitizeValue: sanitizeSingleSelectValue,
3915
- create: (options = {}) => {
3916
- const defaults = {};
3917
-
3918
- // provide default values if valuesKey isn't set
3919
- if (!options.valuesKey) {
3920
- defaults.values = [{
3921
- label: 'Value',
3922
- value: 'value'
3923
- }];
3924
- }
3925
- return {
3926
- ...defaults,
3927
- ...options
3928
- };
3929
- }
4961
+ create: createEmptyOptions
3930
4962
  };
3931
4963
 
3932
- var _path$c;
3933
- 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); }
4964
+ var _path$d;
4965
+ 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); }
3934
4966
  var SvgXMark = function SvgXMark(props) {
3935
- return /*#__PURE__*/React.createElement("svg", _extends$e({
4967
+ return /*#__PURE__*/React.createElement("svg", _extends$f({
3936
4968
  xmlns: "http://www.w3.org/2000/svg",
3937
4969
  width: 8,
3938
4970
  height: 8
3939
- }, props), _path$c || (_path$c = /*#__PURE__*/React.createElement("path", {
4971
+ }, props), _path$d || (_path$d = /*#__PURE__*/React.createElement("path", {
3940
4972
  fill: "currentColor",
3941
4973
  fillRule: "evenodd",
3942
4974
  stroke: "currentColor",
@@ -3952,6 +4984,7 @@ function SearchableSelect(props) {
3952
4984
  id,
3953
4985
  disabled,
3954
4986
  errors,
4987
+ onBlur,
3955
4988
  field,
3956
4989
  readonly,
3957
4990
  value
@@ -4071,6 +5104,7 @@ function SearchableSelect(props) {
4071
5104
  onBlur: () => {
4072
5105
  setIsDropdownExpanded(false);
4073
5106
  setFilter(valueLabel);
5107
+ onBlur();
4074
5108
  },
4075
5109
  "aria-describedby": props['aria-describedby']
4076
5110
  }), displayState.displayCross && jsxs("span", {
@@ -4105,6 +5139,7 @@ function SimpleSelect(props) {
4105
5139
  id,
4106
5140
  disabled,
4107
5141
  errors,
5142
+ onBlur,
4108
5143
  field,
4109
5144
  readonly,
4110
5145
  value
@@ -4159,7 +5194,10 @@ function SimpleSelect(props) {
4159
5194
  'hasErrors': errors.length
4160
5195
  }),
4161
5196
  onFocus: () => setIsDropdownExpanded(true),
4162
- onBlur: () => setIsDropdownExpanded(false),
5197
+ onBlur: () => {
5198
+ setIsDropdownExpanded(false);
5199
+ onBlur();
5200
+ },
4163
5201
  onMouseDown: onMouseDown,
4164
5202
  children: [jsx("div", {
4165
5203
  class: classNames('fjs-select-display', {
@@ -4201,11 +5239,12 @@ function SimpleSelect(props) {
4201
5239
  });
4202
5240
  }
4203
5241
 
4204
- const type$4 = 'select';
5242
+ const type$5 = 'select';
4205
5243
  function Select(props) {
4206
5244
  const {
4207
5245
  disabled,
4208
5246
  errors = [],
5247
+ onBlur,
4209
5248
  field,
4210
5249
  onChange,
4211
5250
  readonly,
@@ -4229,14 +5268,15 @@ function Select(props) {
4229
5268
  id,
4230
5269
  disabled,
4231
5270
  errors,
5271
+ onBlur,
4232
5272
  field,
4233
5273
  value,
4234
5274
  onChange,
4235
5275
  readonly,
4236
5276
  'aria-describedby': errorMessageId
4237
- }), [disabled, errors, field, id, value, onChange, readonly, errorMessageId]);
5277
+ }), [disabled, errors, field, id, value, onChange, onBlur, readonly, errorMessageId]);
4238
5278
  return jsxs("div", {
4239
- class: formFieldClasses(type$4, {
5279
+ class: formFieldClasses(type$5, {
4240
5280
  errors,
4241
5281
  disabled,
4242
5282
  readonly
@@ -4264,27 +5304,39 @@ function Select(props) {
4264
5304
  });
4265
5305
  }
4266
5306
  Select.config = {
4267
- type: type$4,
5307
+ type: type$5,
4268
5308
  keyed: true,
4269
5309
  label: 'Select',
4270
5310
  group: 'selection',
4271
5311
  emptyValue: null,
4272
5312
  sanitizeValue: sanitizeSingleSelectValue,
4273
- create: (options = {}) => {
4274
- const defaults = {};
5313
+ create: createEmptyOptions
5314
+ };
4275
5315
 
4276
- // provide default values if valuesKey isn't set
4277
- if (!options.valuesKey) {
4278
- defaults.values = [{
4279
- label: 'Value',
4280
- value: 'value'
4281
- }];
5316
+ const type$4 = 'spacer';
5317
+ function Spacer(props) {
5318
+ const {
5319
+ field
5320
+ } = props;
5321
+ const {
5322
+ height = 60
5323
+ } = field;
5324
+ return jsx("div", {
5325
+ class: formFieldClasses(type$4),
5326
+ style: {
5327
+ height: height
4282
5328
  }
4283
- return {
4284
- ...defaults,
4285
- ...options
4286
- };
4287
- }
5329
+ });
5330
+ }
5331
+ Spacer.config = {
5332
+ type: type$4,
5333
+ keyed: false,
5334
+ label: 'Spacer',
5335
+ group: 'presentation',
5336
+ create: (options = {}) => ({
5337
+ height: 60,
5338
+ ...options
5339
+ })
4288
5340
  };
4289
5341
 
4290
5342
  const type$3 = 'taglist';
@@ -4292,6 +5344,7 @@ function Taglist(props) {
4292
5344
  const {
4293
5345
  disabled,
4294
5346
  errors = [],
5347
+ onBlur,
4295
5348
  field,
4296
5349
  readonly,
4297
5350
  value: values = []
@@ -4384,9 +5437,10 @@ function Taglist(props) {
4384
5437
  break;
4385
5438
  }
4386
5439
  };
4387
- const onBlur = () => {
5440
+ const onComponentBlur = () => {
4388
5441
  setIsDropdownExpanded(false);
4389
5442
  setFilter('');
5443
+ onBlur();
4390
5444
  };
4391
5445
  const onTagRemoveClick = (event, value) => {
4392
5446
  const {
@@ -4457,9 +5511,7 @@ function Taglist(props) {
4457
5511
  onKeyDown: onInputKeyDown,
4458
5512
  onMouseDown: () => setIsEscapeClose(false),
4459
5513
  onFocus: () => !readonly && setIsDropdownExpanded(true),
4460
- onBlur: () => {
4461
- !readonly && onBlur();
4462
- },
5514
+ onBlur: () => !readonly && onComponentBlur(),
4463
5515
  "aria-describedby": errorMessageId
4464
5516
  })]
4465
5517
  }), jsx("div", {
@@ -4486,21 +5538,7 @@ Taglist.config = {
4486
5538
  group: 'selection',
4487
5539
  emptyValue: [],
4488
5540
  sanitizeValue: sanitizeMultiSelectValue,
4489
- create: (options = {}) => {
4490
- const defaults = {};
4491
-
4492
- // provide default values if valuesKey isn't set
4493
- if (!options.valuesKey) {
4494
- defaults.values = [{
4495
- label: 'Value',
4496
- value: 'value'
4497
- }];
4498
- }
4499
- return {
4500
- ...defaults,
4501
- ...options
4502
- };
4503
- }
5541
+ create: createEmptyOptions
4504
5542
  };
4505
5543
 
4506
5544
  const type$2 = 'text';
@@ -4592,6 +5630,7 @@ function Textfield(props) {
4592
5630
  const {
4593
5631
  disabled,
4594
5632
  errors = [],
5633
+ onBlur,
4595
5634
  field,
4596
5635
  readonly,
4597
5636
  value = ''
@@ -4643,6 +5682,7 @@ function Textfield(props) {
4643
5682
  readOnly: readonly,
4644
5683
  id: prefixId(id, formId),
4645
5684
  onInput: onChange,
5685
+ onBlur: onBlur,
4646
5686
  type: "text",
4647
5687
  value: value,
4648
5688
  "aria-describedby": errorMessageId
@@ -4684,6 +5724,7 @@ function Textarea(props) {
4684
5724
  const {
4685
5725
  disabled,
4686
5726
  errors = [],
5727
+ onBlur,
4687
5728
  field,
4688
5729
  readonly,
4689
5730
  value = ''
@@ -4706,22 +5747,12 @@ function Textarea(props) {
4706
5747
  value: target.value
4707
5748
  });
4708
5749
  };
4709
- const autoSizeTextarea = useCallback(textarea => {
4710
- // Ensures the textarea shrinks back, and improves resizing behavior consistency
4711
- textarea.style.height = '0px';
4712
- const computed = window.getComputedStyle(textarea);
4713
- 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'));
4714
- const minHeight = 75;
4715
- const maxHeight = 350;
4716
- const displayHeight = Math.max(Math.min(calculatedHeight, maxHeight), minHeight);
4717
- textarea.style.height = `${displayHeight}px`;
4718
-
4719
- // Overflow is hidden by default to hide scrollbar flickering
4720
- textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
4721
- }, []);
5750
+ useLayoutEffect(() => {
5751
+ autoSizeTextarea(textareaRef.current);
5752
+ }, [value]);
4722
5753
  useEffect(() => {
4723
5754
  autoSizeTextarea(textareaRef.current);
4724
- }, [autoSizeTextarea, value]);
5755
+ }, []);
4725
5756
  const {
4726
5757
  formId
4727
5758
  } = useContext(FormContext$1);
@@ -4742,6 +5773,7 @@ function Textarea(props) {
4742
5773
  readonly: readonly,
4743
5774
  id: prefixId(id, formId),
4744
5775
  onInput: onInput,
5776
+ onBlur: onBlur,
4745
5777
  value: value,
4746
5778
  ref: textareaRef,
4747
5779
  "aria-describedby": errorMessageId
@@ -4766,40 +5798,58 @@ Textarea.config = {
4766
5798
  ...options
4767
5799
  })
4768
5800
  };
5801
+ const autoSizeTextarea = textarea => {
5802
+ // Ensures the textarea shrinks back, and improves resizing behavior consistency
5803
+ textarea.style.height = '0px';
5804
+ const computed = window.getComputedStyle(textarea);
5805
+ const heightFromLines = () => {
5806
+ const lineHeight = parseInt(computed.getPropertyValue('line-height').replace('px', '')) || 0;
5807
+ const lines = textarea.value ? textarea.value.toString().split('\n').length : 0;
5808
+ return lines * lineHeight;
5809
+ };
5810
+ 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'));
5811
+ const minHeight = 75;
5812
+ const maxHeight = 350;
5813
+ const displayHeight = Math.max(Math.min(calculatedHeight || 0, maxHeight), minHeight);
5814
+ textarea.style.height = `${displayHeight}px`;
5815
+
5816
+ // Overflow is hidden by default to hide scrollbar flickering
5817
+ textarea.style.overflow = calculatedHeight > maxHeight ? 'visible' : 'hidden';
5818
+ };
4769
5819
 
4770
- var _path$b;
4771
- 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); }
5820
+ var _path$c;
5821
+ 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); }
4772
5822
  var SvgButton = function SvgButton(props) {
4773
- return /*#__PURE__*/React.createElement("svg", _extends$d({
5823
+ return /*#__PURE__*/React.createElement("svg", _extends$e({
4774
5824
  xmlns: "http://www.w3.org/2000/svg",
4775
5825
  width: 54,
4776
5826
  height: 54,
4777
5827
  fill: "currentcolor"
4778
- }, props), _path$b || (_path$b = /*#__PURE__*/React.createElement("path", {
5828
+ }, props), _path$c || (_path$c = /*#__PURE__*/React.createElement("path", {
4779
5829
  fillRule: "evenodd",
4780
5830
  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"
4781
5831
  })));
4782
5832
  };
4783
5833
  var ButtonIcon = SvgButton;
4784
5834
 
4785
- var _path$a;
4786
- 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); }
5835
+ var _path$b;
5836
+ 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); }
4787
5837
  var SvgCheckbox = function SvgCheckbox(props) {
4788
- return /*#__PURE__*/React.createElement("svg", _extends$c({
5838
+ return /*#__PURE__*/React.createElement("svg", _extends$d({
4789
5839
  xmlns: "http://www.w3.org/2000/svg",
4790
5840
  width: 54,
4791
5841
  height: 54,
4792
5842
  fill: "currentcolor"
4793
- }, props), _path$a || (_path$a = /*#__PURE__*/React.createElement("path", {
5843
+ }, props), _path$b || (_path$b = /*#__PURE__*/React.createElement("path", {
4794
5844
  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"
4795
5845
  })));
4796
5846
  };
4797
5847
  var CheckboxIcon = SvgCheckbox;
4798
5848
 
4799
5849
  var _g, _use, _use2, _use3, _defs;
4800
- 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); }
5850
+ 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); }
4801
5851
  var SvgChecklist = function SvgChecklist(props) {
4802
- return /*#__PURE__*/React.createElement("svg", _extends$b({
5852
+ return /*#__PURE__*/React.createElement("svg", _extends$c({
4803
5853
  xmlns: "http://www.w3.org/2000/svg",
4804
5854
  xmlnsXlink: "http://www.w3.org/1999/xlink",
4805
5855
  width: 54,
@@ -4833,18 +5883,18 @@ var SvgChecklist = function SvgChecklist(props) {
4833
5883
  };
4834
5884
  var ChecklistIcon = SvgChecklist;
4835
5885
 
4836
- var _path$9, _path2$2, _path3;
4837
- 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); }
5886
+ var _path$a, _path2$3, _path3;
5887
+ 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); }
4838
5888
  var SvgDatetime = function SvgDatetime(props) {
4839
- return /*#__PURE__*/React.createElement("svg", _extends$a({
5889
+ return /*#__PURE__*/React.createElement("svg", _extends$b({
4840
5890
  xmlns: "http://www.w3.org/2000/svg",
4841
5891
  width: 54,
4842
5892
  height: 54,
4843
5893
  fill: "currentcolor"
4844
- }, props), _path$9 || (_path$9 = /*#__PURE__*/React.createElement("path", {
5894
+ }, props), _path$a || (_path$a = /*#__PURE__*/React.createElement("path", {
4845
5895
  fillRule: "evenodd",
4846
5896
  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"
4847
- })), _path2$2 || (_path2$2 = /*#__PURE__*/React.createElement("path", {
5897
+ })), _path2$3 || (_path2$3 = /*#__PURE__*/React.createElement("path", {
4848
5898
  d: "m35.13 37.603 1.237-1.237-3.468-3.475v-5.926h-1.754v6.654l3.984 3.984Z"
4849
5899
  })), _path3 || (_path3 = /*#__PURE__*/React.createElement("path", {
4850
5900
  fillRule: "evenodd",
@@ -4853,27 +5903,27 @@ var SvgDatetime = function SvgDatetime(props) {
4853
5903
  };
4854
5904
  var DatetimeIcon = SvgDatetime;
4855
5905
 
4856
- var _path$8, _path2$1;
4857
- 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); }
5906
+ var _path$9, _path2$2;
5907
+ 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); }
4858
5908
  var SvgTaglist = function SvgTaglist(props) {
4859
- return /*#__PURE__*/React.createElement("svg", _extends$9({
5909
+ return /*#__PURE__*/React.createElement("svg", _extends$a({
4860
5910
  xmlns: "http://www.w3.org/2000/svg",
4861
5911
  width: 54,
4862
5912
  height: 54,
4863
5913
  fill: "currentcolor"
4864
- }, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
5914
+ }, props), _path$9 || (_path$9 = /*#__PURE__*/React.createElement("path", {
4865
5915
  fillRule: "evenodd",
4866
5916
  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"
4867
- })), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
5917
+ })), _path2$2 || (_path2$2 = /*#__PURE__*/React.createElement("path", {
4868
5918
  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"
4869
5919
  })));
4870
5920
  };
4871
5921
  var TaglistIcon = SvgTaglist;
4872
5922
 
4873
5923
  var _rect, _rect2, _rect3;
4874
- 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); }
5924
+ 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); }
4875
5925
  var SvgForm = function SvgForm(props) {
4876
- return /*#__PURE__*/React.createElement("svg", _extends$8({
5926
+ return /*#__PURE__*/React.createElement("svg", _extends$9({
4877
5927
  xmlns: "http://www.w3.org/2000/svg",
4878
5928
  width: 54,
4879
5929
  height: 54
@@ -4899,63 +5949,87 @@ var SvgForm = function SvgForm(props) {
4899
5949
  };
4900
5950
  var FormIcon = SvgForm;
4901
5951
 
5952
+ var _path$8;
5953
+ 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); }
5954
+ var SvgGroup = function SvgGroup(props) {
5955
+ return /*#__PURE__*/React.createElement("svg", _extends$8({
5956
+ xmlns: "http://www.w3.org/2000/svg",
5957
+ width: 54,
5958
+ height: 54,
5959
+ fill: "currentcolor"
5960
+ }, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
5961
+ fillRule: "evenodd",
5962
+ 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"
5963
+ })));
5964
+ };
5965
+ var GroupIcon = SvgGroup;
5966
+
4902
5967
  var _path$7;
4903
5968
  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); }
4904
- var SvgGroup = function SvgGroup(props) {
5969
+ var SvgNumber = function SvgNumber(props) {
4905
5970
  return /*#__PURE__*/React.createElement("svg", _extends$7({
4906
5971
  xmlns: "http://www.w3.org/2000/svg",
4907
5972
  width: 54,
4908
- height: 54
5973
+ height: 54,
5974
+ fill: "currentcolor"
4909
5975
  }, props), _path$7 || (_path$7 = /*#__PURE__*/React.createElement("path", {
4910
5976
  fillRule: "evenodd",
4911
- 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"
5977
+ 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"
4912
5978
  })));
4913
5979
  };
4914
- var ColumnsIcon = SvgGroup;
5980
+ var NumberIcon = SvgNumber;
4915
5981
 
4916
5982
  var _path$6;
4917
5983
  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); }
4918
- var SvgNumber = function SvgNumber(props) {
5984
+ var SvgRadio = function SvgRadio(props) {
4919
5985
  return /*#__PURE__*/React.createElement("svg", _extends$6({
4920
5986
  xmlns: "http://www.w3.org/2000/svg",
4921
5987
  width: 54,
4922
5988
  height: 54,
4923
5989
  fill: "currentcolor"
4924
5990
  }, props), _path$6 || (_path$6 = /*#__PURE__*/React.createElement("path", {
4925
- fillRule: "evenodd",
4926
- 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"
5991
+ 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"
4927
5992
  })));
4928
5993
  };
4929
- var NumberIcon = SvgNumber;
5994
+ var RadioIcon = SvgRadio;
4930
5995
 
4931
5996
  var _path$5;
4932
5997
  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); }
4933
- var SvgRadio = function SvgRadio(props) {
5998
+ var SvgSelect = function SvgSelect(props) {
4934
5999
  return /*#__PURE__*/React.createElement("svg", _extends$5({
4935
6000
  xmlns: "http://www.w3.org/2000/svg",
4936
6001
  width: 54,
4937
6002
  height: 54,
4938
6003
  fill: "currentcolor"
4939
6004
  }, props), _path$5 || (_path$5 = /*#__PURE__*/React.createElement("path", {
4940
- 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"
6005
+ fillRule: "evenodd",
6006
+ 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"
4941
6007
  })));
4942
6008
  };
4943
- var RadioIcon = SvgRadio;
6009
+ var SelectIcon = SvgSelect;
4944
6010
 
4945
- var _path$4;
6011
+ var _path$4, _path2$1;
4946
6012
  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); }
4947
- var SvgSelect = function SvgSelect(props) {
6013
+ var SvgSpacer = function SvgSpacer(props) {
4948
6014
  return /*#__PURE__*/React.createElement("svg", _extends$4({
4949
6015
  xmlns: "http://www.w3.org/2000/svg",
4950
6016
  width: 54,
4951
6017
  height: 54,
4952
6018
  fill: "currentcolor"
4953
6019
  }, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
4954
- fillRule: "evenodd",
4955
- 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"
6020
+ stroke: "currentcolor",
6021
+ strokeLinecap: "square",
6022
+ strokeWidth: 2,
6023
+ d: "M9 23h36M9 31h36"
6024
+ })), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
6025
+ stroke: "currentcolor",
6026
+ strokeLinecap: "round",
6027
+ strokeLinejoin: "round",
6028
+ strokeWidth: 2,
6029
+ d: "m23 17 4-4 4 4M31 37l-4 4-4-4"
4956
6030
  })));
4957
6031
  };
4958
- var SelectIcon = SvgSelect;
6032
+ var SpacerIcon = SvgSpacer;
4959
6033
 
4960
6034
  var _path$3;
4961
6035
  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); }
@@ -5026,12 +6100,14 @@ const iconsByType = type => {
5026
6100
  button: ButtonIcon,
5027
6101
  checkbox: CheckboxIcon,
5028
6102
  checklist: ChecklistIcon,
5029
- columns: ColumnsIcon,
6103
+ columns: GroupIcon,
5030
6104
  datetime: DatetimeIcon,
6105
+ group: GroupIcon,
5031
6106
  image: ImageIcon,
5032
6107
  number: NumberIcon,
5033
6108
  radio: RadioIcon,
5034
6109
  select: SelectIcon,
6110
+ spacer: SpacerIcon,
5035
6111
  taglist: TaglistIcon,
5036
6112
  text: TextIcon,
5037
6113
  textfield: TextfieldIcon,
@@ -5040,7 +6116,7 @@ const iconsByType = type => {
5040
6116
  }[type];
5041
6117
  };
5042
6118
 
5043
- const formFields = [Button, Checkbox, Checklist, Default, Image, Numberfield, Datetime, Radio, Select, Taglist, Text, Textfield, Textarea];
6119
+ const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, Image, Numberfield, Datetime, Radio, Select, Spacer, Taglist, Text, Textfield, Textarea];
5044
6120
 
5045
6121
  class FormFields {
5046
6122
  constructor() {
@@ -5116,9 +6192,12 @@ var renderModule = {
5116
6192
  };
5117
6193
 
5118
6194
  var core = {
5119
- __depends__: [importModule, renderModule],
6195
+ __depends__: [renderModule],
5120
6196
  eventBus: ['type', EventBus],
6197
+ importer: ['type', Importer],
6198
+ fieldFactory: ['type', FieldFactory],
5121
6199
  formFieldRegistry: ['type', FormFieldRegistry],
6200
+ pathRegistry: ['type', PathRegistry],
5122
6201
  formLayouter: ['type', FormLayouter],
5123
6202
  validator: ['type', Validator]
5124
6203
  };
@@ -5233,9 +6312,9 @@ class Form {
5233
6312
  this.clear();
5234
6313
  const {
5235
6314
  schema: importedSchema,
5236
- data: initializedData,
5237
6315
  warnings
5238
- } = this.get('importer').importSchema(schema, data);
6316
+ } = this.get('importer').importSchema(schema);
6317
+ const initializedData = this._initializeFieldData(clone(data));
5239
6318
  this._setState({
5240
6319
  data: initializedData,
5241
6320
  errors: {},
@@ -5293,21 +6372,21 @@ class Form {
5293
6372
  */
5294
6373
  validate() {
5295
6374
  const formFieldRegistry = this.get('formFieldRegistry'),
6375
+ pathRegistry = this.get('pathRegistry'),
5296
6376
  validator = this.get('validator');
5297
6377
  const {
5298
6378
  data
5299
6379
  } = this._getState();
5300
6380
  const errors = formFieldRegistry.getAll().reduce((errors, field) => {
5301
6381
  const {
5302
- disabled,
5303
- _path
6382
+ disabled
5304
6383
  } = field;
5305
6384
  if (disabled) {
5306
6385
  return errors;
5307
6386
  }
5308
- const value = get(data, _path);
6387
+ const value = get(data, pathRegistry.getValuePath(field));
5309
6388
  const fieldErrors = validator.validateField(field, value);
5310
- return set(errors, [pathStringify(_path)], fieldErrors.length ? fieldErrors : undefined);
6389
+ return set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
5311
6390
  }, /** @type {Errors} */{});
5312
6391
  this._setState({
5313
6392
  errors
@@ -5413,16 +6492,14 @@ class Form {
5413
6492
  value
5414
6493
  } = update;
5415
6494
  const {
5416
- _path
5417
- } = field;
5418
- let {
5419
6495
  data,
5420
6496
  errors
5421
6497
  } = this._getState();
5422
- const validator = this.get('validator');
6498
+ const validator = this.get('validator'),
6499
+ pathRegistry = this.get('pathRegistry');
5423
6500
  const fieldErrors = validator.validateField(field, value);
5424
- set(data, _path, value);
5425
- set(errors, [pathStringify(_path)], fieldErrors.length ? fieldErrors : undefined);
6501
+ set(data, pathRegistry.getValuePath(field), value);
6502
+ set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
5426
6503
  this._setState({
5427
6504
  data: clone(data),
5428
6505
  errors: clone(errors)
@@ -5451,7 +6528,7 @@ class Form {
5451
6528
  * @internal
5452
6529
  */
5453
6530
  _getModules() {
5454
- return [ExpressionLanguageModule, MarkdownModule];
6531
+ return [ExpressionLanguageModule, MarkdownModule, ViewerCommandsModule];
5455
6532
  }
5456
6533
 
5457
6534
  /**
@@ -5465,23 +6542,26 @@ class Form {
5465
6542
  * @internal
5466
6543
  */
5467
6544
  _getSubmitData() {
5468
- const formFieldRegistry = this.get('formFieldRegistry');
6545
+ const formFieldRegistry = this.get('formFieldRegistry'),
6546
+ pathRegistry = this.get('pathRegistry'),
6547
+ formFields = this.get('formFields');
5469
6548
  const formData = this._getState().data;
5470
6549
  const submitData = formFieldRegistry.getAll().reduce((previous, field) => {
5471
6550
  const {
5472
6551
  disabled,
5473
- _path
6552
+ type
5474
6553
  } = field;
6554
+ const {
6555
+ config: fieldConfig
6556
+ } = formFields.get(type);
5475
6557
 
5476
- // do not submit disabled form fields
5477
- if (disabled || !_path) {
6558
+ // do not submit disabled form fields or routing fields
6559
+ if (disabled || !fieldConfig.keyed) {
5478
6560
  return previous;
5479
6561
  }
5480
- const value = get(formData, _path);
5481
- return {
5482
- ...previous,
5483
- [_path[0]]: value
5484
- };
6562
+ const valuePath = pathRegistry.getValuePath(field);
6563
+ const value = get(formData, valuePath);
6564
+ return set(previous, valuePath, value);
5485
6565
  }, {});
5486
6566
  const filteredSubmitData = this._applyConditions(submitData, formData);
5487
6567
  return filteredSubmitData;
@@ -5494,9 +6574,46 @@ class Form {
5494
6574
  const conditionChecker = this.get('conditionChecker');
5495
6575
  return conditionChecker.applyConditions(toFilter, data);
5496
6576
  }
6577
+
6578
+ /**
6579
+ * @internal
6580
+ */
6581
+ _initializeFieldData(data) {
6582
+ const formFieldRegistry = this.get('formFieldRegistry'),
6583
+ formFields = this.get('formFields'),
6584
+ pathRegistry = this.get('pathRegistry');
6585
+ return formFieldRegistry.getAll().reduce((initializedData, formField) => {
6586
+ const {
6587
+ defaultValue,
6588
+ type
6589
+ } = formField;
6590
+
6591
+ // try to get value from data
6592
+ // if unavailable - try to get default value from form field
6593
+ // if unavailable - get empty value from form field
6594
+
6595
+ const valuePath = pathRegistry.getValuePath(formField);
6596
+ if (valuePath) {
6597
+ const {
6598
+ config: fieldConfig
6599
+ } = formFields.get(type);
6600
+ let valueData = get(data, valuePath);
6601
+ if (!isUndefined(valueData) && fieldConfig.sanitizeValue) {
6602
+ valueData = fieldConfig.sanitizeValue({
6603
+ formField,
6604
+ data,
6605
+ value: valueData
6606
+ });
6607
+ }
6608
+ const initializedFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
6609
+ return set(initializedData, valuePath, initializedFieldValue);
6610
+ }
6611
+ return initializedData;
6612
+ }, data);
6613
+ }
5497
6614
  }
5498
6615
 
5499
- const schemaVersion = 9;
6616
+ const schemaVersion = 11;
5500
6617
 
5501
6618
  /**
5502
6619
  * @typedef { import('./types').CreateFormOptions } CreateFormOptions
@@ -5521,5 +6638,5 @@ function createForm(options) {
5521
6638
  });
5522
6639
  }
5523
6640
 
5524
- export { Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, Default, ExpressionLanguageModule, FeelExpressionLanguage, FeelersTemplating, Form, FormComponent, FormContext$1 as FormContext, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext$1 as FormRenderContext, Image, MINUTES_IN_DAY, MarkdownModule, MarkdownRenderer, Numberfield, Radio, Select, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Taglist, Text, Textarea, Textfield, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, VALUES_SOURCES_PATHS, VALUES_SOURCE_DEFAULT, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, getSchemaVariables, getValuesSource, iconsByType, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
6641
+ export { Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, FormComponent$1 as Default, ExpressionLanguageModule, FeelExpressionLanguage, FeelersTemplating, FieldFactory, Form, FormComponent, FormContext$1 as FormContext, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext$1 as FormRenderContext, Group, Image, Importer, MINUTES_IN_DAY, MarkdownModule, MarkdownRenderer, Numberfield, PathRegistry, Radio, Select, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Taglist, Text, Textarea, Textfield, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, VALUES_SOURCES_PATHS, VALUES_SOURCE_DEFAULT, ViewerCommands, ViewerCommandsModule, clone, createForm, createFormContainer, createInjector, formFields, generateIdForType, generateIndexForType, getSchemaVariables, getValuesSource, iconsByType, isRequired, pathParse, pathsEqual, runRecursively, schemaVersion };
5525
6642
  //# sourceMappingURL=index.es.js.map