@evergis/react 4.0.54 → 4.0.55

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/react.esm.js CHANGED
@@ -8040,10 +8040,31 @@ const useGlobalContext = () => {
8040
8040
  }), [language, translate, api, ewktGeometry, themeName]);
8041
8041
  };
8042
8042
 
8043
+ const ImagePreviewError = styled.div `
8044
+ display: flex;
8045
+ align-items: center;
8046
+ justify-content: center;
8047
+ width: 100%;
8048
+ height: 100%;
8049
+ background-color: ${({ theme }) => theme.palette.elementDark};
8050
+ border-radius: ${({ theme: { borderRadius: themeBorder }, borderRadius }) => borderRadius || themeBorder.smallest};
8051
+
8052
+ ${Icon} {
8053
+ width: 37.5%;
8054
+ height: 37.5%;
8055
+ }
8056
+
8057
+ ${Icon}:after {
8058
+ font-size: 1.5rem;
8059
+ color: ${({ theme }) => theme.palette.textSecondary};
8060
+ }
8061
+ `;
8043
8062
  const FileImagePreview = ({ link, isExternal, size, borderRadius, }) => {
8044
8063
  const { api } = useGlobalContext();
8045
8064
  const [imageSrc, setImageSrc] = useState();
8065
+ const [hasError, setHasError] = useState(false);
8046
8066
  useEffect(() => {
8067
+ setHasError(false);
8047
8068
  if (isExternal) {
8048
8069
  setImageSrc(link);
8049
8070
  return;
@@ -8061,14 +8082,18 @@ const FileImagePreview = ({ link, isExternal, size, borderRadius, }) => {
8061
8082
  objectUrl = URL.createObjectURL(blob);
8062
8083
  setImageSrc(objectUrl);
8063
8084
  })
8064
- .catch(() => { });
8085
+ .catch(() => {
8086
+ if (cancelled)
8087
+ return;
8088
+ setHasError(true);
8089
+ });
8065
8090
  return () => {
8066
8091
  cancelled = true;
8067
8092
  if (objectUrl)
8068
8093
  URL.revokeObjectURL(objectUrl);
8069
8094
  };
8070
8095
  }, [api, link, isExternal]);
8071
- return (jsxs(ImagePreviewContainer, { size: size, children: [!imageSrc && (jsx(ImagePreviewLoaderContainer, { children: jsx(LinearProgress, {}) })), imageSrc && (jsx(GridImagePreview, { borderRadius: borderRadius, size: size, src: imageSrc, alt: "" }))] }));
8096
+ return (jsxs(ImagePreviewContainer, { size: size, children: [hasError && (jsx(ImagePreviewError, { borderRadius: borderRadius, children: jsx(Icon, { kind: "alert" }) })), !hasError && !imageSrc && (jsx(ImagePreviewLoaderContainer, { children: jsx(LinearProgress, {}) })), !hasError && imageSrc && (jsx(GridImagePreview, { borderRadius: borderRadius, size: size, src: imageSrc, alt: "", onError: () => setHasError(true) }))] }));
8072
8097
  };
8073
8098
 
8074
8099
  const getFileType = (mimeType = "", name = "") => {
@@ -8221,6 +8246,60 @@ const useAttachmentContainer = ({ type, elementConfig, valueOverride, }) => {
8221
8246
  };
8222
8247
  };
8223
8248
 
8249
+ const usePreviewImages = ({ items, active }) => {
8250
+ const { api } = useGlobalContext();
8251
+ const [blobUrls, setBlobUrls] = useState({});
8252
+ const [failedLinks, setFailedLinks] = useState({});
8253
+ const inFlightRef = useRef(new Set());
8254
+ const blobUrlsRef = useRef(blobUrls);
8255
+ blobUrlsRef.current = blobUrls;
8256
+ useEffect(() => {
8257
+ if (!active || !api?.catalog?.getFile)
8258
+ return;
8259
+ items.forEach(item => {
8260
+ const fileType = getFileType(item.mimeType, item.name);
8261
+ const isImage = IMAGE_FILE_TYPES.includes(fileType);
8262
+ if (!isImage || item.isExternal)
8263
+ return;
8264
+ if (blobUrlsRef.current[item.link] || inFlightRef.current.has(item.link))
8265
+ return;
8266
+ inFlightRef.current.add(item.link);
8267
+ api.catalog
8268
+ .getFile(item.link)
8269
+ .then(blob => {
8270
+ const objectUrl = URL.createObjectURL(blob);
8271
+ setBlobUrls(prev => ({ ...prev, [item.link]: objectUrl }));
8272
+ })
8273
+ .catch(() => {
8274
+ setFailedLinks(prev => (prev[item.link] ? prev : { ...prev, [item.link]: true }));
8275
+ })
8276
+ .finally(() => {
8277
+ inFlightRef.current.delete(item.link);
8278
+ });
8279
+ });
8280
+ }, [active, api, items]);
8281
+ useEffect(() => () => {
8282
+ Object.values(blobUrlsRef.current).forEach(URL.revokeObjectURL);
8283
+ }, []);
8284
+ return useMemo(() => items.map(item => {
8285
+ const fileType = getFileType(item.mimeType, item.name);
8286
+ const isImage = IMAGE_FILE_TYPES.includes(fileType);
8287
+ const fileName = item.name;
8288
+ if (!isImage)
8289
+ return { src: getFileTypeIcon(fileType), fileName };
8290
+ if (item.isExternal)
8291
+ return { src: item.link, fileName };
8292
+ const hasError = !!failedLinks[item.link];
8293
+ const blobUrl = blobUrls[item.link];
8294
+ return {
8295
+ src: blobUrl ?? "",
8296
+ fileName,
8297
+ hasError,
8298
+ isLoading: !hasError && !blobUrl,
8299
+ };
8300
+ }), [items, blobUrls, failedLinks]);
8301
+ };
8302
+
8224
8303
  const EXTENSION_TO_MIME = {
8225
8304
  apng: "image/apng",
8226
8305
  avif: "image/avif",
@@ -8267,20 +8346,6 @@ const getFileNameFromUrl = (url) => {
8267
8346
  }
8268
8347
  };
8269
8348
 
8270
- const getResourceUrl = (url) => {
8271
- return url ? (url.startsWith("http") ? url : `/sp/resources/file/${url}`) : "";
8272
- };
8273
-
8274
- const buildPreviewImage$1 = (item) => {
8275
- const fileType = getFileType(item.mimeType, item.name);
8276
- const isImage = IMAGE_FILE_TYPES.includes(fileType);
8277
- const src = isImage
8278
- ? item.isExternal
8279
- ? item.link
8280
- : getResourceUrl(item.link)
8281
- : getFileTypeIcon(fileType);
8282
- return { src, fileName: item.name };
8283
- };
8284
8349
  const EditAttachmentContainer = memo(({ type, elementConfig, renderElement }) => {
8285
8350
  const { api } = useGlobalContext();
8286
8351
  const { selectAttachmentsFromCatalog } = useWidgetContext(type);
@@ -8291,7 +8356,7 @@ const EditAttachmentContainer = memo(({ type, elementConfig, renderElement }) =>
8291
8356
  const [previewIndex, setPreviewIndex] = useState(null);
8292
8357
  const [uploading, setUploading] = useState(false);
8293
8358
  const [isLinkDialogOpen, , setLinkDialogOpen] = useToggle(false);
8294
- const previewImages = useMemo(() => items.map(buildPreviewImage$1), [items]);
8359
+ const previewImages = usePreviewImages({ items, active: previewIndex !== null });
8295
8360
  const orderedPreviewImages = useMemo(() => {
8296
8361
  if (previewIndex === null)
8297
8362
  return previewImages;
@@ -8300,7 +8365,7 @@ const EditAttachmentContainer = memo(({ type, elementConfig, renderElement }) =>
8300
8365
  ...previewImages.filter((_, idx) => idx !== previewIndex),
8301
8366
  ];
8302
8367
  }, [previewImages, previewIndex]);
8303
- const persist = useCallback((next) => onChange(JSON.stringify(next)), [onChange]);
8368
+ const persist = useCallback((next) => onChange(next), [onChange]);
8304
8369
  const handlePreview = useCallback((link) => {
8305
8370
  const idx = items.findIndex(item => item.link === link);
8306
8371
  if (idx >= 0)
@@ -8360,23 +8425,14 @@ const EditAttachmentContainer = memo(({ type, elementConfig, renderElement }) =>
8360
8425
  return (jsxs(AttachmentsContainer, { id: id, style: { ...BASE_CONTAINER_STYLE, ...style }, children: [jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsx(AttachmentsGrid, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) : (jsx(AttachmentsList, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) }), hasMore && !showMore && (jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), jsx(AddButton, { accept: fileExtensions, onSelectFiles: uploading ? () => undefined : handleUpload, onSelectFromCatalog: handleSelectFromCatalog, onSelectFromLink: handleOpenLinkDialog }), jsx(AttachmentLinkDialog, { isOpen: isLinkDialogOpen, onClose: handleCloseLinkDialog, onSubmit: handleAddByLink }), previewIndex !== null && (jsx(Preview, { images: orderedPreviewImages, isOpen: previewIndex !== null, onClose: handleClosePreview }))] }));
8361
8426
  });
8362
8427
 
8363
- const buildPreviewImage = (item) => {
8364
- const fileType = getFileType(item.mimeType, item.name);
8365
- const isImage = IMAGE_FILE_TYPES.includes(fileType);
8366
- const src = isImage
8367
- ? item.isExternal
8368
- ? item.link
8369
- : getResourceUrl(item.link)
8370
- : getFileTypeIcon(fileType);
8371
- return { src, fileName: item.name };
8372
- };
8373
8428
  const AttachmentContainer = memo(({ type, elementConfig, renderElement }) => {
8429
+ const { t } = useGlobalContext();
8374
8430
  const { expandedContainers } = useWidgetContext(type);
8375
8431
  const { items, visibleItems, viewMode, setViewMode, showMore, setShowMore, hasMore, hiddenCount } = useAttachmentContainer({ type, elementConfig });
8376
8432
  const { id, style, options } = elementConfig || {};
8377
8433
  const { expandable, expanded } = options || {};
8378
8434
  const [previewIndex, setPreviewIndex] = useState(null);
8379
- const previewImages = useMemo(() => items.map(buildPreviewImage), [items]);
8435
+ const previewImages = usePreviewImages({ items, active: previewIndex !== null });
8380
8436
  const orderedPreviewImages = useMemo(() => {
8381
8437
  if (previewIndex === null)
8382
8438
  return previewImages;
@@ -8393,7 +8449,10 @@ const AttachmentContainer = memo(({ type, elementConfig, renderElement }) => {
8393
8449
  const handleClosePreview = useCallback(() => setPreviewIndex(null), []);
8394
8450
  const handleShowMore = useCallback(() => setShowMore(true), [setShowMore]);
8395
8451
  const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
8396
- return (jsxs(Fragment$1, { children: [jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { id: id, isColumn: true, style: style, children: [jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsx(AttachmentsGrid, { items: visibleItems, isEdit: false, onPreview: handlePreview })) : (jsx(AttachmentsList, { items: visibleItems, isEdit: false, onPreview: handlePreview })) }), hasMore && !showMore && (jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), previewIndex !== null && (jsx(Preview, { images: orderedPreviewImages, isOpen: previewIndex !== null, onClose: handleClosePreview }))] }))] }));
8452
+ return (jsxs(Fragment$1, { children: [jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { id: id, isColumn: true, style: style, children: [jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsx(AttachmentsGrid, { items: visibleItems, isEdit: false, onPreview: handlePreview })) : (jsx(AttachmentsList, { items: visibleItems, isEdit: false, onPreview: handlePreview })) }), hasMore && !showMore && (jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), previewIndex !== null && (jsx(Preview, { images: orderedPreviewImages, isOpen: previewIndex !== null, onClose: handleClosePreview, errorTitleText: t("attachments.resourceUnavailable", {
8453
+ ns: "common",
8454
+ defaultValue: "Ресурс недоступен",
8455
+ }) }))] }))] }));
8397
8456
  });
8398
8457
 
8399
8458
  const ContainerDivider = styled(Divider) `
@@ -11172,6 +11231,10 @@ const getRelatedAttribute = (layerInfo, sourceAttributeName, relatedLayerName) =
11172
11231
  return attributeConfig?.options?.relatedAttributes?.find(({ layerName }) => layerName === relatedLayerName);
11173
11232
  };
11174
11233
 
11234
+ const getResourceUrl = (url) => {
11235
+ return url ? (url.startsWith("http") ? url : `/sp/resources/file/${url}`) : "";
11236
+ };
11237
+
11175
11238
  const getSlideshowImages = ({ element, attribute, }) => {
11176
11239
  const { defaultValue, options } = element;
11177
11240
  const { separator } = options || {};