@topconsultnpm/sdkui-react 6.20.0-dev1.5 → 6.20.0-dev1.50

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 (70) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.d.ts +4 -0
  2. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +302 -0
  3. package/lib/components/NewComponents/ContextMenu/hooks.d.ts +13 -0
  4. package/lib/components/NewComponents/ContextMenu/hooks.js +61 -0
  5. package/lib/components/NewComponents/ContextMenu/index.d.ts +2 -0
  6. package/lib/components/NewComponents/ContextMenu/index.js +1 -0
  7. package/lib/components/NewComponents/ContextMenu/styles.d.ts +31 -0
  8. package/lib/components/NewComponents/ContextMenu/styles.js +336 -0
  9. package/lib/components/NewComponents/ContextMenu/types.d.ts +38 -0
  10. package/lib/components/NewComponents/ContextMenu/types.js +1 -0
  11. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.d.ts +4 -0
  12. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +686 -0
  13. package/lib/components/NewComponents/FloatingMenuBar/index.d.ts +2 -0
  14. package/lib/components/NewComponents/FloatingMenuBar/index.js +2 -0
  15. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +47 -0
  16. package/lib/components/NewComponents/FloatingMenuBar/styles.js +346 -0
  17. package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +28 -0
  18. package/lib/components/NewComponents/FloatingMenuBar/types.js +1 -0
  19. package/lib/components/base/TMCustomButton.js +61 -17
  20. package/lib/components/base/TMDataGrid.d.ts +7 -4
  21. package/lib/components/base/TMDataGrid.js +112 -11
  22. package/lib/components/choosers/TMMetadataChooser.js +8 -1
  23. package/lib/components/editors/TMMetadataValues.js +23 -5
  24. package/lib/components/features/documents/TMDcmtForm.d.ts +13 -1
  25. package/lib/components/features/documents/TMDcmtForm.js +385 -193
  26. package/lib/components/features/documents/TMDcmtPreview.js +37 -66
  27. package/lib/components/features/documents/TMMasterDetailDcmts.js +1 -1
  28. package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +8 -0
  29. package/lib/components/features/search/{TMSearchResultCheckoutInfoForm.js → TMDcmtCheckoutInfoForm.js} +5 -10
  30. package/lib/components/features/search/TMSearch.js +30 -5
  31. package/lib/components/features/search/TMSearchQueryPanel.js +13 -12
  32. package/lib/components/features/search/TMSearchResult.js +58 -208
  33. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +3 -3
  34. package/lib/components/features/search/TMSearchResultsMenuItems.js +205 -169
  35. package/lib/components/features/search/TMSignSettingsForm.js +1 -1
  36. package/lib/components/features/search/TMSignatureInfoContent.d.ts +6 -0
  37. package/lib/components/features/search/TMSignatureInfoContent.js +140 -0
  38. package/lib/components/features/search/TMViewHistoryDcmt.js +1 -1
  39. package/lib/components/features/tasks/TMTasksView.js +2 -2
  40. package/lib/components/features/workflow/diagram/WFDiagram.js +2 -2
  41. package/lib/components/forms/Login/LoginValidatorService.d.ts +2 -0
  42. package/lib/components/forms/Login/LoginValidatorService.js +7 -2
  43. package/lib/components/forms/Login/TMLoginForm.js +34 -6
  44. package/lib/components/forms/TMChooserForm.js +1 -1
  45. package/lib/components/index.d.ts +1 -0
  46. package/lib/components/index.js +1 -0
  47. package/lib/css/tm-sdkui.css +1 -1
  48. package/lib/helper/SDKUI_Globals.d.ts +17 -0
  49. package/lib/helper/SDKUI_Globals.js +9 -0
  50. package/lib/helper/SDKUI_Localizator.d.ts +2 -1
  51. package/lib/helper/SDKUI_Localizator.js +11 -1
  52. package/lib/helper/TMIcons.d.ts +1 -0
  53. package/lib/helper/TMIcons.js +3 -0
  54. package/lib/helper/TMPdfViewer.d.ts +8 -0
  55. package/lib/helper/TMPdfViewer.js +187 -0
  56. package/lib/helper/checkinCheckoutManager.d.ts +32 -2
  57. package/lib/helper/checkinCheckoutManager.js +115 -38
  58. package/lib/helper/devextremeCustomMessages.d.ts +30 -0
  59. package/lib/helper/devextremeCustomMessages.js +30 -0
  60. package/lib/helper/helpers.d.ts +2 -1
  61. package/lib/helper/helpers.js +12 -2
  62. package/lib/helper/index.d.ts +1 -0
  63. package/lib/helper/index.js +1 -0
  64. package/lib/helper/queryHelper.js +29 -0
  65. package/lib/hooks/useCheckInOutOperations.d.ts +28 -0
  66. package/lib/hooks/useCheckInOutOperations.js +223 -0
  67. package/lib/hooks/useWorkflowApprove.d.ts +4 -0
  68. package/lib/hooks/useWorkflowApprove.js +14 -1
  69. package/package.json +5 -2
  70. package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +0 -8
@@ -0,0 +1,2 @@
1
+ export { default } from './TMFloatingMenuBar';
2
+ export * from './types';
@@ -0,0 +1,2 @@
1
+ export { default } from './TMFloatingMenuBar';
2
+ export * from './types';
@@ -0,0 +1,47 @@
1
+ export declare const Overlay: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
2
+ $visible: boolean;
3
+ }>> & string;
4
+ export declare const FloatingContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("styled-components/dist/types").Substitute<import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
5
+ ref?: ((instance: HTMLDivElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLDivElement> | null | undefined;
6
+ }>, {
7
+ $x: number;
8
+ $y: number;
9
+ $orientation: "horizontal" | "vertical";
10
+ $isDragging: boolean;
11
+ $isConfigMode: boolean;
12
+ $isConstrained?: boolean;
13
+ }>, {
14
+ $x: number;
15
+ $y: number;
16
+ $orientation: "horizontal" | "vertical";
17
+ $isDragging: boolean;
18
+ $isConfigMode: boolean;
19
+ $isConstrained?: boolean;
20
+ }>> & string;
21
+ export declare const GripHandle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
22
+ $orientation: "horizontal" | "vertical";
23
+ }>> & string;
24
+ export declare const Separator: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
25
+ $orientation: "horizontal" | "vertical";
26
+ }>> & string;
27
+ export declare const MenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
28
+ $isActive?: boolean;
29
+ }>> & string;
30
+ export declare const ConfigButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
31
+ export declare const ApplyButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
32
+ export declare const ContextMenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
33
+ $isActive?: boolean;
34
+ }, "ref"> & {
35
+ ref?: ((instance: HTMLButtonElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLButtonElement> | null | undefined;
36
+ }, never>> & string;
37
+ export declare const AddButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
38
+ export declare const RemoveButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
39
+ export declare const UndoButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
40
+ export declare const DraggableItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
41
+ $isDragging: boolean;
42
+ $isDragOver: boolean;
43
+ }>> & string;
44
+ export declare const ContextMenuWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
45
+ export declare const ButtonGroup: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
46
+ $orientation: "horizontal" | "vertical";
47
+ }>> & string;
@@ -0,0 +1,346 @@
1
+ import styled, { keyframes, css } from 'styled-components';
2
+ const fadeIn = keyframes `
3
+ from {
4
+ opacity: 0;
5
+ transform: scale(0.95);
6
+ }
7
+ to {
8
+ opacity: 1;
9
+ transform: scale(1);
10
+ }
11
+ `;
12
+ const shake = keyframes `
13
+ 0%, 100% { transform: translateX(0); }
14
+ 25% { transform: translateX(-2px); }
15
+ 75% { transform: translateX(2px); }
16
+ `;
17
+ export const Overlay = styled.div `
18
+ position: fixed;
19
+ top: 0;
20
+ left: 0;
21
+ right: 0;
22
+ bottom: 0;
23
+ background: rgba(255, 255, 255, 0.8);
24
+ z-index: 9998;
25
+ display: ${props => props.$visible ? 'block' : 'none'};
26
+ animation: ${fadeIn} 0.2s ease-out;
27
+ backdrop-filter: blur(2px);
28
+
29
+ [data-theme='dark'] & {
30
+ background: rgba(0, 0, 0, 0.8);
31
+ }
32
+ `;
33
+ export const FloatingContainer = styled.div.attrs(props => ({
34
+ style: {
35
+ left: `${props.$x}px`,
36
+ top: `${props.$y}px`,
37
+ },
38
+ })) `
39
+ position: ${props => props.$isConstrained ? 'absolute' : 'fixed'};
40
+ z-index: ${props => props.$isConfigMode ? 9999 : 10000};
41
+ display: flex;
42
+ flex-direction: ${props => props.$orientation === 'horizontal' ? 'row' : 'column'};
43
+ align-items: center;
44
+ background: linear-gradient(135deg, #0071BC 0%, #1B1464 100%);
45
+ border: 1px solid #667eea;
46
+ border-radius: 14px;
47
+ padding: 6px;
48
+ gap: 3px;
49
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3),
50
+ 0 6px 16px rgba(0, 0, 0, 0.2);
51
+ cursor: ${props => props.$isDragging ? 'grabbing' : 'default'};
52
+ user-select: none;
53
+ animation: ${props => props.$isConfigMode && css `${shake} 0.3s ease-in-out`};
54
+ transition: none;
55
+
56
+ &:hover {
57
+ background: linear-gradient(135deg, #0071BC 0%, #1B1464 100%);
58
+ border: 1px solid #667eea;
59
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3),
60
+ 0 6px 16px rgba(0, 0, 0, 0.2);
61
+ }
62
+
63
+ [data-theme='dark'] & {
64
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
65
+ border: 1px solid #1a1a2e;
66
+
67
+ &:hover {
68
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
69
+ border: 1px solid #1a1a2e;
70
+ }
71
+ }
72
+ `;
73
+ export const GripHandle = styled.div `
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ padding: ${props => props.$orientation === 'horizontal' ? '10px 6px' : '6px 10px'};
78
+ cursor: grab;
79
+ color: rgba(255, 255, 255, 0.7);
80
+ transition: all 0.2s ease;
81
+ border-radius: 6px;
82
+
83
+ &:hover {
84
+ background: rgba(255, 255, 255, 0.1);
85
+ color: rgba(255, 255, 255, 1);
86
+ }
87
+
88
+ &:active {
89
+ cursor: grabbing;
90
+ background: rgba(255, 255, 255, 0.15);
91
+ }
92
+
93
+ svg {
94
+ width: 14px;
95
+ height: 14px;
96
+ }
97
+ `;
98
+ export const Separator = styled.div `
99
+ background: rgba(255, 255, 255, 0.25);
100
+ width: ${props => props.$orientation === 'horizontal' ? '1px' : '100%'};
101
+ height: ${props => props.$orientation === 'horizontal' ? '24px' : '1px'};
102
+ margin: ${props => props.$orientation === 'horizontal' ? '0 4px' : '4px 0'};
103
+ flex-shrink: 0;
104
+ `;
105
+ export const MenuButton = styled.button `
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ width: 34px;
110
+ height: 34px;
111
+ background: transparent;
112
+ border: none;
113
+ border-radius: 8px;
114
+ color: white;
115
+ font-size: 16px;
116
+ cursor: pointer;
117
+ transition: background 0.2s ease;
118
+ position: relative;
119
+
120
+ &:hover:not(:disabled) {
121
+ background: rgba(255, 255, 255, 0.2);
122
+ }
123
+
124
+ &:active:not(:disabled) {
125
+ opacity: 0.8;
126
+ }
127
+
128
+ &:disabled {
129
+ opacity: 0.5;
130
+ cursor: default;
131
+ color: rgba(255, 255, 255, 0.6);
132
+ }
133
+
134
+ svg {
135
+ width: 18px;
136
+ height: 18px;
137
+ }
138
+ `;
139
+ export const ConfigButton = styled.button `
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ width: 21px;
144
+ height: 21px;
145
+ background: transparent;
146
+ border: none;
147
+ border-radius: 4px;
148
+ color: rgba(255, 255, 255, 0.5);
149
+ font-size: 10px;
150
+ cursor: pointer;
151
+ transition: all 0.2s ease;
152
+ padding: 4px;
153
+
154
+ &:hover:not(:disabled) {
155
+ background: rgba(255, 255, 255, 0.1);
156
+ color: rgba(255, 255, 255, 0.8);
157
+ }
158
+
159
+ &:active:not(:disabled) {
160
+ transform: scale(0.9);
161
+ }
162
+
163
+ &:disabled {
164
+ opacity: 0.3;
165
+ cursor: not-allowed;
166
+ }
167
+
168
+ svg {
169
+ width: 16px;
170
+ height: 16px;
171
+ }
172
+ `;
173
+ export const ApplyButton = styled.button `
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ width: 24px;
178
+ height: 24px;
179
+ background: transparent;
180
+ border: none;
181
+ border-radius: 4px;
182
+ color: rgba(34, 197, 94, 1);
183
+ font-size: 10px;
184
+ cursor: pointer;
185
+ transition: all 0.2s ease;
186
+ padding: 3px;
187
+
188
+ &:hover:not(:disabled) {
189
+ background: rgba(255, 255, 255, 0.1);
190
+ color: rgba(34, 197, 94, 1);
191
+ }
192
+
193
+ &:active:not(:disabled) {
194
+ transform: scale(0.9);
195
+ }
196
+
197
+ &:disabled {
198
+ opacity: 0.3;
199
+ cursor: default;
200
+ }
201
+
202
+ svg {
203
+ width: 20px;
204
+ height: 20px;
205
+ }
206
+ `;
207
+ export const ContextMenuButton = styled(MenuButton) `
208
+ svg {
209
+ transform: translateY(0);
210
+ }
211
+ `;
212
+ export const AddButton = styled.button `
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: center;
216
+ width: 28px;
217
+ height: 28px;
218
+ background: rgba(255, 255, 255, 0.15);
219
+ border: 1px dashed rgba(255, 255, 255, 0.4);
220
+ border-radius: 8px;
221
+ color: white;
222
+ font-size: 20px;
223
+ font-weight: bold;
224
+ line-height: 0;
225
+ cursor: pointer;
226
+ transition: all 0.2s ease;
227
+ position: relative;
228
+ margin-right: 6px;
229
+ margin-left: 8px;
230
+ padding: 0;
231
+
232
+ &:hover {
233
+ background: rgba(255, 255, 255, 0.25);
234
+ border-color: rgba(255, 255, 255, 0.6);
235
+ }
236
+
237
+ &:active {
238
+ transform: scale(0.95);
239
+ }
240
+ `;
241
+ export const RemoveButton = styled.button `
242
+ position: absolute;
243
+ top: -6px;
244
+ right: -6px;
245
+ width: 20px;
246
+ height: 20px;
247
+ background: #ef4444;
248
+ border: none;
249
+ border-radius: 50%;
250
+ color: white;
251
+ font-size: 14px;
252
+ font-weight: bold;
253
+ line-height: 0;
254
+ cursor: pointer;
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ padding: 0;
259
+ transition: all 0.2s ease;
260
+ z-index: 1;
261
+
262
+ &:hover {
263
+ background: #dc2626;
264
+ }
265
+
266
+ &:active {
267
+ background: #b91c1c;
268
+ }
269
+ `;
270
+ export const UndoButton = styled.button `
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ width: 24px;
275
+ height: 24px;
276
+ background: transparent;
277
+ border: none;
278
+ border-radius: 4px;
279
+ color: rgba(249, 115, 22, 1);
280
+ font-size: 10px;
281
+ cursor: pointer;
282
+ transition: all 0.2s ease;
283
+ padding: 2px;
284
+
285
+ &:hover:not(:disabled) {
286
+ background: rgba(255, 255, 255, 0.1);
287
+ color: rgba(249, 115, 22, 1);
288
+ }
289
+
290
+ &:active:not(:disabled) {
291
+ transform: scale(0.9);
292
+ }
293
+
294
+ &:disabled {
295
+ opacity: 0.3;
296
+ cursor: default;
297
+ }
298
+
299
+ svg {
300
+ width: 16px;
301
+ height: 16px;
302
+ }
303
+ `;
304
+ export const DraggableItem = styled.div `
305
+ position: relative;
306
+ opacity: ${props => props.$isDragging ? 0.3 : 1};
307
+ transform: ${props => {
308
+ if (props.$isDragging)
309
+ return 'scale(1.1) rotate(5deg)';
310
+ if (props.$isDragOver)
311
+ return 'scale(1.05)';
312
+ return 'scale(1)';
313
+ }};
314
+ transition: all 0.2s ease;
315
+ filter: ${props => props.$isDragging ? 'brightness(1.3)' : 'brightness(1)'};
316
+
317
+ ${props => props.$isDragging && css `
318
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
319
+ z-index: 100;
320
+ `}
321
+
322
+ ${props => props.$isDragOver && css `
323
+ &::before {
324
+ content: '';
325
+ position: absolute;
326
+ top: -4px;
327
+ left: 50%;
328
+ transform: translateX(-50%);
329
+ width: 40px;
330
+ height: 3px;
331
+ background: rgba(255, 255, 255, 0.8);
332
+ border-radius: 2px;
333
+ box-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
334
+ }
335
+ `}
336
+ `;
337
+ export const ContextMenuWrapper = styled.div `
338
+ position: static;
339
+ display: contents;
340
+ `;
341
+ export const ButtonGroup = styled.div `
342
+ display: flex;
343
+ flex-direction: ${props => props.$orientation === 'vertical' ? 'column' : 'row'};
344
+ align-items: center;
345
+ gap: 0;
346
+ `;
@@ -0,0 +1,28 @@
1
+ import type { TMContextMenuItemProps } from '../ContextMenu';
2
+ export interface TMFloatingMenuItem {
3
+ id: string;
4
+ name: string;
5
+ icon: React.ReactNode;
6
+ onClick: () => void;
7
+ disabled?: boolean;
8
+ isPinned?: boolean;
9
+ }
10
+ export interface TMFloatingMenuBarProps {
11
+ containerRef: React.RefObject<HTMLElement | null>;
12
+ contextMenuItems?: TMContextMenuItemProps[];
13
+ isConstrained?: boolean;
14
+ defaultPosition?: Position;
15
+ maxItems?: number;
16
+ }
17
+ export interface Position {
18
+ x: number;
19
+ y: number;
20
+ }
21
+ export interface TMFloatingMenuBarState {
22
+ position: Position;
23
+ isDragging: boolean;
24
+ isConfigMode: boolean;
25
+ orientation: 'horizontal' | 'vertical';
26
+ items: TMFloatingMenuItem[];
27
+ draggedItemIndex: number | null;
28
+ }
@@ -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;