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