@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.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
|
|
25
|
-
*
|
|
26
|
-
* @param {
|
|
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 (
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* @param {string} template
|
|
120
|
-
*
|
|
121
|
-
* @
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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,
|