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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,25 +20,25 @@ class FeelExpressionLanguage {
20
20
  this._eventBus = eventBus;
21
21
  }
22
22
 
23
- /**
24
- * Determines if the given string is a FEEL expression.
25
- *
26
- * @param {string} value
27
- * @returns {boolean}
28
- *
23
+ /**
24
+ * Determines if the given string is a FEEL expression.
25
+ *
26
+ * @param {string} value
27
+ * @returns {boolean}
28
+ *
29
29
  */
30
30
  isExpression(value) {
31
31
  return minDash.isString(value) && value.startsWith('=');
32
32
  }
33
33
 
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[]}
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[]}
42
42
  */
43
43
  getVariableNames(expression, options = {}) {
44
44
  const {
@@ -55,13 +55,13 @@ class FeelExpressionLanguage {
55
55
  throw new Error('Unknown expression type: ' + options.type);
56
56
  }
57
57
 
58
- /**
59
- * Evaluate an expression.
60
- *
61
- * @param {string} expression
62
- * @param {import('../../types').Data} [data]
63
- *
64
- * @returns {any}
58
+ /**
59
+ * Evaluate an expression.
60
+ *
61
+ * @param {string} expression
62
+ * @param {import('../../types').Data} [data]
63
+ *
64
+ * @returns {any}
65
65
  */
66
66
  evaluate(expression, data = {}) {
67
67
  if (!expression) {
@@ -113,17 +113,17 @@ class FeelersTemplating {
113
113
  return minDash.isString(value) && (value.startsWith('=') || /{{/.test(value));
114
114
  }
115
115
 
116
- /**
117
- * Evaluate a template.
118
- *
119
- * @param {string} template
120
- * @param {Object<string, any>} context
121
- * @param {Object} options
122
- * @param {boolean} [options.debug = false]
123
- * @param {boolean} [options.strict = false]
124
- * @param {Function} [options.buildDebugString]
125
- *
126
- * @returns
116
+ /**
117
+ * Evaluate a template.
118
+ *
119
+ * @param {string} template
120
+ * @param {Object<string, any>} context
121
+ * @param {Object} options
122
+ * @param {boolean} [options.debug = false]
123
+ * @param {boolean} [options.strict = false]
124
+ * @param {Function} [options.buildDebugString]
125
+ *
126
+ * @returns
127
127
  */
128
128
  evaluate(template, context = {}, options = {}) {
129
129
  const {
@@ -140,9 +140,9 @@ class FeelersTemplating {
140
140
  }
141
141
  FeelersTemplating.$inject = [];
142
142
 
143
- /**
144
- * @typedef {object} Condition
145
- * @property {string} [hide]
143
+ /**
144
+ * @typedef {object} Condition
145
+ * @property {string} [hide]
146
146
  */
147
147
 
148
148
  class ConditionChecker {
@@ -151,11 +151,11 @@ class ConditionChecker {
151
151
  this._eventBus = eventBus;
152
152
  }
153
153
 
154
- /**
155
- * For given data, remove properties based on condition.
156
- *
157
- * @param {Object<string, any>} properties
158
- * @param {Object<string, any>} data
154
+ /**
155
+ * For given data, remove properties based on condition.
156
+ *
157
+ * @param {Object<string, any>} properties
158
+ * @param {Object<string, any>} data
159
159
  */
160
160
  applyConditions(properties, data = {}) {
161
161
  const conditions = this._getConditions();
@@ -174,13 +174,13 @@ class ConditionChecker {
174
174
  return newProperties;
175
175
  }
176
176
 
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}
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}
184
184
  */
185
185
  check(condition, data = {}) {
186
186
  if (!condition) {
@@ -201,12 +201,12 @@ class ConditionChecker {
201
201
  }
202
202
  }
203
203
 
204
- /**
205
- * Check if hide condition is met.
206
- *
207
- * @param {Condition} condition
208
- * @param {Object<string, any>} data
209
- * @returns {boolean}
204
+ /**
205
+ * Check if hide condition is met.
206
+ *
207
+ * @param {Condition} condition
208
+ * @param {Object<string, any>} data
209
+ * @returns {boolean}
210
210
  */
211
211
  _checkHideCondition(condition, data) {
212
212
  if (!condition.hide) {
@@ -248,12 +248,12 @@ class MarkdownRenderer {
248
248
  this._converter = new showdown.Converter();
249
249
  }
250
250
 
251
- /**
252
- * Render markdown to HTML.
253
- *
254
- * @param {string} markdown - The markdown to render
255
- *
256
- * @returns {string} HTML
251
+ /**
252
+ * Render markdown to HTML.
253
+ *
254
+ * @param {string} markdown - The markdown to render
255
+ *
256
+ * @returns {string} HTML
257
257
  */
258
258
  render(markdown) {
259
259
  return this._converter.makeHtml(markdown);
@@ -762,8 +762,13 @@ class Validator {
762
762
  if (validate.pattern && value && !new RegExp(validate.pattern).test(value)) {
763
763
  errors = [...errors, `Field must match pattern ${validate.pattern}.`];
764
764
  }
765
- if (validate.required && (minDash.isNil(value) || value === '')) {
766
- errors = [...errors, 'Field is required.'];
765
+ if (validate.required) {
766
+ const isUncheckedCheckbox = type === 'checkbox' && value === false;
767
+ const isUnsetValue = minDash.isNil(value) || value === '';
768
+ const isEmptyMultiselect = Array.isArray(value) && value.length === 0;
769
+ if (isUncheckedCheckbox || isUnsetValue || isEmptyMultiselect) {
770
+ errors = [...errors, 'Field is required.'];
771
+ }
767
772
  }
768
773
  if ('min' in validate && (value || value === 0) && value < validate.min) {
769
774
  errors = [...errors, `Field must have minimum value of ${validate.min}.`];
@@ -837,23 +842,23 @@ class FormFieldRegistry {
837
842
  }
838
843
  FormFieldRegistry.$inject = ['eventBus'];
839
844
 
840
- /**
841
- * @typedef { { id: String, components: Array<String> } } FormRow
842
- * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
845
+ /**
846
+ * @typedef { { id: String, components: Array<String> } } FormRow
847
+ * @typedef { { formFieldId: String, rows: Array<FormRow> } } FormRows
843
848
  */
844
849
 
845
- /**
846
- * Maintains the Form layout in a given structure, for example
847
- *
848
- * [
849
- * {
850
- * formFieldId: 'FormField_1',
851
- * rows: [
852
- * { id: 'Row_1', components: [ 'Text_1', 'Textdield_1', ... ] }
853
- * ]
854
- * }
855
- * ]
856
- *
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
+ *
857
862
  */
858
863
  class FormLayouter {
859
864
  constructor(eventBus) {
@@ -863,8 +868,8 @@ class FormLayouter {
863
868
  this._eventBus = eventBus;
864
869
  }
865
870
 
866
- /**
867
- * @param {FormRow} row
871
+ /**
872
+ * @param {FormRow} row
868
873
  */
869
874
  addRow(formFieldId, row) {
870
875
  let rowsPerComponent = this._rows.find(r => r.formFieldId === formFieldId);
@@ -878,18 +883,18 @@ class FormLayouter {
878
883
  rowsPerComponent.rows.push(row);
879
884
  }
880
885
 
881
- /**
882
- * @param {String} id
883
- * @returns {FormRow}
886
+ /**
887
+ * @param {String} id
888
+ * @returns {FormRow}
884
889
  */
885
890
  getRow(id) {
886
891
  const rows = allRows(this._rows);
887
892
  return rows.find(r => r.id === id);
888
893
  }
889
894
 
890
- /**
891
- * @param {any} formField
892
- * @returns {FormRow}
895
+ /**
896
+ * @param {any} formField
897
+ * @returns {FormRow}
893
898
  */
894
899
  getRowForField(formField) {
895
900
  return allRows(this._rows).find(r => {
@@ -900,9 +905,9 @@ class FormLayouter {
900
905
  });
901
906
  }
902
907
 
903
- /**
904
- * @param {String} formFieldId
905
- * @returns { Array<FormRow> }
908
+ /**
909
+ * @param {String} formFieldId
910
+ * @returns { Array<FormRow> }
906
911
  */
907
912
  getRows(formFieldId) {
908
913
  const rowsForField = this._rows.find(r => formFieldId === r.formFieldId);
@@ -912,15 +917,15 @@ class FormLayouter {
912
917
  return rowsForField.rows;
913
918
  }
914
919
 
915
- /**
916
- * @returns {string}
920
+ /**
921
+ * @returns {string}
917
922
  */
918
923
  nextRowId() {
919
924
  return this._ids.nextPrefixed('Row_');
920
925
  }
921
926
 
922
- /**
923
- * @param {any} formField
927
+ /**
928
+ * @param {any} formField
924
929
  */
925
930
  calculateLayout(formField) {
926
931
  const {
@@ -974,9 +979,9 @@ function groupByRow(components, ids) {
974
979
  });
975
980
  }
976
981
 
977
- /**
978
- * @param {Array<FormRows>} formRows
979
- * @returns {Array<FormRow>}
982
+ /**
983
+ * @param {Array<FormRows>} formRows
984
+ * @returns {Array<FormRow>}
980
985
  */
981
986
  function allRows(formRows) {
982
987
  return minDash.flatten(formRows.map(c => c.rows));
@@ -1053,10 +1058,10 @@ function createInjector(bootstrapModules) {
1053
1058
  return injector;
1054
1059
  }
1055
1060
 
1056
- /**
1057
- * @param {string?} prefix
1058
- *
1059
- * @returns Element
1061
+ /**
1062
+ * @param {string?} prefix
1063
+ *
1064
+ * @returns Element
1060
1065
  */
1061
1066
  function createFormContainer(prefix = 'fjs') {
1062
1067
  const container = document.createElement('div');
@@ -1101,22 +1106,22 @@ function generateIdForType(type) {
1101
1106
  return `${type}${generateIndexForType(type)}`;
1102
1107
  }
1103
1108
 
1104
- /**
1105
- * @template T
1106
- * @param {T} data
1107
- * @param {(this: any, key: string, value: any) => any} [replacer]
1108
- * @return {T}
1109
+ /**
1110
+ * @template T
1111
+ * @param {T} data
1112
+ * @param {(this: any, key: string, value: any) => any} [replacer]
1113
+ * @return {T}
1109
1114
  */
1110
1115
  function clone(data, replacer) {
1111
1116
  return JSON.parse(JSON.stringify(data, replacer));
1112
1117
  }
1113
1118
 
1114
- /**
1115
- * Parse the schema for input variables a form might make use of
1116
- *
1117
- * @param {any} schema
1118
- *
1119
- * @return {string[]}
1119
+ /**
1120
+ * Parse the schema for input variables a form might make use of
1121
+ *
1122
+ * @param {any} schema
1123
+ *
1124
+ * @return {string[]}
1120
1125
  */
1121
1126
  function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLanguage(null)) {
1122
1127
  if (!schema.components) {
@@ -1161,11 +1166,11 @@ function getSchemaVariables(schema, expressionLanguage = new FeelExpressionLangu
1161
1166
  }
1162
1167
 
1163
1168
  class Importer {
1164
- /**
1165
- * @constructor
1166
- * @param { import('../core').FormFieldRegistry } formFieldRegistry
1167
- * @param { import('../render/FormFields').default } formFields
1168
- * @param { import('../core').FormLayouter } formLayouter
1169
+ /**
1170
+ * @constructor
1171
+ * @param { import('../core').FormFieldRegistry } formFieldRegistry
1172
+ * @param { import('../render/FormFields').default } formFields
1173
+ * @param { import('../core').FormLayouter } formLayouter
1169
1174
  */
1170
1175
  constructor(formFieldRegistry, formFields, formLayouter) {
1171
1176
  this._formFieldRegistry = formFieldRegistry;
@@ -1173,15 +1178,15 @@ class Importer {
1173
1178
  this._formLayouter = formLayouter;
1174
1179
  }
1175
1180
 
1176
- /**
1177
- * Import schema adding `id`, `_parent` and `_path`
1178
- * information to each field and adding it to the
1179
- * form field registry.
1180
- *
1181
- * @param {any} schema
1182
- * @param {any} [data]
1183
- *
1184
- * @return { { warnings: Array<any>, schema: any, data: any } }
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 } }
1185
1190
  */
1186
1191
  importSchema(schema, data = {}) {
1187
1192
  // TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
@@ -1202,11 +1207,11 @@ class Importer {
1202
1207
  }
1203
1208
  }
1204
1209
 
1205
- /**
1206
- * @param {any} formField
1207
- * @param {string} [parentId]
1208
- *
1209
- * @return {any} importedField
1210
+ /**
1211
+ * @param {any} formField
1212
+ * @param {string} [parentId]
1213
+ *
1214
+ * @return {any} importedField
1210
1215
  */
1211
1216
  importFormField(formField, parentId) {
1212
1217
  const {
@@ -1257,10 +1262,10 @@ class Importer {
1257
1262
  });
1258
1263
  }
1259
1264
 
1260
- /**
1261
- * @param {Object} data
1262
- *
1263
- * @return {Object} initializedData
1265
+ /**
1266
+ * @param {Object} data
1267
+ *
1268
+ * @return {Object} initializedData
1264
1269
  */
1265
1270
  initializeFieldValues(data) {
1266
1271
  return this._formFieldRegistry.getAll().reduce((initializedData, formField) => {
@@ -1392,11 +1397,11 @@ const FormRenderContext = preact.createContext({
1392
1397
  });
1393
1398
  var FormRenderContext$1 = FormRenderContext;
1394
1399
 
1395
- /**
1396
- * @param {string} type
1397
- * @param {boolean} [strict]
1398
- *
1399
- * @returns {any}
1400
+ /**
1401
+ * @param {string} type
1402
+ * @param {boolean} [strict]
1403
+ *
1404
+ * @returns {any}
1400
1405
  */
1401
1406
  function getService(type, strict) {}
1402
1407
  const FormContext = preact.createContext({
@@ -1467,8 +1472,12 @@ function Checkbox(props) {
1467
1472
  const {
1468
1473
  description,
1469
1474
  id,
1470
- label
1475
+ label,
1476
+ validate = {}
1471
1477
  } = field;
1478
+ const {
1479
+ required
1480
+ } = validate;
1472
1481
  const onChange = ({
1473
1482
  target
1474
1483
  }) => {
@@ -1490,7 +1499,7 @@ function Checkbox(props) {
1490
1499
  children: [jsxRuntime.jsx(Label, {
1491
1500
  id: prefixId(id, formId),
1492
1501
  label: label,
1493
- required: false,
1502
+ required: required,
1494
1503
  children: jsxRuntime.jsx("input", {
1495
1504
  checked: value,
1496
1505
  class: "fjs-input",
@@ -1571,8 +1580,8 @@ function useService(type, strict) {
1571
1580
  return getService(type, strict);
1572
1581
  }
1573
1582
 
1574
- /**
1575
- * @enum { String }
1583
+ /**
1584
+ * @enum { String }
1576
1585
  */
1577
1586
  const LOAD_STATES = {
1578
1587
  LOADING: 'loading',
@@ -1580,17 +1589,17 @@ const LOAD_STATES = {
1580
1589
  ERROR: 'error'
1581
1590
  };
1582
1591
 
1583
- /**
1584
- * @typedef {Object} ValuesGetter
1585
- * @property {Object[]} values - The values data
1586
- * @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
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
1587
1596
  */
1588
1597
 
1589
- /**
1590
- * A hook to load values for single and multiselect components.
1591
- *
1592
- * @param {Object} field - The form field to handle values for
1593
- * @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
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
1594
1603
  */
1595
1604
  function useValuesAsync (field) {
1596
1605
  const {
@@ -1857,8 +1866,12 @@ function Checklist(props) {
1857
1866
  const {
1858
1867
  description,
1859
1868
  id,
1860
- label
1869
+ label,
1870
+ validate = {}
1861
1871
  } = field;
1872
+ const {
1873
+ required
1874
+ } = validate;
1862
1875
  const toggleCheckbox = v => {
1863
1876
  let newValue = [...value];
1864
1877
  if (!newValue.includes(v)) {
@@ -1884,7 +1897,8 @@ function Checklist(props) {
1884
1897
  disabled
1885
1898
  })),
1886
1899
  children: [jsxRuntime.jsx(Label, {
1887
- label: label
1900
+ label: label,
1901
+ required: required
1888
1902
  }), loadState == LOAD_STATES.LOADED && options.map((v, index) => {
1889
1903
  return jsxRuntime.jsx(Label, {
1890
1904
  id: prefixId(`${id}-${index}`, formId),
@@ -1931,10 +1945,10 @@ Checklist.emptyValue = [];
1931
1945
  Checklist.sanitizeValue = sanitizeMultiSelectValue;
1932
1946
  Checklist.group = 'selection';
1933
1947
 
1934
- /**
1935
- * Returns the conditionally filtered data of a form reactively.
1936
- * Memoised to minimize re-renders
1937
- *
1948
+ /**
1949
+ * Returns the conditionally filtered data of a form reactively.
1950
+ * Memoised to minimize re-renders
1951
+ *
1938
1952
  */
1939
1953
  function useFilteredFormData() {
1940
1954
  const {
@@ -1951,12 +1965,12 @@ function useFilteredFormData() {
1951
1965
  }, [conditionChecker, data, initialData]);
1952
1966
  }
1953
1967
 
1954
- /**
1955
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
1956
- *
1957
- * @param {string | undefined} condition
1958
- *
1959
- * @returns {boolean} true if condition is met or no condition or condition checker exists
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
1960
1974
  */
1961
1975
  function useCondition(condition) {
1962
1976
  const conditionChecker = useService('conditionChecker', false);
@@ -1966,13 +1980,13 @@ function useCondition(condition) {
1966
1980
  }, [conditionChecker, condition, filteredData]);
1967
1981
  }
1968
1982
 
1969
- /**
1970
- * Evaluate a string reactively based on the expressionLanguage and form data.
1971
- * If the string is not an expression, it is returned as is.
1972
- * Memoised to minimize re-renders.
1973
- *
1974
- * @param {string} value
1975
- *
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
+ *
1976
1990
  */
1977
1991
  function useExpressionEvaluation(value) {
1978
1992
  const formData = useFilteredFormData();
@@ -2001,16 +2015,16 @@ function useKeyDownAction(targetKey, action, listenerElement = window) {
2001
2015
  });
2002
2016
  }
2003
2017
 
2004
- /**
2005
- * Template a string reactively based on form data. If the string is not a template, it is returned as is.
2006
- * Memoised to minimize re-renders
2007
- *
2008
- * @param {string} value
2009
- * @param {Object} options
2010
- * @param {boolean} [options.debug = false]
2011
- * @param {boolean} [options.strict = false]
2012
- * @param {Function} [options.buildDebugString]
2013
- *
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
+ *
2014
2028
  */
2015
2029
  function useTemplateEvaluation(value, options) {
2016
2030
  const filteredData = useFilteredFormData();
@@ -2761,10 +2775,10 @@ Datetime.sanitizeValue = sanitizeDateTimePickerValue;
2761
2775
  Datetime.label = 'Date time';
2762
2776
  Datetime.group = 'basic-input';
2763
2777
 
2764
- /**
2765
- * This file must not be changed or exchanged.
2766
- *
2767
- * @see http://bpmn.io/license for more information.
2778
+ /**
2779
+ * This file must not be changed or exchanged.
2780
+ *
2781
+ * @see http://bpmn.io/license for more information.
2768
2782
  */
2769
2783
  function Logo() {
2770
2784
  return jsxRuntime.jsxs("svg", {
@@ -2891,11 +2905,11 @@ const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u
2891
2905
 
2892
2906
  const FORM_ELEMENT = document.createElement('form');
2893
2907
 
2894
- /**
2895
- * Sanitize a HTML string and return the cleaned, safe version.
2896
- *
2897
- * @param {string} html
2898
- * @return {string}
2908
+ /**
2909
+ * Sanitize a HTML string and return the cleaned, safe version.
2910
+ *
2911
+ * @param {string} html
2912
+ * @return {string}
2899
2913
  */
2900
2914
 
2901
2915
  // see https://github.com/developit/snarkdown/issues/70
@@ -2913,29 +2927,29 @@ function sanitizeHTML(html) {
2913
2927
  }
2914
2928
  }
2915
2929
 
2916
- /**
2917
- * Sanitizes an image source to ensure we only allow for data URI and links
2918
- * that start with http(s).
2919
- *
2920
- * Note: Most browsers anyway do not support script execution in <img> elements.
2921
- *
2922
- * @param {string} src
2923
- * @returns {string}
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}
2924
2938
  */
2925
2939
  function sanitizeImageSource(src) {
2926
2940
  const valid = ALLOWED_IMAGE_SRC_PATTERN.test(src);
2927
2941
  return valid ? src : '';
2928
2942
  }
2929
2943
 
2930
- /**
2931
- * Recursively sanitize a HTML node, potentially
2932
- * removing it, its children or attributes.
2933
- *
2934
- * Inspired by https://github.com/developit/snarkdown/issues/70
2935
- * and https://github.com/cure53/DOMPurify. Simplified
2936
- * for our use-case.
2937
- *
2938
- * @param {Element} node
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
2939
2953
  */
2940
2954
  function sanitizeNode(node) {
2941
2955
  // allow text nodes
@@ -2979,13 +2993,13 @@ function sanitizeNode(node) {
2979
2993
  }
2980
2994
  }
2981
2995
 
2982
- /**
2983
- * Validates attributes for validity.
2984
- *
2985
- * @param {string} lcTag
2986
- * @param {string} lcName
2987
- * @param {string} value
2988
- * @return {boolean}
2996
+ /**
2997
+ * Validates attributes for validity.
2998
+ *
2999
+ * @param {string} lcTag
3000
+ * @param {string} lcName
3001
+ * @param {string} value
3002
+ * @return {boolean}
2989
3003
  */
2990
3004
  function isValidAttribute(lcTag, lcName, value) {
2991
3005
  // disallow most attributes based on whitelist
@@ -3754,8 +3768,12 @@ function Taglist(props) {
3754
3768
  const {
3755
3769
  description,
3756
3770
  id,
3757
- label
3771
+ label,
3772
+ validate = {}
3758
3773
  } = field;
3774
+ const {
3775
+ required
3776
+ } = validate;
3759
3777
  const {
3760
3778
  formId
3761
3779
  } = hooks.useContext(FormContext$1);
@@ -3854,6 +3872,7 @@ function Taglist(props) {
3854
3872
  }),
3855
3873
  children: [jsxRuntime.jsx(Label, {
3856
3874
  label: label,
3875
+ required: required,
3857
3876
  id: prefixId(`${id}-search`, formId)
3858
3877
  }), jsxRuntime.jsxs("div", {
3859
3878
  class: classNames('fjs-taglist', {