@topconsultnpm/sdkui-react 6.19.0-dev2.35 → 6.19.0-dev2.37

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.
Files changed (38) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.d.ts +4 -0
  2. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +187 -0
  3. package/lib/components/NewComponents/ContextMenu/hooks.d.ts +11 -0
  4. package/lib/components/NewComponents/ContextMenu/hooks.js +48 -0
  5. package/lib/components/NewComponents/ContextMenu/index.d.ts +2 -0
  6. package/lib/components/NewComponents/ContextMenu/index.js +1 -0
  7. package/lib/components/NewComponents/ContextMenu/styles.d.ts +27 -0
  8. package/lib/components/NewComponents/ContextMenu/styles.js +308 -0
  9. package/lib/components/NewComponents/ContextMenu/types.d.ts +26 -0
  10. package/lib/components/NewComponents/ContextMenu/types.js +1 -0
  11. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.d.ts +4 -0
  12. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +370 -0
  13. package/lib/components/NewComponents/FloatingMenuBar/index.d.ts +2 -0
  14. package/lib/components/NewComponents/FloatingMenuBar/index.js +2 -0
  15. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +38 -0
  16. package/lib/components/NewComponents/FloatingMenuBar/styles.js +267 -0
  17. package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +30 -0
  18. package/lib/components/NewComponents/FloatingMenuBar/types.js +1 -0
  19. package/lib/components/NewComponents/Notification/Notification.d.ts +4 -0
  20. package/lib/components/NewComponents/Notification/Notification.js +60 -0
  21. package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +8 -0
  22. package/lib/components/NewComponents/Notification/NotificationContainer.js +33 -0
  23. package/lib/components/NewComponents/Notification/index.d.ts +2 -0
  24. package/lib/components/NewComponents/Notification/index.js +2 -0
  25. package/lib/components/NewComponents/Notification/styles.d.ts +21 -0
  26. package/lib/components/NewComponents/Notification/styles.js +180 -0
  27. package/lib/components/NewComponents/Notification/types.d.ts +18 -0
  28. package/lib/components/NewComponents/Notification/types.js +1 -0
  29. package/lib/components/base/TMCustomButton.js +1 -1
  30. package/lib/components/base/TMWaitPanel.js +8 -2
  31. package/lib/components/features/documents/TMDcmtPreview.js +45 -8
  32. package/lib/components/grids/TMBlogsPost.js +3 -1
  33. package/lib/helper/SDKUI_Localizator.d.ts +1 -0
  34. package/lib/helper/SDKUI_Localizator.js +10 -0
  35. package/lib/helper/dcmtsHelper.d.ts +2 -2
  36. package/lib/helper/dcmtsHelper.js +14 -17
  37. package/lib/helper/helpers.js +3 -0
  38. package/package.json +1 -1
@@ -0,0 +1,30 @@
1
+ import type { TMContextMenuItemProps } from '../ContextMenu';
2
+ export interface TMFloatingMenuItem {
3
+ id: string;
4
+ name: string;
5
+ icon: React.ReactNode;
6
+ onClick: () => void;
7
+ disabled?: boolean;
8
+ isPinned?: boolean;
9
+ originalMenuItem?: TMContextMenuItemProps;
10
+ }
11
+ export interface TMFloatingMenuBarProps {
12
+ containerRef: React.RefObject<HTMLElement | null>;
13
+ contextMenuItems?: TMContextMenuItemProps[];
14
+ storageKey?: string;
15
+ isConstrained?: boolean;
16
+ defaultPosition?: Position;
17
+ maxItems?: number;
18
+ }
19
+ export interface Position {
20
+ x: number;
21
+ y: number;
22
+ }
23
+ export interface TMFloatingMenuBarState {
24
+ position: Position;
25
+ isDragging: boolean;
26
+ isConfigMode: boolean;
27
+ orientation: 'horizontal' | 'vertical';
28
+ items: TMFloatingMenuItem[];
29
+ draggedItemIndex: number | null;
30
+ }
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import type { NotificationProps } from './types';
3
+ declare const Notification: React.FC<NotificationProps>;
4
+ export default Notification;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useRef } from 'react';
3
+ import * as S from './styles';
4
+ const Notification = ({ title, message, mode = 'info', position = 'top-right', duration = 3000, closable = false, stopOnMouseEnter = true, hasProgress = true, onClose, }) => {
5
+ const [state, setState] = useState({
6
+ visible: true,
7
+ isPaused: false,
8
+ progress: 100,
9
+ });
10
+ const timeoutRef = useRef(undefined);
11
+ const remainingTimeRef = useRef(duration);
12
+ const pauseTimeRef = useRef(0);
13
+ const closeNotification = () => {
14
+ setState(prev => ({ ...prev, visible: false }));
15
+ setTimeout(() => {
16
+ onClose?.();
17
+ }, 300);
18
+ };
19
+ useEffect(() => {
20
+ // Set up auto-close timer
21
+ timeoutRef.current = setTimeout(() => {
22
+ closeNotification();
23
+ }, duration);
24
+ return () => {
25
+ if (timeoutRef.current) {
26
+ clearTimeout(timeoutRef.current);
27
+ }
28
+ };
29
+ }, [duration]);
30
+ const handleMouseEnter = () => {
31
+ if (!stopOnMouseEnter)
32
+ return;
33
+ // Pause the timer
34
+ if (timeoutRef.current) {
35
+ clearTimeout(timeoutRef.current);
36
+ pauseTimeRef.current = Date.now();
37
+ }
38
+ setState(prev => ({ ...prev, isPaused: true }));
39
+ };
40
+ const handleMouseLeave = () => {
41
+ if (!stopOnMouseEnter)
42
+ return;
43
+ // Resume the timer with remaining time
44
+ const pauseDuration = Date.now() - pauseTimeRef.current;
45
+ remainingTimeRef.current = Math.max(0, remainingTimeRef.current - pauseDuration);
46
+ timeoutRef.current = setTimeout(() => {
47
+ closeNotification();
48
+ }, remainingTimeRef.current);
49
+ setState(prev => ({ ...prev, isPaused: false }));
50
+ };
51
+ const handleClose = (e) => {
52
+ e.stopPropagation();
53
+ if (timeoutRef.current) {
54
+ clearTimeout(timeoutRef.current);
55
+ }
56
+ closeNotification();
57
+ };
58
+ return (_jsxs(S.NotificationContainer, { "$position": position, "$mode": mode, "$visible": state.visible, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, role: "alert", "aria-live": "assertive", children: [_jsxs(S.NotificationContent, { children: [_jsx(S.NotificationTitle, { children: title }), _jsx(S.NotificationMessage, { children: message })] }), closable && (_jsx(S.CloseButton, { onClick: handleClose, "aria-label": "Close notification", children: "\u00D7" })), hasProgress && (_jsx(S.ProgressBar, { "$duration": duration, "$mode": mode, "$isPaused": state.isPaused }))] }));
59
+ };
60
+ export default Notification;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import type { NotificationPosition } from './types';
3
+ interface NotificationContainerProps {
4
+ position: NotificationPosition;
5
+ children: React.ReactNode;
6
+ }
7
+ declare const NotificationContainer: React.FC<NotificationContainerProps>;
8
+ export default NotificationContainer;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import styled from 'styled-components';
3
+ const Container = styled.div `
4
+ position: fixed;
5
+ z-index: 10002;
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 12px;
9
+ pointer-events: none;
10
+
11
+ ${props => {
12
+ const isTop = props.$position.startsWith('top');
13
+ const isBottom = props.$position.startsWith('bottom');
14
+ const isLeft = props.$position.endsWith('left');
15
+ const isRight = props.$position.endsWith('right');
16
+ const isCenter = props.$position.endsWith('center');
17
+ return `
18
+ ${isTop ? 'top: 24px;' : ''}
19
+ ${isBottom ? 'bottom: 24px;' : ''}
20
+ ${isLeft ? 'left: 24px;' : ''}
21
+ ${isRight ? 'right: 24px;' : ''}
22
+ ${isCenter ? 'left: 50%; transform: translateX(-50%);' : ''}
23
+ `;
24
+ }}
25
+
26
+ & > * {
27
+ pointer-events: auto;
28
+ }
29
+ `;
30
+ const NotificationContainer = ({ position, children }) => {
31
+ return _jsx(Container, { "$position": position, children: children });
32
+ };
33
+ export default NotificationContainer;
@@ -0,0 +1,2 @@
1
+ export { default } from './Notification';
2
+ export * from './types';
@@ -0,0 +1,2 @@
1
+ export { default } from './Notification';
2
+ export * from './types';
@@ -0,0 +1,21 @@
1
+ import type { NotificationMode, NotificationPosition } from './types';
2
+ export declare const NotificationContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
3
+ $position: NotificationPosition;
4
+ $mode: NotificationMode;
5
+ $visible: boolean;
6
+ }>> & string;
7
+ export declare const NotificationContent: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
8
+ export declare const NotificationTitle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
9
+ export declare const NotificationMessage: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
10
+ export declare const CloseButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
11
+ export declare const ProgressBar: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("styled-components/dist/types").Substitute<import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
12
+ ref?: ((instance: HTMLDivElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLDivElement> | null | undefined;
13
+ }>, {
14
+ $duration: number;
15
+ $mode: NotificationMode;
16
+ $isPaused: boolean;
17
+ }>, {
18
+ $duration: number;
19
+ $mode: NotificationMode;
20
+ $isPaused: boolean;
21
+ }>> & string;
@@ -0,0 +1,180 @@
1
+ import styled, { keyframes } from 'styled-components';
2
+ const slideInFromTop = keyframes `
3
+ from {
4
+ opacity: 0;
5
+ transform: translateY(-100%);
6
+ }
7
+ to {
8
+ opacity: 1;
9
+ transform: translateY(0);
10
+ }
11
+ `;
12
+ const slideInFromBottom = keyframes `
13
+ from {
14
+ opacity: 0;
15
+ transform: translateY(100%);
16
+ }
17
+ to {
18
+ opacity: 1;
19
+ transform: translateY(0);
20
+ }
21
+ `;
22
+ const slideOut = keyframes `
23
+ from {
24
+ opacity: 1;
25
+ transform: scale(1);
26
+ }
27
+ to {
28
+ opacity: 0;
29
+ transform: scale(0.9);
30
+ }
31
+ `;
32
+ const getModeColors = (mode) => {
33
+ const colors = {
34
+ success: {
35
+ bg: '#10b981',
36
+ bgDark: '#059669',
37
+ border: '#34d399',
38
+ text: '#ffffff',
39
+ },
40
+ error: {
41
+ bg: '#ef4444',
42
+ bgDark: '#dc2626',
43
+ border: '#f87171',
44
+ text: '#ffffff',
45
+ },
46
+ warning: {
47
+ bg: '#f59e0b',
48
+ bgDark: '#d97706',
49
+ border: '#fbbf24',
50
+ text: '#ffffff',
51
+ },
52
+ info: {
53
+ bg: '#3b82f6',
54
+ bgDark: '#2563eb',
55
+ border: '#60a5fa',
56
+ text: '#ffffff',
57
+ },
58
+ };
59
+ return colors[mode];
60
+ };
61
+ export const NotificationContainer = styled.div `
62
+ position: relative;
63
+ z-index: 1;
64
+ min-width: 320px;
65
+ max-width: 420px;
66
+ background: ${props => getModeColors(props.$mode).bg};
67
+ border-radius: 12px;
68
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2),
69
+ 0 4px 12px rgba(0, 0, 0, 0.15);
70
+ padding: 16px 20px;
71
+ animation: ${props => {
72
+ if (!props.$visible)
73
+ return slideOut;
74
+ return props.$position.startsWith('top') ? slideInFromTop : slideInFromBottom;
75
+ }} 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
76
+ backdrop-filter: blur(10px);
77
+ border: 2px solid ${props => getModeColors(props.$mode).border};
78
+ color: ${props => getModeColors(props.$mode).text};
79
+ overflow: hidden;
80
+
81
+ [data-theme='dark'] & {
82
+ background: ${props => getModeColors(props.$mode).bgDark};
83
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4),
84
+ 0 4px 12px rgba(0, 0, 0, 0.3);
85
+ }
86
+
87
+ @media (max-width: 768px) {
88
+ min-width: 280px;
89
+ max-width: calc(100vw - 48px);
90
+ }
91
+ `;
92
+ export const NotificationContent = styled.div `
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: 6px;
96
+ padding-right: 24px;
97
+ `;
98
+ export const NotificationTitle = styled.div `
99
+ font-size: 16px;
100
+ font-weight: 600;
101
+ line-height: 1.4;
102
+ letter-spacing: -0.01em;
103
+ `;
104
+ export const NotificationMessage = styled.div `
105
+ font-size: 14px;
106
+ font-weight: 400;
107
+ line-height: 1.5;
108
+ opacity: 0.95;
109
+ `;
110
+ export const CloseButton = styled.button `
111
+ position: absolute;
112
+ top: 12px;
113
+ right: 12px;
114
+ background: transparent;
115
+ border: none;
116
+ color: inherit;
117
+ cursor: pointer;
118
+ width: 24px;
119
+ height: 24px;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ border-radius: 6px;
124
+ transition: all 0.15s ease;
125
+ font-size: 18px;
126
+ line-height: 1;
127
+ padding: 0;
128
+ opacity: 0.8;
129
+
130
+ &:hover {
131
+ opacity: 1;
132
+ background: rgba(255, 255, 255, 0.2);
133
+ transform: scale(1.1);
134
+ }
135
+
136
+ &:active {
137
+ transform: scale(0.95);
138
+ }
139
+
140
+ &:focus {
141
+ outline: 2px solid rgba(255, 255, 255, 0.5);
142
+ outline-offset: 2px;
143
+ }
144
+ `;
145
+ export const ProgressBar = styled.div.attrs(props => ({
146
+ style: {
147
+ animationDuration: `${props.$duration}ms`,
148
+ },
149
+ })) `
150
+ position: absolute;
151
+ bottom: 0;
152
+ left: 0;
153
+ height: 4px;
154
+ width: 100%;
155
+ background: ${props => getModeColors(props.$mode).border};
156
+ border-radius: 0 0 0 10px;
157
+ box-shadow: 0 0 8px ${props => getModeColors(props.$mode).border};
158
+ transform-origin: left;
159
+ animation: progress-shrink linear forwards;
160
+ animation-play-state: ${props => props.$isPaused ? 'paused' : 'running'};
161
+
162
+ @keyframes progress-shrink {
163
+ from {
164
+ transform: scaleX(1);
165
+ }
166
+ to {
167
+ transform: scaleX(0);
168
+ }
169
+ }
170
+
171
+ &::after {
172
+ content: '';
173
+ position: absolute;
174
+ top: 0;
175
+ right: 0;
176
+ width: 20px;
177
+ height: 100%;
178
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4));
179
+ }
180
+ `;
@@ -0,0 +1,18 @@
1
+ export type NotificationMode = 'warning' | 'info' | 'error' | 'success';
2
+ export type NotificationPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
3
+ export interface NotificationProps {
4
+ title: string;
5
+ message: string;
6
+ mode?: NotificationMode;
7
+ position?: NotificationPosition;
8
+ duration?: number;
9
+ closable?: boolean;
10
+ stopOnMouseEnter?: boolean;
11
+ hasProgress?: boolean;
12
+ onClose?: () => void;
13
+ }
14
+ export interface NotificationState {
15
+ visible: boolean;
16
+ isPaused: boolean;
17
+ progress: number;
18
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -20,7 +20,7 @@ const TMCustomButton = (props) => {
20
20
  const { appName: scriptUrl, arguments: args } = button;
21
21
  const iframeRef = useRef(null);
22
22
  const attributes = useMemo(() => processButtonAttributes(args, formData), [args, formData]);
23
- const selectedItemsProcessed = useMemo(() => processSelectedItems(selectedItems), [selectedItems]);
23
+ const selectedItemsProcessed = useMemo(() => processSelectedItems(args, formData, selectedItems), [args, formData, selectedItems]);
24
24
  const RunOnce = button.mode === "RunOnce";
25
25
  const [loading, setLoading] = useState(true);
26
26
  const [error, setError] = useState(false);
@@ -33,8 +33,14 @@ const StyledProgressText = styled.p ` font-weight: bold; color: #333; margin: 0;
33
33
  const StyledMessage = styled.p ` color: #666; font-size: 0.9em; margin-top: 10px; `;
34
34
  const StyledAbortButton = styled.button ` background: #ff4d4d; color: white; border: none; border-radius: 5px; padding: 10px 20px; font-size: 1em; cursor: pointer; margin-top: 20px; &:hover { background: #ff6666; } `;
35
35
  export const TMWaitPanel = (props) => {
36
- let progressValue1 = 100 * (((props.valuePrimary ?? 0) / (props.maxValuePrimary ?? 0)) || 0);
37
- let progressValue2 = 100 * (((props.valueSecondary ?? 0) / (props.maxValueSecondary ?? 0)) || 0);
36
+ const calculateProgress = (value = 0, maxValue = 0) => {
37
+ if (maxValue === 0)
38
+ return 0;
39
+ const progress = (value / maxValue) * 100;
40
+ return Number.isFinite(progress) ? progress : 0;
41
+ };
42
+ let progressValue1 = calculateProgress(props.valuePrimary, props.maxValuePrimary);
43
+ let progressValue2 = calculateProgress(props.valueSecondary, props.maxValueSecondary);
38
44
  return (_jsx(StyledWaitPanelOverlay, { children: _jsxs(StyledWaitPanel, { "$height": (props.showPrimary && props.showSecondary) ? '350px' : '250px', children: [_jsx(StyledTitle, { children: props.title }), props.showPrimary &&
39
45
  _jsxs("div", { style: { width: '100%', height: '100px' }, children: [_jsx(StyledProgressBarContainer, { children: _jsx(StyledProgressBar, { style: { width: `${progressValue1.toFixed(2)}%` } }) }), _jsxs(StyledProgressText, { children: [progressValue1.toFixed(2), "%"] }), _jsx(StyledMessage, { children: props.textPrimary })] }), props.showSecondary &&
40
46
  _jsxs("div", { style: { width: '100%', height: '100px' }, children: [_jsx(StyledProgressBarContainer, { children: _jsx(StyledProgressBar, { style: { width: `${progressValue2.toFixed(2)}%` } }) }), _jsxs(StyledProgressText, { children: [progressValue2.toFixed(2), "%"] }), _jsx(StyledMessage, { children: props.textSecondary })] }), props.isCancelable && _jsx(StyledAbortButton, { onClick: () => props.onAbortClick?.(props.abortController), children: SDKUI_Localizator.Abort })] }) }));
@@ -15,11 +15,18 @@ import { TMSaveFormButtonPrevious, TMSaveFormButtonNext } from '../../forms/TMSa
15
15
  import { StyledAnimatedComponentOpacity } from '../../base/Styled';
16
16
  import TMPanel from '../../base/TMPanel';
17
17
  import TMTooltip from '../../base/TMTooltip';
18
+ const ErrorContent = ({ error, isAbortError, onRetry }) => {
19
+ if (isAbortError) {
20
+ return (_jsx(StyledAnimatedComponentOpacity, { style: { width: '100%', height: '100%' }, children: _jsxs(StyledPanelStatusContainer, { children: [_jsx(IconCloseOutline, { fontSize: 92, color: TMColors.error }), _jsxs(StyledPreviewNotAvailable, { children: [_jsx("div", { children: error }), _jsx("div", { children: SDKUI_Localizator.PreviewNotAvailable })] }), _jsx(TMButton, { caption: SDKUI_Localizator.TryAgain, onClick: onRetry, showTooltip: false })] }) }));
21
+ }
22
+ return _jsx(TMNothingToShow, { icon: _jsx(IconCloseOutline, { fontSize: 92, color: TMColors.error }), text: error });
23
+ };
18
24
  const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev, onClosePanel, onNext, onPrev, allowMaximize = true, onMaximizePanel }) => {
19
25
  const [dcmtBlob, setDcmtBlob] = useState(undefined);
20
26
  const [showPreview, setShowPreview] = useState(false);
21
27
  const [isFromCache, setIsFromCache] = useState(false);
22
28
  const [error, setError] = useState('');
29
+ const [isAbortError, setIsAbortError] = useState(false);
23
30
  const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, getDcmtFileAsync, clearDcmtsFileCache, removeDcmtsFileCache, isDcmtFileInCache } = useDcmtOperations();
24
31
  const cacheKey = dcmtData ? `${dcmtData.tid}-${dcmtData.did}` : '00';
25
32
  const [hasLoadedDataOnce, setHasLoadedDataOnce] = useState(false);
@@ -29,6 +36,7 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev
29
36
  setLastLoadedDid(undefined); // Reset
30
37
  setDcmtBlob(undefined);
31
38
  setError('');
39
+ setIsAbortError(false);
32
40
  setShowPreview(false);
33
41
  return;
34
42
  }
@@ -42,6 +50,7 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev
42
50
  if (shouldFetch) {
43
51
  setDcmtBlob(undefined);
44
52
  setError('');
53
+ setIsAbortError(false);
45
54
  if ((extensionHandler(dcmtData.fileExt) !== FileExtensionHandler.NONE) && ((dcmtData.fileSize ?? 0) <= (SDKUI_Globals.userSettings.searchSettings.previewThreshold * 1024))) {
46
55
  loadDocumentWithCache();
47
56
  setShowPreview(true);
@@ -65,15 +74,19 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev
65
74
  let dcmtFile = await getDcmtFileAsync({ TID: dcmtData?.tid, DID: dcmtData?.did, FILEEXT: dcmtData?.fileExt }, rfo, 'Anteprima', false);
66
75
  setDcmtBlob(dcmtFile?.file);
67
76
  setIsFromCache(!!dcmtFile?.isFromCache);
77
+ setError('');
78
+ setIsAbortError(false);
68
79
  }
69
80
  catch (ex) {
70
81
  const err = ex;
71
82
  if (err.name === 'CanceledError') {
72
83
  setError('Operazione annullata.');
84
+ setIsAbortError(true);
73
85
  ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
74
86
  }
75
87
  else {
76
88
  setError(getExceptionMessage(ex));
89
+ setIsAbortError(false);
77
90
  TMExceptionBoxManager.show({ exception: ex });
78
91
  }
79
92
  }
@@ -101,6 +114,9 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev
101
114
  const reOpenDcmt = async () => {
102
115
  removeDcmtsFileCache(cacheKey);
103
116
  setIsFromCache(false);
117
+ setError('');
118
+ setIsAbortError(false);
119
+ setDcmtBlob(undefined);
104
120
  try {
105
121
  await loadDocumentWithCache();
106
122
  }
@@ -112,7 +128,7 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev
112
128
  { icon: _jsx(IconCloseCircle, {}), text: SDKUI_Localizator.RemoveFromCache, onClick: () => { removeDcmtsFileCache(cacheKey); setIsFromCache(false); } },
113
129
  { icon: _jsx(IconClear, {}), text: SDKUI_Localizator.ClearCache, onClick: () => { clearDcmtsFileCache(); setIsFromCache(false); } },
114
130
  ] }, "btn13") }), _jsx(StyledHeaderIcon, { onClick: reOpenDcmt, "$color": TMColors.primaryColor, children: _jsx(TMTooltip, { content: SDKUI_Localizator.ReopenDocument, children: _jsx(IconRefresh, {}) }) })] }), children: error
115
- ? _jsx(TMNothingToShow, { icon: _jsx(IconCloseOutline, { fontSize: 92, color: TMColors.error }), text: error })
131
+ ? _jsx(ErrorContent, { error: error, isAbortError: isAbortError, onRetry: reOpenDcmt })
116
132
  : renderedPreview(dcmtData?.tid, dcmtData?.did, dcmtData?.fileExt, dcmtData?.fileSize, dcmtData?.fileCount, extensionHandler(dcmtData?.fileExt), showPreview, isResizingActive, () => { loadDocumentWithCache(); setShowPreview(true); }, dcmtBlob) }) }));
117
133
  };
118
134
  export default TMDcmtPreview;
@@ -148,11 +164,12 @@ export const TMFileViewer = ({ fileBlob, isResizingActive }) => {
148
164
  }, []);
149
165
  useEffect(() => {
150
166
  if (fileBlob) {
151
- const url = URL.createObjectURL(fileBlob);
152
- setBlobUrl(url);
153
167
  setFileType(fileBlob.type);
154
168
  setFormattedXml(undefined);
155
- if (fileBlob.type.includes("xml")) {
169
+ const fileName = fileBlob.name || '';
170
+ const fileExtension = fileName.split('.').pop()?.toLowerCase() || '';
171
+ const isConfigFile = ['config', 'cfg'].includes(fileExtension);
172
+ if (fileBlob.type.includes("xml") && !isConfigFile) {
156
173
  fileBlob.text().then((text) => {
157
174
  const parser = new DOMParser();
158
175
  const xmlDoc = parser.parseFromString(text, "application/xml");
@@ -169,10 +186,28 @@ export const TMFileViewer = ({ fileBlob, isResizingActive }) => {
169
186
  .replace(/</g, "&lt;")
170
187
  .replace(/>/g, "&gt;")}</pre>`);
171
188
  });
189
+ setBlobUrl(undefined);
190
+ }
191
+ else if (isConfigFile) {
192
+ fileBlob.text().then((text) => {
193
+ const escapedText = text
194
+ .replace(/&/g, "&amp;")
195
+ .replace(/</g, "&lt;")
196
+ .replace(/>/g, "&gt;");
197
+ setFormattedXml(`<pre style="font-family: monospace; white-space: pre-wrap; line-height: 1.5; margin: 0; padding: 10px;">${escapedText}</pre>`);
198
+ }).catch((error) => {
199
+ console.error("Error reading text file:", error);
200
+ setFormattedXml(`<div style="color: red;">Error reading file</div>`);
201
+ });
202
+ setBlobUrl(undefined);
203
+ }
204
+ else {
205
+ const url = URL.createObjectURL(fileBlob);
206
+ setBlobUrl(url);
207
+ return () => {
208
+ URL.revokeObjectURL(url);
209
+ };
172
210
  }
173
- return () => {
174
- URL.revokeObjectURL(url);
175
- };
176
211
  }
177
212
  else {
178
213
  setBlobUrl(undefined);
@@ -225,7 +260,9 @@ export const TMFileViewer = ({ fileBlob, isResizingActive }) => {
225
260
  display: 'inline-block'
226
261
  }, children: SDKUI_Localizator.OpenInNewTab })] })] }));
227
262
  }
228
- return (_jsx("iframe", { srcDoc: fileType?.includes("xml") && formattedXml ? `<html><body>${formattedXml}</body></html>` : undefined, src: !fileType?.includes("xml") || !formattedXml ? blobUrl : undefined, title: "File Viewer", width: "100%", height: "100%", style: { border: 'none', zIndex: 0, pointerEvents: isResizingActive === true ? "none" : "auto" } }));
263
+ return (_jsx("iframe", { srcDoc: formattedXml ? `<html><body>${formattedXml}</body></html>` : undefined, src: !formattedXml
264
+ ? (fileType === 'application/pdf' ? `${blobUrl}#view=FitH&scrollbar=1` : blobUrl)
265
+ : undefined, title: "File Viewer", width: "100%", height: "100%", style: { border: 'none', zIndex: 0, pointerEvents: isResizingActive === true ? "none" : "auto" } }, blobUrl));
229
266
  };
230
267
  const ImageViewer = ({ fileBlob, alt = 'Image', className }) => {
231
268
  const containerRef = useRef(null);
@@ -92,8 +92,10 @@ const TMBlogsPost = (props) => {
92
92
  setFocusedAttachment(attachment);
93
93
  };
94
94
  useEffect(() => {
95
- if (context === undefined)
95
+ if (context === undefined) {
96
+ setTaskContext(undefined);
96
97
  return;
98
+ }
97
99
  if (context.engine === 'WorkingGroupEngine' && context.object) {
98
100
  setTaskContext({ workingGroup: { id: context?.object?.id ?? 0, name: context?.object?.name ?? '' } });
99
101
  return;
@@ -505,6 +505,7 @@ export declare class SDKUI_Localizator {
505
505
  static get RemovingFromList(): string;
506
506
  static get RememberCredentials(): "Anmeldedaten merken" | "Remember credentials" | "Recordar credenciales" | "Se souvenir des identifiants" | "Lembrar credenciais" | "Ricorda credenziali";
507
507
  static get ReopenDocument(): string;
508
+ static get TryAgain(): string;
508
509
  static get ReplaceDocument(): "Dokument ersetzen" | "Replace Document" | "Reemplazar Documento" | "Remplacer le Document" | "Substituir Documento" | "Sostituisci Documento";
509
510
  static get Request(): string;
510
511
  static get RequestTo(): string;
@@ -4978,6 +4978,16 @@ export class SDKUI_Localizator {
4978
4978
  default: return "Riapri documento";
4979
4979
  }
4980
4980
  }
4981
+ static get TryAgain() {
4982
+ switch (this._cultureID) {
4983
+ case CultureIDs.De_DE: return "Erneut versuchen";
4984
+ case CultureIDs.En_US: return "Try Again";
4985
+ case CultureIDs.Es_ES: return "Intentar de nuevo";
4986
+ case CultureIDs.Fr_FR: return "Réessayer";
4987
+ case CultureIDs.Pt_PT: return "Tentar novamente";
4988
+ default: return "Riprova";
4989
+ }
4990
+ }
4981
4991
  static get ReplaceDocument() {
4982
4992
  switch (this._cultureID) {
4983
4993
  case CultureIDs.De_DE: return "Dokument ersetzen";
@@ -4,5 +4,5 @@ export declare const hasDetailRelations: (mTID: number | undefined) => Promise<b
4
4
  /** Check if dcmtType (mTID) has configured Master or Many-to-Many relations */
5
5
  export declare const hasMasterRelations: (mTID: number | undefined) => Promise<boolean>;
6
6
  export declare const isXMLFileExt: (fileExt: string | undefined) => boolean;
7
- export declare const processButtonAttributes: (args: string | undefined, formData: MetadataValueDescriptorEx[] | undefined) => string[] | undefined;
8
- export declare const processSelectedItems: (selectedItems: Array<any> | undefined) => any[] | undefined;
7
+ export declare const processButtonAttributes: (args: string | undefined, formData: MetadataValueDescriptorEx[] | undefined) => (string | null)[] | undefined;
8
+ export declare const processSelectedItems: (args: string | undefined, formData: MetadataValueDescriptorEx[] | undefined, selectedItems: Array<any> | undefined) => any[][];
@@ -25,29 +25,26 @@ export const isXMLFileExt = (fileExt) => {
25
25
  };
26
26
  /*utility functions for TMCustomButton*/
27
27
  export const processButtonAttributes = (args, formData) => args && formData ? formDataMap(formData, args) : undefined;
28
- export const processSelectedItems = (selectedItems) => selectedItems?.map(item => {
29
- const tid = item.TID;
30
- if (!tid)
31
- return item;
32
- const prefix = `${tid}_`;
33
- const prefixLength = prefix.length;
34
- return Object.keys(item).reduce((acc, key) => {
35
- acc[key.startsWith(prefix) ? key.substring(prefixLength) : key] = item[key];
36
- return acc;
37
- }, {});
38
- });
39
- const formDataMap = (data, args) => {
28
+ export const processSelectedItems = (args, formData, selectedItems) => {
29
+ if (!args || !selectedItems)
30
+ return [];
31
+ const MidList = formData ? formDataMap(formData, args, true) : [];
32
+ return selectedItems.map(item =>
33
+ //salvo il did come primo elemento dell'array
34
+ [item["DID"], ...MidList.map(key => key && item[key])]);
35
+ };
36
+ const formDataMap = (data, args, returnMid = false) => {
40
37
  const tokens = args.match(/\{@?[^}]+\}/g) || [];
41
38
  return tokens.map(token => {
42
39
  if (token.startsWith('{@')) {
43
40
  // Campo dinamico: {@campo} -> cerca in formData
44
- const fieldName = token.slice(2, -1); // Rimuove {@...}
41
+ const fieldName = token.slice(2, -1);
45
42
  const md = data.find(md => md.md?.name === fieldName);
46
- return md?.value;
43
+ return returnMid ? (md ? `${md.tid}_${md.mid}` : null) : (md?.value ?? null);
47
44
  }
48
45
  else {
49
- // Campo statico: {valore} -> ritorna il valore direttamente
50
- return token.slice(1, -1); // Rimuove {...}
46
+ // Campo statico: {valore} -> ritorna il valore o null
47
+ return returnMid ? null : token.slice(1, -1);
51
48
  }
52
- }).filter((value) => value !== undefined && value !== null);
49
+ });
53
50
  };
@@ -480,6 +480,9 @@ export const extensionHandler = (fileExt) => {
480
480
  case 'xml.p7m.tsd': return FileExtensionHandler.READY_TO_SHOW;
481
481
  case 'pdf': return FileExtensionHandler.READY_TO_SHOW;
482
482
  case 'txt': return FileExtensionHandler.READY_TO_SHOW;
483
+ case 'config':
484
+ case 'cfg':
485
+ case 'json': return FileExtensionHandler.READY_TO_SHOW;
483
486
  default: return FileExtensionHandler.NONE;
484
487
  }
485
488
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.19.0-dev2.35",
3
+ "version": "6.19.0-dev2.37",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",