@bpmn-io/form-js-viewer 1.0.0-alpha.0 → 1.0.0-alpha.3

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
@@ -44,26 +44,26 @@ const getFlavouredFeelVariableNames = (feelString, feelFlavour, options = {}) =>
44
44
  }(simpleExpressionTree);
45
45
  };
46
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.
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
53
  */
54
54
  const _getVariableNameAtPathIndex = (root, index) => {
55
55
  const accessors = _deconstructPathExpression(root);
56
56
  return accessors[index] || null;
57
57
  };
58
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.
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
67
  */
68
68
  const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) => {
69
69
  // depth info represents the previous (initialised as null) and current depth of the current accessor in the path expression
@@ -109,11 +109,11 @@ const _smartExtractVariableNames = (node, initialDepth, specialDepthAccessors) =
109
109
  return new Set(extractedVariables);
110
110
  };
111
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.
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
117
  */
118
118
  const _deconstructPathExpression = root => {
119
119
  let node = root;
@@ -132,13 +132,13 @@ const _deconstructPathExpression = root => {
132
132
  return parts.reverse();
133
133
  };
134
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.
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
142
  */
143
143
  const _buildSimpleFeelStructureTree = (parseTree, feelString) => {
144
144
  const stack = [{
@@ -169,25 +169,25 @@ class FeelExpressionLanguage {
169
169
  this._eventBus = eventBus;
170
170
  }
171
171
 
172
- /**
173
- * Determines if the given value is a FEEL expression.
174
- *
175
- * @param {any} value
176
- * @returns {boolean}
177
- *
172
+ /**
173
+ * Determines if the given value is a FEEL expression.
174
+ *
175
+ * @param {any} value
176
+ * @returns {boolean}
177
+ *
178
178
  */
179
179
  isExpression(value) {
180
180
  return minDash.isString(value) && value.startsWith('=');
181
181
  }
182
182
 
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[]}
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[]}
191
191
  */
192
192
  getVariableNames(expression, options = {}) {
193
193
  const {
@@ -202,13 +202,13 @@ class FeelExpressionLanguage {
202
202
  return getFlavouredFeelVariableNames(expression, type);
203
203
  }
204
204
 
205
- /**
206
- * Evaluate an expression.
207
- *
208
- * @param {string} expression
209
- * @param {import('../../types').Data} [data]
210
- *
211
- * @returns {any}
205
+ /**
206
+ * Evaluate an expression.
207
+ *
208
+ * @param {string} expression
209
+ * @param {import('../../types').Data} [data]
210
+ *
211
+ * @returns {any}
212
212
  */
213
213
  evaluate(expression, data = {}) {
214
214
  if (!expression) {
@@ -233,23 +233,23 @@ FeelExpressionLanguage.$inject = ['eventBus'];
233
233
  class FeelersTemplating {
234
234
  constructor() {}
235
235
 
236
- /**
237
- * Determines if the given value is a feelers template.
238
- *
239
- * @param {any} value
240
- * @returns {boolean}
241
- *
236
+ /**
237
+ * Determines if the given value is a feelers template.
238
+ *
239
+ * @param {any} value
240
+ * @returns {boolean}
241
+ *
242
242
  */
243
243
  isTemplate(value) {
244
244
  return minDash.isString(value) && (value.startsWith('=') || /{{.*?}}/.test(value));
245
245
  }
246
246
 
247
- /**
248
- * Retrieve variable names from a given feelers template.
249
- *
250
- * @param {string} template
251
- *
252
- * @returns {string[]}
247
+ /**
248
+ * Retrieve variable names from a given feelers template.
249
+ *
250
+ * @param {string} template
251
+ *
252
+ * @returns {string[]}
253
253
  */
254
254
  getVariableNames(template) {
255
255
  if (!this.isTemplate(template)) {
@@ -275,17 +275,17 @@ class FeelersTemplating {
275
275
  }, []);
276
276
  }
277
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
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
289
289
  */
290
290
  evaluate(template, context = {}, options = {}) {
291
291
  const {
@@ -300,22 +300,22 @@ class FeelersTemplating {
300
300
  });
301
301
  }
302
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
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
307
  */
308
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);
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
319
  */
320
320
  _extractExpressionsWithDepth(template) {
321
321
  // build simplified feelers syntax tree
@@ -346,9 +346,9 @@ class FeelersTemplating {
346
346
  }
347
347
  FeelersTemplating.$inject = [];
348
348
 
349
- /**
350
- * @typedef {object} Condition
351
- * @property {string} [hide]
349
+ /**
350
+ * @typedef {object} Condition
351
+ * @property {string} [hide]
352
352
  */
353
353
 
354
354
  class ConditionChecker {
@@ -357,11 +357,11 @@ class ConditionChecker {
357
357
  this._eventBus = eventBus;
358
358
  }
359
359
 
360
- /**
361
- * For given data, remove properties based on condition.
362
- *
363
- * @param {Object<string, any>} properties
364
- * @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
365
365
  */
366
366
  applyConditions(properties, data = {}) {
367
367
  const conditions = this._getConditions();
@@ -380,13 +380,13 @@ class ConditionChecker {
380
380
  return newProperties;
381
381
  }
382
382
 
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}
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}
390
390
  */
391
391
  check(condition, data = {}) {
392
392
  if (!condition) {
@@ -407,12 +407,12 @@ class ConditionChecker {
407
407
  }
408
408
  }
409
409
 
410
- /**
411
- * Check if hide condition is met.
412
- *
413
- * @param {Condition} condition
414
- * @param {Object<string, any>} data
415
- * @returns {boolean}
410
+ /**
411
+ * Check if hide condition is met.
412
+ *
413
+ * @param {Condition} condition
414
+ * @param {Object<string, any>} data
415
+ * @returns {boolean}
416
416
  */
417
417
  _checkHideCondition(condition, data) {
418
418
  if (!condition.hide) {
@@ -454,12 +454,12 @@ class MarkdownRenderer {
454
454
  this._converter = new showdown.Converter();
455
455
  }
456
456
 
457
- /**
458
- * Render markdown to HTML.
459
- *
460
- * @param {string} markdown - The markdown to render
461
- *
462
- * @returns {string} HTML
457
+ /**
458
+ * Render markdown to HTML.
459
+ *
460
+ * @param {string} markdown - The markdown to render
461
+ *
462
+ * @returns {string} HTML
463
463
  */
464
464
  render(markdown) {
465
465
  return this._converter.makeHtml(markdown);
@@ -1115,23 +1115,23 @@ class FormFieldRegistry {
1115
1115
  }
1116
1116
  FormFieldRegistry.$inject = ['eventBus'];
1117
1117
 
1118
- /**
1119
- * @typedef { { id: String, components: Array<String> } } FormRow
1120
- * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1118
+ /**
1119
+ * @typedef { { id: String, components: Array<String> } } FormRow
1120
+ * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
1121
1121
  */
1122
1122
 
1123
- /**
1124
- * Maintains the Form layout in a given structure, for example
1125
- *
1126
- * [
1127
- * {
1128
- * formFieldId: 'FormField_1',
1129
- * rows: [
1130
- * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1131
- * ]
1132
- * }
1133
- * ]
1134
- *
1123
+ /**
1124
+ * Maintains the Form layout in a given structure, for example
1125
+ *
1126
+ * [
1127
+ * {
1128
+ * formFieldId: 'FormField_1',
1129
+ * rows: [
1130
+ * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
1131
+ * ]
1132
+ * }
1133
+ * ]
1134
+ *
1135
1135
  */
1136
1136
  class FormLayouter {
1137
1137
  constructor(eventBus) {
@@ -1141,8 +1141,8 @@ class FormLayouter {
1141
1141
  this._eventBus = eventBus;
1142
1142
  }
1143
1143
 
1144
- /**
1145
- * @param {FormRow} row
1144
+ /**
1145
+ * @param {FormRow} row
1146
1146
  */
1147
1147
  addRow(formFieldId, row) {
1148
1148
  let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
@@ -1156,18 +1156,18 @@ class FormLayouter {
1156
1156
  rowsPerComponent.rows.push(row);
1157
1157
  }
1158
1158
 
1159
- /**
1160
- * @param {String} id
1161
- * @returns {FormRow}
1159
+ /**
1160
+ * @param {String} id
1161
+ * @returns {FormRow}
1162
1162
  */
1163
1163
  getRow(id) {
1164
1164
  const rows = allRows(this._rows);
1165
1165
  return rows.find(r => r.id === id);
1166
1166
  }
1167
1167
 
1168
- /**
1169
- * @param {any} formField
1170
- * @returns {FormRow}
1168
+ /**
1169
+ * @param {any} formField
1170
+ * @returns {FormRow}
1171
1171
  */
1172
1172
  getRowForField(formField) {
1173
1173
  return allRows(this._rows).find(r => {
@@ -1178,9 +1178,9 @@ class FormLayouter {
1178
1178
  });
1179
1179
  }
1180
1180
 
1181
- /**
1182
- * @param {String} formFieldId
1183
- * @returns { Array<FormRow> }
1181
+ /**
1182
+ * @param {String} formFieldId
1183
+ * @returns { Array<FormRow> }
1184
1184
  */
1185
1185
  getRows(formFieldId) {
1186
1186
  const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
@@ -1190,15 +1190,15 @@ class FormLayouter {
1190
1190
  return rowsForField.rows;
1191
1191
  }
1192
1192
 
1193
- /**
1194
- * @returns {string}
1193
+ /**
1194
+ * @returns {string}
1195
1195
  */
1196
1196
  nextRowId() {
1197
1197
  return this._ids.nextPrefixed('Row_');
1198
1198
  }
1199
1199
 
1200
- /**
1201
- * @param {any} formField
1200
+ /**
1201
+ * @param {any} formField
1202
1202
  */
1203
1203
  calculateLayout(formField) {
1204
1204
  const {
@@ -1252,9 +1252,9 @@ function groupByRow(components, ids) {
1252
1252
  });
1253
1253
  }
1254
1254
 
1255
- /**
1256
- * @param {Array<FormRows>} formRows
1257
- * @returns {Array<FormRow>}
1255
+ /**
1256
+ * @param {Array<FormRows>} formRows
1257
+ * @returns {Array<FormRow>}
1258
1258
  */
1259
1259
  function allRows(formRows) {
1260
1260
  return minDash.flatten(formRows.map(c => c.rows));
@@ -1331,10 +1331,10 @@ function createInjector(bootstrapModules) {
1331
1331
  return injector;
1332
1332
  }
1333
1333
 
1334
- /**
1335
- * @param {string?} prefix
1336
- *
1337
- * @returns Element
1334
+ /**
1335
+ * @param {string?} prefix
1336
+ *
1337
+ * @returns Element
1338
1338
  */
1339
1339
  function createFormContainer(prefix = 'fjs') {
1340
1340
  const container = document.createElement('div');
@@ -1342,8 +1342,8 @@ function createFormContainer(prefix = 'fjs') {
1342
1342
  return container;
1343
1343
  }
1344
1344
 
1345
- const EXPRESSION_PROPERTIES = ['alt', 'source', 'readonly', 'text'];
1346
- const TEMPLATE_PROPERTIES = ['text'];
1345
+ const EXPRESSION_PROPERTIES = ['alt', 'description', 'label', 'source', 'readonly', 'text'];
1346
+ const TEMPLATE_PROPERTIES = ['description', 'label', 'text'];
1347
1347
  function findErrors(errors, path) {
1348
1348
  return errors[pathStringify(path)];
1349
1349
  }
@@ -1380,22 +1380,22 @@ function generateIdForType(type) {
1380
1380
  return `${type}${generateIndexForType(type)}`;
1381
1381
  }
1382
1382
 
1383
- /**
1384
- * @template T
1385
- * @param {T} data
1386
- * @param {(this: any, key: string, value: any) => any} [replacer]
1387
- * @return {T}
1383
+ /**
1384
+ * @template T
1385
+ * @param {T} data
1386
+ * @param {(this: any, key: string, value: any) => any} [replacer]
1387
+ * @return {T}
1388
1388
  */
1389
1389
  function clone(data, replacer) {
1390
1390
  return JSON.parse(JSON.stringify(data, replacer));
1391
1391
  }
1392
1392
 
1393
- /**
1394
- * Parse the schema for input variables a form might make use of
1395
- *
1396
- * @param {any} schema
1397
- *
1398
- * @return {string[]}
1393
+ /**
1394
+ * Parse the schema for input variables a form might make use of
1395
+ *
1396
+ * @param {any} schema
1397
+ *
1398
+ * @return {string[]}
1399
1399
  */
1400
1400
  function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null), templating = new FeelersTemplating()) {
1401
1401
  if (!schema.components) {
@@ -1447,11 +1447,11 @@ function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLangu
1447
1447
  }
1448
1448
 
1449
1449
  class Importer {
1450
- /**
1451
- * @constructor
1452
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
1453
- * @param { import('../render/FormFields').default } formFields
1454
- * @param { import('../core').FormLayouter } formLayouter
1450
+ /**
1451
+ * @constructor
1452
+ * @param { import('../core').FormFieldRegistry } formFieldRegistry
1453
+ * @param { import('../render/FormFields').default } formFields
1454
+ * @param { import('../core').FormLayouter } formLayouter
1455
1455
  */
1456
1456
  constructor(formFieldRegistry, formFields, formLayouter) {
1457
1457
  this._formFieldRegistry = formFieldRegistry;
@@ -1459,15 +1459,15 @@ class Importer {
1459
1459
  this._formLayouter = formLayouter;
1460
1460
  }
1461
1461
 
1462
- /**
1463
- * Import schema adding `id`, `_parent` and `_path`
1464
- * information to each field and adding it to the
1465
- * form field registry.
1466
- *
1467
- * @param {any} schema
1468
- * @param {any} [data]
1469
- *
1470
- * @return { { warnings: Array<any>, schema: any, data: any } }
1462
+ /**
1463
+ * Import schema adding `id`, `_parent` and `_path`
1464
+ * information to each field and adding it to the
1465
+ * form field registry.
1466
+ *
1467
+ * @param {any} schema
1468
+ * @param {any} [data]
1469
+ *
1470
+ * @return { { warnings: Array<any>, schema: any, data: any } }
1471
1471
  */
1472
1472
  importSchema(schema, data = {}) {
1473
1473
  // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
@@ -1488,11 +1488,11 @@ class Importer {
1488
1488
  }
1489
1489
  }
1490
1490
 
1491
- /**
1492
- * @param {any} formField
1493
- * @param {string} [parentId]
1494
- *
1495
- * @return {any} importedField
1491
+ /**
1492
+ * @param {any} formField
1493
+ * @param {string} [parentId]
1494
+ *
1495
+ * @return {any} importedField
1496
1496
  */
1497
1497
  importFormField(formField, parentId) {
1498
1498
  const {
@@ -1543,10 +1543,10 @@ class Importer {
1543
1543
  });
1544
1544
  }
1545
1545
 
1546
- /**
1547
- * @param {Object} data
1548
- *
1549
- * @return {Object} initializedData
1546
+ /**
1547
+ * @param {Object} data
1548
+ *
1549
+ * @return {Object} initializedData
1550
1550
  */
1551
1551
  initializeFieldValues(data) {
1552
1552
  return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
@@ -1684,11 +1684,11 @@ const FormRenderContext = preact.createContext({
1684
1684
  });
1685
1685
  var FormRenderContext$1 = FormRenderContext;
1686
1686
 
1687
- /**
1688
- * @param {string} type
1689
- * @param {boolean} [strict]
1690
- *
1691
- * @returns {any}
1687
+ /**
1688
+ * @param {string} type
1689
+ * @param {boolean} [strict]
1690
+ *
1691
+ * @returns {any}
1692
1692
  */
1693
1693
  function getService(type, strict) {}
1694
1694
  const FormContext = preact.createContext({
@@ -1697,16 +1697,162 @@ const FormContext = preact.createContext({
1697
1697
  });
1698
1698
  var FormContext$1 = FormContext;
1699
1699
 
1700
+ function useService(type, strict) {
1701
+ const {
1702
+ getService
1703
+ } = hooks.useContext(FormContext$1);
1704
+ return getService(type, strict);
1705
+ }
1706
+
1707
+ /**
1708
+ * Returns the conditionally filtered data of a form reactively.
1709
+ * Memoised to minimize re-renders
1710
+ *
1711
+ */
1712
+ function useFilteredFormData() {
1713
+ const {
1714
+ initialData,
1715
+ data
1716
+ } = useService('form')._getState();
1717
+ const conditionChecker = useService('conditionChecker', false);
1718
+ return hooks.useMemo(() => {
1719
+ const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
1720
+ return {
1721
+ ...initialData,
1722
+ ...newData
1723
+ };
1724
+ }, [conditionChecker, data, initialData]);
1725
+ }
1726
+
1727
+ /**
1728
+ * Evaluate if condition is met reactively based on the conditionChecker and form data.
1729
+ *
1730
+ * @param {string | undefined} condition
1731
+ *
1732
+ * @returns {boolean} true if condition is met or no condition or condition checker exists
1733
+ */
1734
+ function useCondition(condition) {
1735
+ const conditionChecker = useService('conditionChecker', false);
1736
+ const filteredData = useFilteredFormData();
1737
+ return hooks.useMemo(() => {
1738
+ return conditionChecker ? conditionChecker.check(condition, filteredData) : null;
1739
+ }, [conditionChecker, condition, filteredData]);
1740
+ }
1741
+
1742
+ /**
1743
+ * Evaluate a string reactively based on the expressionLanguage and form data.
1744
+ * If the string is not an expression, it is returned as is.
1745
+ * Memoised to minimize re-renders.
1746
+ *
1747
+ * @param {string} value
1748
+ *
1749
+ */
1750
+ function useExpressionEvaluation(value) {
1751
+ const formData = useFilteredFormData();
1752
+ const expressionLanguage = useService('expressionLanguage');
1753
+ return hooks.useMemo(() => {
1754
+ if (expressionLanguage && expressionLanguage.isExpression(value)) {
1755
+ return expressionLanguage.evaluate(value, formData);
1756
+ }
1757
+ return value;
1758
+ }, [expressionLanguage, formData, value]);
1759
+ }
1760
+
1761
+ function useKeyDownAction(targetKey, action, listenerElement = window) {
1762
+ function downHandler({
1763
+ key
1764
+ }) {
1765
+ if (key === targetKey) {
1766
+ action();
1767
+ }
1768
+ }
1769
+ hooks.useEffect(() => {
1770
+ listenerElement.addEventListener('keydown', downHandler);
1771
+ return () => {
1772
+ listenerElement.removeEventListener('keydown', downHandler);
1773
+ };
1774
+ });
1775
+ }
1776
+
1777
+ /**
1778
+ * Retrieve readonly value of a form field, given it can be an
1779
+ * expression optionally or configured globally.
1780
+ *
1781
+ * @typedef { import('../../types').FormProperties } FormProperties
1782
+ *
1783
+ * @param {any} formField
1784
+ * @param {FormProperties} properties
1785
+ *
1786
+ * @returns {boolean}
1787
+ */
1788
+ function useReadonly(formField, properties = {}) {
1789
+ const expressionLanguage = useService('expressionLanguage');
1790
+ const conditionChecker = useService('conditionChecker', false);
1791
+ const filteredData = useFilteredFormData();
1792
+ const {
1793
+ readonly
1794
+ } = formField;
1795
+ if (properties.readOnly) {
1796
+ return true;
1797
+ }
1798
+ if (expressionLanguage && expressionLanguage.isExpression(readonly)) {
1799
+ return conditionChecker ? conditionChecker.check(readonly, filteredData) : false;
1800
+ }
1801
+ return readonly || false;
1802
+ }
1803
+
1804
+ /**
1805
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1806
+ * Memoised to minimize re-renders
1807
+ *
1808
+ * @param {string} value
1809
+ * @param {Object} options
1810
+ * @param {boolean} [options.debug = false]
1811
+ * @param {boolean} [options.strict = false]
1812
+ * @param {Function} [options.buildDebugString]
1813
+ *
1814
+ */
1815
+ function useTemplateEvaluation(value, options) {
1816
+ const filteredData = useFilteredFormData();
1817
+ const templating = useService('templating');
1818
+ return hooks.useMemo(() => {
1819
+ if (templating && templating.isTemplate(value)) {
1820
+ return templating.evaluate(value, filteredData, options);
1821
+ }
1822
+ return value;
1823
+ }, [filteredData, templating, value, options]);
1824
+ }
1825
+
1826
+ /**
1827
+ * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1828
+ * If the string contains multiple lines, only the first line is returned.
1829
+ * Memoised to minimize re-renders
1830
+ *
1831
+ * @param {string} value
1832
+ * @param {Object} [options]
1833
+ * @param {boolean} [options.debug = false]
1834
+ * @param {boolean} [options.strict = false]
1835
+ * @param {Function} [options.buildDebugString]
1836
+ *
1837
+ */
1838
+ function useSingleLineTemplateEvaluation(value, options = {}) {
1839
+ const evaluatedTemplate = useTemplateEvaluation(value, options);
1840
+ return hooks.useMemo(() => evaluatedTemplate && evaluatedTemplate.split('\n')[0], [evaluatedTemplate]);
1841
+ }
1842
+
1700
1843
  function Description(props) {
1701
1844
  const {
1702
1845
  description
1703
1846
  } = props;
1704
- if (!description) {
1847
+ const evaluatedDescription = useSingleLineTemplateEvaluation(description || '', {
1848
+ debug: true
1849
+ });
1850
+ if (!evaluatedDescription) {
1705
1851
  return null;
1706
1852
  }
1707
1853
  return jsxRuntime.jsx("div", {
1708
1854
  class: "fjs-form-field-description",
1709
- children: description
1855
+ children: evaluatedDescription
1710
1856
  });
1711
1857
  }
1712
1858
 
@@ -1739,12 +1885,15 @@ function Label(props) {
1739
1885
  collapseOnEmpty = true,
1740
1886
  required = false
1741
1887
  } = props;
1888
+ const evaluatedLabel = useSingleLineTemplateEvaluation(label || '', {
1889
+ debug: true
1890
+ });
1742
1891
  return jsxRuntime.jsxs("label", {
1743
1892
  for: id,
1744
1893
  class: classNames('fjs-form-field-label', {
1745
1894
  'fjs-incollapsible-label': !collapseOnEmpty
1746
1895
  }, props['class']),
1747
- children: [props.children, label || '', required && jsxRuntime.jsx("span", {
1896
+ children: [props.children, evaluatedLabel, required && jsxRuntime.jsx("span", {
1748
1897
  class: "fjs-asterix",
1749
1898
  children: "*"
1750
1899
  })]
@@ -1871,15 +2020,8 @@ function _isValueSomething(value) {
1871
2020
  return value || value === 0 || value === false;
1872
2021
  }
1873
2022
 
1874
- function useService(type, strict) {
1875
- const {
1876
- getService
1877
- } = hooks.useContext(FormContext$1);
1878
- return getService(type, strict);
1879
- }
1880
-
1881
- /**
1882
- * @enum { String }
2023
+ /**
2024
+ * @enum { String }
1883
2025
  */
1884
2026
  const LOAD_STATES = {
1885
2027
  LOADING: 'loading',
@@ -1887,17 +2029,17 @@ const LOAD_STATES = {
1887
2029
  ERROR: 'error'
1888
2030
  };
1889
2031
 
1890
- /**
1891
- * @typedef {Object} ValuesGetter
1892
- * @property {Object[]} values - The values data
1893
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
2032
+ /**
2033
+ * @typedef {Object} ValuesGetter
2034
+ * @property {Object[]} values - The values data
2035
+ * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
1894
2036
  */
1895
2037
 
1896
- /**
1897
- * A hook to load values for single and multiselect components.
1898
- *
1899
- * @param {Object} field - The form field to handle values for
1900
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
2038
+ /**
2039
+ * A hook to load values for single and multiselect components.
2040
+ *
2041
+ * @param {Object} field - The form field to handle values for
2042
+ * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
1901
2043
  */
1902
2044
  function useValuesAsync (field) {
1903
2045
  const {
@@ -2251,125 +2393,6 @@ Checklist.config = {
2251
2393
  }
2252
2394
  };
2253
2395
 
2254
- /**
2255
- * Returns the conditionally filtered data of a form reactively.
2256
- * Memoised to minimize re-renders
2257
- *
2258
- */
2259
- function useFilteredFormData() {
2260
- const {
2261
- initialData,
2262
- data
2263
- } = useService('form')._getState();
2264
- const conditionChecker = useService('conditionChecker', false);
2265
- return hooks.useMemo(() => {
2266
- const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data;
2267
- return {
2268
- ...initialData,
2269
- ...newData
2270
- };
2271
- }, [conditionChecker, data, initialData]);
2272
- }
2273
-
2274
- /**
2275
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
2276
- *
2277
- * @param {string | undefined} condition
2278
- *
2279
- * @returns {boolean} true if condition is met or no condition or condition checker exists
2280
- */
2281
- function useCondition(condition) {
2282
- const conditionChecker = useService('conditionChecker', false);
2283
- const filteredData = useFilteredFormData();
2284
- return hooks.useMemo(() => {
2285
- return conditionChecker ? conditionChecker.check(condition, filteredData) : null;
2286
- }, [conditionChecker, condition, filteredData]);
2287
- }
2288
-
2289
- /**
2290
- * Evaluate a string reactively based on the expressionLanguage and form data.
2291
- * If the string is not an expression, it is returned as is.
2292
- * Memoised to minimize re-renders.
2293
- *
2294
- * @param {string} value
2295
- *
2296
- */
2297
- function useExpressionEvaluation(value) {
2298
- const formData = useFilteredFormData();
2299
- const expressionLanguage = useService('expressionLanguage');
2300
- return hooks.useMemo(() => {
2301
- if (expressionLanguage && expressionLanguage.isExpression(value)) {
2302
- return expressionLanguage.evaluate(value, formData);
2303
- }
2304
- return value;
2305
- }, [expressionLanguage, formData, value]);
2306
- }
2307
-
2308
- function useKeyDownAction(targetKey, action, listenerElement = window) {
2309
- function downHandler({
2310
- key
2311
- }) {
2312
- if (key === targetKey) {
2313
- action();
2314
- }
2315
- }
2316
- hooks.useEffect(() => {
2317
- listenerElement.addEventListener('keydown', downHandler);
2318
- return () => {
2319
- listenerElement.removeEventListener('keydown', downHandler);
2320
- };
2321
- });
2322
- }
2323
-
2324
- /**
2325
- * Retrieve readonly value of a form field, given it can be an
2326
- * expression optionally or configured globally.
2327
- *
2328
- * @typedef { import('../../types').FormProperties } FormProperties
2329
- *
2330
- * @param {any} formField
2331
- * @param {FormProperties} properties
2332
- *
2333
- * @returns {boolean}
2334
- */
2335
- function useReadonly(formField, properties = {}) {
2336
- const expressionLanguage = useService('expressionLanguage');
2337
- const conditionChecker = useService('conditionChecker', false);
2338
- const filteredData = useFilteredFormData();
2339
- const {
2340
- readonly
2341
- } = formField;
2342
- if (properties.readOnly) {
2343
- return true;
2344
- }
2345
- if (expressionLanguage && expressionLanguage.isExpression(readonly)) {
2346
- return conditionChecker ? conditionChecker.check(readonly, filteredData) : false;
2347
- }
2348
- return readonly || false;
2349
- }
2350
-
2351
- /**
2352
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2353
- * Memoised to minimize re-renders
2354
- *
2355
- * @param {string} value
2356
- * @param {Object} options
2357
- * @param {boolean} [options.debug = false]
2358
- * @param {boolean} [options.strict = false]
2359
- * @param {Function} [options.buildDebugString]
2360
- *
2361
- */
2362
- function useTemplateEvaluation(value, options) {
2363
- const filteredData = useFilteredFormData();
2364
- const templating = useService('templating');
2365
- return hooks.useMemo(() => {
2366
- if (templating.isTemplate(value)) {
2367
- return templating.evaluate(value, filteredData, options);
2368
- }
2369
- return value;
2370
- }, [filteredData, templating, value, options]);
2371
- }
2372
-
2373
2396
  const noop$1 = () => false;
2374
2397
  function FormField(props) {
2375
2398
  const {
@@ -3131,10 +3154,10 @@ Datetime.config = {
3131
3154
  }
3132
3155
  };
3133
3156
 
3134
- /**
3135
- * This file must not be changed or exchanged.
3136
- *
3137
- * @see http://bpmn.io/license for more information.
3157
+ /**
3158
+ * This file must not be changed or exchanged.
3159
+ *
3160
+ * @see http://bpmn.io/license for more information.
3138
3161
  */
3139
3162
  function Logo() {
3140
3163
  return jsxRuntime.jsxs("svg", {
@@ -3266,11 +3289,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
3266
3289
 
3267
3290
  const FORM_ELEMENT = document.createElement('form');
3268
3291
 
3269
- /**
3270
- * Sanitize a HTML string and return the cleaned, safe version.
3271
- *
3272
- * @param {string} html
3273
- * @return {string}
3292
+ /**
3293
+ * Sanitize a HTML string and return the cleaned, safe version.
3294
+ *
3295
+ * @param {string} html
3296
+ * @return {string}
3274
3297
  */
3275
3298
 
3276
3299
  // see https://github.com/developit/snarkdown/issues/70
@@ -3288,29 +3311,29 @@ function sanitizeHTML(html) {
3288
3311
  }
3289
3312
  }
3290
3313
 
3291
- /**
3292
- * Sanitizes an image source to ensure we only allow for data URI and links
3293
- * that start with http(s).
3294
- *
3295
- * Note: Most browsers anyway do not support script execution in <img> elements.
3296
- *
3297
- * @param {string} src
3298
- * @returns {string}
3314
+ /**
3315
+ * Sanitizes an image source to ensure we only allow for data URI and links
3316
+ * that start with http(s).
3317
+ *
3318
+ * Note: Most browsers anyway do not support script execution in <img> elements.
3319
+ *
3320
+ * @param {string} src
3321
+ * @returns {string}
3299
3322
  */
3300
3323
  function sanitizeImageSource(src) {
3301
3324
  const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
3302
3325
  return valid ? src : '';
3303
3326
  }
3304
3327
 
3305
- /**
3306
- * Recursively sanitize a HTML node, potentially
3307
- * removing it, its children or attributes.
3308
- *
3309
- * Inspired by https://github.com/developit/snarkdown/issues/70
3310
- * and https://github.com/cure53/DOMPurify. Simplified
3311
- * for our use-case.
3312
- *
3313
- * @param {Element} node
3328
+ /**
3329
+ * Recursively sanitize a HTML node, potentially
3330
+ * removing it, its children or attributes.
3331
+ *
3332
+ * Inspired by https://github.com/developit/snarkdown/issues/70
3333
+ * and https://github.com/cure53/DOMPurify. Simplified
3334
+ * for our use-case.
3335
+ *
3336
+ * @param {Element} node
3314
3337
  */
3315
3338
  function sanitizeNode(node) {
3316
3339
  // allow text nodes
@@ -3354,13 +3377,13 @@ function sanitizeNode(node) {
3354
3377
  }
3355
3378
  }
3356
3379
 
3357
- /**
3358
- * Validates attributes for validity.
3359
- *
3360
- * @param {string} lcTag
3361
- * @param {string} lcName
3362
- * @param {string} value
3363
- * @return {boolean}
3380
+ /**
3381
+ * Validates attributes for validity.
3382
+ *
3383
+ * @param {string} lcTag
3384
+ * @param {string} lcName
3385
+ * @param {string} value
3386
+ * @return {boolean}
3364
3387
  */
3365
3388
  function isValidAttribute(lcTag, lcName, value) {
3366
3389
  // disallow most attributes based on whitelist