@topconsultnpm/sdkui-react-beta 6.14.15 → 6.14.17

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.
@@ -11,12 +11,12 @@ export interface ITMPanelProps {
11
11
  totalItems?: number;
12
12
  toolbar?: any;
13
13
  padding?: string;
14
- keepActiveState?: boolean;
15
14
  isVisible?: boolean;
16
15
  onBack?: () => void;
17
16
  onClose?: () => void;
18
17
  onHeaderDoubleClick?: () => void;
19
18
  onMaximize?: (isMaximized: boolean) => void;
19
+ onActiveChanged?: (isActive: boolean) => void;
20
20
  }
21
21
  declare const TMPanel: React.FC<ITMPanelProps>;
22
22
  export default TMPanel;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from 'react';
2
+ import { useCallback, useRef, useState } from 'react';
3
3
  import styled from 'styled-components';
4
4
  import { IconArrowLeft, IconClearButton, IconWindowMaximize, IconWindowMinimize, isPositiveNumber, SDKUI_Globals, SDKUI_Localizator } from '../../helper';
5
5
  import TMButton from './TMButton';
@@ -65,23 +65,29 @@ const StyledPanelContent = styled.div `
65
65
  outline: none;
66
66
  }
67
67
  `;
68
- const TMPanel = ({ allowMaximize = true, color, backgroundColor, backgroundColorContainer, children, showHeader = true, title, totalItems, displayedItemsCount, toolbar, padding = '5px', keepActiveState = false, isVisible = true, onBack, onClose, onHeaderDoubleClick, onMaximize }) => {
68
+ const TMPanel = ({ allowMaximize = true, color, backgroundColor, backgroundColorContainer, children, showHeader = true, title, totalItems, displayedItemsCount, toolbar, padding = '5px', isVisible = true, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged }) => {
69
69
  const [isActive, setIsActive] = useState(false);
70
70
  const [isMaximized, setIsMaximized] = useState(false);
71
71
  const titleRowRef = useRef(null);
72
- // If keepActiveState is true, always force isActive to true
73
- useEffect(() => {
74
- if (keepActiveState)
72
+ const handleFocus = useCallback(() => {
73
+ if (!isActive) {
75
74
  setIsActive(true);
76
- }, [keepActiveState]);
77
- const handleFocus = () => {
78
- if (!keepActiveState)
79
- setIsActive(true);
80
- };
81
- const handleBlur = () => {
82
- if (!keepActiveState)
83
- setIsActive(false);
84
- };
75
+ onActiveChanged?.(true);
76
+ }
77
+ }, [isActive, onActiveChanged]);
78
+ const handleBlur = useCallback(() => {
79
+ // Usiamo un setTimeout per dare il tempo al browser di spostare il focus
80
+ // e controllare se il focus è andato a un figlio che poi si è disconnesso
81
+ // o se il focus è uscito definitivamente dal pannello
82
+ setTimeout(() => {
83
+ if (!document.activeElement || !document.activeElement.closest('tmpanel-container')) {
84
+ if (isActive) {
85
+ setIsActive(false);
86
+ onActiveChanged?.(false);
87
+ }
88
+ }
89
+ }, 0); // Un piccolo ritardo per consentire al browser di stabilire il nuovo activeElement
90
+ }, [isActive, onActiveChanged]);
85
91
  // handler for external maximize management
86
92
  const handleMaximize = () => {
87
93
  setIsMaximized(prevState => {
@@ -92,7 +98,7 @@ const TMPanel = ({ allowMaximize = true, color, backgroundColor, backgroundColor
92
98
  return newValue;
93
99
  });
94
100
  };
95
- return (_jsxs(StyledPanelContainer, { "$isMaximized": onMaximize ? false : isMaximized, style: {
101
+ return (_jsxs(StyledPanelContainer, { className: "tmpanel-container", "$isMaximized": onMaximize ? false : isMaximized, style: {
96
102
  visibility: isVisible ? 'visible' : 'hidden',
97
103
  }, children: [showHeader &&
98
104
  _jsx(StyledPanelHeader, { "$backgroundColor": backgroundColor, "$color": color, "$isActive": isActive, onDoubleClick: () => {
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from 'react';
3
3
  import Logo from '../../../assets/Toppy-generico.png';
4
4
  import { DcmtTypeListCacheService, LayoutModes, SDK_Localizator } from '@topconsultnpm/sdk-ts-beta';
5
- import { IconTree, SDKUI_Globals, SDKUI_Localizator, IconRecentlyViewed, IconPreview, IconShow, IconBoard, IconDcmtTypeSys } from '../../../helper';
5
+ import { IconTree, SDKUI_Globals, SDKUI_Localizator, IconRecentlyViewed, IconPreview, IconShow, IconBoard, IconDcmtTypeSys, removeMruTid } from '../../../helper';
6
6
  import { useDeviceType, DeviceType } from '../../base/TMDeviceProvider';
7
7
  import TMLayoutContainer, { TMLayoutItem } from '../../base/TMLayout';
8
8
  import TMRecentsManager from '../../grids/TMRecentsManager';
@@ -13,13 +13,12 @@ import TMPanel from '../../base/TMPanel';
13
13
  import { TMPanelManagerProvider, useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
14
14
  import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
15
15
  const TMArchive = ({ inputTID, fileFromConnector = null }) => {
16
- const TIDs = SDKUI_Globals.userSettings.archivingSettings.mruTIDs;
17
16
  const [currentTID, setCurrentTID] = useState(0);
18
- const [mruTIDs, setMruTIDs] = useState(TIDs);
17
+ const [mruTIDs, setMruTIDs] = useState([]);
19
18
  const [currentMruTID, setCurrentMruTID] = useState(0);
20
19
  const [fromDTD, setFromDTD] = useState();
21
20
  const deviceType = useDeviceType();
22
- useEffect(() => { setMruTIDs(TIDs); }, []);
21
+ useEffect(() => { console.log(SDKUI_Globals.userSettings.archivingSettings.mruTIDs); setMruTIDs(SDKUI_Globals.userSettings.archivingSettings.mruTIDs); }, []);
23
22
  useEffect(() => {
24
23
  if (!inputTID)
25
24
  return;
@@ -33,10 +32,9 @@ const TMArchive = ({ inputTID, fileFromConnector = null }) => {
33
32
  setFromDTD(dtd);
34
33
  });
35
34
  }, [currentTID]);
36
- const setSearchByTID = (tid) => { setCurrentTID(tid); };
37
35
  const isMobile = deviceType === DeviceType.MOBILE;
38
36
  const tmTreeSelectorElement = useMemo(() => _jsx(TMTreeSelectorWrapper, { isMobile: isMobile, onSelectedTIDChanged: (tid) => {
39
- setSearchByTID(tid);
37
+ setCurrentTID(tid);
40
38
  if (tid && mruTIDs.includes(tid))
41
39
  setCurrentMruTID(tid);
42
40
  else
@@ -46,11 +44,8 @@ const TMArchive = ({ inputTID, fileFromConnector = null }) => {
46
44
  setCurrentMruTID(tid);
47
45
  setCurrentTID(tid);
48
46
  }, onDeletedTID: (tid) => {
49
- let newMruTIDS = mruTIDs.slice();
50
- let index = newMruTIDS.findIndex(o => o == tid);
51
- if (index >= 0)
52
- newMruTIDS.splice(index, 1);
53
- SDKUI_Globals.userSettings.searchSettings.mruTIDs = newMruTIDS.filter(tid => tid != undefined && tid != null);
47
+ let newMruTIDS = removeMruTid(SDKUI_Globals.userSettings.archivingSettings.mruTIDs, tid);
48
+ SDKUI_Globals.userSettings.archivingSettings.mruTIDs = newMruTIDS;
54
49
  setMruTIDs(newMruTIDS);
55
50
  } }), [mruTIDs, currentMruTID, deviceType]);
56
51
  const tmFormElement = useMemo(() => currentTID ?
@@ -117,7 +112,7 @@ const TMArchive = ({ inputTID, fileFromConnector = null }) => {
117
112
  ]
118
113
  },
119
114
  ], [tmTreeSelectorElement, tmRecentsManagerElement, tmFormElement, currentTID, mruTIDs]);
120
- return (_jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }));
115
+ return (_jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }));
121
116
  };
122
117
  export default TMArchive;
123
118
  const TMTreeSelectorWrapper = ({ isMobile, onSelectedTIDChanged }) => {
@@ -136,6 +131,7 @@ const TMTreeSelectorWrapper = ({ isMobile, onSelectedTIDChanged }) => {
136
131
  };
137
132
  const TMRecentsManagerWrapper = ({ mruTIDs, currentMruTID, deviceType, onSelectedTID, onDeletedTID }) => {
138
133
  const { setPanelVisibilityById, setToolbarButtonVisibility } = useTMPanelManagerContext();
134
+ useEffect(() => { console.log('TMRecentsManagerWrapper -mruTIDs', mruTIDs); }, [mruTIDs]);
139
135
  return (_jsx(TMRecentsManager, { mruTIDs: mruTIDs, currentMruTID: currentMruTID, deviceType: deviceType, onSelectedTID: (tid) => {
140
136
  onSelectedTID?.(tid);
141
137
  if (deviceType === DeviceType.MOBILE)
@@ -9,7 +9,7 @@ import { DownloadTypes, FormModes } from '../../../ts';
9
9
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
10
10
  import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
11
11
  import { handleArchiveVisibility, searchResultToMetadataValues } from '../../../helper/queryHelper';
12
- import { genUniqueId, IconShow, SDKUI_Localizator, IconBoard, IconDcmtTypeSys, IconDetailDcmts, svgToString, IconDownload, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, defaultDcmtFormLayout } from '../../../helper';
12
+ import { genUniqueId, IconShow, SDKUI_Localizator, IconBoard, IconDcmtTypeSys, IconDetailDcmts, svgToString, IconDownload, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview } from '../../../helper';
13
13
  import { hasDetailRelations, hasMasterRelations, isXMLFileExt } from '../../../helper/dcmtsHelper';
14
14
  import { TMColors } from '../../../utils/theme';
15
15
  import { StyledFormButtonsContainer, StyledModalContainer, StyledToolbarCardContainer } from '../../base/Styled';
@@ -29,12 +29,13 @@ import TMDcmtBlog from './TMDcmtBlog';
29
29
  import { useInputAttachmentsDialog } from '../../../hooks/useInputDialog';
30
30
  import TMModal from '../../base/TMModal';
31
31
  import toppy from '../../../assets/Toppy-generico.png';
32
- import { TMPanelManagerProvider, useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
32
+ import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
33
33
  import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
34
+ import { TMPanelManagerWithPersistenceProvider } from '../../layout/panelManager/TMPanelManagerWithPersistenceProvider';
35
+ import { updateMruTids } from '../../../helper';
34
36
  let abortControllerLocal = new AbortController();
35
37
  //#endregion
36
38
  const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, onClose, onSavedAsyncCallback, TID, DID, formMode = FormModes.Update, canNext, canPrev, count, itemIndex, onNext, onPrev, allowNavigation = true, allowRelations = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, showDcmtFormSidebar = true, invokedByTodo = false, titleModal, isModal = false, widthModal = "100%", heightModal = "100%", groupId, onWFOperationCompleted, fileFromConnector = null, }) => {
37
- const mruTIDs = SDKUI_Globals.userSettings.archivingSettings.mruTIDs;
38
39
  const [id, setID] = useState('');
39
40
  const [showWaitPanelLocal, setShowWaitPanelLocal] = useState(false);
40
41
  const [waitPanelTitleLocal, setWaitPanelTitleLocal] = useState('');
@@ -326,14 +327,8 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
326
327
  });
327
328
  await setMetadataListAsync(fromDTD?.metadata ?? [], true, res);
328
329
  resetHandler();
329
- let newMruTIDS = mruTIDs ? mruTIDs.slice() : [];
330
- let index = newMruTIDS.findIndex((o) => o == TID);
331
- if (index >= 0)
332
- newMruTIDS.splice(index, 1);
333
- if (newMruTIDS.length >= 10)
334
- newMruTIDS.splice(0, 1);
335
- newMruTIDS.push(TID);
336
- SDKUI_Globals.userSettings.archivingSettings.mruTIDs = newMruTIDS.filter(tid => tid != undefined && tid != null);
330
+ let newMruTIDS = updateMruTids(SDKUI_Globals.userSettings.archivingSettings.mruTIDs, TID);
331
+ SDKUI_Globals.userSettings.archivingSettings.mruTIDs = newMruTIDS;
337
332
  onSaveRecents?.(newMruTIDS);
338
333
  ShowAlert({ mode: 'success', title: 'Archiviazione', message: 'Il documento è stato archiviato con successo', duration: 3000 });
339
334
  }
@@ -440,55 +435,18 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
440
435
  setDcmtFile(setFile);
441
436
  } }), [currentDcmt, dcmtFile, deviceType, fromDTD, layoutMode]);
442
437
  const normalizedTID = TID !== undefined ? Number(TID) : undefined;
443
- const dcmtFormSettings = SDKUI_Globals.userSettings.dcmtFormSettings.find(o => o.TID === normalizedTID);
444
- const layout = dcmtFormSettings?.layout;
445
- const layoutToDo = dcmtFormSettings?.layoutToDo;
446
- const layoutObj = invokedByTodo ? layoutToDo : layout;
447
438
  const defaultPanelDimensions = {
448
439
  'tmDcmtForm': { width: '20%', height: '100%' },
449
440
  'tmBlog': { width: '30%', height: '100%' },
450
441
  'tmSysMetadata': { width: '20%', height: '100%' },
451
442
  'tmDcmtPreview': { width: '30%', height: '100%' },
452
443
  };
453
- const allInitialPanelVisibility = useMemo(() => {
454
- if (layoutObj) {
455
- return {
456
- 'tmDcmtForm': layoutObj['tmDcmtForm']?.visible ?? true,
457
- 'tmBlog': layoutObj['tmBlog']?.visible ?? false,
458
- 'tmSysMetadata': layoutObj['tmSysMetadata']?.visible ?? false,
459
- 'tmDcmtPreview': layoutObj['tmDcmtPreview']?.visible ?? true,
460
- };
461
- }
462
- return {
463
- 'tmDcmtForm': true,
464
- 'tmBlog': false,
465
- 'tmSysMetadata': false,
466
- 'tmDcmtPreview': true,
467
- };
468
- }, [layoutObj]);
469
- const initialPanelDimensions = useMemo(() => {
470
- if (layoutObj) {
471
- return {
472
- 'tmDcmtForm': {
473
- width: layoutObj['tmDcmtForm']?.width ?? '20%',
474
- height: layoutObj['tmDcmtForm']?.height ?? '100%',
475
- },
476
- 'tmBlog': {
477
- width: layoutObj['tmBlog']?.width ?? '30%',
478
- height: layoutObj['tmBlog']?.height ?? '100%',
479
- },
480
- 'tmSysMetadata': {
481
- width: layoutObj['tmSysMetadata']?.width ?? '20%',
482
- height: layoutObj['tmSysMetadata']?.height ?? '100%',
483
- },
484
- 'tmDcmtPreview': {
485
- width: layoutObj['tmDcmtPreview']?.width ?? '30%',
486
- height: layoutObj['tmDcmtPreview']?.height ?? '100%',
487
- },
488
- };
489
- }
490
- return defaultPanelDimensions;
491
- }, [layoutObj]);
444
+ const allInitialPanelVisibility = {
445
+ 'tmDcmtForm': true,
446
+ 'tmBlog': false,
447
+ 'tmSysMetadata': false,
448
+ 'tmDcmtPreview': true,
449
+ };
492
450
  const initialPanels = useMemo(() => [
493
451
  {
494
452
  id: 'tmDcmtForm',
@@ -531,36 +489,58 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
531
489
  toolbarOptions: { icon: _jsx(IconShow, { fontSize: 24 }), disabled: isPreviewDisabled, visible: true, orderNumber: 4, isActive: allInitialPanelVisibility['tmDcmtPreview'] }
532
490
  }
533
491
  ], [tmDcmtForm, tmBlog, tmSysMetadata, tmDcmtPreview, isPreviewDisabled, isBoardDisabled, isSysMetadataDisabled, isClosable]);
534
- function saveLayout(panelVisibility, panelDimensions) {
535
- // console.log('panelVisibility', panelVisibility)
536
- // console.log('panelDimensions', panelDimensions)
537
- if (normalizedTID === undefined)
538
- return;
539
- const layoutPanels = {};
540
- Object.keys(panelVisibility).forEach(id => {
541
- layoutPanels[id] = {
542
- visible: panelVisibility[id],
543
- width: panelDimensions[id]?.width ?? defaultPanelDimensions[id]?.width ?? '100%',
544
- height: panelDimensions[id]?.height ?? defaultPanelDimensions[id]?.height ?? '100%'
545
- };
546
- });
547
- // console.log('layoutPanels', layoutPanels)
492
+ // Retrieves the current document form setting based on the normalized TID
493
+ const getCurrentDcmtFormSetting = () => {
548
494
  const settings = SDKUI_Globals.userSettings.dcmtFormSettings;
495
+ // Find the index of the setting that matches the normalized TID
549
496
  const idx = settings.findIndex(s => Number(s.TID) === normalizedTID);
550
- const prevSetting = settings[idx] ?? {};
497
+ // Return both the index and the corresponding setting (or empty object if not found)
498
+ return { idx, setting: settings[idx] ?? {} };
499
+ };
500
+ // Checks if there's a saved panel layout for the current context (ToDo or general)
501
+ const hasSavedLayout = () => {
502
+ const { setting } = getCurrentDcmtFormSetting();
503
+ if (invokedByTodo) {
504
+ // If invoked by ToDo, check for existence of layoutToDo and that it has keys
505
+ return setting.layoutToDo !== undefined && Object.keys(setting.layoutToDo).length > 0;
506
+ }
507
+ else {
508
+ // Otherwise, check for general layout
509
+ return setting.layout !== undefined && Object.keys(setting.layout).length > 0;
510
+ }
511
+ };
512
+ // Persists the current panel states (layout) into user settings
513
+ const persistPanelStates = (state) => {
514
+ // If no valid TID or empty state, skip saving
515
+ if (normalizedTID === undefined || Object.keys(state).length === 0)
516
+ return;
517
+ const settings = SDKUI_Globals.userSettings.dcmtFormSettings;
518
+ const { idx, setting: existingSetting } = getCurrentDcmtFormSetting();
519
+ // Prepare the new setting object with updated layout depending on context
551
520
  const newSetting = {
552
521
  TID: normalizedTID,
553
- layout: invokedByTodo ? (prevSetting.layout ?? defaultDcmtFormLayout) : layoutPanels,
554
- layoutToDo: invokedByTodo ? layoutPanels : (prevSetting.layoutToDo ?? defaultDcmtFormLayout),
522
+ layout: invokedByTodo ? (existingSetting.layout ?? {}) : state,
523
+ layoutToDo: invokedByTodo ? state : (existingSetting.layoutToDo ?? {}),
555
524
  };
525
+ // Replace existing setting if found, otherwise push a new one
556
526
  if (idx >= 0) {
557
527
  settings[idx] = newSetting;
558
528
  }
559
529
  else {
560
530
  settings.push(newSetting);
561
531
  }
532
+ // Assign the updated settings array back to the global settings
562
533
  SDKUI_Globals.userSettings.dcmtFormSettings = [...settings];
563
- }
534
+ };
535
+ // Retrieves the persisted panel states (layout) for the current context
536
+ const getPersistedPanelStates = () => {
537
+ // Do not return layout on mobile devices
538
+ if (isMobile)
539
+ return undefined;
540
+ const settings = getCurrentDcmtFormSetting()?.setting;
541
+ // Return the appropriate layout based on context
542
+ return invokedByTodo ? settings?.layoutToDo : settings?.layout;
543
+ };
564
544
  const renderDcmtForm = () => {
565
545
  return (_jsxs("div", { style: {
566
546
  display: 'flex',
@@ -571,7 +551,7 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
571
551
  height: '100%',
572
552
  }, children: [_jsxs(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: useWaitPanelLocalState ? showWaitPanelLocal : showWaitPanel, showWaitPanelPrimary: useWaitPanelLocalState ? showPrimaryLocal : showPrimary, showWaitPanelSecondary: useWaitPanelLocalState ? showSecondaryLocal : showSecondary, waitPanelTitle: useWaitPanelLocalState ? waitPanelTitleLocal : waitPanelTitle, waitPanelTextPrimary: useWaitPanelLocalState ? waitPanelTextPrimaryLocal : waitPanelTextPrimary, waitPanelValuePrimary: useWaitPanelLocalState ? waitPanelValuePrimaryLocal : waitPanelValuePrimary, waitPanelMaxValuePrimary: useWaitPanelLocalState ? waitPanelMaxValuePrimaryLocal : waitPanelMaxValuePrimary, waitPanelTextSecondary: useWaitPanelLocalState ? waitPanelTextSecondaryLocal : waitPanelTextSecondary, waitPanelValueSecondary: useWaitPanelLocalState ? waitPanelValueSecondaryLocal : waitPanelValueSecondary, waitPanelMaxValueSecondary: useWaitPanelLocalState ? waitPanelMaxValueSecondaryLocal : waitPanelMaxValueSecondary, isCancelable: useWaitPanelLocalState ? dcmtFile ? dcmtFile.size >= 1000000 : false : true, abortController: useWaitPanelLocalState ? abortControllerLocal : abortController, children: [(groupId && groupId.length > 0)
573
553
  ? _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showDcmtFormSidebar })
574
- : _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmDcmtForm', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showDcmtFormSidebar, onLayoutChanged: saveLayout }) }), isOpenDistinctValues &&
554
+ : _jsx(TMPanelManagerWithPersistenceProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: defaultPanelDimensions, initialDimensions: defaultPanelDimensions, initialMobilePanelId: 'tmDcmtForm', isPersistenceEnabled: !isMobile ? hasSavedLayout() : false, persistPanelStates: !isMobile ? (state) => persistPanelStates(state) : undefined, persistedPanelStates: getPersistedPanelStates(), children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showDcmtFormSidebar }) }), isOpenDistinctValues &&
575
555
  _jsx(TMDistinctValues, { tid: TID, mid: focusedMetadataValue?.mid, isModal: true, showHeader: false, layoutMode: layoutMode, onSelectionChanged: (e) => {
576
556
  if (!e)
577
557
  return;
@@ -585,10 +565,8 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
585
565
  isEditable: true,
586
566
  value: FormulaHelper.addFormulaTag(newFormula.expression)
587
567
  }));
588
- } }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }), (fromDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isOpenDetails && !isOpenMaster) &&
589
- _jsx(ToppyHelpCenter, { deviceType: deviceType,
590
- // onClick={() => isMobile ? openConfigureMode?.() : undefined}
591
- content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { onApprove: () => setShowApprovePopup(true), onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), isInDcmtForm: deviceType === DeviceType.MOBILE }) }) }), isOpenDetails &&
568
+ } }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }), (fromDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isOpenDetails && !isOpenMaster && layoutMode === LayoutModes.Update) &&
569
+ _jsx(ToppyHelpCenter, { deviceType: deviceType, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { onApprove: () => setShowApprovePopup(true), onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), isInDcmtForm: deviceType === DeviceType.MOBILE }) }) }), isOpenDetails &&
592
570
  _jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, isForMaster: false, inputDcmts: getSelectionDcmtInfo(), allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenDetails(false) }) }), isOpenMaster &&
593
571
  _jsxs(StyledModalContainer, { style: { backgroundColor: 'white' }, children: [_jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: getSelectionDcmtInfo(), isForMaster: true, allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenMaster(false), appendMasterDcmts: handleAddItem }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
594
572
  return (_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: [dcmt], isForMaster: true, allowNavigation: false, onBack: () => handleRemoveItem(dcmt.TID, dcmt.DID), appendMasterDcmts: handleAddItem }) }, `${index}-${dcmt.DID}`));
@@ -483,7 +483,7 @@ const TMMasterDetailDcmts = ({ deviceType, inputDcmts, isForMaster, showCurrentD
483
483
  toolbarOptions: { icon: _jsx(IconSearchCheck, { fontSize: 24 }), visible: false, orderNumber: 2, isActive: allInitialPanelVisibility['tmFormOrResult'] }
484
484
  }
485
485
  ], [tmTreeView, tmFormOrResult, focusedItem?.isDcmt]);
486
- return (_jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmTreeView', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }));
486
+ return (_jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmTreeView', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }));
487
487
  };
488
488
  export default TMMasterDetailDcmts;
489
489
  function getMetadataKeys(obj) {
@@ -5,7 +5,7 @@ import TMSavedQuerySelector from './TMSavedQuerySelector';
5
5
  import TMTreeSelector from './TMTreeSelector';
6
6
  import { TabPanel, Item } from 'devextreme-react/tab-panel';
7
7
  import TMSearchQueryPanel, { refreshLastSearch } from './TMSearchQueryPanel';
8
- import { getSysAllDcmtsSQD, IconFilter, IconRecentlyViewed, IconSavedQuery, IconTree, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
8
+ import { getSysAllDcmtsSQD, IconFilter, IconRecentlyViewed, IconSavedQuery, IconTree, removeMruTid, SDKUI_Globals, SDKUI_Localizator, updateMruTids } from '../../../helper';
9
9
  import TMSearchResult from './TMSearchResult';
10
10
  import TMRecentsManager from '../../grids/TMRecentsManager';
11
11
  import { SearchResultContext } from '../../../ts';
@@ -131,11 +131,8 @@ const TMSearch = ({ inputTID, inputSqdID, isExpertMode = SDKUI_Globals.userSetti
131
131
  setCurrentMruTID(tid);
132
132
  setCurrentTID(tid);
133
133
  }, onDeletedTID: (tid) => {
134
- let newMruTIDS = mruTIDs.slice();
135
- let index = newMruTIDS.findIndex(o => o == tid);
136
- if (index >= 0)
137
- newMruTIDS.splice(index, 1);
138
- SDKUI_Globals.userSettings.searchSettings.mruTIDs = newMruTIDS.filter(tid => tid != undefined && tid != null);
134
+ let newMruTIDS = removeMruTid(SDKUI_Globals.userSettings.searchSettings.mruTIDs, tid);
135
+ SDKUI_Globals.userSettings.searchSettings.mruTIDs = newMruTIDS;
139
136
  setMruTIDs(newMruTIDS);
140
137
  } }), [mruTIDs, currentMruTID, deviceType]);
141
138
  const tmSearchQueryPanelElement = useMemo(() => _jsx(TMSearchQueryPanelWrapper, { isExpertMode: isExpertMode, showBackToResultButton: searchResult.length > 0, fromDTD: fromDTD, SQD: currentSQD, onBackToResult: () => { setCurrentSearchView(TMSearchViews.Result); }, onSearchCompleted: (searchResult, qd) => {
@@ -146,16 +143,10 @@ const TMSearch = ({ inputTID, inputSqdID, isExpertMode = SDKUI_Globals.userSetti
146
143
  setCurrentSearchView(TMSearchViews.Result);
147
144
  // Salvataggio ultimi 10 TIDs
148
145
  let fromTID = searchResult?.[0].fromTID;
149
- let newMruTIDS = mruTIDs.slice();
150
- let index = newMruTIDS.findIndex(o => o == fromTID);
151
- if (index >= 0)
152
- newMruTIDS.splice(index, 1);
153
- if (newMruTIDS.length >= 10)
154
- newMruTIDS.splice(0, 1);
155
- newMruTIDS.push(fromTID);
146
+ let newMruTIDS = updateMruTids(SDKUI_Globals.userSettings.searchSettings.mruTIDs, fromTID);
147
+ SDKUI_Globals.userSettings.searchSettings.mruTIDs = newMruTIDS;
156
148
  setMruTIDs(newMruTIDS);
157
149
  setCurrentMruTID(fromTID);
158
- SDKUI_Globals.userSettings.searchSettings.mruTIDs = newMruTIDS.filter(tid => tid != undefined && tid != null);
159
150
  }, onSqdSaved: async (newSqd) => {
160
151
  await loadDataSQDsAsync(true, newSqd.masterTID);
161
152
  await setSQDAsync(newSqd);
@@ -204,7 +195,7 @@ const TMSearch = ({ inputTID, inputSqdID, isExpertMode = SDKUI_Globals.userSetti
204
195
  toolbarOptions: { icon: _jsx(IconSavedQuery, { fontSize: 24 }), visible: true, orderNumber: 4, isActive: allInitialPanelVisibility['TMSavedQuerySelector'] }
205
196
  }
206
197
  ], [tmTreeSelectorElement, tmRecentsManagerElement, tmSearchQueryPanelElement, tmSavedQuerySelectorElement, fromDTD, mruTIDs]);
207
- return (_jsxs(_Fragment, { children: [_jsx(StyledMultiViewPanel, { "$isVisible": currentSearchView === TMSearchViews.Search, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'TMRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }), searchResult.length > 0 &&
198
+ return (_jsxs(_Fragment, { children: [_jsx(StyledMultiViewPanel, { "$isVisible": currentSearchView === TMSearchViews.Search, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'TMRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }), searchResult.length > 0 &&
208
199
  _jsx(TMSearchResult, { isVisible: currentSearchView === TMSearchViews.Result, context: SearchResultContext.METADATA_SEARCH, searchResults: searchResult, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onRefreshSearchAsync: async () => {
209
200
  setSearchResult(await refreshLastSearch(lastQdSearched) ?? []);
210
201
  }, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setCurrentSearchView(TMSearchViews.Search); }, onFileOpened: onFileOpened })] }));
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { useCallback, useEffect, useState } from 'react';
3
3
  import { PlatformObjectValidator, WhereItem, SDK_Localizator, OrderByItem, SelectItem, SelectItemVisibilities, SDK_Globals, SavedQueryCacheService, SearchEngine, QueryOperators } from '@topconsultnpm/sdk-ts-beta';
4
4
  import styled from 'styled-components';
5
5
  import TMSearchQueryEditor from './TMSearchQueryEditor';
@@ -26,7 +26,8 @@ import TMShowAllOrMaxItemsButton from '../../base/TMShowAllOrMaxItemsButton';
26
26
  const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, onSearchCompleted, onSqdSaved, onBack, onClosePanel, onMaximizePanel, onBackToResult }) => {
27
27
  const [confirmQueryParams, ConfirmQueryParamsDialog] = useQueryParametersDialog();
28
28
  const [qd, setQd] = useState();
29
- const [shouldSearch, setShouldSearch] = useState(false); // Nuovo stato per attivare la ricerca
29
+ const [shouldSearch, setShouldSearch] = useState(false);
30
+ const [isQueryPanelActive, setIsQueryPanelActive] = useState(false);
30
31
  const [lastQdParams, setLastQdParams] = useState([]);
31
32
  const [dcmtTypesList, setDcmtTypesList] = useState([]);
32
33
  const [showSqdForm, setShowSqdForm] = useState(false);
@@ -41,7 +42,6 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
41
42
  const deviceType = useDeviceType();
42
43
  const isMobile = deviceType === DeviceType.MOBILE;
43
44
  let initialMaxItems = deviceType === DeviceType.MOBILE ? 8 : 12;
44
- const panelRef = useRef(null);
45
45
  useEffect(() => {
46
46
  if (!SQD)
47
47
  return;
@@ -59,8 +59,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
59
59
  }, [shouldSearch, qd, showAdvancedSearch]);
60
60
  useEffect(() => {
61
61
  const handleKeyDown = (e) => {
62
- if (panelRef.current &&
63
- panelRef.current.contains(document.activeElement)) {
62
+ if (isQueryPanelActive) {
64
63
  if (e.key === 'Enter') {
65
64
  e.preventDefault();
66
65
  // Disattiva l'elemento attivo per forzare l'onBlur
@@ -76,7 +75,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
76
75
  return () => {
77
76
  window.removeEventListener('keydown', handleKeyDown);
78
77
  };
79
- }, []);
78
+ }, [isQueryPanelActive]);
80
79
  const setDataAsync = async (sqd) => {
81
80
  let newSqd = (sqd?.id == 1) ? sqd : await SavedQueryCacheService.GetAsync(sqd?.id);
82
81
  let newQd = SearchEngine.NormalizeQueryDescriptor(newSqd?.qd);
@@ -146,6 +145,9 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
146
145
  setQd(newQd);
147
146
  }
148
147
  }, []);
148
+ const handlePanelActiveChanged = useCallback((isActive) => {
149
+ setIsQueryPanelActive(isActive);
150
+ }, []);
149
151
  const handleAdvancedMenuClick = useCallback((e) => {
150
152
  if (e.button === AdvancedMenuButtons.DistinctValues) {
151
153
  setShowDistinctValuesPanel(true);
@@ -227,7 +229,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
227
229
  }
228
230
  setQd({ ...qd, orderBy: newOrderBy });
229
231
  }, [qd, fromDTD?.metadata, SQD?.masterTID]);
230
- return (_jsxs("div", { ref: panelRef, style: { height: '100%' }, children: [_jsxs(TMPanel, { title: fromDTD?.nameLoc ?? SDKUI_Localizator.Search_Metadata, allowMaximize: !isMobile, onMaximize: isMobile ? undefined : onMaximizePanel, onHeaderDoubleClick: isMobile ? undefined : onMaximizePanel, onBack: onBack, toolbar: _jsx(_Fragment, { children: (SQD && !showSqdForm) ?
232
+ return (_jsxs(_Fragment, { children: [_jsxs(TMPanel, { title: fromDTD?.nameLoc ?? SDKUI_Localizator.Search_Metadata, allowMaximize: !isMobile, onMaximize: isMobile ? undefined : onMaximizePanel, onHeaderDoubleClick: isMobile ? undefined : onMaximizePanel, onBack: onBack, onActiveChanged: handlePanelActiveChanged, toolbar: _jsx(_Fragment, { children: (SQD && !showSqdForm) ?
231
233
  _jsx(TMDropDownMenu, { backgroundColor: 'white', borderRadius: '3px', content: _jsx(TMButton, { btnStyle: 'icon', caption: 'Altro', icon: _jsx(IconMenuVertical, { color: 'white' }), showTooltip: false }), items: [
232
234
  ...(showBackToResultButton ? [{ icon: _jsx(IconArrowRight, {}), text: "Vai a risultato", onClick: () => { onBackToResult?.(); } }] : []),
233
235
  { icon: _jsx(IconAddCircleOutline, {}), beginGroup: true, text: SDKUI_Localizator.SavedQueryNew, onClick: () => { openSqdForm(FormModes.Create); } },
@@ -427,7 +427,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
427
427
  }, children: _jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: (groupId && groupId.length > 0) ?
428
428
  _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar })
429
429
  :
430
- _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmSearchResult', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar }) }) }) }), _jsx(StyledMultiViewPanel, { "$isVisible": isOpenDetails, children: isOpenDetails && _jsx(TMMasterDetailDcmts, { deviceType: deviceType, isForMaster: false, inputDcmts: getSelectionDcmtInfo(), allowNavigation: focusedItem && selectedItems.length <= 0, canNext: canNavigateHandler('next'), canPrev: canNavigateHandler('prev'), onNext: () => onNavigateHandler('next'), onPrev: () => onNavigateHandler('prev'), onBack: () => setIsOpenDetails(false) }) }), _jsxs(StyledMultiViewPanel, { "$isVisible": isOpenMaster, children: [isOpenMaster && _jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: getSelectionDcmtInfo(), isForMaster: true, allowNavigation: focusedItem && selectedItems.length <= 0, canNext: canNavigateHandler('next'), canPrev: canNavigateHandler('prev'), onNext: () => onNavigateHandler('next'), onPrev: () => onNavigateHandler('prev'), onBack: () => setIsOpenMaster(false), appendMasterDcmts: handleAddItem }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
430
+ _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmSearchResult', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar }) }) }) }), _jsx(StyledMultiViewPanel, { "$isVisible": isOpenDetails, children: isOpenDetails && _jsx(TMMasterDetailDcmts, { deviceType: deviceType, isForMaster: false, inputDcmts: getSelectionDcmtInfo(), allowNavigation: focusedItem && selectedItems.length <= 0, canNext: canNavigateHandler('next'), canPrev: canNavigateHandler('prev'), onNext: () => onNavigateHandler('next'), onPrev: () => onNavigateHandler('prev'), onBack: () => setIsOpenDetails(false) }) }), _jsxs(StyledMultiViewPanel, { "$isVisible": isOpenMaster, children: [isOpenMaster && _jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: getSelectionDcmtInfo(), isForMaster: true, allowNavigation: focusedItem && selectedItems.length <= 0, canNext: canNavigateHandler('next'), canPrev: canNavigateHandler('prev'), onNext: () => onNavigateHandler('next'), onPrev: () => onNavigateHandler('prev'), onBack: () => setIsOpenMaster(false), appendMasterDcmts: handleAddItem }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
431
431
  return (_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: [dcmt], isForMaster: true, allowNavigation: false, onBack: () => handleRemoveItem(dcmt.TID, dcmt.DID), appendMasterDcmts: handleAddItem }) }, `${index}-${dcmt.DID}`));
432
432
  })] }), _jsx(StyledMultiViewPanel, { "$isVisible": isOpenDcmtForm, children: isOpenDcmtForm && _jsx(TMDcmtForm, { isModal: openDcmtFormAsModal, titleModal: fromDTD?.name ?? '', TID: focusedItem?.TID, DID: focusedItem?.DID, layoutMode: dcmtFormLayoutMode, count: visibleItems.length, itemIndex: visibleItems.findIndex(o => o.rowIndex === focusedItem?.rowIndex) + 1, canNext: canNavigateHandler('next'), canPrev: canNavigateHandler('prev'), onNext: () => onNavigateHandler('next'), onPrev: () => onNavigateHandler('prev'), onClose: () => { setIsOpenDcmtForm(false); }, onWFOperationCompleted: onWFOperationCompleted, onSavedAsyncCallback: async (tid, did) => { await refreshFocusedDataRowAsync(tid, did, true); } }) })] }));
433
433
  };
@@ -1,12 +1,9 @@
1
- import { TMPanelDefinition, TMPanelDimensionsMap, TMPanelDirection } from './types';
1
+ import { TMPanelDefinition, TMPanelDirection } from './types';
2
2
  interface TMPanelManagerContainerProps {
3
3
  panels: Array<TMPanelDefinition>;
4
4
  showToolbar: boolean;
5
5
  direction: TMPanelDirection;
6
6
  parentId?: string;
7
- onLayoutChanged?: (panelVisibility: {
8
- [id: string]: boolean;
9
- }, panelDimensions: TMPanelDimensionsMap) => void;
10
7
  }
11
8
  declare const TMPanelManagerContainer: (props: TMPanelManagerContainerProps) => import("react/jsx-runtime").JSX.Element | null;
12
9
  export default TMPanelManagerContainer;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useRef, useMemo, useEffect } from 'react';
2
+ import React, { useRef, useMemo } from 'react';
3
3
  import styled from 'styled-components';
4
4
  import { useTMPanelManagerContext } from './TMPanelManagerContext';
5
5
  import TMPanelWrapper from './TMPanelWrapper';
@@ -23,13 +23,8 @@ const StyledResizerGutter = styled.div `
23
23
  z-index: 10;
24
24
  `;
25
25
  const TMPanelManagerContainer = (props) => {
26
- const { panels, showToolbar, direction, parentId, onLayoutChanged } = props;
27
- const { panelVisibility, panelDimensions, setPanelDimensionsById, hasVisiblePanels, maximizedPanels, updateIsResizingActive } = useTMPanelManagerContext();
28
- // Returns data to parent for saving layout, only for desktop
29
- useEffect(() => {
30
- // console.log('useEffect panelVisibility, panelDimensions', panelVisibility, panelDimensions)
31
- deviceType === DeviceType.DESKTOP && onLayoutChanged?.(panelVisibility, panelDimensions);
32
- }, [panelVisibility, panelDimensions]);
26
+ const { panels, showToolbar, direction, parentId } = props;
27
+ const { panelVisibility, setPanelDimensionsById, hasVisiblePanels, maximizedPanels, updateIsResizingActive } = useTMPanelManagerContext();
33
28
  // Get the current device type (e.g., mobile, tablet, desktop) using a custom hook
34
29
  const deviceType = useDeviceType();
35
30
  // This avoids unnecessary re-renders by only recalculating when deviceType changes
@@ -28,8 +28,10 @@ interface TMPanelManagerProviderProps {
28
28
  initialVisibility: {
29
29
  [id: string]: boolean;
30
30
  };
31
+ defaultDimensions: TMPanelDimensionsMap;
31
32
  initialDimensions: TMPanelDimensionsMap;
32
33
  initialMobilePanelId: string;
34
+ isPersistenceEnabled?: boolean;
33
35
  }
34
36
  export declare const TMPanelManagerProvider: (props: TMPanelManagerProviderProps) => import("react/jsx-runtime").JSX.Element;
35
37
  export declare const useTMPanelManagerContext: () => TMPanelManagerContextType;
@@ -5,13 +5,13 @@ import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
5
5
  // Create a React context for the panel state and actions
6
6
  const TMPanelManagerContext = createContext(undefined);
7
7
  export const TMPanelManagerProvider = (props) => {
8
- const { children, panels, initialDimensions, initialVisibility, initialMobilePanelId } = props;
8
+ const { children, panels, initialVisibility, defaultDimensions, initialDimensions, initialMobilePanelId, isPersistenceEnabled = false } = props;
9
9
  // Get the current device type (e.g., mobile, tablet, desktop) using a custom hook
10
10
  const deviceType = useDeviceType();
11
11
  // This avoids unnecessary re-renders by only recalculating when deviceType changes
12
12
  let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
13
13
  // Ref to persist the initial panel dimensions across renders without causing re-renders
14
- const initialPanelDimensionsRef = useRef(initialDimensions);
14
+ const defaultPanelDimensionsRef = useRef(defaultDimensions);
15
15
  // Memoize the panel hierarchy map to avoid re-generating it on every render unless `panels` change
16
16
  const hierarchyMap = useMemo(() => generatePanelHierarchyMap(panels), [panels]);
17
17
  // State to track the dimensions (width and height) of each panel, initialized with props
@@ -32,69 +32,78 @@ export const TMPanelManagerProvider = (props) => {
32
32
  setToolbarButtonsDisabled(disabledMap);
33
33
  }, []);
34
34
  // Callback to update the visibility state of a specific panel and its related hierarchy
35
- const updatePanelVisibility = useCallback((id, isVisible, prevVisibility) => {
35
+ const adjustPanelVisibilityAndSize = useCallback((id, isVisible, prevVisibility) => {
36
36
  // Clone previous visibility state to work with
37
- let updated = { ...prevVisibility };
37
+ let updatedVisibility = { ...prevVisibility };
38
38
  if (isMobile) {
39
39
  if (isVisible) {
40
40
  // On mobile, showing one panel hides all others first
41
- updated = Object.keys(prevVisibility).reduce((acc, key) => { acc[key] = false; return acc; }, {});
41
+ updatedVisibility = Object.keys(prevVisibility).reduce((acc, key) => { acc[key] = false; return acc; }, {});
42
42
  // Recursively determine and show parent panels
43
43
  const parentsToShow = showParentRecursively(id, hierarchyMap);
44
- parentsToShow.forEach(([pid, visible]) => { updated[pid] = visible; });
44
+ parentsToShow.forEach(([pid, visible]) => { updatedVisibility[pid] = visible; });
45
45
  // Show the target panel
46
- updated[id] = true;
46
+ updatedVisibility[id] = true;
47
47
  // Redistribute dimensions to account for newly shown panel
48
- setPanelDimensions(prev => redistributeDimensionsOnShow(id, prev, initialPanelDimensionsRef.current, hierarchyMap, updated));
48
+ setPanelDimensions(prev => redistributeDimensionsOnShow(id, prev, defaultPanelDimensionsRef.current, hierarchyMap, updatedVisibility));
49
49
  }
50
50
  else {
51
51
  // Hide the panel
52
- updated[id] = false;
52
+ updatedVisibility[id] = false;
53
53
  // Recursively hide parents
54
- const parentsToHide = hideParentRecursively(id, hierarchyMap, updated);
55
- parentsToHide.forEach(([pid, visible]) => { updated[pid] = visible; });
54
+ const parentsToHide = hideParentRecursively(id, hierarchyMap, updatedVisibility);
55
+ parentsToHide.forEach(([pid, visible]) => { updatedVisibility[pid] = visible; });
56
56
  4;
57
57
  // Redistribute dimensions after hiding
58
- setPanelDimensions(prev => redistributeDimensionsOnHide(id, prev, hierarchyMap, updated));
58
+ setPanelDimensions(prev => redistributeDimensionsOnHide(id, prev, hierarchyMap, updatedVisibility));
59
59
  }
60
60
  }
61
61
  else {
62
62
  if (isVisible) {
63
63
  // On desktop, show panel and its parents without hiding others
64
64
  const parentsToShow = showParentRecursively(id, hierarchyMap);
65
- parentsToShow.forEach(([pid, visible]) => { updated[pid] = visible; });
65
+ parentsToShow.forEach(([pid, visible]) => { updatedVisibility[pid] = visible; });
66
66
  // Show the panel
67
- updated[id] = true;
67
+ updatedVisibility[id] = true;
68
68
  // Adjust panel dimensions accordingly
69
- setPanelDimensions(prev => redistributeDimensionsOnShow(id, prev, initialPanelDimensionsRef.current, hierarchyMap, updated));
69
+ setPanelDimensions(prev => redistributeDimensionsOnShow(id, prev, defaultPanelDimensionsRef.current, hierarchyMap, updatedVisibility));
70
70
  }
71
71
  else {
72
72
  // Hide the panel
73
- updated[id] = false;
73
+ updatedVisibility[id] = false;
74
74
  // Recursively hide parent panels
75
- const parentsToHide = hideParentRecursively(id, hierarchyMap, updated);
76
- parentsToHide.forEach(([pid, visible]) => { updated[pid] = visible; });
75
+ const parentsToHide = hideParentRecursively(id, hierarchyMap, updatedVisibility);
76
+ parentsToHide.forEach(([pid, visible]) => { updatedVisibility[pid] = visible; });
77
77
  // Adjust dimensions after hiding
78
- setPanelDimensions(prev => redistributeDimensionsOnHide(id, prev, hierarchyMap, updated));
78
+ setPanelDimensions(prev => redistributeDimensionsOnHide(id, prev, hierarchyMap, updatedVisibility));
79
79
  }
80
80
  }
81
- return updated;
81
+ return updatedVisibility;
82
82
  }, [hierarchyMap, isMobile]);
83
83
  // On initial mount: initialize panel visibility using the provided `initialVisibility` config
84
84
  // Each panel's visibility is processed through `updatePanelVisibility` to ensure hierarchy logic is applied
85
85
  useEffect(() => {
86
- let updated = { ...initialVisibility };
87
- Object.entries(initialVisibility).forEach(([id, isVisible]) => {
88
- updated = updatePanelVisibility(id, isVisible, updated);
89
- });
90
- setPanelVisibility(updated);
86
+ if (isPersistenceEnabled) {
87
+ // If persistence is enabled, visibility and dimensions are already synced from persisted state, so just set them directly
88
+ setPanelVisibility(initialVisibility);
89
+ setPanelDimensions(initialDimensions);
90
+ }
91
+ else {
92
+ // If persistence is not enabled, recalculate visibility and dimensions based on the initial visibility, applying the hierarchy logic
93
+ let updated = { ...initialVisibility };
94
+ Object.entries(initialVisibility).forEach(([id, isVisible]) => {
95
+ updated = adjustPanelVisibilityAndSize(id, isVisible, updated);
96
+ });
97
+ // Update panel visibility state with recalculated values
98
+ setPanelVisibility(updated);
99
+ }
91
100
  }, []);
92
101
  // On mobile devices: automatically show the initial mobile panel when layout switches to mobile
93
102
  // This ensures the correct panel is visible by default on smaller screens
94
103
  useEffect(() => {
95
104
  if (isMobile) {
96
105
  setPanelVisibility(prev => {
97
- return updatePanelVisibility(initialMobilePanelId, true, prev);
106
+ return adjustPanelVisibilityAndSize(initialMobilePanelId, true, prev);
98
107
  });
99
108
  }
100
109
  }, [isMobile]);
@@ -130,18 +139,18 @@ export const TMPanelManagerProvider = (props) => {
130
139
  // Clone the current visibility state of panels
131
140
  let updatedVisibility = { ...panelVisibility };
132
141
  // Start with the initial panel dimensions as the base for restoration
133
- let nextDimensions = { ...initialPanelDimensionsRef.current };
142
+ let nextDimensions = { ...defaultPanelDimensionsRef.current };
134
143
  nextDimensions = Object.entries(updatedVisibility).reduce((acc, [id, isVisible]) => {
135
144
  if (isVisible) {
136
145
  // If the panel is visible, adjust dimensions to show it properly
137
- acc = redistributeDimensionsOnShow(id, acc, initialPanelDimensionsRef.current, hierarchyMap, updatedVisibility);
146
+ acc = redistributeDimensionsOnShow(id, acc, defaultPanelDimensionsRef.current, hierarchyMap, updatedVisibility);
138
147
  }
139
148
  else {
140
149
  // If the panel is hidden, adjust dimensions to hide it
141
150
  acc = redistributeDimensionsOnHide(id, acc, hierarchyMap, updatedVisibility);
142
151
  }
143
152
  return acc;
144
- }, initialPanelDimensionsRef.current);
153
+ }, defaultPanelDimensionsRef.current);
145
154
  // Apply the recalculated dimensions to the panel state
146
155
  setPanelDimensions(nextDimensions);
147
156
  }, [initialVisibility, panelVisibility, hierarchyMap]);
@@ -165,17 +174,17 @@ export const TMPanelManagerProvider = (props) => {
165
174
  // Then update the visibility of the panel, toggling its current state
166
175
  setPanelVisibility(prev => {
167
176
  const isCurrentlyVisible = prev[id];
168
- return updatePanelVisibility(id, !isCurrentlyVisible, prev);
177
+ return adjustPanelVisibilityAndSize(id, !isCurrentlyVisible, prev);
169
178
  });
170
179
  }
171
180
  else {
172
181
  // If the panel is not maximized, simply toggle its visibility
173
182
  setPanelVisibility(prev => {
174
183
  const isCurrentlyVisible = prev[id];
175
- return updatePanelVisibility(id, !isCurrentlyVisible, prev);
184
+ return adjustPanelVisibilityAndSize(id, !isCurrentlyVisible, prev);
176
185
  });
177
186
  }
178
- }, [maximizedPanels, resetMaximization, updatePanelVisibility]);
187
+ }, [maximizedPanels, resetMaximization, adjustPanelVisibilityAndSize]);
179
188
  // Sets the visibility of a panel by its ID to a specific value (true = show, false = hide)
180
189
  const setPanelVisibilityById = useCallback((id, isVisible) => {
181
190
  // If we are trying to hide a panel that is currently maximized, first reset the maximization to restore normal view before hiding
@@ -183,12 +192,12 @@ export const TMPanelManagerProvider = (props) => {
183
192
  resetMaximization();
184
193
  }
185
194
  // Then update the visibility state of the panel to the given value
186
- setPanelVisibility(prev => updatePanelVisibility(id, isVisible, prev));
187
- }, [maximizedPanels, resetMaximization, updatePanelVisibility]);
195
+ setPanelVisibility(prev => adjustPanelVisibilityAndSize(id, isVisible, prev));
196
+ }, [maximizedPanels, resetMaximization, adjustPanelVisibilityAndSize]);
188
197
  // Sets the dimensions (width and height) of a specific panel by its ID
189
198
  const setPanelDimensionsById = useCallback((id, width, height) => {
190
199
  // Update the ref holding the initial dimensions
191
- initialPanelDimensionsRef.current = { ...initialPanelDimensionsRef.current, [id]: { width, height } };
200
+ defaultPanelDimensionsRef.current = { ...defaultPanelDimensionsRef.current, [id]: { width, height } };
192
201
  // Update the panel dimensions state
193
202
  setPanelDimensions(prev => ({ ...prev, [id]: { width, height } }));
194
203
  }, []);
@@ -222,7 +231,7 @@ export const TMPanelManagerProvider = (props) => {
222
231
  setToolbarButtonVisibility,
223
232
  setToolbarButtonDisabled,
224
233
  isResizingActive,
225
- updateIsResizingActive
234
+ updateIsResizingActive,
226
235
  }, children: children }));
227
236
  };
228
237
  export const useTMPanelManagerContext = () => {
@@ -0,0 +1,21 @@
1
+ import { ReactNode } from 'react';
2
+ import type { TMPanelDefinition, TMPanelDimensionsMap } from './types';
3
+ interface TMPanelManagerWithPersistenceProps {
4
+ children: ReactNode;
5
+ panels: Array<TMPanelDefinition>;
6
+ initialVisibility: {
7
+ [id: string]: boolean;
8
+ };
9
+ defaultDimensions: TMPanelDimensionsMap;
10
+ initialDimensions: TMPanelDimensionsMap;
11
+ initialMobilePanelId: string;
12
+ isPersistenceEnabled?: boolean;
13
+ persistPanelStates?: (state: Record<string, {
14
+ visible: boolean;
15
+ width: string;
16
+ height: string;
17
+ }>) => void;
18
+ persistedPanelStates?: Record<string, any>;
19
+ }
20
+ export declare const TMPanelManagerWithPersistenceProvider: (props: TMPanelManagerWithPersistenceProps) => import("react/jsx-runtime").JSX.Element | null;
21
+ export {};
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect, useState } from 'react';
3
+ import { TMPanelManagerProvider, useTMPanelManagerContext } from './TMPanelManagerContext';
4
+ export const TMPanelManagerWithPersistenceProvider = (props) => {
5
+ const { children, panels, initialVisibility, defaultDimensions, initialDimensions, initialMobilePanelId, isPersistenceEnabled = false, persistedPanelStates, persistPanelStates } = props;
6
+ // Track when persisted state is loaded
7
+ const [loaded, setLoaded] = useState(false);
8
+ // State for visibility and dimensions based on persisted state (or fallback to props)
9
+ const [persistedInitialVisibility, setPersistedInitialVisibility] = useState(initialVisibility);
10
+ const [persistedInitialDimensions, setPersistedInitialDimensions] = useState(initialDimensions);
11
+ // Effect to initialize panel state from persisted data
12
+ useEffect(() => {
13
+ const loadPersistedState = () => {
14
+ if (persistedPanelStates && typeof persistedPanelStates === 'object' && Object.keys(persistedPanelStates).length > 0) {
15
+ try {
16
+ const visibility = {};
17
+ const dimensions = {};
18
+ for (const [key, panel] of Object.entries(persistedPanelStates)) {
19
+ visibility[key] = panel.visible;
20
+ dimensions[key] = { width: panel.width, height: panel.height };
21
+ }
22
+ setPersistedInitialVisibility(visibility);
23
+ setPersistedInitialDimensions(dimensions);
24
+ }
25
+ catch (err) {
26
+ console.warn('Error retrieving data', err);
27
+ }
28
+ }
29
+ // Mark that initial load is complete (even if nothing is loaded)
30
+ setLoaded(true);
31
+ };
32
+ loadPersistedState();
33
+ }, []);
34
+ if (!loaded)
35
+ return null;
36
+ return (_jsxs(TMPanelManagerProvider, { panels: panels, initialVisibility: persistedInitialVisibility, defaultDimensions: defaultDimensions, initialDimensions: persistedInitialDimensions, initialMobilePanelId: initialMobilePanelId, isPersistenceEnabled: isPersistenceEnabled, children: [_jsx(PanelDimensionPersistenceEffect, { persistPanelStates: persistPanelStates }), children] }));
37
+ };
38
+ // Effect to watch for panel visibility and size changes and persist them
39
+ const PanelDimensionPersistenceEffect = (props) => {
40
+ const { persistPanelStates } = props;
41
+ const { panelDimensions, panelVisibility, maximizedPanels } = useTMPanelManagerContext();
42
+ // Track if this is the first time persistence effect runs after initialization
43
+ const didSkipFirstSave = React.useRef(false);
44
+ useEffect(() => {
45
+ if (Object.keys(panelVisibility).length === 0 || Object.keys(panelDimensions).length === 0)
46
+ return;
47
+ // Skip the very first save to avoid saving immediately after init
48
+ if (!didSkipFirstSave.current) {
49
+ didSkipFirstSave.current = true;
50
+ return;
51
+ }
52
+ // If any panel is maximized, don't persist to avoid saving intermediate states
53
+ if (maximizedPanels.length > 0)
54
+ return;
55
+ const panelState = {};
56
+ for (const panelKey in panelVisibility) {
57
+ const visibility = panelVisibility[panelKey] ?? false;
58
+ const dimensions = panelDimensions[panelKey] ?? { width: "0%", height: "0%" };
59
+ panelState[panelKey] = { visible: visibility, width: dimensions.width, height: dimensions.height };
60
+ }
61
+ persistPanelStates?.(panelState);
62
+ }, [panelVisibility, panelDimensions, maximizedPanels]);
63
+ return null;
64
+ };
@@ -14,8 +14,8 @@ const TMPanelWrapper = (props) => {
14
14
  // This avoids unnecessary re-renders by only recalculating when deviceType changes
15
15
  let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
16
16
  // Extract panel dimensions based on panel id
17
- const width = panelDimensions[panel.id].width;
18
- const height = panelDimensions[panel.id].height;
17
+ const width = panelDimensions[panel.id]?.width ?? '0%';
18
+ const height = panelDimensions[panel.id]?.height ?? '0%';
19
19
  // Determine visibility:
20
20
  // - If any panels are maximized, only show those maximized panels
21
21
  // - Otherwise, rely on the normal panel visibility state
@@ -55,13 +55,6 @@ export declare class AdvancedSettings {
55
55
  expertMode: number;
56
56
  };
57
57
  }
58
- export declare const defaultDcmtFormLayout: {
59
- [id: string]: {
60
- visible: boolean;
61
- width: string;
62
- height: string;
63
- };
64
- };
65
58
  export declare class DcmtFormSettings {
66
59
  TID: number | undefined;
67
60
  layout: {
@@ -115,16 +115,10 @@ export class AdvancedSettings {
115
115
  };
116
116
  }
117
117
  }
118
- export const defaultDcmtFormLayout = {
119
- 'tmDcmtForm': { visible: true, width: '20%', height: '100%' },
120
- 'tmBlog': { visible: false, width: '30%', height: '100%' },
121
- 'tmSysMetadata': { visible: false, width: '20%', height: '100%' },
122
- 'tmDcmtPreview': { visible: true, width: '30%', height: '100%' },
123
- };
124
118
  export class DcmtFormSettings {
125
119
  constructor() {
126
- this.layout = defaultDcmtFormLayout;
127
- this.layoutToDo = defaultDcmtFormLayout;
120
+ this.layout = {};
121
+ this.layoutToDo = {};
128
122
  }
129
123
  }
130
124
  export class SDKUI_Globals {
@@ -57,3 +57,7 @@ export declare function getContrastColor(inputColor: string): {
57
57
  bgColor: string;
58
58
  textColor: string;
59
59
  };
60
+ /** Adds TID to a list of "most recently used" (MRU) TIDs, keeping a maximum of 10 elements */
61
+ export declare function updateMruTids(currentMruTIDs: number[] | undefined | null, newTid: unknown): number[];
62
+ /** Remove TID to a list of "most recently used" (MRU) TIDs, keeping a maximum of 10 elements */
63
+ export declare function removeMruTid(currentMruTIDs: number[] | undefined | null, tidToRemove: unknown): number[];
@@ -718,3 +718,47 @@ export function getContrastColor(inputColor) {
718
718
  textColor: luminance > 0.6 ? '#2559A5' : '#FFFFFF'
719
719
  };
720
720
  }
721
+ // #endregion
722
+ /** Adds TID to a list of "most recently used" (MRU) TIDs, keeping a maximum of 10 elements */
723
+ export function updateMruTids(currentMruTIDs, newTid) {
724
+ // 1. Make sure newTid is a valid number.
725
+ // Using unknown forces validation.
726
+ // This is a best practice for robust functions that need to handle potentially non-compliant input.
727
+ const tidAsNumber = Number(newTid);
728
+ if (isNaN(tidAsNumber)) {
729
+ console.warn(`Tentativo di aggiungere un TID non numerico: ${newTid}. Ignorato.`);
730
+ return currentMruTIDs ? currentMruTIDs.filter(tid => tid != null) : [];
731
+ }
732
+ // 2. Initialize or copy existing MRU list.
733
+ let updatedMruTIDs = currentMruTIDs ? currentMruTIDs.filter(tid => tid != null) : [];
734
+ // 3. Remove the TID if it already exists to avoid duplicates and update its position.
735
+ const existingIndex = updatedMruTIDs.findIndex((o) => o === tidAsNumber);
736
+ if (existingIndex >= 0) {
737
+ updatedMruTIDs.splice(existingIndex, 1);
738
+ }
739
+ // 4. Remove oldest element if list exceeds maximum size of 10.
740
+ if (updatedMruTIDs.length >= 10) {
741
+ updatedMruTIDs.splice(0, 1); // Rimuove il primo elemento (il più vecchio)
742
+ }
743
+ // 5. Add new TID to queue (most recent).
744
+ updatedMruTIDs.push(tidAsNumber);
745
+ return updatedMruTIDs;
746
+ }
747
+ /** Remove TID to a list of "most recently used" (MRU) TIDs, keeping a maximum of 10 elements */
748
+ export function removeMruTid(currentMruTIDs, tidToRemove) {
749
+ // 1. Make sure tidToRemove is a valid number.
750
+ const tidAsNumber = Number(tidToRemove);
751
+ if (isNaN(tidAsNumber)) {
752
+ console.warn(`Tentativo di rimuovere un TID non numerico: ${tidToRemove}. Ignorato.`);
753
+ return currentMruTIDs ? currentMruTIDs.filter(tid => tid != null) : [];
754
+ }
755
+ // 2. Initialize or copy existing MRU list.
756
+ let updatedMruTIDs = currentMruTIDs ? currentMruTIDs.filter(tid => tid != null) : [];
757
+ // 3. Find the TID index to remove.
758
+ const indexToRemove = updatedMruTIDs.findIndex((o) => o === tidAsNumber);
759
+ // 4. If the TID is found, remove it from the list.
760
+ if (indexToRemove >= 0) {
761
+ updatedMruTIDs.splice(indexToRemove, 1);
762
+ }
763
+ return updatedMruTIDs;
764
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react-beta",
3
- "version": "6.14.15",
3
+ "version": "6.14.17",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",