@topconsultnpm/sdkui-react-beta 6.14.136 → 6.14.138
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/base/TMConfirm.d.ts +13 -0
- package/lib/components/base/TMConfirm.js +119 -0
- package/lib/components/base/TMTreeView.d.ts +2 -1
- package/lib/components/base/TMTreeView.js +50 -7
- package/lib/components/features/documents/TMDcmtIcon.js +28 -10
- package/lib/components/features/documents/TMMasterDetailDcmts.js +14 -29
- package/lib/helper/SDKUI_Localizator.d.ts +2 -0
- package/lib/helper/SDKUI_Localizator.js +20 -0
- package/lib/hooks/useDcmtOperations.js +16 -0
- package/package.json +2 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface ShowConfirmOptions {
|
|
2
|
+
message: string;
|
|
3
|
+
title: string;
|
|
4
|
+
okButtonText?: string;
|
|
5
|
+
cancelButtonText?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Mostra una finestra di conferma personalizzata tramite un componente modale.
|
|
9
|
+
* @param options Opzioni per la finestra di conferma (messaggio, titolo, testi dei bottoni).
|
|
10
|
+
* @returns Una Promise che si risolve a `true` se l'utente conferma, `false` se annulla.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ShowConfirm: (options: ShowConfirmOptions) => Promise<boolean>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import ReactDOM from 'react-dom/client';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import TMModal from './TMModal';
|
|
7
|
+
import TMButton from './TMButton';
|
|
8
|
+
import Toppy from '../../assets/Toppy-generico.png';
|
|
9
|
+
import { TMColors } from '../../utils/theme';
|
|
10
|
+
// *** Stili per la modale (immutati) ***
|
|
11
|
+
const StyledModalBodyWrapper = styled.div `
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
height: 100%;
|
|
15
|
+
`;
|
|
16
|
+
const StyledModalFooter = styled.div `
|
|
17
|
+
padding: 12px;
|
|
18
|
+
height: auto;
|
|
19
|
+
display: flex;
|
|
20
|
+
justify-content: flex-end;
|
|
21
|
+
flex-direction: row;
|
|
22
|
+
gap: 10px;
|
|
23
|
+
`;
|
|
24
|
+
const StyledModalContentContainer = styled.div `
|
|
25
|
+
width: 100%;
|
|
26
|
+
padding: 10px;
|
|
27
|
+
flex: 1;
|
|
28
|
+
overflow-y: auto;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: row;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: 10px;
|
|
33
|
+
`;
|
|
34
|
+
// *** Componente della modale di conferma ***
|
|
35
|
+
const TMConfirmModal = ({ isOpen, title, message, okButtonText = 'OK', cancelButtonText = 'Annulla', onConfirm, onCancel, onClose }) => {
|
|
36
|
+
// Stato per la larghezza e l'altezza della modale
|
|
37
|
+
const [modalWidth, setModalWidth] = useState('520px');
|
|
38
|
+
const [modalHeight, setModalHeight] = useState('240px');
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const handleResize = () => {
|
|
41
|
+
if (window.innerWidth <= 768) {
|
|
42
|
+
setModalWidth('300px');
|
|
43
|
+
setModalHeight('200px');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
setModalWidth('520px');
|
|
47
|
+
setModalHeight('240px');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
// Imposta la dimensione iniziale
|
|
51
|
+
handleResize();
|
|
52
|
+
// Aggiungi il listener per l'evento di ridimensionamento della finestra
|
|
53
|
+
window.addEventListener('resize', handleResize);
|
|
54
|
+
// Rimuovi il listener quando il componente viene smontato
|
|
55
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
56
|
+
}, []);
|
|
57
|
+
return (_jsx(TMModal, { title: title, width: modalWidth, height: modalHeight, resizable: false, onClose: onClose, children: _jsxs(StyledModalBodyWrapper, { children: [_jsxs(StyledModalContentContainer, { children: [_jsx("img", { width: 60, height: 75, src: Toppy, alt: "Toppy" }), _jsx("p", { style: { color: TMColors.primaryColor }, children: message })] }), _jsxs(StyledModalFooter, { children: [_jsx(TMButton, { btnStyle: 'text', showTooltip: false, caption: okButtonText, onClick: onConfirm }), _jsx(TMButton, { btnStyle: 'text', showTooltip: false, caption: cancelButtonText, onClick: onCancel })] })] }) }));
|
|
58
|
+
};
|
|
59
|
+
// *** Variabili globali per la gestione della Promise e del root dinamico ***
|
|
60
|
+
let resolvePromise = null;
|
|
61
|
+
let confirmRoot = null;
|
|
62
|
+
let portalContainer = null;
|
|
63
|
+
/**
|
|
64
|
+
* Funzione interna per creare/aggiornare/rimuovere la modale tramite un Portal.
|
|
65
|
+
* Questa non è esportata e viene gestita solo da `ShowConfirm`.
|
|
66
|
+
*/
|
|
67
|
+
const updateConfirmModal = (options, isOpen, onConfirm, onCancel, onClose) => {
|
|
68
|
+
if (!portalContainer) {
|
|
69
|
+
// Crea il div in cui il portal verrà renderizzato la prima volta
|
|
70
|
+
portalContainer = document.createElement('div');
|
|
71
|
+
portalContainer.id = 'confirm-modal-portal-root';
|
|
72
|
+
document.body.appendChild(portalContainer);
|
|
73
|
+
confirmRoot = ReactDOM.createRoot(portalContainer);
|
|
74
|
+
}
|
|
75
|
+
if (isOpen && options && confirmRoot) {
|
|
76
|
+
// Renderizza il componente usando un Portal
|
|
77
|
+
confirmRoot.render(createPortal(_jsx(TMConfirmModal, { isOpen: isOpen, title: options.title, message: options.message, okButtonText: options.okButtonText, cancelButtonText: options.cancelButtonText, onConfirm: onConfirm, onCancel: onCancel, onClose: onClose }), portalContainer));
|
|
78
|
+
}
|
|
79
|
+
else if (confirmRoot) {
|
|
80
|
+
// Se non è più aperto, renderizza null per smontare il componente dal portal
|
|
81
|
+
confirmRoot.render(null);
|
|
82
|
+
// È buona pratica pulire anche il container dal DOM se non è più necessario
|
|
83
|
+
if (portalContainer && portalContainer.parentNode) {
|
|
84
|
+
confirmRoot.unmount(); // Smonta il root completamente
|
|
85
|
+
portalContainer.parentNode.removeChild(portalContainer);
|
|
86
|
+
portalContainer = null;
|
|
87
|
+
confirmRoot = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Mostra una finestra di conferma personalizzata tramite un componente modale.
|
|
93
|
+
* @param options Opzioni per la finestra di conferma (messaggio, titolo, testi dei bottoni).
|
|
94
|
+
* @returns Una Promise che si risolve a `true` se l'utente conferma, `false` se annulla.
|
|
95
|
+
*/
|
|
96
|
+
export const ShowConfirm = (options) => {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (resolvePromise) {
|
|
99
|
+
// Se c'è già una modale di conferma aperta, rifiuta la nuova richiesta.
|
|
100
|
+
// Potresti voler mettere in coda le richieste o gestirle diversamente.
|
|
101
|
+
reject(new Error('Another confirm modal is already open.'));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
resolvePromise = resolve;
|
|
105
|
+
const cleanupAndResolve = (value) => {
|
|
106
|
+
if (resolvePromise) {
|
|
107
|
+
resolvePromise(value);
|
|
108
|
+
resolvePromise = null;
|
|
109
|
+
// Chiudi e pulisci la modale
|
|
110
|
+
updateConfirmModal(null, false, () => { }, () => { }, () => { });
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const handleConfirm = () => cleanupAndResolve(true);
|
|
114
|
+
const handleCancel = () => cleanupAndResolve(false);
|
|
115
|
+
const handleClose = () => cleanupAndResolve(false); // Gestione della chiusura tramite 'X' o ESC
|
|
116
|
+
// Mostra la modale
|
|
117
|
+
updateConfirmModal(options, true, handleConfirm, handleCancel, handleClose);
|
|
118
|
+
});
|
|
119
|
+
};
|
|
@@ -19,8 +19,9 @@ interface ITMTreeViewProps<T extends ITMTreeItem> {
|
|
|
19
19
|
onSelectionChanged?: (selectedItems: T[]) => void;
|
|
20
20
|
onNodeUpdate?: (updatedNode: T) => void;
|
|
21
21
|
onDataChanged?: (items: T[]) => void;
|
|
22
|
+
shouldDelayFocusOnEvent?: (node: T, event: React.MouseEvent) => boolean;
|
|
22
23
|
}
|
|
23
|
-
declare const TMTreeView: <T extends ITMTreeItem>({ dataSource, focusedItem, selectedItems, allowMultipleSelection, onDataChanged, calculateItemsForNode, itemRender, onNodeUpdate, onFocusedItemChanged, onSelectionChanged }: ITMTreeViewProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
declare const TMTreeView: <T extends ITMTreeItem>({ dataSource, focusedItem, selectedItems, allowMultipleSelection, onDataChanged, calculateItemsForNode, itemRender, onNodeUpdate, onFocusedItemChanged, onSelectionChanged, shouldDelayFocusOnEvent }: ITMTreeViewProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
24
25
|
export default TMTreeView;
|
|
25
26
|
export declare const StyledTreeNode: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
26
27
|
$isSelected?: boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { IconChevronDown, IconChevronRight } from '../../helper';
|
|
5
5
|
import { TMColors } from '../../utils/theme';
|
|
6
|
-
const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMultipleSelection, onDataChanged, calculateItemsForNode, itemRender, onNodeUpdate, onFocusedItemChanged, onSelectionChanged }) => {
|
|
6
|
+
const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMultipleSelection, onDataChanged, calculateItemsForNode, itemRender, onNodeUpdate, onFocusedItemChanged, onSelectionChanged, shouldDelayFocusOnEvent }) => {
|
|
7
7
|
useEffect(() => {
|
|
8
8
|
const handleKeyDown = (event) => {
|
|
9
9
|
if (!focusedItem)
|
|
@@ -102,10 +102,53 @@ const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMul
|
|
|
102
102
|
onDataChanged?.(updatedData);
|
|
103
103
|
});
|
|
104
104
|
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
// Ref per distinguere click singolo/doppio sulla riga (esempio TMDcmtIcon)
|
|
106
|
+
const clickTimeoutRef = useRef(null);
|
|
107
|
+
const lastClickedNodeKeyRef = useRef(undefined);
|
|
108
|
+
const handleNodeClick = useCallback((node, e) => {
|
|
109
|
+
const targetElement = e.target;
|
|
110
|
+
// Se il click non è sulla riga (es. click sul paddiing esterno), ignora.
|
|
111
|
+
if (e.currentTarget !== targetElement && !e.currentTarget.contains(targetElement)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Se shouldDelayFocusOnEvent non è fornita, il comportamento predefinito è il focus immediato (false)
|
|
115
|
+
const delayFocus = shouldDelayFocusOnEvent ? shouldDelayFocusOnEvent(node, e) : false;
|
|
116
|
+
// Reset del timer se il click è su un nodo diverso dal precedente
|
|
117
|
+
if (lastClickedNodeKeyRef.current && lastClickedNodeKeyRef.current !== node.key) {
|
|
118
|
+
if (clickTimeoutRef.current) {
|
|
119
|
+
clearTimeout(clickTimeoutRef.current);
|
|
120
|
+
}
|
|
121
|
+
clickTimeoutRef.current = null;
|
|
122
|
+
}
|
|
123
|
+
if (delayFocus) {
|
|
124
|
+
// Logica per il ritardo del focus (es. click singolo su icona desktop, gestito dal padre)
|
|
125
|
+
if (clickTimeoutRef.current) {
|
|
126
|
+
// Questo è il secondo click di un potenziale doppio click.
|
|
127
|
+
// Annulla l'azione di focus dal click singolo precedente.
|
|
128
|
+
clearTimeout(clickTimeoutRef.current);
|
|
129
|
+
clickTimeoutRef.current = null;
|
|
130
|
+
lastClickedNodeKeyRef.current = undefined;
|
|
131
|
+
return; // Non attivare onFocusedItemChanged per il doppio click
|
|
132
|
+
}
|
|
133
|
+
// Primo click di un potenziale doppio click o un click singolo che necessita di ritardo
|
|
134
|
+
lastClickedNodeKeyRef.current = node.key;
|
|
135
|
+
clickTimeoutRef.current = setTimeout(() => {
|
|
136
|
+
// Se il timer scade, significa che è stato solo un click singolo.
|
|
137
|
+
onFocusedItemChanged?.(node);
|
|
138
|
+
clickTimeoutRef.current = null;
|
|
139
|
+
lastClickedNodeKeyRef.current = undefined;
|
|
140
|
+
}, 200);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Logica per il focus immediato (per tutti gli altri click, o se shouldDelayFocusOnEvent è false)
|
|
144
|
+
if (clickTimeoutRef.current) {
|
|
145
|
+
clearTimeout(clickTimeoutRef.current);
|
|
146
|
+
clickTimeoutRef.current = null;
|
|
147
|
+
lastClickedNodeKeyRef.current = undefined;
|
|
148
|
+
}
|
|
149
|
+
onFocusedItemChanged?.(node); // Chiama onFocusedItemChanged immediatamente
|
|
150
|
+
}
|
|
151
|
+
}, [onFocusedItemChanged, calculateItemsForNode, onNodeUpdate]);
|
|
109
152
|
const handleCheckboxChange = (node, checked) => {
|
|
110
153
|
let newSelectedItems;
|
|
111
154
|
if (checked) {
|
|
@@ -179,7 +222,7 @@ const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMul
|
|
|
179
222
|
if (input) {
|
|
180
223
|
input.indeterminate = isIndeterminate(node);
|
|
181
224
|
}
|
|
182
|
-
} })), _jsx("div", { style: { display: 'flex', alignItems: 'center', width: '100%', height: '100%' }, onClick: () => {
|
|
225
|
+
} })), _jsx("div", { style: { display: 'flex', alignItems: 'center', width: '100%', height: '100%' }, onClick: (e) => { handleNodeClick(node, e); }, children: itemRender(node) })] }), node.expanded && node.items && (_jsx("div", { style: { paddingLeft: 20, width: '100%' }, children: renderTree(node.items) }))] }, node.key)));
|
|
183
226
|
}, [handleNodeClick, handleNodeToggle, handleCheckboxChange, focusedItem, selectedItems, allowMultipleSelection]);
|
|
184
227
|
return (_jsx("div", { style: { height: '100%', width: '100%', overflowY: 'auto', padding: '2px 5px 2px 2px' }, children: renderTree(dataSource) }));
|
|
185
228
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { getFileIcon } from '../../../helper';
|
|
5
5
|
import TMTooltip from '../../base/TMTooltip';
|
|
6
6
|
import { DownloadTypes } from '../../../ts';
|
|
7
7
|
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
|
8
|
+
import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
|
|
9
|
+
import { TMWaitPanel } from '../../base/TMWaitPanel';
|
|
8
10
|
const StyledCellRenderDcmtIcon = styled.div `
|
|
9
11
|
display: flex;
|
|
10
12
|
flex-direction: row;
|
|
@@ -14,24 +16,40 @@ const StyledCellRenderDcmtIcon = styled.div `
|
|
|
14
16
|
position: relative;
|
|
15
17
|
`;
|
|
16
18
|
const TMDcmtIcon = ({ fileExtension, fileCount, isLexProt, isSigned, isMail, isShared, tid, did, downloadMode = "none", onDownloadDcmtsAsync }) => {
|
|
19
|
+
const { downloadDcmtsAsync, showWaitPanel, waitPanelTitle, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, abortController } = useDcmtOperations();
|
|
17
20
|
const deviceType = useDeviceType();
|
|
18
21
|
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
|
22
|
+
const icon = useMemo(() => {
|
|
23
|
+
return getFileIcon(isMail == 2 ? "PEC" : fileExtension, fileCount);
|
|
24
|
+
}, [fileExtension, fileCount, isMail]);
|
|
25
|
+
// Gestore unificato per l'azione di download (click singolo su mobile, doppio click su desktop)
|
|
26
|
+
const handleDownloadAction = useCallback(async (e) => {
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
if (tid && did) {
|
|
30
|
+
let dcmt = [{ TID: tid, DID: did, FILEEXT: fileExtension }];
|
|
31
|
+
// Usa la funzione passata tramite prop se disponibile, altrimenti usa l'hook interno
|
|
32
|
+
if (onDownloadDcmtsAsync) {
|
|
33
|
+
await onDownloadDcmtsAsync?.(dcmt, DownloadTypes.Dcmt, downloadMode);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
await downloadDcmtsAsync(dcmt, DownloadTypes.Dcmt, downloadMode);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, [tid, did, downloadMode, onDownloadDcmtsAsync, downloadDcmtsAsync]);
|
|
19
40
|
const isDownloadable = downloadMode !== "none" &&
|
|
20
41
|
tid !== undefined &&
|
|
21
42
|
did !== undefined &&
|
|
22
|
-
|
|
23
|
-
const handleClick = async (e) => {
|
|
24
|
-
if (isDownloadable) {
|
|
25
|
-
e.stopPropagation();
|
|
26
|
-
let dcmt = [{ TID: tid, DID: did, FILEEXT: fileExtension }];
|
|
27
|
-
await onDownloadDcmtsAsync?.(dcmt, DownloadTypes.Dcmt, downloadMode);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
43
|
+
fileCount && fileCount > 0;
|
|
30
44
|
return (_jsxs(StyledCellRenderDcmtIcon, { className: "tm-dcmt-icon", style: {
|
|
31
45
|
cursor: isMobile
|
|
32
46
|
? (isDownloadable ? "pointer" : undefined)
|
|
33
47
|
: (isDownloadable ? "default" : undefined)
|
|
34
|
-
},
|
|
48
|
+
}, ...(deviceType === DeviceType.MOBILE
|
|
49
|
+
? { onClick: handleDownloadAction } // Su mobile, un singolo click sull'icona avvia il download
|
|
50
|
+
: { onDoubleClick: handleDownloadAction } // Su desktop, un doppio click sull'icona avvia il download
|
|
51
|
+
), children: [icon, isLexProt == 1 && _jsx("div", { style: { position: 'absolute', left: '-7px', top: isShared ? undefined : '2px' }, children: _jsx(TMTooltip, { content: "Protezione LEX", children: _jsx(IconLexProtLock, { color: 'blue', fontSize: 13 }) }) }), isShared == 1 && _jsx("div", { style: { position: 'absolute', top: '-7px', left: '-5px' }, children: _jsx(TMTooltip, { content: "Documento condiviso", children: _jsx(IconShared, { fontSize: 16 }) }) }), isSigned == 1 && _jsx("div", { style: { position: 'absolute', bottom: '-4px', right: '-7px' }, children: _jsx(TMTooltip, { content: "Documento firmato", children: _jsx(IconSignature, { fontSize: 28 }) }) }), showWaitPanel &&
|
|
52
|
+
_jsx(TMWaitPanel, { title: waitPanelTitle, showSecondary: showSecondary, textSecondary: waitPanelTextSecondary, valueSecondary: waitPanelValueSecondary, maxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, onAbortClick: (abortController) => { setTimeout(() => { abortController?.abort(); }, 1000); } })] }));
|
|
35
53
|
};
|
|
36
54
|
export default TMDcmtIcon;
|
|
37
55
|
function IconLexProtLock(props) {
|
|
@@ -11,7 +11,7 @@ import { FormModes, SearchResultContext } from '../../../ts';
|
|
|
11
11
|
import { TMColors } from '../../../utils/theme';
|
|
12
12
|
import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
|
|
13
13
|
import ShowAlert from '../../base/TMAlert';
|
|
14
|
-
import { DeviceType } from '../../base/TMDeviceProvider';
|
|
14
|
+
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
|
15
15
|
import { TMExceptionBoxManager } from '../../base/TMPopUp';
|
|
16
16
|
import TMSpinner from '../../base/TMSpinner';
|
|
17
17
|
import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
|
|
@@ -223,34 +223,9 @@ const TMMasterDetailDcmts = ({ deviceType, inputDcmts, isForMaster, showCurrentD
|
|
|
223
223
|
const renderItem = useCallback((itemData) => {
|
|
224
224
|
return (!itemData ? _jsx(_Fragment, {}) :
|
|
225
225
|
_jsxs("div", { style: { minWidth: '90px', width: '100%', display: 'flex', marginLeft: itemData.did ? '5px' : '0px', gap: itemData.did ? '15px' : '10px', alignItems: 'center', opacity: !itemData.isDcmt && (itemData.items?.length ?? 0) <= 0 ? 0.5 : 1 }, children: [itemData.did && showCurrentDcmtIndicator && itemData.did === inputDcmts?.[0].DID ? _jsx(IconBackhandIndexPointingRight, { fontSize: 22, overflow: 'visible' }) : _jsx(_Fragment, {}), itemData.did
|
|
226
|
-
? _jsx(TMDcmtIcon, { fileExtension: itemData.values?.FILEEXT?.value, fileCount: itemData.values?.FILECOUNT?.value, isLexProt: itemData.values?.IsLexProt?.value, isMail: itemData.values?.ISMAIL?.value, isShared: itemData.values?.ISSHARED?.value, isSigned: itemData.values?.ISSIGNED?.value })
|
|
226
|
+
? _jsx(TMDcmtIcon, { tid: itemData.values?.TID.value, did: itemData.values?.DID.value, fileExtension: itemData.values?.FILEEXT?.value, fileCount: itemData.values?.FILECOUNT?.value, isLexProt: itemData.values?.IsLexProt?.value, isMail: itemData.values?.ISMAIL?.value, isShared: itemData.values?.ISSHARED?.value, isSigned: itemData.values?.ISSIGNED?.value, downloadMode: 'openInNewWindow' })
|
|
227
227
|
: itemData.dtd && _jsx(TMDcmtTypeTooltip, { dtd: itemData.dtd, children: _jsx("div", { style: { position: 'relative' }, children: _jsx(IconFolder, { color: itemData.isManyToMany ? '#ff8f44' : TMColors.iconLight, fontSize: 24 }) }) }), itemData.did
|
|
228
228
|
?
|
|
229
|
-
// <TMTooltip content={
|
|
230
|
-
// <StyledTooltipContainer style={{ gap: '3px' }}>
|
|
231
|
-
// <TMTidViewer tid={itemData.tid} showIcon showId />
|
|
232
|
-
// <StyledTooltipSeparatorItem />
|
|
233
|
-
// {
|
|
234
|
-
// getMetadataKeys(itemData.values).map((key, index) => {
|
|
235
|
-
// let md = itemData.values?.[key].md as MetadataDescriptor;
|
|
236
|
-
// let value = itemData.values?.[key].value;
|
|
237
|
-
// return (
|
|
238
|
-
// <StyledDivHorizontal key={`${key}_${index}`} style={{ gap: '3px' }} >
|
|
239
|
-
// <p style={{ fontSize: '1rem' }}>{`${key}: `}</p>
|
|
240
|
-
// {
|
|
241
|
-
// md.dataDomain == MetadataDataDomains.DataList ?
|
|
242
|
-
// <TMDataListItemViewer dataListId={md.dataListID} viewMode={md.dataListViewMode} value={value} />
|
|
243
|
-
// : md.dataDomain == MetadataDataDomains.UserID ?
|
|
244
|
-
// <TMUserIdViewer userId={value as number} showIcon noneSelectionText='' />
|
|
245
|
-
// :
|
|
246
|
-
// <p style={{ fontSize: '1rem', fontWeight: 600 }}>{value}</p>
|
|
247
|
-
// }
|
|
248
|
-
// </StyledDivHorizontal>
|
|
249
|
-
// )
|
|
250
|
-
// })
|
|
251
|
-
// }
|
|
252
|
-
// </StyledTooltipContainer>
|
|
253
|
-
// }>
|
|
254
229
|
_jsx(StyledDivHorizontal, { style: { whiteSpace: 'nowrap', textOverflow: 'ellipsis' }, children: getDcmtDisplayValue(itemData.values).map((key, index) => {
|
|
255
230
|
let md = itemData.values?.[key].md;
|
|
256
231
|
let value = itemData.values?.[key].value;
|
|
@@ -261,7 +236,6 @@ const TMMasterDetailDcmts = ({ deviceType, inputDcmts, isForMaster, showCurrentD
|
|
|
261
236
|
:
|
|
262
237
|
_jsx("p", { style: { fontSize: '1rem' }, children: value })] }, `${key}_${index}`));
|
|
263
238
|
}) })
|
|
264
|
-
// </TMTooltip>
|
|
265
239
|
: _jsxs(StyledDivHorizontal, { style: { gap: 5, overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }, children: [_jsx("p", { style: { whiteSpace: 'nowrap', textOverflow: 'ellipsis' }, children: itemData.name }), _jsx(StyledBadge, { "$backgroundColor": TMColors.info, children: itemData.items?.length })] })] }));
|
|
266
240
|
}, []);
|
|
267
241
|
/** Function for calculate related documents */
|
|
@@ -565,6 +539,17 @@ export function IconBackhandIndexPointingRight(props) {
|
|
|
565
539
|
}
|
|
566
540
|
const TMTreeViewWrapper = ({ data, allowMultipleSelection, focusedItem, selectedItems, renderItem, calculateItemsForNode, onFocusedItemChanged, onSelectionChanged, onDataChanged }) => {
|
|
567
541
|
const { setPanelVisibilityById, setToolbarButtonVisibility } = useTMPanelManagerContext();
|
|
542
|
+
const deviceType = useDeviceType();
|
|
543
|
+
// Determina se il focus deve essere ritardato in base all'elemento cliccato
|
|
544
|
+
const handleShouldDelayFocusOnEvent = useCallback((node, event) => {
|
|
545
|
+
const targetElement = event.target;
|
|
546
|
+
// Se il click è sull'icona TMDcmtIcon (identificata dalla sua classe CSS)
|
|
547
|
+
// E siamo su un dispositivo NON mobile (su mobile, TMDcmtIcon gestisce già il click con stopPropagation)
|
|
548
|
+
if (targetElement.closest('.tm-dcmt-icon') && deviceType !== DeviceType.MOBILE) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
return false;
|
|
552
|
+
}, [deviceType]);
|
|
568
553
|
return (_jsx(TMTreeView, { dataSource: data, allowMultipleSelection: allowMultipleSelection, calculateItemsForNode: calculateItemsForNode, itemRender: renderItem, focusedItem: focusedItem, selectedItems: selectedItems, onFocusedItemChanged: (item) => {
|
|
569
554
|
onFocusedItemChanged?.(item);
|
|
570
555
|
if (item?.isDcmt) {
|
|
@@ -579,7 +564,7 @@ const TMTreeViewWrapper = ({ data, allowMultipleSelection, focusedItem, selected
|
|
|
579
564
|
setToolbarButtonVisibility('tmSearchResult', true);
|
|
580
565
|
setToolbarButtonVisibility('tmDcmtForm', false);
|
|
581
566
|
}
|
|
582
|
-
}, onSelectionChanged: onSelectionChanged, onDataChanged: onDataChanged }));
|
|
567
|
+
}, onSelectionChanged: onSelectionChanged, onDataChanged: onDataChanged, shouldDelayFocusOnEvent: handleShouldDelayFocusOnEvent }));
|
|
583
568
|
};
|
|
584
569
|
const TMFormOrResultWrapper = ({ deviceType, focusedItem, onTaskCreateRequest }) => {
|
|
585
570
|
const { setPanelVisibilityById } = useTMPanelManagerContext();
|
|
@@ -79,6 +79,7 @@ export declare class SDKUI_Localizator {
|
|
|
79
79
|
static get CompleteError(): "Kompletter Fehler" | "Complete error" | "Error completo" | "Erreur complète" | "Erro completo" | "Errore completo";
|
|
80
80
|
static get Configure(): "Konfigurieren" | "Configure" | "Configurar" | "Configura";
|
|
81
81
|
static get Confirm(): string;
|
|
82
|
+
static get ConfirmDownload(): string;
|
|
82
83
|
static get ConfirmPassword(): "Bestätige das Passwort" | "Confirm password" | "Confirmar Contraseña" | "Confirmez le mot de passe" | "Confirme sua senha" | "Conferma password";
|
|
83
84
|
static get ConfirmOnCancel(): "Wenn wir fortfahren, gehen die vorgenommenen Änderungen verloren. Fortfahren?" | "All modifications will be lost. Continue?" | "Si sigue adelante, se perderán las modificaciones aportadas. ¿Seguir?" | "Continuant, les changements seront perdus. Continuer?" | "Continuando as alterações feitas serão perdidas. Continuar?" | "Proseguendo le modifiche apportate andranno perse. Proseguire?";
|
|
84
85
|
static get Continue(): string;
|
|
@@ -169,6 +170,7 @@ export declare class SDKUI_Localizator {
|
|
|
169
170
|
static get FEFormats_SDI_HTML(): "SdI Style Sheet (HTML)" | "Hoja de estilo SdI (HTML)" | "Feuille de style SdI (HTML)" | "Folha de estilo SdI (HTML)" | "Foglio di stile SdI (HTML)";
|
|
170
171
|
static get FEFormats_SDI_PDF(): "SdI Style Sheet (PDF)" | "Hoja de estilo SdI (PDF)" | "Feuille de style SdI (PDF)" | "Folha de estilo SdI (PDF)" | "Foglio di stile SdI (PDF)";
|
|
171
172
|
static get FileCheck(): string;
|
|
173
|
+
static get FileExtensionNotViewable(): string;
|
|
172
174
|
static get FileConversion(): string;
|
|
173
175
|
static get FileManager_QuestionAlreadyExistsFile(): "Ziel enthält bereits eine Datei mit der Bezeichnung {{0}}, ersetzen durch die neue Datei?" | "The destination already contains a file called {{0}}, replace with the new file?" | "El destino ya contiene un archivo llamado {{0}}, ¿sustituir con el nuevo archivo?" | "La destination contient déjà un fichier appelé {{0}}, remplacer avec le nouveau fichier?" | "O destino já contém um ficheiro chamado {{0}}, substitua com o novo arquivo?" | "La destinazione contiene già un file denominato {{0}}, sostituire con il nuovo file?";
|
|
174
176
|
static get FileManager_QuestionAlreadyExistsFiles(): "Ziel enthält {{0}} Datei mit dem gleichen Namen, ersetzen durch neue Dateien?" | "Destination contains {{0}} files with the same name, replace with new files?" | "El destino contiene {{0}} archivos con el mismo nombre, ¿sustituir con los nuevos archivos?" | "La destination contient {{0}} fichier portant le même nom, remplacer avec les nouveaux fichiers?" | "O destino contém ficheiros {{0}} com o mesmo nome, substitua por novos arquivos?" | "La destinazione contiene {{0}} file con lo stesso nome, sostituire con i nuovi file?";
|
|
@@ -739,6 +739,16 @@ export class SDKUI_Localizator {
|
|
|
739
739
|
default: return "Conferma";
|
|
740
740
|
}
|
|
741
741
|
}
|
|
742
|
+
static get ConfirmDownload() {
|
|
743
|
+
switch (this._cultureID) {
|
|
744
|
+
case CultureIDs.De_DE: return "Download bestätigen";
|
|
745
|
+
case CultureIDs.En_US: return "Confirm Download";
|
|
746
|
+
case CultureIDs.Es_ES: return "Confirmar Descarga";
|
|
747
|
+
case CultureIDs.Fr_FR: return "Confirmer le téléchargement";
|
|
748
|
+
case CultureIDs.Pt_PT: return "Confirmar Download";
|
|
749
|
+
default: return "Conferma Download";
|
|
750
|
+
}
|
|
751
|
+
}
|
|
742
752
|
static get ConfirmPassword() {
|
|
743
753
|
switch (this._cultureID) {
|
|
744
754
|
case CultureIDs.De_DE: return "Bestätige das Passwort";
|
|
@@ -1651,6 +1661,16 @@ export class SDKUI_Localizator {
|
|
|
1651
1661
|
default: return "Controllo file";
|
|
1652
1662
|
}
|
|
1653
1663
|
}
|
|
1664
|
+
static get FileExtensionNotViewable() {
|
|
1665
|
+
switch (this._cultureID) {
|
|
1666
|
+
case CultureIDs.De_DE: return "Die Datei mit der Erweiterung \"{{0}}\" kann möglicherweise nicht direkt im Browser angezeigt werden. Möchten Sie mit dem Download fortfahren?";
|
|
1667
|
+
case CultureIDs.En_US: return "The file with extension \"{{0}}\" might not be viewable directly in the browser. Do you want to proceed with the download?";
|
|
1668
|
+
case CultureIDs.Es_ES: return "El archivo con extensión \"{{0}}\" podría no ser visible directamente en el navegador. ¿Desea proceder con la descarga?";
|
|
1669
|
+
case CultureIDs.Fr_FR: return "Le fichier avec l'extension \"{{0}}\" pourrait ne pas être visualisable directement dans le navigateur. Voulez-vous procéder au téléchargement ?";
|
|
1670
|
+
case CultureIDs.Pt_PT: return "O arquivo com a extensão \"{{0}}\" pode não ser visível diretamente no navegador. Deseja prosseguir com o download?";
|
|
1671
|
+
default: return "Il file con estensione \"{{0}}\" potrebbe non essere visualizzabile direttamente nel browser. Vuoi procedere con il download?";
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1654
1674
|
static get FileConversion() {
|
|
1655
1675
|
switch (this._cultureID) {
|
|
1656
1676
|
case CultureIDs.De_DE: return "Dateikonvertierung";
|
|
@@ -6,6 +6,7 @@ import { Globalization, getExceptionMessage, dialogConfirmOperation, extensionHa
|
|
|
6
6
|
import { DcmtOperationTypes, DownloadTypes, FileExtensionHandler } from '../ts';
|
|
7
7
|
import { useFileDialog } from './useInputDialog';
|
|
8
8
|
import { isXMLFileExt } from '../helper/dcmtsHelper';
|
|
9
|
+
import { ShowConfirm } from '../components/base/TMConfirm';
|
|
9
10
|
let abortController = new AbortController();
|
|
10
11
|
export function useDcmtOperations() {
|
|
11
12
|
const [showWaitPanel, setShowWaitPanel] = useState(false);
|
|
@@ -24,6 +25,21 @@ export function useDcmtOperations() {
|
|
|
24
25
|
return;
|
|
25
26
|
if (inputDcmts.length <= 0)
|
|
26
27
|
return;
|
|
28
|
+
if (inputDcmts.length === 1 && downloadMode === "openInNewWindow") {
|
|
29
|
+
if (!inputDcmts[0].FILEEXT)
|
|
30
|
+
return;
|
|
31
|
+
let readyToShow = extensionHandler(inputDcmts[0].FILEEXT) === FileExtensionHandler.READY_TO_SHOW;
|
|
32
|
+
if (!readyToShow) {
|
|
33
|
+
const confirmDownload = await ShowConfirm({
|
|
34
|
+
message: SDKUI_Localizator.FileExtensionNotViewable.replaceParams(inputDcmts[0].FILEEXT),
|
|
35
|
+
title: SDKUI_Localizator.ConfirmDownload,
|
|
36
|
+
okButtonText: 'Download',
|
|
37
|
+
cancelButtonText: SDKUI_Localizator.Cancel
|
|
38
|
+
});
|
|
39
|
+
if (!confirmDownload)
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
27
43
|
let operationTitle = 'Download File';
|
|
28
44
|
setShowWaitPanel(true);
|
|
29
45
|
setShowPrimary(inputDcmts.length > 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topconsultnpm/sdkui-react-beta",
|
|
3
|
-
"version": "6.14.
|
|
3
|
+
"version": "6.14.138",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"lib"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@topconsultnpm/sdk-ts-beta": "6.14.
|
|
45
|
+
"@topconsultnpm/sdk-ts-beta": "6.14.20",
|
|
46
46
|
"buffer": "^6.0.3",
|
|
47
47
|
"devextreme": "24.2.6",
|
|
48
48
|
"devextreme-react": "24.2.6",
|