@topconsultnpm/sdkui-react 6.20.0-dev1.11 → 6.20.0-dev1.111

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.
Files changed (177) hide show
  1. package/lib/assets/Toppy-help-center.png +0 -0
  2. package/lib/assets/headergradient.svg +87 -0
  3. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +285 -28
  4. package/lib/components/NewComponents/ContextMenu/hooks.d.ts +8 -1
  5. package/lib/components/NewComponents/ContextMenu/hooks.js +80 -8
  6. package/lib/components/NewComponents/ContextMenu/index.d.ts +3 -0
  7. package/lib/components/NewComponents/ContextMenu/index.js +2 -0
  8. package/lib/components/NewComponents/ContextMenu/styles.d.ts +9 -1
  9. package/lib/components/NewComponents/ContextMenu/styles.js +157 -37
  10. package/lib/components/NewComponents/ContextMenu/types.d.ts +14 -1
  11. package/lib/components/NewComponents/ContextMenu/useLongPress.d.ts +21 -0
  12. package/lib/components/NewComponents/ContextMenu/useLongPress.js +112 -0
  13. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +563 -112
  14. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +21 -5
  15. package/lib/components/NewComponents/FloatingMenuBar/styles.js +210 -58
  16. package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +8 -2
  17. package/lib/components/base/TMAccordionNew.js +35 -14
  18. package/lib/components/base/TMCustomButton.js +61 -17
  19. package/lib/components/base/TMDataGrid.d.ts +7 -4
  20. package/lib/components/base/TMDataGrid.js +153 -11
  21. package/lib/components/base/TMDropDownMenu.js +19 -18
  22. package/lib/components/base/TMFileManager.d.ts +4 -3
  23. package/lib/components/base/TMFileManager.js +32 -24
  24. package/lib/components/base/TMFileManagerDataGridView.d.ts +3 -2
  25. package/lib/components/base/TMFileManagerDataGridView.js +1 -11
  26. package/lib/components/base/TMFileManagerThumbnailItems.d.ts +7 -1
  27. package/lib/components/base/TMFileManagerThumbnailItems.js +5 -2
  28. package/lib/components/base/TMFileManagerThumbnailsView.d.ts +17 -4
  29. package/lib/components/base/TMFileManagerThumbnailsView.js +18 -6
  30. package/lib/components/base/TMFileManagerUtils.d.ts +0 -12
  31. package/lib/components/base/TMListView.js +33 -15
  32. package/lib/components/base/TMPanel.d.ts +1 -1
  33. package/lib/components/base/TMPanel.js +1 -1
  34. package/lib/components/base/TMTreeView.d.ts +2 -1
  35. package/lib/components/base/TMTreeView.js +33 -26
  36. package/lib/components/choosers/TMDistinctValues.js +2 -2
  37. package/lib/components/choosers/TMInvoiceRetrieveFormats.js +1 -1
  38. package/lib/components/choosers/TMMetadataChooser.js +8 -1
  39. package/lib/components/choosers/TMOrderRetrieveFormats.js +1 -1
  40. package/lib/components/choosers/TMUserChooser.d.ts +0 -5
  41. package/lib/components/choosers/TMUserChooser.js +25 -45
  42. package/lib/components/editors/TMDateBox.js +18 -9
  43. package/lib/components/editors/TMLocalizedTextBox.d.ts +3 -1
  44. package/lib/components/editors/TMLocalizedTextBox.js +16 -14
  45. package/lib/components/editors/TMMetadataTextBox.d.ts +9 -0
  46. package/lib/components/editors/TMMetadataTextBox.js +92 -0
  47. package/lib/components/editors/TMMetadataValues.js +23 -5
  48. package/lib/components/editors/TMTextArea.js +18 -30
  49. package/lib/components/editors/TMTextBox.d.ts +1 -1
  50. package/lib/components/editors/TMTextBox.js +6 -3
  51. package/lib/components/editors/TMTextExpression.js +6 -91
  52. package/lib/components/features/archive/TMArchive.js +2 -2
  53. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.d.ts +15 -0
  54. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +460 -0
  55. package/lib/components/features/assistant/TMToppySpeechBubble.d.ts +11 -0
  56. package/lib/components/features/assistant/TMToppySpeechBubble.js +126 -0
  57. package/lib/components/features/documents/TMDcmtForm.d.ts +14 -2
  58. package/lib/components/features/documents/TMDcmtForm.js +457 -206
  59. package/lib/components/features/documents/TMDcmtPreview.js +44 -110
  60. package/lib/components/features/documents/TMDcmtTasks.js +9 -9
  61. package/lib/components/features/documents/TMMasterDetailDcmts.js +38 -53
  62. package/lib/components/features/documents/TMRelationViewer.d.ts +1 -1
  63. package/lib/components/features/documents/TMRelationViewer.js +2 -2
  64. package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +8 -0
  65. package/lib/components/features/search/{TMSearchResultCheckoutInfoForm.js → TMDcmtCheckoutInfoForm.js} +2 -2
  66. package/lib/components/features/search/TMSavedQuerySelector.js +72 -67
  67. package/lib/components/features/search/TMSearch.d.ts +3 -0
  68. package/lib/components/features/search/TMSearch.js +50 -11
  69. package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -0
  70. package/lib/components/features/search/TMSearchQueryPanel.js +29 -21
  71. package/lib/components/features/search/TMSearchResult.d.ts +3 -0
  72. package/lib/components/features/search/TMSearchResult.js +208 -250
  73. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +3 -3
  74. package/lib/components/features/search/TMSearchResultsMenuItems.js +205 -169
  75. package/lib/components/features/search/TMSignSettingsForm.js +1 -1
  76. package/lib/components/features/search/TMSignatureInfoContent.d.ts +6 -0
  77. package/lib/components/features/search/TMSignatureInfoContent.js +140 -0
  78. package/lib/components/features/search/TMViewHistoryDcmt.js +2 -2
  79. package/lib/components/features/tasks/TMTaskForm.js +20 -1
  80. package/lib/components/features/tasks/TMTasksAgenda.d.ts +3 -1
  81. package/lib/components/features/tasks/TMTasksAgenda.js +48 -9
  82. package/lib/components/features/tasks/TMTasksCalendar.d.ts +2 -0
  83. package/lib/components/features/tasks/TMTasksCalendar.js +19 -7
  84. package/lib/components/features/tasks/TMTasksUtils.d.ts +2 -2
  85. package/lib/components/features/tasks/TMTasksUtils.js +43 -36
  86. package/lib/components/features/tasks/TMTasksView.js +28 -19
  87. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +33 -2
  88. package/lib/components/features/workflow/TMWorkflowPopup.js +139 -34
  89. package/lib/components/features/workflow/diagram/DiagramItemComponent.d.ts +2 -0
  90. package/lib/components/features/workflow/diagram/DiagramItemComponent.js +12 -7
  91. package/lib/components/features/workflow/diagram/DiagramItemForm.js +1 -1
  92. package/lib/components/features/workflow/diagram/RecipientList.js +3 -2
  93. package/lib/components/features/workflow/diagram/WFDiagram.d.ts +4 -0
  94. package/lib/components/features/workflow/diagram/WFDiagram.js +164 -13
  95. package/lib/components/forms/Login/LoginValidatorService.d.ts +2 -0
  96. package/lib/components/forms/Login/LoginValidatorService.js +7 -2
  97. package/lib/components/forms/Login/TMLoginForm.js +34 -6
  98. package/lib/components/forms/TMChooserForm.js +1 -1
  99. package/lib/components/grids/TMBlogsPost.js +56 -31
  100. package/lib/components/grids/TMRecentsManager.js +20 -10
  101. package/lib/components/index.d.ts +6 -3
  102. package/lib/components/index.js +6 -3
  103. package/lib/components/query/TMQueryEditor.d.ts +2 -1
  104. package/lib/components/query/TMQueryEditor.js +92 -92
  105. package/lib/components/settings/SettingsAppearance.d.ts +2 -1
  106. package/lib/components/settings/SettingsAppearance.js +99 -30
  107. package/lib/components/sidebar/TMHeader.js +7 -7
  108. package/lib/components/sidebar/TMSidebar.d.ts +0 -1
  109. package/lib/components/sidebar/TMSidebar.js +16 -44
  110. package/lib/components/sidebar/TMSidebarItem.js +34 -17
  111. package/lib/components/viewers/TMDataListItemViewer.d.ts +2 -1
  112. package/lib/components/viewers/TMDataListItemViewer.js +35 -71
  113. package/lib/components/viewers/TMDataUserIdItemViewer.d.ts +8 -0
  114. package/lib/components/viewers/TMDataUserIdItemViewer.js +39 -0
  115. package/lib/css/tm-sdkui.css +1 -1
  116. package/lib/helper/SDKUI_Globals.d.ts +22 -0
  117. package/lib/helper/SDKUI_Globals.js +10 -1
  118. package/lib/helper/SDKUI_Localizator.d.ts +17 -1
  119. package/lib/helper/SDKUI_Localizator.js +167 -1
  120. package/lib/helper/TMCommandsContextMenu.d.ts +4 -2
  121. package/lib/helper/TMCommandsContextMenu.js +15 -4
  122. package/lib/helper/TMIcons.d.ts +4 -0
  123. package/lib/helper/TMIcons.js +13 -3
  124. package/lib/helper/TMPdfViewer.d.ts +8 -0
  125. package/lib/helper/TMPdfViewer.js +373 -0
  126. package/lib/helper/checkinCheckoutManager.d.ts +31 -1
  127. package/lib/helper/checkinCheckoutManager.js +112 -30
  128. package/lib/helper/devextremeCustomMessages.d.ts +30 -0
  129. package/lib/helper/devextremeCustomMessages.js +30 -0
  130. package/lib/helper/helpers.d.ts +28 -1
  131. package/lib/helper/helpers.js +130 -3
  132. package/lib/helper/index.d.ts +2 -0
  133. package/lib/helper/index.js +2 -0
  134. package/lib/helper/queryHelper.d.ts +1 -1
  135. package/lib/helper/queryHelper.js +33 -3
  136. package/lib/helper/workItemsHelper.d.ts +6 -0
  137. package/lib/helper/workItemsHelper.js +230 -0
  138. package/lib/hooks/useCheckInOutOperations.d.ts +28 -0
  139. package/lib/hooks/useCheckInOutOperations.js +223 -0
  140. package/lib/hooks/useDataListItem.d.ts +12 -0
  141. package/lib/hooks/useDataListItem.js +132 -0
  142. package/lib/hooks/useDataUserIdItem.d.ts +10 -0
  143. package/lib/hooks/useDataUserIdItem.js +96 -0
  144. package/lib/hooks/useMetadataExpression.d.ts +19 -0
  145. package/lib/hooks/useMetadataExpression.js +99 -0
  146. package/lib/hooks/useSettingsFeedback.d.ts +11 -0
  147. package/lib/hooks/useSettingsFeedback.js +38 -0
  148. package/lib/hooks/useWorkflowApprove.d.ts +4 -0
  149. package/lib/hooks/useWorkflowApprove.js +14 -1
  150. package/lib/index.d.ts +1 -0
  151. package/lib/index.js +3 -2
  152. package/lib/services/platform_services.d.ts +3 -3
  153. package/lib/ts/types.d.ts +61 -1
  154. package/lib/utils/theme.d.ts +1 -1
  155. package/lib/utils/theme.js +1 -1
  156. package/package.json +7 -4
  157. package/lib/components/NewComponents/Notification/Notification.d.ts +0 -4
  158. package/lib/components/NewComponents/Notification/Notification.js +0 -60
  159. package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +0 -8
  160. package/lib/components/NewComponents/Notification/NotificationContainer.js +0 -33
  161. package/lib/components/NewComponents/Notification/index.d.ts +0 -2
  162. package/lib/components/NewComponents/Notification/index.js +0 -2
  163. package/lib/components/NewComponents/Notification/styles.d.ts +0 -21
  164. package/lib/components/NewComponents/Notification/styles.js +0 -180
  165. package/lib/components/NewComponents/Notification/types.d.ts +0 -18
  166. package/lib/components/NewComponents/Notification/types.js +0 -1
  167. package/lib/components/base/TMContextMenu.d.ts +0 -25
  168. package/lib/components/base/TMContextMenu.js +0 -109
  169. package/lib/components/base/TMContextMenuOLD.d.ts +0 -26
  170. package/lib/components/base/TMContextMenuOLD.js +0 -56
  171. package/lib/components/base/TMFloatingToolbar.d.ts +0 -9
  172. package/lib/components/base/TMFloatingToolbar.js +0 -101
  173. package/lib/components/features/assistant/ToppyDraggableHelpCenter.d.ts +0 -30
  174. package/lib/components/features/assistant/ToppyDraggableHelpCenter.js +0 -482
  175. package/lib/components/features/assistant/ToppySpeechBubble.d.ts +0 -9
  176. package/lib/components/features/assistant/ToppySpeechBubble.js +0 -117
  177. package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +0 -8
@@ -4,31 +4,68 @@ import TMModal from './TMModal';
4
4
  import styled from 'styled-components';
5
5
  import { SDKUI_Localizator, TMLayoutWaitingContainer } from '../..';
6
6
  import { getButtonAttributes, getSelectedItem } from '../../helper/dcmtsHelper';
7
+ import { DeviceType, useDeviceType } from './TMDeviceProvider';
7
8
  const IframeContainer = styled.div `
8
9
  display: flex;
9
10
  height: 100%;
10
11
  flex-direction: column;
11
- padding-left: 15px;
12
+ position: relative;
12
13
  `;
13
14
  const StyledIframe = styled.iframe `
14
15
  border: none;
15
16
  flex: 1;
16
17
  `;
18
+ const LoadingOverlay = styled.div `
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ right: 0;
23
+ bottom: 0;
24
+ background-color: rgba(128, 128, 128, 0.3);
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ z-index: 1000;
29
+
30
+ &::after {
31
+ content: '';
32
+ width: 40px;
33
+ height: 40px;
34
+ border: 4px solid rgba(255, 255, 255, 0.3);
35
+ border-top-color: #fff;
36
+ border-radius: 50%;
37
+ animation: spin 0.8s linear infinite;
38
+ }
39
+
40
+ @keyframes spin {
41
+ to {
42
+ transform: rotate(360deg);
43
+ }
44
+ }
45
+ `;
17
46
  const TMCustomButton = (props) => {
18
47
  const { button, isModal = true, formData, selectedItems, onClose } = props;
19
48
  const { appName: scriptUrl, arguments: args } = button;
49
+ const Device = useDeviceType();
20
50
  const iframeRef = useRef(null);
21
51
  const attributes = useMemo(() => getButtonAttributes(args, formData, selectedItems), [args, formData, selectedItems]);
22
52
  const RunOnce = button.mode === "RunOnce";
23
53
  const [loading, setLoading] = useState(true);
24
54
  const [error, setError] = useState(false);
25
- const selectedItemsCount = selectedItems?.length || 0;
55
+ const itemsToProcess = useMemo(() => selectedItems && selectedItems.length > 0 ? selectedItems : [attributes], [selectedItems, attributes]);
26
56
  // Stati per il wait panel
27
- const [showWaitPanel, setShowWaitPanel] = useState(selectedItemsCount > 0 && !RunOnce);
28
- const [waitPanelText, setWaitPanelText] = useState(SDKUI_Localizator.CustomButtonActions.replaceParams(1, selectedItemsCount));
57
+ const [showWaitPanel, setShowWaitPanel] = useState(itemsToProcess.length > 0 && !RunOnce);
58
+ const [waitPanelText, setWaitPanelText] = useState(SDKUI_Localizator.CustomButtonActions.replaceParams(1, itemsToProcess.length));
29
59
  const [waitPanelValue, setWaitPanelValue] = useState(0);
30
- const [waitPanelMaxValue, setWaitPanelMaxValue] = useState(selectedItemsCount);
60
+ const [waitPanelMaxValue, setWaitPanelMaxValue] = useState(itemsToProcess.length);
31
61
  const [abortController, setAbortController] = useState(undefined);
62
+ // Aggiungi timestamp all'URL per evitare cache
63
+ const iframeUrl = useMemo(() => {
64
+ if (!scriptUrl)
65
+ return '';
66
+ const separator = scriptUrl.includes('?') ? '&' : '?';
67
+ return `${scriptUrl}${separator}t=${Date.now()}`;
68
+ }, [scriptUrl]);
32
69
  const targetOrigin = useMemo(() => {
33
70
  if (!scriptUrl)
34
71
  return '*';
@@ -41,26 +78,35 @@ const TMCustomButton = (props) => {
41
78
  }
42
79
  }, [scriptUrl]);
43
80
  const handleLoad = () => setLoading(false);
81
+ const isMobile = Device === DeviceType.MOBILE;
44
82
  const handleError = () => {
45
83
  setLoading(false);
46
84
  setError(true);
47
85
  };
48
86
  const executeSequentially = async (controller) => {
49
- if (!selectedItems)
87
+ if (!itemsToProcess)
50
88
  return;
51
- for (const [index, item] of selectedItems.entries()) {
89
+ for (const [index, item] of itemsToProcess.entries()) {
52
90
  if (controller.signal.aborted)
53
91
  break;
54
- setWaitPanelText(SDKUI_Localizator.CustomButtonActions.replaceParams(index + 1, selectedItemsCount));
92
+ setWaitPanelText(SDKUI_Localizator.CustomButtonActions.replaceParams(index + 1, itemsToProcess.length));
55
93
  setWaitPanelValue(index);
56
94
  // Attendi che l'iframe sia pronto e invia il messaggio
57
95
  await new Promise((resolve) => {
58
96
  const checkIframe = setInterval(() => {
59
97
  if (iframeRef.current?.contentWindow) {
60
98
  clearInterval(checkIframe);
61
- //devo convertire item in formData
62
- const processedItem = getSelectedItem(args, formData, item);
63
- postMessageIframe(processedItem);
99
+ if (selectedItems && selectedItems.length > 0) {
100
+ //devo convertire item in formData
101
+ const processedItem = getSelectedItem(args, formData, item);
102
+ postMessageIframe(processedItem);
103
+ }
104
+ else {
105
+ postMessageIframe(item);
106
+ }
107
+ //imposta 100% se sono all'ultimo item
108
+ if (index === itemsToProcess.length - 1)
109
+ setWaitPanelValue(index + 1);
64
110
  // Attendi prima di passare al prossimo
65
111
  setTimeout(() => {
66
112
  setWaitPanelValue(index + 1);
@@ -82,8 +128,7 @@ const TMCustomButton = (props) => {
82
128
  useEffect(() => {
83
129
  if (loading || error)
84
130
  return;
85
- //if(error) clearTimeout(timeoutIframe);
86
- if (!RunOnce && selectedItemsCount > 0) {
131
+ if (!RunOnce && itemsToProcess.length > 0) {
87
132
  // esegui per ogni item selezionato
88
133
  const controller = new AbortController();
89
134
  controller.signal.addEventListener('abort', () => {
@@ -91,7 +136,7 @@ const TMCustomButton = (props) => {
91
136
  onClose?.();
92
137
  });
93
138
  setAbortController(controller);
94
- setWaitPanelMaxValue(selectedItemsCount);
139
+ setWaitPanelMaxValue(itemsToProcess.length);
95
140
  executeSequentially(controller);
96
141
  }
97
142
  else {
@@ -103,7 +148,6 @@ const TMCustomButton = (props) => {
103
148
  onClose?.();
104
149
  }, 2000);
105
150
  }
106
- //clearTimeout(timeoutIframe);
107
151
  }
108
152
  }, [loading, error, RunOnce]);
109
153
  useEffect(() => {
@@ -112,7 +156,7 @@ const TMCustomButton = (props) => {
112
156
  onClose?.();
113
157
  }
114
158
  }, []);
115
- const iframeContent = (_jsxs(IframeContainer, { style: !RunOnce ? { visibility: 'hidden' } : {}, children: [error && _jsx("div", { children: "Si \u00E8 verificato un errore nel caricamento del contenuto." }), !error && _jsx(StyledIframe, { ref: iframeRef, loading: 'lazy', onLoad: handleLoad, onError: handleError, src: scriptUrl })] }));
116
- return isModal && RunOnce ? (_jsx(TMModal, { title: button.title, width: '60%', height: '70%', resizable: true, expandable: true, onClose: onClose, children: iframeContent })) : !RunOnce && (_jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { showWaitPanel: showWaitPanel, waitPanelTitle: SDKUI_Localizator.CustomButtonAction, showWaitPanelPrimary: true, waitPanelTextPrimary: waitPanelText, waitPanelValuePrimary: waitPanelValue, waitPanelMaxValuePrimary: waitPanelMaxValue, showWaitPanelSecondary: false, isCancelable: true, abortController: abortController, children: undefined }), iframeContent] }));
159
+ const iframeContent = (_jsxs(IframeContainer, { style: !RunOnce ? { visibility: 'hidden' } : {}, children: [loading && _jsx(LoadingOverlay, {}), error && _jsx("div", { children: "Si \u00E8 verificato un errore nel caricamento del contenuto." }), !error && _jsx(StyledIframe, { ref: iframeRef, loading: 'lazy', onLoad: handleLoad, onError: handleError, src: iframeUrl })] }));
160
+ return isModal && RunOnce ? (_jsx(TMModal, { title: button.title, width: isMobile ? '95%' : '60%', height: isMobile ? '95%' : '70%', resizable: isMobile ? false : true, expandable: isMobile ? false : true, onClose: onClose, children: iframeContent })) : !RunOnce && (_jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { showWaitPanel: showWaitPanel, waitPanelTitle: SDKUI_Localizator.CustomButtonAction, showWaitPanelPrimary: true, waitPanelTextPrimary: waitPanelText, waitPanelValuePrimary: waitPanelValue, waitPanelMaxValuePrimary: waitPanelMaxValue, showWaitPanelSecondary: false, isCancelable: true, abortController: abortController, children: undefined }), iframeContent] }));
117
161
  };
118
162
  export default TMCustomButton;
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { IColumnProps, IDataGridOptions, IMasterDetailProps } from 'devextreme-react/data-grid';
3
3
  import dxDataGrid from 'devextreme/ui/data_grid';
4
4
  import { ITMCounterContainerProps } from './TMCounterContainer';
5
+ import { TMContextMenuItemProps } from '../NewComponents/ContextMenu/types';
5
6
  export interface TMDataGridContextMenuItem {
6
7
  text: string;
7
8
  icon: string;
@@ -31,16 +32,16 @@ export interface TMDataGridProps<T> extends IDataGridOptions {
31
32
  pageSize?: TMDataGridPageSize;
32
33
  /** Configures the search panel position in the toolbar */
33
34
  searchPanelToolbarPosition?: 'before' | 'default';
34
- /** if visible, set focus on SearchPanel */
35
- searchPanelFocusStarting?: boolean;
35
+ /** Trigger to set focus on SearchPanel (only if visible) - change the number to trigger focus */
36
+ searchPanelFocusTrigger?: number;
36
37
  /** Show the header filter */
37
38
  showHeaderFilter?: boolean;
38
39
  /** Show the filter panel */
39
40
  showFilterPanel?: boolean;
40
41
  /** Show the load panel */
41
42
  showLoadPanel?: boolean;
42
- /** Show the column chooser */
43
- showColumnChooser?: boolean;
43
+ /** Show the header column chooser in context menu */
44
+ showHeaderColumnChooser?: boolean;
44
45
  /** Show the search panel */
45
46
  showSearchPanel?: boolean;
46
47
  /** Show the group panel */
@@ -51,6 +52,8 @@ export interface TMDataGridProps<T> extends IDataGridOptions {
51
52
  masterDetail?: IMasterDetailProps;
52
53
  /** On Has Filters Change */
53
54
  onHasFiltersChange?: (hasFilters: boolean) => void;
55
+ /** Custom context menu items - when provided, replaces DevExtreme's native context menu with TMContextMenu */
56
+ customContextMenuItems?: TMContextMenuItemProps[];
54
57
  }
55
58
  declare const TMDataGrid: React.ForwardRefExoticComponent<TMDataGridProps<unknown> & React.RefAttributes<dxDataGrid<any, any>>>;
56
59
  export default TMDataGrid;
@@ -1,9 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
- import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail } from 'devextreme-react/data-grid';
3
+ import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail, Position, ColumnChooserSearch, ColumnChooserSelection } from 'devextreme-react/data-grid';
4
4
  import DataSource from 'devextreme/data/data_source';
5
5
  import { IconAll, IconSelected, IconVisible, SDKUI_Globals, SDKUI_Localizator } from '../../helper';
6
6
  import TMCounterContainer, { CounterItemKey } from './TMCounterContainer';
7
+ import TMContextMenu from '../NewComponents/ContextMenu/TMContextMenu';
8
+ import { useLongPress } from '../NewComponents/ContextMenu/useLongPress';
7
9
  ;
8
10
  export var TMDataGridPageSize;
9
11
  (function (TMDataGridPageSize) {
@@ -16,9 +18,9 @@ const TMDataGrid = React.forwardRef((props, ref) => {
16
18
  // main properties
17
19
  keyExpr = 'id', dataSource, focusedRowEnabled = true, hoverStateEnabled = true, focusedRowKey, selectedRowKeys = [],
18
20
  // custom options
19
- dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusStarting = false, counterConfig = { show: false, items: new Map() }, onHasFiltersChange,
21
+ dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showHeaderColumnChooser = false, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusTrigger = 0, counterConfig = { show: false, items: new Map() }, onHasFiltersChange, customContextMenuItems,
20
22
  // events and callbacks
21
- onSelectionChanged, onFocusedRowChanged, onRowDblClick, onRowClick, onCellClick, onCellDblClick, onOptionChanged, onContentReady, onContextMenuPreparing, onInitialized, onEditorPreparing, onCellPrepared, onRowPrepared, onRowUpdating, onRowExpanded, onRowCollapsed, onRowUpdated, onSaved, onEditCanceled, onEditingStart, onEditingChange, customizeColumns, onKeyDown, scrolling = { mode: 'standard', useNative: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1 }, paging = { enabled: true, pageSize: pageSize }, pager = { visible: true, showInfo: true, showNavigationButtons: true }, selection = { mode: 'multiple', showCheckBoxesMode: "always", selectAllMode: "allPages" }, sorting, summary, stateStoring, columnChooser, grouping, groupPanel, filterRow, headerFilter, editing, rowDragging, masterDetail,
23
+ onSelectionChanged, onFocusedRowChanged, onRowDblClick, onRowClick, onCellClick, onCellDblClick, onOptionChanged, onContentReady, onContextMenuPreparing, onInitialized, onEditorPreparing, onCellPrepared, onRowPrepared, onRowUpdating, onRowExpanded, onRowCollapsed, onRowUpdated, onSaved, onEditCanceled, onEditingStart, onEditingChange, customizeColumns, onKeyDown, scrolling = { mode: 'standard', useNative: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1 }, paging = { enabled: true, pageSize: pageSize }, pager = { visible: true, showInfo: true, showNavigationButtons: true }, selection = { mode: 'multiple', showCheckBoxesMode: "always", selectAllMode: "allPages" }, sorting, summary, stateStoring, grouping, groupPanel, filterRow, headerFilter, editing, rowDragging, masterDetail,
22
24
  // other properties
23
25
  disabled = false, autoNavigateToFocusedRow = true, columnResizingMode = 'widget', columnHidingEnabled = true, columnAutoWidth = true, allowColumnResizing = true, allowColumnReordering = true, showBorders = true, showRowLines = SDKUI_Globals.userSettings?.themeSettings.gridSettings.showRowLines === 1, showColumnLines = SDKUI_Globals.userSettings?.themeSettings.gridSettings.showColumnLines === 1, showColumnHeaders = true, rowAlternationEnabled = false, wordWrapEnabled = false, noDataText,
24
26
  // styles
@@ -29,10 +31,84 @@ const TMDataGrid = React.forwardRef((props, ref) => {
29
31
  const [totalRecordCount, setTotalRecordCount] = useState(0);
30
32
  const [visibleItemsCount, setVisibleItemsCount] = useState(0);
31
33
  const [hasFilters, setHasFilters] = useState(false);
34
+ // Custom context menu states
35
+ const [customContextMenuVisible, setCustomContextMenuVisible] = useState(false);
36
+ const [customContextMenuPosition, setCustomContextMenuPosition] = useState({ x: 0, y: 0 });
37
+ const [customContextMenuRowKey, setCustomContextMenuRowKey] = useState(undefined);
38
+ const gridContainerRef = useRef(null);
32
39
  useEffect(() => {
33
40
  const count = getRecordCount(dataSource);
34
41
  setTotalRecordCount(count);
35
42
  }, [dataSource]);
43
+ // Handle search panel focus when trigger changes
44
+ useEffect(() => {
45
+ if (!searchPanelFocusTrigger || searchPanelFocusTrigger <= 0 || !showSearchPanel || !internalRef.current)
46
+ return;
47
+ setTimeout(() => {
48
+ const searchInput = internalRef.current?.instance().element().querySelector('.dx-datagrid-search-panel input');
49
+ if (searchInput) {
50
+ searchInput.focus();
51
+ }
52
+ }, 100);
53
+ }, [searchPanelFocusTrigger, showSearchPanel]);
54
+ // Handle custom context menu (only when customContextMenuItems is provided)
55
+ useEffect(() => {
56
+ if (!customContextMenuItems || !gridContainerRef.current)
57
+ return;
58
+ const gridContainer = gridContainerRef.current;
59
+ const handleContextMenu = (e) => {
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ // Get the clicked row (if any)
63
+ const target = e.target;
64
+ const rowElement = target.closest('.dx-data-row');
65
+ if (rowElement && internalRef.current) {
66
+ // Clicked on a row - focus that row
67
+ const rowIndex = Array.from(rowElement.parentElement?.children || []).indexOf(rowElement);
68
+ const rowKey = internalRef.current.instance().getKeyByRowIndex(rowIndex);
69
+ internalRef.current.instance().option('focusedRowKey', rowKey);
70
+ setCustomContextMenuRowKey(rowKey);
71
+ }
72
+ else {
73
+ setCustomContextMenuRowKey(focusedRowKey);
74
+ }
75
+ setCustomContextMenuVisible(true);
76
+ setCustomContextMenuPosition({ x: e.clientX, y: e.clientY });
77
+ };
78
+ gridContainer.addEventListener('contextmenu', handleContextMenu);
79
+ return () => {
80
+ gridContainer.removeEventListener('contextmenu', handleContextMenu);
81
+ };
82
+ }, [customContextMenuItems, focusedRowKey]);
83
+ // iOS-specific: Handle touch for focused row change and long-press for context menu
84
+ useLongPress({
85
+ containerRef: gridContainerRef,
86
+ targetSelector: '.dx-data-row',
87
+ enabled: !!customContextMenuItems,
88
+ onTouchStart: ({ rowElement }) => {
89
+ // Change focused row immediately on touch (fixes focus issue)
90
+ if (!internalRef.current)
91
+ return;
92
+ const rowIndex = Array.from(rowElement.parentElement?.children || []).indexOf(rowElement);
93
+ const rowKey = internalRef.current.instance().getKeyByRowIndex(rowIndex);
94
+ if (rowKey !== undefined && rowKey !== focusedRowKey) {
95
+ internalRef.current.instance().option('focusedRowKey', rowKey);
96
+ }
97
+ },
98
+ onLongPress: ({ clientX, clientY, target }) => {
99
+ if (!internalRef.current)
100
+ return;
101
+ const rowElement = target.closest('.dx-data-row');
102
+ if (!rowElement)
103
+ return;
104
+ const rowIndex = Array.from(rowElement.parentElement?.children || []).indexOf(rowElement);
105
+ const rowKey = internalRef.current.instance().getKeyByRowIndex(rowIndex);
106
+ // Show context menu
107
+ setCustomContextMenuVisible(true);
108
+ setCustomContextMenuPosition({ x: clientX, y: clientY });
109
+ setCustomContextMenuRowKey(rowKey);
110
+ },
111
+ });
36
112
  // Creating a ref to store the timestamp of the last selection change
37
113
  const lastSelectionChangeTime = useRef(Date.now());
38
114
  useEffect(() => {
@@ -101,6 +177,8 @@ const TMDataGrid = React.forwardRef((props, ref) => {
101
177
  }
102
178
  return {
103
179
  ...item,
180
+ // Ensure icon is not null/undefined to prevent DevExtreme errors
181
+ icon: item.icon || '',
104
182
  disabled: disabled || disabledCalculation, // An item is disabled if it's explicitly set to `true` or if the calculation above determines so
105
183
  // Define the behavior when the menu item is clicked
106
184
  onClick: () => {
@@ -132,12 +210,68 @@ const TMDataGrid = React.forwardRef((props, ref) => {
132
210
  };
133
211
  });
134
212
  }, [focusedRowEnabled, focusedRowKey, selectedRowKeys]);
135
- // Handle context menu preparation
213
+ // Process custom context menu items (for TMContextMenuItemProps)
214
+ const processCustomContextMenuItems = useCallback((items, rowID) => {
215
+ return items.map(item => {
216
+ let disabled = item.disabled ?? false;
217
+ let disabledCalculation = false;
218
+ const id = focusedRowEnabled ? focusedRowKey : rowID;
219
+ if (item.operationType === 'singleRow') {
220
+ disabledCalculation = selectedRowKeys.length > 1 || id === undefined;
221
+ }
222
+ if (item.operationType === 'multiRow') {
223
+ disabledCalculation = selectedRowKeys.length === 0 && id === undefined;
224
+ }
225
+ const originalOnClick = item.onClick;
226
+ return {
227
+ ...item,
228
+ disabled: disabled || disabledCalculation,
229
+ onClick: originalOnClick ? () => {
230
+ if (item.operationType === 'singleRow' && id !== undefined) {
231
+ originalOnClick(id);
232
+ }
233
+ else if (item.operationType === 'multiRow' && id !== undefined) {
234
+ if (selectedRowKeys.length > 0) {
235
+ originalOnClick(selectedRowKeys);
236
+ }
237
+ else {
238
+ originalOnClick([id]);
239
+ }
240
+ }
241
+ else {
242
+ originalOnClick();
243
+ }
244
+ } : undefined,
245
+ submenu: item.submenu ? processCustomContextMenuItems(item.submenu, id) : undefined,
246
+ };
247
+ });
248
+ }, [focusedRowEnabled, focusedRowKey, selectedRowKeys]);
249
+ // Gestisce la preparazione del menu contestuale della griglia (header e righe)
136
250
  const onContextMenuPreparingCallback = useCallback((e) => {
137
251
  if (e === undefined)
138
252
  return;
253
+ // MENU DELL'HEADER: Aggiunge l'opzione "Mostra/nascondi colonne" al menu contestuale dell'intestazione
254
+ if (e.target === 'header' && showHeaderColumnChooser) {
255
+ e.items = e.items || [];
256
+ e.items.push({
257
+ text: SDKUI_Localizator.ShowColumnSelection,
258
+ icon: 'columnchooser',
259
+ onItemClick: () => {
260
+ internalRef.current?.instance().showColumnChooser();
261
+ }
262
+ });
263
+ }
264
+ // MENU PERSONALIZZATO: Se è configurato un menu custom, disabilita completamente il menu nativo di DevExtreme
265
+ // Il menu custom (TMContextMenu) viene gestito separatamente tramite event listener e stato locale
266
+ if (customContextMenuItems && e.target === 'content') {
267
+ e.items = undefined;
268
+ return;
269
+ }
270
+ // Chiama il callback esterno se fornito (permette personalizzazioni aggiuntive dal parent)
139
271
  if (onContextMenuPreparing)
140
272
  onContextMenuPreparing(e);
273
+ // MENU NATIVO DELLE RIGHE: Aggiorna lo stato (enabled/disabled) delle voci in base alla selezione
274
+ // Applica la logica per operazioni singleRow (1 riga) e multiRow (1+ righe)
141
275
  if (e.target === 'content') {
142
276
  e.items = e.items || [];
143
277
  if (e.items.length > 0) {
@@ -146,7 +280,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
146
280
  e.items = [...updatedContextMenuItems];
147
281
  }
148
282
  }
149
- }, [updateContextMenuItems, onContextMenuPreparing]);
283
+ }, [updateContextMenuItems, onContextMenuPreparing, showHeaderColumnChooser, customContextMenuItems]);
150
284
  // Handle toolbar preparation, especially for the search panel
151
285
  const onToolbarPreparingCallback = useCallback((e) => {
152
286
  if (e === undefined || e.toolbarOptions === undefined || e.toolbarOptions.items === undefined)
@@ -171,10 +305,9 @@ const TMDataGrid = React.forwardRef((props, ref) => {
171
305
  return;
172
306
  // Update state with the current number of visible rows in the DataGrid
173
307
  setVisibleItemsCount(internalRef.current.instance()?.getVisibleRows()?.length ?? 0);
174
- // Focusing SearchPanel
175
- if (showSearchPanel && searchPanelFocusStarting) {
308
+ // Focusing SearchPanel on content ready
309
+ if (showSearchPanel && searchPanelFocusTrigger && searchPanelFocusTrigger > 0) {
176
310
  // Use a small delay to ensure the DOM is fully rendered before trying to focus
177
- // This can prevent issues with the focus not being set correctly
178
311
  setTimeout(() => {
179
312
  const searchInput = internalRef.current?.instance().element().querySelector('.dx-datagrid-search-panel input');
180
313
  if (searchInput) {
@@ -182,7 +315,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
182
315
  }
183
316
  }, 100);
184
317
  }
185
- }, [onContentReady]);
318
+ }, [onContentReady, showSearchPanel, searchPanelFocusTrigger]);
186
319
  const onOptionChangedCallback = useCallback((e) => {
187
320
  // Assicurati che component esista
188
321
  const grid = e.component;
@@ -215,7 +348,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
215
348
  // Propaga l'evento originale
216
349
  onOptionChanged?.(e);
217
350
  }, [onOptionChanged, onHasFiltersChange]);
218
- return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
351
+ return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
219
352
  // main properties
220
353
  keyExpr: keyExpr, dataSource: dataSource, selectedRowKeys: selectedRowKeys, focusedRowEnabled: focusedRowEnabled, hoverStateEnabled: hoverStateEnabled,
221
354
  // events and callbacks
@@ -223,7 +356,16 @@ const TMDataGrid = React.forwardRef((props, ref) => {
223
356
  // other properties
224
357
  disabled: disabled, autoNavigateToFocusedRow: autoNavigateToFocusedRow, focusedRowKey: focusedRowKey, columnHidingEnabled: columnHidingEnabled, columnResizingMode: columnResizingMode, columnAutoWidth: columnAutoWidth, allowColumnResizing: allowColumnResizing, allowColumnReordering: allowColumnReordering, showBorders: showBorders, showRowLines: showRowLines, showColumnLines: showColumnLines, showColumnHeaders: showColumnHeaders, rowAlternationEnabled: rowAlternationEnabled, wordWrapEnabled: wordWrapEnabled, noDataText: noDataText,
225
358
  // styles
226
- width: width, height: height, style: { userSelect: 'none' }, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), columnChooser && _jsx(ColumnChooser, { ...columnChooser }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), pager && _jsx(Pager, { ...pager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) })] });
359
+ width: width, height: height, style: {
360
+ userSelect: 'none',
361
+ // Temporary: iOS-specific styles to prevent default long-press menu and text selection
362
+ WebkitTouchCallout: 'none',
363
+ WebkitUserSelect: 'none'
364
+ }, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), pager && _jsx(Pager, { ...pager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { target: ".dx-data-row", items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
365
+ visible: customContextMenuVisible,
366
+ position: customContextMenuPosition,
367
+ onClose: () => setCustomContextMenuVisible(false)
368
+ } }))] });
227
369
  });
228
370
  export default TMDataGrid;
229
371
  const getRecordCount = (dataSource) => {
@@ -1,9 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState, forwardRef, useRef, useImperativeHandle } from 'react';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useRef, useImperativeHandle } from 'react';
3
3
  import styled from 'styled-components';
4
- import ContextMenu from 'devextreme-react/context-menu';
5
- import { FontSize, TMColors } from '../../utils/theme';
6
- import { genUniqueId } from '../../helper';
4
+ import { ContextMenu as TMContextMenu } from '../NewComponents/ContextMenu';
5
+ import { TMColors } from '../../utils/theme';
7
6
  const StyledContent = styled.div `
8
7
  cursor: pointer;
9
8
  border-radius: ${props => props.$borderRadius};
@@ -22,24 +21,26 @@ const StyledContent = styled.div `
22
21
  border-bottom-color: ${TMColors.primary};
23
22
  }
24
23
  `;
25
- const StyledMenuItem = styled.div `
26
- display: flex;
27
- align-items: center;
28
- justify-content: space-between;
29
- gap: 6px;
30
- width: 100%;
31
- font-size: ${FontSize.defaultFontSize};
32
- `;
33
24
  const TMDropDownMenu = forwardRef(({ content, items, disabled = false, color = TMColors.text_normal, backgroundColor = TMColors.default_background, borderRadius, onMenuShown }, ref) => {
34
- const [id, setID] = useState('');
35
- const dropDownMenuElementRef = useRef(null); // Ref all'elemento DOM div principale
36
- useEffect(() => { setID(genUniqueId()); }, [content]);
25
+ const dropDownMenuElementRef = useRef(null);
37
26
  useImperativeHandle(ref, () => ({
38
27
  focus: () => {
39
28
  dropDownMenuElementRef.current?.focus();
40
29
  },
41
30
  }));
42
- const renderItemTemplate = (itemData) => (_jsxs(StyledMenuItem, { children: [itemData.icon && _jsx("div", { style: { display: 'flex', alignItems: 'center' }, children: itemData.icon }), _jsx("span", { style: { flexGrow: 1 }, children: itemData.text }), itemData.items && _jsx("span", { className: "dx-icon-spinright dx-icon", style: { marginLeft: '10px' } })] }));
43
- return (_jsxs(_Fragment, { children: [_jsx(StyledContent, { id: `idContainer${id}`, ref: dropDownMenuElementRef, tabIndex: disabled ? -1 : 0, "$disabled": disabled, "$color": color, "$backgroundColor": backgroundColor, "$borderRadius": borderRadius, children: content }), _jsx(ContextMenu, { target: `#idContainer${id}`, dataSource: items, showEvent: 'click', itemRender: renderItemTemplate, onShown: (e) => onMenuShown?.(), onHidden: (e) => dropDownMenuElementRef.current?.focus() })] }));
31
+ // Converter function: ITMDropDownMenuItem -> TMContextMenuItemProps
32
+ const convertToContextMenuItems = (dropDownItems) => {
33
+ if (!dropDownItems)
34
+ return [];
35
+ return dropDownItems.map(item => ({
36
+ name: item.text,
37
+ icon: item.icon,
38
+ disabled: item.disabled,
39
+ beginGroup: item.beginGroup,
40
+ onClick: item.onClick,
41
+ submenu: item.items ? convertToContextMenuItems(item.items) : undefined,
42
+ }));
43
+ };
44
+ return (_jsx(TMContextMenu, { items: convertToContextMenuItems(items), trigger: "left", children: _jsx(StyledContent, { ref: dropDownMenuElementRef, tabIndex: disabled ? -1 : 0, "$disabled": disabled, "$color": color, "$backgroundColor": backgroundColor, "$borderRadius": borderRadius, children: content }) }));
44
45
  });
45
46
  export default TMDropDownMenu;
@@ -1,4 +1,5 @@
1
- import { FileItem, TMFileManagerContextMenuItem } from "./TMFileManagerUtils";
1
+ import { FileItem } from "./TMFileManagerUtils";
2
+ import { TMContextMenuItemProps } from '../NewComponents/ContextMenu/types';
2
3
  interface TMFileManagerProps {
3
4
  /** The currently focused file */
4
5
  focusedFile: FileItem | undefined;
@@ -17,9 +18,9 @@ interface TMFileManagerProps {
17
18
  /** Represents the file system tree structure */
18
19
  treeFs: FileItem;
19
20
  /** Context menu items for folders */
20
- folderContextMenuItems: Array<TMFileManagerContextMenuItem>;
21
+ folderContextMenuItems: Array<TMContextMenuItemProps>;
21
22
  /** Context menu items for files */
22
- fileContextMenuItems: Array<TMFileManagerContextMenuItem>;
23
+ fileContextMenuItems: Array<TMContextMenuItemProps>;
23
24
  /** Optional: Callback to handle folder selection changes */
24
25
  handleSelectedFolder?: (folderItem: FileItem | undefined) => void;
25
26
  /** Optional: Callback for handling double-click events on a file */
@@ -1,13 +1,14 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useState } from "react";
3
3
  import { extractTextsFromDirectory, findFileItems, setFolderTreeViewItems } from "./TMFileManagerUtils";
4
4
  import { DeviceType, useDeviceType } from "./TMDeviceProvider";
5
- import { formatBytes, Globalization, IconDashboard, IconFolder, IconHide, IconList, IconMenuVertical, IconRefresh, IconShow, SDKUI_Globals, SDKUI_Localizator, svgToString, TMCommandsContextMenu, TMConditionalWrapper } from "../../helper";
5
+ import { formatBytes, Globalization, IconDashboard, IconFolder, IconHide, IconList, IconMenuVertical, IconRefresh, IconShow, SDKUI_Globals, SDKUI_Localizator, TMCommandsContextMenu, TMConditionalWrapper } from "../../helper";
6
+ import TMContextMenu from '../NewComponents/ContextMenu/TMContextMenu';
6
7
  import TMTooltip from "./TMTooltip";
7
8
  import { TMColors } from "../../utils/theme";
8
9
  import TMPanel from "./TMPanel";
9
10
  import { TMSplitterLayout } from "./TMLayout";
10
- import { ContextMenu, TreeView } from "devextreme-react";
11
+ import { TreeView } from "devextreme-react";
11
12
  import Toolbar, { Item as ToolbarItem } from 'devextreme-react/toolbar';
12
13
  import TMButton from "./TMButton";
13
14
  import { TMSearchBar } from "../sidebar/TMHeader";
@@ -29,12 +30,12 @@ const TMFileManager = (props) => {
29
30
  const [viewMode, setViewMode] = useState(initialViewMode ?? 'details');
30
31
  // State to store transformed directory data for file manager
31
32
  const [treeViewData, setTreeViewData] = useState([]);
32
- // State to store the anchor element for the tree view context men
33
- const [treeViewAnchor, setTreeViewAnchor] = useState(null);
33
+ // State to store the tree view context menu control
34
+ const [treeViewContextMenuControl, setTreeViewContextMenuControl] = useState({ visible: false, position: { x: 0, y: 0 } });
34
35
  // State to store the filtered file items after search or filtering
35
36
  const [filteredFileItems, setFilteredFileItems] = useState([]);
36
- // State to store the anchor element for draft thumbnails view
37
- const [draftThumbViewAnchor, setDraftThumbViewAnchor] = useState(null);
37
+ // State to store the thumbnails view context menu control
38
+ const [thumbViewContextMenuControl, setThumbViewContextMenuControl] = useState({ visible: false, position: { x: 0, y: 0 } });
38
39
  // State to store the search text entered by the user
39
40
  const [searchText, setSearchText] = useState('');
40
41
  // State to control the collapse/expand of the left panel
@@ -98,21 +99,21 @@ const TMFileManager = (props) => {
98
99
  const onBackCallback = useCallback(() => {
99
100
  setOpenDraftList(false);
100
101
  }, []);
101
- const toolbar = useMemo(() => _jsxs(_Fragment, { children: [_jsx(IconMenuVertical, { id: "TMPanel-FileManager-Commands-Header", color: 'white', cursor: 'pointer' }), _jsx(TMCommandsContextMenu, { target: '#TMPanel-FileManager-Commands-Header', menuItems: [
102
- {
103
- icon: isLeftPanelCollapsed ? "eyeclose" : "eyeopen",
104
- onClick: () => setIsLeftPanelCollapsed(prev => !prev),
105
- text: isLeftPanelCollapsed ? SDKUI_Localizator.ShowLeftPanel : SDKUI_Localizator.HideLeftPanel,
106
- },
107
- {
108
- icon: svgToString(_jsx(IconRefresh, {})),
109
- onClick: async () => await refreshCallback(),
110
- text: SDKUI_Localizator.Refresh,
111
- },
112
- ], showEvent: "click" })] }), [isLeftPanelCollapsed]);
102
+ const toolbar = useMemo(() => _jsx(TMCommandsContextMenu, { target: '#TMPanel-FileManager-Commands-Header', menuItems: [
103
+ {
104
+ icon: isLeftPanelCollapsed ? _jsx(IconShow, {}) : _jsx(IconHide, {}),
105
+ onClick: () => setIsLeftPanelCollapsed(prev => !prev),
106
+ text: isLeftPanelCollapsed ? SDKUI_Localizator.ShowLeftPanel : SDKUI_Localizator.HideLeftPanel,
107
+ },
108
+ {
109
+ icon: _jsx(IconRefresh, {}),
110
+ onClick: async () => await refreshCallback(),
111
+ text: SDKUI_Localizator.Refresh,
112
+ },
113
+ ], showEvent: "click", children: _jsx(IconMenuVertical, { id: "TMPanel-FileManager-Commands-Header", color: 'white', cursor: 'pointer' }) }), [isLeftPanelCollapsed]);
113
114
  // Handle closing the context menu
114
115
  const closeTreeViewContextMenu = useCallback(() => {
115
- setTreeViewAnchor(null);
116
+ setTreeViewContextMenuControl(prev => ({ ...prev, visible: false }));
116
117
  }, []);
117
118
  const handleTreeViewItemClick = useCallback((e) => {
118
119
  if (!e)
@@ -161,7 +162,10 @@ const TMFileManager = (props) => {
161
162
  if (event === undefined)
162
163
  return;
163
164
  event.preventDefault();
164
- setTreeViewAnchor(event.currentTarget);
165
+ setTreeViewContextMenuControl({
166
+ visible: true,
167
+ position: { x: event.clientX, y: event.clientY }
168
+ });
165
169
  handleFocusedFolder?.(undefined);
166
170
  }, []);
167
171
  const onItemTreeViewContextMenu = useCallback((contextMenuEvent) => {
@@ -198,11 +202,15 @@ const TMFileManager = (props) => {
198
202
  const handleDragLeave = (e) => {
199
203
  setIsDragging(false);
200
204
  };
201
- return _jsx(TMPanel, { title: SDKUI_Localizator.Drafts, totalItems: dcmtsFound ?? 0, showHeader: showPanel, onBack: (isMobile && openDraftList) ? onBackCallback : undefined, onClose: onClosePanel, allowMaximize: !isMobile ? allowMaximize : false, onMaximize: !isMobile ? onMaximizePanel : undefined, onHeaderDoubleClick: !isMobile ? onMaximizePanel : undefined, toolbar: toolbar, children: _jsx("div", { style: { flexDirection: "column", height: "100%", width: "100%", }, children: _jsxs(TMConditionalWrapper, { condition: !isMobile, wrapper: children => _jsx("div", { style: { display: "flex", flexGrow: 1, height: "100%" }, children: _jsx(TMSplitterLayout, { direction: 'horizontal', showSeparator: true, separatorColor: 'transparent', separatorActiveColor: 'transparent', min: ['0', '0'], start: [isLeftPanelCollapsed ? '0%' : "32%", isLeftPanelCollapsed ? '100%' : "68%"], children: children }, "TMWGs-panels-treeView") }), children: [_jsxs("div", { style: {
205
+ return _jsx(TMPanel, { title: SDKUI_Localizator.Drafts, totalItems: dcmtsFound ?? 0, showHeader: showPanel, onBack: (isMobile && openDraftList) ? onBackCallback : undefined, onClose: onClosePanel, allowMaximize: !isMobile ? allowMaximize : false, onMaximize: !isMobile ? onMaximizePanel : undefined, onHeaderDoubleClick: !isMobile ? onMaximizePanel : undefined, toolbar: toolbar, children: _jsx("div", { style: { flexDirection: "column", height: "100%", width: "100%", }, children: _jsxs(TMConditionalWrapper, { condition: !isMobile, wrapper: children => _jsx("div", { style: { display: "flex", flexGrow: 1, height: "100%" }, children: _jsx(TMSplitterLayout, { direction: 'horizontal', showSeparator: true, separatorColor: 'transparent', separatorActiveColor: 'transparent', min: ['0', '0'], start: [isLeftPanelCollapsed ? '0%' : "32%", isLeftPanelCollapsed ? '100%' : "68%"], children: children }, "TMWGs-panels-treeView") }), children: [_jsxs("div", { id: "file-manager-tree-view-container", style: {
202
206
  height: "100%",
203
207
  width: "100%",
204
208
  ...(isMobile && { display: openDraftList ? 'none' : 'block', transition: "opacity 0.3s ease-in-out" }),
205
- }, onContextMenu: onBackgroundTreeViewContextMenu, children: [_jsx(TreeView, { height: "100%", width: "100%", useNativeScrolling: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1, dataSource: treeViewData, displayExpr: "text", itemRender: renderTreeViewItem, onItemClick: handleTreeViewItemClick, onItemContextMenu: onItemTreeViewContextMenu }), treeViewAnchor && _jsx(ContextMenu, { id: "treeViewContextMenuDesktop", target: treeViewAnchor, dataSource: folderContextMenuItems, onHiding: closeTreeViewContextMenu })] }), _jsxs("div", { style: {
209
+ }, onContextMenu: onBackgroundTreeViewContextMenu, children: [_jsx(TreeView, { height: "100%", width: "100%", useNativeScrolling: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1, dataSource: treeViewData, displayExpr: "text", itemRender: renderTreeViewItem, onItemClick: handleTreeViewItemClick, onItemContextMenu: onItemTreeViewContextMenu }), _jsx(TMContextMenu, { items: folderContextMenuItems, target: "#file-manager-tree-view-container", externalControl: {
210
+ visible: treeViewContextMenuControl.visible,
211
+ position: treeViewContextMenuControl.position,
212
+ onClose: closeTreeViewContextMenu
213
+ } })] }), _jsxs("div", { style: {
206
214
  backgroundColor: "#fff",
207
215
  width: "100%",
208
216
  height: "100%",
@@ -228,6 +236,6 @@ const TMFileManager = (props) => {
228
236
  pointerEvents: "none",
229
237
  backdropFilter: "blur(3px)",
230
238
  textShadow: "0 2px 4px rgba(0,0,0,0.5)"
231
- }, children: SDKUI_Localizator.DropFileToShare }), viewMode === 'thumbnails' && _jsx(TMFileManagerThumbnailsView, { items: filteredFileItems ?? [], allUsers: allUsers, focusedFile: focusedFile, handleFocusedFile: handleFocusedFile, selectedFiles: selectedFiles, handleSelectedFiles: handleSelectedFiles, fileContextMenuItems: fileContextMenuItems, searchText: searchText, userID: userID, draftThumbViewAnchor: draftThumbViewAnchor, setDraftThumbViewAnchor: setDraftThumbViewAnchor, onDoubleClickHandler: onDoubleClickHandler }), viewMode === 'details' && _jsx(TMFileManagerDataGridView, { items: filteredFileItems ?? [], allUsers: allUsers, focusedFile: focusedFile, handleFocusedFile: handleFocusedFile, selectedFiles: selectedFiles, handleSelectedFiles: handleSelectedFiles, fileContextMenuItems: fileContextMenuItems, searchText: searchText, userID: userID, onDoubleClickHandler: onDoubleClickHandler })] })] })] }) }) });
239
+ }, children: SDKUI_Localizator.DropFileToShare }), viewMode === 'thumbnails' && _jsx(TMFileManagerThumbnailsView, { items: filteredFileItems ?? [], allUsers: allUsers, focusedFile: focusedFile, handleFocusedFile: handleFocusedFile, selectedFiles: selectedFiles, handleSelectedFiles: handleSelectedFiles, fileContextMenuItems: fileContextMenuItems, searchText: searchText, userID: userID, thumbViewContextMenuControl: thumbViewContextMenuControl, setThumbViewContextMenuControl: setThumbViewContextMenuControl, onDoubleClickHandler: onDoubleClickHandler }), viewMode === 'details' && _jsx(TMFileManagerDataGridView, { items: filteredFileItems ?? [], allUsers: allUsers, focusedFile: focusedFile, handleFocusedFile: handleFocusedFile, selectedFiles: selectedFiles, handleSelectedFiles: handleSelectedFiles, fileContextMenuItems: fileContextMenuItems, searchText: searchText, userID: userID, onDoubleClickHandler: onDoubleClickHandler })] })] })] }) }) });
232
240
  };
233
241
  export default TMFileManager;