@bpmn-io/form-js-viewer 0.14.0 → 0.14.1

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/index.cjs CHANGED
@@ -15,30 +15,179 @@ var flatpickr = require('flatpickr');
15
15
  var Markup = require('preact-markup');
16
16
  var didi = require('didi');
17
17
 
18
+ const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) => {
19
+ const {
20
+ depth = 0,
21
+ specialDepthAccessors = {}
22
+ } = options;
23
+ if (!['expression', 'unaryTest'].includes(feelFlavour)) return [];
24
+ const tree = feelFlavour === 'expression' ? feelin.parseExpressions(feelString) : feelin.parseUnaryTests(feelString);
25
+ const simpleExpressionTree = _buildSimpleFeelStructureTree(tree, feelString);
26
+ return function _unfoldVariables(node) {
27
+ if (node.name === 'PathExpression') {
28
+ if (Object.keys(specialDepthAccessors).length === 0) {
29
+ return depth === 0 ? [_getVariableNameAtPathIndex(node, 0)] : [];
30
+ }
31
+
32
+ // if using special depth accessors, use a more complex extraction
33
+ return Array.from(_smartExtractVariableNames(node, depth, specialDepthAccessors));
34
+ }
35
+ if (depth === 0 && node.name === 'VariableName') return [node.variableName];
36
+
37
+ // for any other kind of node, traverse its children and flatten the result
38
+ if (node.children) {
39
+ return node.children.reduce((acc, child) => {
40
+ return acc.concat(_unfoldVariables(child));
41
+ }, []);
42
+ }
43
+ return [];
44
+ }(simpleExpressionTree);
45
+ };
46
+
47
+ /**
48
+ * Get the variable name at the specified index in a given path expression.
49
+ *
50
+ * @param {Object} root - The root node of the path expression tree.
51
+ * @param {number} index - The index of the variable name to retrieve.
52
+ * @returns {string|null} The variable name at the specified index or null if index is out of bounds.
53
+ */
54
+ const _getVariableNameAtPathIndex = (root, index) => {
55
+ const accessors = _deconstructPathExpression(root);
56
+ return accessors[index] || null;
57
+ };
58
+
59
+ /**
60
+ * Extracts the variables which are required of the external context for a given path expression.
61
+ * This is done by traversing the path expression tree and keeping track of the current depth relative to the external context.
62
+ *
63
+ * @param {Object} node - The root node of the path expression tree.
64
+ * @param {number} initialDepth - The depth at which the root node is located in the outer context.
65
+ * @param {Object} specialDepthAccessors - Definitions of special keywords which represent more complex accesses of the outer context.
66
+ * @returns {Set} - A set containing the extracted variable names.
67
+ */
68
+ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
69
+ // depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
70
+ // we track multiple of these to account for the fact that a path expression may be ambiguous due to special keywords
71
+ let accessorDepthInfos = [{
72
+ previous: null,
73
+ current: initialDepth - 1
74
+ }];
75
+ const extractedVariables = new Set();
76
+ const nodeAccessors = _deconstructPathExpression(node);
77
+ for (let i = 0; i < nodeAccessors.length; i++) {
78
+ const currentAccessor = nodeAccessors[i];
79
+ if (currentAccessor in specialDepthAccessors) {
80
+ const depthOffsets = specialDepthAccessors[currentAccessor];
81
+
82
+ // if the current accessor is a special keyword, we need to expand the current depth info set
83
+ // this is done to account for the ambiguity of keywords like parent, which may be used to access
84
+ // the parent of the current node, or a child variable of the same name
85
+ accessorDepthInfos = depthOffsets.reduce((accumulator, offset) => {
86
+ return [...accumulator, ...accessorDepthInfos.map(depthInfo => ({
87
+ previous: depthInfo.current,
88
+ current: depthInfo.current + offset
89
+ }))];
90
+ }, []).filter(depthInfo => depthInfo.current >= -1); // discard all depth infos which are out of bounds
91
+ } else {
92
+ // if the current accessor is not a special keyword, we know it's simply accessing a child
93
+ // hence we are now one level deeper in the tree and simply increment
94
+ accessorDepthInfos = accessorDepthInfos.map(depthInfo => ({
95
+ previous: depthInfo.current,
96
+ current: depthInfo.current + 1
97
+ }));
98
+ }
99
+
100
+ // finally, we check if for the current accessor, there is a scenario where:
101
+ // previous it was at depth -1 (i.e. the root context), and is now at depth 0 (i.e. a variable)
102
+ // these are the variables we need to request, so we add them to the set
103
+ if (accessorDepthInfos.some(depthInfo => depthInfo.previous === -1 && depthInfo.current === 0)) {
104
+ extractedVariables.add(currentAccessor);
105
+ }
106
+ }
107
+
108
+ // we return a set to avoid duplicates
109
+ return new Set(extractedVariables);
110
+ };
111
+
112
+ /**
113
+ * Deconstructs a path expression tree into an array of components.
114
+ *
115
+ * @param {Object} root - The root node of the path expression tree.
116
+ * @returns {Array<string>} An array of components in the path expression, in the correct order.
117
+ */
118
+ const _deconstructPathExpression = root => {
119
+ let node = root;
120
+ let parts = [];
121
+
122
+ // Traverse the tree and collect path components
123
+ while (node.name === 'PathExpression') {
124
+ parts.push(node.children[1].variableName);
125
+ node = node.children[0];
126
+ }
127
+
128
+ // Add the last component to the array
129
+ parts.push(node.variableName);
130
+
131
+ // Reverse and return the array to get the correct order
132
+ return parts.reverse();
133
+ };
134
+
135
+ /**
136
+ * Builds a simplified feel structure tree from the given parse tree and feel string.
137
+ * The nodes follow this structure: `{ name: string, children: Array, variableName?: string }`
138
+ *
139
+ * @param {Object} parseTree - The parse tree generated by a parser.
140
+ * @param {string} feelString - The feel string used for parsing.
141
+ * @returns {Object} The simplified feel structure tree.
142
+ */
143
+ const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
144
+ const stack = [{
145
+ children: []
146
+ }];
147
+ parseTree.iterate({
148
+ enter: node => {
149
+ const nodeRepresentation = {
150
+ name: node.type.name,
151
+ children: []
152
+ };
153
+ if (node.type.name === 'VariableName') {
154
+ nodeRepresentation.variableName = feelString.slice(node.from, node.to);
155
+ }
156
+ stack.push(nodeRepresentation);
157
+ },
158
+ leave: () => {
159
+ const result = stack.pop();
160
+ const parent = stack[stack.length - 1];
161
+ parent.children.push(result);
162
+ }
163
+ });
164
+ return stack[0].children[0];
165
+ };
166
+
18
167
  class FeelExpressionLanguage {
19
168
  constructor(eventBus) {
20
169
  this._eventBus = eventBus;
21
170
  }
22
171
 
23
- /**
24
- * Determines if the given string is a FEEL expression.
25
- *
26
- * @param {string} value
27
- * @returns {boolean}
28
- *
172
+ /**
173
+ * Determines if the given value is a FEEL expression.
174
+ *
175
+ * @param {any} value
176
+ * @returns {boolean}
177
+ *
29
178
  */
30
179
  isExpression(value) {
31
180
  return minDash.isString(value) && value.startsWith('=');
32
181
  }
33
182
 
34
- /**
35
- * Retrieve variable names from a given FEEL expression.
36
- *
37
- * @param {string} expression
38
- * @param {object} [options]
39
- * @param {string} [options.type]
40
- *
41
- * @returns {string[]}
183
+ /**
184
+ * Retrieve variable names from a given FEEL expression.
185
+ *
186
+ * @param {string} expression
187
+ * @param {object} [options]
188
+ * @param {string} [options.type]
189
+ *
190
+ * @returns {string[]}
42
191
  */
43
192
  getVariableNames(expression, options = {}) {
44
193
  const {
@@ -47,21 +196,19 @@ class FeelExpressionLanguage {
47
196
  if (!this.isExpression(expression)) {
48
197
  return [];
49
198
  }
50
- if (type === 'unaryTest') {
51
- return this._getUnaryVariableNames(expression);
52
- } else if (type === 'expression') {
53
- return this._getExpressionVariableNames(expression);
199
+ if (!['unaryTest', 'expression'].includes(type)) {
200
+ throw new Error('Unknown expression type: ' + type);
54
201
  }
55
- throw new Error('Unknown expression type: ' + options.type);
202
+ return getFlavouredFeelVariableNames(expression, type);
56
203
  }
57
204
 
58
- /**
59
- * Evaluate an expression.
60
- *
61
- * @param {string} expression
62
- * @param {import('../../types').Data} [data]
63
- *
64
- * @returns {any}
205
+ /**
206
+ * Evaluate an expression.
207
+ *
208
+ * @param {string} expression
209
+ * @param {import('../../types').Data} [data]
210
+ *
211
+ * @returns {any}
65
212
  */
66
213
  evaluate(expression, data = {}) {
67
214
  if (!expression) {
@@ -80,50 +227,65 @@ class FeelExpressionLanguage {
80
227
  return null;
81
228
  }
82
229
  }
83
- _getExpressionVariableNames(expression) {
84
- const tree = feelin.parseExpressions(expression);
85
- const cursor = tree.cursor();
86
- const variables = new Set();
87
- do {
88
- const node = cursor.node;
89
- if (node.type.name === 'VariableName') {
90
- variables.add(expression.slice(node.from, node.to));
91
- }
92
- } while (cursor.next());
93
- return Array.from(variables);
94
- }
95
- _getUnaryVariableNames(unaryTest) {
96
- const tree = feelin.parseUnaryTests(unaryTest);
97
- const cursor = tree.cursor();
98
- const variables = new Set();
99
- do {
100
- const node = cursor.node;
101
- if (node.type.name === 'VariableName') {
102
- variables.add(unaryTest.slice(node.from, node.to));
103
- }
104
- } while (cursor.next());
105
- return Array.from(variables);
106
- }
107
230
  }
108
231
  FeelExpressionLanguage.$inject = ['eventBus'];
109
232
 
110
233
  class FeelersTemplating {
111
234
  constructor() {}
235
+
236
+ /**
237
+ * Determines if the given value is a feelers template.
238
+ *
239
+ * @param {any} value
240
+ * @returns {boolean}
241
+ *
242
+ */
112
243
  isTemplate(value) {
113
- return minDash.isString(value) && (value.startsWith('=') || /{{/.test(value));
244
+ return minDash.isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
114
245
  }
115
246
 
116
- /**
117
- * Evaluate a template.
118
- *
119
- * @param {string} template
120
- * @param {Object<string, any>} context
121
- * @param {Object} options
122
- * @param {boolean} [options.debug = false]
123
- * @param {boolean} [options.strict = false]
124
- * @param {Function} [options.buildDebugString]
125
- *
126
- * @returns
247
+ /**
248
+ * Retrieve variable names from a given feelers template.
249
+ *
250
+ * @param {string} template
251
+ *
252
+ * @returns {string[]}
253
+ */
254
+ getVariableNames(template) {
255
+ if (!this.isTemplate(template)) {
256
+ return [];
257
+ }
258
+ const expressions = this._extractExpressionsWithDepth(template);
259
+
260
+ // defines special accessors, and the change(s) in depth they could imply (e.g. parent can be used to access the parent context (depth - 1) or a child variable named parent (depth + 1)
261
+ const specialDepthAccessors = {
262
+ parent: [-1, 1],
263
+ _parent_: [-1],
264
+ this: [0, 1],
265
+ _this_: [0]
266
+ };
267
+ return expressions.reduce((variables, {
268
+ expression,
269
+ depth
270
+ }) => {
271
+ return variables.concat(getFlavouredFeelVariableNames(expression, 'expression', {
272
+ depth,
273
+ specialDepthAccessors
274
+ }));
275
+ }, []);
276
+ }
277
+
278
+ /**
279
+ * Evaluate a template.
280
+ *
281
+ * @param {string} template
282
+ * @param {Object<string, any>} context
283
+ * @param {Object} options
284
+ * @param {boolean} [options.debug = false]
285
+ * @param {boolean} [options.strict = false]
286
+ * @param {Function} [options.buildDebugString]
287
+ *
288
+ * @returns
127
289
  */
128
290
  evaluate(template, context = {}, options = {}) {
129
291
  const {
@@ -137,12 +299,56 @@ class FeelersTemplating {
137
299
  buildDebugString
138
300
  });
139
301
  }
302
+
303
+ /**
304
+ * @typedef {Object} ExpressionWithDepth
305
+ * @property {number} depth - The depth of the expression in the syntax tree.
306
+ * @property {string} expression - The extracted expression
307
+ */
308
+
309
+ /**
310
+ * Extracts all feel expressions in the template along with their depth in the syntax tree.
311
+ * The depth is incremented for child expressions of loops to account for context drilling.
312
+ * @name extractExpressionsWithDepth
313
+ * @param {string} template - A feelers template string.
314
+ * @returns {Array<ExpressionWithDepth>} An array of objects, each containing the depth and the extracted expression.
315
+ *
316
+ * @example
317
+ * const template = "Hello {{user}}, you have:{{#loop items}}\n- {{amount}} {{name}}{{/loop}}.";
318
+ * const extractedExpressions = _extractExpressionsWithDepth(template);
319
+ */
320
+ _extractExpressionsWithDepth(template) {
321
+ // build simplified feelers syntax tree
322
+ const parseTree = feelers.parser.parse(template);
323
+ const tree = feelers.buildSimpleTree(parseTree, template);
324
+ return function _traverse(n, depth = 0) {
325
+ if (['Feel', 'FeelBlock'].includes(n.name)) {
326
+ return [{
327
+ depth,
328
+ expression: n.content
329
+ }];
330
+ }
331
+ if (n.name === 'LoopSpanner') {
332
+ const loopExpression = n.children[0].content;
333
+ const childResults = n.children.slice(1).reduce((acc, child) => {
334
+ return acc.concat(_traverse(child, depth + 1));
335
+ }, []);
336
+ return [{
337
+ depth,
338
+ expression: loopExpression
339
+ }, ...childResults];
340
+ }
341
+ return n.children.reduce((acc, child) => {
342
+ return acc.concat(_traverse(child, depth));
343
+ }, []);
344
+ }(tree);
345
+ }
140
346
  }
141
347
  FeelersTemplating.$inject = [];
142
348
 
143
- /**
144
- * @typedef {object} Condition
145
- * @property {string} [hide]
349
+ /**
350
+ * @typedef {object} Condition
351
+ * @property {string} [hide]
146
352
  */
147
353
 
148
354
  class ConditionChecker {
@@ -151,11 +357,11 @@ class ConditionChecker {
151
357
  this._eventBus = eventBus;
152
358
  }
153
359
 
154
- /**
155
- * For given data, remove properties based on condition.
156
- *
157
- * @param {Object<string, any>} properties
158
- * @param {Object<string, any>} data
360
+ /**
361
+ * For given data, remove properties based on condition.
362
+ *
363
+ * @param {Object<string, any>} properties
364
+ * @param {Object<string, any>} data
159
365
  */
160
366
  applyConditions(properties, data = {}) {
161
367
  const conditions = this._getConditions();
@@ -174,13 +380,13 @@ class ConditionChecker {
174
380
  return newProperties;
175
381
  }
176
382
 
177
- /**
178
- * Check if given condition is met. Returns null for invalid/missing conditions.
179
- *
180
- * @param {string} condition
181
- * @param {import('../../types').Data} [data]
182
- *
183
- * @returns {boolean|null}
383
+ /**
384
+ * Check if given condition is met. Returns null for invalid/missing conditions.
385
+ *
386
+ * @param {string} condition
387
+ * @param {import('../../types').Data} [data]
388
+ *
389
+ * @returns {boolean|null}
184
390
  */
185
391
  check(condition, data = {}) {
186
392
  if (!condition) {
@@ -201,12 +407,12 @@ class ConditionChecker {
201
407
  }
202
408
  }
203
409
 
204
- /**
205
- * Check if hide condition is met.
206
- *
207
- * @param {Condition} condition
208
- * @param {Object<string, any>} data
209
- * @returns {boolean}
410
+ /**
411
+ * Check if hide condition is met.
412
+ *
413
+ * @param {Condition} condition
414
+ * @param {Object<string, any>} data
415
+ * @returns {boolean}
210
416
  */
211
417
  _checkHideCondition(condition, data) {
212
418
  if (!condition.hide) {
@@ -248,12 +454,12 @@ class MarkdownRenderer {
248
454
  this._converter = new showdown.Converter();
249
455
  }
250
456
 
251
- /**
252
- * Render markdown to HTML.
253
- *
254
- * @param {string} markdown - The markdown to render
255
- *
256
- * @returns {string} HTML
457
+ /**
458
+ * Render markdown to HTML.
459
+ *
460
+ * @param {string} markdown - The markdown to render
461
+ *
462
+ * @returns {string} HTML
257
463
  */
258
464
  render(markdown) {
259
465
  return this._converter.makeHtml(markdown);
@@ -842,23 +1048,23 @@ class FormFieldRegistry {
842
1048
  }
843
1049
  FormFieldRegistry.$inject = ['eventBus'];
844
1050
 
845
- /**
846
- * @typedef { { id: String, components: Array<String> } } FormRow
847
- * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1051
+ /**
1052
+ * @typedef { { id: String, components: Array<String> } } FormRow
1053
+ * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
848
1054
  */
849
1055
 
850
- /**
851
- * Maintains the Form layout in a given structure, for example
852
- *
853
- * [
854
- * {
855
- * formFieldId: 'FormField_1',
856
- * rows: [
857
- * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
858
- * ]
859
- * }
860
- * ]
861
- *
1056
+ /**
1057
+ * Maintains the Form layout in a given structure, for example
1058
+ *
1059
+ * [
1060
+ * {
1061
+ * formFieldId: 'FormField_1',
1062
+ * rows: [
1063
+ * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1064
+ * ]
1065
+ * }
1066
+ * ]
1067
+ *
862
1068
  */
863
1069
  class FormLayouter {
864
1070
  constructor(eventBus) {
@@ -868,8 +1074,8 @@ class FormLayouter {
868
1074
  this._eventBus = eventBus;
869
1075
  }
870
1076
 
871
- /**
872
- * @param {FormRow} row
1077
+ /**
1078
+ * @param {FormRow} row
873
1079
  */
874
1080
  addRow(formFieldId, row) {
875
1081
  let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
@@ -883,18 +1089,18 @@ class FormLayouter {
883
1089
  rowsPerComponent.rows.push(row);
884
1090
  }
885
1091
 
886
- /**
887
- * @param {String} id
888
- * @returns {FormRow}
1092
+ /**
1093
+ * @param {String} id
1094
+ * @returns {FormRow}
889
1095
  */
890
1096
  getRow(id) {
891
1097
  const rows = allRows(this._rows);
892
1098
  return rows.find(r => r.id === id);
893
1099
  }
894
1100
 
895
- /**
896
- * @param {any} formField
897
- * @returns {FormRow}
1101
+ /**
1102
+ * @param {any} formField
1103
+ * @returns {FormRow}
898
1104
  */
899
1105
  getRowForField(formField) {
900
1106
  return allRows(this._rows).find(r => {
@@ -905,9 +1111,9 @@ class FormLayouter {
905
1111
  });
906
1112
  }
907
1113
 
908
- /**
909
- * @param {String} formFieldId
910
- * @returns { Array<FormRow> }
1114
+ /**
1115
+ * @param {String} formFieldId
1116
+ * @returns { Array<FormRow> }
911
1117
  */
912
1118
  getRows(formFieldId) {
913
1119
  const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
@@ -917,15 +1123,15 @@ class FormLayouter {
917
1123
  return rowsForField.rows;
918
1124
  }
919
1125
 
920
- /**
921
- * @returns {string}
1126
+ /**
1127
+ * @returns {string}
922
1128
  */
923
1129
  nextRowId() {
924
1130
  return this._ids.nextPrefixed('Row_');
925
1131
  }
926
1132
 
927
- /**
928
- * @param {any} formField
1133
+ /**
1134
+ * @param {any} formField
929
1135
  */
930
1136
  calculateLayout(formField) {
931
1137
  const {
@@ -979,9 +1185,9 @@ function groupByRow(components, ids) {
979
1185
  });
980
1186
  }
981
1187
 
982
- /**
983
- * @param {Array<FormRows>} formRows
984
- * @returns {Array<FormRow>}
1188
+ /**
1189
+ * @param {Array<FormRows>} formRows
1190
+ * @returns {Array<FormRow>}
985
1191
  */
986
1192
  function allRows(formRows) {
987
1193
  return minDash.flatten(formRows.map(c => c.rows));
@@ -1058,10 +1264,10 @@ function createInjector(bootstrapModules) {
1058
1264
  return injector;
1059
1265
  }
1060
1266
 
1061
- /**
1062
- * @param {string?} prefix
1063
- *
1064
- * @returns Element
1267
+ /**
1268
+ * @param {string?} prefix
1269
+ *
1270
+ * @returns Element
1065
1271
  */
1066
1272
  function createFormContainer(prefix = 'fjs') {
1067
1273
  const container = document.createElement('div');
@@ -1070,6 +1276,7 @@ function createFormContainer(prefix = 'fjs') {
1070
1276
  }
1071
1277
 
1072
1278
  const EXPRESSION_PROPERTIES = ['alt', 'source', 'text'];
1279
+ const TEMPLATE_PROPERTIES = ['text'];
1073
1280
  function findErrors(errors, path) {
1074
1281
  return errors[pathStringify(path)];
1075
1282
  }
@@ -1106,24 +1313,24 @@ function generateIdForType(type) {
1106
1313
  return `${type}${generateIndexForType(type)}`;
1107
1314
  }
1108
1315
 
1109
- /**
1110
- * @template T
1111
- * @param {T} data
1112
- * @param {(this: any, key: string, value: any) => any} [replacer]
1113
- * @return {T}
1316
+ /**
1317
+ * @template T
1318
+ * @param {T} data
1319
+ * @param {(this: any, key: string, value: any) => any} [replacer]
1320
+ * @return {T}
1114
1321
  */
1115
1322
  function clone(data, replacer) {
1116
1323
  return JSON.parse(JSON.stringify(data, replacer));
1117
1324
  }
1118
1325
 
1119
- /**
1120
- * Parse the schema for input variables a form might make use of
1121
- *
1122
- * @param {any} schema
1123
- *
1124
- * @return {string[]}
1326
+ /**
1327
+ * Parse the schema for input variables a form might make use of
1328
+ *
1329
+ * @param {any} schema
1330
+ *
1331
+ * @return {string[]}
1125
1332
  */
1126
- function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null)) {
1333
+ function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null), templating = new FeelersTemplating()) {
1127
1334
  if (!schema.components) {
1128
1335
  return [];
1129
1336
  }
@@ -1158,6 +1365,13 @@ function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLangu
1158
1365
  variables = [...variables, ...expressionVariables];
1159
1366
  }
1160
1367
  });
1368
+ TEMPLATE_PROPERTIES.forEach(prop => {
1369
+ const property = component[prop];
1370
+ if (property && !expressionLanguage.isExpression(property) && templating.isTemplate(property)) {
1371
+ const templateVariables = templating.getVariableNames(property);
1372
+ variables = [...variables, ...templateVariables];
1373
+ }
1374
+ });
1161
1375
  return variables;
1162
1376
  }, []);
1163
1377
 
@@ -1166,11 +1380,11 @@ function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLangu
1166
1380
  }
1167
1381
 
1168
1382
  class Importer {
1169
- /**
1170
- * @constructor
1171
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
1172
- * @param { import('../render/FormFields').default } formFields
1173
- * @param { import('../core').FormLayouter } formLayouter
1383
+ /**
1384
+ * @constructor
1385
+ * @param { import('../core').FormFieldRegistry } formFieldRegistry
1386
+ * @param { import('../render/FormFields').default } formFields
1387
+ * @param { import('../core').FormLayouter } formLayouter
1174
1388
  */
1175
1389
  constructor(formFieldRegistry, formFields, formLayouter) {
1176
1390
  this._formFieldRegistry = formFieldRegistry;
@@ -1178,15 +1392,15 @@ class Importer {
1178
1392
  this._formLayouter = formLayouter;
1179
1393
  }
1180
1394
 
1181
- /**
1182
- * Import schema adding `id`, `_parent` and `_path`
1183
- * information to each field and adding it to the
1184
- * form field registry.
1185
- *
1186
- * @param {any} schema
1187
- * @param {any} [data]
1188
- *
1189
- * @return { { warnings: Array<any>, schema: any, data: any } }
1395
+ /**
1396
+ * Import schema adding `id`, `_parent` and `_path`
1397
+ * information to each field and adding it to the
1398
+ * form field registry.
1399
+ *
1400
+ * @param {any} schema
1401
+ * @param {any} [data]
1402
+ *
1403
+ * @return { { warnings: Array<any>, schema: any, data: any } }
1190
1404
  */
1191
1405
  importSchema(schema, data = {}) {
1192
1406
  // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
@@ -1207,11 +1421,11 @@ class Importer {
1207
1421
  }
1208
1422
  }
1209
1423
 
1210
- /**
1211
- * @param {any} formField
1212
- * @param {string} [parentId]
1213
- *
1214
- * @return {any} importedField
1424
+ /**
1425
+ * @param {any} formField
1426
+ * @param {string} [parentId]
1427
+ *
1428
+ * @return {any} importedField
1215
1429
  */
1216
1430
  importFormField(formField, parentId) {
1217
1431
  const {
@@ -1262,10 +1476,10 @@ class Importer {
1262
1476
  });
1263
1477
  }
1264
1478
 
1265
- /**
1266
- * @param {Object} data
1267
- *
1268
- * @return {Object} initializedData
1479
+ /**
1480
+ * @param {Object} data
1481
+ *
1482
+ * @return {Object} initializedData
1269
1483
  */
1270
1484
  initializeFieldValues(data) {
1271
1485
  return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
@@ -1397,11 +1611,11 @@ const FormRenderContext = preact.createContext({
1397
1611
  });
1398
1612
  var FormRenderContext$1 = FormRenderContext;
1399
1613
 
1400
- /**
1401
- * @param {string} type
1402
- * @param {boolean} [strict]
1403
- *
1404
- * @returns {any}
1614
+ /**
1615
+ * @param {string} type
1616
+ * @param {boolean} [strict]
1617
+ *
1618
+ * @returns {any}
1405
1619
  */
1406
1620
  function getService(type, strict) {}
1407
1621
  const FormContext = preact.createContext({
@@ -1580,8 +1794,8 @@ function useService(type, strict) {
1580
1794
  return getService(type, strict);
1581
1795
  }
1582
1796
 
1583
- /**
1584
- * @enum { String }
1797
+ /**
1798
+ * @enum { String }
1585
1799
  */
1586
1800
  const LOAD_STATES = {
1587
1801
  LOADING: 'loading',
@@ -1589,17 +1803,17 @@ const LOAD_STATES = {
1589
1803
  ERROR: 'error'
1590
1804
  };
1591
1805
 
1592
- /**
1593
- * @typedef {Object} ValuesGetter
1594
- * @property {Object[]} values - The values data
1595
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
1806
+ /**
1807
+ * @typedef {Object} ValuesGetter
1808
+ * @property {Object[]} values - The values data
1809
+ * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
1596
1810
  */
1597
1811
 
1598
- /**
1599
- * A hook to load values for single and multiselect components.
1600
- *
1601
- * @param {Object} field - The form field to handle values for
1602
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
1812
+ /**
1813
+ * A hook to load values for single and multiselect components.
1814
+ *
1815
+ * @param {Object} field - The form field to handle values for
1816
+ * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
1603
1817
  */
1604
1818
  function useValuesAsync (field) {
1605
1819
  const {
@@ -1945,10 +2159,10 @@ Checklist.emptyValue = [];
1945
2159
  Checklist.sanitizeValue = sanitizeMultiSelectValue;
1946
2160
  Checklist.group = 'selection';
1947
2161
 
1948
- /**
1949
- * Returns the conditionally filtered data of a form reactively.
1950
- * Memoised to minimize re-renders
1951
- *
2162
+ /**
2163
+ * Returns the conditionally filtered data of a form reactively.
2164
+ * Memoised to minimize re-renders
2165
+ *
1952
2166
  */
1953
2167
  function useFilteredFormData() {
1954
2168
  const {
@@ -1965,12 +2179,12 @@ function useFilteredFormData() {
1965
2179
  }, [conditionChecker, data, initialData]);
1966
2180
  }
1967
2181
 
1968
- /**
1969
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
1970
- *
1971
- * @param {string | undefined} condition
1972
- *
1973
- * @returns {boolean} true if condition is met or no condition or condition checker exists
2182
+ /**
2183
+ * Evaluate if condition is met reactively based on the conditionChecker and form data.
2184
+ *
2185
+ * @param {string | undefined} condition
2186
+ *
2187
+ * @returns {boolean} true if condition is met or no condition or condition checker exists
1974
2188
  */
1975
2189
  function useCondition(condition) {
1976
2190
  const conditionChecker = useService('conditionChecker', false);
@@ -1980,13 +2194,13 @@ function useCondition(condition) {
1980
2194
  }, [conditionChecker, condition, filteredData]);
1981
2195
  }
1982
2196
 
1983
- /**
1984
- * Evaluate a string reactively based on the expressionLanguage and form data.
1985
- * If the string is not an expression, it is returned as is.
1986
- * Memoised to minimize re-renders.
1987
- *
1988
- * @param {string} value
1989
- *
2197
+ /**
2198
+ * Evaluate a string reactively based on the expressionLanguage and form data.
2199
+ * If the string is not an expression, it is returned as is.
2200
+ * Memoised to minimize re-renders.
2201
+ *
2202
+ * @param {string} value
2203
+ *
1990
2204
  */
1991
2205
  function useExpressionEvaluation(value) {
1992
2206
  const formData = useFilteredFormData();
@@ -2015,16 +2229,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
2015
2229
  });
2016
2230
  }
2017
2231
 
2018
- /**
2019
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2020
- * Memoised to minimize re-renders
2021
- *
2022
- * @param {string} value
2023
- * @param {Object} options
2024
- * @param {boolean} [options.debug = false]
2025
- * @param {boolean} [options.strict = false]
2026
- * @param {Function} [options.buildDebugString]
2027
- *
2232
+ /**
2233
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2234
+ * Memoised to minimize re-renders
2235
+ *
2236
+ * @param {string} value
2237
+ * @param {Object} options
2238
+ * @param {boolean} [options.debug = false]
2239
+ * @param {boolean} [options.strict = false]
2240
+ * @param {Function} [options.buildDebugString]
2241
+ *
2028
2242
  */
2029
2243
  function useTemplateEvaluation(value, options) {
2030
2244
  const filteredData = useFilteredFormData();
@@ -2775,10 +2989,10 @@ Datetime.sanitizeValue = sanitizeDateTimePickerValue;
2775
2989
  Datetime.label = 'Date time';
2776
2990
  Datetime.group = 'basic-input';
2777
2991
 
2778
- /**
2779
- * This file must not be changed or exchanged.
2780
- *
2781
- * @see http://bpmn.io/license for more information.
2992
+ /**
2993
+ * This file must not be changed or exchanged.
2994
+ *
2995
+ * @see http://bpmn.io/license for more information.
2782
2996
  */
2783
2997
  function Logo() {
2784
2998
  return jsxRuntime.jsxs("svg", {
@@ -2905,11 +3119,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
2905
3119
 
2906
3120
  const FORM_ELEMENT = document.createElement('form');
2907
3121
 
2908
- /**
2909
- * Sanitize a HTML string and return the cleaned, safe version.
2910
- *
2911
- * @param {string} html
2912
- * @return {string}
3122
+ /**
3123
+ * Sanitize a HTML string and return the cleaned, safe version.
3124
+ *
3125
+ * @param {string} html
3126
+ * @return {string}
2913
3127
  */
2914
3128
 
2915
3129
  // see https://github.com/developit/snarkdown/issues/70
@@ -2927,29 +3141,29 @@ function sanitizeHTML(html) {
2927
3141
  }
2928
3142
  }
2929
3143
 
2930
- /**
2931
- * Sanitizes an image source to ensure we only allow for data URI and links
2932
- * that start with http(s).
2933
- *
2934
- * Note: Most browsers anyway do not support script execution in <img> elements.
2935
- *
2936
- * @param {string} src
2937
- * @returns {string}
3144
+ /**
3145
+ * Sanitizes an image source to ensure we only allow for data URI and links
3146
+ * that start with http(s).
3147
+ *
3148
+ * Note: Most browsers anyway do not support script execution in <img> elements.
3149
+ *
3150
+ * @param {string} src
3151
+ * @returns {string}
2938
3152
  */
2939
3153
  function sanitizeImageSource(src) {
2940
3154
  const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
2941
3155
  return valid ? src : '';
2942
3156
  }
2943
3157
 
2944
- /**
2945
- * Recursively sanitize a HTML node, potentially
2946
- * removing it, its children or attributes.
2947
- *
2948
- * Inspired by https://github.com/developit/snarkdown/issues/70
2949
- * and https://github.com/cure53/DOMPurify. Simplified
2950
- * for our use-case.
2951
- *
2952
- * @param {Element} node
3158
+ /**
3159
+ * Recursively sanitize a HTML node, potentially
3160
+ * removing it, its children or attributes.
3161
+ *
3162
+ * Inspired by https://github.com/developit/snarkdown/issues/70
3163
+ * and https://github.com/cure53/DOMPurify. Simplified
3164
+ * for our use-case.
3165
+ *
3166
+ * @param {Element} node
2953
3167
  */
2954
3168
  function sanitizeNode(node) {
2955
3169
  // allow text nodes
@@ -2993,13 +3207,13 @@ function sanitizeNode(node) {
2993
3207
  }
2994
3208
  }
2995
3209
 
2996
- /**
2997
- * Validates attributes for validity.
2998
- *
2999
- * @param {string} lcTag
3000
- * @param {string} lcName
3001
- * @param {string} value
3002
- * @return {boolean}
3210
+ /**
3211
+ * Validates attributes for validity.
3212
+ *
3213
+ * @param {string} lcTag
3214
+ * @param {string} lcName
3215
+ * @param {string} value
3216
+ * @return {boolean}
3003
3217
  */
3004
3218
  function isValidAttribute(lcTag, lcName, value) {
3005
3219
  // disallow most attributes based on whitelist
@@ -3720,6 +3934,12 @@ function Select(props) {
3720
3934
  errors,
3721
3935
  disabled
3722
3936
  }),
3937
+ onKeyDown: event => {
3938
+ if (event.key === 'Enter') {
3939
+ event.preventDefault();
3940
+ event.stopPropagation();
3941
+ }
3942
+ },
3723
3943
  children: [jsxRuntime.jsx(Label, {
3724
3944
  id: prefixId(id, formId),
3725
3945
  label: label,
@@ -3870,6 +4090,12 @@ function Taglist(props) {
3870
4090
  errors,
3871
4091
  disabled
3872
4092
  }),
4093
+ onKeyDown: event => {
4094
+ if (event.key === 'Enter') {
4095
+ event.stopPropagation();
4096
+ event.preventDefault();
4097
+ }
4098
+ },
3873
4099
  children: [jsxRuntime.jsx(Label, {
3874
4100
  label: label,
3875
4101
  required: required,