@bpmn-io/form-js-viewer 1.1.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.
- package/LICENSE +22 -22
- package/README.md +164 -164
- package/dist/assets/form-js-base.css +985 -932
- package/dist/assets/form-js.css +54 -1
- package/dist/index.cjs +1151 -745
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +1147 -744
- package/dist/index.es.js.map +1 -1
- package/dist/types/Form.d.ts +4 -0
- package/dist/types/core/FieldFactory.d.ts +19 -0
- package/dist/types/core/FormFieldRegistry.d.ts +0 -1
- package/dist/types/core/Importer.d.ts +56 -0
- package/dist/types/core/PathRegistry.d.ts +71 -0
- package/dist/types/core/index.d.ts +9 -5
- package/dist/types/features/expression-language/ConditionChecker.d.ts +3 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/render/components/form-fields/Checklist.d.ts +2 -6
- package/dist/types/render/components/form-fields/Default.d.ts +3 -3
- package/dist/types/render/components/form-fields/Group.d.ts +14 -0
- package/dist/types/render/components/form-fields/Radio.d.ts +2 -6
- package/dist/types/render/components/form-fields/Select.d.ts +2 -6
- package/dist/types/render/components/form-fields/Taglist.d.ts +2 -6
- package/dist/types/render/components/form-fields/parts/Grid.d.ts +1 -0
- package/dist/types/render/components/index.d.ts +3 -2
- package/dist/types/render/components/util/localisationUtil.d.ts +24 -0
- package/dist/types/render/components/util/valuesUtil.d.ts +6 -0
- package/dist/types/render/context/FormRenderContext.d.ts +3 -0
- package/dist/types/types.d.ts +35 -35
- package/dist/types/util/index.d.ts +1 -2
- package/package.json +3 -2
- package/dist/types/import/Importer.d.ts +0 -45
- package/dist/types/import/index.d.ts +0 -5
package/dist/index.cjs
CHANGED
|
@@ -67,26 +67,26 @@ const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', o
|
|
|
67
67
|
return [...new Set(variables)];
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* Get the variable name at the specified index in a given path expression.
|
|
72
|
-
*
|
|
73
|
-
* @param {Object} root - The root node of the path expression tree.
|
|
74
|
-
* @param {number} index - The index of the variable name to retrieve.
|
|
75
|
-
* @returns {string|null} The variable name at the specified index or null if index is out of bounds.
|
|
70
|
+
/**
|
|
71
|
+
* Get the variable name at the specified index in a given path expression.
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} root - The root node of the path expression tree.
|
|
74
|
+
* @param {number} index - The index of the variable name to retrieve.
|
|
75
|
+
* @returns {string|null} The variable name at the specified index or null if index is out of bounds.
|
|
76
76
|
*/
|
|
77
77
|
const _getVariableNameAtPathIndex = (root, index) => {
|
|
78
78
|
const accessors = _deconstructPathExpression(root);
|
|
79
79
|
return accessors[index] || null;
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
/**
|
|
83
|
-
* Extracts the variables which are required of the external context for a given path expression.
|
|
84
|
-
* This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
|
|
85
|
-
*
|
|
86
|
-
* @param {Object} node - The root node of the path expression tree.
|
|
87
|
-
* @param {number} initialDepth - The depth at which the root node is located in the outer context.
|
|
88
|
-
* @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
|
|
89
|
-
* @returns {Set} - A set containing the extracted variable names.
|
|
82
|
+
/**
|
|
83
|
+
* Extracts the variables which are required of the external context for a given path expression.
|
|
84
|
+
* This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} node - The root node of the path expression tree.
|
|
87
|
+
* @param {number} initialDepth - The depth at which the root node is located in the outer context.
|
|
88
|
+
* @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
|
|
89
|
+
* @returns {Set} - A set containing the extracted variable names.
|
|
90
90
|
*/
|
|
91
91
|
const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
|
|
92
92
|
// depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
|
|
@@ -132,11 +132,11 @@ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) =
|
|
|
132
132
|
return new Set(extractedVariables);
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
-
/**
|
|
136
|
-
* Deconstructs a path expression tree into an array of components.
|
|
137
|
-
*
|
|
138
|
-
* @param {Object} root - The root node of the path expression tree.
|
|
139
|
-
* @returns {Array<string>} An array of components in the path expression, in the correct order.
|
|
135
|
+
/**
|
|
136
|
+
* Deconstructs a path expression tree into an array of components.
|
|
137
|
+
*
|
|
138
|
+
* @param {Object} root - The root node of the path expression tree.
|
|
139
|
+
* @returns {Array<string>} An array of components in the path expression, in the correct order.
|
|
140
140
|
*/
|
|
141
141
|
const _deconstructPathExpression = root => {
|
|
142
142
|
let node = root;
|
|
@@ -155,13 +155,13 @@ const _deconstructPathExpression = root => {
|
|
|
155
155
|
return parts.reverse();
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
-
/**
|
|
159
|
-
* Builds a simplified feel structure tree from the given parse tree and feel string.
|
|
160
|
-
* The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
|
|
161
|
-
*
|
|
162
|
-
* @param {Object} parseTree - The parse tree generated by a parser.
|
|
163
|
-
* @param {string} feelString - The feel string used for parsing.
|
|
164
|
-
* @returns {Object} The simplified feel structure tree.
|
|
158
|
+
/**
|
|
159
|
+
* Builds a simplified feel structure tree from the given parse tree and feel string.
|
|
160
|
+
* The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
|
|
161
|
+
*
|
|
162
|
+
* @param {Object} parseTree - The parse tree generated by a parser.
|
|
163
|
+
* @param {string} feelString - The feel string used for parsing.
|
|
164
|
+
* @returns {Object} The simplified feel structure tree.
|
|
165
165
|
*/
|
|
166
166
|
const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
|
|
167
167
|
const stack = [{
|
|
@@ -187,9 +187,9 @@ const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
|
|
|
187
187
|
return _extractFilterExpressions(stack[0].children[0]);
|
|
188
188
|
};
|
|
189
189
|
|
|
190
|
-
/**
|
|
191
|
-
* Restructure the tree in such a way to bring filters (which create new contexts) to the root of the tree.
|
|
192
|
-
* This is done to simplify the extraction of variables and match the context hierarchy.
|
|
190
|
+
/**
|
|
191
|
+
* Restructure the tree in such a way to bring filters (which create new contexts) to the root of the tree.
|
|
192
|
+
* This is done to simplify the extraction of variables and match the context hierarchy.
|
|
193
193
|
*/
|
|
194
194
|
const _extractFilterExpressions = tree => {
|
|
195
195
|
const flattenedExpressionTree = {
|
|
@@ -230,25 +230,25 @@ class FeelExpressionLanguage {
|
|
|
230
230
|
this._eventBus = eventBus;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
/**
|
|
234
|
-
* Determines if the given value is a FEEL expression.
|
|
235
|
-
*
|
|
236
|
-
* @param {any} value
|
|
237
|
-
* @returns {boolean}
|
|
238
|
-
*
|
|
233
|
+
/**
|
|
234
|
+
* Determines if the given value is a FEEL expression.
|
|
235
|
+
*
|
|
236
|
+
* @param {any} value
|
|
237
|
+
* @returns {boolean}
|
|
238
|
+
*
|
|
239
239
|
*/
|
|
240
240
|
isExpression(value) {
|
|
241
241
|
return minDash.isString(value) && value.startsWith('=');
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
/**
|
|
245
|
-
* Retrieve variable names from a given FEEL expression.
|
|
246
|
-
*
|
|
247
|
-
* @param {string} expression
|
|
248
|
-
* @param {object} [options]
|
|
249
|
-
* @param {string} [options.type]
|
|
250
|
-
*
|
|
251
|
-
* @returns {string[]}
|
|
244
|
+
/**
|
|
245
|
+
* Retrieve variable names from a given FEEL expression.
|
|
246
|
+
*
|
|
247
|
+
* @param {string} expression
|
|
248
|
+
* @param {object} [options]
|
|
249
|
+
* @param {string} [options.type]
|
|
250
|
+
*
|
|
251
|
+
* @returns {string[]}
|
|
252
252
|
*/
|
|
253
253
|
getVariableNames(expression, options = {}) {
|
|
254
254
|
const {
|
|
@@ -263,13 +263,13 @@ class FeelExpressionLanguage {
|
|
|
263
263
|
return getFlavouredFeelVariableNames(expression, type);
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
/**
|
|
267
|
-
* Evaluate an expression.
|
|
268
|
-
*
|
|
269
|
-
* @param {string} expression
|
|
270
|
-
* @param {import('../../types').Data} [data]
|
|
271
|
-
*
|
|
272
|
-
* @returns {any}
|
|
266
|
+
/**
|
|
267
|
+
* Evaluate an expression.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} expression
|
|
270
|
+
* @param {import('../../types').Data} [data]
|
|
271
|
+
*
|
|
272
|
+
* @returns {any}
|
|
273
273
|
*/
|
|
274
274
|
evaluate(expression, data = {}) {
|
|
275
275
|
if (!expression) {
|
|
@@ -294,23 +294,23 @@ FeelExpressionLanguage.$inject = ['eventBus'];
|
|
|
294
294
|
class FeelersTemplating {
|
|
295
295
|
constructor() {}
|
|
296
296
|
|
|
297
|
-
/**
|
|
298
|
-
* Determines if the given value is a feelers template.
|
|
299
|
-
*
|
|
300
|
-
* @param {any} value
|
|
301
|
-
* @returns {boolean}
|
|
302
|
-
*
|
|
297
|
+
/**
|
|
298
|
+
* Determines if the given value is a feelers template.
|
|
299
|
+
*
|
|
300
|
+
* @param {any} value
|
|
301
|
+
* @returns {boolean}
|
|
302
|
+
*
|
|
303
303
|
*/
|
|
304
304
|
isTemplate(value) {
|
|
305
305
|
return minDash.isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
/**
|
|
309
|
-
* Retrieve variable names from a given feelers template.
|
|
310
|
-
*
|
|
311
|
-
* @param {string} template
|
|
312
|
-
*
|
|
313
|
-
* @returns {string[]}
|
|
308
|
+
/**
|
|
309
|
+
* Retrieve variable names from a given feelers template.
|
|
310
|
+
*
|
|
311
|
+
* @param {string} template
|
|
312
|
+
*
|
|
313
|
+
* @returns {string[]}
|
|
314
314
|
*/
|
|
315
315
|
getVariableNames(template) {
|
|
316
316
|
if (!this.isTemplate(template)) {
|
|
@@ -336,17 +336,17 @@ class FeelersTemplating {
|
|
|
336
336
|
}, []);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
/**
|
|
340
|
-
* Evaluate a template.
|
|
341
|
-
*
|
|
342
|
-
* @param {string} template
|
|
343
|
-
* @param {Object<string, any>} context
|
|
344
|
-
* @param {Object} options
|
|
345
|
-
* @param {boolean} [options.debug = false]
|
|
346
|
-
* @param {boolean} [options.strict = false]
|
|
347
|
-
* @param {Function} [options.buildDebugString]
|
|
348
|
-
*
|
|
349
|
-
* @returns
|
|
339
|
+
/**
|
|
340
|
+
* Evaluate a template.
|
|
341
|
+
*
|
|
342
|
+
* @param {string} template
|
|
343
|
+
* @param {Object<string, any>} context
|
|
344
|
+
* @param {Object} options
|
|
345
|
+
* @param {boolean} [options.debug = false]
|
|
346
|
+
* @param {boolean} [options.strict = false]
|
|
347
|
+
* @param {Function} [options.buildDebugString]
|
|
348
|
+
*
|
|
349
|
+
* @returns
|
|
350
350
|
*/
|
|
351
351
|
evaluate(template, context = {}, options = {}) {
|
|
352
352
|
const {
|
|
@@ -361,22 +361,22 @@ class FeelersTemplating {
|
|
|
361
361
|
});
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
/**
|
|
365
|
-
* @typedef {Object} ExpressionWithDepth
|
|
366
|
-
* @property {number} depth - The depth of the expression in the syntax tree.
|
|
367
|
-
* @property {string} expression - The extracted expression
|
|
364
|
+
/**
|
|
365
|
+
* @typedef {Object} ExpressionWithDepth
|
|
366
|
+
* @property {number} depth - The depth of the expression in the syntax tree.
|
|
367
|
+
* @property {string} expression - The extracted expression
|
|
368
368
|
*/
|
|
369
369
|
|
|
370
|
-
/**
|
|
371
|
-
* Extracts all feel expressions in the template along with their depth in the syntax tree.
|
|
372
|
-
* The depth is incremented for child expressions of loops to account for context drilling.
|
|
373
|
-
|
|
374
|
-
* @param {string} template - A feelers template string.
|
|
375
|
-
* @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
|
|
376
|
-
*
|
|
377
|
-
* @example
|
|
378
|
-
* const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
|
|
379
|
-
* const extractedExpressions = _extractExpressionsWithDepth(template);
|
|
370
|
+
/**
|
|
371
|
+
* Extracts all feel expressions in the template along with their depth in the syntax tree.
|
|
372
|
+
* The depth is incremented for child expressions of loops to account for context drilling.
|
|
373
|
+
* @name extractExpressionsWithDepth
|
|
374
|
+
* @param {string} template - A feelers template string.
|
|
375
|
+
* @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
|
|
379
|
+
* const extractedExpressions = _extractExpressionsWithDepth(template);
|
|
380
380
|
*/
|
|
381
381
|
_extractExpressionsWithDepth(template) {
|
|
382
382
|
// build simplified feelers syntax tree
|
|
@@ -407,47 +407,271 @@ class FeelersTemplating {
|
|
|
407
407
|
}
|
|
408
408
|
FeelersTemplating.$inject = [];
|
|
409
409
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
410
|
+
// config ///////////////////
|
|
411
|
+
|
|
412
|
+
const MINUTES_IN_DAY = 60 * 24;
|
|
413
|
+
const DATETIME_SUBTYPES = {
|
|
414
|
+
DATE: 'date',
|
|
415
|
+
TIME: 'time',
|
|
416
|
+
DATETIME: 'datetime'
|
|
417
|
+
};
|
|
418
|
+
const TIME_SERIALISING_FORMATS = {
|
|
419
|
+
UTC_OFFSET: 'utc_offset',
|
|
420
|
+
UTC_NORMALIZED: 'utc_normalized',
|
|
421
|
+
NO_TIMEZONE: 'no_timezone'
|
|
422
|
+
};
|
|
423
|
+
const DATETIME_SUBTYPES_LABELS = {
|
|
424
|
+
[DATETIME_SUBTYPES.DATE]: 'Date',
|
|
425
|
+
[DATETIME_SUBTYPES.TIME]: 'Time',
|
|
426
|
+
[DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
|
|
427
|
+
};
|
|
428
|
+
const TIME_SERIALISINGFORMAT_LABELS = {
|
|
429
|
+
[TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
|
|
430
|
+
[TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
|
|
431
|
+
[TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
|
|
432
|
+
};
|
|
433
|
+
const DATETIME_SUBTYPE_PATH = ['subtype'];
|
|
434
|
+
const DATE_LABEL_PATH = ['dateLabel'];
|
|
435
|
+
const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
|
|
436
|
+
const TIME_LABEL_PATH = ['timeLabel'];
|
|
437
|
+
const TIME_USE24H_PATH = ['use24h'];
|
|
438
|
+
const TIME_INTERVAL_PATH = ['timeInterval'];
|
|
439
|
+
const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
|
|
440
|
+
|
|
441
|
+
// config ///////////////////
|
|
442
|
+
|
|
443
|
+
const VALUES_SOURCES = {
|
|
444
|
+
STATIC: 'static',
|
|
445
|
+
INPUT: 'input',
|
|
446
|
+
EXPRESSION: 'expression'
|
|
447
|
+
};
|
|
448
|
+
const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
|
|
449
|
+
const VALUES_SOURCES_LABELS = {
|
|
450
|
+
[VALUES_SOURCES.STATIC]: 'Static',
|
|
451
|
+
[VALUES_SOURCES.INPUT]: 'Input data',
|
|
452
|
+
[VALUES_SOURCES.EXPRESSION]: 'Expression'
|
|
453
|
+
};
|
|
454
|
+
const VALUES_SOURCES_PATHS = {
|
|
455
|
+
[VALUES_SOURCES.STATIC]: ['values'],
|
|
456
|
+
[VALUES_SOURCES.INPUT]: ['valuesKey'],
|
|
457
|
+
[VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
|
|
458
|
+
};
|
|
459
|
+
const VALUES_SOURCES_DEFAULTS = {
|
|
460
|
+
[VALUES_SOURCES.STATIC]: [{
|
|
461
|
+
label: 'Value',
|
|
462
|
+
value: 'value'
|
|
463
|
+
}],
|
|
464
|
+
[VALUES_SOURCES.INPUT]: '',
|
|
465
|
+
[VALUES_SOURCES.EXPRESSION]: '='
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// helpers ///////////////////
|
|
469
|
+
|
|
470
|
+
function getValuesSource(field) {
|
|
471
|
+
for (const source of Object.values(VALUES_SOURCES)) {
|
|
472
|
+
if (minDash.get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
|
|
473
|
+
return source;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return VALUES_SOURCE_DEFAULT;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function createInjector(bootstrapModules) {
|
|
480
|
+
const injector = new didi.Injector(bootstrapModules);
|
|
481
|
+
injector.init();
|
|
482
|
+
return injector;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* @param {string?} prefix
|
|
487
|
+
*
|
|
488
|
+
* @returns Element
|
|
489
|
+
*/
|
|
490
|
+
function createFormContainer(prefix = 'fjs') {
|
|
491
|
+
const container = document.createElement('div');
|
|
492
|
+
container.classList.add(`${prefix}-container`);
|
|
493
|
+
return container;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
|
|
497
|
+
const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
|
|
498
|
+
function isRequired(field) {
|
|
499
|
+
return field.required;
|
|
500
|
+
}
|
|
501
|
+
function pathParse(path) {
|
|
502
|
+
if (!path) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
return path.split('.').map(key => {
|
|
506
|
+
return isNaN(parseInt(key)) ? key : parseInt(key);
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
function pathsEqual(a, b) {
|
|
510
|
+
return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
|
|
511
|
+
}
|
|
512
|
+
const indices = {};
|
|
513
|
+
function generateIndexForType(type) {
|
|
514
|
+
if (type in indices) {
|
|
515
|
+
indices[type]++;
|
|
516
|
+
} else {
|
|
517
|
+
indices[type] = 1;
|
|
518
|
+
}
|
|
519
|
+
return indices[type];
|
|
520
|
+
}
|
|
521
|
+
function generateIdForType(type) {
|
|
522
|
+
return `${type}${generateIndexForType(type)}`;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* @template T
|
|
527
|
+
* @param {T} data
|
|
528
|
+
* @param {(this: any, key: string, value: any) => any} [replacer]
|
|
529
|
+
* @return {T}
|
|
530
|
+
*/
|
|
531
|
+
function clone(data, replacer) {
|
|
532
|
+
return JSON.parse(JSON.stringify(data, replacer));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Parse the schema for input variables a form might make use of
|
|
537
|
+
*
|
|
538
|
+
* @param {any} schema
|
|
539
|
+
*
|
|
540
|
+
* @return {string[]}
|
|
541
|
+
*/
|
|
542
|
+
function getSchemaVariables(schema, options = {}) {
|
|
543
|
+
const {
|
|
544
|
+
expressionLanguage = new FeelExpressionLanguage(null),
|
|
545
|
+
templating = new FeelersTemplating(),
|
|
546
|
+
inputs = true,
|
|
547
|
+
outputs = true
|
|
548
|
+
} = options;
|
|
549
|
+
if (!schema.components) {
|
|
550
|
+
return [];
|
|
551
|
+
}
|
|
552
|
+
const getAllComponents = node => {
|
|
553
|
+
const components = [];
|
|
554
|
+
if (node.components) {
|
|
555
|
+
node.components.forEach(component => {
|
|
556
|
+
components.push(component);
|
|
557
|
+
components.push(...getAllComponents(component));
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
return components;
|
|
561
|
+
};
|
|
562
|
+
const variables = getAllComponents(schema).reduce((variables, component) => {
|
|
563
|
+
const {
|
|
564
|
+
valuesKey
|
|
565
|
+
} = component;
|
|
566
|
+
|
|
567
|
+
// collect input-only variables
|
|
568
|
+
if (inputs) {
|
|
569
|
+
if (valuesKey) {
|
|
570
|
+
variables = [...variables, valuesKey];
|
|
571
|
+
}
|
|
572
|
+
EXPRESSION_PROPERTIES.forEach(prop => {
|
|
573
|
+
const property = minDash.get(component, prop.split('.'));
|
|
574
|
+
if (property && expressionLanguage.isExpression(property)) {
|
|
575
|
+
const expressionVariables = expressionLanguage.getVariableNames(property, {
|
|
576
|
+
type: 'expression'
|
|
577
|
+
});
|
|
578
|
+
variables = [...variables, ...expressionVariables];
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
TEMPLATE_PROPERTIES.forEach(prop => {
|
|
582
|
+
const property = minDash.get(component, prop.split('.'));
|
|
583
|
+
if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
|
|
584
|
+
const templateVariables = templating.getVariableNames(property);
|
|
585
|
+
variables = [...variables, ...templateVariables];
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
return variables.filter(variable => variable !== undefined || variable !== null);
|
|
590
|
+
}, []);
|
|
591
|
+
const getBindingVariables = node => {
|
|
592
|
+
const bindingVariable = [];
|
|
593
|
+
|
|
594
|
+
// c.f. https://github.com/bpmn-io/form-js/issues/778 @Skaiir to remove?
|
|
595
|
+
if (node.type === 'button') {
|
|
596
|
+
return [];
|
|
597
|
+
} else if (node.key) {
|
|
598
|
+
return [node.key.split('.')[0]];
|
|
599
|
+
} else if (node.path) {
|
|
600
|
+
return [node.path.split('.')[0]];
|
|
601
|
+
} else if (node.components) {
|
|
602
|
+
node.components.forEach(component => {
|
|
603
|
+
bindingVariable.push(...getBindingVariables(component));
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return bindingVariable;
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// collect binding variables
|
|
610
|
+
if (inputs || outputs) {
|
|
611
|
+
variables.push(...getBindingVariables(schema));
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// remove duplicates
|
|
615
|
+
return Array.from(new Set(variables));
|
|
616
|
+
}
|
|
617
|
+
function runRecursively(formField, fn) {
|
|
618
|
+
const components = formField.components || [];
|
|
619
|
+
components.forEach((component, index) => {
|
|
620
|
+
runRecursively(component, fn);
|
|
621
|
+
});
|
|
622
|
+
fn(formField);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* @typedef {object} Condition
|
|
627
|
+
* @property {string} [hide]
|
|
413
628
|
*/
|
|
414
629
|
|
|
415
630
|
class ConditionChecker {
|
|
416
|
-
constructor(formFieldRegistry, eventBus) {
|
|
631
|
+
constructor(formFieldRegistry, pathRegistry, eventBus) {
|
|
417
632
|
this._formFieldRegistry = formFieldRegistry;
|
|
633
|
+
this._pathRegistry = pathRegistry;
|
|
418
634
|
this._eventBus = eventBus;
|
|
419
635
|
}
|
|
420
636
|
|
|
421
|
-
/**
|
|
422
|
-
* For given data, remove properties based on condition.
|
|
423
|
-
*
|
|
424
|
-
* @param {Object<string, any>} properties
|
|
425
|
-
* @param {Object<string, any>} data
|
|
637
|
+
/**
|
|
638
|
+
* For given data, remove properties based on condition.
|
|
639
|
+
*
|
|
640
|
+
* @param {Object<string, any>} properties
|
|
641
|
+
* @param {Object<string, any>} data
|
|
426
642
|
*/
|
|
427
643
|
applyConditions(properties, data = {}) {
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
for (const {
|
|
433
|
-
key,
|
|
434
|
-
condition
|
|
435
|
-
} of conditions) {
|
|
436
|
-
const shouldRemove = this._checkHideCondition(condition, data);
|
|
437
|
-
if (shouldRemove) {
|
|
438
|
-
delete newProperties[key];
|
|
439
|
-
}
|
|
644
|
+
const newProperties = clone(properties);
|
|
645
|
+
const form = this._formFieldRegistry.getAll().find(field => field.type === 'default');
|
|
646
|
+
if (!form) {
|
|
647
|
+
throw new Error('form field registry has no form');
|
|
440
648
|
}
|
|
649
|
+
this._pathRegistry.executeRecursivelyOnFields(form, ({
|
|
650
|
+
field,
|
|
651
|
+
isClosed,
|
|
652
|
+
context
|
|
653
|
+
}) => {
|
|
654
|
+
const {
|
|
655
|
+
conditional: condition
|
|
656
|
+
} = field;
|
|
657
|
+
context.isHidden = context.isHidden || condition && this._checkHideCondition(condition, data);
|
|
658
|
+
|
|
659
|
+
// only clear the leaf nodes, as groups may both point to the same path
|
|
660
|
+
if (context.isHidden && isClosed) {
|
|
661
|
+
const valuePath = this._pathRegistry.getValuePath(field);
|
|
662
|
+
this._clearObjectValueRecursively(valuePath, newProperties);
|
|
663
|
+
}
|
|
664
|
+
});
|
|
441
665
|
return newProperties;
|
|
442
666
|
}
|
|
443
667
|
|
|
444
|
-
/**
|
|
445
|
-
* Check if given condition is met. Returns null for invalid/missing conditions.
|
|
446
|
-
*
|
|
447
|
-
* @param {string} condition
|
|
448
|
-
* @param {import('../../types').Data} [data]
|
|
449
|
-
*
|
|
450
|
-
* @returns {boolean|null}
|
|
668
|
+
/**
|
|
669
|
+
* Check if given condition is met. Returns null for invalid/missing conditions.
|
|
670
|
+
*
|
|
671
|
+
* @param {string} condition
|
|
672
|
+
* @param {import('../../types').Data} [data]
|
|
673
|
+
*
|
|
674
|
+
* @returns {boolean|null}
|
|
451
675
|
*/
|
|
452
676
|
check(condition, data = {}) {
|
|
453
677
|
if (!condition) {
|
|
@@ -468,12 +692,12 @@ class ConditionChecker {
|
|
|
468
692
|
}
|
|
469
693
|
}
|
|
470
694
|
|
|
471
|
-
/**
|
|
472
|
-
* Check if hide condition is met.
|
|
473
|
-
*
|
|
474
|
-
* @param {Condition} condition
|
|
475
|
-
* @param {Object<string, any>} data
|
|
476
|
-
* @returns {boolean}
|
|
695
|
+
/**
|
|
696
|
+
* Check if hide condition is met.
|
|
697
|
+
*
|
|
698
|
+
* @param {Condition} condition
|
|
699
|
+
* @param {Object<string, any>} data
|
|
700
|
+
* @returns {boolean}
|
|
477
701
|
*/
|
|
478
702
|
_checkHideCondition(condition, data) {
|
|
479
703
|
if (!condition.hide) {
|
|
@@ -482,24 +706,18 @@ class ConditionChecker {
|
|
|
482
706
|
const result = this.check(condition.hide, data);
|
|
483
707
|
return result === true;
|
|
484
708
|
}
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
key,
|
|
495
|
-
condition
|
|
496
|
-
}];
|
|
497
|
-
}
|
|
498
|
-
return conditions;
|
|
499
|
-
}, []);
|
|
709
|
+
_clearObjectValueRecursively(valuePath, obj) {
|
|
710
|
+
const workingValuePath = [...valuePath];
|
|
711
|
+
let recurse = false;
|
|
712
|
+
do {
|
|
713
|
+
minDash.set(obj, workingValuePath, undefined);
|
|
714
|
+
workingValuePath.pop();
|
|
715
|
+
const parentObject = minDash.get(obj, workingValuePath);
|
|
716
|
+
recurse = minDash.isObject(parentObject) && !minDash.values(parentObject).length && !!workingValuePath.length;
|
|
717
|
+
} while (recurse);
|
|
500
718
|
}
|
|
501
719
|
}
|
|
502
|
-
ConditionChecker.$inject = ['formFieldRegistry', 'eventBus'];
|
|
720
|
+
ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'eventBus'];
|
|
503
721
|
|
|
504
722
|
var ExpressionLanguageModule = {
|
|
505
723
|
__init__: ['expressionLanguage', 'templating', 'conditionChecker'],
|
|
@@ -515,12 +733,12 @@ class MarkdownRenderer {
|
|
|
515
733
|
this._converter = new showdown.Converter();
|
|
516
734
|
}
|
|
517
735
|
|
|
518
|
-
/**
|
|
519
|
-
* Render markdown to HTML.
|
|
520
|
-
*
|
|
521
|
-
* @param {string} markdown - The markdown to render
|
|
522
|
-
*
|
|
523
|
-
* @returns {string} HTML
|
|
736
|
+
/**
|
|
737
|
+
* Render markdown to HTML.
|
|
738
|
+
*
|
|
739
|
+
* @param {string} markdown - The markdown to render
|
|
740
|
+
*
|
|
741
|
+
* @returns {string} HTML
|
|
524
742
|
*/
|
|
525
743
|
render(markdown) {
|
|
526
744
|
return this._converter.makeHtml(markdown);
|
|
@@ -1004,203 +1222,6 @@ var commandModule = {
|
|
|
1004
1222
|
commandStack: ['type', CommandStack]
|
|
1005
1223
|
};
|
|
1006
1224
|
|
|
1007
|
-
// config ///////////////////
|
|
1008
|
-
|
|
1009
|
-
const MINUTES_IN_DAY = 60 * 24;
|
|
1010
|
-
const DATETIME_SUBTYPES = {
|
|
1011
|
-
DATE: 'date',
|
|
1012
|
-
TIME: 'time',
|
|
1013
|
-
DATETIME: 'datetime'
|
|
1014
|
-
};
|
|
1015
|
-
const TIME_SERIALISING_FORMATS = {
|
|
1016
|
-
UTC_OFFSET: 'utc_offset',
|
|
1017
|
-
UTC_NORMALIZED: 'utc_normalized',
|
|
1018
|
-
NO_TIMEZONE: 'no_timezone'
|
|
1019
|
-
};
|
|
1020
|
-
const DATETIME_SUBTYPES_LABELS = {
|
|
1021
|
-
[DATETIME_SUBTYPES.DATE]: 'Date',
|
|
1022
|
-
[DATETIME_SUBTYPES.TIME]: 'Time',
|
|
1023
|
-
[DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
|
|
1024
|
-
};
|
|
1025
|
-
const TIME_SERIALISINGFORMAT_LABELS = {
|
|
1026
|
-
[TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
|
|
1027
|
-
[TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
|
|
1028
|
-
[TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
|
|
1029
|
-
};
|
|
1030
|
-
const DATETIME_SUBTYPE_PATH = ['subtype'];
|
|
1031
|
-
const DATE_LABEL_PATH = ['dateLabel'];
|
|
1032
|
-
const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
|
|
1033
|
-
const TIME_LABEL_PATH = ['timeLabel'];
|
|
1034
|
-
const TIME_USE24H_PATH = ['use24h'];
|
|
1035
|
-
const TIME_INTERVAL_PATH = ['timeInterval'];
|
|
1036
|
-
const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
|
|
1037
|
-
|
|
1038
|
-
// config ///////////////////
|
|
1039
|
-
|
|
1040
|
-
const VALUES_SOURCES = {
|
|
1041
|
-
STATIC: 'static',
|
|
1042
|
-
INPUT: 'input',
|
|
1043
|
-
EXPRESSION: 'expression'
|
|
1044
|
-
};
|
|
1045
|
-
const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
|
|
1046
|
-
const VALUES_SOURCES_LABELS = {
|
|
1047
|
-
[VALUES_SOURCES.STATIC]: 'Static',
|
|
1048
|
-
[VALUES_SOURCES.INPUT]: 'Input data',
|
|
1049
|
-
[VALUES_SOURCES.EXPRESSION]: 'Expression'
|
|
1050
|
-
};
|
|
1051
|
-
const VALUES_SOURCES_PATHS = {
|
|
1052
|
-
[VALUES_SOURCES.STATIC]: ['values'],
|
|
1053
|
-
[VALUES_SOURCES.INPUT]: ['valuesKey'],
|
|
1054
|
-
[VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
|
|
1055
|
-
};
|
|
1056
|
-
const VALUES_SOURCES_DEFAULTS = {
|
|
1057
|
-
[VALUES_SOURCES.STATIC]: [{
|
|
1058
|
-
label: 'Value',
|
|
1059
|
-
value: 'value'
|
|
1060
|
-
}],
|
|
1061
|
-
[VALUES_SOURCES.INPUT]: '',
|
|
1062
|
-
[VALUES_SOURCES.EXPRESSION]: '='
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
|
-
// helpers ///////////////////
|
|
1066
|
-
|
|
1067
|
-
function getValuesSource(field) {
|
|
1068
|
-
for (const source of Object.values(VALUES_SOURCES)) {
|
|
1069
|
-
if (minDash.get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
|
|
1070
|
-
return source;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
return VALUES_SOURCE_DEFAULT;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
function createInjector(bootstrapModules) {
|
|
1077
|
-
const injector = new didi.Injector(bootstrapModules);
|
|
1078
|
-
injector.init();
|
|
1079
|
-
return injector;
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
/**
|
|
1083
|
-
* @param {string?} prefix
|
|
1084
|
-
*
|
|
1085
|
-
* @returns Element
|
|
1086
|
-
*/
|
|
1087
|
-
function createFormContainer(prefix = 'fjs') {
|
|
1088
|
-
const container = document.createElement('div');
|
|
1089
|
-
container.classList.add(`${prefix}-container`);
|
|
1090
|
-
return container;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
|
|
1094
|
-
const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
|
|
1095
|
-
function findErrors(errors, path) {
|
|
1096
|
-
return errors[pathStringify(path)];
|
|
1097
|
-
}
|
|
1098
|
-
function isRequired(field) {
|
|
1099
|
-
return field.required;
|
|
1100
|
-
}
|
|
1101
|
-
function pathParse(path) {
|
|
1102
|
-
if (!path) {
|
|
1103
|
-
return [];
|
|
1104
|
-
}
|
|
1105
|
-
return path.split('.').map(key => {
|
|
1106
|
-
return isNaN(parseInt(key)) ? key : parseInt(key);
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
function pathsEqual(a, b) {
|
|
1110
|
-
return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
|
|
1111
|
-
}
|
|
1112
|
-
function pathStringify(path) {
|
|
1113
|
-
if (!path) {
|
|
1114
|
-
return '';
|
|
1115
|
-
}
|
|
1116
|
-
return path.join('.');
|
|
1117
|
-
}
|
|
1118
|
-
const indices = {};
|
|
1119
|
-
function generateIndexForType(type) {
|
|
1120
|
-
if (type in indices) {
|
|
1121
|
-
indices[type]++;
|
|
1122
|
-
} else {
|
|
1123
|
-
indices[type] = 1;
|
|
1124
|
-
}
|
|
1125
|
-
return indices[type];
|
|
1126
|
-
}
|
|
1127
|
-
function generateIdForType(type) {
|
|
1128
|
-
return `${type}${generateIndexForType(type)}`;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
/**
|
|
1132
|
-
* @template T
|
|
1133
|
-
* @param {T} data
|
|
1134
|
-
* @param {(this: any, key: string, value: any) => any} [replacer]
|
|
1135
|
-
* @return {T}
|
|
1136
|
-
*/
|
|
1137
|
-
function clone(data, replacer) {
|
|
1138
|
-
return JSON.parse(JSON.stringify(data, replacer));
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
/**
|
|
1142
|
-
* Parse the schema for input variables a form might make use of
|
|
1143
|
-
*
|
|
1144
|
-
* @param {any} schema
|
|
1145
|
-
*
|
|
1146
|
-
* @return {string[]}
|
|
1147
|
-
*/
|
|
1148
|
-
function getSchemaVariables(schema, options = {}) {
|
|
1149
|
-
const {
|
|
1150
|
-
expressionLanguage = new FeelExpressionLanguage(null),
|
|
1151
|
-
templating = new FeelersTemplating(),
|
|
1152
|
-
inputs = true,
|
|
1153
|
-
outputs = true
|
|
1154
|
-
} = options;
|
|
1155
|
-
if (!schema.components) {
|
|
1156
|
-
return [];
|
|
1157
|
-
}
|
|
1158
|
-
const variables = schema.components.reduce((variables, component) => {
|
|
1159
|
-
const {
|
|
1160
|
-
key,
|
|
1161
|
-
valuesKey,
|
|
1162
|
-
type
|
|
1163
|
-
} = component;
|
|
1164
|
-
if (['button'].includes(type)) {
|
|
1165
|
-
return variables;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
// collect bi-directional variables
|
|
1169
|
-
if (inputs || outputs) {
|
|
1170
|
-
if (key) {
|
|
1171
|
-
variables = [...variables, key];
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
// collect input-only variables
|
|
1176
|
-
if (inputs) {
|
|
1177
|
-
if (valuesKey) {
|
|
1178
|
-
variables = [...variables, valuesKey];
|
|
1179
|
-
}
|
|
1180
|
-
EXPRESSION_PROPERTIES.forEach(prop => {
|
|
1181
|
-
const property = minDash.get(component, prop.split('.'));
|
|
1182
|
-
if (property && expressionLanguage.isExpression(property)) {
|
|
1183
|
-
const expressionVariables = expressionLanguage.getVariableNames(property, {
|
|
1184
|
-
type: 'expression'
|
|
1185
|
-
});
|
|
1186
|
-
variables = [...variables, ...expressionVariables];
|
|
1187
|
-
}
|
|
1188
|
-
});
|
|
1189
|
-
TEMPLATE_PROPERTIES.forEach(prop => {
|
|
1190
|
-
const property = minDash.get(component, prop.split('.'));
|
|
1191
|
-
if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
|
|
1192
|
-
const templateVariables = templating.getVariableNames(property);
|
|
1193
|
-
variables = [...variables, ...templateVariables];
|
|
1194
|
-
}
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
return variables.filter(variable => variable !== undefined || variable !== null);
|
|
1198
|
-
}, []);
|
|
1199
|
-
|
|
1200
|
-
// remove duplicates
|
|
1201
|
-
return Array.from(new Set(variables));
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
1225
|
class UpdateFieldValidationHandler {
|
|
1205
1226
|
constructor(form, validator) {
|
|
1206
1227
|
this._form = form;
|
|
@@ -1211,15 +1232,12 @@ class UpdateFieldValidationHandler {
|
|
|
1211
1232
|
field,
|
|
1212
1233
|
value
|
|
1213
1234
|
} = context;
|
|
1214
|
-
const {
|
|
1215
|
-
_path
|
|
1216
|
-
} = field;
|
|
1217
1235
|
const {
|
|
1218
1236
|
errors
|
|
1219
1237
|
} = this._form._getState();
|
|
1220
1238
|
context.oldErrors = clone(errors);
|
|
1221
1239
|
const fieldErrors = this._validator.validateField(field, value);
|
|
1222
|
-
const updatedErrors = minDash.set(errors, [
|
|
1240
|
+
const updatedErrors = minDash.set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
1223
1241
|
this._form._setState({
|
|
1224
1242
|
errors: updatedErrors
|
|
1225
1243
|
});
|
|
@@ -1868,8 +1886,8 @@ Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
|
|
|
1868
1886
|
|
|
1869
1887
|
// helpers //////////
|
|
1870
1888
|
|
|
1871
|
-
/**
|
|
1872
|
-
* Helper function to evaluate optional FEEL validation values.
|
|
1889
|
+
/**
|
|
1890
|
+
* Helper function to evaluate optional FEEL validation values.
|
|
1873
1891
|
*/
|
|
1874
1892
|
function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
|
|
1875
1893
|
const evaluatedValidate = {
|
|
@@ -1902,72 +1920,415 @@ function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form
|
|
|
1902
1920
|
return evaluatedValidate;
|
|
1903
1921
|
}
|
|
1904
1922
|
|
|
1905
|
-
class
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1923
|
+
class Importer {
|
|
1924
|
+
/**
|
|
1925
|
+
* @constructor
|
|
1926
|
+
* @param { import('./FormFieldRegistry').default } formFieldRegistry
|
|
1927
|
+
* @param { import('./PathRegistry').default } pathRegistry
|
|
1928
|
+
* @param { import('./FieldFactory').default } fieldFactory
|
|
1929
|
+
* @param { import('./FormLayouter').default } formLayouter
|
|
1930
|
+
*/
|
|
1931
|
+
constructor(formFieldRegistry, pathRegistry, fieldFactory, formLayouter) {
|
|
1932
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
1933
|
+
this._pathRegistry = pathRegistry;
|
|
1934
|
+
this._fieldFactory = fieldFactory;
|
|
1935
|
+
this._formLayouter = formLayouter;
|
|
1912
1936
|
}
|
|
1913
|
-
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* Import schema creating rows, fields, attaching additional
|
|
1940
|
+
* information to each field and adding fields to the
|
|
1941
|
+
* field registry.
|
|
1942
|
+
*
|
|
1943
|
+
* Additional information attached:
|
|
1944
|
+
*
|
|
1945
|
+
* * `id` (unless present)
|
|
1946
|
+
* * `_parent`
|
|
1947
|
+
* * `_path`
|
|
1948
|
+
*
|
|
1949
|
+
* @param {any} schema
|
|
1950
|
+
*
|
|
1951
|
+
* @typedef {{ warnings: Error[], schema: any }} ImportResult
|
|
1952
|
+
* @returns {ImportResult}
|
|
1953
|
+
*/
|
|
1954
|
+
importSchema(schema) {
|
|
1955
|
+
// TODO: Add warnings
|
|
1956
|
+
const warnings = [];
|
|
1957
|
+
try {
|
|
1958
|
+
this._cleanup();
|
|
1959
|
+
const importedSchema = this.importFormField(clone(schema));
|
|
1960
|
+
this._formLayouter.calculateLayout(clone(importedSchema));
|
|
1961
|
+
return {
|
|
1962
|
+
schema: importedSchema,
|
|
1963
|
+
warnings
|
|
1964
|
+
};
|
|
1965
|
+
} catch (err) {
|
|
1966
|
+
this._cleanup();
|
|
1967
|
+
err.warnings = warnings;
|
|
1968
|
+
throw err;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
_cleanup() {
|
|
1972
|
+
this._formLayouter.clear();
|
|
1973
|
+
this._formFieldRegistry.clear();
|
|
1974
|
+
this._pathRegistry.clear();
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
/**
|
|
1978
|
+
* @param {{[x: string]: any}} fieldAttrs
|
|
1979
|
+
* @param {String} [parentId]
|
|
1980
|
+
* @param {number} [index]
|
|
1981
|
+
*
|
|
1982
|
+
* @return {any} field
|
|
1983
|
+
*/
|
|
1984
|
+
importFormField(fieldAttrs, parentId, index) {
|
|
1914
1985
|
const {
|
|
1915
|
-
|
|
1916
|
-
} =
|
|
1917
|
-
|
|
1918
|
-
|
|
1986
|
+
components
|
|
1987
|
+
} = fieldAttrs;
|
|
1988
|
+
let parent, path;
|
|
1989
|
+
if (parentId) {
|
|
1990
|
+
parent = this._formFieldRegistry.get(parentId);
|
|
1919
1991
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1992
|
+
|
|
1993
|
+
// set form field path
|
|
1994
|
+
path = parent ? [...parent._path, 'components', index] : [];
|
|
1995
|
+
const field = this._fieldFactory.create({
|
|
1996
|
+
...fieldAttrs,
|
|
1997
|
+
_path: path,
|
|
1998
|
+
_parent: parentId
|
|
1999
|
+
}, false);
|
|
2000
|
+
this._formFieldRegistry.add(field);
|
|
2001
|
+
if (components) {
|
|
2002
|
+
field.components = this.importFormFields(components, field.id);
|
|
2003
|
+
}
|
|
2004
|
+
return field;
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
/**
|
|
2008
|
+
* @param {Array<any>} components
|
|
2009
|
+
* @param {string} parentId
|
|
2010
|
+
*
|
|
2011
|
+
* @return {Array<any>} imported components
|
|
2012
|
+
*/
|
|
2013
|
+
importFormFields(components, parentId) {
|
|
2014
|
+
return components.map((component, index) => {
|
|
2015
|
+
return this.importFormField(component, parentId, index);
|
|
1922
2016
|
});
|
|
1923
|
-
this._formFields[id] = formField;
|
|
1924
2017
|
}
|
|
1925
|
-
|
|
2018
|
+
}
|
|
2019
|
+
Importer.$inject = ['formFieldRegistry', 'pathRegistry', 'fieldFactory', 'formLayouter'];
|
|
2020
|
+
|
|
2021
|
+
class FieldFactory {
|
|
2022
|
+
/**
|
|
2023
|
+
* @constructor
|
|
2024
|
+
*
|
|
2025
|
+
* @param formFieldRegistry
|
|
2026
|
+
* @param formFields
|
|
2027
|
+
*/
|
|
2028
|
+
constructor(formFieldRegistry, pathRegistry, formFields) {
|
|
2029
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
2030
|
+
this._pathRegistry = pathRegistry;
|
|
2031
|
+
this._formFields = formFields;
|
|
2032
|
+
}
|
|
2033
|
+
create(attrs, applyDefaults = true) {
|
|
1926
2034
|
const {
|
|
1927
|
-
id
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2035
|
+
id,
|
|
2036
|
+
type,
|
|
2037
|
+
key,
|
|
2038
|
+
path,
|
|
2039
|
+
_parent
|
|
2040
|
+
} = attrs;
|
|
2041
|
+
const fieldDefinition = this._formFields.get(type);
|
|
2042
|
+
if (!fieldDefinition) {
|
|
2043
|
+
throw new Error(`form field of type <${type}> not supported`);
|
|
1931
2044
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
2045
|
+
const {
|
|
2046
|
+
config
|
|
2047
|
+
} = fieldDefinition;
|
|
2048
|
+
if (!config) {
|
|
2049
|
+
throw new Error(`form field of type <${type}> has no config`);
|
|
2050
|
+
}
|
|
2051
|
+
if (id && this._formFieldRegistry._ids.assigned(id)) {
|
|
2052
|
+
throw new Error(`form field with id <${id}> already exists`);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// ensure that we can claim the path
|
|
2056
|
+
|
|
2057
|
+
const parent = _parent && this._formFieldRegistry.get(_parent);
|
|
2058
|
+
const parentPath = parent && this._pathRegistry.getValuePath(parent) || [];
|
|
2059
|
+
if (config.keyed && key && !this._pathRegistry.canClaimPath([...parentPath, ...key.split('.')], true)) {
|
|
2060
|
+
throw new Error(`binding path '${[...parentPath, key].join('.')}' is already claimed`);
|
|
2061
|
+
}
|
|
2062
|
+
if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], false)) {
|
|
2063
|
+
throw new Error(`binding path '${[...parentPath, ...path.split('.')].join('.')}' is already claimed`);
|
|
2064
|
+
}
|
|
2065
|
+
const labelAttrs = applyDefaults && config.label ? {
|
|
2066
|
+
label: config.label
|
|
2067
|
+
} : {};
|
|
2068
|
+
const field = config.create({
|
|
2069
|
+
...labelAttrs,
|
|
2070
|
+
...attrs
|
|
1934
2071
|
});
|
|
1935
|
-
|
|
2072
|
+
this._ensureId(field);
|
|
2073
|
+
if (config.keyed) {
|
|
2074
|
+
this._ensureKey(field);
|
|
2075
|
+
}
|
|
2076
|
+
if (config.pathed && path) {
|
|
2077
|
+
this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), false);
|
|
2078
|
+
}
|
|
2079
|
+
return field;
|
|
1936
2080
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
2081
|
+
_ensureId(field) {
|
|
2082
|
+
if (field.id) {
|
|
2083
|
+
this._formFieldRegistry._ids.claim(field.id, field);
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
let prefix = 'Field';
|
|
2087
|
+
if (field.type === 'default') {
|
|
2088
|
+
prefix = 'Form';
|
|
2089
|
+
}
|
|
2090
|
+
field.id = this._formFieldRegistry._ids.nextPrefixed(`${prefix}_`, field);
|
|
1939
2091
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
2092
|
+
_ensureKey(field) {
|
|
2093
|
+
if (!field.key) {
|
|
2094
|
+
let random;
|
|
2095
|
+
const parent = this._formFieldRegistry.get(field._parent);
|
|
2096
|
+
|
|
2097
|
+
// ensure key uniqueness at level
|
|
2098
|
+
do {
|
|
2099
|
+
random = Math.random().toString(36).substring(7);
|
|
2100
|
+
} while (parent && parent.components.some(child => child.key === random));
|
|
2101
|
+
field.key = `${field.type}_${random}`;
|
|
2102
|
+
}
|
|
2103
|
+
this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), true);
|
|
1942
2104
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
2105
|
+
}
|
|
2106
|
+
FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
|
|
2107
|
+
|
|
2108
|
+
/**
|
|
2109
|
+
* The PathRegistry class manages a hierarchical structure of paths associated with form fields.
|
|
2110
|
+
* It enables claiming, unclaiming, and validating paths within this structure.
|
|
2111
|
+
*
|
|
2112
|
+
* Example Tree Structure:
|
|
2113
|
+
*
|
|
2114
|
+
* [
|
|
2115
|
+
* {
|
|
2116
|
+
* segment: 'root',
|
|
2117
|
+
* claimCount: 1,
|
|
2118
|
+
* children: [
|
|
2119
|
+
* {
|
|
2120
|
+
* segment: 'child1',
|
|
2121
|
+
* claimCount: 2,
|
|
2122
|
+
* children: null // A leaf node (closed path)
|
|
2123
|
+
* },
|
|
2124
|
+
* {
|
|
2125
|
+
* segment: 'child2',
|
|
2126
|
+
* claimCount: 1,
|
|
2127
|
+
* children: [
|
|
2128
|
+
* {
|
|
2129
|
+
* segment: 'subChild1',
|
|
2130
|
+
* claimCount: 1,
|
|
2131
|
+
* children: [] // An open node (open path)
|
|
2132
|
+
* }
|
|
2133
|
+
* ]
|
|
2134
|
+
* }
|
|
2135
|
+
* ]
|
|
2136
|
+
* }
|
|
2137
|
+
* ]
|
|
2138
|
+
*/
|
|
2139
|
+
class PathRegistry {
|
|
2140
|
+
constructor(formFieldRegistry, formFields) {
|
|
2141
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
2142
|
+
this._formFields = formFields;
|
|
2143
|
+
this._dataPaths = [];
|
|
2144
|
+
}
|
|
2145
|
+
canClaimPath(path, closed = false) {
|
|
2146
|
+
let node = {
|
|
2147
|
+
children: this._dataPaths
|
|
2148
|
+
};
|
|
2149
|
+
for (const segment of path) {
|
|
2150
|
+
node = _getNextSegment(node, segment);
|
|
2151
|
+
|
|
2152
|
+
// if no node at that path, we can claim it no matter what
|
|
2153
|
+
if (!node) {
|
|
2154
|
+
return true;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// if we reach a leaf node, definitely not claimable
|
|
2158
|
+
if (node.children === null) {
|
|
2159
|
+
return false;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
// if after all segments we reach a node with children, we can claim it only openly
|
|
2164
|
+
return !closed;
|
|
2165
|
+
}
|
|
2166
|
+
claimPath(path, closed = false) {
|
|
2167
|
+
if (!this.canClaimPath(path, closed)) {
|
|
2168
|
+
throw new Error(`cannot claim path '${path.join('.')}'`);
|
|
2169
|
+
}
|
|
2170
|
+
let node = {
|
|
2171
|
+
children: this._dataPaths
|
|
2172
|
+
};
|
|
2173
|
+
for (const segment of path) {
|
|
2174
|
+
let child = _getNextSegment(node, segment);
|
|
2175
|
+
if (!child) {
|
|
2176
|
+
child = {
|
|
2177
|
+
segment,
|
|
2178
|
+
claimCount: 1,
|
|
2179
|
+
children: []
|
|
2180
|
+
};
|
|
2181
|
+
node.children.push(child);
|
|
2182
|
+
} else {
|
|
2183
|
+
child.claimCount++;
|
|
2184
|
+
}
|
|
2185
|
+
node = child;
|
|
2186
|
+
}
|
|
2187
|
+
if (closed) {
|
|
2188
|
+
node.children = null;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
unclaimPath(path) {
|
|
2192
|
+
// verification Pass
|
|
2193
|
+
let node = {
|
|
2194
|
+
children: this._dataPaths
|
|
2195
|
+
};
|
|
2196
|
+
for (const segment of path) {
|
|
2197
|
+
const child = _getNextSegment(node, segment);
|
|
2198
|
+
if (!child) {
|
|
2199
|
+
throw new Error(`no open path found for '${path.join('.')}'`);
|
|
2200
|
+
}
|
|
2201
|
+
node = child;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
// mutation Pass
|
|
2205
|
+
node = {
|
|
2206
|
+
children: this._dataPaths
|
|
2207
|
+
};
|
|
2208
|
+
for (const segment of path) {
|
|
2209
|
+
const child = _getNextSegment(node, segment);
|
|
2210
|
+
child.claimCount--;
|
|
2211
|
+
if (child.claimCount === 0) {
|
|
2212
|
+
node.children.splice(node.children.indexOf(child), 1);
|
|
2213
|
+
break; // Abort early if claimCount reaches zero
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
node = child;
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/**
|
|
2221
|
+
* Applies a function (fn) recursively on a given field and its children.
|
|
2222
|
+
*
|
|
2223
|
+
* - `field`: Starting field object.
|
|
2224
|
+
* - `fn`: Function to apply.
|
|
2225
|
+
* - `context`: Optional object for passing data between calls.
|
|
2226
|
+
*
|
|
2227
|
+
* Stops early if `fn` returns `false`. Useful for traversing the form field tree.
|
|
2228
|
+
*
|
|
2229
|
+
* @returns {boolean} Success status based on function execution.
|
|
2230
|
+
*/
|
|
2231
|
+
executeRecursivelyOnFields(field, fn, context = {}) {
|
|
2232
|
+
let result = true;
|
|
2233
|
+
const formFieldConfig = this._formFields.get(field.type).config;
|
|
2234
|
+
if (formFieldConfig.keyed) {
|
|
2235
|
+
const callResult = fn({
|
|
2236
|
+
field,
|
|
2237
|
+
isClosed: true,
|
|
2238
|
+
context
|
|
2239
|
+
});
|
|
2240
|
+
return result && callResult;
|
|
2241
|
+
} else if (formFieldConfig.pathed) {
|
|
2242
|
+
const callResult = fn({
|
|
2243
|
+
field,
|
|
2244
|
+
isClosed: false,
|
|
2245
|
+
context
|
|
2246
|
+
});
|
|
2247
|
+
result = result && callResult;
|
|
2248
|
+
}
|
|
2249
|
+
if (field.components) {
|
|
2250
|
+
for (const child of field.components) {
|
|
2251
|
+
const callResult = this.executeRecursivelyOnFields(child, fn, clone(context));
|
|
2252
|
+
result = result && callResult;
|
|
2253
|
+
|
|
2254
|
+
// only stop executing if false is specifically returned, not if undefined
|
|
2255
|
+
if (result === false) {
|
|
2256
|
+
return result;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
return result;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
/**
|
|
2264
|
+
* Generates an array representing the binding path to an underlying data object for a form field.
|
|
2265
|
+
*
|
|
2266
|
+
* @param {Object} field - The field object with properties: `key`, `path`, `id`, and optionally `_parent`.
|
|
2267
|
+
* @param {Object} [options={}] - Configuration options.
|
|
2268
|
+
* @param {Object} [options.replacements={}] - A map of field IDs to alternative path arrays.
|
|
2269
|
+
* @param {Object} [options.cutoffNode] - The ID of the parent field at which to stop generating the path.
|
|
2270
|
+
*
|
|
2271
|
+
* @returns {(Array<string>|undefined)} An array of strings representing the binding path, or undefined if not determinable.
|
|
2272
|
+
*/
|
|
2273
|
+
getValuePath(field, options = {}) {
|
|
2274
|
+
const {
|
|
2275
|
+
replacements = {},
|
|
2276
|
+
cutoffNode = null
|
|
2277
|
+
} = options;
|
|
2278
|
+
let localValuePath = [];
|
|
2279
|
+
const hasReplacement = Object.prototype.hasOwnProperty.call(replacements, field.id);
|
|
2280
|
+
const formFieldConfig = this._formFields.get(field.type).config;
|
|
2281
|
+
if (hasReplacement) {
|
|
2282
|
+
const replacement = replacements[field.id];
|
|
2283
|
+
if (replacement === null || replacement === undefined || replacement === '') {
|
|
2284
|
+
localValuePath = [];
|
|
2285
|
+
} else if (typeof replacement === 'string') {
|
|
2286
|
+
localValuePath = replacement.split('.');
|
|
2287
|
+
} else if (Array.isArray(replacement)) {
|
|
2288
|
+
localValuePath = replacement;
|
|
2289
|
+
} else {
|
|
2290
|
+
throw new Error(`replacements for field ${field.id} must be a string, array or null/undefined`);
|
|
2291
|
+
}
|
|
2292
|
+
} else if (formFieldConfig.keyed) {
|
|
2293
|
+
localValuePath = field.key.split('.');
|
|
2294
|
+
} else if (formFieldConfig.pathed && field.path) {
|
|
2295
|
+
localValuePath = field.path.split('.');
|
|
2296
|
+
}
|
|
2297
|
+
if (field._parent && field._parent !== cutoffNode) {
|
|
2298
|
+
const parent = this._formFieldRegistry.get(field._parent);
|
|
2299
|
+
return [...(this.getValuePath(parent, options) || []), ...localValuePath];
|
|
2300
|
+
}
|
|
2301
|
+
return localValuePath;
|
|
1945
2302
|
}
|
|
1946
2303
|
clear() {
|
|
1947
|
-
this.
|
|
1948
|
-
this._ids.clear();
|
|
1949
|
-
this._keys.clear();
|
|
2304
|
+
this._dataPaths = [];
|
|
1950
2305
|
}
|
|
1951
2306
|
}
|
|
1952
|
-
|
|
2307
|
+
const _getNextSegment = (node, segment) => {
|
|
2308
|
+
if (minDash.isArray(node.children)) {
|
|
2309
|
+
return node.children.find(node => node.segment === segment) || null;
|
|
2310
|
+
}
|
|
2311
|
+
return null;
|
|
2312
|
+
};
|
|
2313
|
+
PathRegistry.$inject = ['formFieldRegistry', 'formFields'];
|
|
1953
2314
|
|
|
1954
|
-
/**
|
|
1955
|
-
* @typedef { { id: String, components: Array<String> } } FormRow
|
|
1956
|
-
* @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
|
|
2315
|
+
/**
|
|
2316
|
+
* @typedef { { id: String, components: Array<String> } } FormRow
|
|
2317
|
+
* @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
|
|
1957
2318
|
*/
|
|
1958
2319
|
|
|
1959
|
-
/**
|
|
1960
|
-
* Maintains the Form layout in a given structure, for example
|
|
1961
|
-
*
|
|
1962
|
-
* [
|
|
1963
|
-
* {
|
|
1964
|
-
* formFieldId: 'FormField_1',
|
|
1965
|
-
* rows: [
|
|
1966
|
-
* { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
|
|
1967
|
-
* ]
|
|
1968
|
-
* }
|
|
1969
|
-
* ]
|
|
1970
|
-
*
|
|
2320
|
+
/**
|
|
2321
|
+
* Maintains the Form layout in a given structure, for example
|
|
2322
|
+
*
|
|
2323
|
+
* [
|
|
2324
|
+
* {
|
|
2325
|
+
* formFieldId: 'FormField_1',
|
|
2326
|
+
* rows: [
|
|
2327
|
+
* { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
|
|
2328
|
+
* ]
|
|
2329
|
+
* }
|
|
2330
|
+
* ]
|
|
2331
|
+
*
|
|
1971
2332
|
*/
|
|
1972
2333
|
class FormLayouter {
|
|
1973
2334
|
constructor(eventBus) {
|
|
@@ -1977,8 +2338,8 @@ class FormLayouter {
|
|
|
1977
2338
|
this._eventBus = eventBus;
|
|
1978
2339
|
}
|
|
1979
2340
|
|
|
1980
|
-
/**
|
|
1981
|
-
* @param {FormRow} row
|
|
2341
|
+
/**
|
|
2342
|
+
* @param {FormRow} row
|
|
1982
2343
|
*/
|
|
1983
2344
|
addRow(formFieldId, row) {
|
|
1984
2345
|
let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
|
|
@@ -1992,18 +2353,18 @@ class FormLayouter {
|
|
|
1992
2353
|
rowsPerComponent.rows.push(row);
|
|
1993
2354
|
}
|
|
1994
2355
|
|
|
1995
|
-
/**
|
|
1996
|
-
* @param {String} id
|
|
1997
|
-
* @returns {FormRow}
|
|
2356
|
+
/**
|
|
2357
|
+
* @param {String} id
|
|
2358
|
+
* @returns {FormRow}
|
|
1998
2359
|
*/
|
|
1999
2360
|
getRow(id) {
|
|
2000
2361
|
const rows = allRows(this._rows);
|
|
2001
2362
|
return rows.find(r => r.id === id);
|
|
2002
2363
|
}
|
|
2003
2364
|
|
|
2004
|
-
/**
|
|
2005
|
-
* @param {any} formField
|
|
2006
|
-
* @returns {FormRow}
|
|
2365
|
+
/**
|
|
2366
|
+
* @param {any} formField
|
|
2367
|
+
* @returns {FormRow}
|
|
2007
2368
|
*/
|
|
2008
2369
|
getRowForField(formField) {
|
|
2009
2370
|
return allRows(this._rows).find(r => {
|
|
@@ -2014,9 +2375,9 @@ class FormLayouter {
|
|
|
2014
2375
|
});
|
|
2015
2376
|
}
|
|
2016
2377
|
|
|
2017
|
-
/**
|
|
2018
|
-
* @param {String} formFieldId
|
|
2019
|
-
* @returns { Array<FormRow> }
|
|
2378
|
+
/**
|
|
2379
|
+
* @param {String} formFieldId
|
|
2380
|
+
* @returns { Array<FormRow> }
|
|
2020
2381
|
*/
|
|
2021
2382
|
getRows(formFieldId) {
|
|
2022
2383
|
const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
|
|
@@ -2026,22 +2387,22 @@ class FormLayouter {
|
|
|
2026
2387
|
return rowsForField.rows;
|
|
2027
2388
|
}
|
|
2028
2389
|
|
|
2029
|
-
/**
|
|
2030
|
-
* @returns {string}
|
|
2390
|
+
/**
|
|
2391
|
+
* @returns {string}
|
|
2031
2392
|
*/
|
|
2032
2393
|
nextRowId() {
|
|
2033
2394
|
return this._ids.nextPrefixed('Row_');
|
|
2034
2395
|
}
|
|
2035
2396
|
|
|
2036
|
-
/**
|
|
2037
|
-
* @param {any} formField
|
|
2397
|
+
/**
|
|
2398
|
+
* @param {any} formField
|
|
2038
2399
|
*/
|
|
2039
2400
|
calculateLayout(formField) {
|
|
2040
2401
|
const {
|
|
2041
2402
|
type,
|
|
2042
2403
|
components
|
|
2043
2404
|
} = formField;
|
|
2044
|
-
if (type !== 'default' || !components) {
|
|
2405
|
+
if (type !== 'default' && type !== 'group' || !components) {
|
|
2045
2406
|
return;
|
|
2046
2407
|
}
|
|
2047
2408
|
|
|
@@ -2088,155 +2449,60 @@ function groupByRow(components, ids) {
|
|
|
2088
2449
|
});
|
|
2089
2450
|
}
|
|
2090
2451
|
|
|
2091
|
-
/**
|
|
2092
|
-
* @param {Array<FormRows>} formRows
|
|
2093
|
-
* @returns {Array<FormRow>}
|
|
2452
|
+
/**
|
|
2453
|
+
* @param {Array<FormRows>} formRows
|
|
2454
|
+
* @returns {Array<FormRow>}
|
|
2094
2455
|
*/
|
|
2095
2456
|
function allRows(formRows) {
|
|
2096
2457
|
return minDash.flatten(formRows.map(c => c.rows));
|
|
2097
2458
|
}
|
|
2098
2459
|
|
|
2099
|
-
class
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
*/
|
|
2106
|
-
constructor(formFieldRegistry, formFields, formLayouter) {
|
|
2107
|
-
this._formFieldRegistry = formFieldRegistry;
|
|
2108
|
-
this._formFields = formFields;
|
|
2109
|
-
this._formLayouter = formLayouter;
|
|
2460
|
+
class FormFieldRegistry {
|
|
2461
|
+
constructor(eventBus) {
|
|
2462
|
+
this._eventBus = eventBus;
|
|
2463
|
+
this._formFields = {};
|
|
2464
|
+
eventBus.on('form.clear', () => this.clear());
|
|
2465
|
+
this._ids = new Ids([32, 36, 1]);
|
|
2110
2466
|
}
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
* @param {any} schema
|
|
2118
|
-
* @param {any} [data]
|
|
2119
|
-
*
|
|
2120
|
-
* @return { { warnings: Array<any>, schema: any, data: any } }
|
|
2121
|
-
*/
|
|
2122
|
-
importSchema(schema, data = {}) {
|
|
2123
|
-
// TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
|
|
2124
|
-
const warnings = [];
|
|
2125
|
-
try {
|
|
2126
|
-
this._formLayouter.clear();
|
|
2127
|
-
const importedSchema = this.importFormField(clone(schema)),
|
|
2128
|
-
initializedData = this.initializeFieldValues(clone(data));
|
|
2129
|
-
this._formLayouter.calculateLayout(clone(importedSchema));
|
|
2130
|
-
return {
|
|
2131
|
-
warnings,
|
|
2132
|
-
schema: importedSchema,
|
|
2133
|
-
data: initializedData
|
|
2134
|
-
};
|
|
2135
|
-
} catch (err) {
|
|
2136
|
-
err.warnings = warnings;
|
|
2137
|
-
throw err;
|
|
2467
|
+
add(formField) {
|
|
2468
|
+
const {
|
|
2469
|
+
id
|
|
2470
|
+
} = formField;
|
|
2471
|
+
if (this._formFields[id]) {
|
|
2472
|
+
throw new Error(`form field with ID ${id} already exists`);
|
|
2138
2473
|
}
|
|
2474
|
+
this._eventBus.fire('formField.add', {
|
|
2475
|
+
formField
|
|
2476
|
+
});
|
|
2477
|
+
this._formFields[id] = formField;
|
|
2139
2478
|
}
|
|
2140
|
-
|
|
2141
|
-
/**
|
|
2142
|
-
* @param {any} formField
|
|
2143
|
-
* @param {string} [parentId]
|
|
2144
|
-
*
|
|
2145
|
-
* @return {any} importedField
|
|
2146
|
-
*/
|
|
2147
|
-
importFormField(formField, parentId) {
|
|
2479
|
+
remove(formField) {
|
|
2148
2480
|
const {
|
|
2149
|
-
|
|
2150
|
-
key,
|
|
2151
|
-
type,
|
|
2152
|
-
id = generateIdForType(type)
|
|
2481
|
+
id
|
|
2153
2482
|
} = formField;
|
|
2154
|
-
if (
|
|
2155
|
-
|
|
2156
|
-
formField._parent = parentId;
|
|
2157
|
-
}
|
|
2158
|
-
if (!this._formFields.get(type)) {
|
|
2159
|
-
throw new Error(`form field of type <${type}> not supported`);
|
|
2160
|
-
}
|
|
2161
|
-
if (key) {
|
|
2162
|
-
// validate <key> uniqueness
|
|
2163
|
-
if (this._formFieldRegistry._keys.assigned(key)) {
|
|
2164
|
-
throw new Error(`form field with key <${key}> already exists`);
|
|
2165
|
-
}
|
|
2166
|
-
this._formFieldRegistry._keys.claim(key, formField);
|
|
2167
|
-
|
|
2168
|
-
// TODO: buttons should not have key
|
|
2169
|
-
if (type !== 'button') {
|
|
2170
|
-
// set form field path
|
|
2171
|
-
formField._path = [key];
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
if (id) {
|
|
2175
|
-
// validate <id> uniqueness
|
|
2176
|
-
if (this._formFieldRegistry._ids.assigned(id)) {
|
|
2177
|
-
throw new Error(`form field with id <${id}> already exists`);
|
|
2178
|
-
}
|
|
2179
|
-
this._formFieldRegistry._ids.claim(id, formField);
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
// set form field ID
|
|
2183
|
-
formField.id = id;
|
|
2184
|
-
this._formFieldRegistry.add(formField);
|
|
2185
|
-
if (components) {
|
|
2186
|
-
this.importFormFields(components, id);
|
|
2483
|
+
if (!this._formFields[id]) {
|
|
2484
|
+
return;
|
|
2187
2485
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
importFormFields(components, parentId) {
|
|
2191
|
-
components.forEach(component => {
|
|
2192
|
-
this.importFormField(component, parentId);
|
|
2486
|
+
this._eventBus.fire('formField.remove', {
|
|
2487
|
+
formField
|
|
2193
2488
|
});
|
|
2489
|
+
delete this._formFields[id];
|
|
2490
|
+
}
|
|
2491
|
+
get(id) {
|
|
2492
|
+
return this._formFields[id];
|
|
2493
|
+
}
|
|
2494
|
+
getAll() {
|
|
2495
|
+
return Object.values(this._formFields);
|
|
2194
2496
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
initializeFieldValues(data) {
|
|
2202
|
-
return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
|
|
2203
|
-
const {
|
|
2204
|
-
defaultValue,
|
|
2205
|
-
_path,
|
|
2206
|
-
type
|
|
2207
|
-
} = formField;
|
|
2208
|
-
|
|
2209
|
-
// try to get value from data
|
|
2210
|
-
// if unavailable - try to get default value from form field
|
|
2211
|
-
// if unavailable - get empty value from form field
|
|
2212
|
-
|
|
2213
|
-
if (_path) {
|
|
2214
|
-
const {
|
|
2215
|
-
config: fieldConfig
|
|
2216
|
-
} = this._formFields.get(type);
|
|
2217
|
-
let valueData = minDash.get(data, _path);
|
|
2218
|
-
if (!minDash.isUndefined(valueData) && fieldConfig.sanitizeValue) {
|
|
2219
|
-
valueData = fieldConfig.sanitizeValue({
|
|
2220
|
-
formField,
|
|
2221
|
-
data,
|
|
2222
|
-
value: valueData
|
|
2223
|
-
});
|
|
2224
|
-
}
|
|
2225
|
-
const initializedFieldValue = !minDash.isUndefined(valueData) ? valueData : !minDash.isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
|
|
2226
|
-
initializedData = {
|
|
2227
|
-
...initializedData,
|
|
2228
|
-
[_path[0]]: initializedFieldValue
|
|
2229
|
-
};
|
|
2230
|
-
}
|
|
2231
|
-
return initializedData;
|
|
2232
|
-
}, data);
|
|
2497
|
+
forEach(callback) {
|
|
2498
|
+
this.getAll().forEach(formField => callback(formField));
|
|
2499
|
+
}
|
|
2500
|
+
clear() {
|
|
2501
|
+
this._formFields = {};
|
|
2502
|
+
this._ids.clear();
|
|
2233
2503
|
}
|
|
2234
2504
|
}
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
var importModule = {
|
|
2238
|
-
importer: ['type', Importer]
|
|
2239
|
-
};
|
|
2505
|
+
FormFieldRegistry.$inject = ['eventBus'];
|
|
2240
2506
|
|
|
2241
2507
|
function formFieldClasses(type, {
|
|
2242
2508
|
errors = [],
|
|
@@ -2291,7 +2557,7 @@ function Button(props) {
|
|
|
2291
2557
|
}
|
|
2292
2558
|
Button.config = {
|
|
2293
2559
|
type: type$c,
|
|
2294
|
-
keyed:
|
|
2560
|
+
keyed: false,
|
|
2295
2561
|
label: 'Button',
|
|
2296
2562
|
group: 'action',
|
|
2297
2563
|
create: (options = {}) => ({
|
|
@@ -2301,6 +2567,9 @@ Button.config = {
|
|
|
2301
2567
|
};
|
|
2302
2568
|
|
|
2303
2569
|
const FormRenderContext = preact.createContext({
|
|
2570
|
+
EmptyRoot: props => {
|
|
2571
|
+
return null;
|
|
2572
|
+
},
|
|
2304
2573
|
Empty: props => {
|
|
2305
2574
|
return null;
|
|
2306
2575
|
},
|
|
@@ -2330,15 +2599,19 @@ const FormRenderContext = preact.createContext({
|
|
|
2330
2599
|
class: props.class,
|
|
2331
2600
|
children: props.children
|
|
2332
2601
|
});
|
|
2602
|
+
},
|
|
2603
|
+
hoveredId: [],
|
|
2604
|
+
setHoveredId: newValue => {
|
|
2605
|
+
console.log(`setHoveredId not defined, called with '${newValue}'`);
|
|
2333
2606
|
}
|
|
2334
2607
|
});
|
|
2335
2608
|
var FormRenderContext$1 = FormRenderContext;
|
|
2336
2609
|
|
|
2337
|
-
/**
|
|
2338
|
-
* @param {string} type
|
|
2339
|
-
* @param {boolean} [strict]
|
|
2340
|
-
*
|
|
2341
|
-
* @returns {any}
|
|
2610
|
+
/**
|
|
2611
|
+
* @param {string} type
|
|
2612
|
+
* @param {boolean} [strict]
|
|
2613
|
+
*
|
|
2614
|
+
* @returns {any}
|
|
2342
2615
|
*/
|
|
2343
2616
|
function getService(type, strict) {}
|
|
2344
2617
|
const FormContext = preact.createContext({
|
|
@@ -2354,10 +2627,10 @@ function useService(type, strict) {
|
|
|
2354
2627
|
return getService(type, strict);
|
|
2355
2628
|
}
|
|
2356
2629
|
|
|
2357
|
-
/**
|
|
2358
|
-
* Returns the conditionally filtered data of a form reactively.
|
|
2359
|
-
* Memoised to minimize re-renders
|
|
2360
|
-
*
|
|
2630
|
+
/**
|
|
2631
|
+
* Returns the conditionally filtered data of a form reactively.
|
|
2632
|
+
* Memoised to minimize re-renders
|
|
2633
|
+
*
|
|
2361
2634
|
*/
|
|
2362
2635
|
function useFilteredFormData() {
|
|
2363
2636
|
const {
|
|
@@ -2374,12 +2647,12 @@ function useFilteredFormData() {
|
|
|
2374
2647
|
}, [conditionChecker, data, initialData]);
|
|
2375
2648
|
}
|
|
2376
2649
|
|
|
2377
|
-
/**
|
|
2378
|
-
* Evaluate if condition is met reactively based on the conditionChecker and form data.
|
|
2379
|
-
*
|
|
2380
|
-
* @param {string | undefined} condition
|
|
2381
|
-
*
|
|
2382
|
-
* @returns {boolean} true if condition is met or no condition or condition checker exists
|
|
2650
|
+
/**
|
|
2651
|
+
* Evaluate if condition is met reactively based on the conditionChecker and form data.
|
|
2652
|
+
*
|
|
2653
|
+
* @param {string | undefined} condition
|
|
2654
|
+
*
|
|
2655
|
+
* @returns {boolean} true if condition is met or no condition or condition checker exists
|
|
2383
2656
|
*/
|
|
2384
2657
|
function useCondition(condition) {
|
|
2385
2658
|
const conditionChecker = useService('conditionChecker', false);
|
|
@@ -2389,13 +2662,13 @@ function useCondition(condition) {
|
|
|
2389
2662
|
}, [conditionChecker, condition, filteredData]);
|
|
2390
2663
|
}
|
|
2391
2664
|
|
|
2392
|
-
/**
|
|
2393
|
-
* Evaluate a string reactively based on the expressionLanguage and form data.
|
|
2394
|
-
* If the string is not an expression, it is returned as is.
|
|
2395
|
-
* Memoised to minimize re-renders.
|
|
2396
|
-
*
|
|
2397
|
-
* @param {string} value
|
|
2398
|
-
*
|
|
2665
|
+
/**
|
|
2666
|
+
* Evaluate a string reactively based on the expressionLanguage and form data.
|
|
2667
|
+
* If the string is not an expression, it is returned as is.
|
|
2668
|
+
* Memoised to minimize re-renders.
|
|
2669
|
+
*
|
|
2670
|
+
* @param {string} value
|
|
2671
|
+
*
|
|
2399
2672
|
*/
|
|
2400
2673
|
function useExpressionEvaluation(value) {
|
|
2401
2674
|
const formData = useFilteredFormData();
|
|
@@ -2424,16 +2697,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
|
|
|
2424
2697
|
});
|
|
2425
2698
|
}
|
|
2426
2699
|
|
|
2427
|
-
/**
|
|
2428
|
-
* Retrieve readonly value of a form field, given it can be an
|
|
2429
|
-
* expression optionally or configured globally.
|
|
2430
|
-
*
|
|
2431
|
-
* @typedef { import('../../types').FormProperties } FormProperties
|
|
2432
|
-
*
|
|
2433
|
-
* @param {any} formField
|
|
2434
|
-
* @param {FormProperties} properties
|
|
2435
|
-
*
|
|
2436
|
-
* @returns {boolean}
|
|
2700
|
+
/**
|
|
2701
|
+
* Retrieve readonly value of a form field, given it can be an
|
|
2702
|
+
* expression optionally or configured globally.
|
|
2703
|
+
*
|
|
2704
|
+
* @typedef { import('../../types').FormProperties } FormProperties
|
|
2705
|
+
*
|
|
2706
|
+
* @param {any} formField
|
|
2707
|
+
* @param {FormProperties} properties
|
|
2708
|
+
*
|
|
2709
|
+
* @returns {boolean}
|
|
2437
2710
|
*/
|
|
2438
2711
|
function useReadonly(formField, properties = {}) {
|
|
2439
2712
|
const expressionLanguage = useService('expressionLanguage');
|
|
@@ -2451,16 +2724,16 @@ function useReadonly(formField, properties = {}) {
|
|
|
2451
2724
|
return readonly || false;
|
|
2452
2725
|
}
|
|
2453
2726
|
|
|
2454
|
-
/**
|
|
2455
|
-
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2456
|
-
* Memoised to minimize re-renders
|
|
2457
|
-
*
|
|
2458
|
-
* @param {string} value
|
|
2459
|
-
* @param {Object} options
|
|
2460
|
-
* @param {boolean} [options.debug = false]
|
|
2461
|
-
* @param {boolean} [options.strict = false]
|
|
2462
|
-
* @param {Function} [options.buildDebugString]
|
|
2463
|
-
*
|
|
2727
|
+
/**
|
|
2728
|
+
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2729
|
+
* Memoised to minimize re-renders
|
|
2730
|
+
*
|
|
2731
|
+
* @param {string} value
|
|
2732
|
+
* @param {Object} options
|
|
2733
|
+
* @param {boolean} [options.debug = false]
|
|
2734
|
+
* @param {boolean} [options.strict = false]
|
|
2735
|
+
* @param {Function} [options.buildDebugString]
|
|
2736
|
+
*
|
|
2464
2737
|
*/
|
|
2465
2738
|
function useTemplateEvaluation(value, options) {
|
|
2466
2739
|
const filteredData = useFilteredFormData();
|
|
@@ -2473,17 +2746,17 @@ function useTemplateEvaluation(value, options) {
|
|
|
2473
2746
|
}, [filteredData, templating, value, options]);
|
|
2474
2747
|
}
|
|
2475
2748
|
|
|
2476
|
-
/**
|
|
2477
|
-
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2478
|
-
* If the string contains multiple lines, only the first line is returned.
|
|
2479
|
-
* Memoised to minimize re-renders
|
|
2480
|
-
*
|
|
2481
|
-
* @param {string} value
|
|
2482
|
-
* @param {Object} [options]
|
|
2483
|
-
* @param {boolean} [options.debug = false]
|
|
2484
|
-
* @param {boolean} [options.strict = false]
|
|
2485
|
-
* @param {Function} [options.buildDebugString]
|
|
2486
|
-
*
|
|
2749
|
+
/**
|
|
2750
|
+
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2751
|
+
* If the string contains multiple lines, only the first line is returned.
|
|
2752
|
+
* Memoised to minimize re-renders
|
|
2753
|
+
*
|
|
2754
|
+
* @param {string} value
|
|
2755
|
+
* @param {Object} [options]
|
|
2756
|
+
* @param {boolean} [options.debug = false]
|
|
2757
|
+
* @param {boolean} [options.strict = false]
|
|
2758
|
+
* @param {Function} [options.buildDebugString]
|
|
2759
|
+
*
|
|
2487
2760
|
*/
|
|
2488
2761
|
function useSingleLineTemplateEvaluation(value, options = {}) {
|
|
2489
2762
|
const evaluatedTemplate = useTemplateEvaluation(value, options);
|
|
@@ -2671,9 +2944,24 @@ function _isReadableType(value) {
|
|
|
2671
2944
|
function _isValueSomething(value) {
|
|
2672
2945
|
return value || value === 0 || value === false;
|
|
2673
2946
|
}
|
|
2947
|
+
function createEmptyOptions(options = {}) {
|
|
2948
|
+
const defaults = {};
|
|
2674
2949
|
|
|
2675
|
-
|
|
2676
|
-
|
|
2950
|
+
// provide default values if valuesKey and valuesExpression are not set
|
|
2951
|
+
if (!options.valuesKey && !options.valuesExpression) {
|
|
2952
|
+
defaults.values = [{
|
|
2953
|
+
label: 'Value',
|
|
2954
|
+
value: 'value'
|
|
2955
|
+
}];
|
|
2956
|
+
}
|
|
2957
|
+
return {
|
|
2958
|
+
...defaults,
|
|
2959
|
+
...options
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
/**
|
|
2964
|
+
* @enum { String }
|
|
2677
2965
|
*/
|
|
2678
2966
|
const LOAD_STATES = {
|
|
2679
2967
|
LOADING: 'loading',
|
|
@@ -2681,17 +2969,17 @@ const LOAD_STATES = {
|
|
|
2681
2969
|
ERROR: 'error'
|
|
2682
2970
|
};
|
|
2683
2971
|
|
|
2684
|
-
/**
|
|
2685
|
-
* @typedef {Object} ValuesGetter
|
|
2686
|
-
* @property {Object[]} values - The values data
|
|
2687
|
-
* @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
|
|
2972
|
+
/**
|
|
2973
|
+
* @typedef {Object} ValuesGetter
|
|
2974
|
+
* @property {Object[]} values - The values data
|
|
2975
|
+
* @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
|
|
2688
2976
|
*/
|
|
2689
2977
|
|
|
2690
|
-
/**
|
|
2691
|
-
* A hook to load values for single and multiselect components.
|
|
2692
|
-
*
|
|
2693
|
-
* @param {Object} field - The form field to handle values for
|
|
2694
|
-
* @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
|
|
2978
|
+
/**
|
|
2979
|
+
* A hook to load values for single and multiselect components.
|
|
2980
|
+
*
|
|
2981
|
+
* @param {Object} field - The form field to handle values for
|
|
2982
|
+
* @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
|
|
2695
2983
|
*/
|
|
2696
2984
|
function useValuesAsync (field) {
|
|
2697
2985
|
const {
|
|
@@ -3047,21 +3335,7 @@ Checklist.config = {
|
|
|
3047
3335
|
group: 'selection',
|
|
3048
3336
|
emptyValue: [],
|
|
3049
3337
|
sanitizeValue: sanitizeMultiSelectValue,
|
|
3050
|
-
create:
|
|
3051
|
-
const defaults = {};
|
|
3052
|
-
|
|
3053
|
-
// provide default values if valuesKey isn't set
|
|
3054
|
-
if (!options.valuesKey) {
|
|
3055
|
-
defaults.values = [{
|
|
3056
|
-
label: 'Value',
|
|
3057
|
-
value: 'value'
|
|
3058
|
-
}];
|
|
3059
|
-
}
|
|
3060
|
-
return {
|
|
3061
|
-
...defaults,
|
|
3062
|
-
...options
|
|
3063
|
-
};
|
|
3064
|
-
}
|
|
3338
|
+
create: createEmptyOptions
|
|
3065
3339
|
};
|
|
3066
3340
|
|
|
3067
3341
|
const noop$1 = () => false;
|
|
@@ -3072,6 +3346,7 @@ function FormField(props) {
|
|
|
3072
3346
|
} = props;
|
|
3073
3347
|
const formFields = useService('formFields'),
|
|
3074
3348
|
viewerCommands = useService('viewerCommands', false),
|
|
3349
|
+
pathRegistry = useService('pathRegistry'),
|
|
3075
3350
|
form = useService('form');
|
|
3076
3351
|
const {
|
|
3077
3352
|
initialData,
|
|
@@ -3088,10 +3363,10 @@ function FormField(props) {
|
|
|
3088
3363
|
if (!FormFieldComponent) {
|
|
3089
3364
|
throw new Error(`cannot render field <${field.type}>`);
|
|
3090
3365
|
}
|
|
3091
|
-
const
|
|
3092
|
-
const
|
|
3093
|
-
const fieldErrors = findErrors(errors, field._path);
|
|
3366
|
+
const valuePath = hooks.useMemo(() => pathRegistry.getValuePath(field), [field, pathRegistry]);
|
|
3367
|
+
const initialValue = hooks.useMemo(() => minDash.get(initialData, valuePath), [initialData, valuePath]);
|
|
3094
3368
|
const readonly = useReadonly(field, properties);
|
|
3369
|
+
const value = minDash.get(data, valuePath);
|
|
3095
3370
|
|
|
3096
3371
|
// add precedence: global readonly > form field disabled
|
|
3097
3372
|
const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
|
|
@@ -3118,7 +3393,7 @@ function FormField(props) {
|
|
|
3118
3393
|
children: jsxRuntime.jsx(FormFieldComponent, {
|
|
3119
3394
|
...props,
|
|
3120
3395
|
disabled: disabled,
|
|
3121
|
-
errors:
|
|
3396
|
+
errors: errors[field.id],
|
|
3122
3397
|
onChange: disabled || readonly ? noop$1 : onChange,
|
|
3123
3398
|
onBlur: disabled || readonly ? noop$1 : onBlur,
|
|
3124
3399
|
readonly: readonly,
|
|
@@ -3128,14 +3403,14 @@ function FormField(props) {
|
|
|
3128
3403
|
});
|
|
3129
3404
|
}
|
|
3130
3405
|
|
|
3131
|
-
function
|
|
3406
|
+
function Grid(props) {
|
|
3132
3407
|
const {
|
|
3133
3408
|
Children,
|
|
3134
|
-
Empty,
|
|
3135
3409
|
Row
|
|
3136
3410
|
} = hooks.useContext(FormRenderContext$1);
|
|
3137
3411
|
const {
|
|
3138
|
-
field
|
|
3412
|
+
field,
|
|
3413
|
+
Empty
|
|
3139
3414
|
} = props;
|
|
3140
3415
|
const {
|
|
3141
3416
|
id,
|
|
@@ -3172,7 +3447,20 @@ function Default(props) {
|
|
|
3172
3447
|
}), components.length ? null : jsxRuntime.jsx(Empty, {})]
|
|
3173
3448
|
});
|
|
3174
3449
|
}
|
|
3175
|
-
|
|
3450
|
+
|
|
3451
|
+
function FormComponent$1(props) {
|
|
3452
|
+
const {
|
|
3453
|
+
EmptyRoot
|
|
3454
|
+
} = hooks.useContext(FormRenderContext$1);
|
|
3455
|
+
const fullProps = {
|
|
3456
|
+
...props,
|
|
3457
|
+
Empty: EmptyRoot
|
|
3458
|
+
};
|
|
3459
|
+
return jsxRuntime.jsx(Grid, {
|
|
3460
|
+
...fullProps
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3463
|
+
FormComponent$1.config = {
|
|
3176
3464
|
type: 'default',
|
|
3177
3465
|
keyed: false,
|
|
3178
3466
|
label: null,
|
|
@@ -3201,6 +3489,74 @@ var SvgCalendar = function SvgCalendar(props) {
|
|
|
3201
3489
|
};
|
|
3202
3490
|
var CalendarIcon = SvgCalendar;
|
|
3203
3491
|
|
|
3492
|
+
/**
|
|
3493
|
+
* Returns date format for the provided locale.
|
|
3494
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3495
|
+
*
|
|
3496
|
+
* @param {string} [locale] - The locale to get date format for.
|
|
3497
|
+
* @returns {string} The date format for the locale.
|
|
3498
|
+
*/
|
|
3499
|
+
function getLocaleDateFormat(locale = 'default') {
|
|
3500
|
+
const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(Date.UTC(2020, 5, 5)));
|
|
3501
|
+
return parts.map(part => {
|
|
3502
|
+
const len = part.value.length;
|
|
3503
|
+
switch (part.type) {
|
|
3504
|
+
case 'day':
|
|
3505
|
+
return 'd'.repeat(len);
|
|
3506
|
+
case 'month':
|
|
3507
|
+
return 'M'.repeat(len);
|
|
3508
|
+
case 'year':
|
|
3509
|
+
return 'y'.repeat(len);
|
|
3510
|
+
default:
|
|
3511
|
+
return part.value;
|
|
3512
|
+
}
|
|
3513
|
+
}).join('');
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
/**
|
|
3517
|
+
* Returns readable date format for the provided locale.
|
|
3518
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3519
|
+
*
|
|
3520
|
+
* @param {string} [locale] - The locale to get readable date format for.
|
|
3521
|
+
* @returns {string} The readable date format for the locale.
|
|
3522
|
+
*/
|
|
3523
|
+
function getLocaleReadableDateFormat(locale) {
|
|
3524
|
+
let format = getLocaleDateFormat(locale).toLowerCase();
|
|
3525
|
+
|
|
3526
|
+
// Ensure month is in 'mm' format
|
|
3527
|
+
if (!format.includes('mm')) {
|
|
3528
|
+
format = format.replace('m', 'mm');
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
// Ensure day is in 'dd' format
|
|
3532
|
+
if (!format.includes('dd')) {
|
|
3533
|
+
format = format.replace('d', 'dd');
|
|
3534
|
+
}
|
|
3535
|
+
return format;
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
/**
|
|
3539
|
+
* Returns flatpickr config for the provided locale.
|
|
3540
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3541
|
+
*
|
|
3542
|
+
* @param {string} [locale] - The locale to get flatpickr config for.
|
|
3543
|
+
* @returns {object} The flatpickr config for the locale.
|
|
3544
|
+
*/
|
|
3545
|
+
function getLocaleDateFlatpickrConfig(locale) {
|
|
3546
|
+
return flatpickerizeDateFormat(getLocaleDateFormat(locale));
|
|
3547
|
+
}
|
|
3548
|
+
function flatpickerizeDateFormat(dateFormat) {
|
|
3549
|
+
const useLeadingZero = {
|
|
3550
|
+
day: dateFormat.includes('dd'),
|
|
3551
|
+
month: dateFormat.includes('MM'),
|
|
3552
|
+
year: dateFormat.includes('yyyy')
|
|
3553
|
+
};
|
|
3554
|
+
dateFormat = useLeadingZero.day ? dateFormat.replace('dd', 'd') : dateFormat.replace('d', 'j');
|
|
3555
|
+
dateFormat = useLeadingZero.month ? dateFormat.replace('MM', 'm') : dateFormat.replace('M', 'n');
|
|
3556
|
+
dateFormat = useLeadingZero.year ? dateFormat.replace('yyyy', 'Y') : dateFormat.replace('yy', 'y');
|
|
3557
|
+
return dateFormat;
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3204
3560
|
function InputAdorner(props) {
|
|
3205
3561
|
const {
|
|
3206
3562
|
pre,
|
|
@@ -3275,7 +3631,7 @@ function Datepicker(props) {
|
|
|
3275
3631
|
hooks.useEffect(() => {
|
|
3276
3632
|
let config = {
|
|
3277
3633
|
allowInput: true,
|
|
3278
|
-
dateFormat:
|
|
3634
|
+
dateFormat: getLocaleDateFlatpickrConfig(),
|
|
3279
3635
|
static: true,
|
|
3280
3636
|
clickOpens: false,
|
|
3281
3637
|
// TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
|
|
@@ -3367,7 +3723,7 @@ function Datepicker(props) {
|
|
|
3367
3723
|
class: "fjs-input",
|
|
3368
3724
|
disabled: disabled,
|
|
3369
3725
|
readOnly: readonly,
|
|
3370
|
-
placeholder:
|
|
3726
|
+
placeholder: getLocaleReadableDateFormat(),
|
|
3371
3727
|
autoComplete: "off",
|
|
3372
3728
|
onFocus: onInputFocus,
|
|
3373
3729
|
onKeyDown: onInputKeyDown,
|
|
@@ -3863,10 +4219,10 @@ Datetime.config = {
|
|
|
3863
4219
|
}
|
|
3864
4220
|
};
|
|
3865
4221
|
|
|
3866
|
-
/**
|
|
3867
|
-
* This file must not be changed or exchanged.
|
|
3868
|
-
*
|
|
3869
|
-
* @see http://bpmn.io/license for more information.
|
|
4222
|
+
/**
|
|
4223
|
+
* This file must not be changed or exchanged.
|
|
4224
|
+
*
|
|
4225
|
+
* @see http://bpmn.io/license for more information.
|
|
3870
4226
|
*/
|
|
3871
4227
|
function Logo() {
|
|
3872
4228
|
return jsxRuntime.jsxs("svg", {
|
|
@@ -3988,6 +4344,52 @@ function FormComponent(props) {
|
|
|
3988
4344
|
});
|
|
3989
4345
|
}
|
|
3990
4346
|
|
|
4347
|
+
function Group(props) {
|
|
4348
|
+
const {
|
|
4349
|
+
field
|
|
4350
|
+
} = props;
|
|
4351
|
+
const {
|
|
4352
|
+
label,
|
|
4353
|
+
id,
|
|
4354
|
+
type,
|
|
4355
|
+
showOutline
|
|
4356
|
+
} = field;
|
|
4357
|
+
const {
|
|
4358
|
+
formId
|
|
4359
|
+
} = hooks.useContext(FormContext$1);
|
|
4360
|
+
const {
|
|
4361
|
+
Empty
|
|
4362
|
+
} = hooks.useContext(FormRenderContext$1);
|
|
4363
|
+
const fullProps = {
|
|
4364
|
+
...props,
|
|
4365
|
+
Empty
|
|
4366
|
+
};
|
|
4367
|
+
return jsxRuntime.jsxs("div", {
|
|
4368
|
+
className: classNames(formFieldClasses(type), {
|
|
4369
|
+
'fjs-outlined': showOutline
|
|
4370
|
+
}),
|
|
4371
|
+
role: "group",
|
|
4372
|
+
"aria-labelledby": prefixId(id, formId),
|
|
4373
|
+
children: [jsxRuntime.jsx(Label, {
|
|
4374
|
+
id: prefixId(id, formId),
|
|
4375
|
+
label: label
|
|
4376
|
+
}), jsxRuntime.jsx(Grid, {
|
|
4377
|
+
...fullProps
|
|
4378
|
+
})]
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
Group.config = {
|
|
4382
|
+
type: 'group',
|
|
4383
|
+
pathed: true,
|
|
4384
|
+
label: 'Group',
|
|
4385
|
+
group: 'presentation',
|
|
4386
|
+
create: (options = {}) => ({
|
|
4387
|
+
components: [],
|
|
4388
|
+
showOutline: true,
|
|
4389
|
+
...options
|
|
4390
|
+
})
|
|
4391
|
+
};
|
|
4392
|
+
|
|
3991
4393
|
const NODE_TYPE_TEXT = 3,
|
|
3992
4394
|
NODE_TYPE_ELEMENT = 1;
|
|
3993
4395
|
const ALLOWED_NODES = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'em', 'a', 'p', 'div', 'ul', 'ol', 'li', 'hr', 'blockquote', 'img', 'pre', 'code', 'br', 'strong', 'table', 'thead', 'tbody', 'tr', 'th', 'td'];
|
|
@@ -3998,11 +4400,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
|
|
|
3998
4400
|
|
|
3999
4401
|
const FORM_ELEMENT = document.createElement('form');
|
|
4000
4402
|
|
|
4001
|
-
/**
|
|
4002
|
-
* Sanitize a HTML string and return the cleaned, safe version.
|
|
4003
|
-
*
|
|
4004
|
-
* @param {string} html
|
|
4005
|
-
* @return {string}
|
|
4403
|
+
/**
|
|
4404
|
+
* Sanitize a HTML string and return the cleaned, safe version.
|
|
4405
|
+
*
|
|
4406
|
+
* @param {string} html
|
|
4407
|
+
* @return {string}
|
|
4006
4408
|
*/
|
|
4007
4409
|
|
|
4008
4410
|
// see https://github.com/developit/snarkdown/issues/70
|
|
@@ -4020,29 +4422,29 @@ function sanitizeHTML(html) {
|
|
|
4020
4422
|
}
|
|
4021
4423
|
}
|
|
4022
4424
|
|
|
4023
|
-
/**
|
|
4024
|
-
* Sanitizes an image source to ensure we only allow for data URI and links
|
|
4025
|
-
* that start with http(s).
|
|
4026
|
-
*
|
|
4027
|
-
* Note: Most browsers anyway do not support script execution in <img> elements.
|
|
4028
|
-
*
|
|
4029
|
-
* @param {string} src
|
|
4030
|
-
* @returns {string}
|
|
4425
|
+
/**
|
|
4426
|
+
* Sanitizes an image source to ensure we only allow for data URI and links
|
|
4427
|
+
* that start with http(s).
|
|
4428
|
+
*
|
|
4429
|
+
* Note: Most browsers anyway do not support script execution in <img> elements.
|
|
4430
|
+
*
|
|
4431
|
+
* @param {string} src
|
|
4432
|
+
* @returns {string}
|
|
4031
4433
|
*/
|
|
4032
4434
|
function sanitizeImageSource(src) {
|
|
4033
4435
|
const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
|
|
4034
4436
|
return valid ? src : '';
|
|
4035
4437
|
}
|
|
4036
4438
|
|
|
4037
|
-
/**
|
|
4038
|
-
* Recursively sanitize a HTML node, potentially
|
|
4039
|
-
* removing it, its children or attributes.
|
|
4040
|
-
*
|
|
4041
|
-
* Inspired by https://github.com/developit/snarkdown/issues/70
|
|
4042
|
-
* and https://github.com/cure53/DOMPurify. Simplified
|
|
4043
|
-
* for our use-case.
|
|
4044
|
-
*
|
|
4045
|
-
* @param {Element} node
|
|
4439
|
+
/**
|
|
4440
|
+
* Recursively sanitize a HTML node, potentially
|
|
4441
|
+
* removing it, its children or attributes.
|
|
4442
|
+
*
|
|
4443
|
+
* Inspired by https://github.com/developit/snarkdown/issues/70
|
|
4444
|
+
* and https://github.com/cure53/DOMPurify. Simplified
|
|
4445
|
+
* for our use-case.
|
|
4446
|
+
*
|
|
4447
|
+
* @param {Element} node
|
|
4046
4448
|
*/
|
|
4047
4449
|
function sanitizeNode(node) {
|
|
4048
4450
|
// allow text nodes
|
|
@@ -4086,13 +4488,13 @@ function sanitizeNode(node) {
|
|
|
4086
4488
|
}
|
|
4087
4489
|
}
|
|
4088
4490
|
|
|
4089
|
-
/**
|
|
4090
|
-
* Validates attributes for validity.
|
|
4091
|
-
*
|
|
4092
|
-
* @param {string} lcTag
|
|
4093
|
-
* @param {string} lcName
|
|
4094
|
-
* @param {string} value
|
|
4095
|
-
* @return {boolean}
|
|
4491
|
+
/**
|
|
4492
|
+
* Validates attributes for validity.
|
|
4493
|
+
*
|
|
4494
|
+
* @param {string} lcTag
|
|
4495
|
+
* @param {string} lcName
|
|
4496
|
+
* @param {string} value
|
|
4497
|
+
* @return {boolean}
|
|
4096
4498
|
*/
|
|
4097
4499
|
function isValidAttribute(lcTag, lcName, value) {
|
|
4098
4500
|
// disallow most attributes based on whitelist
|
|
@@ -4576,21 +4978,7 @@ Radio.config = {
|
|
|
4576
4978
|
group: 'selection',
|
|
4577
4979
|
emptyValue: null,
|
|
4578
4980
|
sanitizeValue: sanitizeSingleSelectValue,
|
|
4579
|
-
create:
|
|
4580
|
-
const defaults = {};
|
|
4581
|
-
|
|
4582
|
-
// provide default values if valuesKey isn't set
|
|
4583
|
-
if (!options.valuesKey) {
|
|
4584
|
-
defaults.values = [{
|
|
4585
|
-
label: 'Value',
|
|
4586
|
-
value: 'value'
|
|
4587
|
-
}];
|
|
4588
|
-
}
|
|
4589
|
-
return {
|
|
4590
|
-
...defaults,
|
|
4591
|
-
...options
|
|
4592
|
-
};
|
|
4593
|
-
}
|
|
4981
|
+
create: createEmptyOptions
|
|
4594
4982
|
};
|
|
4595
4983
|
|
|
4596
4984
|
var _path$d;
|
|
@@ -4942,21 +5330,7 @@ Select.config = {
|
|
|
4942
5330
|
group: 'selection',
|
|
4943
5331
|
emptyValue: null,
|
|
4944
5332
|
sanitizeValue: sanitizeSingleSelectValue,
|
|
4945
|
-
create:
|
|
4946
|
-
const defaults = {};
|
|
4947
|
-
|
|
4948
|
-
// provide default values if valuesKey isn't set
|
|
4949
|
-
if (!options.valuesKey) {
|
|
4950
|
-
defaults.values = [{
|
|
4951
|
-
label: 'Value',
|
|
4952
|
-
value: 'value'
|
|
4953
|
-
}];
|
|
4954
|
-
}
|
|
4955
|
-
return {
|
|
4956
|
-
...defaults,
|
|
4957
|
-
...options
|
|
4958
|
-
};
|
|
4959
|
-
}
|
|
5333
|
+
create: createEmptyOptions
|
|
4960
5334
|
};
|
|
4961
5335
|
|
|
4962
5336
|
const type$4 = 'spacer';
|
|
@@ -5184,21 +5558,7 @@ Taglist.config = {
|
|
|
5184
5558
|
group: 'selection',
|
|
5185
5559
|
emptyValue: [],
|
|
5186
5560
|
sanitizeValue: sanitizeMultiSelectValue,
|
|
5187
|
-
create:
|
|
5188
|
-
const defaults = {};
|
|
5189
|
-
|
|
5190
|
-
// provide default values if valuesKey isn't set
|
|
5191
|
-
if (!options.valuesKey) {
|
|
5192
|
-
defaults.values = [{
|
|
5193
|
-
label: 'Value',
|
|
5194
|
-
value: 'value'
|
|
5195
|
-
}];
|
|
5196
|
-
}
|
|
5197
|
-
return {
|
|
5198
|
-
...defaults,
|
|
5199
|
-
...options
|
|
5200
|
-
};
|
|
5201
|
-
}
|
|
5561
|
+
create: createEmptyOptions
|
|
5202
5562
|
};
|
|
5203
5563
|
|
|
5204
5564
|
const type$2 = 'text';
|
|
@@ -5615,13 +5975,14 @@ var SvgGroup = function SvgGroup(props) {
|
|
|
5615
5975
|
return /*#__PURE__*/React__namespace.createElement("svg", _extends$8({
|
|
5616
5976
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5617
5977
|
width: 54,
|
|
5618
|
-
height: 54
|
|
5978
|
+
height: 54,
|
|
5979
|
+
fill: "currentcolor"
|
|
5619
5980
|
}, props), _path$8 || (_path$8 = /*#__PURE__*/React__namespace.createElement("path", {
|
|
5620
5981
|
fillRule: "evenodd",
|
|
5621
5982
|
d: "M8 33v5a1 1 0 0 0 1 1h4v2H9a3 3 0 0 1-3-3v-5h2Zm18 6v2H15v-2h11Zm13 0v2H28v-2h11Zm9-6v5a3 3 0 0 1-3 3h-4v-2h4a1 1 0 0 0 .993-.883L46 38v-5h2ZM8 22v9H6v-9h2Zm40 0v9h-2v-9h2Zm-35-9v2H9a1 1 0 0 0-.993.883L8 16v4H6v-4a3 3 0 0 1 3-3h4Zm32 0a3 3 0 0 1 3 3v4h-2v-4a1 1 0 0 0-.883-.993L45 15h-4v-2h4Zm-6 0v2H28v-2h11Zm-13 0v2H15v-2h11Z"
|
|
5622
5983
|
})));
|
|
5623
5984
|
};
|
|
5624
|
-
var
|
|
5985
|
+
var GroupIcon = SvgGroup;
|
|
5625
5986
|
|
|
5626
5987
|
var _path$7;
|
|
5627
5988
|
function _extends$7() { _extends$7 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$7.apply(this, arguments); }
|
|
@@ -5674,14 +6035,14 @@ var SvgSpacer = function SvgSpacer(props) {
|
|
|
5674
6035
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5675
6036
|
width: 54,
|
|
5676
6037
|
height: 54,
|
|
5677
|
-
fill: "
|
|
6038
|
+
fill: "currentcolor"
|
|
5678
6039
|
}, props), _path$4 || (_path$4 = /*#__PURE__*/React__namespace.createElement("path", {
|
|
5679
|
-
stroke: "
|
|
6040
|
+
stroke: "currentcolor",
|
|
5680
6041
|
strokeLinecap: "square",
|
|
5681
6042
|
strokeWidth: 2,
|
|
5682
6043
|
d: "M9 23h36M9 31h36"
|
|
5683
6044
|
})), _path2$1 || (_path2$1 = /*#__PURE__*/React__namespace.createElement("path", {
|
|
5684
|
-
stroke: "
|
|
6045
|
+
stroke: "currentcolor",
|
|
5685
6046
|
strokeLinecap: "round",
|
|
5686
6047
|
strokeLinejoin: "round",
|
|
5687
6048
|
strokeWidth: 2,
|
|
@@ -5759,8 +6120,9 @@ const iconsByType = type => {
|
|
|
5759
6120
|
button: ButtonIcon,
|
|
5760
6121
|
checkbox: CheckboxIcon,
|
|
5761
6122
|
checklist: ChecklistIcon,
|
|
5762
|
-
columns:
|
|
6123
|
+
columns: GroupIcon,
|
|
5763
6124
|
datetime: DatetimeIcon,
|
|
6125
|
+
group: GroupIcon,
|
|
5764
6126
|
image: ImageIcon,
|
|
5765
6127
|
number: NumberIcon,
|
|
5766
6128
|
radio: RadioIcon,
|
|
@@ -5774,7 +6136,7 @@ const iconsByType = type => {
|
|
|
5774
6136
|
}[type];
|
|
5775
6137
|
};
|
|
5776
6138
|
|
|
5777
|
-
const formFields = [Button, Checkbox, Checklist,
|
|
6139
|
+
const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, Image, Numberfield, Datetime, Radio, Select, Spacer, Taglist, Text, Textfield, Textarea];
|
|
5778
6140
|
|
|
5779
6141
|
class FormFields {
|
|
5780
6142
|
constructor() {
|
|
@@ -5850,9 +6212,12 @@ var renderModule = {
|
|
|
5850
6212
|
};
|
|
5851
6213
|
|
|
5852
6214
|
var core = {
|
|
5853
|
-
__depends__: [
|
|
6215
|
+
__depends__: [renderModule],
|
|
5854
6216
|
eventBus: ['type', EventBus],
|
|
6217
|
+
importer: ['type', Importer],
|
|
6218
|
+
fieldFactory: ['type', FieldFactory],
|
|
5855
6219
|
formFieldRegistry: ['type', FormFieldRegistry],
|
|
6220
|
+
pathRegistry: ['type', PathRegistry],
|
|
5856
6221
|
formLayouter: ['type', FormLayouter],
|
|
5857
6222
|
validator: ['type', Validator]
|
|
5858
6223
|
};
|
|
@@ -5967,9 +6332,9 @@ class Form {
|
|
|
5967
6332
|
this.clear();
|
|
5968
6333
|
const {
|
|
5969
6334
|
schema: importedSchema,
|
|
5970
|
-
data: initializedData,
|
|
5971
6335
|
warnings
|
|
5972
|
-
} = this.get('importer').importSchema(schema
|
|
6336
|
+
} = this.get('importer').importSchema(schema);
|
|
6337
|
+
const initializedData = this._initializeFieldData(clone(data));
|
|
5973
6338
|
this._setState({
|
|
5974
6339
|
data: initializedData,
|
|
5975
6340
|
errors: {},
|
|
@@ -6027,21 +6392,21 @@ class Form {
|
|
|
6027
6392
|
*/
|
|
6028
6393
|
validate() {
|
|
6029
6394
|
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6395
|
+
pathRegistry = this.get('pathRegistry'),
|
|
6030
6396
|
validator = this.get('validator');
|
|
6031
6397
|
const {
|
|
6032
6398
|
data
|
|
6033
6399
|
} = this._getState();
|
|
6034
6400
|
const errors = formFieldRegistry.getAll().reduce((errors, field) => {
|
|
6035
6401
|
const {
|
|
6036
|
-
disabled
|
|
6037
|
-
_path
|
|
6402
|
+
disabled
|
|
6038
6403
|
} = field;
|
|
6039
6404
|
if (disabled) {
|
|
6040
6405
|
return errors;
|
|
6041
6406
|
}
|
|
6042
|
-
const value = minDash.get(data,
|
|
6407
|
+
const value = minDash.get(data, pathRegistry.getValuePath(field));
|
|
6043
6408
|
const fieldErrors = validator.validateField(field, value);
|
|
6044
|
-
return minDash.set(errors, [
|
|
6409
|
+
return minDash.set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
6045
6410
|
}, /** @type {Errors} */{});
|
|
6046
6411
|
this._setState({
|
|
6047
6412
|
errors
|
|
@@ -6147,16 +6512,14 @@ class Form {
|
|
|
6147
6512
|
value
|
|
6148
6513
|
} = update;
|
|
6149
6514
|
const {
|
|
6150
|
-
_path
|
|
6151
|
-
} = field;
|
|
6152
|
-
let {
|
|
6153
6515
|
data,
|
|
6154
6516
|
errors
|
|
6155
6517
|
} = this._getState();
|
|
6156
|
-
const validator = this.get('validator')
|
|
6518
|
+
const validator = this.get('validator'),
|
|
6519
|
+
pathRegistry = this.get('pathRegistry');
|
|
6157
6520
|
const fieldErrors = validator.validateField(field, value);
|
|
6158
|
-
minDash.set(data,
|
|
6159
|
-
minDash.set(errors, [
|
|
6521
|
+
minDash.set(data, pathRegistry.getValuePath(field), value);
|
|
6522
|
+
minDash.set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
6160
6523
|
this._setState({
|
|
6161
6524
|
data: clone(data),
|
|
6162
6525
|
errors: clone(errors)
|
|
@@ -6199,23 +6562,26 @@ class Form {
|
|
|
6199
6562
|
* @internal
|
|
6200
6563
|
*/
|
|
6201
6564
|
_getSubmitData() {
|
|
6202
|
-
const formFieldRegistry = this.get('formFieldRegistry')
|
|
6565
|
+
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6566
|
+
pathRegistry = this.get('pathRegistry'),
|
|
6567
|
+
formFields = this.get('formFields');
|
|
6203
6568
|
const formData = this._getState().data;
|
|
6204
6569
|
const submitData = formFieldRegistry.getAll().reduce((previous, field) => {
|
|
6205
6570
|
const {
|
|
6206
6571
|
disabled,
|
|
6207
|
-
|
|
6572
|
+
type
|
|
6208
6573
|
} = field;
|
|
6574
|
+
const {
|
|
6575
|
+
config: fieldConfig
|
|
6576
|
+
} = formFields.get(type);
|
|
6209
6577
|
|
|
6210
|
-
// do not submit disabled form fields
|
|
6211
|
-
if (disabled || !
|
|
6578
|
+
// do not submit disabled form fields or routing fields
|
|
6579
|
+
if (disabled || !fieldConfig.keyed) {
|
|
6212
6580
|
return previous;
|
|
6213
6581
|
}
|
|
6214
|
-
const
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
[_path[0]]: value
|
|
6218
|
-
};
|
|
6582
|
+
const valuePath = pathRegistry.getValuePath(field);
|
|
6583
|
+
const value = minDash.get(formData, valuePath);
|
|
6584
|
+
return minDash.set(previous, valuePath, value);
|
|
6219
6585
|
}, {});
|
|
6220
6586
|
const filteredSubmitData = this._applyConditions(submitData, formData);
|
|
6221
6587
|
return filteredSubmitData;
|
|
@@ -6228,9 +6594,46 @@ class Form {
|
|
|
6228
6594
|
const conditionChecker = this.get('conditionChecker');
|
|
6229
6595
|
return conditionChecker.applyConditions(toFilter, data);
|
|
6230
6596
|
}
|
|
6597
|
+
|
|
6598
|
+
/**
|
|
6599
|
+
* @internal
|
|
6600
|
+
*/
|
|
6601
|
+
_initializeFieldData(data) {
|
|
6602
|
+
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6603
|
+
formFields = this.get('formFields'),
|
|
6604
|
+
pathRegistry = this.get('pathRegistry');
|
|
6605
|
+
return formFieldRegistry.getAll().reduce((initializedData, formField) => {
|
|
6606
|
+
const {
|
|
6607
|
+
defaultValue,
|
|
6608
|
+
type
|
|
6609
|
+
} = formField;
|
|
6610
|
+
|
|
6611
|
+
// try to get value from data
|
|
6612
|
+
// if unavailable - try to get default value from form field
|
|
6613
|
+
// if unavailable - get empty value from form field
|
|
6614
|
+
|
|
6615
|
+
const valuePath = pathRegistry.getValuePath(formField);
|
|
6616
|
+
if (valuePath) {
|
|
6617
|
+
const {
|
|
6618
|
+
config: fieldConfig
|
|
6619
|
+
} = formFields.get(type);
|
|
6620
|
+
let valueData = minDash.get(data, valuePath);
|
|
6621
|
+
if (!minDash.isUndefined(valueData) && fieldConfig.sanitizeValue) {
|
|
6622
|
+
valueData = fieldConfig.sanitizeValue({
|
|
6623
|
+
formField,
|
|
6624
|
+
data,
|
|
6625
|
+
value: valueData
|
|
6626
|
+
});
|
|
6627
|
+
}
|
|
6628
|
+
const initializedFieldValue = !minDash.isUndefined(valueData) ? valueData : !minDash.isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
|
|
6629
|
+
return minDash.set(initializedData, valuePath, initializedFieldValue);
|
|
6630
|
+
}
|
|
6631
|
+
return initializedData;
|
|
6632
|
+
}, data);
|
|
6633
|
+
}
|
|
6231
6634
|
}
|
|
6232
6635
|
|
|
6233
|
-
const schemaVersion =
|
|
6636
|
+
const schemaVersion = 11;
|
|
6234
6637
|
|
|
6235
6638
|
/**
|
|
6236
6639
|
* @typedef { import('./types').CreateFormOptions } CreateFormOptions
|
|
@@ -6265,10 +6668,11 @@ exports.DATETIME_SUBTYPE_PATH = DATETIME_SUBTYPE_PATH;
|
|
|
6265
6668
|
exports.DATE_DISALLOW_PAST_PATH = DATE_DISALLOW_PAST_PATH;
|
|
6266
6669
|
exports.DATE_LABEL_PATH = DATE_LABEL_PATH;
|
|
6267
6670
|
exports.Datetime = Datetime;
|
|
6268
|
-
exports.Default =
|
|
6671
|
+
exports.Default = FormComponent$1;
|
|
6269
6672
|
exports.ExpressionLanguageModule = ExpressionLanguageModule;
|
|
6270
6673
|
exports.FeelExpressionLanguage = FeelExpressionLanguage;
|
|
6271
6674
|
exports.FeelersTemplating = FeelersTemplating;
|
|
6675
|
+
exports.FieldFactory = FieldFactory;
|
|
6272
6676
|
exports.Form = Form;
|
|
6273
6677
|
exports.FormComponent = FormComponent;
|
|
6274
6678
|
exports.FormContext = FormContext$1;
|
|
@@ -6276,11 +6680,14 @@ exports.FormFieldRegistry = FormFieldRegistry;
|
|
|
6276
6680
|
exports.FormFields = FormFields;
|
|
6277
6681
|
exports.FormLayouter = FormLayouter;
|
|
6278
6682
|
exports.FormRenderContext = FormRenderContext$1;
|
|
6683
|
+
exports.Group = Group;
|
|
6279
6684
|
exports.Image = Image;
|
|
6685
|
+
exports.Importer = Importer;
|
|
6280
6686
|
exports.MINUTES_IN_DAY = MINUTES_IN_DAY;
|
|
6281
6687
|
exports.MarkdownModule = MarkdownModule;
|
|
6282
6688
|
exports.MarkdownRenderer = MarkdownRenderer;
|
|
6283
6689
|
exports.Numberfield = Numberfield;
|
|
6690
|
+
exports.PathRegistry = PathRegistry;
|
|
6284
6691
|
exports.Radio = Radio;
|
|
6285
6692
|
exports.Select = Select;
|
|
6286
6693
|
exports.Spacer = Spacer;
|
|
@@ -6305,7 +6712,6 @@ exports.clone = clone;
|
|
|
6305
6712
|
exports.createForm = createForm;
|
|
6306
6713
|
exports.createFormContainer = createFormContainer;
|
|
6307
6714
|
exports.createInjector = createInjector;
|
|
6308
|
-
exports.findErrors = findErrors;
|
|
6309
6715
|
exports.formFields = formFields;
|
|
6310
6716
|
exports.generateIdForType = generateIdForType;
|
|
6311
6717
|
exports.generateIndexForType = generateIndexForType;
|
|
@@ -6314,7 +6720,7 @@ exports.getValuesSource = getValuesSource;
|
|
|
6314
6720
|
exports.iconsByType = iconsByType;
|
|
6315
6721
|
exports.isRequired = isRequired;
|
|
6316
6722
|
exports.pathParse = pathParse;
|
|
6317
|
-
exports.pathStringify = pathStringify;
|
|
6318
6723
|
exports.pathsEqual = pathsEqual;
|
|
6724
|
+
exports.runRecursively = runRecursively;
|
|
6319
6725
|
exports.schemaVersion = schemaVersion;
|
|
6320
6726
|
//# sourceMappingURL=index.cjs.map
|