@bpmn-io/form-js-viewer 1.14.0 → 1.15.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.es.js CHANGED
@@ -907,7 +907,6 @@ function useDeepCompareMemoize(value) {
907
907
  * @enum { String }
908
908
  */
909
909
  const LOAD_STATES = {
910
- LOADING: 'loading',
911
910
  LOADED: 'loaded',
912
911
  ERROR: 'error'
913
912
  };
@@ -1118,6 +1117,9 @@ function _isElementScrollable(el) {
1118
1117
  return (overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight;
1119
1118
  }
1120
1119
 
1120
+ const EMPTY_OBJECT = {};
1121
+ const EMPTY_ARRAY$2 = [];
1122
+
1121
1123
  /**
1122
1124
  * Custom hook to scroll an element within a scrollable container.
1123
1125
  *
@@ -1131,8 +1133,8 @@ function _isElementScrollable(el) {
1131
1133
  * @param {Array} [flagRefs] - An array of refs that are used as flags to control when to scroll.
1132
1134
  */
1133
1135
  function useScrollIntoView(scrolledElementRef, deps, scrollOptions, flagRefs) {
1134
- const _scrollOptions = scrollOptions;
1135
- const _flagRefs = flagRefs;
1136
+ const _scrollOptions = scrollOptions || EMPTY_OBJECT;
1137
+ const _flagRefs = flagRefs || EMPTY_ARRAY$2;
1136
1138
  useEffect(() => {
1137
1139
  // return early if flags are not raised, or component is not mounted
1138
1140
  if (some(_flagRefs, ref => !ref.current) || !scrolledElementRef.current) {
@@ -5901,10 +5903,15 @@ var SvgDownload = function SvgDownload(props) {
5901
5903
 
5902
5904
  const type = 'documentPreview';
5903
5905
 
5906
+ /**
5907
+ * @typedef DocumentEndpointBuilder
5908
+ * @property {(document: DocumentMetadata) => string} buildUrl
5909
+ */
5910
+
5904
5911
  /**
5905
5912
  * @typedef DocumentMetadata
5906
5913
  * @property {string} documentId
5907
- * @property {string} contentHash
5914
+ * @property {string} endpoint
5908
5915
  * @property {Object} metadata
5909
5916
  * @property {string|undefined} [metadata.contentType]
5910
5917
  * @property {string} metadata.fileName
@@ -5913,7 +5920,6 @@ const type = 'documentPreview';
5913
5920
  * @property {string} id
5914
5921
  * @property {string} [title]
5915
5922
  * @property {string} [dataSource]
5916
- * @property {string} [endpointKey]
5917
5923
  * @property {number} [maxHeight]
5918
5924
  * @property {string} [label]
5919
5925
  *
@@ -5925,18 +5931,18 @@ const type = 'documentPreview';
5925
5931
  * @returns {import("preact").JSX.Element}
5926
5932
  */
5927
5933
  function DocumentPreview(props) {
5934
+ /** @type {DocumentEndpointBuilder | null} */
5935
+ const documentEndpointBuilder = useService('documentEndpointBuilder', false);
5928
5936
  const {
5929
5937
  field,
5930
5938
  domId
5931
5939
  } = props;
5932
5940
  const {
5933
5941
  dataSource,
5934
- endpointKey,
5935
5942
  maxHeight,
5936
5943
  label
5937
5944
  } = field;
5938
5945
  const errorMessageId = `${domId}-error-message`;
5939
- const endpoint = useExpressionEvaluation(endpointKey || '');
5940
5946
  const data = useValidDocumentData(dataSource || '');
5941
5947
  const evaluatedLabel = useSingleLineTemplateEvaluation(label, {
5942
5948
  debug: true
@@ -5949,18 +5955,19 @@ function DocumentPreview(props) {
5949
5955
  }), jsx("div", {
5950
5956
  class: `fjs-${type}-document-container`,
5951
5957
  id: domId,
5952
- children: isValidDocumentEndpoint(endpoint) ? data.map((document, index) => jsx(DocumentRenderer, {
5953
- documentMetadata: document,
5954
- endpoint: endpoint,
5955
- maxHeight: maxHeight,
5956
- domId: `${domId}-${index}`
5957
- }, document.documentId)) : null
5958
+ children: data.map((document, index) => {
5959
+ const finalEndpoint = tryCatch(() => documentEndpointBuilder?.buildUrl(document)) ?? document.endpoint;
5960
+ return isValidDocumentEndpoint(finalEndpoint) ? jsx(DocumentRenderer, {
5961
+ documentMetadata: document,
5962
+ endpoint: finalEndpoint,
5963
+ maxHeight: maxHeight,
5964
+ domId: `${domId}-${index}`
5965
+ }, document.documentId) : null;
5966
+ })
5958
5967
  }), jsx(Errors, {
5959
5968
  id: errorMessageId,
5960
5969
  errors: getErrors({
5961
- dataSource,
5962
- endpoint,
5963
- endpointKey
5970
+ dataSource
5964
5971
  })
5965
5972
  })]
5966
5973
  });
@@ -5972,43 +5979,27 @@ DocumentPreview.config = {
5972
5979
  name: 'Document preview',
5973
5980
  create: (options = {}) => ({
5974
5981
  label: 'Document preview',
5975
- endpointKey: DEFAULT_ENDPOINT_KEY,
5976
5982
  ...options
5977
5983
  })
5978
5984
  };
5979
5985
 
5980
5986
  // helpers /////////////////////////////
5981
5987
 
5982
- const DOCUMENT_ID_PLACEHOLDER = '{documentId}';
5983
- const DEFAULT_ENDPOINT_KEY = '=defaultDocumentsEndpointKey';
5984
-
5985
5988
  /**
5986
5989
  * @typedef GetErrorOptions
5987
5990
  * @property {string|undefined} dataSource
5988
- * @property {string|undefined} endpointKey
5989
- * @property {string|null} endpoint
5990
5991
  *
5991
5992
  * @param {GetErrorOptions} options
5992
5993
  * @returns {string[]}
5993
5994
  */
5994
5995
  function getErrors(options) {
5995
5996
  const {
5996
- dataSource,
5997
- endpointKey,
5998
- endpoint
5997
+ dataSource
5999
5998
  } = options;
6000
5999
  let errors = [];
6001
6000
  if (!isString(dataSource) || dataSource.length < 1) {
6002
6001
  errors.push('Document reference is not defined.');
6003
6002
  }
6004
- if (!isString(endpointKey) || endpointKey.length < 1) {
6005
- errors.push('Endpoint key is not defined.');
6006
- }
6007
- if (endpointKey !== DEFAULT_ENDPOINT_KEY && !URL.canParse(endpoint)) {
6008
- errors.push(`If you change the endpoint key from "${DEFAULT_ENDPOINT_KEY}", the document preview won't work with Camunda Tasklist and you must provide a valid URL.`);
6009
- } else if (endpointKey !== DEFAULT_ENDPOINT_KEY && !isValidDocumentEndpoint(endpoint)) {
6010
- errors.push('Endpoint must contain "{documentId}".');
6011
- }
6012
6003
  return errors;
6013
6004
  }
6014
6005
 
@@ -6018,7 +6009,7 @@ function getErrors(options) {
6018
6009
  * @returns boolean
6019
6010
  */
6020
6011
  function isValidDocumentEndpoint(endpoint) {
6021
- return typeof endpoint === 'string' && URL.canParse(endpoint) && endpoint.includes(DOCUMENT_ID_PLACEHOLDER);
6012
+ return typeof endpoint === 'string' && URL.canParse(endpoint);
6022
6013
  }
6023
6014
 
6024
6015
  /**
@@ -6041,6 +6032,61 @@ function useValidDocumentData(dataSource) {
6041
6032
  return data.filter(isValidDocument);
6042
6033
  }
6043
6034
 
6035
+ /**
6036
+ * @param {Object} props
6037
+ * @param {string} props.url
6038
+ * @param {string} props.fileName
6039
+ * @param {Function} props.onError
6040
+ * @param {string} props.errorMessageId
6041
+ * @returns {import("preact").JSX.Element}
6042
+ */
6043
+ function PdfRenderer(props) {
6044
+ const {
6045
+ url,
6046
+ onError,
6047
+ errorMessageId
6048
+ } = props;
6049
+ /** @type {ReturnType<typeof import("preact/hooks").useState<null | string>>} */
6050
+ const [pdfObjectUrl, setPdfObjectUrl] = useState(null);
6051
+ const [hasError, setHasError] = useState(false);
6052
+ useEffect(() => {
6053
+ /** @type {null | string} */
6054
+ let objectUrl = null;
6055
+ const fetchPdf = async () => {
6056
+ try {
6057
+ const response = await fetch(url);
6058
+ if (!response.ok) {
6059
+ setHasError(true);
6060
+ onError();
6061
+ return;
6062
+ }
6063
+ const blob = await response.blob();
6064
+ objectUrl = URL.createObjectURL(blob);
6065
+ setPdfObjectUrl(objectUrl);
6066
+ } catch {
6067
+ setHasError(true);
6068
+ onError();
6069
+ }
6070
+ };
6071
+ fetchPdf();
6072
+ return () => {
6073
+ if (objectUrl) {
6074
+ URL.revokeObjectURL(objectUrl);
6075
+ }
6076
+ };
6077
+ }, [url, onError]);
6078
+ return jsxs(Fragment, {
6079
+ children: [pdfObjectUrl !== null ? jsx("embed", {
6080
+ src: pdfObjectUrl,
6081
+ type: "application/pdf",
6082
+ class: `fjs-${type}-pdf-viewer`
6083
+ }) : null, hasError ? jsx(Errors, {
6084
+ id: errorMessageId,
6085
+ errors: ['Unable to download document']
6086
+ }) : null]
6087
+ });
6088
+ }
6089
+
6044
6090
  /**
6045
6091
  *
6046
6092
  * @param {Object} props
@@ -6064,11 +6110,6 @@ function DocumentRenderer(props) {
6064
6110
  const [hasError, setHasError] = useState(false);
6065
6111
  const ref = useRef(null);
6066
6112
  const isInViewport = useInViewport(ref);
6067
- const fullUrl = buildUrl({
6068
- baseUrl: endpoint,
6069
- documentId: documentMetadata.documentId,
6070
- contentHash: documentMetadata.contentHash
6071
- });
6072
6113
  const singleDocumentContainerClassName = `fjs-${type}-single-document-container`;
6073
6114
  const errorMessageId = `${domId}-error-message`;
6074
6115
  const errorMessage = 'Unable to download document';
@@ -6081,11 +6122,11 @@ function DocumentRenderer(props) {
6081
6122
  },
6082
6123
  "aria-describedby": hasError ? errorMessageId : undefined,
6083
6124
  children: [jsx("img", {
6084
- src: fullUrl,
6125
+ src: endpoint,
6085
6126
  alt: metadata.fileName,
6086
6127
  class: `fjs-${type}-image`
6087
6128
  }), jsx(DownloadButton, {
6088
- endpoint: fullUrl,
6129
+ endpoint: endpoint,
6089
6130
  fileName: metadata.fileName,
6090
6131
  onDownloadError: () => {
6091
6132
  setHasError(true);
@@ -6097,20 +6138,18 @@ function DocumentRenderer(props) {
6097
6138
  });
6098
6139
  }
6099
6140
  if (isContentTypePresent && metadata.contentType.toLowerCase() === 'application/pdf' && isInViewport) {
6100
- return jsxs("div", {
6141
+ return jsx("div", {
6101
6142
  class: singleDocumentContainerClassName,
6102
6143
  style: {
6103
6144
  maxHeight
6104
6145
  },
6105
6146
  "aria-describedby": hasError ? errorMessageId : undefined,
6106
- children: [jsx("embed", {
6107
- src: fullUrl,
6108
- type: "application/pdf",
6109
- class: `fjs-${type}-pdf-viewer`
6110
- }), hasError ? jsx(Errors, {
6111
- id: errorMessageId,
6112
- errors: [errorMessage]
6113
- }) : null]
6147
+ children: jsx(PdfRenderer, {
6148
+ url: endpoint,
6149
+ fileName: metadata.fileName,
6150
+ onError: () => setHasError(true),
6151
+ errorMessageId: errorMessageId
6152
+ })
6114
6153
  });
6115
6154
  }
6116
6155
  return jsxs("div", {
@@ -6126,7 +6165,7 @@ function DocumentRenderer(props) {
6126
6165
  errors: [errorMessage]
6127
6166
  }) : null]
6128
6167
  }), jsx(DownloadButton, {
6129
- endpoint: fullUrl,
6168
+ endpoint: endpoint,
6130
6169
  fileName: metadata.fileName,
6131
6170
  onDownloadError: () => {
6132
6171
  setHasError(true);
@@ -6206,27 +6245,18 @@ function useInViewport(ref) {
6206
6245
  }
6207
6246
 
6208
6247
  /**
6209
- * This solution should be a temporary fix, we should try to remove it via: https://github.com/bpmn-io/form-js/issues/1341
6210
- *
6211
- * @param {Object} options
6212
- * @param {string} options.baseUrl
6213
- * @param {string} options.documentId
6214
- * @param {string} [options.contentHash]
6215
- *
6216
- * @returns {string}
6248
+ * @template T
6249
+ * @param {() => T} fn - Function to execute
6250
+ * @returns {T | null}
6217
6251
  */
6218
- function buildUrl(options) {
6219
- const {
6220
- baseUrl,
6221
- documentId,
6222
- contentHash
6223
- } = options;
6224
- const finalUrl = new URL(baseUrl.replace(DOCUMENT_ID_PLACEHOLDER, documentId));
6225
- if (contentHash !== undefined) {
6226
- finalUrl.searchParams.set('contentHash', contentHash);
6252
+ const tryCatch = fn => {
6253
+ try {
6254
+ return fn();
6255
+ } catch (error) {
6256
+ console.error(error);
6257
+ return null;
6227
6258
  }
6228
- return decodeURI(finalUrl.toString());
6229
- }
6259
+ };
6230
6260
 
6231
6261
  /**
6232
6262
  * This file must not be changed or exchanged.
@@ -6385,7 +6415,7 @@ class FormFields {
6385
6415
  }
6386
6416
  }
6387
6417
 
6388
- const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression', 'url', 'dataSource', 'columnsExpression', 'expression', 'multiple', 'accept', 'endpointKey', 'title'];
6418
+ const EXPRESSION_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'conditional.hide', 'description', 'label', 'source', 'readonly', 'text', 'validate.min', 'validate.max', 'validate.minLength', 'validate.maxLength', 'valuesExpression', 'url', 'dataSource', 'columnsExpression', 'expression', 'multiple', 'accept', 'title'];
6389
6419
  const TEMPLATE_PROPERTIES = ['alt', 'appearance.prefixAdorner', 'appearance.suffixAdorner', 'description', 'label', 'source', 'text', 'content', 'url', 'title'];
6390
6420
 
6391
6421
  /**