@topconsultnpm/sdkui-react 6.19.0-dev2.16 → 6.19.0-dev2.18
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/lib/components/NewComponents/ContextMenu/TMContextMenu.d.ts +4 -0
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +187 -0
- package/lib/components/NewComponents/ContextMenu/hooks.d.ts +11 -0
- package/lib/components/NewComponents/ContextMenu/hooks.js +48 -0
- package/lib/components/NewComponents/ContextMenu/index.d.ts +2 -0
- package/lib/components/NewComponents/ContextMenu/index.js +1 -0
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +27 -0
- package/lib/components/NewComponents/ContextMenu/styles.js +308 -0
- package/lib/components/NewComponents/ContextMenu/types.d.ts +26 -0
- package/lib/components/NewComponents/ContextMenu/types.js +1 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.d.ts +4 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +370 -0
- package/lib/components/NewComponents/FloatingMenuBar/index.d.ts +2 -0
- package/lib/components/NewComponents/FloatingMenuBar/index.js +2 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +38 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +267 -0
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +30 -0
- package/lib/components/NewComponents/FloatingMenuBar/types.js +1 -0
- package/lib/components/NewComponents/Notification/Notification.d.ts +4 -0
- package/lib/components/NewComponents/Notification/Notification.js +60 -0
- package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +8 -0
- package/lib/components/NewComponents/Notification/NotificationContainer.js +33 -0
- package/lib/components/NewComponents/Notification/index.d.ts +2 -0
- package/lib/components/NewComponents/Notification/index.js +2 -0
- package/lib/components/NewComponents/Notification/styles.d.ts +21 -0
- package/lib/components/NewComponents/Notification/styles.js +180 -0
- package/lib/components/NewComponents/Notification/types.d.ts +18 -0
- package/lib/components/NewComponents/Notification/types.js +1 -0
- package/lib/components/base/TMCustomButton.js +2 -2
- package/lib/components/base/TMDataGridExportForm.js +9 -3
- package/lib/components/features/documents/TMDcmtForm.js +2 -1
- package/lib/components/features/documents/TMRelationViewer.js +55 -22
- package/lib/components/features/search/TMSearchResult.js +3 -3
- package/lib/helper/TMUtils.d.ts +1 -4
- package/lib/helper/TMUtils.js +13 -9
- 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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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,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 {};
|
|
@@ -8,6 +8,7 @@ const IframeContainer = styled.div `
|
|
|
8
8
|
display: flex;
|
|
9
9
|
height: 100%;
|
|
10
10
|
flex-direction: column;
|
|
11
|
+
padding-left: 15px;
|
|
11
12
|
`;
|
|
12
13
|
const StyledIframe = styled.iframe `
|
|
13
14
|
border: none;
|
|
@@ -36,7 +37,6 @@ const TMCustomButton = (props) => {
|
|
|
36
37
|
const mergedAttributes = { ...attributes, selectedItems: selectedItems };
|
|
37
38
|
iframeRef.current.contentWindow.postMessage({
|
|
38
39
|
"options": mergedAttributes,
|
|
39
|
-
"selectedItems": selectedItems,
|
|
40
40
|
"session": SDK_Globals.tmSession
|
|
41
41
|
}, getTargetOrigin(scriptUrl));
|
|
42
42
|
}
|
|
@@ -58,6 +58,6 @@ const TMCustomButton = (props) => {
|
|
|
58
58
|
onClose?.();
|
|
59
59
|
}
|
|
60
60
|
}, [isModal, scriptUrl, onClose]);
|
|
61
|
-
return isModal ? (_jsx(TMModal, { title: button.title, width: '60%', height: '
|
|
61
|
+
return isModal ? (_jsx(TMModal, { title: button.title, width: '60%', height: '70%', resizable: true, onClose: onClose, children: _jsxs(IframeContainer, { children: [error && _jsx("div", { children: "Si \u00E8 verificato un errore nel caricamento del contenuto." }), !error && _jsx(StyledIframe, { ref: iframeRef, loading: 'lazy', onLoad: handleLoad, onError: handleError, src: scriptUrl })] }) })) : null;
|
|
62
62
|
};
|
|
63
63
|
export default TMCustomButton;
|
|
@@ -64,7 +64,7 @@ const TMDataGridExportForm = (props) => {
|
|
|
64
64
|
// Retrieve columns from the search result, or use an empty array if not available
|
|
65
65
|
const columns = searchResult?.dtdResult?.columns ?? [];
|
|
66
66
|
// If exportDescriptionsForDataLists is true, build a map of values to labels for the columns; otherwise use an empty Map
|
|
67
|
-
const
|
|
67
|
+
const valueToNameMap = exportDescriptionsForDataLists ? await buildValueToLabelMapFromDataColumns(columns) : new Map();
|
|
68
68
|
// Create a Set from selectedRowKeys for efficient lookup
|
|
69
69
|
const selectedSet = new Set(selectedRowKeys);
|
|
70
70
|
// If exporting only selected rows, filter the dataSource accordingly; otherwise use the full dataSource
|
|
@@ -82,8 +82,14 @@ const TMDataGridExportForm = (props) => {
|
|
|
82
82
|
const getValue = (col, value) => {
|
|
83
83
|
// Replace raw value with corresponding label from the map if applicable
|
|
84
84
|
let result = value;
|
|
85
|
-
if (exportDescriptionsForDataLists && col.
|
|
86
|
-
|
|
85
|
+
if (exportDescriptionsForDataLists && col.dataField && valueToNameMap.size > 0) {
|
|
86
|
+
const mapForField = valueToNameMap.get(col.dataField);
|
|
87
|
+
if (mapForField) {
|
|
88
|
+
result = mapForField.get(value) ?? value;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
result = value;
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
// If the column is a datetime type, attempt to format it as a locale date string
|
|
89
95
|
if (col.dataType === 'datetime' && result) {
|
|
@@ -8,7 +8,7 @@ import { DownloadTypes, FormModes } from '../../../ts';
|
|
|
8
8
|
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
|
9
9
|
import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
|
|
10
10
|
import { getWorkItemSetIDAsync, handleArchiveVisibility, searchResultToMetadataValues } from '../../../helper/queryHelper';
|
|
11
|
-
import { genUniqueId, IconShow, SDKUI_Localizator, updateMruTids, IconBoard, IconDcmtTypeSys, IconDetailDcmts, svgToString, IconDownload, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, isTaskMoreInfo, IconWorkflow, IconSearch, deepCompare, IconCheck, IconActivity } from '../../../helper';
|
|
11
|
+
import { genUniqueId, IconShow, SDKUI_Localizator, updateMruTids, IconBoard, IconDcmtTypeSys, IconDetailDcmts, svgToString, IconDownload, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, isTaskMoreInfo, IconWorkflow, IconSearch, deepCompare, IconCheck, IconActivity, TMImageLibrary } from '../../../helper';
|
|
12
12
|
import { hasDetailRelations, hasMasterRelations, isXMLFileExt } from '../../../helper/dcmtsHelper';
|
|
13
13
|
import { Gutters, TMColors } from '../../../utils/theme';
|
|
14
14
|
import { StyledFormButtonsContainer, StyledLoadingContainer, StyledModalContainer, StyledReferenceButton, StyledSpinner, StyledToolbarCardContainer } from '../../base/Styled';
|
|
@@ -480,6 +480,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
|
|
|
480
480
|
const customButtonsItems = customButtonsLayout.customButtons
|
|
481
481
|
.filter((customButton) => customButton.isForUpdate && customButton.isForUpdate > 0)
|
|
482
482
|
.map((customButton) => ({
|
|
483
|
+
icon: svgToString(TMImageLibrary({ imageID: customButton.glyphID, showPath: true })),
|
|
483
484
|
text: customButton.title || 'Bottone personalizzato',
|
|
484
485
|
onClick: () => setCustomButton(customButton)
|
|
485
486
|
}));
|
|
@@ -142,12 +142,16 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
142
142
|
const [treeData, setTreeData] = useState([]);
|
|
143
143
|
const [showZeroDcmts, setShowZeroDcmts] = useState(initialShowZeroDcmts);
|
|
144
144
|
const [staticItemsState, setStaticItemsState] = useState([]);
|
|
145
|
-
// Wait Panel State (only used if allowWaitPanel is true)
|
|
146
145
|
const [showWaitPanel, setShowWaitPanel] = useState(false);
|
|
147
146
|
const [waitPanelTextPrimary, setWaitPanelTextPrimary] = useState('');
|
|
148
147
|
const [waitPanelValuePrimary, setWaitPanelValuePrimary] = useState(0);
|
|
149
148
|
const [waitPanelMaxValuePrimary, setWaitPanelMaxValuePrimary] = useState(0);
|
|
150
149
|
const [abortController] = useState(new AbortController());
|
|
150
|
+
const [showExpansionWaitPanel, setShowExpansionWaitPanel] = useState(false);
|
|
151
|
+
const [expansionWaitPanelText, setExpansionWaitPanelText] = useState('');
|
|
152
|
+
const [expansionWaitPanelValue, setExpansionWaitPanelValue] = useState(0);
|
|
153
|
+
const [expansionWaitPanelMaxValue, setExpansionWaitPanelMaxValue] = useState(0);
|
|
154
|
+
const [expansionAbortController, setExpansionAbortController] = useState(undefined);
|
|
151
155
|
// Ref to track last loaded input to prevent unnecessary reloads
|
|
152
156
|
const lastLoadedInputRef = React.useRef('');
|
|
153
157
|
// Ref to track if user has manually expanded/collapsed static items
|
|
@@ -560,30 +564,55 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
560
564
|
// If container is already loaded, return items
|
|
561
565
|
if (node.isLoaded)
|
|
562
566
|
return node.items;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
567
|
+
const newAbortController = new AbortController();
|
|
568
|
+
setExpansionAbortController(newAbortController);
|
|
569
|
+
const itemsToLoad = node.items?.length ?? 0;
|
|
570
|
+
setShowExpansionWaitPanel(true);
|
|
571
|
+
setExpansionWaitPanelMaxValue(itemsToLoad);
|
|
572
|
+
setExpansionWaitPanelValue(0);
|
|
573
|
+
setExpansionWaitPanelText(`Caricamento documenti correlati...`);
|
|
574
|
+
try {
|
|
575
|
+
const newItems = [];
|
|
576
|
+
let processedCount = 0;
|
|
577
|
+
for (const dcmt of node.items ?? []) {
|
|
578
|
+
if (newAbortController.signal.aborted) {
|
|
579
|
+
console.log('Folder expansion aborted by user');
|
|
580
|
+
return node.items;
|
|
574
581
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
582
|
+
const item = { ...dcmt };
|
|
583
|
+
if (item.tid && item.did && !item.isLoaded) {
|
|
584
|
+
// Update progress
|
|
585
|
+
processedCount++;
|
|
586
|
+
setExpansionWaitPanelValue(processedCount);
|
|
587
|
+
setExpansionWaitPanelText(`Caricamento ${processedCount} di ${itemsToLoad}...`);
|
|
588
|
+
// Nella modalità originale (invertMasterNavigation=false),
|
|
589
|
+
// i documenti detail non devono caricare i master come figli
|
|
590
|
+
if (isForMaster && !invertMasterNavigation) {
|
|
591
|
+
// Carica i detail dei detail (navigazione naturale detail→detail)
|
|
592
|
+
const loadedItems = await getDetailDcmtsAsync(item.tid, item.did, 1);
|
|
593
|
+
item.items = updateHiddenProperty(loadedItems);
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// Modalità standard o invertita
|
|
597
|
+
const loadedItems = isForMaster
|
|
598
|
+
? await getMasterDcmtsAsync(item.tid, item.did, 1)
|
|
599
|
+
: await getDetailDcmtsAsync(item.tid, item.did, 1);
|
|
600
|
+
item.items = updateHiddenProperty(loadedItems);
|
|
601
|
+
}
|
|
602
|
+
item.isLoaded = true;
|
|
581
603
|
}
|
|
582
|
-
item
|
|
604
|
+
newItems.push(item);
|
|
583
605
|
}
|
|
584
|
-
newItems
|
|
606
|
+
return newItems;
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
console.error('Error loading folder contents:', error);
|
|
610
|
+
return node.items;
|
|
611
|
+
}
|
|
612
|
+
finally {
|
|
613
|
+
setShowExpansionWaitPanel(false);
|
|
614
|
+
setExpansionAbortController(undefined);
|
|
585
615
|
}
|
|
586
|
-
return newItems;
|
|
587
616
|
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty]);
|
|
588
617
|
/**
|
|
589
618
|
* Default item renderer with metadata display (adapted from TMMasterDetailDcmts.tsx)
|
|
@@ -715,6 +744,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
715
744
|
if (mergedTreeData.length === 0) {
|
|
716
745
|
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
|
|
717
746
|
}
|
|
718
|
-
return (_jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, onSelectionChanged: handleSelectedItemsChanged }))
|
|
747
|
+
return (_jsxs(_Fragment, { children: [_jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, onSelectionChanged: handleSelectedItemsChanged }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? 'Caricamento documenti master' : 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: (abortController) => {
|
|
748
|
+
setTimeout(() => {
|
|
749
|
+
abortController?.abort();
|
|
750
|
+
}, 100);
|
|
751
|
+
} }))] }));
|
|
719
752
|
};
|
|
720
753
|
export default TMRelationViewer;
|
|
@@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
3
3
|
import { SDK_Globals, DataColumnTypes, MetadataDataDomains, DataListViewModes, MetadataFormats, LayoutModes, TemplateTIDs, DcmtTypeListCacheService, AccessLevels, SystemMIDsAsNumber, RetrieveFileOptions, DcmtOpers, GeneralRetrieveFormats, AccessLevelsEx, ResultTypes, LayoutCacheService } from '@topconsultnpm/sdk-ts';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import { getCommandsMenuItems, getSelectedDcmtsOrFocused } from './TMSearchResultsMenuItems';
|
|
6
|
-
import { genUniqueId, IconShow, IconBoard, IconDcmtTypeSys, IconDetailDcmts, SDKUI_Localizator, IconDelete, IconRefresh, IconMenuVertical, IconDownload, deepCompare, getDataColumnName, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, IconSearchCheck, TMCommandsContextMenu, getExceptionMessage, IconCheck, svgToString } from '../../../helper';
|
|
6
|
+
import { genUniqueId, IconShow, IconBoard, IconDcmtTypeSys, IconDetailDcmts, SDKUI_Localizator, IconDelete, IconRefresh, IconMenuVertical, IconDownload, deepCompare, getDataColumnName, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, IconSearchCheck, TMCommandsContextMenu, getExceptionMessage, IconCheck, svgToString, TMImageLibrary } from '../../../helper';
|
|
7
7
|
import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
|
|
8
8
|
import { useInputAttachmentsDialog, useInputCvtFormatDialog } from '../../../hooks/useInputDialog';
|
|
9
9
|
import { useRelatedDocuments } from '../../../hooks/useRelatedDocuments';
|
|
@@ -309,7 +309,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
|
|
|
309
309
|
const customButtonMenuItems = () => {
|
|
310
310
|
const customButtonsItems = customButtonsLayout?.customButtons?.filter((customButton) => customButton.isForSearchResult && customButton.isForSearchResult > 0)
|
|
311
311
|
.map((customButton) => ({
|
|
312
|
-
icon: svgToString(
|
|
312
|
+
icon: svgToString(TMImageLibrary({ imageID: customButton.glyphID, showPath: true })),
|
|
313
313
|
text: customButton.title || 'Bottone personalizzato',
|
|
314
314
|
onClick: () => setCustomButton(customButton)
|
|
315
315
|
}));
|
|
@@ -595,7 +595,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
|
|
|
595
595
|
setShowReAssignPopup(true);
|
|
596
596
|
}, onMoreInfo: () => {
|
|
597
597
|
setShowMoreInfoPopup(true);
|
|
598
|
-
}, approveDisable: selectedDocs.length === 0, signApproveDisable: disableSignApproveDisable, rejectDisable: selectedDocs.length === 0, reassignDisable: selectedDocs.length === 0, infoDisable: selectedDocs.length !== 1, dtd: fromDTD }) }) })] }), _jsx(ConfirmFormatDialog, {}), _jsx(ConfirmAttachmentsDialog, {}), customButton && _jsx(TMCustomButton, { button: customButton, formData: currentMetadataValues, selectedItems:
|
|
598
|
+
}, approveDisable: selectedDocs.length === 0, signApproveDisable: disableSignApproveDisable, rejectDisable: selectedDocs.length === 0, reassignDisable: selectedDocs.length === 0, infoDisable: selectedDocs.length !== 1, dtd: fromDTD }) }) })] }), _jsx(ConfirmFormatDialog, {}), _jsx(ConfirmAttachmentsDialog, {}), customButton && _jsx(TMCustomButton, { button: customButton, formData: currentMetadataValues, selectedItems: selectedItems, onClose: () => setCustomButton(undefined) }), showRelatedDcmtsChooser &&
|
|
599
599
|
_jsx(TMChooserForm, { dataSource: relatedDcmtsChooserDataSource, onChoose: async (selectedRelation) => {
|
|
600
600
|
try {
|
|
601
601
|
setShowRelatedDcmtsChooser(false);
|
package/lib/helper/TMUtils.d.ts
CHANGED
|
@@ -6,10 +6,7 @@ export interface RowData {
|
|
|
6
6
|
[key: string]: string | number | null;
|
|
7
7
|
}
|
|
8
8
|
export declare const associateColumnsToRows: (columns: Array<DataColumnDescriptor> | undefined, rows: Array<Array<string>> | undefined) => Array<RowData>;
|
|
9
|
-
export declare const buildValueToLabelMapFromDataColumns: (columns: Array<DataColumnDescriptor>) => Promise<
|
|
10
|
-
valueToNameMap: Map<string, string>;
|
|
11
|
-
captions: Set<string>;
|
|
12
|
-
}>;
|
|
9
|
+
export declare const buildValueToLabelMapFromDataColumns: (columns: Array<DataColumnDescriptor>) => Promise<Map<string, Map<string, string>>>;
|
|
13
10
|
export declare const getAvatarColor: (name: string) => string;
|
|
14
11
|
export declare const extractInitialsFromName: (name: string) => string;
|
|
15
12
|
export declare const SIGN4_TOP_WIDGET_ID = "60003";
|
package/lib/helper/TMUtils.js
CHANGED
|
@@ -119,23 +119,27 @@ export const associateColumnsToRows = (columns, rows) => {
|
|
|
119
119
|
};
|
|
120
120
|
export const buildValueToLabelMapFromDataColumns = async (columns) => {
|
|
121
121
|
const valueToNameMap = new Map();
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
const dataDomain = MetadataDataDomains[(
|
|
122
|
+
const dataListColumns = columns.filter(col => {
|
|
123
|
+
const dataDomainRaw = col.extendedProperties?.["DataDomain"];
|
|
124
|
+
const dataDomain = MetadataDataDomains[(dataDomainRaw ?? "None")];
|
|
125
|
+
return dataDomain === MetadataDataDomains.DataList;
|
|
126
|
+
});
|
|
127
|
+
for (const col of dataListColumns) {
|
|
128
|
+
const tid = Number(col.extendedProperties?.["TID"]);
|
|
129
|
+
const mid = Number(col.extendedProperties?.["MID"]);
|
|
125
130
|
const dataListID = Number(col.extendedProperties?.["DataListID"]);
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
131
|
+
if (tid && mid && dataListID) {
|
|
132
|
+
const tid_mid = `${tid}_${mid}`;
|
|
133
|
+
valueToNameMap.set(tid_mid, new Map());
|
|
130
134
|
const dl = await DataListCacheService.GetAsync(dataListID);
|
|
131
135
|
dl?.items?.forEach((item) => {
|
|
132
136
|
if (item?.name !== undefined && item?.value !== undefined) {
|
|
133
|
-
valueToNameMap.set(item.value, item.name);
|
|
137
|
+
valueToNameMap.get(tid_mid)?.set(item.value, item.name);
|
|
134
138
|
}
|
|
135
139
|
});
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
|
-
return
|
|
142
|
+
return valueToNameMap;
|
|
139
143
|
};
|
|
140
144
|
export const getAvatarColor = (name) => {
|
|
141
145
|
const colors = [
|