@evergis/react 4.0.61 → 4.0.63

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
@@ -4215,7 +4215,7 @@ const useAttachmentItems = ({ type, elementConfig, valueOverride, }) => {
4215
4215
  };
4216
4216
 
4217
4217
  const useGlobalContext = () => {
4218
- const { t, language, themeName, api, ewktGeometry } = useContext(GlobalContext) || {};
4218
+ const { t, language, themeName, api, ewktGeometry, notification } = useContext(GlobalContext) || {};
4219
4219
  const translate = useCallback((value, options) => {
4220
4220
  if (t)
4221
4221
  return t(value, options);
@@ -4227,7 +4227,8 @@ const useGlobalContext = () => {
4227
4227
  themeName,
4228
4228
  api,
4229
4229
  ewktGeometry,
4230
- }), [language, translate, api, ewktGeometry, themeName]);
4230
+ notification,
4231
+ }), [language, translate, api, ewktGeometry, themeName, notification]);
4231
4232
  };
4232
4233
 
4233
4234
  const GRID_TILE_SIZE = "4.5rem";
@@ -5620,6 +5621,7 @@ const usePythonTask = () => {
5620
5621
  const { preparePythonSandbox } = usePythonSandbox();
5621
5622
  const [status, setStatus] = useState(RemoteTaskStatus.Unknown);
5622
5623
  const [log, setLog] = useState(null);
5624
+ const [lastMessage, setLastMessage] = useState(null);
5623
5625
  const [error, setError] = useState(null);
5624
5626
  const [loading, setLoading] = useState(false);
5625
5627
  const [executionTime, setExecutionTime] = useState(null);
@@ -5641,6 +5643,7 @@ const usePythonTask = () => {
5641
5643
  reset();
5642
5644
  setStatus(RemoteTaskStatus.Process);
5643
5645
  setLog(null);
5646
+ setLastMessage(null);
5644
5647
  setLoading(true);
5645
5648
  const start = Date.now();
5646
5649
  let prototypeId = await api.remoteTaskManager.createTaskPrototype({
@@ -5681,6 +5684,8 @@ const usePythonTask = () => {
5681
5684
  const isFinished = [RemoteTaskStatus.Completed, RemoteTaskStatus.Error].includes(data.status);
5682
5685
  setStatus(data.status);
5683
5686
  setLog([logRef.current, data.log].filter(Boolean).join("\n"));
5687
+ if (data.log)
5688
+ setLastMessage(data.log);
5684
5689
  setExecutionTime(Date.now() - start);
5685
5690
  setLoading(false);
5686
5691
  setTaskId(isFinished ? null : newTaskId);
@@ -5733,6 +5738,7 @@ const usePythonTask = () => {
5733
5738
  stopTask,
5734
5739
  openLog,
5735
5740
  log,
5741
+ lastMessage,
5736
5742
  error,
5737
5743
  status,
5738
5744
  loading,
@@ -6252,7 +6258,7 @@ const formatElementValue = ({ t, value, elementConfig, attributes, wrap, }) => {
6252
6258
 
6253
6259
  const getAttributeValue = (element, attributes) => {
6254
6260
  const attribute = getAttributeByName(element?.attributeName, attributes);
6255
- const { maxLength, separator, expandable, lineBreak } = element.options || {};
6261
+ const { maxLength, wordBreak, separator, expandable, lineBreak } = element.options || {};
6256
6262
  let value;
6257
6263
  if (attribute?.type === AttributeType.Boolean && typeof attribute.value === "boolean") {
6258
6264
  return jsx(DashboardCheckbox, { title: attribute.alias || attribute.attributeName, checked: attribute.value });
@@ -6267,7 +6273,7 @@ const getAttributeValue = (element, attributes) => {
6267
6273
  ? JSON.stringify(rawValue)
6268
6274
  : (rawValue || "");
6269
6275
  }
6270
- return typeof value === "string" && maxLength && maxLength < value.length ? (jsx(TextTrim, { maxLength: maxLength, expandable: expandable, lineBreak: lineBreak, children: value })) : (value);
6276
+ return typeof value === "string" && maxLength && maxLength < value.length ? (jsx(TextTrim, { maxLength: maxLength, wordBreak: wordBreak, expandable: expandable, lineBreak: lineBreak, children: value })) : (value);
6271
6277
  };
6272
6278
 
6273
6279
  const getChartAxes = (chartElement) => chartElement?.options?.relatedDataSources?.filter(({ chartAxis }) => chartAxis === "y");
@@ -7397,7 +7403,7 @@ const TabsContainer = memo(({ elementConfig, type }) => {
7397
7403
  const { palette } = useTheme();
7398
7404
  const { selectedTabId, setSelectedTabId } = useWidgetContext(type);
7399
7405
  const { id, options, style, children: tabs } = elementConfig || {};
7400
- const { radius, column, bgColor, noBg, onlyIcon, shownItems, maxLength = 12 } = options || {};
7406
+ const { radius, column, bgColor, noBg, onlyIcon, shownItems, maxLength = 12, wordBreak } = options || {};
7401
7407
  const renderIcon = useCallback((icon, active) => {
7402
7408
  if (!icon)
7403
7409
  return null;
@@ -7405,16 +7411,16 @@ const TabsContainer = memo(({ elementConfig, type }) => {
7405
7411
  return jsx(Icon, { kind: icon });
7406
7412
  return icon.endsWith(".svg") ? (jsx(SvgImage, { url: icon, width: 16, fontColor: active ? palette.textContrast : palette.textSecondary })) : (jsx("img", { src: icon, alt: "" }));
7407
7413
  }, [palette.textContrast, palette.textSecondary]);
7408
- const onClick = useCallback((id) => {
7409
- setSelectedTabId(id);
7410
- window.location.hash = `#${id}`;
7414
+ const onClick = useCallback((tabId) => {
7415
+ setSelectedTabId(tabId);
7416
+ window.location.hash = `#${tabId}`;
7411
7417
  }, [setSelectedTabId]);
7412
7418
  useEffect(() => {
7413
7419
  if (!selectedTabId) {
7414
7420
  setSelectedTabId(tabs[0].id);
7415
7421
  }
7416
7422
  }, []);
7417
- return (jsx(SwiperContainer, { id: id, style: style, children: jsx(Swiper, { spaceBetween: 0, slidesPerView: shownItems || 2, children: tabs.map(({ id, value, options: tabOptions }) => (jsxs(SwiperSlide, { children: [jsxs(TabContainer, { href: `#${id}`, active: selectedTabId === id, column: column, bgColor: bgColor, noBg: noBg, radius: radius, onlyIcon: onlyIcon, hasIcon: !!tabOptions?.icon, onClick: () => onClick(id), children: [renderIcon(tabOptions?.icon, selectedTabId === id), !onlyIcon && (jsx(TabValue, { children: jsx(TextTrim, { maxLength: maxLength, children: value }) }))] }), jsx(TabAnchor, { id: id })] }, id))) }) }));
7423
+ return (jsx(SwiperContainer, { id: id, style: style, children: jsx(Swiper, { spaceBetween: 0, slidesPerView: shownItems || 2, children: tabs.map(({ id: tabId, value, options: tabOptions }) => (jsxs(SwiperSlide, { children: [jsxs(TabContainer, { href: `#${tabId}`, active: selectedTabId === tabId, column: column, bgColor: bgColor, noBg: noBg, radius: radius, onlyIcon: onlyIcon, hasIcon: !!tabOptions?.icon, onClick: () => onClick(tabId), children: [renderIcon(tabOptions?.icon, selectedTabId === tabId), !onlyIcon && (jsx(TabValue, { children: jsx(TextTrim, { maxLength: maxLength, wordBreak: wordBreak, children: value }) }))] }), jsx(TabAnchor, { id: tabId })] }, tabId))) }) }));
7418
7424
  });
7419
7425
 
7420
7426
  const ContainerIconValue = styled(Flex) ``;
@@ -7522,7 +7528,7 @@ const RoundedBackgroundContainer = memo(({ type, elementConfig, feature, renderE
7522
7528
  feature,
7523
7529
  });
7524
7530
  const { id, options, style, children } = elementConfig || {};
7525
- const { maxLength, center, fontColor, innerTemplateStyle, inlineUnits, big, bigIcon, hideEmpty, colorAttribute } = options || {};
7531
+ const { maxLength, wordBreak, center, fontColor, innerTemplateStyle, inlineUnits, big, bigIcon, hideEmpty, colorAttribute } = options || {};
7526
7532
  const iconElement = children?.find(item => item.id === "icon");
7527
7533
  const aliasElement = children?.find(item => item.id === "alias");
7528
7534
  const unitsElement = children?.find(item => item.id === "units");
@@ -7541,7 +7547,7 @@ const RoundedBackgroundContainer = memo(({ type, elementConfig, feature, renderE
7541
7547
  return null;
7542
7548
  return (jsx(FlexSpan, { width: iconElement.options?.width || "1rem", alignItems: "center", mr: "0.5rem", children: renderElement({ id: "icon", wrap: false }) }));
7543
7549
  }, [iconElement, renderElement]);
7544
- const renderAlias = useMemo(() => (jsx(ContainerAlias, { style: aliasElement?.style, children: jsx(TextTrim, { maxLength: maxLength || ALIAS_DEFAULT_MAX_LENGTH, children: renderElement({ id: "alias", wrap: false }) }) })), [aliasElement?.style, maxLength, renderElement]);
7550
+ const renderAlias = useMemo(() => (jsx(ContainerAlias, { style: aliasElement?.style, children: jsx(TextTrim, { maxLength: maxLength || ALIAS_DEFAULT_MAX_LENGTH, wordBreak: wordBreak, children: renderElement({ id: "alias", wrap: false }) }) })), [aliasElement?.style, maxLength, renderElement, wordBreak]);
7545
7551
  const renderValue = useMemo(() => isNil(value) ? null : (jsxs(ContainerValue, { style: valueElement?.style, big: true, children: [value, !!unitsElement && (jsx(ContainerUnits, { style: unitsElement?.style, children: renderElement({ id: "units" }) }))] })), [valueElement?.style, value, unitsElement, renderElement]);
7546
7552
  if (isNil(value) && hideEmpty)
7547
7553
  return null;
@@ -7706,14 +7712,93 @@ const buildFiltersFromResponse = (responseFilters, result) => {
7706
7712
  return Object.keys(filters).length ? filters : null;
7707
7713
  };
7708
7714
 
7715
+ const RUN_ID_RADIX = 36;
7716
+ const RUN_ID_LENGTH = 8;
7717
+ const generateRunId = () => `task-${Date.now()}-${Math.random().toString(RUN_ID_RADIX).slice(2, RUN_ID_LENGTH)}`;
7718
+ const useTaskNotifications = ({ enabled, status, lastMessage, openLog, }) => {
7719
+ const { t, notification } = useGlobalContext();
7720
+ const runIdRef = useRef(null);
7721
+ const prevStatusRef = useRef(RemoteTaskStatus.Unknown);
7722
+ useEffect(() => {
7723
+ if (!enabled) {
7724
+ if (runIdRef.current) {
7725
+ notification?.close(runIdRef.current);
7726
+ runIdRef.current = null;
7727
+ }
7728
+ prevStatusRef.current = status;
7729
+ return;
7730
+ }
7731
+ if (status === RemoteTaskStatus.Unknown) {
7732
+ prevStatusRef.current = status;
7733
+ return;
7734
+ }
7735
+ const isFreshRun = status === RemoteTaskStatus.Process && prevStatusRef.current !== RemoteTaskStatus.Process;
7736
+ if (isFreshRun) {
7737
+ runIdRef.current = generateRunId();
7738
+ }
7739
+ if (!runIdRef.current) {
7740
+ runIdRef.current = generateRunId();
7741
+ }
7742
+ const id = runIdRef.current;
7743
+ const description = lastMessage || t("taskStarted", { ns: "dashboard", defaultValue: "Запуск задачи…" });
7744
+ const button = t("details", { ns: "dashboard", defaultValue: "Подробнее" });
7745
+ const base = {
7746
+ id,
7747
+ title: "",
7748
+ description,
7749
+ duration: Number.MAX_SAFE_INTEGER,
7750
+ button,
7751
+ buttonIcon: "info",
7752
+ buttonColor: "primary",
7753
+ onButtonClick: openLog,
7754
+ };
7755
+ if (status === RemoteTaskStatus.Process) {
7756
+ const item = {
7757
+ ...base,
7758
+ title: t("taskInProgress", { ns: "dashboard", defaultValue: "Выполняется" }),
7759
+ progress: true,
7760
+ };
7761
+ if (isFreshRun) {
7762
+ notification?.add(item);
7763
+ }
7764
+ else {
7765
+ notification?.update(item);
7766
+ }
7767
+ }
7768
+ else if (status === RemoteTaskStatus.Completed) {
7769
+ notification?.update({
7770
+ ...base,
7771
+ title: t("taskCompleted", { ns: "dashboard", defaultValue: "Выполнено" }),
7772
+ success: true,
7773
+ progress: false,
7774
+ });
7775
+ }
7776
+ else if (status === RemoteTaskStatus.Error) {
7777
+ notification?.update({
7778
+ ...base,
7779
+ title: t("taskError", { ns: "dashboard", defaultValue: "Ошибка" }),
7780
+ error: true,
7781
+ progress: false,
7782
+ });
7783
+ }
7784
+ prevStatusRef.current = status;
7785
+ }, [enabled, status, lastMessage, openLog, t, notification]);
7786
+ };
7787
+
7709
7788
  const TaskContainer = memo(({ type, elementConfig, renderElement }) => {
7710
7789
  const { t, ewktGeometry } = useGlobalContext();
7711
7790
  const { dataSources, filters: selectedFilters, attributes, layerInfo, changeFilters, } = useWidgetContext(type);
7712
7791
  const { dataSources: projectDataSources } = useWidgetContext(WidgetType.Dashboard);
7713
7792
  const { currentPage } = useWidgetPage(type);
7714
- const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, log, result, } = usePythonTask();
7793
+ const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, log, lastMessage, result, } = usePythonTask();
7715
7794
  const { options } = elementConfig || {};
7716
- const { title, relatedResources, center, icon, statusColors, responseFilters } = options || {};
7795
+ const { title, relatedResources, center, icon, statusColors, responseFilters, useNotifications, } = options || {};
7796
+ useTaskNotifications({
7797
+ enabled: !!useNotifications,
7798
+ status,
7799
+ lastMessage,
7800
+ openLog,
7801
+ });
7717
7802
  useEffect(() => {
7718
7803
  const filtersToApply = buildFiltersFromResponse(responseFilters, result);
7719
7804
  if (filtersToApply)
@@ -8785,15 +8870,7 @@ const RowHeaderMixin = css `
8785
8870
  const OverlayHeaderMixin = (overlay) => css `
8786
8871
  &&& {
8787
8872
  :after {
8788
- content: "";
8789
- z-index: 2;
8790
- position: absolute;
8791
- top: 0;
8792
- left: 0;
8793
- width: 100%;
8794
- height: 100%;
8795
8873
  background: ${overlay};
8796
- pointer-events: none;
8797
8874
  }
8798
8875
  }
8799
8876
  `;
@@ -8849,8 +8926,10 @@ const LayerIconContainer = styled.div `
8849
8926
  display: flex;
8850
8927
  align-items: center;
8851
8928
  justify-content: center;
8852
- min-width: 2rem;
8853
- margin-right: 0.5rem;
8929
+ min-width: 1.5rem;
8930
+ width: 1.5rem;
8931
+ height: 1.5rem;
8932
+ margin-right: 1rem;
8854
8933
  `;
8855
8934
  const AlertIconContainer = styled(Flex) `
8856
8935
  align-items: center;
@@ -9482,29 +9561,49 @@ const ElementIcon = memo(({ type, elementConfig }) => {
9482
9561
  return (jsx(StyledIcon, { kind: iconValue, fontSize: fontSize, fontColor: fontColor, style: style }));
9483
9562
  });
9484
9563
 
9485
- const ElementImage = memo(({ type, elementConfig }) => {
9564
+ const useElementImage = ({ type, elementConfig, }) => {
9486
9565
  const { attributes } = useWidgetContext(type);
9487
- const { value, attributeName, options, style } = elementConfig || {};
9488
- const { width } = options || {};
9489
- const firstImage = useMemo(() => {
9490
- if (value) {
9566
+ const { value, attributeName } = elementConfig || {};
9567
+ const attribute = useMemo(() => {
9568
+ if (!attributeName || Array.isArray(attributeName))
9569
+ return undefined;
9570
+ return attributes?.find(item => item.attributeName === attributeName);
9571
+ }, [attributeName, attributes]);
9572
+ const isAttachmentAttribute = attribute?.subType === StringSubType.Attachments;
9573
+ const { items } = useAttachmentItems({ type, elementConfig });
9574
+ const imageItems = useMemo(() => items
9575
+ .filter(item => IMAGE_FILE_TYPES.includes(getFileType(item.mimeType, item.name)))
9576
+ .slice(0, 1), [items]);
9577
+ const attachmentImages = useAttachmentPreviewImages({
9578
+ items: imageItems,
9579
+ active: isAttachmentAttribute,
9580
+ });
9581
+ const stringUrl = useMemo(() => {
9582
+ if (value)
9491
9583
  return getResourceUrl(value.toString());
9492
- }
9493
- if (!attributeName || Array.isArray(attributeName)) {
9584
+ if (isAttachmentAttribute)
9585
+ return null;
9586
+ if (!attributeName || Array.isArray(attributeName))
9494
9587
  return null;
9495
- }
9496
- const attribute = attributes?.find(item => item.attributeName === attributeName);
9497
9588
  const imageUrl = attribute?.value?.split(";")?.[0];
9498
- if (!imageUrl) {
9589
+ if (!imageUrl)
9499
9590
  return null;
9500
- }
9501
9591
  return getResourceUrl(imageUrl);
9502
- }, [attributeName, attributes, value]);
9503
- const blobUrl = useFetchImageWithAuth(firstImage);
9504
- if (!blobUrl) {
9592
+ }, [value, isAttachmentAttribute, attributeName, attribute]);
9593
+ const stringBlobUrl = useFetchImageWithAuth(stringUrl);
9594
+ const attachmentSrc = attachmentImages[0]?.src || null;
9595
+ const src = isAttachmentAttribute ? attachmentSrc : stringBlobUrl;
9596
+ const alt = isAttachmentAttribute ? attachmentImages[0]?.fileName ?? "" : stringUrl ?? "";
9597
+ return { src, alt };
9598
+ };
9599
+
9600
+ const ElementImage = memo(({ type, elementConfig }) => {
9601
+ const { options, style } = elementConfig || {};
9602
+ const { width } = options || {};
9603
+ const { src, alt } = useElementImage({ type, elementConfig });
9604
+ if (!src)
9505
9605
  return null;
9506
- }
9507
- return (jsx("img", { src: blobUrl, alt: firstImage ?? "", width: width, style: style }));
9606
+ return jsx("img", { src: src, alt: alt, width: width, style: style });
9508
9607
  });
9509
9608
 
9510
9609
  const ElementLegend = memo(({ type, element, elementConfig }) => {
@@ -9872,7 +9971,8 @@ const SlideshowHeaderWrapper = styled.div `
9872
9971
  position: absolute;
9873
9972
  top: 0;
9874
9973
  left: 0;
9875
- right: 0;
9974
+ width: 100%;
9975
+ height: 100%;
9876
9976
  pointer-events: none;
9877
9977
  }
9878
9978
 
@@ -9882,8 +9982,7 @@ const SlideshowHeaderWrapper = styled.div `
9882
9982
  }
9883
9983
 
9884
9984
  :after {
9885
- height: 4.5rem;
9886
- background: linear-gradient(180deg, #000000 0%, rgba(0, 0, 0, 0) 100%);
9985
+ background: linear-gradient(180deg, rgba(17, 37, 47, 0.75) 0%, rgba(17, 37, 47, 0.00) 100%);
9887
9986
  }
9888
9987
 
9889
9988
  :hover {
@@ -9931,12 +10030,13 @@ const HeaderSlideshow = styled.div `
9931
10030
  `;
9932
10031
 
9933
10032
  const FeatureCardBackgroundHeader = () => {
10033
+ const { themeName: pageThemeName } = useGlobalContext();
9934
10034
  const { config } = useWidgetConfig(WidgetType.FeatureCard);
9935
10035
  const { header } = config || {};
9936
10036
  const { options } = header || {};
9937
10037
  const { fontColor, bgColor, height, overlay, bigIcon, withPadding, bottomBlur, themeName, column } = options || {};
9938
10038
  const renderElement = useHeaderRender(header);
9939
- return (jsx(BackgroundHeaderWrapper, { "$fontColor": fontColor, "$bgColor": bgColor, "$height": height, "$bigIcon": bigIcon, "$withPadding": withPadding, "$bottomBlur": bottomBlur, children: jsx(ThemeProvider, { theme: getThemeByName(themeName), children: jsxs(Header, { "$overlay": overlay, "$isRow": !column, children: [jsxs(HeaderFrontView, { children: [jsxs(HeaderContainer, { column: column, children: [jsx(HeaderLayerIcon, {}), jsx(FeatureCardTitle, { title: renderElement({
10039
+ return (jsx(BackgroundHeaderWrapper, { "$fontColor": fontColor, "$bgColor": bgColor, "$height": height, "$bigIcon": bigIcon, "$withPadding": withPadding, "$bottomBlur": bottomBlur, children: jsx(ThemeProvider, { theme: getThemeByName(themeName ?? pageThemeName), children: jsxs(Header, { "$overlay": overlay, "$isRow": !column, children: [jsxs(HeaderFrontView, { children: [jsxs(HeaderContainer, { column: column, children: [jsx(HeaderLayerIcon, {}), jsx(FeatureCardTitle, { title: renderElement({
9940
10040
  id: "title",
9941
10041
  wrap: false,
9942
10042
  }), description: renderElement({
@@ -10322,8 +10422,8 @@ const getElementValue = ({ getDefaultContainer, ...props }) => {
10322
10422
  return "";
10323
10423
  }
10324
10424
  const alias = attribute?.alias || attributeName || "";
10325
- const { maxLength, lineBreak, expandable } = options || {};
10326
- return alias && maxLength && maxLength < alias.length ? (jsx(TextTrim, { maxLength: maxLength, expandable: expandable, lineBreak: lineBreak, children: alias })) : (alias);
10425
+ const { maxLength, wordBreak, lineBreak, expandable } = options || {};
10426
+ return alias && maxLength && maxLength < alias.length ? (jsx(TextTrim, { maxLength: maxLength, wordBreak: wordBreak, expandable: expandable, lineBreak: lineBreak, children: alias })) : (alias);
10327
10427
  }
10328
10428
  if (type === "attributeValue") {
10329
10429
  /* return isHandbookAttribute(attribute?.name, layerInfo) ? (
@@ -13185,15 +13285,20 @@ const SvgImage = memo(({ url, width, height, fontColor }) => {
13185
13285
  return (jsx(StyledSvg, { "$width": width, "$height": height, "$fontColor": fontColor, dangerouslySetInnerHTML: { __html: svg } }));
13186
13286
  });
13187
13287
 
13188
- const TextTrim = memo(({ maxLength, expandable, lineBreak, children }) => {
13288
+ const TextTrimValue = styled.div `
13289
+ word-break: ${({ wordBreak }) => wordBreak ?? "break-word"};
13290
+ overflow: hidden;
13291
+ `;
13292
+
13293
+ const TextTrim = memo(({ maxLength, expandable, lineBreak, wordBreak, children }) => {
13189
13294
  const { t } = useGlobalContext();
13190
13295
  const [expanded, toggleExpanded] = useToggle();
13191
13296
  const text = children?.toString();
13192
13297
  const formatValue = useCallback((value) => {
13193
13298
  if (!lineBreak)
13194
- return value;
13195
- return jsx("div", { dangerouslySetInnerHTML: { __html: unescape(value).split(lineBreak).join("<br />") } });
13196
- }, [lineBreak]);
13299
+ return jsx(TextTrimValue, { wordBreak: wordBreak, children: value });
13300
+ return jsx(TextTrimValue, { wordBreak: wordBreak, dangerouslySetInnerHTML: { __html: unescape(value).split(lineBreak).join("<br />") } });
13301
+ }, [lineBreak, wordBreak]);
13197
13302
  if (!text?.length || !maxLength || text.length <= maxLength)
13198
13303
  return jsx(Fragment$1, { children: formatValue(text) });
13199
13304
  const substring = `${text.substring(0, maxLength)}...`;