@evergis/react 3.1.67 → 3.1.68

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.
@@ -0,0 +1,6 @@
1
+ import { FC } from 'react';
2
+ interface TaskLogTerminalProps {
3
+ log?: string | Record<string, any>;
4
+ }
5
+ export declare const LogTerminal: FC<TaskLogTerminalProps>;
6
+ export {};
@@ -0,0 +1 @@
1
+ export declare const TerminalWrapper: import('styled-components').StyledComponent<"div", any, {}, never>;
@@ -10,7 +10,7 @@ export declare const usePythonTask: () => {
10
10
  }) => Promise<void>;
11
11
  stopTask: () => Promise<void>;
12
12
  openLog: () => void;
13
- result: string;
13
+ log: string;
14
14
  error: Error;
15
15
  status: RemoteTaskStatus;
16
16
  loading: boolean;
package/dist/index.js CHANGED
@@ -24,6 +24,9 @@ require('mapbox-gl/dist/mapbox-gl.css');
24
24
  var react = require('swiper/react');
25
25
  var ReactMarkdown = require('react-markdown');
26
26
  var remarkGfm = require('remark-gfm');
27
+ var xterm = require('@xterm/xterm');
28
+ var addonFit = require('@xterm/addon-fit');
29
+ require('@xterm/xterm/css/xterm.css');
27
30
 
28
31
  const AddFeatureButton = ({ title, icon = "feature_add" /* , layerName, geometryType*/ }) => {
29
32
  // const [, handleAddFeature] = useFeatureCreator(layerName, geometryType);
@@ -4828,14 +4831,14 @@ const usePythonTask = () => {
4828
4831
  const { api: api$1, t } = useGlobalContext();
4829
4832
  const { addSubscription, connection, unsubscribeById } = useServerNotificationsContext();
4830
4833
  const [status, setStatus] = React.useState(api.RemoteTaskStatus.Unknown);
4831
- const [result, setResult] = React.useState(null);
4834
+ const [log, setLog] = React.useState(null);
4832
4835
  const [error, setError] = React.useState(null);
4833
4836
  const [loading, setLoading] = React.useState(false);
4834
4837
  const [executionTime, setExecutionTime] = React.useState(null);
4835
4838
  const [taskId, setTaskId] = React.useState(null);
4836
4839
  const [subscriptionId, setSubscriptionId] = React.useState(null);
4837
4840
  const [isLogDialogOpen, setIsLogDialogOpen] = React.useState(false);
4838
- const resultRef = React.useRef(null);
4841
+ const logRef = React.useRef(null);
4839
4842
  const reset = React.useCallback(() => {
4840
4843
  setStatus(api.RemoteTaskStatus.Unknown);
4841
4844
  setError(null);
@@ -4847,7 +4850,7 @@ const usePythonTask = () => {
4847
4850
  const runTask = React.useCallback(async ({ resourceId, parameters, script, fileName, methodName, }) => {
4848
4851
  reset();
4849
4852
  setStatus(api.RemoteTaskStatus.Process);
4850
- setResult(null);
4853
+ setLog(null);
4851
4854
  setLoading(true);
4852
4855
  const start = Date.now();
4853
4856
  let prototypeId = await api$1.remoteTaskManager.createTaskPrototype({
@@ -4872,7 +4875,7 @@ const usePythonTask = () => {
4872
4875
  const { id: newTaskId, success } = await api$1.remoteTaskManager.startTask1(prototypeId);
4873
4876
  if (!success) {
4874
4877
  setStatus(api.RemoteTaskStatus.Error);
4875
- setResult(t("taskRunFail", { ns: "devMode", defaultValue: "Не удалось запустить задачу" }));
4878
+ setLog(t("taskRunFail", { ns: "devMode", defaultValue: "Не удалось запустить задачу" }));
4876
4879
  setExecutionTime(Date.now() - start);
4877
4880
  setLoading(false);
4878
4881
  }
@@ -4885,10 +4888,8 @@ const usePythonTask = () => {
4885
4888
  const onNotification = ({ data }) => {
4886
4889
  if (data?.taskId === newTaskId) {
4887
4890
  setStatus(data.status);
4888
- setResult([data.log, resultRef.current].filter(Boolean).join("\n\n"));
4889
- setError(null);
4891
+ setLog([logRef.current, data.log].filter(Boolean).join("\n\n"));
4890
4892
  setExecutionTime(Date.now() - start);
4891
- setLoading(false);
4892
4893
  setTaskId([api.RemoteTaskStatus.Completed, api.RemoteTaskStatus.Error].includes(data.status)
4893
4894
  ? undefined
4894
4895
  : newTaskId);
@@ -4915,14 +4916,14 @@ const usePythonTask = () => {
4915
4916
  setIsLogDialogOpen(false);
4916
4917
  }, []);
4917
4918
  React.useEffect(() => {
4918
- resultRef.current = result;
4919
- }, [result]);
4919
+ logRef.current = log;
4920
+ }, [log]);
4920
4921
  return {
4921
4922
  taskId,
4922
4923
  runTask,
4923
4924
  stopTask,
4924
4925
  openLog,
4925
- result,
4926
+ log,
4926
4927
  error,
4927
4928
  status,
4928
4929
  loading,
@@ -6768,7 +6769,7 @@ const StatusBadge = styled(uilibGl.Flex) `
6768
6769
  color: ${({ theme }) => theme.palette.iconContrast};
6769
6770
  border-radius: 0.25rem;
6770
6771
  `;
6771
- const LogContainer = styled.div `
6772
+ styled.div `
6772
6773
  flex: 1;
6773
6774
  padding: 1rem;
6774
6775
  background-color: ${({ theme }) => theme.palette.element};
@@ -6782,14 +6783,127 @@ const LogContainer = styled.div `
6782
6783
  max-height: 31.25rem;
6783
6784
  `;
6784
6785
 
6786
+ const TerminalWrapper = styled.div `
6787
+ flex: 1;
6788
+ overflow: hidden;
6789
+ padding: 0;
6790
+ margin: 1rem;
6791
+ box-sizing: border-box;
6792
+ min-height: 0;
6793
+
6794
+ & && .xterm-viewport {
6795
+ overflow-y: auto;
6796
+ }
6797
+ `;
6798
+
6799
+ const LogTerminal = ({ log }) => {
6800
+ const terminalRef = React.useRef(null);
6801
+ const xtermRef = React.useRef(null);
6802
+ const fitAddonRef = React.useRef(null);
6803
+ const previousLogRef = React.useRef("");
6804
+ const theme = styled.useTheme();
6805
+ // Initialize terminal
6806
+ React.useEffect(() => {
6807
+ if (!terminalRef.current)
6808
+ return;
6809
+ // Create terminal instance
6810
+ const terminal = new xterm.Terminal({
6811
+ cursorBlink: false,
6812
+ fontSize: 12,
6813
+ fontFamily: '"Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", monospace',
6814
+ theme: {
6815
+ background: theme.palette.devBackgroundDark,
6816
+ foreground: theme.palette.textPrimary,
6817
+ cursor: theme.palette.primary,
6818
+ },
6819
+ scrollback: 10000,
6820
+ convertEol: true,
6821
+ lineHeight: 1.5,
6822
+ });
6823
+ // Create fit addon
6824
+ const fitAddon = new addonFit.FitAddon();
6825
+ terminal.loadAddon(fitAddon);
6826
+ // Open terminal
6827
+ terminal.open(terminalRef.current);
6828
+ fitAddon.fit();
6829
+ // Store refs
6830
+ xtermRef.current = terminal;
6831
+ fitAddonRef.current = fitAddon;
6832
+ // Handle window resize
6833
+ const handleResize = () => {
6834
+ fitAddon.fit();
6835
+ };
6836
+ window.addEventListener("resize", handleResize);
6837
+ // Cleanup
6838
+ return () => {
6839
+ window.removeEventListener("resize", handleResize);
6840
+ terminal.dispose();
6841
+ xtermRef.current = null;
6842
+ fitAddonRef.current = null;
6843
+ };
6844
+ }, [theme]);
6845
+ // Update log content
6846
+ React.useEffect(() => {
6847
+ if (!xtermRef.current)
6848
+ return;
6849
+ // Handle different log types
6850
+ if (typeof log === "string") {
6851
+ // For string logs, only write the new content (append mode)
6852
+ const previousLog = previousLogRef.current;
6853
+ if (log !== previousLog) {
6854
+ if (log.startsWith(previousLog)) {
6855
+ // Log is accumulated - write only the new part
6856
+ const newContent = log.substring(previousLog.length);
6857
+ xtermRef.current.write(`${newContent.replace(/\n/g, "\r\n")}\r\n`);
6858
+ }
6859
+ else {
6860
+ // Log was replaced completely - clear and write all
6861
+ xtermRef.current.clear();
6862
+ xtermRef.current.write(`${log.replace(/\n/g, "\r\n")}\r\n`);
6863
+ }
6864
+ previousLogRef.current = log;
6865
+ }
6866
+ }
6867
+ else if (typeof log === "object") {
6868
+ // JSON object (results) - always replace
6869
+ xtermRef.current.clear();
6870
+ const formatted = JSON.stringify(log, null, 2);
6871
+ xtermRef.current.write(formatted.replace(/\n/g, "\r\n"));
6872
+ previousLogRef.current = "";
6873
+ }
6874
+ else if (!log) {
6875
+ // No log - clear terminal
6876
+ xtermRef.current.clear();
6877
+ previousLogRef.current = "";
6878
+ }
6879
+ // Scroll to bottom
6880
+ xtermRef.current.scrollToBottom();
6881
+ }, [log]);
6882
+ // Fit terminal on container resize
6883
+ React.useEffect(() => {
6884
+ if (!fitAddonRef.current)
6885
+ return;
6886
+ const resizeObserver = new ResizeObserver(() => {
6887
+ fitAddonRef.current?.fit();
6888
+ });
6889
+ if (terminalRef.current) {
6890
+ resizeObserver.observe(terminalRef.current);
6891
+ }
6892
+ return () => {
6893
+ resizeObserver.disconnect();
6894
+ };
6895
+ }, []);
6896
+ return jsxRuntime.jsx(TerminalWrapper, { ref: terminalRef });
6897
+ };
6898
+
6785
6899
  const STATUS_TRANSLATION_KEYS = {
6786
- [api.RemoteTaskStatus.Process]: "task.status.process",
6787
- [api.RemoteTaskStatus.Completed]: "task.status.completed",
6788
- [api.RemoteTaskStatus.Error]: "task.status.error",
6789
- [api.RemoteTaskStatus.Unknown]: "task.status.unknown",
6900
+ [api.RemoteTaskStatus.Process]: "taskProcess",
6901
+ [api.RemoteTaskStatus.Completed]: "taskCompleted",
6902
+ [api.RemoteTaskStatus.Error]: "taskError",
6903
+ [api.RemoteTaskStatus.Unknown]: "taskUnknown",
6790
6904
  };
6791
6905
  const STATUS_DEFAULT_VALUES = {
6792
- [api.RemoteTaskStatus.Process]: "Выполняется...",
6906
+ [api.RemoteTaskStatus.Process]: "Остановить",
6793
6907
  [api.RemoteTaskStatus.Completed]: "Завершено",
6794
6908
  [api.RemoteTaskStatus.Error]: "Ошибка",
6795
6909
  [api.RemoteTaskStatus.Unknown]: "Неизвестно",
@@ -6803,12 +6917,6 @@ const STATUS_COLORS = {
6803
6917
 
6804
6918
  const LogDialog = ({ isOpen, onClose, logs, status, statusColors }) => {
6805
6919
  const { t } = useGlobalContext();
6806
- const contentRef = React.useRef(null);
6807
- React.useEffect(() => {
6808
- if (contentRef.current) {
6809
- contentRef.current.scrollTop = 0;
6810
- }
6811
- }, [logs]);
6812
6920
  const getStatusText = React.useCallback((status) => {
6813
6921
  const translationKey = STATUS_TRANSLATION_KEYS[status] || STATUS_TRANSLATION_KEYS[api.RemoteTaskStatus.Unknown];
6814
6922
  const defaultValue = STATUS_DEFAULT_VALUES[status] || STATUS_DEFAULT_VALUES[api.RemoteTaskStatus.Unknown];
@@ -6817,7 +6925,7 @@ const LogDialog = ({ isOpen, onClose, logs, status, statusColors }) => {
6817
6925
  const getStatusColor = React.useCallback((status) => {
6818
6926
  return statusColors?.[status] || STATUS_COLORS[status] || STATUS_COLORS[api.RemoteTaskStatus.Unknown];
6819
6927
  }, [statusColors]);
6820
- return (jsxRuntime.jsxs(uilibGl.Dialog, { isOpen: isOpen, onCloseRequest: onClose, modal: true, maxWidth: "800px", minWidth: "600px", minHeight: "400px", children: [jsxRuntime.jsx(uilibGl.DialogTitle, { children: jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxRuntime.jsx("span", { children: t("task.logs.title", { ns: "dashboard", defaultValue: "Логи выполнения задачи" }) }), jsxRuntime.jsx(uilibGl.IconButton, { kind: "close", onClick: onClose })] }) }), jsxRuntime.jsx(uilibGl.DialogContent, { children: jsxRuntime.jsxs(uilibGl.Flex, { flexDirection: "column", height: "100%", marginBottom: "2rem", children: [jsxRuntime.jsxs(StatusBadge, { "$statusColor": getStatusColor(status), children: [t("task.status.label", { ns: "dashboard", defaultValue: "Статус" }), ": ", getStatusText(status)] }), jsxRuntime.jsx(LogContainer, { ref: contentRef, children: logs || t("task.logs.empty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) })] }) })] }));
6928
+ return (jsxRuntime.jsxs(uilibGl.Dialog, { isOpen: isOpen, onCloseRequest: onClose, modal: true, maxWidth: "800px", minWidth: "600px", minHeight: "400px", children: [jsxRuntime.jsx(uilibGl.DialogTitle, { children: jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxRuntime.jsx("span", { children: t("taskLogs", { ns: "dashboard", defaultValue: "Логи выполнения задачи" }) }), jsxRuntime.jsx(uilibGl.IconButton, { kind: "close", onClick: onClose })] }) }), jsxRuntime.jsx(uilibGl.DialogContent, { children: jsxRuntime.jsxs(uilibGl.Flex, { flexDirection: "column", height: "100%", marginBottom: "2rem", children: [jsxRuntime.jsxs(StatusBadge, { "$statusColor": getStatusColor(status), children: [t("taskStatus", { ns: "dashboard", defaultValue: "Статус" }), ": ", getStatusText(status)] }), jsxRuntime.jsx(LogTerminal, { log: logs || t("taskLogsEmpty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) })] }) })] }));
6821
6929
  };
6822
6930
 
6823
6931
  const StatusWaitingButton = styled(uilibGl.WaitingButton) `
@@ -6831,14 +6939,18 @@ const StatusWaitingButton = styled(uilibGl.WaitingButton) `
6831
6939
  `}
6832
6940
  `;
6833
6941
 
6834
- const TaskContainer = React.memo(({ type, elementConfig }) => {
6942
+ const TaskContainer = React.memo(({ type, elementConfig, renderElement }) => {
6835
6943
  const { t, ewktGeometry } = useGlobalContext();
6836
6944
  const { dataSources, filters: selectedFilters } = useWidgetContext(type);
6837
6945
  const { currentPage } = useWidgetPage(type);
6838
- const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, result } = usePythonTask();
6946
+ const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, log } = usePythonTask();
6839
6947
  const { options } = elementConfig || {};
6840
6948
  const { title, relatedResources, center, icon, statusColors } = options || {};
6841
6949
  const onClick = React.useCallback(async () => {
6950
+ if (taskId) {
6951
+ await stopTask();
6952
+ return;
6953
+ }
6842
6954
  await Promise.all(relatedResources.map(({ resourceId, parameters, script, fileName, methodName }) => {
6843
6955
  const newParams = applyQueryFilters({
6844
6956
  parameters,
@@ -6849,8 +6961,8 @@ const TaskContainer = React.memo(({ type, elementConfig }) => {
6849
6961
  });
6850
6962
  return runTask({ resourceId, parameters: newParams, script, fileName, methodName });
6851
6963
  }));
6852
- }, [currentPage.filters, dataSources, ewktGeometry, relatedResources, runTask, selectedFilters]);
6853
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: center ? "center" : "flex-start", children: [jsxRuntime.jsxs(StatusWaitingButton, { primary: true, status: status, statusColors: statusColors, isWaiting: loading, disabled: !relatedResources?.length, onClick: onClick, children: [icon && jsxRuntime.jsx(uilibGl.FlexSpan, { marginRight: "0.5rem", children: jsxRuntime.jsx(uilibGl.Icon, { kind: icon }) }), title || t("run", { ns: "dashboard", defaultValue: "Запуск" })] }), !!taskId && (jsxRuntime.jsx(uilibGl.IconButton, { kind: "stop", onClick: stopTask })), !!result && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(uilibGl.IconButton, { kind: "info", onClick: openLog }), jsxRuntime.jsx(LogDialog, { isOpen: isLogDialogOpen, onClose: closeLog, logs: result, status: status, statusColors: statusColors })] }))] }) }));
6964
+ }, [currentPage.filters, dataSources, ewktGeometry, relatedResources, runTask, selectedFilters, stopTask, taskId]);
6965
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), jsxRuntime.jsxs(uilibGl.Flex, { justifyContent: center ? "center" : "flex-start", children: [jsxRuntime.jsxs(StatusWaitingButton, { primary: true, status: status, statusColors: statusColors, isWaiting: loading || !!taskId, disabled: !relatedResources?.length, onClick: onClick, children: [icon && jsxRuntime.jsx(uilibGl.FlexSpan, { marginRight: "0.5rem", children: jsxRuntime.jsx(uilibGl.Icon, { kind: icon }) }), title || t("run", { ns: "dashboard", defaultValue: "Запуск" })] }), !!taskId && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(uilibGl.IconButton, { kind: "info", onClick: openLog }), jsxRuntime.jsx(LogDialog, { logs: log, status: status, statusColors: statusColors, isOpen: isLogDialogOpen, onClose: closeLog })] }))] })] }));
6854
6966
  });
6855
6967
 
6856
6968
  const containerComponents = {
@@ -7680,27 +7792,30 @@ const StyledIcon = styled(uilibGl.Icon) `
7680
7792
 
7681
7793
  const ElementIcon = React.memo(({ type, elementConfig }) => {
7682
7794
  const { attributes } = useWidgetContext(type);
7683
- const { value, attributeName, options } = elementConfig || {};
7795
+ const { value, attributeName, options, style } = elementConfig || {};
7684
7796
  const { fontSize, fontColor } = options || {};
7685
7797
  const iconValue = React.useMemo(() => (attributeName ? attributes?.find(item => item.name === attributeName)?.value : value), [attributeName, attributes, value]);
7686
- return jsxRuntime.jsx(StyledIcon, { kind: iconValue, fontSize: fontSize, fontColor: fontColor });
7798
+ return jsxRuntime.jsx(StyledIcon, { kind: iconValue, fontSize: fontSize, fontColor: fontColor, style: style });
7687
7799
  });
7688
7800
 
7689
7801
  const ElementImage = React.memo(({ type, elementConfig }) => {
7690
7802
  const { attributes } = useWidgetContext(type);
7691
- const { value, attributeName, options } = elementConfig || {};
7803
+ const { value, attributeName, options, style } = elementConfig || {};
7692
7804
  const { width } = options || {};
7693
7805
  const firstImage = React.useMemo(() => {
7694
7806
  if (value) {
7695
- return value.toString();
7807
+ return getResourceUrl(value.toString());
7696
7808
  }
7697
7809
  if (!attributeName || Array.isArray(attributeName)) {
7698
7810
  return null;
7699
7811
  }
7700
7812
  const attribute = attributes?.find(item => item.name === attributeName);
7701
- return attribute?.value?.split(";")?.[0];
7813
+ const imageUrl = attribute?.value?.split(";")?.[0];
7814
+ if (!imageUrl)
7815
+ return null;
7816
+ return getResourceUrl(imageUrl);
7702
7817
  }, [attributeName, attributes, value]);
7703
- return firstImage ? jsxRuntime.jsx("img", { src: firstImage, alt: firstImage, width: width }) : null;
7818
+ return firstImage ? jsxRuntime.jsx("img", { src: firstImage, alt: firstImage, width: width, style: style }) : null;
7704
7819
  });
7705
7820
 
7706
7821
  const ElementLegend = React.memo(({ type, element, elementConfig }) => {