@bpmn-io/form-js-viewer 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/form-js-base.css +54 -1
- package/dist/assets/form-js.css +54 -1
- package/dist/index.cjs +1074 -636
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +1070 -636
- 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 +4 -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/render/hooks/index.d.ts +2 -0
- package/dist/types/render/hooks/useDeepCompareState.d.ts +8 -0
- package/dist/types/render/hooks/usePrevious.d.ts +1 -0
- 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,11 +1,11 @@
|
|
|
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';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
|
|
8
|
-
import { useContext, useMemo, useEffect,
|
|
8
|
+
import { useContext, useMemo, useEffect, useRef, useState, useCallback, useLayoutEffect } from 'preact/hooks';
|
|
9
9
|
import { createContext, createElement, Fragment, render } from 'preact';
|
|
10
10
|
import * as React from 'preact/compat';
|
|
11
11
|
import { createPortal } from 'preact/compat';
|
|
@@ -387,14 +387,230 @@ class FeelersTemplating {
|
|
|
387
387
|
}
|
|
388
388
|
FeelersTemplating.$inject = [];
|
|
389
389
|
|
|
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
|
+
|
|
390
605
|
/**
|
|
391
606
|
* @typedef {object} Condition
|
|
392
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
|
|
|
@@ -405,19 +621,27 @@ class ConditionChecker {
|
|
|
405
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
|
|
|
@@ -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'],
|
|
@@ -947,239 +1165,42 @@ CommandStack.prototype._popAction = function () {
|
|
|
947
1165
|
execution.trigger = null;
|
|
948
1166
|
}
|
|
949
1167
|
};
|
|
950
|
-
CommandStack.prototype._markDirty = function (elements) {
|
|
951
|
-
const execution = this._currentExecution;
|
|
952
|
-
if (!elements) {
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
elements = isArray(elements) ? elements : [elements];
|
|
956
|
-
execution.dirty = execution.dirty.concat(elements);
|
|
957
|
-
};
|
|
958
|
-
CommandStack.prototype._executedAction = function (action, redo) {
|
|
959
|
-
const stackIdx = ++this._stackIdx;
|
|
960
|
-
if (!redo) {
|
|
961
|
-
this._stack.splice(stackIdx, this._stack.length, action);
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
CommandStack.prototype._revertedAction = function (action) {
|
|
965
|
-
this._stackIdx--;
|
|
966
|
-
};
|
|
967
|
-
CommandStack.prototype._getHandler = function (command) {
|
|
968
|
-
return this._handlerMap[command];
|
|
969
|
-
};
|
|
970
|
-
CommandStack.prototype._setHandler = function (command, handler) {
|
|
971
|
-
if (!command || !handler) {
|
|
972
|
-
throw new Error('command and handler required');
|
|
973
|
-
}
|
|
974
|
-
if (this._handlerMap[command]) {
|
|
975
|
-
throw new Error('overriding handler for command <' + command + '>');
|
|
976
|
-
}
|
|
977
|
-
this._handlerMap[command] = handler;
|
|
978
|
-
};
|
|
979
|
-
|
|
980
|
-
/**
|
|
981
|
-
* @type { import('didi').ModuleDeclaration }
|
|
982
|
-
*/
|
|
983
|
-
var commandModule = {
|
|
984
|
-
commandStack: ['type', CommandStack]
|
|
985
|
-
};
|
|
986
|
-
|
|
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
|
-
}, []);
|
|
1168
|
+
CommandStack.prototype._markDirty = function (elements) {
|
|
1169
|
+
const execution = this._currentExecution;
|
|
1170
|
+
if (!elements) {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
elements = isArray(elements) ? elements : [elements];
|
|
1174
|
+
execution.dirty = execution.dirty.concat(elements);
|
|
1175
|
+
};
|
|
1176
|
+
CommandStack.prototype._executedAction = function (action, redo) {
|
|
1177
|
+
const stackIdx = ++this._stackIdx;
|
|
1178
|
+
if (!redo) {
|
|
1179
|
+
this._stack.splice(stackIdx, this._stack.length, action);
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
CommandStack.prototype._revertedAction = function (action) {
|
|
1183
|
+
this._stackIdx--;
|
|
1184
|
+
};
|
|
1185
|
+
CommandStack.prototype._getHandler = function (command) {
|
|
1186
|
+
return this._handlerMap[command];
|
|
1187
|
+
};
|
|
1188
|
+
CommandStack.prototype._setHandler = function (command, handler) {
|
|
1189
|
+
if (!command || !handler) {
|
|
1190
|
+
throw new Error('command and handler required');
|
|
1191
|
+
}
|
|
1192
|
+
if (this._handlerMap[command]) {
|
|
1193
|
+
throw new Error('overriding handler for command <' + command + '>');
|
|
1194
|
+
}
|
|
1195
|
+
this._handlerMap[command] = handler;
|
|
1196
|
+
};
|
|
1179
1197
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1198
|
+
/**
|
|
1199
|
+
* @type { import('didi').ModuleDeclaration }
|
|
1200
|
+
*/
|
|
1201
|
+
var commandModule = {
|
|
1202
|
+
commandStack: ['type', CommandStack]
|
|
1203
|
+
};
|
|
1183
1204
|
|
|
1184
1205
|
class UpdateFieldValidationHandler {
|
|
1185
1206
|
constructor(form, validator) {
|
|
@@ -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
|
});
|
|
@@ -1882,54 +1900,397 @@ 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
2295
|
/**
|
|
1935
2296
|
* @typedef { { id: String, components: Array<String> } } FormRow
|
|
@@ -2021,7 +2382,7 @@ class FormLayouter {
|
|
|
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
|
|
|
@@ -2076,147 +2437,52 @@ 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;
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
/**
|
|
2093
|
-
* Import schema adding `id`, `_parent` and `_path`
|
|
2094
|
-
* information to each field and adding it to the
|
|
2095
|
-
* form field registry.
|
|
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;
|
|
2118
|
-
}
|
|
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]);
|
|
2119
2446
|
}
|
|
2120
|
-
|
|
2121
|
-
/**
|
|
2122
|
-
* @param {any} formField
|
|
2123
|
-
* @param {string} [parentId]
|
|
2124
|
-
*
|
|
2125
|
-
* @return {any} importedField
|
|
2126
|
-
*/
|
|
2127
|
-
importFormField(formField, parentId) {
|
|
2447
|
+
add(formField) {
|
|
2128
2448
|
const {
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
id
|
|
2133
|
-
} = formField;
|
|
2134
|
-
if (parentId) {
|
|
2135
|
-
// set form field parent
|
|
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);
|
|
2449
|
+
id
|
|
2450
|
+
} = formField;
|
|
2451
|
+
if (this._formFields[id]) {
|
|
2452
|
+
throw new Error(`form field with ID ${id} already exists`);
|
|
2167
2453
|
}
|
|
2168
|
-
|
|
2454
|
+
this._eventBus.fire('formField.add', {
|
|
2455
|
+
formField
|
|
2456
|
+
});
|
|
2457
|
+
this._formFields[id] = formField;
|
|
2169
2458
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2459
|
+
remove(formField) {
|
|
2460
|
+
const {
|
|
2461
|
+
id
|
|
2462
|
+
} = formField;
|
|
2463
|
+
if (!this._formFields[id]) {
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
this._eventBus.fire('formField.remove', {
|
|
2467
|
+
formField
|
|
2173
2468
|
});
|
|
2469
|
+
delete this._formFields[id];
|
|
2174
2470
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
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);
|
|
2471
|
+
get(id) {
|
|
2472
|
+
return this._formFields[id];
|
|
2473
|
+
}
|
|
2474
|
+
getAll() {
|
|
2475
|
+
return Object.values(this._formFields);
|
|
2476
|
+
}
|
|
2477
|
+
forEach(callback) {
|
|
2478
|
+
this.getAll().forEach(formField => callback(formField));
|
|
2479
|
+
}
|
|
2480
|
+
clear() {
|
|
2481
|
+
this._formFields = {};
|
|
2482
|
+
this._ids.clear();
|
|
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,6 +2579,10 @@ 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;
|
|
@@ -2431,6 +2704,37 @@ function useReadonly(formField, properties = {}) {
|
|
|
2431
2704
|
return readonly || false;
|
|
2432
2705
|
}
|
|
2433
2706
|
|
|
2707
|
+
function usePrevious(value, defaultValue, dependencies) {
|
|
2708
|
+
const ref = useRef(defaultValue);
|
|
2709
|
+
useEffect(() => ref.current = value, dependencies);
|
|
2710
|
+
return ref.current;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
/**
|
|
2714
|
+
* A custom hook to manage state changes with deep comparison.
|
|
2715
|
+
*
|
|
2716
|
+
* @param {any} value - The current value to manage.
|
|
2717
|
+
* @param {any} defaultValue - The initial default value for the state.
|
|
2718
|
+
* @returns {any} - Returns the current state.
|
|
2719
|
+
*/
|
|
2720
|
+
function useDeepCompareState(value, defaultValue) {
|
|
2721
|
+
const [state, setState] = useState(defaultValue);
|
|
2722
|
+
const previous = usePrevious(value, defaultValue, [value]);
|
|
2723
|
+
const changed = !compare(previous, value);
|
|
2724
|
+
useEffect(() => {
|
|
2725
|
+
if (changed) {
|
|
2726
|
+
setState(value);
|
|
2727
|
+
}
|
|
2728
|
+
}, [changed, value]);
|
|
2729
|
+
return state;
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
// helpers //////////////////////////
|
|
2733
|
+
|
|
2734
|
+
function compare(a, b) {
|
|
2735
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2434
2738
|
/**
|
|
2435
2739
|
* Template a string reactively based on form data. If the string is not a template, it is returned as is.
|
|
2436
2740
|
* Memoised to minimize re-renders
|
|
@@ -2651,6 +2955,21 @@ function _isReadableType(value) {
|
|
|
2651
2955
|
function _isValueSomething(value) {
|
|
2652
2956
|
return value || value === 0 || value === false;
|
|
2653
2957
|
}
|
|
2958
|
+
function createEmptyOptions(options = {}) {
|
|
2959
|
+
const defaults = {};
|
|
2960
|
+
|
|
2961
|
+
// provide default values if valuesKey and valuesExpression are not set
|
|
2962
|
+
if (!options.valuesKey && !options.valuesExpression) {
|
|
2963
|
+
defaults.values = [{
|
|
2964
|
+
label: 'Value',
|
|
2965
|
+
value: 'value'
|
|
2966
|
+
}];
|
|
2967
|
+
}
|
|
2968
|
+
return {
|
|
2969
|
+
...defaults,
|
|
2970
|
+
...options
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2654
2973
|
|
|
2655
2974
|
/**
|
|
2656
2975
|
* @enum { String }
|
|
@@ -2685,11 +3004,8 @@ function useValuesAsync (field) {
|
|
|
2685
3004
|
state: LOAD_STATES.LOADING
|
|
2686
3005
|
});
|
|
2687
3006
|
const initialData = useService('form')._getState().initialData;
|
|
2688
|
-
const
|
|
2689
|
-
|
|
2690
|
-
return useExpressionEvaluation(valuesExpression);
|
|
2691
|
-
}
|
|
2692
|
-
}, [valuesExpression]);
|
|
3007
|
+
const expressionEvaluation = useExpressionEvaluation(valuesExpression);
|
|
3008
|
+
const evaluatedValues = useDeepCompareState(expressionEvaluation || [], []);
|
|
2693
3009
|
useEffect(() => {
|
|
2694
3010
|
let values = [];
|
|
2695
3011
|
|
|
@@ -2705,8 +3021,10 @@ function useValuesAsync (field) {
|
|
|
2705
3021
|
values = Array.isArray(staticValues) ? staticValues : [];
|
|
2706
3022
|
|
|
2707
3023
|
// expression
|
|
2708
|
-
} else if (
|
|
2709
|
-
|
|
3024
|
+
} else if (valuesExpression) {
|
|
3025
|
+
if (evaluatedValues && Array.isArray(evaluatedValues)) {
|
|
3026
|
+
values = evaluatedValues;
|
|
3027
|
+
}
|
|
2710
3028
|
} else {
|
|
2711
3029
|
setValuesGetter(buildErrorState('No values source defined in the form definition'));
|
|
2712
3030
|
return;
|
|
@@ -2715,7 +3033,7 @@ function useValuesAsync (field) {
|
|
|
2715
3033
|
// normalize data to support primitives and partially defined objects
|
|
2716
3034
|
values = normalizeValuesData(values);
|
|
2717
3035
|
setValuesGetter(buildLoadedState(values));
|
|
2718
|
-
}, [valuesKey, staticValues, initialData]);
|
|
3036
|
+
}, [valuesKey, staticValues, initialData, valuesExpression, evaluatedValues]);
|
|
2719
3037
|
return valuesGetter;
|
|
2720
3038
|
}
|
|
2721
3039
|
const buildErrorState = error => ({
|
|
@@ -3027,21 +3345,7 @@ Checklist.config = {
|
|
|
3027
3345
|
group: 'selection',
|
|
3028
3346
|
emptyValue: [],
|
|
3029
3347
|
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
|
-
}
|
|
3348
|
+
create: createEmptyOptions
|
|
3045
3349
|
};
|
|
3046
3350
|
|
|
3047
3351
|
const noop$1 = () => false;
|
|
@@ -3052,6 +3356,7 @@ function FormField(props) {
|
|
|
3052
3356
|
} = props;
|
|
3053
3357
|
const formFields = useService('formFields'),
|
|
3054
3358
|
viewerCommands = useService('viewerCommands', false),
|
|
3359
|
+
pathRegistry = useService('pathRegistry'),
|
|
3055
3360
|
form = useService('form');
|
|
3056
3361
|
const {
|
|
3057
3362
|
initialData,
|
|
@@ -3068,10 +3373,10 @@ function FormField(props) {
|
|
|
3068
3373
|
if (!FormFieldComponent) {
|
|
3069
3374
|
throw new Error(`cannot render field <${field.type}>`);
|
|
3070
3375
|
}
|
|
3071
|
-
const
|
|
3072
|
-
const
|
|
3073
|
-
const fieldErrors = findErrors(errors, field._path);
|
|
3376
|
+
const valuePath = useMemo(() => pathRegistry.getValuePath(field), [field, pathRegistry]);
|
|
3377
|
+
const initialValue = useMemo(() => get(initialData, valuePath), [initialData, valuePath]);
|
|
3074
3378
|
const readonly = useReadonly(field, properties);
|
|
3379
|
+
const value = get(data, valuePath);
|
|
3075
3380
|
|
|
3076
3381
|
// add precedence: global readonly > form field disabled
|
|
3077
3382
|
const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
|
|
@@ -3098,7 +3403,7 @@ function FormField(props) {
|
|
|
3098
3403
|
children: jsx(FormFieldComponent, {
|
|
3099
3404
|
...props,
|
|
3100
3405
|
disabled: disabled,
|
|
3101
|
-
errors:
|
|
3406
|
+
errors: errors[field.id],
|
|
3102
3407
|
onChange: disabled || readonly ? noop$1 : onChange,
|
|
3103
3408
|
onBlur: disabled || readonly ? noop$1 : onBlur,
|
|
3104
3409
|
readonly: readonly,
|
|
@@ -3108,14 +3413,14 @@ function FormField(props) {
|
|
|
3108
3413
|
});
|
|
3109
3414
|
}
|
|
3110
3415
|
|
|
3111
|
-
function
|
|
3416
|
+
function Grid(props) {
|
|
3112
3417
|
const {
|
|
3113
3418
|
Children,
|
|
3114
|
-
Empty,
|
|
3115
3419
|
Row
|
|
3116
3420
|
} = useContext(FormRenderContext$1);
|
|
3117
3421
|
const {
|
|
3118
|
-
field
|
|
3422
|
+
field,
|
|
3423
|
+
Empty
|
|
3119
3424
|
} = props;
|
|
3120
3425
|
const {
|
|
3121
3426
|
id,
|
|
@@ -3152,7 +3457,20 @@ function Default(props) {
|
|
|
3152
3457
|
}), components.length ? null : jsx(Empty, {})]
|
|
3153
3458
|
});
|
|
3154
3459
|
}
|
|
3155
|
-
|
|
3460
|
+
|
|
3461
|
+
function FormComponent$1(props) {
|
|
3462
|
+
const {
|
|
3463
|
+
EmptyRoot
|
|
3464
|
+
} = useContext(FormRenderContext$1);
|
|
3465
|
+
const fullProps = {
|
|
3466
|
+
...props,
|
|
3467
|
+
Empty: EmptyRoot
|
|
3468
|
+
};
|
|
3469
|
+
return jsx(Grid, {
|
|
3470
|
+
...fullProps
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
FormComponent$1.config = {
|
|
3156
3474
|
type: 'default',
|
|
3157
3475
|
keyed: false,
|
|
3158
3476
|
label: null,
|
|
@@ -3181,6 +3499,74 @@ var SvgCalendar = function SvgCalendar(props) {
|
|
|
3181
3499
|
};
|
|
3182
3500
|
var CalendarIcon = SvgCalendar;
|
|
3183
3501
|
|
|
3502
|
+
/**
|
|
3503
|
+
* Returns date format for the provided locale.
|
|
3504
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3505
|
+
*
|
|
3506
|
+
* @param {string} [locale] - The locale to get date format for.
|
|
3507
|
+
* @returns {string} The date format for the locale.
|
|
3508
|
+
*/
|
|
3509
|
+
function getLocaleDateFormat(locale = 'default') {
|
|
3510
|
+
const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(Date.UTC(2020, 5, 5)));
|
|
3511
|
+
return parts.map(part => {
|
|
3512
|
+
const len = part.value.length;
|
|
3513
|
+
switch (part.type) {
|
|
3514
|
+
case 'day':
|
|
3515
|
+
return 'd'.repeat(len);
|
|
3516
|
+
case 'month':
|
|
3517
|
+
return 'M'.repeat(len);
|
|
3518
|
+
case 'year':
|
|
3519
|
+
return 'y'.repeat(len);
|
|
3520
|
+
default:
|
|
3521
|
+
return part.value;
|
|
3522
|
+
}
|
|
3523
|
+
}).join('');
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
/**
|
|
3527
|
+
* Returns readable date format for the provided locale.
|
|
3528
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3529
|
+
*
|
|
3530
|
+
* @param {string} [locale] - The locale to get readable date format for.
|
|
3531
|
+
* @returns {string} The readable date format for the locale.
|
|
3532
|
+
*/
|
|
3533
|
+
function getLocaleReadableDateFormat(locale) {
|
|
3534
|
+
let format = getLocaleDateFormat(locale).toLowerCase();
|
|
3535
|
+
|
|
3536
|
+
// Ensure month is in 'mm' format
|
|
3537
|
+
if (!format.includes('mm')) {
|
|
3538
|
+
format = format.replace('m', 'mm');
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
// Ensure day is in 'dd' format
|
|
3542
|
+
if (!format.includes('dd')) {
|
|
3543
|
+
format = format.replace('d', 'dd');
|
|
3544
|
+
}
|
|
3545
|
+
return format;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
/**
|
|
3549
|
+
* Returns flatpickr config for the provided locale.
|
|
3550
|
+
* If the locale is not provided, uses the browser's locale.
|
|
3551
|
+
*
|
|
3552
|
+
* @param {string} [locale] - The locale to get flatpickr config for.
|
|
3553
|
+
* @returns {object} The flatpickr config for the locale.
|
|
3554
|
+
*/
|
|
3555
|
+
function getLocaleDateFlatpickrConfig(locale) {
|
|
3556
|
+
return flatpickerizeDateFormat(getLocaleDateFormat(locale));
|
|
3557
|
+
}
|
|
3558
|
+
function flatpickerizeDateFormat(dateFormat) {
|
|
3559
|
+
const useLeadingZero = {
|
|
3560
|
+
day: dateFormat.includes('dd'),
|
|
3561
|
+
month: dateFormat.includes('MM'),
|
|
3562
|
+
year: dateFormat.includes('yyyy')
|
|
3563
|
+
};
|
|
3564
|
+
dateFormat = useLeadingZero.day ? dateFormat.replace('dd', 'd') : dateFormat.replace('d', 'j');
|
|
3565
|
+
dateFormat = useLeadingZero.month ? dateFormat.replace('MM', 'm') : dateFormat.replace('M', 'n');
|
|
3566
|
+
dateFormat = useLeadingZero.year ? dateFormat.replace('yyyy', 'Y') : dateFormat.replace('yy', 'y');
|
|
3567
|
+
return dateFormat;
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3184
3570
|
function InputAdorner(props) {
|
|
3185
3571
|
const {
|
|
3186
3572
|
pre,
|
|
@@ -3255,7 +3641,7 @@ function Datepicker(props) {
|
|
|
3255
3641
|
useEffect(() => {
|
|
3256
3642
|
let config = {
|
|
3257
3643
|
allowInput: true,
|
|
3258
|
-
dateFormat:
|
|
3644
|
+
dateFormat: getLocaleDateFlatpickrConfig(),
|
|
3259
3645
|
static: true,
|
|
3260
3646
|
clickOpens: false,
|
|
3261
3647
|
// TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
|
|
@@ -3347,7 +3733,7 @@ function Datepicker(props) {
|
|
|
3347
3733
|
class: "fjs-input",
|
|
3348
3734
|
disabled: disabled,
|
|
3349
3735
|
readOnly: readonly,
|
|
3350
|
-
placeholder:
|
|
3736
|
+
placeholder: getLocaleReadableDateFormat(),
|
|
3351
3737
|
autoComplete: "off",
|
|
3352
3738
|
onFocus: onInputFocus,
|
|
3353
3739
|
onKeyDown: onInputKeyDown,
|
|
@@ -3432,7 +3818,8 @@ function DropdownList(props) {
|
|
|
3432
3818
|
useEffect(() => {
|
|
3433
3819
|
const individualEntries = dropdownContainer.current.children;
|
|
3434
3820
|
if (individualEntries.length && !mouseControl) {
|
|
3435
|
-
individualEntries[focusedValueIndex]
|
|
3821
|
+
const focusedEntry = individualEntries[focusedValueIndex];
|
|
3822
|
+
focusedEntry && focusedEntry.scrollIntoView({
|
|
3436
3823
|
block: 'nearest',
|
|
3437
3824
|
inline: 'nearest'
|
|
3438
3825
|
});
|
|
@@ -3968,6 +4355,52 @@ function FormComponent(props) {
|
|
|
3968
4355
|
});
|
|
3969
4356
|
}
|
|
3970
4357
|
|
|
4358
|
+
function Group(props) {
|
|
4359
|
+
const {
|
|
4360
|
+
field
|
|
4361
|
+
} = props;
|
|
4362
|
+
const {
|
|
4363
|
+
label,
|
|
4364
|
+
id,
|
|
4365
|
+
type,
|
|
4366
|
+
showOutline
|
|
4367
|
+
} = field;
|
|
4368
|
+
const {
|
|
4369
|
+
formId
|
|
4370
|
+
} = useContext(FormContext$1);
|
|
4371
|
+
const {
|
|
4372
|
+
Empty
|
|
4373
|
+
} = useContext(FormRenderContext$1);
|
|
4374
|
+
const fullProps = {
|
|
4375
|
+
...props,
|
|
4376
|
+
Empty
|
|
4377
|
+
};
|
|
4378
|
+
return jsxs("div", {
|
|
4379
|
+
className: classNames(formFieldClasses(type), {
|
|
4380
|
+
'fjs-outlined': showOutline
|
|
4381
|
+
}),
|
|
4382
|
+
role: "group",
|
|
4383
|
+
"aria-labelledby": prefixId(id, formId),
|
|
4384
|
+
children: [jsx(Label, {
|
|
4385
|
+
id: prefixId(id, formId),
|
|
4386
|
+
label: label
|
|
4387
|
+
}), jsx(Grid, {
|
|
4388
|
+
...fullProps
|
|
4389
|
+
})]
|
|
4390
|
+
});
|
|
4391
|
+
}
|
|
4392
|
+
Group.config = {
|
|
4393
|
+
type: 'group',
|
|
4394
|
+
pathed: true,
|
|
4395
|
+
label: 'Group',
|
|
4396
|
+
group: 'presentation',
|
|
4397
|
+
create: (options = {}) => ({
|
|
4398
|
+
components: [],
|
|
4399
|
+
showOutline: true,
|
|
4400
|
+
...options
|
|
4401
|
+
})
|
|
4402
|
+
};
|
|
4403
|
+
|
|
3971
4404
|
const NODE_TYPE_TEXT = 3,
|
|
3972
4405
|
NODE_TYPE_ELEMENT = 1;
|
|
3973
4406
|
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'];
|
|
@@ -4556,21 +4989,7 @@ Radio.config = {
|
|
|
4556
4989
|
group: 'selection',
|
|
4557
4990
|
emptyValue: null,
|
|
4558
4991
|
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
|
-
}
|
|
4992
|
+
create: createEmptyOptions
|
|
4574
4993
|
};
|
|
4575
4994
|
|
|
4576
4995
|
var _path$d;
|
|
@@ -4922,21 +5341,7 @@ Select.config = {
|
|
|
4922
5341
|
group: 'selection',
|
|
4923
5342
|
emptyValue: null,
|
|
4924
5343
|
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
|
-
}
|
|
5344
|
+
create: createEmptyOptions
|
|
4940
5345
|
};
|
|
4941
5346
|
|
|
4942
5347
|
const type$4 = 'spacer';
|
|
@@ -5164,21 +5569,7 @@ Taglist.config = {
|
|
|
5164
5569
|
group: 'selection',
|
|
5165
5570
|
emptyValue: [],
|
|
5166
5571
|
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
|
-
}
|
|
5572
|
+
create: createEmptyOptions
|
|
5182
5573
|
};
|
|
5183
5574
|
|
|
5184
5575
|
const type$2 = 'text';
|
|
@@ -5595,13 +5986,14 @@ var SvgGroup = function SvgGroup(props) {
|
|
|
5595
5986
|
return /*#__PURE__*/React.createElement("svg", _extends$8({
|
|
5596
5987
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5597
5988
|
width: 54,
|
|
5598
|
-
height: 54
|
|
5989
|
+
height: 54,
|
|
5990
|
+
fill: "currentcolor"
|
|
5599
5991
|
}, props), _path$8 || (_path$8 = /*#__PURE__*/React.createElement("path", {
|
|
5600
5992
|
fillRule: "evenodd",
|
|
5601
5993
|
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
5994
|
})));
|
|
5603
5995
|
};
|
|
5604
|
-
var
|
|
5996
|
+
var GroupIcon = SvgGroup;
|
|
5605
5997
|
|
|
5606
5998
|
var _path$7;
|
|
5607
5999
|
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 +6046,14 @@ var SvgSpacer = function SvgSpacer(props) {
|
|
|
5654
6046
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5655
6047
|
width: 54,
|
|
5656
6048
|
height: 54,
|
|
5657
|
-
fill: "
|
|
6049
|
+
fill: "currentcolor"
|
|
5658
6050
|
}, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
|
|
5659
|
-
stroke: "
|
|
6051
|
+
stroke: "currentcolor",
|
|
5660
6052
|
strokeLinecap: "square",
|
|
5661
6053
|
strokeWidth: 2,
|
|
5662
6054
|
d: "M9 23h36M9 31h36"
|
|
5663
6055
|
})), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
|
|
5664
|
-
stroke: "
|
|
6056
|
+
stroke: "currentcolor",
|
|
5665
6057
|
strokeLinecap: "round",
|
|
5666
6058
|
strokeLinejoin: "round",
|
|
5667
6059
|
strokeWidth: 2,
|
|
@@ -5739,8 +6131,9 @@ const iconsByType = type => {
|
|
|
5739
6131
|
button: ButtonIcon,
|
|
5740
6132
|
checkbox: CheckboxIcon,
|
|
5741
6133
|
checklist: ChecklistIcon,
|
|
5742
|
-
columns:
|
|
6134
|
+
columns: GroupIcon,
|
|
5743
6135
|
datetime: DatetimeIcon,
|
|
6136
|
+
group: GroupIcon,
|
|
5744
6137
|
image: ImageIcon,
|
|
5745
6138
|
number: NumberIcon,
|
|
5746
6139
|
radio: RadioIcon,
|
|
@@ -5754,7 +6147,7 @@ const iconsByType = type => {
|
|
|
5754
6147
|
}[type];
|
|
5755
6148
|
};
|
|
5756
6149
|
|
|
5757
|
-
const formFields = [Button, Checkbox, Checklist,
|
|
6150
|
+
const formFields = [Button, Checkbox, Checklist, FormComponent$1, Group, Image, Numberfield, Datetime, Radio, Select, Spacer, Taglist, Text, Textfield, Textarea];
|
|
5758
6151
|
|
|
5759
6152
|
class FormFields {
|
|
5760
6153
|
constructor() {
|
|
@@ -5830,62 +6223,65 @@ var renderModule = {
|
|
|
5830
6223
|
};
|
|
5831
6224
|
|
|
5832
6225
|
var core = {
|
|
5833
|
-
__depends__: [
|
|
6226
|
+
__depends__: [renderModule],
|
|
5834
6227
|
eventBus: ['type', EventBus],
|
|
6228
|
+
importer: ['type', Importer],
|
|
6229
|
+
fieldFactory: ['type', FieldFactory],
|
|
5835
6230
|
formFieldRegistry: ['type', FormFieldRegistry],
|
|
6231
|
+
pathRegistry: ['type', PathRegistry],
|
|
5836
6232
|
formLayouter: ['type', FormLayouter],
|
|
5837
6233
|
validator: ['type', Validator]
|
|
5838
6234
|
};
|
|
5839
6235
|
|
|
5840
|
-
/**
|
|
5841
|
-
* @typedef { import('./types').Injector } Injector
|
|
5842
|
-
* @typedef { import('./types').Data } Data
|
|
5843
|
-
* @typedef { import('./types').Errors } Errors
|
|
5844
|
-
* @typedef { import('./types').Schema } Schema
|
|
5845
|
-
* @typedef { import('./types').FormProperties } FormProperties
|
|
5846
|
-
* @typedef { import('./types').FormProperty } FormProperty
|
|
5847
|
-
* @typedef { import('./types').FormEvent } FormEvent
|
|
5848
|
-
* @typedef { import('./types').FormOptions } FormOptions
|
|
5849
|
-
*
|
|
5850
|
-
* @typedef { {
|
|
5851
|
-
* data: Data,
|
|
5852
|
-
* initialData: Data,
|
|
5853
|
-
* errors: Errors,
|
|
5854
|
-
* properties: FormProperties,
|
|
5855
|
-
* schema: Schema
|
|
5856
|
-
* } } State
|
|
5857
|
-
*
|
|
5858
|
-
* @typedef { (type:FormEvent, priority:number, handler:Function) => void } OnEventWithPriority
|
|
5859
|
-
* @typedef { (type:FormEvent, handler:Function) => void } OnEventWithOutPriority
|
|
5860
|
-
* @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
|
|
6236
|
+
/**
|
|
6237
|
+
* @typedef { import('./types').Injector } Injector
|
|
6238
|
+
* @typedef { import('./types').Data } Data
|
|
6239
|
+
* @typedef { import('./types').Errors } Errors
|
|
6240
|
+
* @typedef { import('./types').Schema } Schema
|
|
6241
|
+
* @typedef { import('./types').FormProperties } FormProperties
|
|
6242
|
+
* @typedef { import('./types').FormProperty } FormProperty
|
|
6243
|
+
* @typedef { import('./types').FormEvent } FormEvent
|
|
6244
|
+
* @typedef { import('./types').FormOptions } FormOptions
|
|
6245
|
+
*
|
|
6246
|
+
* @typedef { {
|
|
6247
|
+
* data: Data,
|
|
6248
|
+
* initialData: Data,
|
|
6249
|
+
* errors: Errors,
|
|
6250
|
+
* properties: FormProperties,
|
|
6251
|
+
* schema: Schema
|
|
6252
|
+
* } } State
|
|
6253
|
+
*
|
|
6254
|
+
* @typedef { (type:FormEvent, priority:number, handler:Function) => void } OnEventWithPriority
|
|
6255
|
+
* @typedef { (type:FormEvent, handler:Function) => void } OnEventWithOutPriority
|
|
6256
|
+
* @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
|
|
5861
6257
|
*/
|
|
5862
6258
|
|
|
5863
6259
|
const ids = new Ids([32, 36, 1]);
|
|
5864
6260
|
|
|
5865
|
-
/**
|
|
5866
|
-
* The form.
|
|
6261
|
+
/**
|
|
6262
|
+
* The form.
|
|
5867
6263
|
*/
|
|
5868
6264
|
class Form {
|
|
5869
|
-
/**
|
|
5870
|
-
* @constructor
|
|
5871
|
-
* @param {FormOptions} options
|
|
6265
|
+
/**
|
|
6266
|
+
* @constructor
|
|
6267
|
+
* @param {FormOptions} options
|
|
5872
6268
|
*/
|
|
5873
6269
|
constructor(options = {}) {
|
|
5874
|
-
/**
|
|
5875
|
-
* @public
|
|
5876
|
-
* @type {OnEventType}
|
|
6270
|
+
/**
|
|
6271
|
+
* @public
|
|
6272
|
+
* @type {OnEventType}
|
|
5877
6273
|
*/
|
|
5878
6274
|
this.on = this._onEvent;
|
|
5879
6275
|
|
|
5880
|
-
/**
|
|
5881
|
-
* @public
|
|
5882
|
-
* @type {String}
|
|
6276
|
+
/**
|
|
6277
|
+
* @public
|
|
6278
|
+
* @type {String}
|
|
5883
6279
|
*/
|
|
5884
6280
|
this._id = ids.next();
|
|
5885
6281
|
|
|
5886
|
-
/**
|
|
5887
|
-
* @private
|
|
5888
|
-
* @type {Element}
|
|
6282
|
+
/**
|
|
6283
|
+
* @private
|
|
6284
|
+
* @type {Element}
|
|
5889
6285
|
*/
|
|
5890
6286
|
this._container = createFormContainer();
|
|
5891
6287
|
const {
|
|
@@ -5894,9 +6290,9 @@ class Form {
|
|
|
5894
6290
|
properties = {}
|
|
5895
6291
|
} = options;
|
|
5896
6292
|
|
|
5897
|
-
/**
|
|
5898
|
-
* @private
|
|
5899
|
-
* @type {State}
|
|
6293
|
+
/**
|
|
6294
|
+
* @private
|
|
6295
|
+
* @type {State}
|
|
5900
6296
|
*/
|
|
5901
6297
|
this._state = {
|
|
5902
6298
|
initialData: null,
|
|
@@ -5920,9 +6316,9 @@ class Form {
|
|
|
5920
6316
|
this._emit('form.clear');
|
|
5921
6317
|
}
|
|
5922
6318
|
|
|
5923
|
-
/**
|
|
5924
|
-
* Destroy the form, removing it from DOM,
|
|
5925
|
-
* if attached.
|
|
6319
|
+
/**
|
|
6320
|
+
* Destroy the form, removing it from DOM,
|
|
6321
|
+
* if attached.
|
|
5926
6322
|
*/
|
|
5927
6323
|
destroy() {
|
|
5928
6324
|
// destroy form services
|
|
@@ -5933,13 +6329,13 @@ class Form {
|
|
|
5933
6329
|
this._detach(false);
|
|
5934
6330
|
}
|
|
5935
6331
|
|
|
5936
|
-
/**
|
|
5937
|
-
* Open a form schema with the given initial data.
|
|
5938
|
-
*
|
|
5939
|
-
* @param {Schema} schema
|
|
5940
|
-
* @param {Data} [data]
|
|
5941
|
-
*
|
|
5942
|
-
* @return Promise<{ warnings: Array<any> }>
|
|
6332
|
+
/**
|
|
6333
|
+
* Open a form schema with the given initial data.
|
|
6334
|
+
*
|
|
6335
|
+
* @param {Schema} schema
|
|
6336
|
+
* @param {Data} [data]
|
|
6337
|
+
*
|
|
6338
|
+
* @return Promise<{ warnings: Array<any> }>
|
|
5943
6339
|
*/
|
|
5944
6340
|
importSchema(schema, data = {}) {
|
|
5945
6341
|
return new Promise((resolve, reject) => {
|
|
@@ -5947,9 +6343,9 @@ class Form {
|
|
|
5947
6343
|
this.clear();
|
|
5948
6344
|
const {
|
|
5949
6345
|
schema: importedSchema,
|
|
5950
|
-
data: initializedData,
|
|
5951
6346
|
warnings
|
|
5952
|
-
} = this.get('importer').importSchema(schema
|
|
6347
|
+
} = this.get('importer').importSchema(schema);
|
|
6348
|
+
const initializedData = this._initializeFieldData(clone(data));
|
|
5953
6349
|
this._setState({
|
|
5954
6350
|
data: initializedData,
|
|
5955
6351
|
errors: {},
|
|
@@ -5972,10 +6368,10 @@ class Form {
|
|
|
5972
6368
|
});
|
|
5973
6369
|
}
|
|
5974
6370
|
|
|
5975
|
-
/**
|
|
5976
|
-
* Submit the form, triggering all field validations.
|
|
5977
|
-
*
|
|
5978
|
-
* @returns { { data: Data, errors: Errors } }
|
|
6371
|
+
/**
|
|
6372
|
+
* Submit the form, triggering all field validations.
|
|
6373
|
+
*
|
|
6374
|
+
* @returns { { data: Data, errors: Errors } }
|
|
5979
6375
|
*/
|
|
5980
6376
|
submit() {
|
|
5981
6377
|
const {
|
|
@@ -6002,26 +6398,26 @@ class Form {
|
|
|
6002
6398
|
});
|
|
6003
6399
|
}
|
|
6004
6400
|
|
|
6005
|
-
/**
|
|
6006
|
-
* @returns {Errors}
|
|
6401
|
+
/**
|
|
6402
|
+
* @returns {Errors}
|
|
6007
6403
|
*/
|
|
6008
6404
|
validate() {
|
|
6009
6405
|
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6406
|
+
pathRegistry = this.get('pathRegistry'),
|
|
6010
6407
|
validator = this.get('validator');
|
|
6011
6408
|
const {
|
|
6012
6409
|
data
|
|
6013
6410
|
} = this._getState();
|
|
6014
6411
|
const errors = formFieldRegistry.getAll().reduce((errors, field) => {
|
|
6015
6412
|
const {
|
|
6016
|
-
disabled
|
|
6017
|
-
_path
|
|
6413
|
+
disabled
|
|
6018
6414
|
} = field;
|
|
6019
6415
|
if (disabled) {
|
|
6020
6416
|
return errors;
|
|
6021
6417
|
}
|
|
6022
|
-
const value = get(data,
|
|
6418
|
+
const value = get(data, pathRegistry.getValuePath(field));
|
|
6023
6419
|
const fieldErrors = validator.validateField(field, value);
|
|
6024
|
-
return set(errors, [
|
|
6420
|
+
return set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
6025
6421
|
}, /** @type {Errors} */{});
|
|
6026
6422
|
this._setState({
|
|
6027
6423
|
errors
|
|
@@ -6029,8 +6425,8 @@ class Form {
|
|
|
6029
6425
|
return errors;
|
|
6030
6426
|
}
|
|
6031
6427
|
|
|
6032
|
-
/**
|
|
6033
|
-
* @param {Element|string} parentNode
|
|
6428
|
+
/**
|
|
6429
|
+
* @param {Element|string} parentNode
|
|
6034
6430
|
*/
|
|
6035
6431
|
attachTo(parentNode) {
|
|
6036
6432
|
if (!parentNode) {
|
|
@@ -6048,10 +6444,10 @@ class Form {
|
|
|
6048
6444
|
this._detach();
|
|
6049
6445
|
}
|
|
6050
6446
|
|
|
6051
|
-
/**
|
|
6052
|
-
* @private
|
|
6053
|
-
*
|
|
6054
|
-
* @param {boolean} [emit]
|
|
6447
|
+
/**
|
|
6448
|
+
* @private
|
|
6449
|
+
*
|
|
6450
|
+
* @param {boolean} [emit]
|
|
6055
6451
|
*/
|
|
6056
6452
|
_detach(emit = true) {
|
|
6057
6453
|
const container = this._container,
|
|
@@ -6065,9 +6461,9 @@ class Form {
|
|
|
6065
6461
|
parentNode.removeChild(container);
|
|
6066
6462
|
}
|
|
6067
6463
|
|
|
6068
|
-
/**
|
|
6069
|
-
* @param {FormProperty} property
|
|
6070
|
-
* @param {any} value
|
|
6464
|
+
/**
|
|
6465
|
+
* @param {FormProperty} property
|
|
6466
|
+
* @param {any} value
|
|
6071
6467
|
*/
|
|
6072
6468
|
setProperty(property, value) {
|
|
6073
6469
|
const properties = set(this._getState().properties, [property], value);
|
|
@@ -6076,21 +6472,21 @@ class Form {
|
|
|
6076
6472
|
});
|
|
6077
6473
|
}
|
|
6078
6474
|
|
|
6079
|
-
/**
|
|
6080
|
-
* @param {FormEvent} type
|
|
6081
|
-
* @param {Function} handler
|
|
6475
|
+
/**
|
|
6476
|
+
* @param {FormEvent} type
|
|
6477
|
+
* @param {Function} handler
|
|
6082
6478
|
*/
|
|
6083
6479
|
off(type, handler) {
|
|
6084
6480
|
this.get('eventBus').off(type, handler);
|
|
6085
6481
|
}
|
|
6086
6482
|
|
|
6087
|
-
/**
|
|
6088
|
-
* @private
|
|
6089
|
-
*
|
|
6090
|
-
* @param {FormOptions} options
|
|
6091
|
-
* @param {Element} container
|
|
6092
|
-
*
|
|
6093
|
-
* @returns {Injector}
|
|
6483
|
+
/**
|
|
6484
|
+
* @private
|
|
6485
|
+
*
|
|
6486
|
+
* @param {FormOptions} options
|
|
6487
|
+
* @param {Element} container
|
|
6488
|
+
*
|
|
6489
|
+
* @returns {Injector}
|
|
6094
6490
|
*/
|
|
6095
6491
|
_createInjector(options, container) {
|
|
6096
6492
|
const {
|
|
@@ -6109,17 +6505,17 @@ class Form {
|
|
|
6109
6505
|
}, core, ...modules, ...additionalModules]);
|
|
6110
6506
|
}
|
|
6111
6507
|
|
|
6112
|
-
/**
|
|
6113
|
-
* @private
|
|
6508
|
+
/**
|
|
6509
|
+
* @private
|
|
6114
6510
|
*/
|
|
6115
6511
|
_emit(type, data) {
|
|
6116
6512
|
this.get('eventBus').fire(type, data);
|
|
6117
6513
|
}
|
|
6118
6514
|
|
|
6119
|
-
/**
|
|
6120
|
-
* @internal
|
|
6121
|
-
*
|
|
6122
|
-
* @param { { add?: boolean, field: any, remove?: number, value?: any } } update
|
|
6515
|
+
/**
|
|
6516
|
+
* @internal
|
|
6517
|
+
*
|
|
6518
|
+
* @param { { add?: boolean, field: any, remove?: number, value?: any } } update
|
|
6123
6519
|
*/
|
|
6124
6520
|
_update(update) {
|
|
6125
6521
|
const {
|
|
@@ -6127,31 +6523,29 @@ class Form {
|
|
|
6127
6523
|
value
|
|
6128
6524
|
} = update;
|
|
6129
6525
|
const {
|
|
6130
|
-
_path
|
|
6131
|
-
} = field;
|
|
6132
|
-
let {
|
|
6133
6526
|
data,
|
|
6134
6527
|
errors
|
|
6135
6528
|
} = this._getState();
|
|
6136
|
-
const validator = this.get('validator')
|
|
6529
|
+
const validator = this.get('validator'),
|
|
6530
|
+
pathRegistry = this.get('pathRegistry');
|
|
6137
6531
|
const fieldErrors = validator.validateField(field, value);
|
|
6138
|
-
set(data,
|
|
6139
|
-
set(errors, [
|
|
6532
|
+
set(data, pathRegistry.getValuePath(field), value);
|
|
6533
|
+
set(errors, [field.id], fieldErrors.length ? fieldErrors : undefined);
|
|
6140
6534
|
this._setState({
|
|
6141
6535
|
data: clone(data),
|
|
6142
6536
|
errors: clone(errors)
|
|
6143
6537
|
});
|
|
6144
6538
|
}
|
|
6145
6539
|
|
|
6146
|
-
/**
|
|
6147
|
-
* @internal
|
|
6540
|
+
/**
|
|
6541
|
+
* @internal
|
|
6148
6542
|
*/
|
|
6149
6543
|
_getState() {
|
|
6150
6544
|
return this._state;
|
|
6151
6545
|
}
|
|
6152
6546
|
|
|
6153
|
-
/**
|
|
6154
|
-
* @internal
|
|
6547
|
+
/**
|
|
6548
|
+
* @internal
|
|
6155
6549
|
*/
|
|
6156
6550
|
_setState(state) {
|
|
6157
6551
|
this._state = {
|
|
@@ -6161,56 +6555,96 @@ class Form {
|
|
|
6161
6555
|
this._emit('changed', this._getState());
|
|
6162
6556
|
}
|
|
6163
6557
|
|
|
6164
|
-
/**
|
|
6165
|
-
* @internal
|
|
6558
|
+
/**
|
|
6559
|
+
* @internal
|
|
6166
6560
|
*/
|
|
6167
6561
|
_getModules() {
|
|
6168
6562
|
return [ExpressionLanguageModule, MarkdownModule, ViewerCommandsModule];
|
|
6169
6563
|
}
|
|
6170
6564
|
|
|
6171
|
-
/**
|
|
6172
|
-
* @internal
|
|
6565
|
+
/**
|
|
6566
|
+
* @internal
|
|
6173
6567
|
*/
|
|
6174
6568
|
_onEvent(type, priority, handler) {
|
|
6175
6569
|
this.get('eventBus').on(type, priority, handler);
|
|
6176
6570
|
}
|
|
6177
6571
|
|
|
6178
|
-
/**
|
|
6179
|
-
* @internal
|
|
6572
|
+
/**
|
|
6573
|
+
* @internal
|
|
6180
6574
|
*/
|
|
6181
6575
|
_getSubmitData() {
|
|
6182
|
-
const formFieldRegistry = this.get('formFieldRegistry')
|
|
6576
|
+
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6577
|
+
pathRegistry = this.get('pathRegistry'),
|
|
6578
|
+
formFields = this.get('formFields');
|
|
6183
6579
|
const formData = this._getState().data;
|
|
6184
6580
|
const submitData = formFieldRegistry.getAll().reduce((previous, field) => {
|
|
6185
6581
|
const {
|
|
6186
6582
|
disabled,
|
|
6187
|
-
|
|
6583
|
+
type
|
|
6188
6584
|
} = field;
|
|
6585
|
+
const {
|
|
6586
|
+
config: fieldConfig
|
|
6587
|
+
} = formFields.get(type);
|
|
6189
6588
|
|
|
6190
|
-
// do not submit disabled form fields
|
|
6191
|
-
if (disabled || !
|
|
6589
|
+
// do not submit disabled form fields or routing fields
|
|
6590
|
+
if (disabled || !fieldConfig.keyed) {
|
|
6192
6591
|
return previous;
|
|
6193
6592
|
}
|
|
6194
|
-
const
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
[_path[0]]: value
|
|
6198
|
-
};
|
|
6593
|
+
const valuePath = pathRegistry.getValuePath(field);
|
|
6594
|
+
const value = get(formData, valuePath);
|
|
6595
|
+
return set(previous, valuePath, value);
|
|
6199
6596
|
}, {});
|
|
6200
6597
|
const filteredSubmitData = this._applyConditions(submitData, formData);
|
|
6201
6598
|
return filteredSubmitData;
|
|
6202
6599
|
}
|
|
6203
6600
|
|
|
6204
|
-
/**
|
|
6205
|
-
* @internal
|
|
6601
|
+
/**
|
|
6602
|
+
* @internal
|
|
6206
6603
|
*/
|
|
6207
6604
|
_applyConditions(toFilter, data) {
|
|
6208
6605
|
const conditionChecker = this.get('conditionChecker');
|
|
6209
6606
|
return conditionChecker.applyConditions(toFilter, data);
|
|
6210
6607
|
}
|
|
6608
|
+
|
|
6609
|
+
/**
|
|
6610
|
+
* @internal
|
|
6611
|
+
*/
|
|
6612
|
+
_initializeFieldData(data) {
|
|
6613
|
+
const formFieldRegistry = this.get('formFieldRegistry'),
|
|
6614
|
+
formFields = this.get('formFields'),
|
|
6615
|
+
pathRegistry = this.get('pathRegistry');
|
|
6616
|
+
return formFieldRegistry.getAll().reduce((initializedData, formField) => {
|
|
6617
|
+
const {
|
|
6618
|
+
defaultValue,
|
|
6619
|
+
type
|
|
6620
|
+
} = formField;
|
|
6621
|
+
|
|
6622
|
+
// try to get value from data
|
|
6623
|
+
// if unavailable - try to get default value from form field
|
|
6624
|
+
// if unavailable - get empty value from form field
|
|
6625
|
+
|
|
6626
|
+
const valuePath = pathRegistry.getValuePath(formField);
|
|
6627
|
+
if (valuePath) {
|
|
6628
|
+
const {
|
|
6629
|
+
config: fieldConfig
|
|
6630
|
+
} = formFields.get(type);
|
|
6631
|
+
let valueData = get(data, valuePath);
|
|
6632
|
+
if (!isUndefined(valueData) && fieldConfig.sanitizeValue) {
|
|
6633
|
+
valueData = fieldConfig.sanitizeValue({
|
|
6634
|
+
formField,
|
|
6635
|
+
data,
|
|
6636
|
+
value: valueData
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6639
|
+
const initializedFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldConfig.emptyValue;
|
|
6640
|
+
return set(initializedData, valuePath, initializedFieldValue);
|
|
6641
|
+
}
|
|
6642
|
+
return initializedData;
|
|
6643
|
+
}, data);
|
|
6644
|
+
}
|
|
6211
6645
|
}
|
|
6212
6646
|
|
|
6213
|
-
const schemaVersion =
|
|
6647
|
+
const schemaVersion = 11;
|
|
6214
6648
|
|
|
6215
6649
|
/**
|
|
6216
6650
|
* @typedef { import('./types').CreateFormOptions } CreateFormOptions
|
|
@@ -6235,5 +6669,5 @@ function createForm(options) {
|
|
|
6235
6669
|
});
|
|
6236
6670
|
}
|
|
6237
6671
|
|
|
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,
|
|
6672
|
+
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, FormField, 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
6673
|
//# sourceMappingURL=index.es.js.map
|