@bpmn-io/form-js-viewer 1.13.1 → 1.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
@@ -1138,9 +1138,6 @@ function _isElementScrollable(el) {
1138
1138
  return (overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight;
1139
1139
  }
1140
1140
 
1141
- const EMPTY_OBJECT = {};
1142
- const EMPTY_ARRAY$2 = [];
1143
-
1144
1141
  /**
1145
1142
  * Custom hook to scroll an element within a scrollable container.
1146
1143
  *
@@ -1154,8 +1151,8 @@ const EMPTY_ARRAY$2 = [];
1154
1151
  * @param {Array} [flagRefs] - An array of refs that are used as flags to control when to scroll.
1155
1152
  */
1156
1153
  function useScrollIntoView(scrolledElementRef, deps, scrollOptions, flagRefs) {
1157
- const _scrollOptions = scrollOptions || EMPTY_OBJECT;
1158
- const _flagRefs = flagRefs || EMPTY_ARRAY$2;
1154
+ const _scrollOptions = scrollOptions;
1155
+ const _flagRefs = flagRefs;
1159
1156
  hooks.useEffect(() => {
1160
1157
  // return early if flags are not raised, or component is not mounted
1161
1158
  if (minDash.some(_flagRefs, ref => !ref.current) || !scrolledElementRef.current) {
@@ -1543,9 +1540,8 @@ function _getZeroPaddedString(time) {
1543
1540
  return time.toString().padStart(2, '0');
1544
1541
  }
1545
1542
 
1546
- const ALLOWED_IMAGE_SRC_PATTERN = /^(https?|data):.*/i; // eslint-disable-line no-useless-escape
1547
- const ALLOWED_IFRAME_SRC_PATTERN = /^(https):\/\/*/i; // eslint-disable-line no-useless-escape
1548
-
1543
+ const ALLOWED_IMAGE_SRC_PATTERN = /^(https?|data):.*/i;
1544
+ const ALLOWED_IFRAME_SRC_PATTERN = /^(https):\/\/*/i;
1549
1545
  function sanitizeDateTimePickerValue(options) {
1550
1546
  const {
1551
1547
  formField,
@@ -1787,10 +1783,10 @@ function Errors(props) {
1787
1783
  "aria-live": "polite",
1788
1784
  id: id,
1789
1785
  children: jsxRuntime.jsx("ul", {
1790
- children: errors.map(error => {
1786
+ children: errors.map((error, index) => {
1791
1787
  return jsxRuntime.jsx("li", {
1792
1788
  children: error
1793
- });
1789
+ }, index);
1794
1790
  })
1795
1791
  })
1796
1792
  });
@@ -1809,7 +1805,7 @@ function Label(props) {
1809
1805
  });
1810
1806
  return jsxRuntime.jsxs("label", {
1811
1807
  id: id,
1812
- for: htmlFor,
1808
+ htmlFor: htmlFor,
1813
1809
  class: classNames('fjs-form-field-label', {
1814
1810
  'fjs-incollapsible-label': !collapseOnEmpty
1815
1811
  }, props['class']),
@@ -2292,7 +2288,7 @@ function RowsRenderer(props) {
2292
2288
  indexes: indexes
2293
2289
  });
2294
2290
  })
2295
- });
2291
+ }, row.id);
2296
2292
  }), ' ']
2297
2293
  });
2298
2294
  }
@@ -2702,16 +2698,16 @@ function DropdownList(props) {
2702
2698
  maxHeight: height,
2703
2699
  scrollBehavior: smoothScrolling ? 'smooth' : 'auto'
2704
2700
  },
2705
- children: [values.length > 0 && values.map((v, i) => {
2701
+ children: [values.length > 0 && values.map((entry, index) => {
2706
2702
  return jsxRuntime.jsx("div", {
2707
2703
  class: classNames('fjs-dropdownlist-item', {
2708
- focused: focusedValueIndex === i
2704
+ focused: focusedValueIndex === index
2709
2705
  }),
2710
- onMouseMove: mouseControl ? undefined : e => onMouseMovedInKeyboardMode(e, i),
2711
- onMouseEnter: mouseControl ? () => setFocusedValueIndex(i) : undefined,
2712
- onMouseDown: e => onValueSelected(v),
2713
- children: getLabel(v)
2714
- });
2706
+ onMouseMove: mouseControl ? undefined : e => onMouseMovedInKeyboardMode(e, index),
2707
+ onMouseEnter: mouseControl ? () => setFocusedValueIndex(index) : undefined,
2708
+ onMouseDown: e => onValueSelected(entry),
2709
+ children: getLabel(entry)
2710
+ }, entry.value);
2715
2711
  }), !values.length && jsxRuntime.jsx("div", {
2716
2712
  class: "fjs-dropdownlist-empty",
2717
2713
  children: emptyListMessage
@@ -4778,7 +4774,7 @@ function Taglist(props) {
4778
4774
  }),
4779
4775
  children: [loadState === LOAD_STATES.LOADED && jsxRuntime.jsx("div", {
4780
4776
  class: "fjs-taglist-tags",
4781
- children: values.map(v => {
4777
+ children: values.map(entry => {
4782
4778
  return jsxRuntime.jsxs("div", {
4783
4779
  class: classNames('fjs-taglist-tag', {
4784
4780
  'fjs-disabled': disabled,
@@ -4787,17 +4783,17 @@ function Taglist(props) {
4787
4783
  onMouseDown: e => e.preventDefault(),
4788
4784
  children: [jsxRuntime.jsx("span", {
4789
4785
  class: "fjs-taglist-tag-label",
4790
- children: getLabelCorrelation(v)
4786
+ children: getLabelCorrelation(entry)
4791
4787
  }), !disabled && !readonly && jsxRuntime.jsx("button", {
4792
4788
  type: "button",
4793
4789
  title: "Remove tag",
4794
4790
  class: "fjs-taglist-tag-remove",
4795
4791
  onFocus: onElementFocus,
4796
4792
  onBlur: onElementBlur,
4797
- onClick: event => onTagRemoveClick(event, v),
4793
+ onClick: event => onTagRemoveClick(event, entry),
4798
4794
  children: jsxRuntime.jsx(SvgXMark, {})
4799
4795
  })]
4800
- });
4796
+ }, entry);
4801
4797
  })
4802
4798
  }), jsxRuntime.jsx("input", {
4803
4799
  disabled: disabled,
@@ -5307,7 +5303,7 @@ function Textarea(props) {
5307
5303
  }), jsxRuntime.jsx("textarea", {
5308
5304
  class: "fjs-textarea",
5309
5305
  disabled: disabled,
5310
- readonly: readonly,
5306
+ readOnly: readonly,
5311
5307
  id: domId,
5312
5308
  onInput: onInputChange,
5313
5309
  onBlur: onInputBlur,
@@ -5454,7 +5450,7 @@ function Table(props) {
5454
5450
  key
5455
5451
  }) => key);
5456
5452
  const evaluatedDataSource = useExpressionEvaluation(dataSource);
5457
- const data = Array.isArray(evaluatedDataSource) ? evaluatedDataSource.filter(i => i !== undefined) : [];
5453
+ const data = Array.isArray(evaluatedDataSource) ? evaluatedDataSource.filter(entry => !minDash.isNil(entry) || typeof entry !== 'object') : [];
5458
5454
  const sortedData = sortBy === null ? data : sortByColumn(data, sortBy.key, sortBy.direction);
5459
5455
 
5460
5456
  /** @type {unknown[][]} */
@@ -5464,12 +5460,6 @@ function Table(props) {
5464
5460
  hooks.useEffect(() => {
5465
5461
  setCurrentPage(0);
5466
5462
  }, [rowCount, sortBy]);
5467
- const serializeCellData = cellData => {
5468
- if (cellData !== null && typeof cellData === 'object') {
5469
- return JSON.stringify(cellData);
5470
- }
5471
- return cellData;
5472
- };
5473
5463
 
5474
5464
  /** @param {string} key */
5475
5465
  function toggleSortBy(key) {
@@ -5740,6 +5730,17 @@ function getHeaderAriaLabel(sortBy, key, label) {
5740
5730
  return `Click to sort by ${label} ascending`;
5741
5731
  }
5742
5732
 
5733
+ /**
5734
+ * @param {unknown} cellData
5735
+ * @returns string
5736
+ */
5737
+ function serializeCellData(cellData) {
5738
+ if (cellData !== null && typeof cellData === 'object') {
5739
+ return JSON.stringify(cellData);
5740
+ }
5741
+ return `${cellData || ''}`;
5742
+ }
5743
+
5743
5744
  const FILE_PICKER_FILE_KEY_PREFIX = 'files::';
5744
5745
 
5745
5746
  const type$1 = 'filepicker';
@@ -5759,6 +5760,8 @@ const EMPTY_ARRAY$1 = [];
5759
5760
  * @property {string} [field.label]
5760
5761
  * @property {string} [field.accept]
5761
5762
  * @property {string|boolean} [field.multiple]
5763
+ * @property {Object} [field.validate]
5764
+ * @property {boolean} [field.validate.required]
5762
5765
  * @property {string} [value]
5763
5766
  *
5764
5767
  * @param {Props} props
@@ -5776,13 +5779,13 @@ function FilePicker(props) {
5776
5779
  errors = [],
5777
5780
  disabled,
5778
5781
  readonly,
5779
- required,
5780
5782
  value: filesKey = ''
5781
5783
  } = props;
5782
5784
  const {
5783
5785
  label,
5784
5786
  multiple = false,
5785
- accept = ''
5787
+ accept = '',
5788
+ validate = {}
5786
5789
  } = field;
5787
5790
  /** @type {string} */
5788
5791
  const evaluatedAccept = useSingleLineTemplateEvaluation(accept, {
@@ -5838,7 +5841,7 @@ function FilePicker(props) {
5838
5841
  children: [jsxRuntime.jsx(Label, {
5839
5842
  htmlFor: domId,
5840
5843
  label: label,
5841
- required: required
5844
+ required: validate.required
5842
5845
  }), jsxRuntime.jsx("input", {
5843
5846
  type: "file",
5844
5847
  className: "fjs-hidden",
@@ -5848,13 +5851,14 @@ function FilePicker(props) {
5848
5851
  disabled: isInputDisabled,
5849
5852
  multiple: evaluatedMultiple || undefined,
5850
5853
  accept: evaluatedAccept || undefined,
5851
- onChange: onFileChange
5854
+ onChange: onFileChange,
5855
+ required: validate.required
5852
5856
  }), jsxRuntime.jsxs("div", {
5853
5857
  className: "fjs-filepicker-container",
5854
5858
  children: [jsxRuntime.jsx("button", {
5855
5859
  type: "button",
5856
5860
  disabled: isInputDisabled,
5857
- readonly: readonly,
5861
+ readOnly: readonly,
5858
5862
  className: "fjs-button fjs-filepicker-button",
5859
5863
  onClick: () => {
5860
5864
  fileInputRef.current.click();
@@ -5920,6 +5924,7 @@ const type = 'documentPreview';
5920
5924
  /**
5921
5925
  * @typedef DocumentMetadata
5922
5926
  * @property {string} documentId
5927
+ * @property {string} contentHash
5923
5928
  * @property {Object} metadata
5924
5929
  * @property {string|undefined} [metadata.contentType]
5925
5930
  * @property {string} metadata.fileName
@@ -6041,7 +6046,7 @@ function isValidDocumentEndpoint(endpoint) {
6041
6046
  * @returns {metadata is DocumentMetadata}
6042
6047
  */
6043
6048
  function isValidDocument(document) {
6044
- return typeof document === 'object' && 'documentId' in document && 'metadata' in document && typeof document.metadata === 'object' && 'fileName' in document.metadata;
6049
+ return typeof document === 'object' && document !== null && 'documentId' in document && 'metadata' in document && typeof document.metadata === 'object' && 'fileName' in document.metadata;
6045
6050
  }
6046
6051
 
6047
6052
  /**
@@ -6079,7 +6084,11 @@ function DocumentRenderer(props) {
6079
6084
  const [hasError, setHasError] = hooks.useState(false);
6080
6085
  const ref = hooks.useRef(null);
6081
6086
  const isInViewport = useInViewport(ref);
6082
- const fullUrl = endpoint.replace(DOCUMENT_ID_PLACEHOLDER, documentMetadata.documentId);
6087
+ const fullUrl = buildUrl({
6088
+ baseUrl: endpoint,
6089
+ documentId: documentMetadata.documentId,
6090
+ contentHash: documentMetadata.contentHash
6091
+ });
6083
6092
  const singleDocumentContainerClassName = `fjs-${type}-single-document-container`;
6084
6093
  const errorMessageId = `${domId}-error-message`;
6085
6094
  const errorMessage = 'Unable to download document';
@@ -6189,12 +6198,16 @@ function DownloadButton(props) {
6189
6198
 
6190
6199
  /**
6191
6200
  *
6192
- * @param {import("preact").RefObject<HTMLElement>} ref
6201
+ * @param {import("preact").RefObject<HTMLElement|null>} ref
6193
6202
  * @returns boolean
6194
6203
  */
6195
6204
  function useInViewport(ref) {
6196
6205
  const [isInViewport, setIsInViewport] = hooks.useState(false);
6197
6206
  hooks.useEffect(() => {
6207
+ const container = ref.current;
6208
+ if (!container) {
6209
+ return;
6210
+ }
6198
6211
  const observer = new IntersectionObserver(([entry]) => {
6199
6212
  if (entry.isIntersecting) {
6200
6213
  setIsInViewport(true);
@@ -6202,18 +6215,39 @@ function useInViewport(ref) {
6202
6215
  }, {
6203
6216
  threshold: 0
6204
6217
  });
6205
- if (ref.current) {
6206
- observer.observe(ref.current);
6207
- }
6218
+ observer.observe(container);
6208
6219
  return () => {
6209
- if (ref.current) {
6210
- observer.unobserve(ref.current);
6220
+ if (container) {
6221
+ observer.unobserve(container);
6211
6222
  }
6212
6223
  };
6213
6224
  }, [ref]);
6214
6225
  return isInViewport;
6215
6226
  }
6216
6227
 
6228
+ /**
6229
+ * This solution should be a temporary fix, we should try to remove it via: https://github.com/bpmn-io/form-js/issues/1341
6230
+ *
6231
+ * @param {Object} options
6232
+ * @param {string} options.baseUrl
6233
+ * @param {string} options.documentId
6234
+ * @param {string} [options.contentHash]
6235
+ *
6236
+ * @returns {string}
6237
+ */
6238
+ function buildUrl(options) {
6239
+ const {
6240
+ baseUrl,
6241
+ documentId,
6242
+ contentHash
6243
+ } = options;
6244
+ const finalUrl = new URL(baseUrl.replace(DOCUMENT_ID_PLACEHOLDER, documentId));
6245
+ if (contentHash !== undefined) {
6246
+ finalUrl.searchParams.set('contentHash', contentHash);
6247
+ }
6248
+ return decodeURI(finalUrl.toString());
6249
+ }
6250
+
6217
6251
  /**
6218
6252
  * This file must not be changed or exchanged.
6219
6253
  *
@@ -6255,14 +6289,14 @@ function Lightbox(props) {
6255
6289
  children: [jsxRuntime.jsx("a", {
6256
6290
  href: "https://bpmn.io",
6257
6291
  target: "_blank",
6258
- rel: "noopener",
6292
+ rel: "noopener noreferrer",
6259
6293
  style: "margin: 15px 20px 15px 10px; align-self: center; color: var(--cds-icon-primary, #404040)",
6260
6294
  children: jsxRuntime.jsx(Logo, {})
6261
6295
  }), jsxRuntime.jsxs("span", {
6262
6296
  children: ["Web-based tooling for BPMN, DMN, and forms powered by", ' ', jsxRuntime.jsx("a", {
6263
6297
  href: "https://bpmn.io",
6264
6298
  target: "_blank",
6265
- rel: "noopener",
6299
+ rel: "noopener noreferrer",
6266
6300
  children: "bpmn.io"
6267
6301
  }), "."]
6268
6302
  })]
@@ -6276,7 +6310,7 @@ function Link(props) {
6276
6310
  children: jsxRuntime.jsx("a", {
6277
6311
  href: "https://bpmn.io",
6278
6312
  target: "_blank",
6279
- rel: "noopener",
6313
+ rel: "noopener noreferrer",
6280
6314
  class: "fjs-powered-by-link",
6281
6315
  title: "Powered by bpmn.io",
6282
6316
  style: "color: var(--cds-text-primary, #404040)",
@@ -7481,7 +7515,7 @@ class RepeatRenderManager {
7481
7515
  showRemove: showRemove,
7482
7516
  ...restProps
7483
7517
  })
7484
- }))
7518
+ }, itemIndex))
7485
7519
  });
7486
7520
  }
7487
7521
  RepeatFooter(props) {