@topconsultnpm/sdkui-react 6.22.0-dev2.28 → 6.22.0-dev2.29
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.
|
@@ -36,6 +36,9 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
36
36
|
const [customContextMenuVisible, setCustomContextMenuVisible] = useState(false);
|
|
37
37
|
const [customContextMenuPosition, setCustomContextMenuPosition] = useState({ x: 0, y: 0 });
|
|
38
38
|
const [customContextMenuRowKey, setCustomContextMenuRowKey] = useState(undefined);
|
|
39
|
+
// true quando il menu contestuale viene aperto su spazio vuoto senza alcuna riga di riferimento:
|
|
40
|
+
// in tal caso i comandi legati alla riga (singleRow/multiRow) devono risultare disabilitati
|
|
41
|
+
const [customContextMenuOnEmptyArea, setCustomContextMenuOnEmptyArea] = useState(false);
|
|
39
42
|
const gridContainerRef = useRef(null);
|
|
40
43
|
// Ref per evitare focus ripetuti sulla search panel
|
|
41
44
|
const searchPanelFocusedRef = useRef(false);
|
|
@@ -76,16 +79,17 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
76
79
|
const rowKey = internalRef.current.instance().getKeyByRowIndex(rowIndex);
|
|
77
80
|
internalRef.current.instance().option('focusedRowKey', rowKey);
|
|
78
81
|
setCustomContextMenuRowKey(rowKey);
|
|
82
|
+
setCustomContextMenuOnEmptyArea(false);
|
|
79
83
|
}
|
|
80
84
|
else {
|
|
81
85
|
// Right-click su spazio vuoto: ricade sulla riga focalizzata solo se esiste ancora.
|
|
82
|
-
//
|
|
83
|
-
//
|
|
86
|
+
// Se non c'è una riga focalizzata (es. primo click dopo l'apertura) o se focusedRowKey punta a
|
|
87
|
+
// una riga non più presente (dopo un'eliminazione), il menu viene comunque mostrato ma senza
|
|
88
|
+
// riga di riferimento: i comandi di riga restano disabilitati, quelli generali utilizzabili
|
|
84
89
|
const grid = internalRef.current?.instance();
|
|
85
90
|
const focusedRowExists = focusedRowKey !== undefined && !!grid && grid.getRowIndexByKey(focusedRowKey) >= 0;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
setCustomContextMenuRowKey(focusedRowKey);
|
|
91
|
+
setCustomContextMenuRowKey(focusedRowExists ? focusedRowKey : undefined);
|
|
92
|
+
setCustomContextMenuOnEmptyArea(!focusedRowExists);
|
|
89
93
|
}
|
|
90
94
|
setCustomContextMenuVisible(true);
|
|
91
95
|
setCustomContextMenuPosition({ x: e.clientX, y: e.clientY });
|
|
@@ -122,6 +126,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
122
126
|
setCustomContextMenuVisible(true);
|
|
123
127
|
setCustomContextMenuPosition({ x: clientX, y: clientY });
|
|
124
128
|
setCustomContextMenuRowKey(rowKey);
|
|
129
|
+
setCustomContextMenuOnEmptyArea(false);
|
|
125
130
|
},
|
|
126
131
|
});
|
|
127
132
|
// Creating a ref to store the timestamp of the last selection change
|
|
@@ -230,7 +235,8 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
230
235
|
return items.map(item => {
|
|
231
236
|
let disabled = item.disabled ?? false;
|
|
232
237
|
let disabledCalculation = false;
|
|
233
|
-
|
|
238
|
+
// Menu aperto su spazio vuoto senza riga di riferimento: nessun id, i comandi di riga sono disabilitati
|
|
239
|
+
const id = customContextMenuOnEmptyArea ? undefined : (focusedRowEnabled ? focusedRowKey : rowID);
|
|
234
240
|
if (item.operationType === 'singleRow') {
|
|
235
241
|
disabledCalculation = selectedRowKeys.length > 1 || id === undefined;
|
|
236
242
|
}
|
|
@@ -245,7 +251,10 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
245
251
|
if (item.operationType === 'singleRow' && id !== undefined) {
|
|
246
252
|
originalOnClick(id);
|
|
247
253
|
}
|
|
248
|
-
else if (item.operationType === 'multiRow' && id !== undefined) {
|
|
254
|
+
else if (item.operationType === 'multiRow' && (selectedRowKeys.length > 0 || id !== undefined)) {
|
|
255
|
+
// Condizione allineata a quella di disabilitazione: con righe selezionate il comando è
|
|
256
|
+
// attivo anche senza riga di riferimento (es. menu aperto su spazio vuoto), quindi qui
|
|
257
|
+
// devono comunque essere passate le chiavi selezionate e non un onClick senza argomenti
|
|
249
258
|
if (selectedRowKeys.length > 0) {
|
|
250
259
|
originalOnClick(selectedRowKeys);
|
|
251
260
|
}
|
|
@@ -260,7 +269,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
260
269
|
submenu: item.submenu ? processCustomContextMenuItems(item.submenu, id) : undefined,
|
|
261
270
|
};
|
|
262
271
|
});
|
|
263
|
-
}, [focusedRowEnabled, focusedRowKey, selectedRowKeys]);
|
|
272
|
+
}, [focusedRowEnabled, focusedRowKey, selectedRowKeys, customContextMenuOnEmptyArea]);
|
|
264
273
|
// Gestisce la preparazione del menu contestuale della griglia (header e righe)
|
|
265
274
|
const onContextMenuPreparingCallback = useCallback((e) => {
|
|
266
275
|
if (e === undefined)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { extractTextsFromDirectory, findFileItems, setFolderTreeViewItems } from "./TMFileManagerUtils";
|
|
3
|
+
import { extractTextsFromDirectory, findFileItems, resolveFileItemUserNames, setFolderTreeViewItems } from "./TMFileManagerUtils";
|
|
4
4
|
import { DeviceType, useDeviceType } from "./TMDeviceProvider";
|
|
5
5
|
import { formatBytes, Globalization, IconDashboard, IconFolder, IconHide, IconList, IconMenuVertical, IconRefresh, IconShow, SDKUI_Globals, SDKUI_Localizator, TMCommandsContextMenu, TMConditionalWrapper } from "../../helper";
|
|
6
6
|
import TMContextMenu from '../NewComponents/ContextMenu/TMContextMenu';
|
|
@@ -91,10 +91,12 @@ const TMFileManager = (props) => {
|
|
|
91
91
|
(Globalization.getDateTimeDisplayValue(item.lastUpdateTime)?.toLowerCase().toString().includes(searchText.toLowerCase().trim())) ||
|
|
92
92
|
(Globalization.getDateTimeDisplayValue(item.creationTime)?.toLowerCase().toString().includes(searchText.toLowerCase().trim())) ||
|
|
93
93
|
(item.version?.toString().includes(searchText.toLowerCase().trim()))));
|
|
94
|
-
|
|
94
|
+
// I nomi utente possono mancare quando la lista viene ricostruita senza i partecipanti
|
|
95
|
+
// (es. refresh dopo la condivisione di un file): vengono risolti qui dagli ID
|
|
96
|
+
setFilteredFileItems(filtered.map(item => resolveFileItemUserNames(item, allUsers)));
|
|
95
97
|
};
|
|
96
98
|
filterItems();
|
|
97
|
-
}, [searchText, selectedFolder]);
|
|
99
|
+
}, [searchText, selectedFolder, allUsers]);
|
|
98
100
|
// Callback to handle the "back" action (closing the draft list view)
|
|
99
101
|
const onBackCallback = useCallback(() => {
|
|
100
102
|
setOpenDraftList(false);
|
|
@@ -26,6 +26,14 @@ export interface FileItem {
|
|
|
26
26
|
version?: number;
|
|
27
27
|
isSigned?: number;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Completa i nomi utente (autore e utente di check-out) di un FileItem quando mancano, risolvendoli dagli ID.
|
|
31
|
+
* buildFolderHierarchy popola i nomi solo se riceve la lista dei partecipanti: nei refresh parziali
|
|
32
|
+
* (es. dopo la condivisione di un nuovo file) i partecipanti possono non essere disponibili e i nomi
|
|
33
|
+
* arrivano vuoti, svuotando la colonna Autore fino al ricaricamento completo.
|
|
34
|
+
* Restituisce lo stesso oggetto se non c'è nulla da risolvere, per non invalidare le reference inutilmente.
|
|
35
|
+
*/
|
|
36
|
+
export declare const resolveFileItemUserNames: (item: FileItem, users: Array<UserDescriptor>) => FileItem;
|
|
29
37
|
export interface TMFileManagerTreeViewDirectory {
|
|
30
38
|
id: number;
|
|
31
39
|
text: string;
|
|
@@ -12,6 +12,28 @@ export const isSigned = (ext) => {
|
|
|
12
12
|
const parts = normalized.split('.');
|
|
13
13
|
return parts.some(part => SIGNATURE_EXTENSIONS.has(part));
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Completa i nomi utente (autore e utente di check-out) di un FileItem quando mancano, risolvendoli dagli ID.
|
|
17
|
+
* buildFolderHierarchy popola i nomi solo se riceve la lista dei partecipanti: nei refresh parziali
|
|
18
|
+
* (es. dopo la condivisione di un nuovo file) i partecipanti possono non essere disponibili e i nomi
|
|
19
|
+
* arrivano vuoti, svuotando la colonna Autore fino al ricaricamento completo.
|
|
20
|
+
* Restituisce lo stesso oggetto se non c'è nulla da risolvere, per non invalidare le reference inutilmente.
|
|
21
|
+
*/
|
|
22
|
+
export const resolveFileItemUserNames = (item, users) => {
|
|
23
|
+
if (!users || users.length === 0)
|
|
24
|
+
return item;
|
|
25
|
+
const isValidUserID = (id) => id !== undefined && Number.isFinite(id) && id > 0;
|
|
26
|
+
const needsUpdaterName = !item.updaterName && isValidUserID(item.updaterID);
|
|
27
|
+
const needsCheckOutUserName = !item.checkOutUserName && isValidUserID(item.checkOutUserID);
|
|
28
|
+
if (!needsUpdaterName && !needsCheckOutUserName)
|
|
29
|
+
return item;
|
|
30
|
+
const resolved = { ...item };
|
|
31
|
+
if (needsUpdaterName)
|
|
32
|
+
resolved.updaterName = users.find(user => user.id === item.updaterID)?.name ?? item.updaterName;
|
|
33
|
+
if (needsCheckOutUserName)
|
|
34
|
+
resolved.checkOutUserName = users.find(user => user.id === item.checkOutUserID)?.name ?? item.checkOutUserName;
|
|
35
|
+
return resolved;
|
|
36
|
+
};
|
|
15
37
|
export var TMFileManagerPageSize;
|
|
16
38
|
(function (TMFileManagerPageSize) {
|
|
17
39
|
TMFileManagerPageSize[TMFileManagerPageSize["Small"] = 30] = "Small";
|