@topconsultnpm/sdkui-react 6.22.0-dev2.22 → 6.22.0-dev2.24
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/TMDataGrid.js +1 -1
- package/lib/components/features/documents/TMMasterInfoFields.js +10 -4
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +27 -13
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +9 -6
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +24 -13
- package/lib/components/layout/panelManager/TMPanelManagerContext.js +0 -1
- package/lib/helper/SDKUI_Globals.d.ts +10 -5
- package/lib/helper/SDKUI_Globals.js +12 -13
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +2 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +7 -1
- package/package.json +1 -1
|
@@ -386,7 +386,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
386
386
|
// Temporary: iOS-specific styles to prevent default long-press menu and text selection
|
|
387
387
|
WebkitTouchCallout: 'none',
|
|
388
388
|
WebkitUserSelect: 'none'
|
|
389
|
-
}, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), effectivePager && _jsx(Pager, { ...effectivePager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { target: ".dx-data-row", items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
|
|
389
|
+
}, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter, search: { enabled: true, ...headerFilter?.search } }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), effectivePager && _jsx(Pager, { ...effectivePager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { target: ".dx-data-row", items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
|
|
390
390
|
visible: customContextMenuVisible,
|
|
391
391
|
position: customContextMenuPosition,
|
|
392
392
|
onClose: () => setCustomContextMenuVisible(false)
|
|
@@ -13,11 +13,17 @@ const styles = {
|
|
|
13
13
|
wrapper: { display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0, borderTop: `1px solid ${TMColors.card_background}` },
|
|
14
14
|
body: { flex: 1, minHeight: 0, padding: '13px 10px 9px', display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(185px, 1fr))', gridAutoRows: 'max-content', alignContent: 'start', columnGap: '10px', rowGap: '13px', overflow: 'auto' },
|
|
15
15
|
item: { position: 'relative', display: 'flex', alignItems: 'center', minWidth: 0, minHeight: '30px', padding: '5px 10px 4px', borderRadius: '8px', border: `1px solid ${TMColors.card_background}`, background: TMColors.default_background, transition: 'border-color 0.15s ease, box-shadow 0.15s ease' },
|
|
16
|
-
|
|
16
|
+
// Come per il pulsante copia, la posizione sta sull'ancora del tooltip; il troncamento con i
|
|
17
|
+
// puntini deve invece stare sull'elemento che contiene il testo
|
|
18
|
+
label: { position: 'absolute', top: '-7px', left: '8px', maxWidth: 'calc(100% - 16px)', minWidth: 0, padding: '0 5px', background: TMColors.default_background },
|
|
19
|
+
labelText: { maxWidth: '100%', fontSize: '9px', fontWeight: 700, lineHeight: '13px', letterSpacing: '0.5px', textTransform: 'uppercase', color: TMColors.label_normal, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' },
|
|
17
20
|
value: { display: 'block', width: '100%', fontSize: '12.5px', fontWeight: 500, color: TMColors.text_normal, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', userSelect: 'text', cursor: 'text', minHeight: '17px', lineHeight: '17px' },
|
|
18
21
|
emptyValue: { color: TMColors.disabled, fontWeight: 400 },
|
|
19
22
|
// Pulsante di copia: pastiglia sul bordo superiore della card, come la caption
|
|
20
|
-
|
|
23
|
+
// La posizione sta sull'ancora e non sul pulsante: è il contenitore del tooltip, che deve stare
|
|
24
|
+
// dove si vede il pulsante, altrimenti il tooltip si aprirebbe altrove
|
|
25
|
+
copyBtnAnchor: { position: 'absolute', top: '-9px', right: '6px', transition: 'opacity 0.15s ease' },
|
|
26
|
+
copyBtn: { display: 'flex', alignItems: 'center', justifyContent: 'center', width: '18px', height: '18px', padding: 0, borderRadius: '50%', border: `1px solid ${TMColors.card_background}`, background: TMColors.default_background, color: TMColors.label_normal, cursor: 'pointer', transition: 'background 0.15s ease, color 0.15s ease, border-color 0.15s ease' },
|
|
21
27
|
placeholder: { gridColumn: '1 / -1', padding: '6px 2px', color: TMColors.label_normal, fontSize: '12px' },
|
|
22
28
|
// Barra di ricerca
|
|
23
29
|
searchBar: { display: 'flex', alignItems: 'center', gap: '8px', padding: '12px 14px 0', flexShrink: 0 },
|
|
@@ -58,7 +64,7 @@ const MetadataCard = ({ caption, copyText, children }) => {
|
|
|
58
64
|
};
|
|
59
65
|
// Il pulsante resta visibile durante il feedback e quando ha il focus (accessibilità da tastiera)
|
|
60
66
|
const isCopyVisible = copyText !== '' && (!SUPPORTS_HOVER || isHovered || isCopied);
|
|
61
|
-
return (_jsxs("div", { style: styles.item, onMouseEnter: (e) => { setIsHovered(true); Object.assign(e.currentTarget.style, { borderColor: TMColors.primary, boxShadow: '0 2px 8px rgba(37, 89, 165, 0.14)' }); }, onMouseLeave: (e) => { setIsHovered(false); Object.assign(e.currentTarget.style, { borderColor: TMColors.card_background, boxShadow: 'none' }); }, children: [_jsx(
|
|
67
|
+
return (_jsxs("div", { style: styles.item, onMouseEnter: (e) => { setIsHovered(true); Object.assign(e.currentTarget.style, { borderColor: TMColors.primary, boxShadow: '0 2px 8px rgba(37, 89, 165, 0.14)' }); }, onMouseLeave: (e) => { setIsHovered(false); Object.assign(e.currentTarget.style, { borderColor: TMColors.card_background, boxShadow: 'none' }); }, children: [_jsx(TMTooltip, { content: caption, parentStyle: { ...styles.label, maxWidth: isCopyVisible ? 'calc(100% - 38px)' : 'calc(100% - 16px)' }, childStyle: { maxWidth: '100%', minWidth: 0 }, children: _jsx("span", { style: styles.labelText, children: caption }) }), children, copyText !== '' && (_jsx(TMTooltip, { content: isCopied ? SDKUI_Localizator.CopiedSuccessfully : SDKUI_Localizator.CopyToClipboard, parentStyle: { ...styles.copyBtnAnchor, opacity: isCopyVisible ? 1 : 0, pointerEvents: isCopyVisible ? 'auto' : 'none' }, children: _jsx("button", { type: 'button', style: { ...styles.copyBtn, ...(isCopied ? COPY_BTN_COPIED : COPY_BTN_IDLE) }, "aria-label": SDKUI_Localizator.CopyToClipboard, onClick: copyAsync, onFocus: () => setIsHovered(true), onBlur: () => setIsHovered(false), ...hoverStyle(isCopied ? COPY_BTN_COPIED : COPY_BTN_HOVER, isCopied ? COPY_BTN_COPIED : COPY_BTN_IDLE), children: isCopied ? _jsx(IconSuccess, { fontSize: 11 }) : _jsx(IconCopy, { fontSize: 11 }) }) }))] }));
|
|
62
68
|
};
|
|
63
69
|
// Riepilogo dei metadati di un documento in card compatte
|
|
64
70
|
const TMMasterInfoFields = ({ dtd, dcmt, getValuesByMidAsync, reloadToken, isExpandedView = false }) => {
|
|
@@ -157,7 +163,7 @@ const TMMasterInfoFields = ({ dtd, dcmt, getValuesByMidAsync, reloadToken, isExp
|
|
|
157
163
|
e.preventDefault();
|
|
158
164
|
e.stopPropagation();
|
|
159
165
|
setFilter('');
|
|
160
|
-
} }, onFocus: (e) => Object.assign(e.currentTarget.parentElement?.style ?? {}, { borderColor: TMColors.primary, boxShadow: '0 0 0 2px rgba(37, 89, 165, 0.14)' }), onBlur: (e) => Object.assign(e.currentTarget.parentElement?.style ?? {}, { borderColor: TMColors.card_background, boxShadow: 'none' }) }), filter !== '' && (_jsx("button", { type: 'button', style: styles.searchClear,
|
|
166
|
+
} }, onFocus: (e) => Object.assign(e.currentTarget.parentElement?.style ?? {}, { borderColor: TMColors.primary, boxShadow: '0 0 0 2px rgba(37, 89, 165, 0.14)' }), onBlur: (e) => Object.assign(e.currentTarget.parentElement?.style ?? {}, { borderColor: TMColors.card_background, boxShadow: 'none' }) }), filter !== '' && (_jsx(TMTooltip, { content: SDKUI_Localizator.Clear, parentStyle: { display: 'flex', flexShrink: 0 }, children: _jsx("button", { type: 'button', style: styles.searchClear, "aria-label": SDKUI_Localizator.Clear, onClick: () => setFilter(''), ...hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal }), children: _jsx(IconClearButton, { fontSize: 13 }) }) }))] }), _jsxs("span", { style: styles.searchCount, children: [filteredRows.length, "/", rows.length] })] }));
|
|
161
167
|
// La ricerca sta solo nella vista ingrandita: in linea lo spazio è poco e l'elenco è limitato
|
|
162
168
|
// all'anteprima, con il pulsante per aprirlo tutto
|
|
163
169
|
const hasSearch = isExpandedView && rows.length > PREVIEW_COUNT;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { LayoutModes } from "@topconsultnpm/sdk-ts";
|
|
4
4
|
import { LoadIndicator } from "devextreme-react/load-indicator";
|
|
5
5
|
import { SearchResultContext } from "../../../ts";
|
|
@@ -19,7 +19,7 @@ import TMDcmtForm from "./TMDcmtForm";
|
|
|
19
19
|
import TMDcmtIcon from "./TMDcmtIcon";
|
|
20
20
|
import TMMasterInfoFields from "./TMMasterInfoFields";
|
|
21
21
|
import { MultiDetailArchivePhase, useMultiMasterDetailDcmts } from "../../../hooks/useMultiMasterDetailDcmts";
|
|
22
|
-
import { accordionStyles, getDtdDisplayName, INLINE_EDITABLE_CELL_CLASS, multiDetailStyles, PANEL_GUTTER_PX, renderAccordionHeader, renderSelectDcmtType, renderStepper, SECTION_HEADER_HEIGHT_PX, sectionPanelHeights, TMSectionPanelsHeights, TMSummarySectionSlot } from "./TMMultiMasterDetailDcmtsUtils";
|
|
22
|
+
import { accordionStyles, getDtdDisplayName, INLINE_EDITABLE_CELL_CLASS, multiDetailStyles, PANEL_GUTTER_PX, renderAccordionHeader, renderSelectDcmtType, renderStepper, SECTION_HEADER_HEIGHT_PX, sectionPanelHeights, TMSectionPanelsHeights, TMSummaryResizeTracker, TMSummarySectionSlot } from "./TMMultiMasterDetailDcmtsUtils";
|
|
23
23
|
// Stili delle sezioni in TMMultiMasterDetailDcmtsUtils (vedi accordionStyles e multiDetailStyles)
|
|
24
24
|
const styles = {
|
|
25
25
|
content: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 },
|
|
@@ -46,7 +46,7 @@ const styles = {
|
|
|
46
46
|
const MOBILE_SECTION = 'archiveList';
|
|
47
47
|
const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
48
48
|
const { dtd, masterDcmt, onClose, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showDcmtFormSidebar, showTodoDcmtForm, openFileUploaderPdfEditor, graphometricManager, editPdfForm, openS4TViewer, showToppyDraggableHelpCenter, toppyHelpCenterUsePortal, fetchRemoteCertificates, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onReferenceClick, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, onFileOpened, passToArchiveCallback, openWGsCopyMoveForm } = props;
|
|
49
|
-
const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
|
|
49
|
+
const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, hasSavedLayout, setHasSavedLayout, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
|
|
50
50
|
const deviceType = useDeviceType();
|
|
51
51
|
// Su mobile il panel manager mostra un pannello alla volta: le sezioni si raggiungono dalla sua barra
|
|
52
52
|
const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
|
|
@@ -56,6 +56,16 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
56
56
|
if (isMobile && isSummaryReady)
|
|
57
57
|
openAllSections();
|
|
58
58
|
}, [isMobile, isSummaryReady]);
|
|
59
|
+
// Le altezze sono una scelta dell'utente solo da quando sposta un separatore: quelle calcolate all'apertura
|
|
60
|
+
// o al collasso di una sezione non vanno salvate. Con un layout già salvato lo sono comunque
|
|
61
|
+
const hasUserResizedRef = useRef(false);
|
|
62
|
+
useEffect(() => { hasUserResizedRef.current = hasMultiDetailPanelStates(); }, [layoutResetToken]);
|
|
63
|
+
const persistPanelStatesOnUserResize = useCallback((panelStates) => {
|
|
64
|
+
if (!hasUserResizedRef.current)
|
|
65
|
+
return;
|
|
66
|
+
saveMultiDetailPanelStates(panelStates);
|
|
67
|
+
setHasSavedLayout(true);
|
|
68
|
+
}, []);
|
|
59
69
|
// Ricerca nella griglia dei dettagli da archiviare, come in TMSearch (vedi useDocumentOperations)
|
|
60
70
|
const [showGridSearch, setShowGridSearch] = useState(false);
|
|
61
71
|
const toggleGridSearchItem = {
|
|
@@ -104,7 +114,7 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
104
114
|
allowSorting: false, allowFiltering: false, allowHeaderFiltering: false, allowResizing: false,
|
|
105
115
|
allowReordering: false, allowHiding: false, showInColumnChooser: false, allowEditing: false,
|
|
106
116
|
cssClass: 'tm-row-actions',
|
|
107
|
-
cellRender: (cell) => (_jsxs("div", { style: styles.actionsCell, children: [_jsx("span", { style: styles.actionIcon,
|
|
117
|
+
cellRender: (cell) => (_jsxs("div", { style: styles.actionsCell, children: [_jsx(TMTooltip, { content: SDKUI_Localizator.Update, children: _jsx("span", { style: styles.actionIcon, onClick: () => openEditForm(cell.data), children: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Duplicate, children: _jsx("span", { style: styles.actionIcon, onClick: () => duplicateRow(cell.data), children: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Deletion, children: _jsx("span", { style: styles.actionIcon, onClick: () => deleteRow(cell.data), children: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }) }) })] })),
|
|
108
118
|
};
|
|
109
119
|
return [actionsColumn, iconColumn, ...detailColumns, ...(detailSupportsFile ? [fileColumn] : [])];
|
|
110
120
|
}, [detailColumns, detailSupportsFile]);
|
|
@@ -139,22 +149,24 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
139
149
|
{ name: SDKUI_Localizator.Deletion, icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }), operationType: 'singleRow', beginGroup: true, onClick: (id) => deleteRow(archiveRows.find(r => r.id === id)) },
|
|
140
150
|
toggleGridSearchItem,
|
|
141
151
|
], [archiveRows, showGridSearch]);
|
|
142
|
-
// Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
// Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo.
|
|
153
|
+
// Con il layout di default non c'è niente da cancellare, quindi la voce non compare
|
|
154
|
+
const optionsMenuItems = useMemo(() => hasSavedLayout
|
|
155
|
+
? [{ name: 'Cancella layout', icon: _jsx(IconEraser, { fontSize: 18, color: TMColors.error }), onClick: clearLayout }]
|
|
156
|
+
: [], [clearLayout, hasSavedLayout]);
|
|
146
157
|
// Le opzioni riguardano i pannelli del riepilogo: fuori da quella fase non hanno effetto, e su mobile
|
|
147
158
|
// non c'è un layout da cancellare (i pannelli si vedono uno alla volta, a tutta altezza)
|
|
148
159
|
const renderOptionsMenu = () => {
|
|
149
|
-
|
|
160
|
+
// Senza voci non si mostra il pulsante, per non aprire un menu vuoto
|
|
161
|
+
if (!isSummaryPhase || isMobile || optionsMenuItems.length === 0)
|
|
150
162
|
return undefined;
|
|
151
|
-
return (_jsx(ContextMenu, { trigger: 'left', items: optionsMenuItems, children: _jsx("span", { style: styles.titleMenuIcon,
|
|
163
|
+
return (_jsx(ContextMenu, { trigger: 'left', items: optionsMenuItems, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Options, children: _jsx("span", { style: styles.titleMenuIcon, "aria-label": SDKUI_Localizator.Options, children: _jsx(IconMenuVertical, { fontSize: 16, color: "white" }) }) }) }));
|
|
152
164
|
};
|
|
153
165
|
// ----- Sezione: informazioni del master -----------------------------------------------------
|
|
154
166
|
const renderMasterInfo = (panel) => {
|
|
155
167
|
const isOpen = sectionsOpen.masterInfo;
|
|
156
168
|
const editMasterButton = canUpdateMaster
|
|
157
|
-
? (_jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); setIsOpenMasterForm(true); }, style: accordionStyles.accordionExpandBtn,
|
|
169
|
+
? (_jsx(TMTooltip, { content: `${SDKUI_Localizator.Update}: ${getDtdDisplayName(dtd)}`, children: _jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); setIsOpenMasterForm(true); }, style: accordionStyles.accordionExpandBtn, ...hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal }), children: _jsx(IconEdit, { fontSize: 16 }) }) }))
|
|
158
170
|
: undefined;
|
|
159
171
|
return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
|
|
160
172
|
section: 'masterInfo',
|
|
@@ -172,7 +184,7 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
172
184
|
const isOpen = sectionsOpen.archivedDetails;
|
|
173
185
|
const hasResults = archivedDetailResults.length > 0 && archivedDetailsCount > 0;
|
|
174
186
|
// Ricarica i dettagli già archiviati: la sezione va aperta, altrimenti l'esito non sarebbe visibile
|
|
175
|
-
const refreshArchivedButton = (_jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); setSectionOpen('archivedDetails', true); refreshArchivedDetailsAsync(); }, disabled: isLoadingArchivedDetails, style: { ...accordionStyles.accordionExpandBtn, ...(isLoadingArchivedDetails ? { cursor: 'default', opacity: 0.5 } : {}) },
|
|
187
|
+
const refreshArchivedButton = (_jsx(TMTooltip, { content: SDKUI_Localizator.Refresh, children: _jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); setSectionOpen('archivedDetails', true); refreshArchivedDetailsAsync(); }, disabled: isLoadingArchivedDetails, style: { ...accordionStyles.accordionExpandBtn, ...(isLoadingArchivedDetails ? { cursor: 'default', opacity: 0.5 } : {}) }, "aria-label": SDKUI_Localizator.Refresh, ...(isLoadingArchivedDetails ? {} : hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal })), children: _jsx(IconRefresh, { fontSize: 16 }) }) }));
|
|
176
188
|
return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
|
|
177
189
|
section: 'archivedDetails',
|
|
178
190
|
isOpen,
|
|
@@ -245,7 +257,9 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
245
257
|
// passaggio fra impilato e un pannello alla volta, i pannelli vanno rimontati
|
|
246
258
|
, { panels: panels, initialVisibility: initialVisibility, defaultDimensions: initialDimensions, initialDimensions: initialDimensions, initialMobilePanelId: MOBILE_SECTION,
|
|
247
259
|
// Su mobile il layout è imposto dal dispositivo: salvarlo sovrascriverebbe quello dell'utente
|
|
248
|
-
isPersistenceEnabled: !isMobile,
|
|
260
|
+
isPersistenceEnabled: !isMobile,
|
|
261
|
+
// Le altezze si salvano solo dopo che l'utente ha spostato un separatore (vedi TMSummaryResizeTracker)
|
|
262
|
+
persistPanelStates: !isMobile ? persistPanelStatesOnUserResize : undefined, children: [_jsx(TMSummaryResizeTracker, { onUserResize: () => { hasUserResizedRef.current = true; } }), _jsx(TMSectionPanelsHeights, { sections: sections, sectionsOpen: sectionsOpen }), _jsx(TMPanelManagerContainer, { panels: panels, direction: 'vertical', showToolbar: isMobile, minPanelSizePx: SECTION_HEADER_HEIGHT_PX, gutterSizePx: isMobile ? undefined : PANEL_GUTTER_PX })] }, `${layoutResetToken}-${isMobile}`) }) }));
|
|
249
263
|
};
|
|
250
264
|
const renderArchiveSummary = () => (_jsxs("div", { style: styles.summaryContainer, children: [renderSectionsInPanels(), _jsxs("div", { style: styles.footer, children: [_jsxs("label", { style: styles.footerLabel, ...hoverStyle({ borderColor: TMColors.primary, color: TMColors.text_normal }, { borderColor: TMColors.card_background, color: TMColors.label_normal }), children: [_jsx("input", { type: "checkbox", checked: stopOnFirstError, onChange: (e) => setStopOnFirstError(e.target.checked), style: styles.checkbox }), SDKUI_Localizator.StopOnFirstError] }), _jsxs("button", { type: "button", onClick: archiveAllAsync, disabled: isArchiveDisabled, style: {
|
|
251
265
|
...styles.archiveButton,
|
|
@@ -211,7 +211,6 @@ export declare const multiDetailStyles: {
|
|
|
211
211
|
lineHeight: number;
|
|
212
212
|
};
|
|
213
213
|
};
|
|
214
|
-
/** Stili delle sezioni collassabili: qui perché la testata comune (renderAccordionHeader) vive in questo file */
|
|
215
214
|
export declare const accordionStyles: {
|
|
216
215
|
accordion: {
|
|
217
216
|
display: "flex";
|
|
@@ -251,11 +250,6 @@ export declare const accordionStyles: {
|
|
|
251
250
|
display: "inline-flex";
|
|
252
251
|
alignItems: "center";
|
|
253
252
|
justifyContent: "center";
|
|
254
|
-
minWidth: string;
|
|
255
|
-
height: string;
|
|
256
|
-
padding: string;
|
|
257
|
-
borderRadius: string;
|
|
258
|
-
background: string;
|
|
259
253
|
color: string;
|
|
260
254
|
fontSize: string;
|
|
261
255
|
fontWeight: number;
|
|
@@ -349,6 +343,15 @@ export declare const TMSummarySectionSlot: ({ section, render, onBeforeMaximize
|
|
|
349
343
|
render: (panel: SectionPanelState) => React.ReactNode;
|
|
350
344
|
onBeforeMaximize?: (section: AccordionSection) => void;
|
|
351
345
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
346
|
+
/**
|
|
347
|
+
* Segnala che l'utente ha spostato un separatore: è l'unico caso in cui le altezze dei pannelli sono
|
|
348
|
+
* una sua scelta, quindi la sola da salvare. Le quote calcolate all'apertura del riepilogo o al
|
|
349
|
+
* collasso di una sezione non vanno scritte nel local storage.
|
|
350
|
+
* Vive dentro il provider perché il contesto è leggibile solo lì
|
|
351
|
+
*/
|
|
352
|
+
export declare const TMSummaryResizeTracker: ({ onUserResize }: {
|
|
353
|
+
onUserResize: () => void;
|
|
354
|
+
}) => null;
|
|
352
355
|
/**
|
|
353
356
|
* Tiene allineate le altezze dei pannelli al collasso delle sezioni: il panel manager non
|
|
354
357
|
* conosce il collasso, quindi è qui che l'apertura di una sezione si traduce nelle nuove quote.
|
|
@@ -9,7 +9,11 @@ import TMTooltip from '../../base/TMTooltip';
|
|
|
9
9
|
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
|
10
10
|
import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
|
|
11
11
|
// l'elenco da archiviare è l'informazione più importante, quindi prende la fetta maggiore
|
|
12
|
-
export const SECTION_WEIGHTS = {
|
|
12
|
+
export const SECTION_WEIGHTS = {
|
|
13
|
+
masterInfo: 25,
|
|
14
|
+
archivedDetails: 30,
|
|
15
|
+
archiveList: 45
|
|
16
|
+
};
|
|
13
17
|
export const SECTION_HEADER_HEIGHT_PX = 30;
|
|
14
18
|
// Spessore dei separatori fra i pannelli
|
|
15
19
|
export const PANEL_GUTTER_PX = 10;
|
|
@@ -128,15 +132,13 @@ export const multiDetailStyles = {
|
|
|
128
132
|
typeCardIcon: { width: '34px', height: '34px', borderRadius: '9px', background: TMColors.primary_container, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 },
|
|
129
133
|
typeCardName: { flex: 1, fontSize: '14px', fontWeight: 500, color: TMColors.text_normal, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.3 },
|
|
130
134
|
};
|
|
131
|
-
|
|
135
|
+
// Stili delle sezioni collassabili
|
|
132
136
|
export const accordionStyles = {
|
|
133
137
|
accordion: { display: 'flex', flexDirection: 'column', minHeight: 0, borderRadius: '8px', border: `1px solid ${TMColors.card_background}`, background: TMColors.default_background, overflow: 'hidden' },
|
|
134
|
-
// Sezione dentro un pannello: l'altezza è quella del pannello, che la sezione riempie. Serve `flex`
|
|
135
|
-
// e non `height: 100%`, perché il pannello chiuso ha altezza 0 e vive del suo ingombro minimo
|
|
136
138
|
accordionInPanel: { flex: 1 },
|
|
137
139
|
accordionHeader: { display: 'flex', alignItems: 'center', gap: '8px', padding: '4px 8px', cursor: 'pointer', userSelect: 'none', transition: 'background 0.15s ease', outline: 'none', flexShrink: 0, minHeight: '28px' },
|
|
138
140
|
accordionTitle: { flex: 1, fontSize: '12px', fontWeight: 600, color: TMColors.text_normal, display: 'flex', alignItems: 'center', gap: '6px', minWidth: 0 },
|
|
139
|
-
accordionBadge: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
|
141
|
+
accordionBadge: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: TMColors.primary, fontSize: '13px', fontWeight: 700, flexShrink: 0 },
|
|
140
142
|
accordionListBody: { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', borderTop: `1px solid ${TMColors.card_background}` },
|
|
141
143
|
accordionListPlaceholder: { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center', gap: '10px', padding: '16px', color: TMColors.label_normal, fontSize: '13px' },
|
|
142
144
|
accordionExpandBtn: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '20px', height: '20px', padding: 0, borderRadius: '6px', border: 'none', background: 'transparent', color: TMColors.label_normal, cursor: 'pointer', flexShrink: 0, transition: 'background 0.15s ease, color 0.15s ease' },
|
|
@@ -186,12 +188,10 @@ export const renderAccordionHeader = ({ section, isOpen, tooltip, count, title,
|
|
|
186
188
|
const canMaximize = panel.canMaximize;
|
|
187
189
|
const onToggle = () => onToggleOpen(section, !isOpen);
|
|
188
190
|
const expandTooltip = panel.isMaximized ? SDKUI_Localizator.Minimize : SDKUI_Localizator.ZoomIn;
|
|
189
|
-
return (_jsxs("div", { role: canToggle ? 'button' : undefined, tabIndex: canToggle ? 0 : undefined, "aria-expanded": canToggle ? isOpen : undefined, style: { ...accordionStyles.accordionHeader, background: restBackground, ...(canToggle ? {} : { cursor: 'default' }) }, onClick: canToggle ? onToggle : undefined,
|
|
190
|
-
// Doppio click sulla testata: ingrandisce e riduce, come nei pannelli del prodotto
|
|
191
|
-
onDoubleClick: canMaximize ? () => panel.toggleMaximize() : undefined, onKeyDown: canToggle ? (e) => { if (e.key === 'Enter' || e.key === ' ') {
|
|
191
|
+
return (_jsxs("div", { role: canToggle ? 'button' : undefined, tabIndex: canToggle ? 0 : undefined, "aria-expanded": canToggle ? isOpen : undefined, style: { ...accordionStyles.accordionHeader, background: restBackground, ...(canToggle ? {} : { cursor: 'default' }) }, onClick: canToggle ? onToggle : undefined, onDoubleClick: canMaximize ? () => panel.toggleMaximize() : undefined, onKeyDown: canToggle ? (e) => { if (e.key === 'Enter' || e.key === ' ') {
|
|
192
192
|
e.preventDefault();
|
|
193
193
|
onToggle();
|
|
194
|
-
} } : undefined,
|
|
194
|
+
} } : undefined, ...(canToggle ? hoverStyle({ background: TMColors.primary_container }, { background: restBackground }) : {}), children: [_jsx(TMTooltip, { content: canToggle ? tooltip : undefined, parentStyle: { flex: 1, minWidth: 0 }, childStyle: { maxWidth: '100%' }, children: _jsxs("span", { style: accordionStyles.accordionTitle, children: [title, _jsx("span", { style: accordionStyles.accordionBadge, children: count })] }) }), actions, canMaximize && (_jsx(TMTooltip, { content: expandTooltip, children: _jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); panel.toggleMaximize(); }, style: accordionStyles.accordionExpandBtn, "aria-label": expandTooltip, ...hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal }), children: panel.isMaximized ? _jsx(IconWindowMinimize, { fontSize: 14 }) : _jsx(IconWindowMaximize, { fontSize: 14 }) }) })), canToggle && (_jsx(IconChevronRight, { fontSize: 16, color: TMColors.label_normal, style: { transform: isOpen ? 'rotate(90deg)' : 'none', transition: 'transform 0.2s ease', flexShrink: 0 } }))] }));
|
|
195
195
|
};
|
|
196
196
|
/**
|
|
197
197
|
* Contenuto di un pannello: il contesto del panel manager è leggibile solo dentro l'albero del
|
|
@@ -213,6 +213,20 @@ export const TMSummarySectionSlot = ({ section, render, onBeforeMaximize }) => {
|
|
|
213
213
|
};
|
|
214
214
|
return _jsx(_Fragment, { children: render({ isMaximized, canMaximize: !isMobile, canCollapse: !isMobile, toggleMaximize: toggleMaximizeSection }) });
|
|
215
215
|
};
|
|
216
|
+
/**
|
|
217
|
+
* Segnala che l'utente ha spostato un separatore: è l'unico caso in cui le altezze dei pannelli sono
|
|
218
|
+
* una sua scelta, quindi la sola da salvare. Le quote calcolate all'apertura del riepilogo o al
|
|
219
|
+
* collasso di una sezione non vanno scritte nel local storage.
|
|
220
|
+
* Vive dentro il provider perché il contesto è leggibile solo lì
|
|
221
|
+
*/
|
|
222
|
+
export const TMSummaryResizeTracker = ({ onUserResize }) => {
|
|
223
|
+
const { isResizingActive } = useTMPanelManagerContext();
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
if (isResizingActive)
|
|
226
|
+
onUserResize();
|
|
227
|
+
}, [isResizingActive]);
|
|
228
|
+
return null;
|
|
229
|
+
};
|
|
216
230
|
/**
|
|
217
231
|
* Tiene allineate le altezze dei pannelli al collasso delle sezioni: il panel manager non
|
|
218
232
|
* conosce il collasso, quindi è qui che l'apertura di una sezione si traduce nelle nuove quote.
|
|
@@ -236,10 +250,7 @@ export const TMSectionPanelsHeights = ({ sections, sectionsOpen }) => {
|
|
|
236
250
|
}
|
|
237
251
|
if (hasMaximizedPanel)
|
|
238
252
|
return;
|
|
239
|
-
// Solo i pannelli a schermo si spartiscono l'altezza
|
|
240
|
-
// resta visibile uno solo) al pannello a schermo toccherebbe la sua sola quota nominale,
|
|
241
|
-
// lasciando vuoto lo spazio dei pannelli che non ci sono. Chi è fuori schermo resta alle proprie
|
|
242
|
-
// quote, che sono quelle da cui il panel manager riparte quando lo si torna a mostrare
|
|
253
|
+
// Solo i pannelli a schermo si spartiscono l'altezza, altrimenti resterebbe vuoto lo spazio dei nascosti
|
|
243
254
|
const visibleSections = sections.filter(section => panelVisibility[section] !== false);
|
|
244
255
|
const heights = sectionPanelHeights(visibleSections, sectionsOpen, dimensionsRef.current);
|
|
245
256
|
visibleSections.forEach(section => setPanelDimensionsById(section, heights[section].width, heights[section].height));
|
|
@@ -53,7 +53,6 @@ export const TMPanelManagerProvider = (props) => {
|
|
|
53
53
|
// Recursively hide parents
|
|
54
54
|
const parentsToHide = hideParentRecursively(id, hierarchyMap, updatedVisibility);
|
|
55
55
|
parentsToHide.forEach(([pid, visible]) => { updatedVisibility[pid] = visible; });
|
|
56
|
-
4;
|
|
57
56
|
// Redistribute dimensions after hiding
|
|
58
57
|
setPanelDimensions(prev => redistributeDimensionsOnHide(id, prev, hierarchyMap, updatedVisibility));
|
|
59
58
|
}
|
|
@@ -133,15 +133,18 @@ export type MultiDetailPanelStates = Record<string, MultiDetailPanelState>;
|
|
|
133
133
|
export declare class ArchivingSettings {
|
|
134
134
|
mruTIDs: number[];
|
|
135
135
|
defaultTree: number;
|
|
136
|
-
/**
|
|
137
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Sezioni collassate dall'utente nel riepilogo, ripristinate alla riapertura. Assente finché
|
|
138
|
+
* l'utente non ne chiude una: le impostazioni di default non vanno scritte nel local storage
|
|
139
|
+
*/
|
|
140
|
+
multiDetailSectionsOpen?: MultiDetailSectionsOpen;
|
|
138
141
|
/**
|
|
139
142
|
* Altezze dei pannelli del riepilogo, gestite dal panel manager. Non comprende il collasso delle
|
|
140
|
-
* sezioni, che è in multiDetailSectionsOpen:
|
|
143
|
+
* sezioni, che è in multiDetailSectionsOpen: assente finché l'utente non sposta un separatore.
|
|
141
144
|
*/
|
|
142
|
-
multiDetailPanelStates
|
|
145
|
+
multiDetailPanelStates?: MultiDetailPanelStates;
|
|
143
146
|
}
|
|
144
|
-
/** Stato di apertura salvato delle sezioni: i default coprono
|
|
147
|
+
/** Stato di apertura salvato delle sezioni: i default coprono l'impostazione mai modificata dall'utente */
|
|
145
148
|
export declare const getMultiDetailSectionsOpen: () => MultiDetailSectionsOpen;
|
|
146
149
|
export declare const saveMultiDetailSectionsOpen: (sectionsOpen: MultiDetailSectionsOpen) => void;
|
|
147
150
|
/** Stato salvato dei pannelli: vuoto finché l'utente non ne ha ridimensionato nessuno */
|
|
@@ -149,6 +152,8 @@ export declare const getMultiDetailPanelStates: () => MultiDetailPanelStates;
|
|
|
149
152
|
/** Il panel manager applica lo stato salvato solo se esiste: altrimenti riparte dalle quote di default */
|
|
150
153
|
export declare const hasMultiDetailPanelStates: () => boolean;
|
|
151
154
|
export declare const saveMultiDetailPanelStates: (panelStates: MultiDetailPanelStates) => void;
|
|
155
|
+
/** C'è un layout dell'utente da cancellare: nel local storage le due impostazioni ci sono solo se diverse dai default */
|
|
156
|
+
export declare const hasMultiDetailLayout: () => boolean;
|
|
152
157
|
/**
|
|
153
158
|
* Elimina dal local storage il layout configurato dall'utente per il riepilogo: altezze dei pannelli
|
|
154
159
|
* e sezioni collassate. Alla riapertura le sezioni sono tutte espanse con le quote di default.
|
|
@@ -198,23 +198,19 @@ export class ArchivingSettings {
|
|
|
198
198
|
constructor() {
|
|
199
199
|
this.mruTIDs = [];
|
|
200
200
|
this.defaultTree = -1;
|
|
201
|
-
/** Sezioni collassate dall'utente nel riepilogo, ripristinate alla riapertura */
|
|
202
|
-
this.multiDetailSectionsOpen = { ...DEFAULT_MULTI_DETAIL_SECTIONS_OPEN };
|
|
203
|
-
/**
|
|
204
|
-
* Altezze dei pannelli del riepilogo, gestite dal panel manager. Non comprende il collasso delle
|
|
205
|
-
* sezioni, che è in multiDetailSectionsOpen: resta vuoto finché l'utente non sposta un separatore.
|
|
206
|
-
*/
|
|
207
|
-
this.multiDetailPanelStates = {};
|
|
208
201
|
}
|
|
209
202
|
}
|
|
210
|
-
/** Stato di apertura salvato delle sezioni: i default coprono
|
|
203
|
+
/** Stato di apertura salvato delle sezioni: i default coprono l'impostazione mai modificata dall'utente */
|
|
211
204
|
export const getMultiDetailSectionsOpen = () => ({
|
|
212
205
|
...DEFAULT_MULTI_DETAIL_SECTIONS_OPEN,
|
|
213
206
|
...SDKUI_Globals.userSettings.archivingSettings?.multiDetailSectionsOpen,
|
|
214
207
|
});
|
|
215
208
|
export const saveMultiDetailSectionsOpen = (sectionsOpen) => {
|
|
216
|
-
|
|
217
|
-
|
|
209
|
+
const isDefault = Object.keys(DEFAULT_MULTI_DETAIL_SECTIONS_OPEN)
|
|
210
|
+
.every(section => sectionsOpen[section] === DEFAULT_MULTI_DETAIL_SECTIONS_OPEN[section]);
|
|
211
|
+
// Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero.
|
|
212
|
+
// Tornata ai default l'impostazione va rimossa, così nel local storage restano solo le modifiche dell'utente
|
|
213
|
+
SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = isDefault ? undefined : { ...sectionsOpen };
|
|
218
214
|
};
|
|
219
215
|
/** Stato salvato dei pannelli: vuoto finché l'utente non ne ha ridimensionato nessuno */
|
|
220
216
|
export const getMultiDetailPanelStates = () => SDKUI_Globals.userSettings.archivingSettings?.multiDetailPanelStates ?? {};
|
|
@@ -226,14 +222,17 @@ export const saveMultiDetailPanelStates = (panelStates) => {
|
|
|
226
222
|
// Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero
|
|
227
223
|
SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = { ...panelStates };
|
|
228
224
|
};
|
|
225
|
+
/** C'è un layout dell'utente da cancellare: nel local storage le due impostazioni ci sono solo se diverse dai default */
|
|
226
|
+
export const hasMultiDetailLayout = () => hasMultiDetailPanelStates() || SDKUI_Globals.userSettings.archivingSettings?.multiDetailSectionsOpen !== undefined;
|
|
229
227
|
/**
|
|
230
228
|
* Elimina dal local storage il layout configurato dall'utente per il riepilogo: altezze dei pannelli
|
|
231
229
|
* e sezioni collassate. Alla riapertura le sezioni sono tutte espanse con le quote di default.
|
|
232
230
|
*/
|
|
233
231
|
export const clearMultiDetailLayout = () => {
|
|
234
|
-
// Il proxy persiste solo intercettando l'assegnazione di una proprietà:
|
|
235
|
-
|
|
236
|
-
SDKUI_Globals.userSettings.archivingSettings.
|
|
232
|
+
// Il proxy persiste solo intercettando l'assegnazione di una proprietà: entrambi i campi vanno riassegnati.
|
|
233
|
+
// Senza valore spariscono dal local storage e alla riapertura tornano validi i default
|
|
234
|
+
SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = undefined;
|
|
235
|
+
SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = undefined;
|
|
237
236
|
};
|
|
238
237
|
export class FullTextSettings {
|
|
239
238
|
constructor() {
|
|
@@ -59,6 +59,8 @@ export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose }: U
|
|
|
59
59
|
openAllSections: () => void;
|
|
60
60
|
clearLayout: () => void;
|
|
61
61
|
layoutResetToken: number;
|
|
62
|
+
hasSavedLayout: boolean;
|
|
63
|
+
setHasSavedLayout: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
62
64
|
canUpdateMaster: boolean;
|
|
63
65
|
masterFieldsCount: number;
|
|
64
66
|
getMasterMetadataAsync: () => Promise<Map<number, MetadataScalarValue>>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { ArchiveConstraints, ArchiveEngineByID, MetadataDataDomains, MetadataDataTypes, RelationCacheService, RelationTypes, ResultTypes, SDK_Globals } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
import { MetadataValueDescriptorEx } from '../ts';
|
|
4
|
-
import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
|
|
4
|
+
import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, hasMultiDetailLayout, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
|
|
5
5
|
import { getDcmtMetadataByMid, getUserMetadataSorted, mapAssociationsFromMetadata } from '../helper/dcmtsHelper';
|
|
6
6
|
import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../components/base/TMPopUp';
|
|
7
7
|
import TMSpinner from '../components/base/TMSpinner';
|
|
@@ -54,6 +54,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
54
54
|
const [sectionsOpen, setSectionsOpen] = useState(getMultiDetailSectionsOpen());
|
|
55
55
|
// Cambia a ogni cancellazione del layout, per rimontare i pannelli sulle dimensioni di default
|
|
56
56
|
const [layoutResetToken, setLayoutResetToken] = useState(0);
|
|
57
|
+
// Layout dell'utente nel local storage: senza non c'è niente da cancellare (è uno stato perché le altezze le salva il panel manager)
|
|
58
|
+
const [hasSavedLayout, setHasSavedLayout] = useState(hasMultiDetailLayout());
|
|
57
59
|
// Master
|
|
58
60
|
const [isOpenMasterForm, setIsOpenMasterForm] = useState(false);
|
|
59
61
|
const [masterInfoReloadToken, setMasterInfoReloadToken] = useState(0);
|
|
@@ -222,6 +224,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
222
224
|
const next = { ...sectionsOpen, [section]: isOpen };
|
|
223
225
|
setSectionsOpen(next);
|
|
224
226
|
saveMultiDetailSectionsOpen(next);
|
|
227
|
+
// Riaperte tutte le sezioni il collasso torna ai default: senza altezze salvate non c'è più layout
|
|
228
|
+
setHasSavedLayout(hasMultiDetailLayout());
|
|
225
229
|
// I dettagli già archiviati si caricano alla prima apertura della sezione
|
|
226
230
|
if (section === 'archivedDetails' && isOpen && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
227
231
|
refreshArchivedDetailsAsync();
|
|
@@ -254,6 +258,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
254
258
|
const restored = getMultiDetailSectionsOpen();
|
|
255
259
|
setSectionsOpen(restored);
|
|
256
260
|
setLayoutResetToken(prev => prev + 1);
|
|
261
|
+
setHasSavedLayout(false);
|
|
257
262
|
// Le sezioni riaperte dal ripristino caricano i dettagli già archiviati come alla prima apertura
|
|
258
263
|
if (restored.archivedDetails && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
259
264
|
refreshArchivedDetailsAsync();
|
|
@@ -562,6 +567,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
562
567
|
selectRelation, backToTypeSelection, isBackLocked, closeModal,
|
|
563
568
|
// Sezioni
|
|
564
569
|
sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken,
|
|
570
|
+
hasSavedLayout, setHasSavedLayout,
|
|
565
571
|
// Master
|
|
566
572
|
canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken,
|
|
567
573
|
isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync,
|