@evergis/react 4.0.136 → 4.0.137

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.
@@ -14,6 +14,7 @@ export declare const SHP_MIME_TYPE = "application/octet-stream";
14
14
  export declare const KML_MIME_TYPE = "application/octet-stream";
15
15
  export declare const ZIP_MIME_TYPE = "application/zip";
16
16
  export declare const PYTHON_MIME_TYPES: string[];
17
+ export declare const DOWNLOAD_ERROR_DURATION = 5000;
17
18
  export declare enum AddAttachmentSource {
18
19
  Pc = "pc",
19
20
  Catalog = "catalog",
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Отдаёт браузеру уже загруженный файл под именем `fileName`.
3
+ *
4
+ * Адрес отзывается не сразу: часть браузеров дочитывает поток уже после клика, и мгновенный
5
+ * `revokeObjectURL` обрывает сохранение.
6
+ */
7
+ export declare const saveBlobAsFile: (blob: Blob, fileName: string) => void;
@@ -1,3 +1,4 @@
1
+ export * from './useAttachmentDownload';
1
2
  export * from './useAttachmentItems';
2
3
  export * from './useAttachmentPreviewImages';
3
4
  export * from './useAutoCompleteControl';
@@ -0,0 +1,9 @@
1
+ import { Attachment } from '../containers/AttachmentContainer/types';
2
+ /**
3
+ * Скачивание вложения по требованию — файл запрашивается в момент клика, а не заранее.
4
+ *
5
+ * Свой файл лежит за авторизацией (`Authorization: Bearer`), поэтому ссылкой его не отдать:
6
+ * он тянется через api и сохраняется из памяти. Чужой открывается ссылкой — кросс-доменный
7
+ * `download` браузер всё равно игнорирует.
8
+ */
9
+ export declare const useAttachmentDownload: (items: Attachment[]) => ((index: number) => void);
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ var dateFns = require('date-fns');
12
12
  var locale = require('date-fns/locale');
13
13
  var wkt = require('wkt');
14
14
  var turf = require('@turf/turf');
15
+ var uuid = require('uuid');
15
16
  var signalr = require('@microsoft/signalr');
16
17
  var MapboxDraw = require('@mapbox/mapbox-gl-draw');
17
18
  var react = require('swiper/react');
@@ -21,7 +22,6 @@ var rehypeRaw = require('rehype-raw');
21
22
  var rehypeSanitize = require('rehype-sanitize');
22
23
  var remarkGfm = require('remark-gfm');
23
24
  var MapGL = require('react-map-gl/maplibre');
24
- var uuid = require('uuid');
25
25
  require('@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css');
26
26
  require('mapbox-gl/dist/mapbox-gl.css');
27
27
  var jspdf = require('jspdf');
@@ -5694,6 +5694,127 @@ const ServerNotificationsProvider = ({ url, initialized, apiClient, children })
5694
5694
  }, children: children }));
5695
5695
  };
5696
5696
 
5697
+ const useGlobalContext = () => {
5698
+ const { t, language, themeName, api, ewktGeometry, ewktExtent, zoomLevel, notification } = React.useContext(GlobalContext) || {};
5699
+ const translate = React.useCallback((value, options) => {
5700
+ if (t)
5701
+ return t(value, options);
5702
+ return options?.defaultValue ?? value;
5703
+ }, [t]);
5704
+ return React.useMemo(() => ({
5705
+ t: translate,
5706
+ language,
5707
+ themeName,
5708
+ api,
5709
+ ewktGeometry,
5710
+ ewktExtent,
5711
+ zoomLevel,
5712
+ notification,
5713
+ }), [language, translate, api, ewktGeometry, ewktExtent, zoomLevel, themeName, notification]);
5714
+ };
5715
+
5716
+ const GRID_TILE_SIZE = "4.5rem";
5717
+ const LIST_ICON_SIZE = "1.5rem";
5718
+ const JPG_MIME_TYPE = "image/jpeg";
5719
+ const PNG_MIME_TYPE = "image/png";
5720
+ const IMAGE_MIME_TYPES = [
5721
+ "image/apng",
5722
+ "image/avif",
5723
+ "image/gif",
5724
+ "image/jpeg",
5725
+ "image/png",
5726
+ "image/svg+xml",
5727
+ "image/webp",
5728
+ ];
5729
+ const XLSX_MIME_TYPES = [
5730
+ "application/vnd.ms-excel",
5731
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
5732
+ ];
5733
+ const PDF_MIME_TYPE = "application/pdf";
5734
+ const DOCX_MIME_TYPES = [
5735
+ "application/msword",
5736
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
5737
+ ];
5738
+ const CSV_MIME_TYPE = "text/csv";
5739
+ const JSON_MIME_TYPE = "application/json";
5740
+ const TXT_MIME_TYPE = "text/plain";
5741
+ const PPTX_MIME_TYPES = [
5742
+ "application/vnd.ms-powerpoint",
5743
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
5744
+ ];
5745
+ const SHP_MIME_TYPE = "application/octet-stream";
5746
+ const KML_MIME_TYPE = "application/octet-stream";
5747
+ const ZIP_MIME_TYPE = "application/zip";
5748
+ const PYTHON_MIME_TYPES = ["application/x-python-code", "text/x-python"];
5749
+ const DOWNLOAD_ERROR_DURATION = 5000;
5750
+ var AddAttachmentSource;
5751
+ (function (AddAttachmentSource) {
5752
+ AddAttachmentSource["Pc"] = "pc";
5753
+ AddAttachmentSource["Catalog"] = "catalog";
5754
+ AddAttachmentSource["Link"] = "link";
5755
+ })(AddAttachmentSource || (AddAttachmentSource = {}));
5756
+
5757
+ const REVOKE_DELAY = 1000;
5758
+ /**
5759
+ * Отдаёт браузеру уже загруженный файл под именем `fileName`.
5760
+ *
5761
+ * Адрес отзывается не сразу: часть браузеров дочитывает поток уже после клика, и мгновенный
5762
+ * `revokeObjectURL` обрывает сохранение.
5763
+ */
5764
+ const saveBlobAsFile = (blob, fileName) => {
5765
+ const url = URL.createObjectURL(blob);
5766
+ const link = document.createElement("a");
5767
+ link.href = url;
5768
+ link.download = fileName;
5769
+ link.style.display = "none";
5770
+ document.body.appendChild(link);
5771
+ link.click();
5772
+ document.body.removeChild(link);
5773
+ window.setTimeout(() => URL.revokeObjectURL(url), REVOKE_DELAY);
5774
+ };
5775
+
5776
+ /**
5777
+ * Скачивание вложения по требованию — файл запрашивается в момент клика, а не заранее.
5778
+ *
5779
+ * Свой файл лежит за авторизацией (`Authorization: Bearer`), поэтому ссылкой его не отдать:
5780
+ * он тянется через api и сохраняется из памяти. Чужой открывается ссылкой — кросс-доменный
5781
+ * `download` браузер всё равно игнорирует.
5782
+ */
5783
+ const useAttachmentDownload = (items) => {
5784
+ const { api, notification, t } = useGlobalContext();
5785
+ const inFlightRef = React.useRef(new Set());
5786
+ return React.useCallback((index) => {
5787
+ const item = items[index];
5788
+ if (!item)
5789
+ return;
5790
+ if (item.isExternal) {
5791
+ window.open(item.link, "_blank", "noopener,noreferrer");
5792
+ return;
5793
+ }
5794
+ if (!api?.catalog?.getFile || inFlightRef.current.has(item.link))
5795
+ return;
5796
+ inFlightRef.current.add(item.link);
5797
+ api.catalog
5798
+ .getFile(item.link)
5799
+ .then(blob => saveBlobAsFile(blob, item.name))
5800
+ .catch(error => {
5801
+ notification?.add({
5802
+ id: uuid.v4(),
5803
+ title: t("attachments.downloadError", {
5804
+ ns: "common",
5805
+ defaultValue: "Не удалось скачать вложение",
5806
+ }),
5807
+ description: error instanceof Error ? error.message : item.name,
5808
+ error: true,
5809
+ duration: DOWNLOAD_ERROR_DURATION,
5810
+ });
5811
+ })
5812
+ .finally(() => {
5813
+ inFlightRef.current.delete(item.link);
5814
+ });
5815
+ }, [items, api, notification, t]);
5816
+ };
5817
+
5697
5818
  /**
5698
5819
  * Контекст виджет-фрейма. Возвращаемый объект включает поля и {@link DashboardContext},
5699
5820
  * и {@link FeatureCardContext}, а гибридные (`config`, `isEditing`, `isLoading`, `pageIndex`,
@@ -5810,65 +5931,6 @@ const useAttachmentItems = ({ type, elementConfig, valueOverride, }) => {
5810
5931
  };
5811
5932
  };
5812
5933
 
5813
- const useGlobalContext = () => {
5814
- const { t, language, themeName, api, ewktGeometry, ewktExtent, zoomLevel, notification } = React.useContext(GlobalContext) || {};
5815
- const translate = React.useCallback((value, options) => {
5816
- if (t)
5817
- return t(value, options);
5818
- return options?.defaultValue ?? value;
5819
- }, [t]);
5820
- return React.useMemo(() => ({
5821
- t: translate,
5822
- language,
5823
- themeName,
5824
- api,
5825
- ewktGeometry,
5826
- ewktExtent,
5827
- zoomLevel,
5828
- notification,
5829
- }), [language, translate, api, ewktGeometry, ewktExtent, zoomLevel, themeName, notification]);
5830
- };
5831
-
5832
- const GRID_TILE_SIZE = "4.5rem";
5833
- const LIST_ICON_SIZE = "1.5rem";
5834
- const JPG_MIME_TYPE = "image/jpeg";
5835
- const PNG_MIME_TYPE = "image/png";
5836
- const IMAGE_MIME_TYPES = [
5837
- "image/apng",
5838
- "image/avif",
5839
- "image/gif",
5840
- "image/jpeg",
5841
- "image/png",
5842
- "image/svg+xml",
5843
- "image/webp",
5844
- ];
5845
- const XLSX_MIME_TYPES = [
5846
- "application/vnd.ms-excel",
5847
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
5848
- ];
5849
- const PDF_MIME_TYPE = "application/pdf";
5850
- const DOCX_MIME_TYPES = [
5851
- "application/msword",
5852
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
5853
- ];
5854
- const CSV_MIME_TYPE = "text/csv";
5855
- const JSON_MIME_TYPE = "application/json";
5856
- const TXT_MIME_TYPE = "text/plain";
5857
- const PPTX_MIME_TYPES = [
5858
- "application/vnd.ms-powerpoint",
5859
- "application/vnd.openxmlformats-officedocument.presentationml.presentation",
5860
- ];
5861
- const SHP_MIME_TYPE = "application/octet-stream";
5862
- const KML_MIME_TYPE = "application/octet-stream";
5863
- const ZIP_MIME_TYPE = "application/zip";
5864
- const PYTHON_MIME_TYPES = ["application/x-python-code", "text/x-python"];
5865
- var AddAttachmentSource;
5866
- (function (AddAttachmentSource) {
5867
- AddAttachmentSource["Pc"] = "pc";
5868
- AddAttachmentSource["Catalog"] = "catalog";
5869
- AddAttachmentSource["Link"] = "link";
5870
- })(AddAttachmentSource || (AddAttachmentSource = {}));
5871
-
5872
5934
  var FileType;
5873
5935
  (function (FileType) {
5874
5936
  FileType[FileType["UNKNOWN"] = 0] = "UNKNOWN";
@@ -9202,9 +9264,11 @@ const AttachmentContainer = React.memo(({ type, elementConfig, renderElement })
9202
9264
  setPreviewIndex(idx);
9203
9265
  }, [items]);
9204
9266
  const handleClosePreview = React.useCallback(() => setPreviewIndex(null), []);
9267
+ const downloadByIndex = useAttachmentDownload(items);
9268
+ const handleDownload = React.useCallback((_image, index) => downloadByIndex(index), [downloadByIndex]);
9205
9269
  const handleShowMore = React.useCallback(() => setShowMore(true), [setShowMore]);
9206
9270
  const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
9207
- return (jsxRuntime.jsxs(ContainerRoot, { ...root, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsx(Container, { ...body, children: jsxRuntime.jsxs(uilibGl.Flex, { column: true, children: [jsxRuntime.jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsxRuntime.jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsxRuntime.jsx(AttachmentsGrid, { items: visibleItems, isEdit: false, onPreview: handlePreview })) : (jsxRuntime.jsx(AttachmentsList, { items: visibleItems, isEdit: false, onPreview: handlePreview })) }), hasMore && !showMore && (jsxRuntime.jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), previewIndex !== null && (jsxRuntime.jsx(uilibGl.Preview, { images: previewImages, initialIndex: previewIndex, isOpen: previewIndex !== null, onClose: handleClosePreview, errorTitleText: t("attachments.resourceUnavailable", {
9271
+ return (jsxRuntime.jsxs(ContainerRoot, { ...root, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsx(Container, { ...body, children: jsxRuntime.jsxs(uilibGl.Flex, { column: true, children: [jsxRuntime.jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsxRuntime.jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsxRuntime.jsx(AttachmentsGrid, { items: visibleItems, isEdit: false, onPreview: handlePreview })) : (jsxRuntime.jsx(AttachmentsList, { items: visibleItems, isEdit: false, onPreview: handlePreview })) }), hasMore && !showMore && (jsxRuntime.jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), previewIndex !== null && (jsxRuntime.jsx(uilibGl.Preview, { images: previewImages, initialIndex: previewIndex, isOpen: previewIndex !== null, onClose: handleClosePreview, onDownload: handleDownload, errorTitleText: t("attachments.resourceUnavailable", {
9208
9272
  ns: "common",
9209
9273
  defaultValue: "Ресурс недоступен",
9210
9274
  }) }, previewIndex))] }) }))] }));
@@ -13249,6 +13313,8 @@ const EditAttachmentContainer = React.memo(({ type, elementConfig, renderElement
13249
13313
  setPreviewIndex(idx);
13250
13314
  }, [items]);
13251
13315
  const handleClosePreview = React.useCallback(() => setPreviewIndex(null), []);
13316
+ const downloadByIndex = useAttachmentDownload(items);
13317
+ const handleDownload = React.useCallback((_image, index) => downloadByIndex(index), [downloadByIndex]);
13252
13318
  const handleShowMore = React.useCallback(() => setShowMore(true), [setShowMore]);
13253
13319
  const handleDelete = React.useCallback((link) => {
13254
13320
  persist(items.filter(item => item.link !== link));
@@ -13299,7 +13365,7 @@ const EditAttachmentContainer = React.memo(({ type, elementConfig, renderElement
13299
13365
  setUploading(false);
13300
13366
  }
13301
13367
  }, [api, items, parentResourceId, persist]);
13302
- return (jsxRuntime.jsxs(AttachmentsContainer, { id: id, style: { ...BASE_CONTAINER_STYLE, ...style }, ...bgHost, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsxRuntime.jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsxRuntime.jsx(AttachmentsGrid, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) : (jsxRuntime.jsx(AttachmentsList, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) }), hasMore && !showMore && (jsxRuntime.jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), jsxRuntime.jsx(AddButton, { accept: fileExtensions, onSelectFiles: uploading ? () => undefined : handleUpload, onSelectFromCatalog: handleSelectFromCatalog, onSelectFromLink: handleOpenLinkDialog }), jsxRuntime.jsx(AttachmentLinkDialog, { isOpen: isLinkDialogOpen, onClose: handleCloseLinkDialog, onSubmit: handleAddByLink }), previewIndex !== null && (jsxRuntime.jsx(uilibGl.Preview, { images: previewImages, initialIndex: previewIndex, isOpen: previewIndex !== null, onClose: handleClosePreview }, previewIndex))] }));
13368
+ return (jsxRuntime.jsxs(AttachmentsContainer, { id: id, style: { ...BASE_CONTAINER_STYLE, ...style }, ...bgHost, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsxRuntime.jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsxRuntime.jsx(AttachmentsGrid, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) : (jsxRuntime.jsx(AttachmentsList, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) }), hasMore && !showMore && (jsxRuntime.jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), jsxRuntime.jsx(AddButton, { accept: fileExtensions, onSelectFiles: uploading ? () => undefined : handleUpload, onSelectFromCatalog: handleSelectFromCatalog, onSelectFromLink: handleOpenLinkDialog }), jsxRuntime.jsx(AttachmentLinkDialog, { isOpen: isLinkDialogOpen, onClose: handleCloseLinkDialog, onSubmit: handleAddByLink }), previewIndex !== null && (jsxRuntime.jsx(uilibGl.Preview, { images: previewImages, initialIndex: previewIndex, isOpen: previewIndex !== null, onClose: handleClosePreview, onDownload: handleDownload }, previewIndex))] }));
13303
13369
  });
13304
13370
 
13305
13371
  // `ProgressContainer` и `RoundedBackgroundContainer` исторически принимают `InnerContainerProps`
@@ -18854,6 +18920,7 @@ exports.transparentizeColor = transparentizeColor;
18854
18920
  exports.updateDataSource = updateDataSource;
18855
18921
  exports.useAfterSave = useAfterSave;
18856
18922
  exports.useAppHeight = useAppHeight;
18923
+ exports.useAttachmentDownload = useAttachmentDownload;
18857
18924
  exports.useAttachmentItems = useAttachmentItems;
18858
18925
  exports.useAttachmentPreviewImages = useAttachmentPreviewImages;
18859
18926
  exports.useAutoCompleteControl = useAutoCompleteControl;