@topconsultnpm/sdkui-react 6.20.0-dev1.76 → 6.20.0-dev1.78

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.
@@ -19,7 +19,9 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
19
19
  const submenuTimeoutRef = useRef(null);
20
20
  const longPressTimeoutRef = useRef(null);
21
21
  const touchStartPos = useRef(null);
22
- const { openLeft, openUp, isCalculated } = useMenuPosition(menuRef, menuState.position);
22
+ // Get current menu to pass items count to positioning hook
23
+ const currentMenu = menuState.submenuStack.at(-1) || items;
24
+ const { openLeft, openUp, isCalculated, needsScroll, maxHeight } = useMenuPosition(menuRef, menuState.position, currentMenu.length);
23
25
  const handleClose = () => {
24
26
  if (externalControl) {
25
27
  externalControl.onClose();
@@ -232,20 +234,26 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
232
234
  };
233
235
  document.addEventListener('mousedown', handleClickOutside);
234
236
  document.addEventListener('touchstart', handleClickOutside);
237
+ document.addEventListener('contextmenu', handleClickOutside); // Close on right-click outside
235
238
  return () => {
236
239
  document.removeEventListener('mousedown', handleClickOutside);
237
240
  document.removeEventListener('touchstart', handleClickOutside);
241
+ document.removeEventListener('contextmenu', handleClickOutside);
238
242
  };
239
243
  }, [menuState.visible]);
240
244
  const handleContextMenu = (e) => {
241
245
  if (trigger === 'right') {
242
246
  e.preventDefault();
243
- setMenuState({
244
- visible: true,
245
- position: { x: e.clientX, y: e.clientY },
246
- submenuStack: [items],
247
- parentNames: [],
248
- });
247
+ e.stopPropagation(); // Prevent event from bubbling to close other menus prematurely
248
+ // Small delay to ensure other menus receive the contextmenu event and close first
249
+ setTimeout(() => {
250
+ setMenuState({
251
+ visible: true,
252
+ position: { x: e.clientX, y: e.clientY },
253
+ submenuStack: [items],
254
+ parentNames: [],
255
+ });
256
+ }, 0);
249
257
  }
250
258
  };
251
259
  const handleClick = (e) => {
@@ -343,13 +351,17 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
343
351
  submenuTimeoutRef.current = null;
344
352
  }
345
353
  const rect = event.currentTarget.getBoundingClientRect();
346
- // Calculate if submenu should open upward based on available space
347
- // Estimate submenu height: ~35px per item (accounting for smaller padding) + container padding
354
+ // Calculate submenu height dynamically
348
355
  const estimatedSubmenuHeight = (item.submenu.length * 35) + 8;
349
356
  const spaceBelow = window.innerHeight - rect.top;
350
357
  const spaceAbove = rect.bottom;
351
- // Open upward only if there's not enough space below AND there's more space above
358
+ const padding = 8;
359
+ // Determine if submenu should open upward
352
360
  const shouldOpenUp = spaceBelow < estimatedSubmenuHeight && spaceAbove > spaceBelow;
361
+ // Calculate if submenu needs scroll and max-height
362
+ const availableSpace = shouldOpenUp ? spaceAbove : spaceBelow;
363
+ const needsScroll = estimatedSubmenuHeight > availableSpace - padding;
364
+ const maxHeight = needsScroll ? availableSpace - padding : undefined;
353
365
  // Remove all submenus at this depth and deeper
354
366
  setHoveredSubmenus(prev => {
355
367
  const filtered = prev.filter(sub => sub.depth < depth);
@@ -362,6 +374,8 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
362
374
  parentRect: rect,
363
375
  depth: depth,
364
376
  openUp: shouldOpenUp,
377
+ needsScroll,
378
+ maxHeight,
365
379
  }
366
380
  ];
367
381
  });
@@ -413,7 +427,6 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
413
427
  return (_jsxs(S.MenuItem, { "$disabled": item.disabled, "$hasSubmenu": !!item.submenu && item.submenu.length > 0, "$beginGroup": item.beginGroup, onMouseDown: handleClick, onMouseEnter: (e) => !isMobile && handleMouseEnter(item, e, depth + 1), onMouseLeave: () => !isMobile && handleMouseLeave(depth + 1), title: item.tooltip, children: [_jsxs(S.MenuItemContent, { children: [item.icon && _jsx(S.IconWrapper, { children: item.icon }), _jsx(S.MenuItemName, { children: item.name })] }), item.rightIcon && item.onRightIconClick && (_jsx(S.RightIconButton, { onClick: handleRightIconClick, onMouseDown: (e) => e.stopPropagation(), "aria-label": `Action for ${item.name}`, children: item.rightIcon })), item.submenu && item.submenu.length > 0 && (_jsx(S.SubmenuIndicator, { "$isMobile": isMobile, children: isMobile ? '›' : '▸' }))] }, itemKey));
414
428
  });
415
429
  };
416
- const currentMenu = menuState.submenuStack.at(-1) || items;
417
430
  const currentParentName = menuState.parentNames.at(-1) || '';
418
431
  return (_jsxs(_Fragment, { children: [!externalControl && children && (_jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, onClick: handleClick, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onTouchCancel: handleTouchEnd, onKeyDown: (e) => {
419
432
  if (e.key === 'Enter' || e.key === ' ') {
@@ -423,6 +436,6 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
423
436
  display: 'inline-block',
424
437
  WebkitTouchCallout: isIOS ? 'none' : undefined,
425
438
  WebkitUserSelect: isIOS ? 'none' : undefined,
426
- }, children: children })), menuState.visible && createPortal(_jsxs(_Fragment, { children: [_jsxs(S.MenuContainer, { ref: menuRef, "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }), document.body)] }));
439
+ }, children: children })), menuState.visible && createPortal(_jsxs(_Fragment, { children: [_jsxs(S.MenuContainer, { ref: menuRef, "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, "$needsScroll": needsScroll, "$maxHeight": maxHeight, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }), document.body)] }));
427
440
  };
428
441
  export default TMContextMenu;
@@ -5,10 +5,12 @@ interface Position {
5
5
  x: number;
6
6
  y: number;
7
7
  }
8
- export declare const useMenuPosition: (menuRef: React.RefObject<HTMLDivElement | null>, position: Position) => {
8
+ export declare const useMenuPosition: (menuRef: React.RefObject<HTMLDivElement | null>, position: Position, itemsCount?: number) => {
9
9
  isCalculated: boolean;
10
10
  openLeft: boolean;
11
11
  openUp: boolean;
12
+ needsScroll: boolean;
13
+ maxHeight: number | undefined;
12
14
  };
13
15
  export declare const getContextMenuTarget: <T extends {
14
16
  id: string;
@@ -38,8 +38,8 @@ export const useClickOutside = (callback) => {
38
38
  }, [callback]);
39
39
  return ref;
40
40
  };
41
- export const useMenuPosition = (menuRef, position) => {
42
- const [adjustedPosition, setAdjustedPosition] = useState({ openLeft: false, openUp: false });
41
+ export const useMenuPosition = (menuRef, position, itemsCount) => {
42
+ const [adjustedPosition, setAdjustedPosition] = useState({ openLeft: false, openUp: false, needsScroll: false, maxHeight: undefined });
43
43
  const [isCalculated, setIsCalculated] = useState(false);
44
44
  useLayoutEffect(() => {
45
45
  if (!menuRef.current) {
@@ -49,14 +49,52 @@ export const useMenuPosition = (menuRef, position) => {
49
49
  const menuRect = menuRef.current.getBoundingClientRect();
50
50
  const viewportWidth = window.innerWidth;
51
51
  const viewportHeight = window.innerHeight;
52
+ const isMobile = viewportWidth <= 768;
52
53
  const spaceRight = viewportWidth - position.x;
53
54
  const spaceBottom = viewportHeight - position.y;
55
+ const spaceAbove = position.y;
56
+ const padding = 8; // Minimal padding from viewport edges - be more aggressive about using available space
57
+ // Use scrollHeight to get natural content height, not constrained height
58
+ const menuHeight = menuRef.current.scrollHeight;
59
+ // Mobile: Always calculate max-height based on position to prevent overflow
60
+ if (isMobile) {
61
+ const maxHeightFromBottom = spaceBottom - padding;
62
+ const maxHeightFromTop = spaceAbove - padding;
63
+ const mobileMaxHeight = Math.max(maxHeightFromBottom, maxHeightFromTop);
64
+ setAdjustedPosition({
65
+ openLeft: spaceRight < menuRect.width + 20,
66
+ openUp: maxHeightFromTop > maxHeightFromBottom && menuHeight > maxHeightFromBottom,
67
+ needsScroll: menuHeight > mobileMaxHeight,
68
+ maxHeight: mobileMaxHeight,
69
+ });
70
+ setIsCalculated(true);
71
+ return;
72
+ }
73
+ // Desktop: Check if menu is too tall to fit in either direction
74
+ const fitsBelow = menuHeight + padding <= spaceBottom;
75
+ const fitsAbove = menuHeight + padding <= spaceAbove;
76
+ const needsScroll = !fitsBelow && !fitsAbove;
77
+ // Calculate max height when scrolling is needed
78
+ let maxHeight = undefined;
79
+ let shouldOpenUp = false;
80
+ if (needsScroll) {
81
+ // When scrolling is needed, open in the direction with MORE space
82
+ const availableSpace = Math.max(spaceBottom, spaceAbove);
83
+ maxHeight = availableSpace - padding;
84
+ shouldOpenUp = spaceAbove > spaceBottom; // Open upward if more space above
85
+ }
86
+ else {
87
+ // Normal behavior: open up only if it fits above but not below
88
+ shouldOpenUp = !fitsBelow && fitsAbove;
89
+ }
54
90
  setAdjustedPosition({
55
91
  openLeft: spaceRight < menuRect.width + 20,
56
- openUp: spaceBottom < menuRect.height + 20,
92
+ openUp: shouldOpenUp,
93
+ needsScroll,
94
+ maxHeight,
57
95
  });
58
96
  setIsCalculated(true);
59
- }, [position, menuRef]);
97
+ }, [position, menuRef, itemsCount]); // Added itemsCount to recalculate when menu content changes
60
98
  return { ...adjustedPosition, isCalculated };
61
99
  };
62
100
  export const getContextMenuTarget = (event, focusedItem, selectedItem, dataSource, idPrefix, isMobile) => {
@@ -5,6 +5,8 @@ export declare const MenuContainer: import("styled-components/dist/types").IStyl
5
5
  $openUp: boolean;
6
6
  $isPositioned: boolean;
7
7
  $externalControl?: boolean;
8
+ $needsScroll?: boolean;
9
+ $maxHeight?: number;
8
10
  }>> & string;
9
11
  export declare const MenuItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
10
12
  $disabled?: boolean;
@@ -23,6 +25,8 @@ export declare const SubmenuIndicator: import("styled-components/dist/types").IS
23
25
  export declare const Submenu: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
24
26
  $parentRect: DOMRect;
25
27
  $openUp?: boolean;
28
+ $needsScroll?: boolean;
29
+ $maxHeight?: number;
26
30
  }>> & string;
27
31
  export declare const MobileMenuHeader: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
28
32
  export declare const BackButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
@@ -38,6 +38,35 @@ export const MenuContainer = styled.div `
38
38
  border: 1px solid rgba(0, 0, 0, 0.06);
39
39
  opacity: ${props => props.$isPositioned ? 1 : 0};
40
40
  transition: opacity 0.05s ease-in;
41
+
42
+ /* Add scrolling when menu is too tall to fit in either direction */
43
+ ${props => props.$needsScroll && props.$maxHeight && `
44
+ max-height: ${props.$maxHeight}px;
45
+ overflow-y: auto;
46
+ overflow-x: hidden;
47
+
48
+ /* Smooth scrolling */
49
+ scroll-behavior: smooth;
50
+
51
+ /* Custom scrollbar styling */
52
+ &::-webkit-scrollbar {
53
+ width: 8px;
54
+ }
55
+
56
+ &::-webkit-scrollbar-track {
57
+ background: rgba(0, 0, 0, 0.05);
58
+ border-radius: 4px;
59
+ }
60
+
61
+ &::-webkit-scrollbar-thumb {
62
+ background: rgba(0, 0, 0, 0.2);
63
+ border-radius: 4px;
64
+ }
65
+
66
+ &::-webkit-scrollbar-thumb:hover {
67
+ background: rgba(0, 0, 0, 0.3);
68
+ }
69
+ `}
41
70
 
42
71
  /* Reset color inheritance from parent with !important to override panel header styles */
43
72
  & *:not(svg):not(.right-icon-btn):not(.right-icon-btn *) {
@@ -57,9 +86,9 @@ export const MenuContainer = styled.div `
57
86
 
58
87
  ${props => props.$externalControl && `
59
88
  @media (max-width: 768px) {
60
- left: 100px !important;
61
- right: 100px !important;
62
- max-width: calc(100vw - 200px);
89
+ left: 75px !important;
90
+ right: 75px !important;
91
+ max-width: calc(100vw - 150px);
63
92
  width: auto;
64
93
  min-width: auto;
65
94
  }
@@ -74,7 +103,7 @@ export const MenuItem = styled.div `
74
103
  transition: all 0.15s ease;
75
104
  position: relative;
76
105
  user-select: none;
77
- font-size: 13px;
106
+ font-size: var(--base-font-size, 13px);
78
107
  color: ${props => props.$disabled ? '#999' : '#1a1a1a'};
79
108
  font-weight: 500;
80
109
  ${props => props.$beginGroup && `
@@ -134,7 +163,7 @@ export const MenuItem = styled.div `
134
163
 
135
164
  @media (max-width: 768px) {
136
165
  padding: 4px 10px;
137
- font-size: 12px;
166
+ font-size: calc(var(--base-font-size, 13px) * 0.92);
138
167
  }
139
168
  `;
140
169
  export const MenuItemContent = styled.div `
@@ -147,7 +176,7 @@ export const IconWrapper = styled.span `
147
176
  display: flex;
148
177
  align-items: center;
149
178
  justify-content: center;
150
- font-size: 14px;
179
+ font-size: calc(var(--base-font-size, 13px) * 1.08);
151
180
  width: 18px;
152
181
  height: 18px;
153
182
  `;
@@ -170,7 +199,7 @@ export const RightIconButton = styled.button.attrs({
170
199
  padding: 4px 8px;
171
200
  margin-left: 8px;
172
201
  border-radius: 6px;
173
- font-size: 14px;
202
+ font-size: calc(var(--base-font-size, 13px) * 1.08);
174
203
  opacity: 0 !important;
175
204
  transition: opacity 0.15s ease, background 0.15s ease, transform 0.15s ease;
176
205
 
@@ -192,7 +221,7 @@ export const RightIconButton = styled.button.attrs({
192
221
  export const SubmenuIndicator = styled.span `
193
222
  display: flex;
194
223
  align-items: center;
195
- font-size: 12px;
224
+ font-size: calc(var(--base-font-size, 13px) * 0.92);
196
225
  margin-left: 8px;
197
226
  opacity: 0.6;
198
227
  transition: transform 0.15s ease;
@@ -272,6 +301,69 @@ export const Submenu = styled.div `
272
301
  [data-theme='dark'] & *:not(svg):not(.right-icon-btn):not(.right-icon-btn *) {
273
302
  color: #e0e0e0 !important;
274
303
  }
304
+
305
+ /* Dynamic scroll handling when submenu is too tall */
306
+ ${props => props.$needsScroll && props.$maxHeight ? `
307
+ max-height: ${props.$maxHeight}px;
308
+ overflow-y: auto;
309
+ overflow-x: hidden;
310
+
311
+ /* Custom scrollbar styling for submenus */
312
+ &::-webkit-scrollbar {
313
+ width: 8px;
314
+ }
315
+
316
+ &::-webkit-scrollbar-track {
317
+ background: rgba(0, 0, 0, 0.05);
318
+ border-radius: 4px;
319
+ }
320
+
321
+ &::-webkit-scrollbar-thumb {
322
+ background: rgba(0, 0, 0, 0.2);
323
+ border-radius: 4px;
324
+ }
325
+
326
+ &::-webkit-scrollbar-thumb:hover {
327
+ background: rgba(0, 0, 0, 0.3);
328
+ }
329
+ ` : `
330
+ /* Fallback max-height for submenus that fit */
331
+ max-height: calc(100vh - 40px);
332
+ overflow-y: auto;
333
+ overflow-x: hidden;
334
+
335
+ &::-webkit-scrollbar {
336
+ width: 8px;
337
+ }
338
+
339
+ &::-webkit-scrollbar-track {
340
+ background: rgba(0, 0, 0, 0.05);
341
+ border-radius: 4px;
342
+ }
343
+
344
+ &::-webkit-scrollbar-thumb {
345
+ background: rgba(0, 0, 0, 0.2);
346
+ border-radius: 4px;
347
+ }
348
+
349
+ &::-webkit-scrollbar-thumb:hover {
350
+ background: rgba(0, 0, 0, 0.3);
351
+ }
352
+ `}
353
+
354
+ [data-theme='dark'] & {
355
+ &::-webkit-scrollbar-track {
356
+ background: rgba(255, 255, 255, 0.05);
357
+ }
358
+
359
+ &::-webkit-scrollbar-thumb {
360
+ background: rgba(255, 255, 255, 0.2);
361
+ }
362
+
363
+ &::-webkit-scrollbar-thumb:hover {
364
+ background: rgba(255, 255, 255, 0.3);
365
+ }
366
+ }
275
367
  `;
276
368
  export const MobileMenuHeader = styled.div `
277
369
  display: flex;
@@ -307,7 +399,7 @@ export const BackButton = styled.button `
307
399
  `;
308
400
  export const HeaderTitle = styled.h3 `
309
401
  margin: 0;
310
- font-size: 16px;
402
+ font-size: calc(var(--base-font-size, 13px) * 1.23);
311
403
  font-weight: 600;
312
404
  color: #1a1a1a;
313
405
  flex: 1;
@@ -48,6 +48,7 @@ import { useCheckInOutOperations } from '../../../hooks/useCheckInOutOperations'
48
48
  import TMDcmtCheckoutInfoForm from './TMDcmtCheckoutInfoForm';
49
49
  import { useDataListItem } from '../../../hooks/useDataListItem';
50
50
  import { useDataUserIdItem } from '../../../hooks/useDataUserIdItem';
51
+ import { LoadIndicator } from 'devextreme-react';
51
52
  let abortControllerLocal = new AbortController();
52
53
  //#region Helper Methods
53
54
  export const getSearchResultCountersSingleCategory = (searchResults) => {
@@ -795,9 +796,6 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
795
796
  }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), (showSignSettingsForm && fromDTD) && _jsx(TMSignSettingsForm, { fromDTD: fromDTD, inputDcmts: allFieldSelectedDocs, onCloseSignSettingsForm: closeSignSettingsForm, onSavedAsyncCallback: handleSavedAsyncCallback }), (showHistory && fromDTD && getSelectedDcmtsOrFocused(selectedItems, focusedItem).length > 0) && _jsx(TMViewHistoryDcmt, { fromDTD: fromDTD, deviceType: deviceType, inputDcmt: getSelectedDcmtsOrFocused(selectedItems, focusedItem)[0], onClose: hideHistoryCallback, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), (commentFormState.show && getSelectedDcmtsOrFocused(selectedItems, focusedItem).length > 0) && _jsx(TMBlogCommentForm, { context: { engine: 'SearchEngine', object: { tid: getSelectedDcmtsOrFocused(selectedItems, focusedItem)[0].TID, did: getSelectedDcmtsOrFocused(selectedItems, focusedItem)[0].DID } }, onClose: hideCommentFormCallback, refreshCallback: triggerBlogRefresh, participants: [], showAttachmentsSection: true, allArchivedDocumentsFileItems: convertSearchResultDescriptorToFileItems(currentSearchResults ?? []), isCommentRequired: commentFormState.isRequired, removeAndEditAttachment: commentFormState.removeAndEditAttachment, selectedAttachmentDid: [Number(getSelectedDcmtsOrFocused(selectedItems, focusedItem)[0].DID)] })] }));
796
797
  };
797
798
  export default TMSearchResult;
798
- const renderDcmtIcon = (cellData, onDownloadDcmtsAsync, openInOffice) => {
799
- return _jsx(TMDcmtIcon, { tid: cellData.data.TID, did: cellData.data.DID, fileExtension: cellData.data.FILEEXT, fileCount: cellData.data.FILECOUNT, isLexProt: cellData.data.IsLexProt, isMail: cellData.data.ISMAIL, isShared: cellData.data.ISSHARED, isSigned: cellData.data.ISSIGNED, downloadMode: 'openInNewWindow', onDownloadDcmtsAsync: onDownloadDcmtsAsync, openInOffice: openInOffice });
800
- };
801
799
  const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem, showSearch, allowMultipleSelection = true, showExportForm = false, onCloseExportForm, onFocusedItemChanged, onDownloadDcmtsAsync, onVisibleItemChanged, inputSelectedItems = [], lastUpdateSearchTime, searchResult, floatingMenuItems, onSelectionChanged, onDblClick }) => {
802
800
  const [dataSource, setDataSource] = useState();
803
801
  const [columns, setColumns] = useState([]);
@@ -806,6 +804,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
806
804
  const [focusedItem, setFocusedItem] = useState();
807
805
  const [visibleItems, setVisibleItems] = useState([]);
808
806
  const [pageSize, setPageSize] = useState(SDKUI_Globals.userSettings.searchSettings?.pageSize ?? TMDataGridPageSize.Large);
807
+ const [isDataGridReady, setIsDataGridReady] = useState(false);
809
808
  const { loadDataListsAsync, renderDataListCell, dataListsCache } = useDataListItem();
810
809
  const { loadUsersAsync, renderUserIdViewer, usersCache } = useDataUserIdItem();
811
810
  useEffect(() => {
@@ -882,6 +881,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
882
881
  }
883
882
  }
884
883
  }, [inputSelectedItems, inputFocusedItem, dataSource]);
884
+ const cellRenderDcmtIcon = useCallback((cellData, onDownloadDcmtsAsync, openInOffice) => {
885
+ return _jsx(TMDcmtIcon, { tid: cellData.data.TID, did: cellData.data.DID, fileExtension: cellData.data.FILEEXT, fileCount: cellData.data.FILECOUNT, isLexProt: cellData.data.IsLexProt, isMail: cellData.data.ISMAIL, isShared: cellData.data.ISSHARED, isSigned: cellData.data.ISSIGNED, downloadMode: 'openInNewWindow', onDownloadDcmtsAsync: onDownloadDcmtsAsync, openInOffice: openInOffice });
886
+ }, []);
885
887
  const cellRender = useCallback((cellData, dataDomain, dataListID, dataListViewMode) => {
886
888
  if (!cellData || cellData.value === undefined)
887
889
  return null;
@@ -1040,6 +1042,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
1040
1042
  const loadColumnsAndData = async () => {
1041
1043
  // Resetta l'elemento focalizzato quando si caricano nuovi dati
1042
1044
  setFocusedItem(undefined);
1045
+ setIsDataGridReady(false);
1043
1046
  // Set per raccogliere gli ID univoci da precaricare
1044
1047
  const dataListIDs = new Set();
1045
1048
  const userIDs = new Set();
@@ -1076,6 +1079,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
1076
1079
  // Converte i risultati di ricerca in un array semplice per la griglia
1077
1080
  let newDataSource = searchResultDescriptorToSimpleArray(searchResult);
1078
1081
  setDataSource(newDataSource);
1082
+ setIsDataGridReady(true);
1079
1083
  };
1080
1084
  loadColumnsAndData();
1081
1085
  }, [searchResult, fromDTD, allUsers, loadDataListsAsync, loadUsersAsync, generateColumns]);
@@ -1126,10 +1130,6 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
1126
1130
  onDblClick();
1127
1131
  }, [onDblClick]);
1128
1132
  const dataColumns = useMemo(() => {
1129
- // Aspetta che le colonne siano completamente caricate
1130
- if (!columns || columns.length === 0) {
1131
- return [];
1132
- }
1133
1133
  return [
1134
1134
  {
1135
1135
  dataType: "object",
@@ -1137,7 +1137,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
1137
1137
  caption: '',
1138
1138
  visible: true,
1139
1139
  width: 50,
1140
- cellRender: (cellData) => renderDcmtIcon(cellData, onDownloadDcmtsAsync, openInOffice),
1140
+ cellRender: (cellData) => cellRenderDcmtIcon(cellData, onDownloadDcmtsAsync, openInOffice),
1141
1141
  allowResizing: false,
1142
1142
  filterOperations: ['=', "anyof"],
1143
1143
  allowHiding: false,
@@ -1154,7 +1154,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
1154
1154
  setVisibleItems(visibleRows.map((row) => { return row.data; }));
1155
1155
  }, []);
1156
1156
  useEffect(() => { onVisibleItemChanged?.(visibleItems); }, [visibleItems]);
1157
- return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx(TMDataGrid, { id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearch, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: pageSize, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: floatingMenuItems, counterConfig: { show: true } }), (showExportForm && searchResult && onCloseExportForm) && _jsx(TMDataGridExportForm, { dataColumns: dataColumns, dataSource: dataSource, selectedRowKeys: selectedRowKeys, onCloseExportForm: onCloseExportForm, searchResult: searchResult })] });
1157
+ return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [!isDataGridReady && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', gap: '10px' }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })), isDataGridReady && _jsx(TMDataGrid, { id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearch, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: pageSize, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: floatingMenuItems, counterConfig: { show: true } }), (showExportForm && searchResult && onCloseExportForm) && _jsx(TMDataGridExportForm, { dataColumns: dataColumns, dataSource: dataSource, selectedRowKeys: selectedRowKeys, onCloseExportForm: onCloseExportForm, searchResult: searchResult })] });
1158
1158
  };
1159
1159
  //#region TMSearchResultSelector
1160
1160
  const StyledItemTemplate = styled.div `
@@ -2,7 +2,7 @@ import { Appointment } from 'devextreme/ui/scheduler';
2
2
  import { ContextMenuTypes } from 'devextreme-react/context-menu';
3
3
  import { TaskDescriptor, Task_States, PdGs, Priorities, ValidationItem } from '@topconsultnpm/sdk-ts';
4
4
  import { FormModes, TaskContext } from '../../../ts';
5
- import { TMContextMenuItemProps } from '../../NewComponents/ContextMenu';
5
+ import { TMDataGridContextMenuItem } from '../../base/TMDataGrid';
6
6
  export declare const TEXT_SELECTED_COLOR = "#ff5e1a";
7
7
  export declare const BG_COLOR_INACTIVE_WIDGET = "#fff";
8
8
  export declare const BG_COLOR_ACTIVE_WIDGET = "#fff0b7";
@@ -100,7 +100,7 @@ export declare const gotoPDGExtendedLabel: (gotoVisible: boolean, pdg: PdGs, iD1
100
100
  export declare const convertToSchedulerAppointments: (tasks: Array<TaskDescriptor>) => Array<Appointment>;
101
101
  export declare const formatDate: (date: Date) => string;
102
102
  export declare const areDifferentIDs: (fromID: number | undefined, userID: number | undefined) => boolean;
103
- export declare const createTasksMenuItems: (taskDescriptor: TaskDescriptor | undefined, showId: boolean, setShowId: React.Dispatch<React.SetStateAction<boolean>>, showSearch: boolean, setShowSearch: React.Dispatch<React.SetStateAction<boolean>>, openTaskForm: (formMode: FormModes, task?: TaskDescriptor, isContextual?: boolean) => void, openEditTaskForm: (rowId: number | undefined) => void, openDuplicateTaskForm: (rowId: number | undefined) => void, onDeleteCallback: (rowIds: Array<number>) => void, markAsStatus: (rowIds: Array<number>, status: Task_States) => void, getAllTasks: () => Promise<void>, fromWG: boolean, showContextualWG: boolean, setShowContextualWG: React.Dispatch<React.SetStateAction<boolean>>, fromDossier: boolean, showContextualDossier: boolean, setShowContextualDossier: React.Dispatch<React.SetStateAction<boolean>>, fromDocument: boolean, showContextualDocument: boolean, setShowContextualDocument: React.Dispatch<React.SetStateAction<boolean>>, showGoToToday: boolean, handleGoToToday?: () => void, fromDatagrid?: boolean) => TMContextMenuItemProps[];
103
+ export declare const createTasksMenuItems: (taskDescriptor: TaskDescriptor | undefined, showId: boolean, setShowId: React.Dispatch<React.SetStateAction<boolean>>, showSearch: boolean, setShowSearch: React.Dispatch<React.SetStateAction<boolean>>, openTaskForm: (formMode: FormModes, task?: TaskDescriptor, isContextual?: boolean) => void, openEditTaskForm: (rowId: number | undefined) => void, openDuplicateTaskForm: (rowId: number | undefined) => void, onDeleteCallback: (rowIds: Array<number>) => void, markAsStatus: (rowIds: Array<number>, status: Task_States) => void, getAllTasks: () => Promise<void>, fromWG: boolean, showContextualWG: boolean, setShowContextualWG: React.Dispatch<React.SetStateAction<boolean>>, fromDossier: boolean, showContextualDossier: boolean, setShowContextualDossier: React.Dispatch<React.SetStateAction<boolean>>, fromDocument: boolean, showContextualDocument: boolean, setShowContextualDocument: React.Dispatch<React.SetStateAction<boolean>>, showGoToToday: boolean, handleGoToToday?: () => void, fromDatagrid?: boolean) => Array<TMDataGridContextMenuItem>;
104
104
  export declare const checkIfNew: (fromId: number | undefined, isNew: number | undefined) => boolean;
105
105
  export declare const getNewTaskCount: (tasks: Array<TaskDescriptor>) => number;
106
106
  export declare const isTaskAssignedToAnotherUser: (task: TaskDescriptor) => boolean;