@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.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ids from 'ids';
|
|
2
|
-
import { isString,
|
|
2
|
+
import { isString, get, set, isObject, values, uniqueBy, isArray, isFunction, isNumber, bind, assign, isNil, groupBy, flatten, findIndex, isUndefined } from 'min-dash';
|
|
3
3
|
import Big from 'big.js';
|
|
4
4
|
import { parseExpression, parseUnaryTests, evaluate, unaryTest } from 'feelin';
|
|
5
5
|
import { evaluate as evaluate$1, parser, buildSimpleTree } from 'feelers';
|
|
@@ -47,26 +47,26 @@ const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', o
|
|
|
47
47
|
return [...new Set(variables)];
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Get the variable name at the specified index in a given path expression.
|
|
52
|
-
*
|
|
53
|
-
* @param {Object} root - The root node of the path expression tree.
|
|
54
|
-
* @param {number} index - The index of the variable name to retrieve.
|
|
55
|
-
* @returns {string|null} The variable name at the specified index or null if index is out of bounds.
|
|
50
|
+
/**
|
|
51
|
+
* Get the variable name at the specified index in a given path expression.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} root - The root node of the path expression tree.
|
|
54
|
+
* @param {number} index - The index of the variable name to retrieve.
|
|
55
|
+
* @returns {string|null} The variable name at the specified index or null if index is out of bounds.
|
|
56
56
|
*/
|
|
57
57
|
const _getVariableNameAtPathIndex = (root, index) => {
|
|
58
58
|
const accessors = _deconstructPathExpression(root);
|
|
59
59
|
return accessors[index] || null;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
* Extracts the variables which are required of the external context for a given path expression.
|
|
64
|
-
* This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
|
|
65
|
-
*
|
|
66
|
-
* @param {Object} node - The root node of the path expression tree.
|
|
67
|
-
* @param {number} initialDepth - The depth at which the root node is located in the outer context.
|
|
68
|
-
* @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
|
|
69
|
-
* @returns {Set} - A set containing the extracted variable names.
|
|
62
|
+
/**
|
|
63
|
+
* Extracts the variables which are required of the external context for a given path expression.
|
|
64
|
+
* This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} node - The root node of the path expression tree.
|
|
67
|
+
* @param {number} initialDepth - The depth at which the root node is located in the outer context.
|
|
68
|
+
* @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
|
|
69
|
+
* @returns {Set} - A set containing the extracted variable names.
|
|
70
70
|
*/
|
|
71
71
|
const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
|
|
72
72
|
// depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
|
|
@@ -112,11 +112,11 @@ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) =
|
|
|
112
112
|
return new Set(extractedVariables);
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
/**
|
|
116
|
-
* Deconstructs a path expression tree into an array of components.
|
|
117
|
-
*
|
|
118
|
-
* @param {Object} root - The root node of the path expression tree.
|
|
119
|
-
* @returns {Array<string>} An array of components in the path expression, in the correct order.
|
|
115
|
+
/**
|
|
116
|
+
* Deconstructs a path expression tree into an array of components.
|
|
117
|
+
*
|
|
118
|
+
* @param {Object} root - The root node of the path expression tree.
|
|
119
|
+
* @returns {Array<string>} An array of components in the path expression, in the correct order.
|
|
120
120
|
*/
|
|
121
121
|
const _deconstructPathExpression = root => {
|
|
122
122
|
let node = root;
|
|
@@ -135,13 +135,13 @@ const _deconstructPathExpression = root => {
|
|
|
135
135
|
return parts.reverse();
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
-
/**
|
|
139
|
-
* Builds a simplified feel structure tree from the given parse tree and feel string.
|
|
140
|
-
* The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
|
|
141
|
-
*
|
|
142
|
-
* @param {Object} parseTree - The parse tree generated by a parser.
|
|
143
|
-
* @param {string} feelString - The feel string used for parsing.
|
|
144
|
-
* @returns {Object} The simplified feel structure tree.
|
|
138
|
+
/**
|
|
139
|
+
* Builds a simplified feel structure tree from the given parse tree and feel string.
|
|
140
|
+
* The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
|
|
141
|
+
*
|
|
142
|
+
* @param {Object} parseTree - The parse tree generated by a parser.
|
|
143
|
+
* @param {string} feelString - The feel string used for parsing.
|
|
144
|
+
* @returns {Object} The simplified feel structure tree.
|
|
145
145
|
*/
|
|
146
146
|
const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
|
|
147
147
|
const stack = [{
|
|
@@ -167,9 +167,9 @@ const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
|
|
|
167
167
|
return _extractFilterExpressions(stack[0].children[0]);
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
/**
|
|
171
|
-
* Restructure the tree in such a way to bring filters (which create new contexts) to the root of the tree.
|
|
172
|
-
* This is done to simplify the extraction of variables and match the context hierarchy.
|
|
170
|
+
/**
|
|
171
|
+
* Restructure the tree in such a way to bring filters (which create new contexts) to the root of the tree.
|
|
172
|
+
* This is done to simplify the extraction of variables and match the context hierarchy.
|
|
173
173
|
*/
|
|
174
174
|
const _extractFilterExpressions = tree => {
|
|
175
175
|
const flattenedExpressionTree = {
|
|
@@ -210,25 +210,25 @@ class FeelExpressionLanguage {
|
|
|
210
210
|
this._eventBus = eventBus;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
/**
|
|
214
|
-
* Determines if the given value is a FEEL expression.
|
|
215
|
-
*
|
|
216
|
-
* @param {any} value
|
|
217
|
-
* @returns {boolean}
|
|
218
|
-
*
|
|
213
|
+
/**
|
|
214
|
+
* Determines if the given value is a FEEL expression.
|
|
215
|
+
*
|
|
216
|
+
* @param {any} value
|
|
217
|
+
* @returns {boolean}
|
|
218
|
+
*
|
|
219
219
|
*/
|
|
220
220
|
isExpression(value) {
|
|
221
221
|
return isString(value) && value.startsWith('=');
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
/**
|
|
225
|
-
* Retrieve variable names from a given FEEL expression.
|
|
226
|
-
*
|
|
227
|
-
* @param {string} expression
|
|
228
|
-
* @param {object} [options]
|
|
229
|
-
* @param {string} [options.type]
|
|
230
|
-
*
|
|
231
|
-
* @returns {string[]}
|
|
224
|
+
/**
|
|
225
|
+
* Retrieve variable names from a given FEEL expression.
|
|
226
|
+
*
|
|
227
|
+
* @param {string} expression
|
|
228
|
+
* @param {object} [options]
|
|
229
|
+
* @param {string} [options.type]
|
|
230
|
+
*
|
|
231
|
+
* @returns {string[]}
|
|
232
232
|
*/
|
|
233
233
|
getVariableNames(expression, options = {}) {
|
|
234
234
|
const {
|
|
@@ -243,13 +243,13 @@ class FeelExpressionLanguage {
|
|
|
243
243
|
return getFlavouredFeelVariableNames(expression, type);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
/**
|
|
247
|
-
* Evaluate an expression.
|
|
248
|
-
*
|
|
249
|
-
* @param {string} expression
|
|
250
|
-
* @param {import('../../types').Data} [data]
|
|
251
|
-
*
|
|
252
|
-
* @returns {any}
|
|
246
|
+
/**
|
|
247
|
+
* Evaluate an expression.
|
|
248
|
+
*
|
|
249
|
+
* @param {string} expression
|
|
250
|
+
* @param {import('../../types').Data} [data]
|
|
251
|
+
*
|
|
252
|
+
* @returns {any}
|
|
253
253
|
*/
|
|
254
254
|
evaluate(expression, data = {}) {
|
|
255
255
|
if (!expression) {
|
|
@@ -274,23 +274,23 @@ FeelExpressionLanguage.$inject = ['eventBus'];
|
|
|
274
274
|
class FeelersTemplating {
|
|
275
275
|
constructor() {}
|
|
276
276
|
|
|
277
|
-
/**
|
|
278
|
-
* Determines if the given value is a feelers template.
|
|
279
|
-
*
|
|
280
|
-
* @param {any} value
|
|
281
|
-
* @returns {boolean}
|
|
282
|
-
*
|
|
277
|
+
/**
|
|
278
|
+
* Determines if the given value is a feelers template.
|
|
279
|
+
*
|
|
280
|
+
* @param {any} value
|
|
281
|
+
* @returns {boolean}
|
|
282
|
+
*
|
|
283
283
|
*/
|
|
284
284
|
isTemplate(value) {
|
|
285
285
|
return isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
/**
|
|
289
|
-
* Retrieve variable names from a given feelers template.
|
|
290
|
-
*
|
|
291
|
-
* @param {string} template
|
|
292
|
-
*
|
|
293
|
-
* @returns {string[]}
|
|
288
|
+
/**
|
|
289
|
+
* Retrieve variable names from a given feelers template.
|
|
290
|
+
*
|
|
291
|
+
* @param {string} template
|
|
292
|
+
*
|
|
293
|
+
* @returns {string[]}
|
|
294
294
|
*/
|
|
295
295
|
getVariableNames(template) {
|
|
296
296
|
if (!this.isTemplate(template)) {
|
|
@@ -316,17 +316,17 @@ class FeelersTemplating {
|
|
|
316
316
|
}, []);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
/**
|
|
320
|
-
* Evaluate a template.
|
|
321
|
-
*
|
|
322
|
-
* @param {string} template
|
|
323
|
-
* @param {Object<string, any>} context
|
|
324
|
-
* @param {Object} options
|
|
325
|
-
* @param {boolean} [options.debug = false]
|
|
326
|
-
* @param {boolean} [options.strict = false]
|
|
327
|
-
* @param {Function} [options.buildDebugString]
|
|
328
|
-
*
|
|
329
|
-
* @returns
|
|
319
|
+
/**
|
|
320
|
+
* Evaluate a template.
|
|
321
|
+
*
|
|
322
|
+
* @param {string} template
|
|
323
|
+
* @param {Object<string, any>} context
|
|
324
|
+
* @param {Object} options
|
|
325
|
+
* @param {boolean} [options.debug = false]
|
|
326
|
+
* @param {boolean} [options.strict = false]
|
|
327
|
+
* @param {Function} [options.buildDebugString]
|
|
328
|
+
*
|
|
329
|
+
* @returns
|
|
330
330
|
*/
|
|
331
331
|
evaluate(template, context = {}, options = {}) {
|
|
332
332
|
const {
|
|
@@ -341,22 +341,22 @@ class FeelersTemplating {
|
|
|
341
341
|
});
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
-
/**
|
|
345
|
-
* @typedef {Object} ExpressionWithDepth
|
|
346
|
-
* @property {number} depth - The depth of the expression in the syntax tree.
|
|
347
|
-
* @property {string} expression - The extracted expression
|
|
344
|
+
/**
|
|
345
|
+
* @typedef {Object} ExpressionWithDepth
|
|
346
|
+
* @property {number} depth - The depth of the expression in the syntax tree.
|
|
347
|
+
* @property {string} expression - The extracted expression
|
|
348
348
|
*/
|
|
349
349
|
|
|
350
|
-
/**
|
|
351
|
-
* Extracts all feel expressions in the template along with their depth in the syntax tree.
|
|
352
|
-
* The depth is incremented for child expressions of loops to account for context drilling.
|
|
353
|
-
|
|
354
|
-
* @param {string} template - A feelers template string.
|
|
355
|
-
* @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
|
|
359
|
-
* const extractedExpressions = _extractExpressionsWithDepth(template);
|
|
350
|
+
/**
|
|
351
|
+
* Extracts all feel expressions in the template along with their depth in the syntax tree.
|
|
352
|
+
* The depth is incremented for child expressions of loops to account for context drilling.
|
|
353
|
+
* @name extractExpressionsWithDepth
|
|
354
|
+
* @param {string} template - A feelers template string.
|
|
355
|
+
* @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
|
|
359
|
+
* const extractedExpressions = _extractExpressionsWithDepth(template);
|
|
360
360
|
*/
|
|
361
361
|
_extractExpressionsWithDepth(template) {
|
|
362
362
|
// build simplified feelers syntax tree
|
|
@@ -387,47 +387,271 @@ class FeelersTemplating {
|
|
|
387
387
|
}
|
|
388
388
|
FeelersTemplating.$inject = [];
|
|
389
389
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
390
|
+
// config ///////////////////
|
|
391
|
+
|
|
392
|
+
const MINUTES_IN_DAY = 60 * 24;
|
|
393
|
+
const DATETIME_SUBTYPES = {
|
|
394
|
+
DATE: 'date',
|
|
395
|
+
TIME: 'time',
|
|
396
|
+
DATETIME: 'datetime'
|
|
397
|
+
};
|
|
398
|
+
const TIME_SERIALISING_FORMATS = {
|
|
399
|
+
UTC_OFFSET: 'utc_offset',
|
|
400
|
+
UTC_NORMALIZED: 'utc_normalized',
|
|
401
|
+
NO_TIMEZONE: 'no_timezone'
|
|
402
|
+
};
|
|
403
|
+
const DATETIME_SUBTYPES_LABELS = {
|
|
404
|
+
[DATETIME_SUBTYPES.DATE]: 'Date',
|
|
405
|
+
[DATETIME_SUBTYPES.TIME]: 'Time',
|
|
406
|
+
[DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
|
|
407
|
+
};
|
|
408
|
+
const TIME_SERIALISINGFORMAT_LABELS = {
|
|
409
|
+
[TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
|
|
410
|
+
[TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
|
|
411
|
+
[TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
|
|
412
|
+
};
|
|
413
|
+
const DATETIME_SUBTYPE_PATH = ['subtype'];
|
|
414
|
+
const DATE_LABEL_PATH = ['dateLabel'];
|
|
415
|
+
const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
|
|
416
|
+
const TIME_LABEL_PATH = ['timeLabel'];
|
|
417
|
+
const TIME_USE24H_PATH = ['use24h'];
|
|
418
|
+
const TIME_INTERVAL_PATH = ['timeInterval'];
|
|
419
|
+
const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
|
|
420
|
+
|
|
421
|
+
// config ///////////////////
|
|
422
|
+
|
|
423
|
+
const VALUES_SOURCES = {
|
|
424
|
+
STATIC: 'static',
|
|
425
|
+
INPUT: 'input',
|
|
426
|
+
EXPRESSION: 'expression'
|
|
427
|
+
};
|
|
428
|
+
const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
|
|
429
|
+
const VALUES_SOURCES_LABELS = {
|
|
430
|
+
[VALUES_SOURCES.STATIC]: 'Static',
|
|
431
|
+
[VALUES_SOURCES.INPUT]: 'Input data',
|
|
432
|
+
[VALUES_SOURCES.EXPRESSION]: 'Expression'
|
|
433
|
+
};
|
|
434
|
+
const VALUES_SOURCES_PATHS = {
|
|
435
|
+
[VALUES_SOURCES.STATIC]: ['values'],
|
|
436
|
+
[VALUES_SOURCES.INPUT]: ['valuesKey'],
|
|
437
|
+
[VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
|
|
438
|
+
};
|
|
439
|
+
const VALUES_SOURCES_DEFAULTS = {
|
|
440
|
+
[VALUES_SOURCES.STATIC]: [{
|
|
441
|
+
label: 'Value',
|
|
442
|
+
value: 'value'
|
|
443
|
+
}],
|
|
444
|
+
[VALUES_SOURCES.INPUT]: '',
|
|
445
|
+
[VALUES_SOURCES.EXPRESSION]: '='
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// helpers ///////////////////
|
|
449
|
+
|
|
450
|
+
function getValuesSource(field) {
|
|
451
|
+
for (const source of Object.values(VALUES_SOURCES)) {
|
|
452
|
+
if (get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
|
|
453
|
+
return source;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return VALUES_SOURCE_DEFAULT;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function createInjector(bootstrapModules) {
|
|
460
|
+
const injector = new Injector(bootstrapModules);
|
|
461
|
+
injector.init();
|
|
462
|
+
return injector;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* @param {string?} prefix
|
|
467
|
+
*
|
|
468
|
+
* @returns Element
|
|
469
|
+
*/
|
|
470
|
+
function createFormContainer(prefix = 'fjs') {
|
|
471
|
+
const container = document.createElement('div');
|
|
472
|
+
container.classList.add(`${prefix}-container`);
|
|
473
|
+
return container;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
|
|
477
|
+
const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
|
|
478
|
+
function isRequired(field) {
|
|
479
|
+
return field.required;
|
|
480
|
+
}
|
|
481
|
+
function pathParse(path) {
|
|
482
|
+
if (!path) {
|
|
483
|
+
return [];
|
|
484
|
+
}
|
|
485
|
+
return path.split('.').map(key => {
|
|
486
|
+
return isNaN(parseInt(key)) ? key : parseInt(key);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
function pathsEqual(a, b) {
|
|
490
|
+
return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
|
|
491
|
+
}
|
|
492
|
+
const indices = {};
|
|
493
|
+
function generateIndexForType(type) {
|
|
494
|
+
if (type in indices) {
|
|
495
|
+
indices[type]++;
|
|
496
|
+
} else {
|
|
497
|
+
indices[type] = 1;
|
|
498
|
+
}
|
|
499
|
+
return indices[type];
|
|
500
|
+
}
|
|
501
|
+
function generateIdForType(type) {
|
|
502
|
+
return `${type}${generateIndexForType(type)}`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* @template T
|
|
507
|
+
* @param {T} data
|
|
508
|
+
* @param {(this: any, key: string, value: any) => any} [replacer]
|
|
509
|
+
* @return {T}
|
|
510
|
+
*/
|
|
511
|
+
function clone(data, replacer) {
|
|
512
|
+
return JSON.parse(JSON.stringify(data, replacer));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Parse the schema for input variables a form might make use of
|
|
517
|
+
*
|
|
518
|
+
* @param {any} schema
|
|
519
|
+
*
|
|
520
|
+
* @return {string[]}
|
|
521
|
+
*/
|
|
522
|
+
function getSchemaVariables(schema, options = {}) {
|
|
523
|
+
const {
|
|
524
|
+
expressionLanguage = new FeelExpressionLanguage(null),
|
|
525
|
+
templating = new FeelersTemplating(),
|
|
526
|
+
inputs = true,
|
|
527
|
+
outputs = true
|
|
528
|
+
} = options;
|
|
529
|
+
if (!schema.components) {
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
const getAllComponents = node => {
|
|
533
|
+
const components = [];
|
|
534
|
+
if (node.components) {
|
|
535
|
+
node.components.forEach(component => {
|
|
536
|
+
components.push(component);
|
|
537
|
+
components.push(...getAllComponents(component));
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
return components;
|
|
541
|
+
};
|
|
542
|
+
const variables = getAllComponents(schema).reduce((variables, component) => {
|
|
543
|
+
const {
|
|
544
|
+
valuesKey
|
|
545
|
+
} = component;
|
|
546
|
+
|
|
547
|
+
// collect input-only variables
|
|
548
|
+
if (inputs) {
|
|
549
|
+
if (valuesKey) {
|
|
550
|
+
variables = [...variables, valuesKey];
|
|
551
|
+
}
|
|
552
|
+
EXPRESSION_PROPERTIES.forEach(prop => {
|
|
553
|
+
const property = get(component, prop.split('.'));
|
|
554
|
+
if (property && expressionLanguage.isExpression(property)) {
|
|
555
|
+
const expressionVariables = expressionLanguage.getVariableNames(property, {
|
|
556
|
+
type: 'expression'
|
|
557
|
+
});
|
|
558
|
+
variables = [...variables, ...expressionVariables];
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
TEMPLATE_PROPERTIES.forEach(prop => {
|
|
562
|
+
const property = get(component, prop.split('.'));
|
|
563
|
+
if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
|
|
564
|
+
const templateVariables = templating.getVariableNames(property);
|
|
565
|
+
variables = [...variables, ...templateVariables];
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
return variables.filter(variable => variable !== undefined || variable !== null);
|
|
570
|
+
}, []);
|
|
571
|
+
const getBindingVariables = node => {
|
|
572
|
+
const bindingVariable = [];
|
|
573
|
+
|
|
574
|
+
// c.f. https://github.com/bpmn-io/form-js/issues/778 @Skaiir to remove?
|
|
575
|
+
if (node.type === 'button') {
|
|
576
|
+
return [];
|
|
577
|
+
} else if (node.key) {
|
|
578
|
+
return [node.key.split('.')[0]];
|
|
579
|
+
} else if (node.path) {
|
|
580
|
+
return [node.path.split('.')[0]];
|
|
581
|
+
} else if (node.components) {
|
|
582
|
+
node.components.forEach(component => {
|
|
583
|
+
bindingVariable.push(...getBindingVariables(component));
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return bindingVariable;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
// collect binding variables
|
|
590
|
+
if (inputs || outputs) {
|
|
591
|
+
variables.push(...getBindingVariables(schema));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// remove duplicates
|
|
595
|
+
return Array.from(new Set(variables));
|
|
596
|
+
}
|
|
597
|
+
function runRecursively(formField, fn) {
|
|
598
|
+
const components = formField.components || [];
|
|
599
|
+
components.forEach((component, index) => {
|
|
600
|
+
runRecursively(component, fn);
|
|
601
|
+
});
|
|
602
|
+
fn(formField);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @typedef {object} Condition
|
|
607
|
+
* @property {string} [hide]
|
|
393
608
|
*/
|
|
394
609
|
|
|
395
610
|
class ConditionChecker {
|
|
396
|
-
constructor(formFieldRegistry, eventBus) {
|
|
611
|
+
constructor(formFieldRegistry, pathRegistry, eventBus) {
|
|
397
612
|
this._formFieldRegistry = formFieldRegistry;
|
|
613
|
+
this._pathRegistry = pathRegistry;
|
|
398
614
|
this._eventBus = eventBus;
|
|
399
615
|
}
|
|
400
616
|
|
|
401
|
-
/**
|
|
402
|
-
* For given data, remove properties based on condition.
|
|
403
|
-
*
|
|
404
|
-
* @param {Object<string, any>} properties
|
|
405
|
-
* @param {Object<string, any>} data
|
|
617
|
+
/**
|
|
618
|
+
* For given data, remove properties based on condition.
|
|
619
|
+
*
|
|
620
|
+
* @param {Object<string, any>} properties
|
|
621
|
+
* @param {Object<string, any>} data
|
|
406
622
|
*/
|
|
407
623
|
applyConditions(properties, data = {}) {
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
for (const {
|
|
413
|
-
key,
|
|
414
|
-
condition
|
|
415
|
-
} of conditions) {
|
|
416
|
-
const shouldRemove = this._checkHideCondition(condition, data);
|
|
417
|
-
if (shouldRemove) {
|
|
418
|
-
delete newProperties[key];
|
|
419
|
-
}
|
|
624
|
+
const newProperties = clone(properties);
|
|
625
|
+
const form = this._formFieldRegistry.getAll().find(field => field.type === 'default');
|
|
626
|
+
if (!form) {
|
|
627
|
+
throw new Error('form field registry has no form');
|
|
420
628
|
}
|
|
629
|
+
this._pathRegistry.executeRecursivelyOnFields(form, ({
|
|
630
|
+
field,
|
|
631
|
+
isClosed,
|
|
632
|
+
context
|
|
633
|
+
}) => {
|
|
634
|
+
const {
|
|
635
|
+
conditional: condition
|
|
636
|
+
} = field;
|
|
637
|
+
context.isHidden = context.isHidden || condition && this._checkHideCondition(condition, data);
|
|
638
|
+
|
|
639
|
+
// only clear the leaf nodes, as groups may both point to the same path
|
|
640
|
+
if (context.isHidden && isClosed) {
|
|
641
|
+
const valuePath = this._pathRegistry.getValuePath(field);
|
|
642
|
+
this._clearObjectValueRecursively(valuePath, newProperties);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
421
645
|
return newProperties;
|
|
422
646
|
}
|
|
423
647
|
|
|
424
|
-
/**
|
|
425
|
-
* Check if given condition is met. Returns null for invalid/missing conditions.
|
|
426
|
-
*
|
|
427
|
-
* @param {string} condition
|
|
428
|
-
* @param {import('../../types').Data} [data]
|
|
429
|
-
*
|
|
430
|
-
* @returns {boolean|null}
|
|
648
|
+
/**
|
|
649
|
+
* Check if given condition is met. Returns null for invalid/missing conditions.
|
|
650
|
+
*
|
|
651
|
+
* @param {string} condition
|
|
652
|
+
* @param {import('../../types').Data} [data]
|
|
653
|
+
*
|
|
654
|
+
* @returns {boolean|null}
|
|
431
655
|
*/
|
|
432
656
|
check(condition, data = {}) {
|
|
433
657
|
if (!condition) {
|
|
@@ -448,12 +672,12 @@ class ConditionChecker {
|
|
|
448
672
|
}
|
|
449
673
|
}
|
|
450
674
|
|
|
451
|
-
/**
|
|
452
|
-
* Check if hide condition is met.
|
|
453
|
-
*
|
|
454
|
-
* @param {Condition} condition
|
|
455
|
-
* @param {Object<string, any>} data
|
|
456
|
-
* @returns {boolean}
|
|
675
|
+
/**
|
|
676
|
+
* Check if hide condition is met.
|
|
677
|
+
*
|
|
678
|
+
* @param {Condition} condition
|
|
679
|
+
* @param {Object<string, any>} data
|
|
680
|
+
* @returns {boolean}
|
|
457
681
|
*/
|
|
458
682
|
_checkHideCondition(condition, data) {
|
|
459
683
|
if (!condition.hide) {
|
|
@@ -462,24 +686,18 @@ class ConditionChecker {
|
|
|
462
686
|
const result = this.check(condition.hide, data);
|
|
463
687
|
return result === true;
|
|
464
688
|
}
|
|
465
|
-
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
key,
|
|
475
|
-
condition
|
|
476
|
-
}];
|
|
477
|
-
}
|
|
478
|
-
return conditions;
|
|
479
|
-
}, []);
|
|
689
|
+
_clearObjectValueRecursively(valuePath, obj) {
|
|
690
|
+
const workingValuePath = [...valuePath];
|
|
691
|
+
let recurse = false;
|
|
692
|
+
do {
|
|
693
|
+
set(obj, workingValuePath, undefined);
|
|
694
|
+
workingValuePath.pop();
|
|
695
|
+
const parentObject = get(obj, workingValuePath);
|
|
696
|
+
recurse = isObject(parentObject) && !values(parentObject).length && !!workingValuePath.length;
|
|
697
|
+
} while (recurse);
|
|
480
698
|
}
|
|
481
699
|
}
|
|
482
|
-
ConditionChecker.$inject = ['formFieldRegistry', 'eventBus'];
|
|
700
|
+
ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'eventBus'];
|
|
483
701
|
|
|
484
702
|
var ExpressionLanguageModule = {
|
|
485
703
|
__init__: ['expressionLanguage', 'templating', 'conditionChecker'],
|
|
@@ -495,12 +713,12 @@ class MarkdownRenderer {
|
|
|
495
713
|
this._converter = new showdown.Converter();
|
|
496
714
|
}
|
|
497
715
|
|
|
498
|
-
/**
|
|
499
|
-
* Render markdown to HTML.
|
|
500
|
-
*
|
|
501
|
-
* @param {string} markdown - The markdown to render
|
|
502
|
-
*
|
|
503
|
-
* @returns {string} HTML
|
|
716
|
+
/**
|
|
717
|
+
* Render markdown to HTML.
|
|
718
|
+
*
|
|
719
|
+
* @param {string} markdown - The markdown to render
|
|
720
|
+
*
|
|
721
|
+
* @returns {string} HTML
|
|
504
722
|
*/
|
|
505
723
|
render(markdown) {
|
|
506
724
|
return this._converter.makeHtml(markdown);
|
|
@@ -984,203 +1202,6 @@ var commandModule = {
|
|
|
984
1202
|
commandStack: ['type', CommandStack]
|
|
985
1203
|
};
|
|
986
1204
|
|
|
987
|
-
// config ///////////////////
|
|
988
|
-
|
|
989
|
-
const MINUTES_IN_DAY = 60 * 24;
|
|
990
|
-
const DATETIME_SUBTYPES = {
|
|
991
|
-
DATE: 'date',
|
|
992
|
-
TIME: 'time',
|
|
993
|
-
DATETIME: 'datetime'
|
|
994
|
-
};
|
|
995
|
-
const TIME_SERIALISING_FORMATS = {
|
|
996
|
-
UTC_OFFSET: 'utc_offset',
|
|
997
|
-
UTC_NORMALIZED: 'utc_normalized',
|
|
998
|
-
NO_TIMEZONE: 'no_timezone'
|
|
999
|
-
};
|
|
1000
|
-
const DATETIME_SUBTYPES_LABELS = {
|
|
1001
|
-
[DATETIME_SUBTYPES.DATE]: 'Date',
|
|
1002
|
-
[DATETIME_SUBTYPES.TIME]: 'Time',
|
|
1003
|
-
[DATETIME_SUBTYPES.DATETIME]: 'Date & Time'
|
|
1004
|
-
};
|
|
1005
|
-
const TIME_SERIALISINGFORMAT_LABELS = {
|
|
1006
|
-
[TIME_SERIALISING_FORMATS.UTC_OFFSET]: 'UTC offset',
|
|
1007
|
-
[TIME_SERIALISING_FORMATS.UTC_NORMALIZED]: 'UTC normalized',
|
|
1008
|
-
[TIME_SERIALISING_FORMATS.NO_TIMEZONE]: 'No timezone'
|
|
1009
|
-
};
|
|
1010
|
-
const DATETIME_SUBTYPE_PATH = ['subtype'];
|
|
1011
|
-
const DATE_LABEL_PATH = ['dateLabel'];
|
|
1012
|
-
const DATE_DISALLOW_PAST_PATH = ['disallowPassedDates'];
|
|
1013
|
-
const TIME_LABEL_PATH = ['timeLabel'];
|
|
1014
|
-
const TIME_USE24H_PATH = ['use24h'];
|
|
1015
|
-
const TIME_INTERVAL_PATH = ['timeInterval'];
|
|
1016
|
-
const TIME_SERIALISING_FORMAT_PATH = ['timeSerializingFormat'];
|
|
1017
|
-
|
|
1018
|
-
// config ///////////////////
|
|
1019
|
-
|
|
1020
|
-
const VALUES_SOURCES = {
|
|
1021
|
-
STATIC: 'static',
|
|
1022
|
-
INPUT: 'input',
|
|
1023
|
-
EXPRESSION: 'expression'
|
|
1024
|
-
};
|
|
1025
|
-
const VALUES_SOURCE_DEFAULT = VALUES_SOURCES.STATIC;
|
|
1026
|
-
const VALUES_SOURCES_LABELS = {
|
|
1027
|
-
[VALUES_SOURCES.STATIC]: 'Static',
|
|
1028
|
-
[VALUES_SOURCES.INPUT]: 'Input data',
|
|
1029
|
-
[VALUES_SOURCES.EXPRESSION]: 'Expression'
|
|
1030
|
-
};
|
|
1031
|
-
const VALUES_SOURCES_PATHS = {
|
|
1032
|
-
[VALUES_SOURCES.STATIC]: ['values'],
|
|
1033
|
-
[VALUES_SOURCES.INPUT]: ['valuesKey'],
|
|
1034
|
-
[VALUES_SOURCES.EXPRESSION]: ['valuesExpression']
|
|
1035
|
-
};
|
|
1036
|
-
const VALUES_SOURCES_DEFAULTS = {
|
|
1037
|
-
[VALUES_SOURCES.STATIC]: [{
|
|
1038
|
-
label: 'Value',
|
|
1039
|
-
value: 'value'
|
|
1040
|
-
}],
|
|
1041
|
-
[VALUES_SOURCES.INPUT]: '',
|
|
1042
|
-
[VALUES_SOURCES.EXPRESSION]: '='
|
|
1043
|
-
};
|
|
1044
|
-
|
|
1045
|
-
// helpers ///////////////////
|
|
1046
|
-
|
|
1047
|
-
function getValuesSource(field) {
|
|
1048
|
-
for (const source of Object.values(VALUES_SOURCES)) {
|
|
1049
|
-
if (get(field, VALUES_SOURCES_PATHS[source]) !== undefined) {
|
|
1050
|
-
return source;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
return VALUES_SOURCE_DEFAULT;
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
function createInjector(bootstrapModules) {
|
|
1057
|
-
const injector = new Injector(bootstrapModules);
|
|
1058
|
-
injector.init();
|
|
1059
|
-
return injector;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
/**
|
|
1063
|
-
* @param {string?} prefix
|
|
1064
|
-
*
|
|
1065
|
-
* @returns Element
|
|
1066
|
-
*/
|
|
1067
|
-
function createFormContainer(prefix = 'fjs') {
|
|
1068
|
-
const container = document.createElement('div');
|
|
1069
|
-
container.classList.add(`${prefix}-container`);
|
|
1070
|
-
return container;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression'];
|
|
1074
|
-
const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text'];
|
|
1075
|
-
function findErrors(errors, path) {
|
|
1076
|
-
return errors[pathStringify(path)];
|
|
1077
|
-
}
|
|
1078
|
-
function isRequired(field) {
|
|
1079
|
-
return field.required;
|
|
1080
|
-
}
|
|
1081
|
-
function pathParse(path) {
|
|
1082
|
-
if (!path) {
|
|
1083
|
-
return [];
|
|
1084
|
-
}
|
|
1085
|
-
return path.split('.').map(key => {
|
|
1086
|
-
return isNaN(parseInt(key)) ? key : parseInt(key);
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
|
-
function pathsEqual(a, b) {
|
|
1090
|
-
return a && b && a.length === b.length && a.every((value, index) => value === b[index]);
|
|
1091
|
-
}
|
|
1092
|
-
function pathStringify(path) {
|
|
1093
|
-
if (!path) {
|
|
1094
|
-
return '';
|
|
1095
|
-
}
|
|
1096
|
-
return path.join('.');
|
|
1097
|
-
}
|
|
1098
|
-
const indices = {};
|
|
1099
|
-
function generateIndexForType(type) {
|
|
1100
|
-
if (type in indices) {
|
|
1101
|
-
indices[type]++;
|
|
1102
|
-
} else {
|
|
1103
|
-
indices[type] = 1;
|
|
1104
|
-
}
|
|
1105
|
-
return indices[type];
|
|
1106
|
-
}
|
|
1107
|
-
function generateIdForType(type) {
|
|
1108
|
-
return `${type}${generateIndexForType(type)}`;
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
/**
|
|
1112
|
-
* @template T
|
|
1113
|
-
* @param {T} data
|
|
1114
|
-
* @param {(this: any, key: string, value: any) => any} [replacer]
|
|
1115
|
-
* @return {T}
|
|
1116
|
-
*/
|
|
1117
|
-
function clone(data, replacer) {
|
|
1118
|
-
return JSON.parse(JSON.stringify(data, replacer));
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
/**
|
|
1122
|
-
* Parse the schema for input variables a form might make use of
|
|
1123
|
-
*
|
|
1124
|
-
* @param {any} schema
|
|
1125
|
-
*
|
|
1126
|
-
* @return {string[]}
|
|
1127
|
-
*/
|
|
1128
|
-
function getSchemaVariables(schema, options = {}) {
|
|
1129
|
-
const {
|
|
1130
|
-
expressionLanguage = new FeelExpressionLanguage(null),
|
|
1131
|
-
templating = new FeelersTemplating(),
|
|
1132
|
-
inputs = true,
|
|
1133
|
-
outputs = true
|
|
1134
|
-
} = options;
|
|
1135
|
-
if (!schema.components) {
|
|
1136
|
-
return [];
|
|
1137
|
-
}
|
|
1138
|
-
const variables = schema.components.reduce((variables, component) => {
|
|
1139
|
-
const {
|
|
1140
|
-
key,
|
|
1141
|
-
valuesKey,
|
|
1142
|
-
type
|
|
1143
|
-
} = component;
|
|
1144
|
-
if (['button'].includes(type)) {
|
|
1145
|
-
return variables;
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// collect bi-directional variables
|
|
1149
|
-
if (inputs || outputs) {
|
|
1150
|
-
if (key) {
|
|
1151
|
-
variables = [...variables, key];
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
// collect input-only variables
|
|
1156
|
-
if (inputs) {
|
|
1157
|
-
if (valuesKey) {
|
|
1158
|
-
variables = [...variables, valuesKey];
|
|
1159
|
-
}
|
|
1160
|
-
EXPRESSION_PROPERTIES.forEach(prop => {
|
|
1161
|
-
const property = get(component, prop.split('.'));
|
|
1162
|
-
if (property && expressionLanguage.isExpression(property)) {
|
|
1163
|
-
const expressionVariables = expressionLanguage.getVariableNames(property, {
|
|
1164
|
-
type: 'expression'
|
|
1165
|
-
});
|
|
1166
|
-
variables = [...variables, ...expressionVariables];
|
|
1167
|
-
}
|
|
1168
|
-
});
|
|
1169
|
-
TEMPLATE_PROPERTIES.forEach(prop => {
|
|
1170
|
-
const property = get(component, prop.split('.'));
|
|
1171
|
-
if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
|
|
1172
|
-
const templateVariables = templating.getVariableNames(property);
|
|
1173
|
-
variables = [...variables, ...templateVariables];
|
|
1174
|
-
}
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
return variables.filter(variable => variable !== undefined || variable !== null);
|
|
1178
|
-
}, []);
|
|
1179
|
-
|
|
1180
|
-
// remove duplicates
|
|
1181
|
-
return Array.from(new Set(variables));
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1205
|
class UpdateFieldValidationHandler {
|
|
1185
1206
|
constructor(form, validator) {
|
|
1186
1207
|
this._form = form;
|
|
@@ -1191,15 +1212,12 @@ class UpdateFieldValidationHandler {
|
|
|
1191
1212
|
field,
|
|
1192
1213
|
value
|
|
1193
1214
|
} = context;
|
|
1194
|
-
const {
|
|
1195
|
-
_path
|
|
1196
|
-
} = field;
|
|
1197
1215
|
const {
|
|
1198
1216
|
errors
|
|
1199
1217
|
} = this._form._getState();
|
|
1200
1218
|
context.oldErrors = clone(errors);
|
|
1201
1219
|
const fieldErrors = this._validator.validateField(field, value);
|
|
1202
|
-
const updatedErrors = set(errors, [
|
|
1220
|
+
const updatedErrors = set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
1203
1221
|
this._form._setState({
|
|
1204
1222
|
errors: updatedErrors
|
|
1205
1223
|
});
|
|
@@ -1848,8 +1866,8 @@ Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
|
|
|
1848
1866
|
|
|
1849
1867
|
// helpers //////////
|
|
1850
1868
|
|
|
1851
|
-
/**
|
|
1852
|
-
* Helper function to evaluate optional FEEL validation values.
|
|
1869
|
+
/**
|
|
1870
|
+
* Helper function to evaluate optional FEEL validation values.
|
|
1853
1871
|
*/
|
|
1854
1872
|
function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
|
|
1855
1873
|
const evaluatedValidate = {
|
|
@@ -1882,72 +1900,415 @@ function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form
|
|
|
1882
1900
|
return evaluatedValidate;
|
|
1883
1901
|
}
|
|
1884
1902
|
|
|
1885
|
-
class
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1903
|
+
class Importer {
|
|
1904
|
+
/**
|
|
1905
|
+
* @constructor
|
|
1906
|
+
* @param { import('./FormFieldRegistry').default } formFieldRegistry
|
|
1907
|
+
* @param { import('./PathRegistry').default } pathRegistry
|
|
1908
|
+
* @param { import('./FieldFactory').default } fieldFactory
|
|
1909
|
+
* @param { import('./FormLayouter').default } formLayouter
|
|
1910
|
+
*/
|
|
1911
|
+
constructor(formFieldRegistry, pathRegistry, fieldFactory, formLayouter) {
|
|
1912
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
1913
|
+
this._pathRegistry = pathRegistry;
|
|
1914
|
+
this._fieldFactory = fieldFactory;
|
|
1915
|
+
this._formLayouter = formLayouter;
|
|
1892
1916
|
}
|
|
1893
|
-
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* Import schema creating rows, fields, attaching additional
|
|
1920
|
+
* information to each field and adding fields to the
|
|
1921
|
+
* field registry.
|
|
1922
|
+
*
|
|
1923
|
+
* Additional information attached:
|
|
1924
|
+
*
|
|
1925
|
+
* * `id` (unless present)
|
|
1926
|
+
* * `_parent`
|
|
1927
|
+
* * `_path`
|
|
1928
|
+
*
|
|
1929
|
+
* @param {any} schema
|
|
1930
|
+
*
|
|
1931
|
+
* @typedef {{ warnings: Error[], schema: any }} ImportResult
|
|
1932
|
+
* @returns {ImportResult}
|
|
1933
|
+
*/
|
|
1934
|
+
importSchema(schema) {
|
|
1935
|
+
// TODO: Add warnings
|
|
1936
|
+
const warnings = [];
|
|
1937
|
+
try {
|
|
1938
|
+
this._cleanup();
|
|
1939
|
+
const importedSchema = this.importFormField(clone(schema));
|
|
1940
|
+
this._formLayouter.calculateLayout(clone(importedSchema));
|
|
1941
|
+
return {
|
|
1942
|
+
schema: importedSchema,
|
|
1943
|
+
warnings
|
|
1944
|
+
};
|
|
1945
|
+
} catch (err) {
|
|
1946
|
+
this._cleanup();
|
|
1947
|
+
err.warnings = warnings;
|
|
1948
|
+
throw err;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
_cleanup() {
|
|
1952
|
+
this._formLayouter.clear();
|
|
1953
|
+
this._formFieldRegistry.clear();
|
|
1954
|
+
this._pathRegistry.clear();
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
/**
|
|
1958
|
+
* @param {{[x: string]: any}} fieldAttrs
|
|
1959
|
+
* @param {String} [parentId]
|
|
1960
|
+
* @param {number} [index]
|
|
1961
|
+
*
|
|
1962
|
+
* @return {any} field
|
|
1963
|
+
*/
|
|
1964
|
+
importFormField(fieldAttrs, parentId, index) {
|
|
1894
1965
|
const {
|
|
1895
|
-
|
|
1896
|
-
} =
|
|
1897
|
-
|
|
1898
|
-
|
|
1966
|
+
components
|
|
1967
|
+
} = fieldAttrs;
|
|
1968
|
+
let parent, path;
|
|
1969
|
+
if (parentId) {
|
|
1970
|
+
parent = this._formFieldRegistry.get(parentId);
|
|
1899
1971
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1972
|
+
|
|
1973
|
+
// set form field path
|
|
1974
|
+
path = parent ? [...parent._path, 'components', index] : [];
|
|
1975
|
+
const field = this._fieldFactory.create({
|
|
1976
|
+
...fieldAttrs,
|
|
1977
|
+
_path: path,
|
|
1978
|
+
_parent: parentId
|
|
1979
|
+
}, false);
|
|
1980
|
+
this._formFieldRegistry.add(field);
|
|
1981
|
+
if (components) {
|
|
1982
|
+
field.components = this.importFormFields(components, field.id);
|
|
1983
|
+
}
|
|
1984
|
+
return field;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* @param {Array<any>} components
|
|
1989
|
+
* @param {string} parentId
|
|
1990
|
+
*
|
|
1991
|
+
* @return {Array<any>} imported components
|
|
1992
|
+
*/
|
|
1993
|
+
importFormFields(components, parentId) {
|
|
1994
|
+
return components.map((component, index) => {
|
|
1995
|
+
return this.importFormField(component, parentId, index);
|
|
1902
1996
|
});
|
|
1903
|
-
this._formFields[id] = formField;
|
|
1904
1997
|
}
|
|
1905
|
-
|
|
1998
|
+
}
|
|
1999
|
+
Importer.$inject = ['formFieldRegistry', 'pathRegistry', 'fieldFactory', 'formLayouter'];
|
|
2000
|
+
|
|
2001
|
+
class FieldFactory {
|
|
2002
|
+
/**
|
|
2003
|
+
* @constructor
|
|
2004
|
+
*
|
|
2005
|
+
* @param formFieldRegistry
|
|
2006
|
+
* @param formFields
|
|
2007
|
+
*/
|
|
2008
|
+
constructor(formFieldRegistry, pathRegistry, formFields) {
|
|
2009
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
2010
|
+
this._pathRegistry = pathRegistry;
|
|
2011
|
+
this._formFields = formFields;
|
|
2012
|
+
}
|
|
2013
|
+
create(attrs, applyDefaults = true) {
|
|
1906
2014
|
const {
|
|
1907
|
-
id
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2015
|
+
id,
|
|
2016
|
+
type,
|
|
2017
|
+
key,
|
|
2018
|
+
path,
|
|
2019
|
+
_parent
|
|
2020
|
+
} = attrs;
|
|
2021
|
+
const fieldDefinition = this._formFields.get(type);
|
|
2022
|
+
if (!fieldDefinition) {
|
|
2023
|
+
throw new Error(`form field of type <${type}> not supported`);
|
|
1911
2024
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
2025
|
+
const {
|
|
2026
|
+
config
|
|
2027
|
+
} = fieldDefinition;
|
|
2028
|
+
if (!config) {
|
|
2029
|
+
throw new Error(`form field of type <${type}> has no config`);
|
|
2030
|
+
}
|
|
2031
|
+
if (id && this._formFieldRegistry._ids.assigned(id)) {
|
|
2032
|
+
throw new Error(`form field with id <${id}> already exists`);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// ensure that we can claim the path
|
|
2036
|
+
|
|
2037
|
+
const parent = _parent && this._formFieldRegistry.get(_parent);
|
|
2038
|
+
const parentPath = parent && this._pathRegistry.getValuePath(parent) || [];
|
|
2039
|
+
if (config.keyed && key && !this._pathRegistry.canClaimPath([...parentPath, ...key.split('.')], true)) {
|
|
2040
|
+
throw new Error(`binding path '${[...parentPath, key].join('.')}' is already claimed`);
|
|
2041
|
+
}
|
|
2042
|
+
if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], false)) {
|
|
2043
|
+
throw new Error(`binding path '${[...parentPath, ...path.split('.')].join('.')}' is already claimed`);
|
|
2044
|
+
}
|
|
2045
|
+
const labelAttrs = applyDefaults && config.label ? {
|
|
2046
|
+
label: config.label
|
|
2047
|
+
} : {};
|
|
2048
|
+
const field = config.create({
|
|
2049
|
+
...labelAttrs,
|
|
2050
|
+
...attrs
|
|
1914
2051
|
});
|
|
1915
|
-
|
|
2052
|
+
this._ensureId(field);
|
|
2053
|
+
if (config.keyed) {
|
|
2054
|
+
this._ensureKey(field);
|
|
2055
|
+
}
|
|
2056
|
+
if (config.pathed && path) {
|
|
2057
|
+
this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), false);
|
|
2058
|
+
}
|
|
2059
|
+
return field;
|
|
1916
2060
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
2061
|
+
_ensureId(field) {
|
|
2062
|
+
if (field.id) {
|
|
2063
|
+
this._formFieldRegistry._ids.claim(field.id, field);
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
let prefix = 'Field';
|
|
2067
|
+
if (field.type === 'default') {
|
|
2068
|
+
prefix = 'Form';
|
|
2069
|
+
}
|
|
2070
|
+
field.id = this._formFieldRegistry._ids.nextPrefixed(`${prefix}_`, field);
|
|
1919
2071
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
2072
|
+
_ensureKey(field) {
|
|
2073
|
+
if (!field.key) {
|
|
2074
|
+
let random;
|
|
2075
|
+
const parent = this._formFieldRegistry.get(field._parent);
|
|
2076
|
+
|
|
2077
|
+
// ensure key uniqueness at level
|
|
2078
|
+
do {
|
|
2079
|
+
random = Math.random().toString(36).substring(7);
|
|
2080
|
+
} while (parent && parent.components.some(child => child.key === random));
|
|
2081
|
+
field.key = `${field.type}_${random}`;
|
|
2082
|
+
}
|
|
2083
|
+
this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), true);
|
|
1922
2084
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
2085
|
+
}
|
|
2086
|
+
FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
|
|
2087
|
+
|
|
2088
|
+
/**
|
|
2089
|
+
* The PathRegistry class manages a hierarchical structure of paths associated with form fields.
|
|
2090
|
+
* It enables claiming, unclaiming, and validating paths within this structure.
|
|
2091
|
+
*
|
|
2092
|
+
* Example Tree Structure:
|
|
2093
|
+
*
|
|
2094
|
+
* [
|
|
2095
|
+
* {
|
|
2096
|
+
* segment: 'root',
|
|
2097
|
+
* claimCount: 1,
|
|
2098
|
+
* children: [
|
|
2099
|
+
* {
|
|
2100
|
+
* segment: 'child1',
|
|
2101
|
+
* claimCount: 2,
|
|
2102
|
+
* children: null // A leaf node (closed path)
|
|
2103
|
+
* },
|
|
2104
|
+
* {
|
|
2105
|
+
* segment: 'child2',
|
|
2106
|
+
* claimCount: 1,
|
|
2107
|
+
* children: [
|
|
2108
|
+
* {
|
|
2109
|
+
* segment: 'subChild1',
|
|
2110
|
+
* claimCount: 1,
|
|
2111
|
+
* children: [] // An open node (open path)
|
|
2112
|
+
* }
|
|
2113
|
+
* ]
|
|
2114
|
+
* }
|
|
2115
|
+
* ]
|
|
2116
|
+
* }
|
|
2117
|
+
* ]
|
|
2118
|
+
*/
|
|
2119
|
+
class PathRegistry {
|
|
2120
|
+
constructor(formFieldRegistry, formFields) {
|
|
2121
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
2122
|
+
this._formFields = formFields;
|
|
2123
|
+
this._dataPaths = [];
|
|
2124
|
+
}
|
|
2125
|
+
canClaimPath(path, closed = false) {
|
|
2126
|
+
let node = {
|
|
2127
|
+
children: this._dataPaths
|
|
2128
|
+
};
|
|
2129
|
+
for (const segment of path) {
|
|
2130
|
+
node = _getNextSegment(node, segment);
|
|
2131
|
+
|
|
2132
|
+
// if no node at that path, we can claim it no matter what
|
|
2133
|
+
if (!node) {
|
|
2134
|
+
return true;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// if we reach a leaf node, definitely not claimable
|
|
2138
|
+
if (node.children === null) {
|
|
2139
|
+
return false;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
// if after all segments we reach a node with children, we can claim it only openly
|
|
2144
|
+
return !closed;
|
|
2145
|
+
}
|
|
2146
|
+
claimPath(path, closed = false) {
|
|
2147
|
+
if (!this.canClaimPath(path, closed)) {
|
|
2148
|
+
throw new Error(`cannot claim path '${path.join('.')}'`);
|
|
2149
|
+
}
|
|
2150
|
+
let node = {
|
|
2151
|
+
children: this._dataPaths
|
|
2152
|
+
};
|
|
2153
|
+
for (const segment of path) {
|
|
2154
|
+
let child = _getNextSegment(node, segment);
|
|
2155
|
+
if (!child) {
|
|
2156
|
+
child = {
|
|
2157
|
+
segment,
|
|
2158
|
+
claimCount: 1,
|
|
2159
|
+
children: []
|
|
2160
|
+
};
|
|
2161
|
+
node.children.push(child);
|
|
2162
|
+
} else {
|
|
2163
|
+
child.claimCount++;
|
|
2164
|
+
}
|
|
2165
|
+
node = child;
|
|
2166
|
+
}
|
|
2167
|
+
if (closed) {
|
|
2168
|
+
node.children = null;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
unclaimPath(path) {
|
|
2172
|
+
// verification Pass
|
|
2173
|
+
let node = {
|
|
2174
|
+
children: this._dataPaths
|
|
2175
|
+
};
|
|
2176
|
+
for (const segment of path) {
|
|
2177
|
+
const child = _getNextSegment(node, segment);
|
|
2178
|
+
if (!child) {
|
|
2179
|
+
throw new Error(`no open path found for '${path.join('.')}'`);
|
|
2180
|
+
}
|
|
2181
|
+
node = child;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
// mutation Pass
|
|
2185
|
+
node = {
|
|
2186
|
+
children: this._dataPaths
|
|
2187
|
+
};
|
|
2188
|
+
for (const segment of path) {
|
|
2189
|
+
const child = _getNextSegment(node, segment);
|
|
2190
|
+
child.claimCount--;
|
|
2191
|
+
if (child.claimCount === 0) {
|
|
2192
|
+
node.children.splice(node.children.indexOf(child), 1);
|
|
2193
|
+
break; // Abort early if claimCount reaches zero
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
node = child;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
/**
|
|
2201
|
+
* Applies a function (fn) recursively on a given field and its children.
|
|
2202
|
+
*
|
|
2203
|
+
* - `field`: Starting field object.
|
|
2204
|
+
* - `fn`: Function to apply.
|
|
2205
|
+
* - `context`: Optional object for passing data between calls.
|
|
2206
|
+
*
|
|
2207
|
+
* Stops early if `fn` returns `false`. Useful for traversing the form field tree.
|
|
2208
|
+
*
|
|
2209
|
+
* @returns {boolean} Success status based on function execution.
|
|
2210
|
+
*/
|
|
2211
|
+
executeRecursivelyOnFields(field, fn, context = {}) {
|
|
2212
|
+
let result = true;
|
|
2213
|
+
const formFieldConfig = this._formFields.get(field.type).config;
|
|
2214
|
+
if (formFieldConfig.keyed) {
|
|
2215
|
+
const callResult = fn({
|
|
2216
|
+
field,
|
|
2217
|
+
isClosed: true,
|
|
2218
|
+
context
|
|
2219
|
+
});
|
|
2220
|
+
return result && callResult;
|
|
2221
|
+
} else if (formFieldConfig.pathed) {
|
|
2222
|
+
const callResult = fn({
|
|
2223
|
+
field,
|
|
2224
|
+
isClosed: false,
|
|
2225
|
+
context
|
|
2226
|
+
});
|
|
2227
|
+
result = result && callResult;
|
|
2228
|
+
}
|
|
2229
|
+
if (field.components) {
|
|
2230
|
+
for (const child of field.components) {
|
|
2231
|
+
const callResult = this.executeRecursivelyOnFields(child, fn, clone(context));
|
|
2232
|
+
result = result && callResult;
|
|
2233
|
+
|
|
2234
|
+
// only stop executing if false is specifically returned, not if undefined
|
|
2235
|
+
if (result === false) {
|
|
2236
|
+
return result;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
return result;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
/**
|
|
2244
|
+
* Generates an array representing the binding path to an underlying data object for a form field.
|
|
2245
|
+
*
|
|
2246
|
+
* @param {Object} field - The field object with properties: `key`, `path`, `id`, and optionally `_parent`.
|
|
2247
|
+
* @param {Object} [options={}] - Configuration options.
|
|
2248
|
+
* @param {Object} [options.replacements={}] - A map of field IDs to alternative path arrays.
|
|
2249
|
+
* @param {Object} [options.cutoffNode] - The ID of the parent field at which to stop generating the path.
|
|
2250
|
+
*
|
|
2251
|
+
* @returns {(Array<string>|undefined)} An array of strings representing the binding path, or undefined if not determinable.
|
|
2252
|
+
*/
|
|
2253
|
+
getValuePath(field, options = {}) {
|
|
2254
|
+
const {
|
|
2255
|
+
replacements = {},
|
|
2256
|
+
cutoffNode = null
|
|
2257
|
+
} = options;
|
|
2258
|
+
let localValuePath = [];
|
|
2259
|
+
const hasReplacement = Object.prototype.hasOwnProperty.call(replacements, field.id);
|
|
2260
|
+
const formFieldConfig = this._formFields.get(field.type).config;
|
|
2261
|
+
if (hasReplacement) {
|
|
2262
|
+
const replacement = replacements[field.id];
|
|
2263
|
+
if (replacement === null || replacement === undefined || replacement === '') {
|
|
2264
|
+
localValuePath = [];
|
|
2265
|
+
} else if (typeof replacement === 'string') {
|
|
2266
|
+
localValuePath = replacement.split('.');
|
|
2267
|
+
} else if (Array.isArray(replacement)) {
|
|
2268
|
+
localValuePath = replacement;
|
|
2269
|
+
} else {
|
|
2270
|
+
throw new Error(`replacements for field ${field.id} must be a string, array or null/undefined`);
|
|
2271
|
+
}
|
|
2272
|
+
} else if (formFieldConfig.keyed) {
|
|
2273
|
+
localValuePath = field.key.split('.');
|
|
2274
|
+
} else if (formFieldConfig.pathed && field.path) {
|
|
2275
|
+
localValuePath = field.path.split('.');
|
|
2276
|
+
}
|
|
2277
|
+
if (field._parent && field._parent !== cutoffNode) {
|
|
2278
|
+
const parent = this._formFieldRegistry.get(field._parent);
|
|
2279
|
+
return [...(this.getValuePath(parent, options) || []), ...localValuePath];
|
|
2280
|
+
}
|
|
2281
|
+
return localValuePath;
|
|
1925
2282
|
}
|
|
1926
2283
|
clear() {
|
|
1927
|
-
this.
|
|
1928
|
-
this._ids.clear();
|
|
1929
|
-
this._keys.clear();
|
|
2284
|
+
this._dataPaths = [];
|
|
1930
2285
|
}
|
|
1931
2286
|
}
|
|
1932
|
-
|
|
2287
|
+
const _getNextSegment = (node, segment) => {
|
|
2288
|
+
if (isArray(node.children)) {
|
|
2289
|
+
return node.children.find(node => node.segment === segment) || null;
|
|
2290
|
+
}
|
|
2291
|
+
return null;
|
|
2292
|
+
};
|
|
2293
|
+
PathRegistry.$inject = ['formFieldRegistry', 'formFields'];
|
|
1933
2294
|
|
|
1934
|
-
/**
|
|
1935
|
-
* @typedef { { id: String, components: Array<String> } } FormRow
|
|
1936
|
-
* @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
|
|
2295
|
+
/**
|
|
2296
|
+
* @typedef { { id: String, components: Array<String> } } FormRow
|
|
2297
|
+
* @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
|
|
1937
2298
|
*/
|
|
1938
2299
|
|
|
1939
|
-
/**
|
|
1940
|
-
* Maintains the Form layout in a given structure, for example
|
|
1941
|
-
*
|
|
1942
|
-
* [
|
|
1943
|
-
* {
|
|
1944
|
-
* formFieldId: 'FormField_1',
|
|
1945
|
-
* rows: [
|
|
1946
|
-
* { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
|
|
1947
|
-
* ]
|
|
1948
|
-
* }
|
|
1949
|
-
* ]
|
|
1950
|
-
*
|
|
2300
|
+
/**
|
|
2301
|
+
* Maintains the Form layout in a given structure, for example
|
|
2302
|
+
*
|
|
2303
|
+
* [
|
|
2304
|
+
* {
|
|
2305
|
+
* formFieldId: 'FormField_1',
|
|
2306
|
+
* rows: [
|
|
2307
|
+
* { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
|
|
2308
|
+
* ]
|
|
2309
|
+
* }
|
|
2310
|
+
* ]
|
|
2311
|
+
*
|
|
1951
2312
|
*/
|
|
1952
2313
|
class FormLayouter {
|
|
1953
2314
|
constructor(eventBus) {
|
|
@@ -1957,8 +2318,8 @@ class FormLayouter {
|
|
|
1957
2318
|
this._eventBus = eventBus;
|
|
1958
2319
|
}
|
|
1959
2320
|
|
|
1960
|
-
/**
|
|
1961
|
-
* @param {FormRow} row
|
|
2321
|
+
/**
|
|
2322
|
+
* @param {FormRow} row
|
|
1962
2323
|
*/
|
|
1963
2324
|
addRow(formFieldId, row) {
|
|
1964
2325
|
let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
|
|
@@ -1972,18 +2333,18 @@ class FormLayouter {
|
|
|
1972
2333
|
rowsPerComponent.rows.push(row);
|
|
1973
2334
|
}
|
|
1974
2335
|
|
|
1975
|
-
/**
|
|
1976
|
-
* @param {String} id
|
|
1977
|
-
* @returns {FormRow}
|
|
2336
|
+
/**
|
|
2337
|
+
* @param {String} id
|
|
2338
|
+
* @returns {FormRow}
|
|
1978
2339
|
*/
|
|
1979
2340
|
getRow(id) {
|
|
1980
2341
|
const rows = allRows(this._rows);
|
|
1981
2342
|
return rows.find(r => r.id === id);
|
|
1982
2343
|
}
|
|
1983
2344
|
|
|
1984
|
-
/**
|
|
1985
|
-
* @param {any} formField
|
|
1986
|
-
* @returns {FormRow}
|
|
2345
|
+
/**
|
|
2346
|
+
* @param {any} formField
|
|
2347
|
+
* @returns {FormRow}
|
|
1987
2348
|
*/
|
|
1988
2349
|
getRowForField(formField) {
|
|
1989
2350
|
return allRows(this._rows).find(r => {
|
|
@@ -1994,9 +2355,9 @@ class FormLayouter {
|
|
|
1994
2355
|
});
|
|
1995
2356
|
}
|
|
1996
2357
|
|
|
1997
|
-
/**
|
|
1998
|
-
* @param {String} formFieldId
|
|
1999
|
-
* @returns { Array<FormRow> }
|
|
2358
|
+
/**
|
|
2359
|
+
* @param {String} formFieldId
|
|
2360
|
+
* @returns { Array<FormRow> }
|
|
2000
2361
|
*/
|
|
2001
2362
|
getRows(formFieldId) {
|
|
2002
2363
|
const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
|
|
@@ -2006,22 +2367,22 @@ class FormLayouter {
|
|
|
2006
2367
|
return rowsForField.rows;
|
|
2007
2368
|
}
|
|
2008
2369
|
|
|
2009
|
-
/**
|
|
2010
|
-
* @returns {string}
|
|
2370
|
+
/**
|
|
2371
|
+
* @returns {string}
|
|
2011
2372
|
*/
|
|
2012
2373
|
nextRowId() {
|
|
2013
2374
|
return this._ids.nextPrefixed('Row_');
|
|
2014
2375
|
}
|
|
2015
2376
|
|
|
2016
|
-
/**
|
|
2017
|
-
* @param {any} formField
|
|
2377
|
+
/**
|
|
2378
|
+
* @param {any} formField
|
|
2018
2379
|
*/
|
|
2019
2380
|
calculateLayout(formField) {
|
|
2020
2381
|
const {
|
|
2021
2382
|
type,
|
|
2022
2383
|
components
|
|
2023
2384
|
} = formField;
|
|
2024
|
-
if (type !== 'default' || !components) {
|
|
2385
|
+
if (type !== 'default' && type !== 'group' || !components) {
|
|
2025
2386
|
return;
|
|
2026
2387
|
}
|
|
2027
2388
|
|
|
@@ -2068,155 +2429,60 @@ function groupByRow(components, ids) {
|
|
|
2068
2429
|
});
|
|
2069
2430
|
}
|
|
2070
2431
|
|
|
2071
|
-
/**
|
|
2072
|
-
* @param {Array<FormRows>} formRows
|
|
2073
|
-
* @returns {Array<FormRow>}
|
|
2432
|
+
/**
|
|
2433
|
+
* @param {Array<FormRows>} formRows
|
|
2434
|
+
* @returns {Array<FormRow>}
|
|
2074
2435
|
*/
|
|
2075
2436
|
function allRows(formRows) {
|
|
2076
2437
|
return flatten(formRows.map(c => c.rows));
|
|
2077
2438
|
}
|
|
2078
2439
|
|
|
2079
|
-
class
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
*/
|
|
2086
|
-
constructor(formFieldRegistry, formFields, formLayouter) {
|
|
2087
|
-
this._formFieldRegistry = formFieldRegistry;
|
|
2088
|
-
this._formFields = formFields;
|
|
2089
|
-
this._formLayouter = formLayouter;
|
|
2440
|
+
class FormFieldRegistry {
|
|
2441
|
+
constructor(eventBus) {
|
|
2442
|
+
this._eventBus = eventBus;
|
|
2443
|
+
this._formFields = {};
|
|
2444
|
+
eventBus.on('form.clear', () => this.clear());
|
|
2445
|
+
this._ids = new Ids([32, 36, 1]);
|
|
2090
2446
|
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
* @param {any} schema
|
|
2098
|
-
* @param {any} [data]
|
|
2099
|
-
*
|
|
2100
|
-
* @return { { warnings: Array<any>, schema: any, data: any } }
|
|
2101
|
-
*/
|
|
2102
|
-
importSchema(schema, data = {}) {
|
|
2103
|
-
// TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
|
|
2104
|
-
const warnings = [];
|
|
2105
|
-
try {
|
|
2106
|
-
this._formLayouter.clear();
|
|
2107
|
-
const importedSchema = this.importFormField(clone(schema)),
|
|
2108
|
-
initializedData = this.initializeFieldValues(clone(data));
|
|
2109
|
-
this._formLayouter.calculateLayout(clone(importedSchema));
|
|
2110
|
-
return {
|
|
2111
|
-
warnings,
|
|
2112
|
-
schema: importedSchema,
|
|
2113
|
-
data: initializedData
|
|
2114
|
-
};
|
|
2115
|
-
} catch (err) {
|
|
2116
|
-
err.warnings = warnings;
|
|
2117
|
-
throw err;
|
|
2447
|
+
add(formField) {
|
|
2448
|
+
const {
|
|
2449
|
+
id
|
|
2450
|
+
} = formField;
|
|
2451
|
+
if (this._formFields[id]) {
|
|
2452
|
+
throw new Error(`form field with ID ${id} already exists`);
|
|
2118
2453
|
}
|
|
2454
|
+
this._eventBus.fire('formField.add', {
|
|
2455
|
+
formField
|
|
2456
|
+
});
|
|
2457
|
+
this._formFields[id] = formField;
|
|
2119
2458
|
}
|
|
2120
|
-
|
|
2121
|
-
/**
|
|
2122
|
-
* @param {any} formField
|
|
2123
|
-
* @param {string} [parentId]
|
|
2124
|
-
*
|
|
2125
|
-
* @return {any} importedField
|
|
2126
|
-
*/
|
|
2127
|
-
importFormField(formField, parentId) {
|
|
2459
|
+
remove(formField) {
|
|
2128
2460
|
const {
|
|
2129
|
-
|
|
2130
|
-
key,
|
|
2131
|
-
type,
|
|
2132
|
-
id = generateIdForType(type)
|
|
2461
|
+
id
|
|
2133
2462
|
} = formField;
|
|
2134
|
-
if (
|
|
2135
|
-
|
|
2136
|
-
formField._parent = parentId;
|
|
2137
|
-
}
|
|
2138
|
-
if (!this._formFields.get(type)) {
|
|
2139
|
-
throw new Error(`form field of type <${type}> not supported`);
|
|
2140
|
-
}
|
|
2141
|
-
if (key) {
|
|
2142
|
-
// validate <key> uniqueness
|
|
2143
|
-
if (this._formFieldRegistry._keys.assigned(key)) {
|
|
2144
|
-
throw new Error(`form field with key <${key}> already exists`);
|
|
2145
|
-
}
|
|
2146
|
-
this._formFieldRegistry._keys.claim(key, formField);
|
|
2147
|
-
|
|
2148
|
-
// TODO: buttons should not have key
|
|
2149
|
-
if (type !== 'button') {
|
|
2150
|
-
// set form field path
|
|
2151
|
-
formField._path = [key];
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
if (id) {
|
|
2155
|
-
// validate <id> uniqueness
|
|
2156
|
-
if (this._formFieldRegistry._ids.assigned(id)) {
|
|
2157
|
-
throw new Error(`form field with id <${id}> already exists`);
|
|
2158
|
-
}
|
|
2159
|
-
this._formFieldRegistry._ids.claim(id, formField);
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
// set form field ID
|
|
2163
|
-
formField.id = id;
|
|
2164
|
-
this._formFieldRegistry.add(formField);
|
|
2165
|
-
if (components) {
|
|
2166
|
-
this.importFormFields(components, id);
|
|
2463
|
+
if (!this._formFields[id]) {
|
|
2464
|
+
return;
|
|
2167
2465
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
importFormFields(components, parentId) {
|
|
2171
|
-
components.forEach(component => {
|
|
2172
|
-
this.importFormField(component, parentId);
|
|
2466
|
+
this._eventBus.fire('formField.remove', {
|
|
2467
|
+
formField
|
|
2173
2468
|
});
|
|
2469
|
+
delete this._formFields[id];
|
|
2470
|
+
}
|
|
2471
|
+
get(id) {
|
|
2472
|
+
return this._formFields[id];
|
|
2473
|
+
}
|
|
2474
|
+
getAll() {
|
|
2475
|
+
return Object.values(this._formFields);
|
|
2174
2476
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
initializeFieldValues(data) {
|
|
2182
|
-
return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
|
|
2183
|
-
const {
|
|
2184
|
-
defaultValue,
|
|
2185
|
-
_path,
|
|
2186
|
-
type
|
|
2187
|
-
} = formField;
|
|
2188
|
-
|
|
2189
|
-
// try to get value from data
|
|
2190
|
-
// if unavailable - try to get default value from form field
|
|
2191
|
-
// if unavailable - get empty value from form field
|
|
2192
|
-
|
|
2193
|
-
if (_path) {
|
|
2194
|
-
const {
|
|
2195
|
-
config: fieldConfig
|
|
2196
|
-
} = this._formFields.get(type);
|
|
2197
|
-
let valueData = get(data, _path);
|
|
2198
|
-
if (!isUndefined(valueData) && fieldConfig.sanitizeValue) {
|
|
2199
|
-
valueData = fieldConfig.sanitizeValue({
|
|
2200
|
-
formField,
|
|
2201
|
-
data,
|
|
2202
|
-
value: valueData
|
|
2203
|
-
});
|
|
2204
|
-
}
|
|
2205
|
-
const initializedFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
|
|
2206
|
-
initializedData = {
|
|
2207
|
-
...initializedData,
|
|
2208
|
-
[_path[0]]: initializedFieldValue
|
|
2209
|
-
};
|
|
2210
|
-
}
|
|
2211
|
-
return initializedData;
|
|
2212
|
-
}, data);
|
|
2477
|
+
forEach(callback) {
|
|
2478
|
+
this.getAll().forEach(formField => callback(formField));
|
|
2479
|
+
}
|
|
2480
|
+
clear() {
|
|
2481
|
+
this._formFields = {};
|
|
2482
|
+
this._ids.clear();
|
|
2213
2483
|
}
|
|
2214
2484
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
var importModule = {
|
|
2218
|
-
importer: ['type', Importer]
|
|
2219
|
-
};
|
|
2485
|
+
FormFieldRegistry.$inject = ['eventBus'];
|
|
2220
2486
|
|
|
2221
2487
|
function formFieldClasses(type, {
|
|
2222
2488
|
errors = [],
|
|
@@ -2271,7 +2537,7 @@ function Button(props) {
|
|
|
2271
2537
|
}
|
|
2272
2538
|
Button.config = {
|
|
2273
2539
|
type: type$c,
|
|
2274
|
-
keyed:
|
|
2540
|
+
keyed: false,
|
|
2275
2541
|
label: 'Button',
|
|
2276
2542
|
group: 'action',
|
|
2277
2543
|
create: (options = {}) => ({
|
|
@@ -2281,6 +2547,9 @@ Button.config = {
|
|
|
2281
2547
|
};
|
|
2282
2548
|
|
|
2283
2549
|
const FormRenderContext = createContext({
|
|
2550
|
+
EmptyRoot: props => {
|
|
2551
|
+
return null;
|
|
2552
|
+
},
|
|
2284
2553
|
Empty: props => {
|
|
2285
2554
|
return null;
|
|
2286
2555
|
},
|
|
@@ -2310,15 +2579,19 @@ const FormRenderContext = createContext({
|
|
|
2310
2579
|
class: props.class,
|
|
2311
2580
|
children: props.children
|
|
2312
2581
|
});
|
|
2582
|
+
},
|
|
2583
|
+
hoveredId: [],
|
|
2584
|
+
setHoveredId: newValue => {
|
|
2585
|
+
console.log(`setHoveredId not defined, called with '${newValue}'`);
|
|
2313
2586
|
}
|
|
2314
2587
|
});
|
|
2315
2588
|
var FormRenderContext$1 = FormRenderContext;
|
|
2316
2589
|
|
|
2317
|
-
/**
|
|
2318
|
-
* @param {string} type
|
|
2319
|
-
* @param {boolean} [strict]
|
|
2320
|
-
*
|
|
2321
|
-
* @returns {any}
|
|
2590
|
+
/**
|
|
2591
|
+
* @param {string} type
|
|
2592
|
+
* @param {boolean} [strict]
|
|
2593
|
+
*
|
|
2594
|
+
* @returns {any}
|
|
2322
2595
|
*/
|
|
2323
2596
|
function getService(type, strict) {}
|
|
2324
2597
|
const FormContext = createContext({
|
|
@@ -2334,10 +2607,10 @@ function useService(type, strict) {
|
|
|
2334
2607
|
return getService(type, strict);
|
|
2335
2608
|
}
|
|
2336
2609
|
|
|
2337
|
-
/**
|
|
2338
|
-
* Returns the conditionally filtered data of a form reactively.
|
|
2339
|
-
* Memoised to minimize re-renders
|
|
2340
|
-
*
|
|
2610
|
+
/**
|
|
2611
|
+
* Returns the conditionally filtered data of a form reactively.
|
|
2612
|
+
* Memoised to minimize re-renders
|
|
2613
|
+
*
|
|
2341
2614
|
*/
|
|
2342
2615
|
function useFilteredFormData() {
|
|
2343
2616
|
const {
|
|
@@ -2354,12 +2627,12 @@ function useFilteredFormData() {
|
|
|
2354
2627
|
}, [conditionChecker, data, initialData]);
|
|
2355
2628
|
}
|
|
2356
2629
|
|
|
2357
|
-
/**
|
|
2358
|
-
* Evaluate if condition is met reactively based on the conditionChecker and form data.
|
|
2359
|
-
*
|
|
2360
|
-
* @param {string | undefined} condition
|
|
2361
|
-
*
|
|
2362
|
-
* @returns {boolean} true if condition is met or no condition or condition checker exists
|
|
2630
|
+
/**
|
|
2631
|
+
* Evaluate if condition is met reactively based on the conditionChecker and form data.
|
|
2632
|
+
*
|
|
2633
|
+
* @param {string | undefined} condition
|
|
2634
|
+
*
|
|
2635
|
+
* @returns {boolean} true if condition is met or no condition or condition checker exists
|
|
2363
2636
|
*/
|
|
2364
2637
|
function useCondition(condition) {
|
|
2365
2638
|
const conditionChecker = useService('conditionChecker', false);
|
|
@@ -2369,13 +2642,13 @@ function useCondition(condition) {
|
|
|
2369
2642
|
}, [conditionChecker, condition, filteredData]);
|
|
2370
2643
|
}
|
|
2371
2644
|
|
|
2372
|
-
/**
|
|
2373
|
-
* Evaluate a string reactively based on the expressionLanguage and form data.
|
|
2374
|
-
* If the string is not an expression, it is returned as is.
|
|
2375
|
-
* Memoised to minimize re-renders.
|
|
2376
|
-
*
|
|
2377
|
-
* @param {string} value
|
|
2378
|
-
*
|
|
2645
|
+
/**
|
|
2646
|
+
* Evaluate a string reactively based on the expressionLanguage and form data.
|
|
2647
|
+
* If the string is not an expression, it is returned as is.
|
|
2648
|
+
* Memoised to minimize re-renders.
|
|
2649
|
+
*
|
|
2650
|
+
* @param {string} value
|
|
2651
|
+
*
|
|
2379
2652
|
*/
|
|
2380
2653
|
function useExpressionEvaluation(value) {
|
|
2381
2654
|
const formData = useFilteredFormData();
|
|
@@ -2404,16 +2677,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
|
|
|
2404
2677
|
});
|
|
2405
2678
|
}
|
|
2406
2679
|
|
|
2407
|
-
/**
|
|
2408
|
-
* Retrieve readonly value of a form field, given it can be an
|
|
2409
|
-
* expression optionally or configured globally.
|
|
2410
|
-
*
|
|
2411
|
-
* @typedef { import('../../types').FormProperties } FormProperties
|
|
2412
|
-
*
|
|
2413
|
-
* @param {any} formField
|
|
2414
|
-
* @param {FormProperties} properties
|
|
2415
|
-
*
|
|
2416
|
-
* @returns {boolean}
|
|
2680
|
+
/**
|
|
2681
|
+
* Retrieve readonly value of a form field, given it can be an
|
|
2682
|
+
* expression optionally or configured globally.
|
|
2683
|
+
*
|
|
2684
|
+
* @typedef { import('../../types').FormProperties } FormProperties
|
|
2685
|
+
*
|
|
2686
|
+
* @param {any} formField
|
|
2687
|
+
* @param {FormProperties} properties
|
|
2688
|
+
*
|
|
2689
|
+
* @returns {boolean}
|
|
2417
2690
|
*/
|
|
2418
2691
|
function useReadonly(formField, properties = {}) {
|
|
2419
2692
|
const expressionLanguage = useService('expressionLanguage');
|
|
@@ -2431,16 +2704,16 @@ function useReadonly(formField, properties = {}) {
|
|
|
2431
2704
|
return readonly || false;
|
|
2432
2705
|
}
|
|
2433
2706
|
|
|
2434
|
-
/**
|
|
2435
|
-
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2436
|
-
* Memoised to minimize re-renders
|
|
2437
|
-
*
|
|
2438
|
-
* @param {string} value
|
|
2439
|
-
* @param {Object} options
|
|
2440
|
-
* @param {boolean} [options.debug = false]
|
|
2441
|
-
* @param {boolean} [options.strict = false]
|
|
2442
|
-
* @param {Function} [options.buildDebugString]
|
|
2443
|
-
*
|
|
2707
|
+
/**
|
|
2708
|
+
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2709
|
+
* Memoised to minimize re-renders
|
|
2710
|
+
*
|
|
2711
|
+
* @param {string} value
|
|
2712
|
+
* @param {Object} options
|
|
2713
|
+
* @param {boolean} [options.debug = false]
|
|
2714
|
+
* @param {boolean} [options.strict = false]
|
|
2715
|
+
* @param {Function} [options.buildDebugString]
|
|
2716
|
+
*
|
|
2444
2717
|
*/
|
|
2445
2718
|
function useTemplateEvaluation(value, options) {
|
|
2446
2719
|
const filteredData = useFilteredFormData();
|
|
@@ -2453,17 +2726,17 @@ function useTemplateEvaluation(value, options) {
|
|
|
2453
2726
|
}, [filteredData, templating, value, options]);
|
|
2454
2727
|
}
|
|
2455
2728
|
|
|
2456
|
-
/**
|
|
2457
|
-
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2458
|
-
* If the string contains multiple lines, only the first line is returned.
|
|
2459
|
-
* Memoised to minimize re-renders
|
|
2460
|
-
*
|
|
2461
|
-
* @param {string} value
|
|
2462
|
-
* @param {Object} [options]
|
|
2463
|
-
* @param {boolean} [options.debug = false]
|
|
2464
|
-
* @param {boolean} [options.strict = false]
|
|
2465
|
-
* @param {Function} [options.buildDebugString]
|
|
2466
|
-
*
|
|
2729
|
+
/**
|
|
2730
|
+
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2731
|
+
* If the string contains multiple lines, only the first line is returned.
|
|
2732
|
+
* Memoised to minimize re-renders
|
|
2733
|
+
*
|
|
2734
|
+
* @param {string} value
|
|
2735
|
+
* @param {Object} [options]
|
|
2736
|
+
* @param {boolean} [options.debug = false]
|
|
2737
|
+
* @param {boolean} [options.strict = false]
|
|
2738
|
+
* @param {Function} [options.buildDebugString]
|
|
2739
|
+
*
|
|
2467
2740
|
*/
|
|
2468
2741
|
function useSingleLineTemplateEvaluation(value, options = {}) {
|
|
2469
2742
|
const evaluatedTemplate = useTemplateEvaluation(value, options);
|
|
@@ -2651,9 +2924,24 @@ function _isReadableType(value) {
|
|
|
2651
2924
|
function _isValueSomething(value) {
|
|
2652
2925
|
return value || value === 0 || value === false;
|
|
2653
2926
|
}
|
|
2927
|
+
function createEmptyOptions(options = {}) {
|
|
2928
|
+
const defaults = {};
|
|
2654
2929
|
|
|
2655
|
-
|
|
2656
|
-
|
|
2930
|
+
// provide default values if valuesKey and valuesExpression are not set
|
|
2931
|
+
if (!options.valuesKey && !options.valuesExpression) {
|
|
2932
|
+
defaults.values = [{
|
|
2933
|
+
label: 'Value',
|
|
2934
|
+
value: 'value'
|
|
2935
|
+
}];
|
|
2936
|
+
}
|
|
2937
|
+
return {
|
|
2938
|
+
...defaults,
|
|
2939
|
+
...options
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
/**
|
|
2944
|
+
* @enum { String }
|
|
2657
2945
|
*/
|
|
2658
2946
|
const LOAD_STATES = {
|
|
2659
2947
|
LOADING: 'loading',
|
|
@@ -2661,17 +2949,17 @@ const LOAD_STATES = {
|
|
|
2661
2949
|
ERROR: 'error'
|
|
2662
2950
|
};
|
|
2663
2951
|
|
|
2664
|
-
/**
|
|
2665
|
-
* @typedef {Object} ValuesGetter
|
|
2666
|
-
* @property {Object[]} values - The values data
|
|
2667
|
-
* @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
|
|
2952
|
+
/**
|
|
2953
|
+
* @typedef {Object} ValuesGetter
|
|
2954
|
+
* @property {Object[]} values - The values data
|
|
2955
|
+
* @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
|
|
2668
2956
|
*/
|
|
2669
2957
|
|
|
2670
|
-
/**
|
|
2671
|
-
* A hook to load values for single and multiselect components.
|
|
2672
|
-
*
|
|
2673
|
-
* @param {Object} field - The form field to handle values for
|
|
2674
|
-
* @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
|
|
2958
|
+
/**
|
|
2959
|
+
* A hook to load values for single and multiselect components.
|
|
2960
|
+
*
|
|
2961
|
+
* @param {Object} field - The form field to handle values for
|
|
2962
|
+
* @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
|
|
2675
2963
|
*/
|
|
2676
2964
|
function useValuesAsync (field) {
|
|
2677
2965
|
const {
|
|
@@ -3027,21 +3315,7 @@ Checklist.config = {
|
|
|
3027
3315
|
group: 'selection',
|
|
3028
3316
|
emptyValue: [],
|
|
3029
3317
|
sanitizeValue: sanitizeMultiSelectValue,
|
|
3030
|
-
create:
|
|
3031
|
-
const defaults = {};
|
|
3032
|
-
|
|
3033
|
-
// provide default values if valuesKey isn't set
|
|
3034
|
-
if (!options.valuesKey) {
|
|
3035
|
-
defaults.values = [{
|
|
3036
|
-
label: 'Value',
|
|
3037
|
-
value: 'value'
|
|
3038
|
-
}];
|
|
3039
|
-
}
|
|
3040
|
-
return {
|
|
3041
|
-
...defaults,
|
|
3042
|
-
...options
|
|
3043
|
-
};
|
|
3044
|
-
}
|
|
3318
|
+
create: createEmptyOptions
|
|
3045
3319
|
};
|
|
3046
3320
|
|
|
3047
3321
|
const noop$1 = () => false;
|
|
@@ -3052,6 +3326,7 @@ function FormField(props) {
|
|
|
3052
3326
|
} = props;
|
|
3053
3327
|
const formFields = useService('formFields'),
|
|
3054
3328
|
viewerCommands = useService('viewerCommands', false),
|
|
3329
|
+
pathRegistry = useService('pathRegistry'),
|
|
3055
3330
|
form = useService('form');
|
|
3056
3331
|
const {
|
|
3057
3332
|
initialData,
|
|
@@ -3068,10 +3343,10 @@ function FormField(props) {
|
|
|
3068
3343
|
if (!FormFieldComponent) {
|
|
3069
3344
|
throw new Error(`cannot render field <${field.type}>`);
|
|
3070
3345
|
}
|
|
3071
|
-
const
|
|
3072
|
-
const
|
|
3073
|
-
const fieldErrors = findErrors(errors, field._path);
|
|
3346
|
+
const valuePath = useMemo(() => pathRegistry.getValuePath(field), [field, pathRegistry]);
|
|
3347
|
+
const initialValue = useMemo(() => get(initialData, valuePath), [initialData, valuePath]);
|
|
3074
3348
|
const readonly = useReadonly(field, properties);
|
|
3349
|
+
const value = get(data, valuePath);
|
|
3075
3350
|
|
|
3076
3351
|
// add precedence: global readonly > form field disabled
|
|
3077
3352
|
const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
|
|
@@ -3098,7 +3373,7 @@ function FormField(props) {
|
|
|
3098
3373
|
children: jsx(FormFieldComponent, {
|
|
3099
3374
|
...props,
|
|
3100
3375
|
disabled: disabled,
|
|
3101
|
-
errors:
|
|
3376
|
+
errors: errors[field.id],
|
|
3102
3377
|
onChange: disabled || readonly ? noop$1 : onChange,
|
|
3103
3378
|
onBlur: disabled || readonly ? noop$1 : onBlur,
|
|
3104
3379
|
readonly: readonly,
|
|
@@ -3108,14 +3383,14 @@ function FormField(props) {
|
|
|
3108
3383
|
});
|
|
3109
3384
|
}
|
|
3110
3385
|
|
|
3111
|
-
function
|
|
3386
|
+
function Grid(props) {
|
|
3112
3387
|
const {
|
|
3113
3388
|
Children,
|
|
3114
|
-
Empty,
|
|
3115
3389
|
Row
|
|
3116
3390
|
} = useContext(FormRenderContext$1);
|
|
3117
3391
|
const {
|
|
3118
|
-
field
|
|
3392
|
+
field,
|
|
3393
|
+
Empty
|
|
3119
3394
|
} = props;
|
|
3120
3395
|
const {
|
|
3121
3396
|
id,
|
|
@@ -3152,7 +3427,20 @@ function Default(props) {
|
|
|
3152
3427
|
}), components.length ? null : jsx(Empty, {})]
|
|
3153
3428
|
});
|
|
3154
3429
|
}
|
|
3155
|
-
|
|
3430
|
+
|
|
3431
|
+
function FormComponent$1(props) {
|
|
3432
|
+
const {
|
|
3433
|
+
EmptyRoot
|
|
3434
|
+
} = useContext(FormRenderContext$1);
|
|
3435
|
+
const fullProps = {
|
|
3436
|
+
...props,
|
|
3437
|
+
Empty: EmptyRoot
|
|
3438
|
+
};
|
|
3439
|
+
return jsx(Grid, {
|
|
3440
|
+
...fullProps
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
FormComponent$1.config = {
|
|
3156
3444
|
type: 'default',
|
|
3157
3445
|
keyed: false,
|
|
3158
3446
|
label: null,
|
|
@@ -3181,6 +3469,74 @@ var SvgCalendar = function SvgCalendar(props) {
|
|
|
3181
3469
|
};
|
|
3182
3470
|
var CalendarIcon = SvgCalendar;
|
|
3183
3471
|
|
|
3472
|
+
/**
|
|
3473
|
+
* Returns date format for the provided locale.
|
|
3474
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3475
|
+
*
|
|
3476
|
+
* @param {string} [locale] - The locale to get date format for.
|
|
3477
|
+
* @returns {string} The date format for the locale.
|
|
3478
|
+
*/
|
|
3479
|
+
function getLocaleDateFormat(locale = 'default') {
|
|
3480
|
+
const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(Date.UTC(2020, 5, 5)));
|
|
3481
|
+
return parts.map(part => {
|
|
3482
|
+
const len = part.value.length;
|
|
3483
|
+
switch (part.type) {
|
|
3484
|
+
case 'day':
|
|
3485
|
+
return 'd'.repeat(len);
|
|
3486
|
+
case 'month':
|
|
3487
|
+
return 'M'.repeat(len);
|
|
3488
|
+
case 'year':
|
|
3489
|
+
return 'y'.repeat(len);
|
|
3490
|
+
default:
|
|
3491
|
+
return part.value;
|
|
3492
|
+
}
|
|
3493
|
+
}).join('');
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
/**
|
|
3497
|
+
* Returns readable date format for the provided locale.
|
|
3498
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3499
|
+
*
|
|
3500
|
+
* @param {string} [locale] - The locale to get readable date format for.
|
|
3501
|
+
* @returns {string} The readable date format for the locale.
|
|
3502
|
+
*/
|
|
3503
|
+
function getLocaleReadableDateFormat(locale) {
|
|
3504
|
+
let format = getLocaleDateFormat(locale).toLowerCase();
|
|
3505
|
+
|
|
3506
|
+
// Ensure month is in 'mm' format
|
|
3507
|
+
if (!format.includes('mm')) {
|
|
3508
|
+
format = format.replace('m', 'mm');
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
// Ensure day is in 'dd' format
|
|
3512
|
+
if (!format.includes('dd')) {
|
|
3513
|
+
format = format.replace('d', 'dd');
|
|
3514
|
+
}
|
|
3515
|
+
return format;
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
/**
|
|
3519
|
+
* Returns flatpickr config for the provided locale.
|
|
3520
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3521
|
+
*
|
|
3522
|
+
* @param {string} [locale] - The locale to get flatpickr config for.
|
|
3523
|
+
* @returns {object} The flatpickr config for the locale.
|
|
3524
|
+
*/
|
|
3525
|
+
function getLocaleDateFlatpickrConfig(locale) {
|
|
3526
|
+
return flatpickerizeDateFormat(getLocaleDateFormat(locale));
|
|
3527
|
+
}
|
|
3528
|
+
function flatpickerizeDateFormat(dateFormat) {
|
|
3529
|
+
const useLeadingZero = {
|
|
3530
|
+
day: dateFormat.includes('dd'),
|
|
3531
|
+
month: dateFormat.includes('MM'),
|
|
3532
|
+
year: dateFormat.includes('yyyy')
|
|
3533
|
+
};
|
|
3534
|
+
dateFormat = useLeadingZero.day ? dateFormat.replace('dd', 'd') : dateFormat.replace('d', 'j');
|
|
3535
|
+
dateFormat = useLeadingZero.month ? dateFormat.replace('MM', 'm') : dateFormat.replace('M', 'n');
|
|
3536
|
+
dateFormat = useLeadingZero.year ? dateFormat.replace('yyyy', 'Y') : dateFormat.replace('yy', 'y');
|
|
3537
|
+
return dateFormat;
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3184
3540
|
function InputAdorner(props) {
|
|
3185
3541
|
const {
|
|
3186
3542
|
pre,
|
|
@@ -3255,7 +3611,7 @@ function Datepicker(props) {
|
|
|
3255
3611
|
useEffect(() => {
|
|
3256
3612
|
let config = {
|
|
3257
3613
|
allowInput: true,
|
|
3258
|
-
dateFormat:
|
|
3614
|
+
dateFormat: getLocaleDateFlatpickrConfig(),
|
|
3259
3615
|
static: true,
|
|
3260
3616
|
clickOpens: false,
|
|
3261
3617
|
// TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
|
|
@@ -3347,7 +3703,7 @@ function Datepicker(props) {
|
|
|
3347
3703
|
class: "fjs-input",
|
|
3348
3704
|
disabled: disabled,
|
|
3349
3705
|
readOnly: readonly,
|
|
3350
|
-
placeholder:
|
|
3706
|
+
placeholder: getLocaleReadableDateFormat(),
|
|
3351
3707
|
autoComplete: "off",
|
|
3352
3708
|
onFocus: onInputFocus,
|
|
3353
3709
|
onKeyDown: onInputKeyDown,
|
|
@@ -3843,10 +4199,10 @@ Datetime.config = {
|
|
|
3843
4199
|
}
|
|
3844
4200
|
};
|
|
3845
4201
|
|
|
3846
|
-
/**
|
|
3847
|
-
* This file must not be changed or exchanged.
|
|
3848
|
-
*
|
|
3849
|
-
* @see http://bpmn.io/license for more information.
|
|
4202
|
+
/**
|
|
4203
|
+
* This file must not be changed or exchanged.
|
|
4204
|
+
*
|
|
4205
|
+
* @see http://bpmn.io/license for more information.
|
|
3850
4206
|
*/
|
|
3851
4207
|
function Logo() {
|
|
3852
4208
|
return jsxs("svg", {
|
|
@@ -3968,6 +4324,52 @@ function FormComponent(props) {
|
|
|
3968
4324
|
});
|
|
3969
4325
|
}
|
|
3970
4326
|
|
|
4327
|
+
function Group(props) {
|
|
4328
|
+
const {
|
|
4329
|
+
field
|
|
4330
|
+
} = props;
|
|
4331
|
+
const {
|
|
4332
|
+
label,
|
|
4333
|
+
id,
|
|
4334
|
+
type,
|
|
4335
|
+
showOutline
|
|
4336
|
+
} = field;
|
|
4337
|
+
const {
|
|
4338
|
+
formId
|
|
4339
|
+
} = useContext(FormContext$1);
|
|
4340
|
+
const {
|
|
4341
|
+
Empty
|
|
4342
|
+
} = useContext(FormRenderContext$1);
|
|
4343
|
+
const fullProps = {
|
|
4344
|
+
...props,
|
|
4345
|
+
Empty
|
|
4346
|
+
};
|
|
4347
|
+
return jsxs("div", {
|
|
4348
|
+
className: classNames(formFieldClasses(type), {
|
|
4349
|
+
'fjs-outlined': showOutline
|
|
4350
|
+
}),
|
|
4351
|
+
role: "group",
|
|
4352
|
+
"aria-labelledby": prefixId(id, formId),
|
|
4353
|
+
children: [jsx(Label, {
|
|
4354
|
+
id: prefixId(id, formId),
|
|
4355
|
+
label: label
|
|
4356
|
+
}), jsx(Grid, {
|
|
4357
|
+
...fullProps
|
|
4358
|
+
})]
|
|
4359
|
+
});
|
|
4360
|
+
}
|
|
4361
|
+
Group.config = {
|
|
4362
|
+
type: 'group',
|
|
4363
|
+
pathed: true,
|
|
4364
|
+
label: 'Group',
|
|
4365
|
+
group: 'presentation',
|
|
4366
|
+
create: (options = {}) => ({
|
|
4367
|
+
components: [],
|
|
4368
|
+
showOutline: true,
|
|
4369
|
+
...options
|
|
4370
|
+
})
|
|
4371
|
+
};
|
|
4372
|
+
|
|
3971
4373
|
const NODE_TYPE_TEXT = 3,
|
|
3972
4374
|
NODE_TYPE_ELEMENT = 1;
|
|
3973
4375
|
const ALLOWED_NODES = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'em', 'a', 'p', 'div', 'ul', 'ol', 'li', 'hr', 'blockquote', 'img', 'pre', 'code', 'br', 'strong', 'table', 'thead', 'tbody', 'tr', 'th', 'td'];
|
|
@@ -3978,11 +4380,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
|
|
|
3978
4380
|
|
|
3979
4381
|
const FORM_ELEMENT = document.createElement('form');
|
|
3980
4382
|
|
|
3981
|
-
/**
|
|
3982
|
-
* Sanitize a HTML string and return the cleaned, safe version.
|
|
3983
|
-
*
|
|
3984
|
-
* @param {string} html
|
|
3985
|
-
* @return {string}
|
|
4383
|
+
/**
|
|
4384
|
+
* Sanitize a HTML string and return the cleaned, safe version.
|
|
4385
|
+
*
|
|
4386
|
+
* @param {string} html
|
|
4387
|
+
* @return {string}
|
|
3986
4388
|
*/
|
|
3987
4389
|
|
|
3988
4390
|
// see https://github.com/developit/snarkdown/issues/70
|
|
@@ -4000,29 +4402,29 @@ function sanitizeHTML(html) {
|
|
|
4000
4402
|
}
|
|
4001
4403
|
}
|
|
4002
4404
|
|
|
4003
|
-
/**
|
|
4004
|
-
* Sanitizes an image source to ensure we only allow for data URI and links
|
|
4005
|
-
* that start with http(s).
|
|
4006
|
-
*
|
|
4007
|
-
* Note: Most browsers anyway do not support script execution in <img> elements.
|
|
4008
|
-
*
|
|
4009
|
-
* @param {string} src
|
|
4010
|
-
* @returns {string}
|
|
4405
|
+
/**
|
|
4406
|
+
* Sanitizes an image source to ensure we only allow for data URI and links
|
|
4407
|
+
* that start with http(s).
|
|
4408
|
+
*
|
|
4409
|
+
* Note: Most browsers anyway do not support script execution in <img> elements.
|
|
4410
|
+
*
|
|
4411
|
+
* @param {string} src
|
|
4412
|
+
* @returns {string}
|
|
4011
4413
|
*/
|
|
4012
4414
|
function sanitizeImageSource(src) {
|
|
4013
4415
|
const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
|
|
4014
4416
|
return valid ? src : '';
|
|
4015
4417
|
}
|
|
4016
4418
|
|
|
4017
|
-
/**
|
|
4018
|
-
* Recursively sanitize a HTML node, potentially
|
|
4019
|
-
* removing it, its children or attributes.
|
|
4020
|
-
*
|
|
4021
|
-
* Inspired by https://github.com/developit/snarkdown/issues/70
|
|
4022
|
-
* and https://github.com/cure53/DOMPurify. Simplified
|
|
4023
|
-
* for our use-case.
|
|
4024
|
-
*
|
|
4025
|
-
* @param {Element} node
|
|
4419
|
+
/**
|
|
4420
|
+
* Recursively sanitize a HTML node, potentially
|
|
4421
|
+
* removing it, its children or attributes.
|
|
4422
|
+
*
|
|
4423
|
+
* Inspired by https://github.com/developit/snarkdown/issues/70
|
|
4424
|
+
* and https://github.com/cure53/DOMPurify. Simplified
|
|
4425
|
+
* for our use-case.
|
|
4426
|
+
*
|
|
4427
|
+
* @param {Element} node
|
|
4026
4428
|
*/
|
|
4027
4429
|
function sanitizeNode(node) {
|
|
4028
4430
|
// allow text nodes
|
|
@@ -4066,13 +4468,13 @@ function sanitizeNode(node) {
|
|
|
4066
4468
|
}
|
|
4067
4469
|
}
|
|
4068
4470
|
|
|
4069
|
-
/**
|
|
4070
|
-
* Validates attributes for validity.
|
|
4071
|
-
*
|
|
4072
|
-
* @param {string} lcTag
|
|
4073
|
-
* @param {string} lcName
|
|
4074
|
-
* @param {string} value
|
|
4075
|
-
* @return {boolean}
|
|
4471
|
+
/**
|
|
4472
|
+
* Validates attributes for validity.
|
|
4473
|
+
*
|
|
4474
|
+
* @param {string} lcTag
|
|
4475
|
+
* @param {string} lcName
|
|
4476
|
+
* @param {string} value
|
|
4477
|
+
* @return {boolean}
|
|
4076
4478
|
*/
|
|
4077
4479
|
function isValidAttribute(lcTag, lcName, value) {
|
|
4078
4480
|
// disallow most attributes based on whitelist
|
|
@@ -4556,21 +4958,7 @@ Radio.config = {
|
|
|
4556
4958
|
group: 'selection',
|
|
4557
4959
|
emptyValue: null,
|
|
4558
4960
|
sanitizeValue: sanitizeSingleSelectValue,
|
|
4559
|
-
create:
|
|
4560
|
-
const defaults = {};
|
|
4561
|
-
|
|
4562
|
-
// provide default values if valuesKey isn't set
|
|
4563
|
-
if (!options.valuesKey) {
|
|
4564
|
-
defaults.values = [{
|
|
4565
|
-
label: 'Value',
|
|
4566
|
-
value: 'value'
|
|
4567
|
-
}];
|
|
4568
|
-
}
|
|
4569
|
-
return {
|
|
4570
|
-
...defaults,
|
|
4571
|
-
...options
|
|
4572
|
-
};
|
|
4573
|
-
}
|
|
4961
|
+
create: createEmptyOptions
|
|
4574
4962
|
};
|
|
4575
4963
|
|
|
4576
4964
|
var _path$d;
|
|
@@ -4922,21 +5310,7 @@ Select.config = {
|
|
|
4922
5310
|
group: 'selection',
|
|
4923
5311
|
emptyValue: null,
|
|
4924
5312
|
sanitizeValue: sanitizeSingleSelectValue,
|
|
4925
|
-
create:
|
|
4926
|
-
const defaults = {};
|
|
4927
|
-
|
|
4928
|
-
// provide default values if valuesKey isn't set
|
|
4929
|
-
if (!options.valuesKey) {
|
|
4930
|
-
defaults.values = [{
|
|
4931
|
-
label: 'Value',
|
|
4932
|
-
value: 'value'
|
|
4933
|
-
}];
|
|
4934
|
-
}
|
|
4935
|
-
return {
|
|
4936
|
-
...defaults,
|
|
4937
|
-
...options
|
|
4938
|
-
};
|
|
4939
|
-
}
|
|
5313
|
+
create: createEmptyOptions
|
|
4940
5314
|
};
|
|
4941
5315
|
|
|
4942
5316
|
const type$4 = 'spacer';
|
|
@@ -5164,21 +5538,7 @@ Taglist.config = {
|
|
|
5164
5538
|
group: 'selection',
|
|
5165
5539
|
emptyValue: [],
|
|
5166
5540
|
sanitizeValue: sanitizeMultiSelectValue,
|
|
5167
|
-
create:
|
|
5168
|
-
const defaults = {};
|
|
5169
|
-
|
|
5170
|
-
// provide default values if valuesKey isn't set
|
|
5171
|
-
if (!options.valuesKey) {
|
|
5172
|
-
defaults.values = [{
|
|
5173
|
-
label: 'Value',
|
|
5174
|
-
value: 'value'
|
|
5175
|
-
}];
|
|
5176
|
-
}
|
|
5177
|
-
return {
|
|
5178
|
-
...defaults,
|
|
5179
|
-
...options
|
|
5180
|
-
};
|
|
5181
|
-
}
|
|
5541
|
+
create: createEmptyOptions
|
|
5182
5542
|
};
|
|
5183
5543
|
|
|
5184
5544
|
const type$2 = 'text';
|
|
@@ -5595,13 +5955,14 @@ var SvgGroup = function SvgGroup(props) {
|
|
|
5595
5955
|
return /*#__PURE__*/React.createElement("svg", _extends$8({
|
|
5596
5956
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5597
5957
|
width: 54,
|
|
5598
|
-
height: 54
|
|
5958
|
+
height: 54,
|
|
5959
|
+
fill: "currentcolor"
|
|
5599
5960
|
}, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
|
|
5600
5961
|
fillRule: "evenodd",
|
|
5601
5962
|
d: "M8 33v5a1 1 0 0 0 1 1h4v2H9a3 3 0 0 1-3-3v-5h2Zm18 6v2H15v-2h11Zm13 0v2H28v-2h11Zm9-6v5a3 3 0 0 1-3 3h-4v-2h4a1 1 0 0 0 .993-.883L46 38v-5h2ZM8 22v9H6v-9h2Zm40 0v9h-2v-9h2Zm-35-9v2H9a1 1 0 0 0-.993.883L8 16v4H6v-4a3 3 0 0 1 3-3h4Zm32 0a3 3 0 0 1 3 3v4h-2v-4a1 1 0 0 0-.883-.993L45 15h-4v-2h4Zm-6 0v2H28v-2h11Zm-13 0v2H15v-2h11Z"
|
|
5602
5963
|
})));
|
|
5603
5964
|
};
|
|
5604
|
-
var
|
|
5965
|
+
var GroupIcon = SvgGroup;
|
|
5605
5966
|
|
|
5606
5967
|
var _path$7;
|
|
5607
5968
|
function _extends$7() { _extends$7 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$7.apply(this, arguments); }
|
|
@@ -5654,14 +6015,14 @@ var SvgSpacer = function SvgSpacer(props) {
|
|
|
5654
6015
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5655
6016
|
width: 54,
|
|
5656
6017
|
height: 54,
|
|
5657
|
-
fill: "
|
|
6018
|
+
fill: "currentcolor"
|
|
5658
6019
|
}, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
|
|
5659
|
-
stroke: "
|
|
6020
|
+
stroke: "currentcolor",
|
|
5660
6021
|
strokeLinecap: "square",
|
|
5661
6022
|
strokeWidth: 2,
|
|
5662
6023
|
d: "M9 23h36M9 31h36"
|
|
5663
6024
|
})), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
|
|
5664
|
-
stroke: "
|
|
6025
|
+
stroke: "currentcolor",
|
|
5665
6026
|
strokeLinecap: "round",
|
|
5666
6027
|
strokeLinejoin: "round",
|
|
5667
6028
|
strokeWidth: 2,
|
|
@@ -5739,8 +6100,9 @@ const iconsByType = type => {
|
|
|
5739
6100
|
button: ButtonIcon,
|
|
5740
6101
|
checkbox: CheckboxIcon,
|
|
5741
6102
|
checklist: ChecklistIcon,
|
|
5742
|
-
columns:
|
|
6103
|
+
columns: GroupIcon,
|
|
5743
6104
|
datetime: DatetimeIcon,
|
|
6105
|
+
group: GroupIcon,
|
|
5744
6106
|
image: ImageIcon,
|
|
5745
6107
|
number: NumberIcon,
|
|
5746
6108
|
radio: RadioIcon,
|
|
@@ -5754,7 +6116,7 @@ const iconsByType = type => {
|
|
|
5754
6116
|
}[type];
|
|
5755
6117
|
};
|
|
5756
6118
|
|
|
5757
|
-
const formFields = [Button, Checkbox, Checklist,
|
|
6119
|
+
const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, Image, Numberfield, Datetime, Radio, Select, Spacer, Taglist, Text, Textfield, Textarea];
|
|
5758
6120
|
|
|
5759
6121
|
class FormFields {
|
|
5760
6122
|
constructor() {
|
|
@@ -5830,9 +6192,12 @@ var renderModule = {
|
|
|
5830
6192
|
};
|
|
5831
6193
|
|
|
5832
6194
|
var core = {
|
|
5833
|
-
__depends__: [
|
|
6195
|
+
__depends__: [renderModule],
|
|
5834
6196
|
eventBus: ['type', EventBus],
|
|
6197
|
+
importer: ['type', Importer],
|
|
6198
|
+
fieldFactory: ['type', FieldFactory],
|
|
5835
6199
|
formFieldRegistry: ['type', FormFieldRegistry],
|
|
6200
|
+
pathRegistry: ['type', PathRegistry],
|
|
5836
6201
|
formLayouter: ['type', FormLayouter],
|
|
5837
6202
|
validator: ['type', Validator]
|
|
5838
6203
|
};
|
|
@@ -5947,9 +6312,9 @@ class Form {
|
|
|
5947
6312
|
this.clear();
|
|
5948
6313
|
const {
|
|
5949
6314
|
schema: importedSchema,
|
|
5950
|
-
data: initializedData,
|
|
5951
6315
|
warnings
|
|
5952
|
-
} = this.get('importer').importSchema(schema
|
|
6316
|
+
} = this.get('importer').importSchema(schema);
|
|
6317
|
+
const initializedData = this._initializeFieldData(clone(data));
|
|
5953
6318
|
this._setState({
|
|
5954
6319
|
data: initializedData,
|
|
5955
6320
|
errors: {},
|
|
@@ -6007,21 +6372,21 @@ class Form {
|
|
|
6007
6372
|
*/
|
|
6008
6373
|
validate() {
|
|
6009
6374
|
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6375
|
+
pathRegistry = this.get('pathRegistry'),
|
|
6010
6376
|
validator = this.get('validator');
|
|
6011
6377
|
const {
|
|
6012
6378
|
data
|
|
6013
6379
|
} = this._getState();
|
|
6014
6380
|
const errors = formFieldRegistry.getAll().reduce((errors, field) => {
|
|
6015
6381
|
const {
|
|
6016
|
-
disabled
|
|
6017
|
-
_path
|
|
6382
|
+
disabled
|
|
6018
6383
|
} = field;
|
|
6019
6384
|
if (disabled) {
|
|
6020
6385
|
return errors;
|
|
6021
6386
|
}
|
|
6022
|
-
const value = get(data,
|
|
6387
|
+
const value = get(data, pathRegistry.getValuePath(field));
|
|
6023
6388
|
const fieldErrors = validator.validateField(field, value);
|
|
6024
|
-
return set(errors, [
|
|
6389
|
+
return set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
6025
6390
|
}, /** @type {Errors} */{});
|
|
6026
6391
|
this._setState({
|
|
6027
6392
|
errors
|
|
@@ -6127,16 +6492,14 @@ class Form {
|
|
|
6127
6492
|
value
|
|
6128
6493
|
} = update;
|
|
6129
6494
|
const {
|
|
6130
|
-
_path
|
|
6131
|
-
} = field;
|
|
6132
|
-
let {
|
|
6133
6495
|
data,
|
|
6134
6496
|
errors
|
|
6135
6497
|
} = this._getState();
|
|
6136
|
-
const validator = this.get('validator')
|
|
6498
|
+
const validator = this.get('validator'),
|
|
6499
|
+
pathRegistry = this.get('pathRegistry');
|
|
6137
6500
|
const fieldErrors = validator.validateField(field, value);
|
|
6138
|
-
set(data,
|
|
6139
|
-
set(errors, [
|
|
6501
|
+
set(data, pathRegistry.getValuePath(field), value);
|
|
6502
|
+
set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
6140
6503
|
this._setState({
|
|
6141
6504
|
data: clone(data),
|
|
6142
6505
|
errors: clone(errors)
|
|
@@ -6179,23 +6542,26 @@ class Form {
|
|
|
6179
6542
|
* @internal
|
|
6180
6543
|
*/
|
|
6181
6544
|
_getSubmitData() {
|
|
6182
|
-
const formFieldRegistry = this.get('formFieldRegistry')
|
|
6545
|
+
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6546
|
+
pathRegistry = this.get('pathRegistry'),
|
|
6547
|
+
formFields = this.get('formFields');
|
|
6183
6548
|
const formData = this._getState().data;
|
|
6184
6549
|
const submitData = formFieldRegistry.getAll().reduce((previous, field) => {
|
|
6185
6550
|
const {
|
|
6186
6551
|
disabled,
|
|
6187
|
-
|
|
6552
|
+
type
|
|
6188
6553
|
} = field;
|
|
6554
|
+
const {
|
|
6555
|
+
config: fieldConfig
|
|
6556
|
+
} = formFields.get(type);
|
|
6189
6557
|
|
|
6190
|
-
// do not submit disabled form fields
|
|
6191
|
-
if (disabled || !
|
|
6558
|
+
// do not submit disabled form fields or routing fields
|
|
6559
|
+
if (disabled || !fieldConfig.keyed) {
|
|
6192
6560
|
return previous;
|
|
6193
6561
|
}
|
|
6194
|
-
const
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
[_path[0]]: value
|
|
6198
|
-
};
|
|
6562
|
+
const valuePath = pathRegistry.getValuePath(field);
|
|
6563
|
+
const value = get(formData, valuePath);
|
|
6564
|
+
return set(previous, valuePath, value);
|
|
6199
6565
|
}, {});
|
|
6200
6566
|
const filteredSubmitData = this._applyConditions(submitData, formData);
|
|
6201
6567
|
return filteredSubmitData;
|
|
@@ -6208,9 +6574,46 @@ class Form {
|
|
|
6208
6574
|
const conditionChecker = this.get('conditionChecker');
|
|
6209
6575
|
return conditionChecker.applyConditions(toFilter, data);
|
|
6210
6576
|
}
|
|
6577
|
+
|
|
6578
|
+
/**
|
|
6579
|
+
* @internal
|
|
6580
|
+
*/
|
|
6581
|
+
_initializeFieldData(data) {
|
|
6582
|
+
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6583
|
+
formFields = this.get('formFields'),
|
|
6584
|
+
pathRegistry = this.get('pathRegistry');
|
|
6585
|
+
return formFieldRegistry.getAll().reduce((initializedData, formField) => {
|
|
6586
|
+
const {
|
|
6587
|
+
defaultValue,
|
|
6588
|
+
type
|
|
6589
|
+
} = formField;
|
|
6590
|
+
|
|
6591
|
+
// try to get value from data
|
|
6592
|
+
// if unavailable - try to get default value from form field
|
|
6593
|
+
// if unavailable - get empty value from form field
|
|
6594
|
+
|
|
6595
|
+
const valuePath = pathRegistry.getValuePath(formField);
|
|
6596
|
+
if (valuePath) {
|
|
6597
|
+
const {
|
|
6598
|
+
config: fieldConfig
|
|
6599
|
+
} = formFields.get(type);
|
|
6600
|
+
let valueData = get(data, valuePath);
|
|
6601
|
+
if (!isUndefined(valueData) && fieldConfig.sanitizeValue) {
|
|
6602
|
+
valueData = fieldConfig.sanitizeValue({
|
|
6603
|
+
formField,
|
|
6604
|
+
data,
|
|
6605
|
+
value: valueData
|
|
6606
|
+
});
|
|
6607
|
+
}
|
|
6608
|
+
const initializedFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
|
|
6609
|
+
return set(initializedData, valuePath, initializedFieldValue);
|
|
6610
|
+
}
|
|
6611
|
+
return initializedData;
|
|
6612
|
+
}, data);
|
|
6613
|
+
}
|
|
6211
6614
|
}
|
|
6212
6615
|
|
|
6213
|
-
const schemaVersion =
|
|
6616
|
+
const schemaVersion = 11;
|
|
6214
6617
|
|
|
6215
6618
|
/**
|
|
6216
6619
|
* @typedef { import('./types').CreateFormOptions } CreateFormOptions
|
|
@@ -6235,5 +6638,5 @@ function createForm(options) {
|
|
|
6235
6638
|
});
|
|
6236
6639
|
}
|
|
6237
6640
|
|
|
6238
|
-
export { Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, Default, ExpressionLanguageModule, FeelExpressionLanguage, FeelersTemplating, Form, FormComponent, FormContext$1 as FormContext, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext$1 as FormRenderContext, Image, MINUTES_IN_DAY, MarkdownModule, MarkdownRenderer, Numberfield, Radio, Select, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Taglist, Text, Textarea, Textfield, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, VALUES_SOURCES_PATHS, VALUES_SOURCE_DEFAULT, ViewerCommands, ViewerCommandsModule, clone, createForm, createFormContainer, createInjector,
|
|
6641
|
+
export { Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, FormComponent$1 as Default, ExpressionLanguageModule, FeelExpressionLanguage, FeelersTemplating, FieldFactory, Form, FormComponent, FormContext$1 as FormContext, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext$1 as FormRenderContext, Group, Image, Importer, MINUTES_IN_DAY, MarkdownModule, MarkdownRenderer, Numberfield, PathRegistry, Radio, Select, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Taglist, Text, Textarea, Textfield, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, VALUES_SOURCES_PATHS, VALUES_SOURCE_DEFAULT, ViewerCommands, ViewerCommandsModule, clone, createForm, createFormContainer, createInjector, formFields, generateIdForType, generateIndexForType, getSchemaVariables, getValuesSource, iconsByType, isRequired, pathParse, pathsEqual, runRecursively, schemaVersion };
|
|
6239
6642
|
//# sourceMappingURL=index.es.js.map
|