@topconsultnpm/sdkui-react 6.22.0-dev3.9 → 6.22.0-dev4.3

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 (96) hide show
  1. package/lib/components/base/TMAlert.js +6 -8
  2. package/lib/components/base/TMAreaManager.js +53 -55
  3. package/lib/components/base/TMButton.js +4 -2
  4. package/lib/components/base/TMCustomButton.js +40 -48
  5. package/lib/components/base/TMDataGrid.js +219 -219
  6. package/lib/components/base/TMPanel.js +173 -170
  7. package/lib/components/base/TMSpinner.d.ts +2 -1
  8. package/lib/components/base/TMSpinner.js +9 -7
  9. package/lib/components/base/TMTabControl.d.ts +5 -0
  10. package/lib/components/base/TMTabControl.js +18 -7
  11. package/lib/components/base/TMTreeView.js +20 -0
  12. package/lib/components/base/TMWaitPanel.js +1 -1
  13. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  14. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  15. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  16. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  17. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
  18. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  19. package/lib/components/base/waitPanel/index.d.ts +1 -0
  20. package/lib/components/base/waitPanel/index.js +1 -0
  21. package/lib/components/choosers/TMPathChooser.js +17 -34
  22. package/lib/components/editors/TMTextBox.d.ts +4 -0
  23. package/lib/components/editors/TMTextBox.js +99 -11
  24. package/lib/components/features/archive/TMArchive.js +7 -2
  25. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +29 -0
  26. package/lib/components/features/assistant/TMHelpOnLineChat.js +1384 -0
  27. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
  28. package/lib/components/features/assistant/dataset/Designer-Suggested-Questions.json +102 -0
  29. package/lib/components/features/assistant/dataset/Login-Suggested-Questions.json +32 -0
  30. package/lib/components/features/assistant/dataset/Surfer-Suggested-Questions.json +102 -0
  31. package/lib/components/features/documents/TMBatchUpdateForm.js +3 -4
  32. package/lib/components/features/documents/TMCopyToFolderForm.js +53 -84
  33. package/lib/components/features/documents/TMDcmtForm.js +50 -61
  34. package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
  35. package/lib/components/features/documents/TMDcmtIcon.js +3 -5
  36. package/lib/components/features/documents/TMDcmtPreview.js +5 -3
  37. package/lib/components/features/documents/TMDcmtTasks.js +2 -9
  38. package/lib/components/features/documents/TMFileUploader.js +26 -26
  39. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  40. package/lib/components/features/documents/TMMasterDetailDcmts.js +53 -148
  41. package/lib/components/features/documents/TMMasterInfoFields.js +3 -2
  42. package/lib/components/features/documents/TMMergeToPdfForm.js +49 -54
  43. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +14 -15
  44. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +1 -1
  45. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +3 -3
  46. package/lib/components/features/documents/TMRelationViewer.d.ts +13 -14
  47. package/lib/components/features/documents/TMRelationViewer.js +206 -143
  48. package/lib/components/features/documents/relationTreeState.d.ts +32 -0
  49. package/lib/components/features/documents/relationTreeState.js +89 -0
  50. package/lib/components/features/search/TMSearch.js +3 -4
  51. package/lib/components/features/search/TMSearchQueryPanel.js +0 -4
  52. package/lib/components/features/search/TMSearchResult.d.ts +5 -0
  53. package/lib/components/features/search/TMSearchResult.js +39 -19
  54. package/lib/components/features/search/TMTreeSelector.js +23 -27
  55. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  56. package/lib/components/features/tasks/TMTasksView.js +91 -90
  57. package/lib/components/features/wg/TMWGsCopyMoveForm.js +91 -91
  58. package/lib/components/forms/TMResultDialog.js +1 -1
  59. package/lib/components/grids/TMBlogsPost.js +92 -96
  60. package/lib/components/index.d.ts +5 -1
  61. package/lib/components/index.js +4 -1
  62. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +16 -15
  63. package/lib/components/layout/panelManager/TMPanelManagerContext.d.ts +1 -0
  64. package/lib/components/layout/panelManager/TMPanelManagerContext.js +5 -1
  65. package/lib/components/layout/panelManager/TMPanelManagerLoader.d.ts +6 -0
  66. package/lib/components/layout/panelManager/TMPanelManagerLoader.js +18 -0
  67. package/lib/components/layout/panelManager/TMPanelManagerToolbar.js +4 -8
  68. package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.d.ts +1 -1
  69. package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.js +2 -1
  70. package/lib/components/pages/TMPage.js +29 -31
  71. package/lib/components/query/TMQueryEditor.d.ts +3 -0
  72. package/lib/components/query/TMQueryEditor.js +2 -1
  73. package/lib/components/sidebar/TMHeader.d.ts +1 -0
  74. package/lib/components/sidebar/TMHeader.js +2 -2
  75. package/lib/helper/Enum_Localizator.js +1 -0
  76. package/lib/helper/SDKUI_Globals.d.ts +3 -0
  77. package/lib/helper/SDKUI_Globals.js +3 -0
  78. package/lib/helper/SDKUI_Localizator.d.ts +21 -0
  79. package/lib/helper/SDKUI_Localizator.js +210 -0
  80. package/lib/helper/ZipManager.d.ts +1 -1
  81. package/lib/helper/ZipManager.js +2 -1
  82. package/lib/helper/dcmtsHelper.d.ts +2 -0
  83. package/lib/helper/dcmtsHelper.js +3 -1
  84. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  85. package/lib/hooks/useCheckInOutOperations.js +14 -34
  86. package/lib/hooks/useDcmtOperations.d.ts +3 -19
  87. package/lib/hooks/useDcmtOperations.js +160 -222
  88. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  89. package/lib/hooks/useDocumentOperations.js +14 -25
  90. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
  91. package/lib/hooks/useMultiMasterDetailDcmts.js +65 -67
  92. package/lib/hooks/usePreventFileDrop.js +9 -0
  93. package/lib/hooks/useWorkflowApprove.js +33 -17
  94. package/lib/services/caseflow/CaseFlowService.d.ts +36 -6
  95. package/lib/services/caseflow/CaseFlowService.js +55 -14
  96. package/package.json +69 -69
@@ -3,100 +3,100 @@ import { useCallback, useRef, useState, forwardRef, useImperativeHandle } from '
3
3
  import styled, { css, keyframes } from 'styled-components';
4
4
  import { getAppModuleGradient, IconArrowLeft, IconClearButton, IconRefresh, IconWindowMaximize, IconWindowMinimize, isPositiveNumber, SDKUI_Localizator } from '../../helper';
5
5
  import TMButton from './TMButton';
6
- import { LoadIndicator } from 'devextreme-react/load-indicator';
6
+ import { SpinnerGraphic } from './TMSpinner';
7
7
  import { TMExceptionBoxManager } from './TMPopUp';
8
8
  import { Gutters, TMColors } from '../../utils/theme';
9
- const StyledBackButton = styled.button `
10
- display: flex;
11
- align-items: center;
12
- justify-content: center;
13
- gap: 4px;
14
- padding: 4px 10px;
15
- border: 1px solid ${({ $isActive }) => $isActive ? 'rgba(37, 89, 165, 0.3)' : 'rgba(255, 255, 255, 0.4)'};
16
- border-radius: 6px;
17
- cursor: pointer;
18
- font-weight: 500;
19
- font-size: 0.85rem;
20
- flex-shrink: 0;
21
- transition: all 0.15s ease;
22
- background-color: ${({ $isActive }) => $isActive ? '#FFFFFF' : '#2559A5'};
23
- box-shadow: none;
24
-
25
- &&, && * {
26
- color: ${({ $isActive }) => $isActive ? '#2559A5' : '#FFFFFF'} !important;
27
- }
28
-
29
- svg {
30
- font-size: 14px;
31
- }
9
+ const StyledBackButton = styled.button `
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ gap: 4px;
14
+ padding: 4px 10px;
15
+ border: 1px solid ${({ $isActive }) => $isActive ? 'rgba(37, 89, 165, 0.3)' : 'rgba(255, 255, 255, 0.4)'};
16
+ border-radius: 6px;
17
+ cursor: pointer;
18
+ font-weight: 500;
19
+ font-size: 0.85rem;
20
+ flex-shrink: 0;
21
+ transition: all 0.15s ease;
22
+ background-color: ${({ $isActive }) => $isActive ? '#FFFFFF' : '#2559A5'};
23
+ box-shadow: none;
24
+
25
+ &&, && * {
26
+ color: ${({ $isActive }) => $isActive ? '#2559A5' : '#FFFFFF'} !important;
27
+ }
28
+
29
+ svg {
30
+ font-size: 14px;
31
+ }
32
32
  `;
33
33
  import { AppModules, SDK_Globals } from '@topconsultnpm/sdk-ts';
34
34
  import { DeviceType, useDeviceType } from './TMDeviceProvider';
35
- const StyledPanelContainer = styled.div `
36
- width: 100%;
37
- height: 100%;
38
- font-size: 1rem;
39
- box-shadow: 3px 3px 5px #D3D3D3BF;
40
- border-radius: 10px;
41
- opacity: 1;
42
- background: #FFFFFF;
43
- min-height: 0;
44
- overflow: hidden;
45
- position: ${({ $isMaximized }) => $isMaximized ? 'fixed' : 'relative'};
46
- top: ${({ $isMaximized }) => $isMaximized ? '50px' : 'auto'};
47
- left: ${({ $isMaximized }) => $isMaximized ? '50px' : 'auto'};
48
- width: ${({ $isMaximized }) => $isMaximized ? `calc(100vw - 50px - (${Gutters.getGutters()}px * 2))` : '100%'};
49
- height: ${({ $isMaximized }) => $isMaximized ? `calc(100vh - 50px - (${Gutters.getGutters()}px * 2))` : '100%'};
50
- z-index: ${({ $isMaximized }) => $isMaximized ? 2000 : 'auto'};
51
- margin: ${({ $isMaximized }) => $isMaximized ? `${Gutters.getGutters()}px` : '0'};
52
- outline: none;
53
- /* transition: all 0.2s; */
35
+ const StyledPanelContainer = styled.div `
36
+ width: 100%;
37
+ height: 100%;
38
+ font-size: 1rem;
39
+ box-shadow: 3px 3px 5px #D3D3D3BF;
40
+ border-radius: 10px;
41
+ opacity: 1;
42
+ background: #FFFFFF;
43
+ min-height: 0;
44
+ overflow: hidden;
45
+ position: ${({ $isMaximized }) => $isMaximized ? 'fixed' : 'relative'};
46
+ top: ${({ $isMaximized }) => $isMaximized ? '50px' : 'auto'};
47
+ left: ${({ $isMaximized }) => $isMaximized ? '50px' : 'auto'};
48
+ width: ${({ $isMaximized }) => $isMaximized ? `calc(100vw - 50px - (${Gutters.getGutters()}px * 2))` : '100%'};
49
+ height: ${({ $isMaximized }) => $isMaximized ? `calc(100vh - 50px - (${Gutters.getGutters()}px * 2))` : '100%'};
50
+ z-index: ${({ $isMaximized }) => $isMaximized ? 2000 : 'auto'};
51
+ margin: ${({ $isMaximized }) => $isMaximized ? `${Gutters.getGutters()}px` : '0'};
52
+ outline: none;
53
+ /* transition: all 0.2s; */
54
54
  `;
55
- const StyledPanelHeader = styled.div `
56
- width:100% ;
57
- display: flex;
58
- flex-direction: row;
59
- align-items: center;
60
- justify-content: flex-start;
61
- padding: 4px 10px;
55
+ const StyledPanelHeader = styled.div `
56
+ width:100% ;
57
+ display: flex;
58
+ flex-direction: row;
59
+ align-items: center;
60
+ justify-content: flex-start;
61
+ padding: 4px 10px;
62
62
  background: ${({ $isActive, $backgroundColor, $activeGradient }) => $isActive
63
63
  ? ($activeGradient ??
64
64
  'linear-gradient(270deg, #46B5A2 16%, #3BAABC 34%, #3BAABC 34%, #3681AD 54%, #3368A5 72%, #2F549D 88%, #304F99 100%)')
65
- : ($backgroundColor ?? '#F4F4F4')};
66
- box-shadow: 3px 3px 5px #D3D3D3BF;
67
- color: ${({ $isActive, $color }) => $isActive ? '#FFFFFF' : ($color ?? '#2559A5')};
68
- border-top-right-radius: 10px;
69
- border-top-left-radius: 10px;
70
- height: 40px;
71
- font-weight: 600;
72
- outline: none;
73
- &:focus {
74
- outline: none;
75
- }
76
- // Ensure all children inherit the header color
77
- &,
78
- * {
79
- color: ${({ $isActive, $color }) => $isActive ? '#FFFFFF' : ($color ?? '#2559A5')} !important;
80
- }
65
+ : ($backgroundColor ?? '#F4F4F4')};
66
+ box-shadow: 3px 3px 5px #D3D3D3BF;
67
+ color: ${({ $isActive, $color }) => $isActive ? '#FFFFFF' : ($color ?? '#2559A5')};
68
+ border-top-right-radius: 10px;
69
+ border-top-left-radius: 10px;
70
+ height: 40px;
71
+ font-weight: 600;
72
+ outline: none;
73
+ &:focus {
74
+ outline: none;
75
+ }
76
+ // Ensure all children inherit the header color
77
+ &,
78
+ * {
79
+ color: ${({ $isActive, $color }) => $isActive ? '#FFFFFF' : ($color ?? '#2559A5')} !important;
80
+ }
81
81
  `;
82
- const StyledPanelContent = styled.div `
83
- width: 100%;
84
- height: ${props => props.$height};
85
- min-height: 0;
86
- overflow: hidden;
87
- border-bottom-right-radius: 10px;
88
- border-bottom-left-radius: 10px;
89
- padding: ${props => props.$padding};
90
- display: flex;
91
- justify-content: space-between;
92
- position: relative;
93
- user-select: none;
94
- -webkit-touch-callout: none;
95
- -webkit-user-select: none;
96
- outline: none;
97
- &:focus {
98
- outline: none;
99
- }
82
+ const StyledPanelContent = styled.div `
83
+ width: 100%;
84
+ height: ${props => props.$height};
85
+ min-height: 0;
86
+ overflow: hidden;
87
+ border-bottom-right-radius: 10px;
88
+ border-bottom-left-radius: 10px;
89
+ padding: ${props => props.$padding};
90
+ display: flex;
91
+ justify-content: space-between;
92
+ position: relative;
93
+ user-select: none;
94
+ -webkit-touch-callout: none;
95
+ -webkit-user-select: none;
96
+ outline: none;
97
+ &:focus {
98
+ outline: none;
99
+ }
100
100
  `;
101
101
  // Allineata al TABLET_BREAKPOINT (768) di TMDeviceProvider
102
102
  const MOBILE_MEDIA_QUERY = '(max-width: 767.98px)';
@@ -105,102 +105,102 @@ const MOBILE_MEDIA_QUERY = '(max-width: 767.98px)';
105
105
  // la track (larga quanto il viewport) va a +100% della PROPRIA larghezza, il testo a -100%
106
106
  // della SUA. Le due animazioni hanno stessa durata ed easing, quindi lo scostamento netto
107
107
  // in ogni istante è esattamente e(t) * (viewport - testo).
108
- const panelTitleTrackMarquee = keyframes `
109
- 0%, 15% { transform: translateX(0); }
110
- 45%, 55% { transform: translateX(100%); }
111
- 85%, 100% { transform: translateX(0); }
108
+ const panelTitleTrackMarquee = keyframes `
109
+ 0%, 15% { transform: translateX(0); }
110
+ 45%, 55% { transform: translateX(100%); }
111
+ 85%, 100% { transform: translateX(0); }
112
112
  `;
113
- const panelTitleTextMarquee = keyframes `
114
- 0%, 15% { transform: translateX(0); }
115
- 45%, 55% { transform: translateX(-100%); }
116
- 85%, 100% { transform: translateX(0); }
113
+ const panelTitleTextMarquee = keyframes `
114
+ 0%, 15% { transform: translateX(0); }
115
+ 45%, 55% { transform: translateX(-100%); }
116
+ 85%, 100% { transform: translateX(0); }
117
117
  `;
118
118
  // Ritaglia il testo in eccesso: è l'unico box con overflow hidden, la track ci scorre dentro
119
- const StyledPanelTitleViewport = styled.div `
120
- flex: 1;
121
- display: flex;
122
- align-items: center;
123
- justify-content: flex-start;
124
- min-width: 0;
125
- overflow: hidden;
126
- padding: 0 8px;
119
+ const StyledPanelTitleViewport = styled.div `
120
+ flex: 1;
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: flex-start;
124
+ min-width: 0;
125
+ overflow: hidden;
126
+ padding: 0 8px;
127
127
  `;
128
128
  // Larga quanto il viewport: è il metro con cui il -100% del testo viene compensato.
129
- const StyledPanelTitleTrack = styled.div `
130
- flex: 1;
131
- min-width: 0;
132
- display: flex;
133
- align-items: center;
134
-
135
- ${({ $marquee }) => $marquee && css `
136
- @media ${MOBILE_MEDIA_QUERY} {
137
- animation: ${panelTitleTrackMarquee} 10s ease-in-out infinite;
138
-
139
- @media (prefers-reduced-motion: reduce) {
140
- animation: none;
141
- }
142
- }
143
- `}
129
+ const StyledPanelTitleTrack = styled.div `
130
+ flex: 1;
131
+ min-width: 0;
132
+ display: flex;
133
+ align-items: center;
134
+
135
+ ${({ $marquee }) => $marquee && css `
136
+ @media ${MOBILE_MEDIA_QUERY} {
137
+ animation: ${panelTitleTrackMarquee} 10s ease-in-out infinite;
138
+
139
+ @media (prefers-reduced-motion: reduce) {
140
+ animation: none;
141
+ }
142
+ }
143
+ `}
144
144
  `;
145
145
  // Senza $marquee resta il troncamento con i puntini di sempre
146
- const StyledPanelTitle = styled.div `
147
- margin: 0;
148
- white-space: nowrap;
149
- overflow: hidden;
150
- text-overflow: ellipsis;
151
-
152
- ${({ $marquee }) => $marquee && css `
153
- @media ${MOBILE_MEDIA_QUERY} {
154
- flex: 0 0 auto;
155
- width: max-content;
156
- min-width: 100%;
157
- max-width: none;
158
- overflow: visible;
159
- text-overflow: clip;
160
- animation: ${panelTitleTextMarquee} 10s ease-in-out infinite;
161
-
162
- @media (prefers-reduced-motion: reduce) {
163
- animation: none;
164
- width: auto;
165
- min-width: 0;
166
- overflow: hidden;
167
- text-overflow: ellipsis;
168
- }
169
- }
170
- `}
146
+ const StyledPanelTitle = styled.div `
147
+ margin: 0;
148
+ white-space: nowrap;
149
+ overflow: hidden;
150
+ text-overflow: ellipsis;
151
+
152
+ ${({ $marquee }) => $marquee && css `
153
+ @media ${MOBILE_MEDIA_QUERY} {
154
+ flex: 0 0 auto;
155
+ width: max-content;
156
+ min-width: 100%;
157
+ max-width: none;
158
+ overflow: visible;
159
+ text-overflow: clip;
160
+ animation: ${panelTitleTextMarquee} 10s ease-in-out infinite;
161
+
162
+ @media (prefers-reduced-motion: reduce) {
163
+ animation: none;
164
+ width: auto;
165
+ min-width: 0;
166
+ overflow: hidden;
167
+ text-overflow: ellipsis;
168
+ }
169
+ }
170
+ `}
171
171
  `;
172
- const refreshSpin = keyframes `
173
- from { transform: rotate(0deg); }
174
- to { transform: rotate(360deg); }
172
+ const refreshSpin = keyframes `
173
+ from { transform: rotate(0deg); }
174
+ to { transform: rotate(360deg); }
175
175
  `;
176
176
  // Fa girare l'icona per tutta la durata del ricaricamento
177
- const StyledRefreshIcon = styled.span `
178
- display: flex;
179
- align-items: center;
180
- justify-content: center;
181
-
182
- ${({ $isSpinning }) => $isSpinning && css `
183
- animation: ${refreshSpin} 1s linear infinite;
184
-
185
- @media (prefers-reduced-motion: reduce) {
186
- animation: none;
187
- }
188
- `}
177
+ const StyledRefreshIcon = styled.span `
178
+ display: flex;
179
+ align-items: center;
180
+ justify-content: center;
181
+
182
+ ${({ $isSpinning }) => $isSpinning && css `
183
+ animation: ${refreshSpin} 1s linear infinite;
184
+
185
+ @media (prefers-reduced-motion: reduce) {
186
+ animation: none;
187
+ }
188
+ `}
189
189
  `;
190
190
  // Copre il solo contenuto del pannello (StyledPanelContent è position: relative con overflow hidden)
191
- const StyledPanelLoaderOverlay = styled.div `
192
- position: absolute;
193
- inset: 0;
194
- z-index: 10;
195
- display: flex;
196
- flex-direction: column;
197
- align-items: center;
198
- justify-content: center;
199
- gap: 10px;
200
- background: rgba(255, 255, 255, 0.65);
201
- border-bottom-right-radius: 10px;
202
- border-bottom-left-radius: 10px;
203
- color: ${TMColors.primaryColor};
191
+ const StyledPanelLoaderOverlay = styled.div `
192
+ position: absolute;
193
+ inset: 0;
194
+ z-index: 10;
195
+ display: flex;
196
+ flex-direction: column;
197
+ align-items: center;
198
+ justify-content: center;
199
+ gap: 10px;
200
+ background: rgba(255, 255, 255, 0.65);
201
+ border-bottom-right-radius: 10px;
202
+ border-bottom-left-radius: 10px;
203
+ color: ${TMColors.primaryColor};
204
204
  `;
205
205
  /**
206
206
  * TMPanel - Pannello con supporto Controlled/Uncontrolled
@@ -295,7 +295,10 @@ onRefresh, isRefreshing, onActivate, onBack, backCaption = SDKUI_Localizator.Bac
295
295
  return (_jsxs(StyledPanelContainer, { ref: panelRef, $isMaximized: onMaximize ? false : isMaximized, style: {
296
296
  visibility: isVisible ? 'visible' : 'hidden',
297
297
  }, tabIndex: -1, onFocus: !isControlled ? handleFocusUncontrolled : undefined, onBlur: !isControlled ? handleBlurUncontrolled : undefined, onClick: handleActivation, onContextMenu: (e) => e.preventDefault(), children: [showHeader &&
298
- _jsx(StyledPanelHeader, { $backgroundColor: backgroundColor, $color: color, $isActive: currentIsActive, onDoubleClick: () => {
298
+ _jsx(StyledPanelHeader, { $backgroundColor: backgroundColor, $color: color, $isActive: currentIsActive, onDoubleClick: (e) => {
299
+ // Ignora il doppio click sui controlli dell'header
300
+ if (e.target.closest('button, a, input, select, textarea, [role="button"]'))
301
+ return;
299
302
  if (onHeaderDoubleClick)
300
303
  onHeaderDoubleClick();
301
304
  else
@@ -326,6 +329,6 @@ onRefresh, isRefreshing, onActivate, onBack, backCaption = SDKUI_Localizator.Bac
326
329
  }, children: [toolbar, onRefresh && _jsx(TMButton, { color: 'primaryOutline', caption: SDKUI_Localizator.Refresh, disabled: isBusy, icon: _jsx(StyledRefreshIcon, { $isSpinning: isBusy, children: _jsx(IconRefresh, {}) }), btnStyle: 'icon', onClick: handleRefresh }), allowMaximize && _jsx(TMButton, { color: 'primaryOutline', caption: isMaximized ? SDKUI_Localizator.Minimize : SDKUI_Localizator.Maximize, icon: isMaximized
327
330
  ? _jsx(IconWindowMinimize, { fontSize: 16 })
328
331
  : _jsx(IconWindowMaximize, { fontSize: 16 }), btnStyle: 'icon', onClick: handleMaximize }), onClose && _jsx(TMButton, { color: 'primaryOutline', caption: SDKUI_Localizator.Close, icon: _jsx(IconClearButton, {}), btnStyle: 'icon', onClick: () => { setIsMaximized(false); onClose?.(); } })] })] }) }), _jsxs(StyledPanelContent, { $height: showHeader ? "calc(100% - 40px)" : "100%", $padding: padding, $backgroundColor: backgroundColorContainer ?? `#FFFFFF`, children: [children, isBusy &&
329
- _jsxs(StyledPanelLoaderOverlay, { children: [_jsx(LoadIndicator, { height: 40, width: 40 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })] })] }));
332
+ _jsxs(StyledPanelLoaderOverlay, { children: [_jsx(SpinnerGraphic, {}), _jsx("div", { children: SDKUI_Localizator.Loading })] })] })] }));
330
333
  });
331
334
  export default TMPanel;
@@ -8,10 +8,11 @@ interface ITMSpinner {
8
8
  onCancel?: () => void;
9
9
  cancelText?: string;
10
10
  }
11
+ declare const SpinnerGraphic: () => React.JSX.Element;
11
12
  declare const Spinner: ({ backgroundColor, description, fontSize, width, flat, onCancel, cancelText }: ITMSpinner) => React.JSX.Element;
12
13
  declare class TMSpinner {
13
14
  static show({ description, backgroundColor, fontSize, flat, onCancel, cancelText }?: ITMSpinner): void;
14
15
  static hide(): void;
15
16
  }
16
- export { Spinner };
17
+ export { Spinner, SpinnerGraphic };
17
18
  export default TMSpinner;
@@ -157,13 +157,15 @@ const StyledCancelButton = styled.button `
157
157
  transform: scale(0.98);
158
158
  }
159
159
  `;
160
+ // Grafica dello spinner (logo + 8 pallini animati), riusabile anche nei loader inline
161
+ const SpinnerGraphic = () => (_jsxs("div", { style: { position: 'relative', width: '80px', height: '80px' }, children: [_jsx("img", { style: {
162
+ position: 'absolute',
163
+ top: '50%',
164
+ left: '50%',
165
+ transform: 'translate(-54%, -54%)'
166
+ }, src: six, width: 35, alt: "" }), _jsxs(StyledSpinnerAnimation, { children: [_jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {})] })] }));
160
167
  const Spinner = ({ backgroundColor = 'transparent', description = '', fontSize = FontSize.defaultFontSize, width = 'fit-content', flat = false, onCancel, cancelText = 'Annulla' }) => {
161
- return (_jsx(StyledSpinnerContainer, { $backgroundColor: backgroundColor, children: _jsxs(StyledSpinnerWrapper, { $width: width, $flat: flat, $hasCancel: !!onCancel, children: [_jsxs("div", { style: { position: 'relative', width: '80px', height: '80px' }, children: [_jsx("img", { style: {
162
- position: 'absolute',
163
- top: '50%',
164
- left: '50%',
165
- transform: 'translate(-54%, -54%)'
166
- }, src: six, width: 35, alt: "" }), _jsxs(StyledSpinnerAnimation, { children: [_jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {})] })] }), _jsx(StyledSpinnerDescription, { $fontSize: fontSize, children: description }), onCancel && (_jsx(StyledCancelButton, { onClick: onCancel, children: cancelText }))] }) }));
168
+ return (_jsx(StyledSpinnerContainer, { $backgroundColor: backgroundColor, children: _jsxs(StyledSpinnerWrapper, { $width: width, $flat: flat, $hasCancel: !!onCancel, children: [_jsx(SpinnerGraphic, {}), _jsx(StyledSpinnerDescription, { $fontSize: fontSize, children: description }), onCancel && (_jsx(StyledCancelButton, { onClick: onCancel, children: cancelText }))] }) }));
167
169
  };
168
170
  class TMSpinner {
169
171
  static show({ description, backgroundColor, fontSize, flat, onCancel, cancelText } = {
@@ -183,5 +185,5 @@ class TMSpinner {
183
185
  container?.remove();
184
186
  }
185
187
  }
186
- export { Spinner };
188
+ export { Spinner, SpinnerGraphic };
187
189
  export default TMSpinner;
@@ -21,6 +21,8 @@ export interface TMTabPanelProps {
21
21
  closable?: boolean;
22
22
  /** Se false disabilita la rinomina su questo tab. Default true. */
23
23
  renamable?: boolean;
24
+ /** Se false il tab non si trascina e non accetta il rilascio di altri: resta al suo posto. Default true. */
25
+ movable?: boolean;
24
26
  children?: React.ReactNode;
25
27
  }
26
28
  /** Contenitore dichiarativo di un tab: figlio diretto di TMTabControl, non renderizza nulla di suo. */
@@ -70,6 +72,9 @@ export interface TMTabControlProps {
70
72
  contentStyle?: React.CSSProperties;
71
73
  /** Stile del contenitore esterno. */
72
74
  style?: React.CSSProperties;
75
+ /** Contenuto del chiamante in coda alla barra, a destra dei tab: es. i pulsanti di azione della pagina.
76
+ Fa parte della barra, quindi con hideBarWhenSingle e un solo tab non viene mostrato. */
77
+ toolbar?: React.ReactNode;
73
78
  children: React.ReactNode;
74
79
  }
75
80
  declare const TMTabControl: React.FC<TMTabControlProps>;
@@ -18,10 +18,11 @@ import { getAppModuleButtonColors, getAppModuleGradient, IconAdd, IconCloseOutli
18
18
  // I tab non attivi restano sempre montati e solo spostati fuori schermo: nulla si ricarica ne perde stato.
19
19
  // onTabClose -> mostra la 'x' sui tab e la voce Chiudi nel menu contestuale.
20
20
  // onTabRename -> doppio clic (o voce Rinomina) per modificare il titolo in linea.
21
- // onTabReorder -> tab spostabili con il trascinamento.
21
+ // onTabReorder -> tab spostabili con il trascinamento; il singolo tab si blocca con movable={false}.
22
22
  // tabMenuItems -> voci del chiamante nel menu del tasto destro sul tab, prima di Rinomina e Chiudi.
23
23
  // contentAnimation -> effetto del contenuto al cambio tab: 'fade' (default), 'slide', 'zoom' o 'none'.
24
24
  // swipeEnabled -> su touch si cambia tab trascinando il contenuto di lato.
25
+ // toolbar -> contenuto del chiamante in coda alla barra, a destra dei tab (azioni di pagina).
25
26
  //
26
27
  // Da tastiera, con il focus sulla barra: frecce destra/giu al tab successivo, sinistra/su al precedente,
27
28
  // Home ed End al primo e all'ultimo. I tab disabilitati vengono saltati.
@@ -90,7 +91,7 @@ const VARIANTS = {
90
91
  joins: false
91
92
  })
92
93
  };
93
- /** Contenitore della barra: frecce ai lati e striscia dei tab in mezzo. */
94
+ /** Contenitore della barra: frecce ai lati, striscia dei tab in mezzo ed eventuale toolbar in coda. */
94
95
  const TabsHeader = styled.div `
95
96
  display: flex;
96
97
  align-items: flex-end;
@@ -152,6 +153,16 @@ const AddButton = styled.button `
152
153
  &:active { background: ${({ $v }) => $v.buttonActive}; }
153
154
  &:focus, &:focus-visible { outline: none; }
154
155
  `;
156
+ /** Contenuto libero del chiamante in coda alla barra: azioni di pagina allineate a destra dei tab.
157
+ E' position: relative cosi un menu a tendina del chiamante puo posizionarsi rispetto a quest'area. */
158
+ const TabsToolbar = styled.div `
159
+ display: flex;
160
+ align-items: center;
161
+ flex-shrink: 0;
162
+ margin-bottom: 3px;
163
+ padding-left: 8px;
164
+ position: relative;
165
+ `;
155
166
  /** Area del contenuto: fa da riferimento ai pannelli nascosti, che restano montati ma non impaginati. */
156
167
  const ContentArea = styled.div `
157
168
  position: relative;
@@ -341,7 +352,7 @@ const shortenLabel = (label) => {
341
352
  /** Contenitore dichiarativo di un tab: figlio diretto di TMTabControl, non renderizza nulla di suo. */
342
353
  export const TMTabPanel = ({ children }) => _jsx(_Fragment, { children: children });
343
354
  const TMTabControl = (props) => {
344
- const { selectedId, defaultSelectedId, onSelectedIdChange, hideBarWhenSingle = false, onTabClose, closableWhenSingle = false, onTabRename, onTabReorder, onTabAdd, tabMenuItems, addCaption = SDKUI_Localizator.AddTab, renameCaption = SDKUI_Localizator.Rename, closeCaption = SDKUI_Localizator.Close, closeOtherCaption = SDKUI_Localizator.CloseOtherTabs, variant = 'module', textColor, idleTextColor, contentAnimation = 'none', animationDuration = 300, swipeEnabled = true, contentStyle, style, children } = props;
355
+ const { selectedId, defaultSelectedId, onSelectedIdChange, hideBarWhenSingle = false, onTabClose, closableWhenSingle = false, onTabRename, onTabReorder, onTabAdd, tabMenuItems, addCaption = SDKUI_Localizator.AddTab, renameCaption = SDKUI_Localizator.Rename, closeCaption = SDKUI_Localizator.Close, closeOtherCaption = SDKUI_Localizator.CloseOtherTabs, variant = 'module', textColor, idleTextColor, contentAnimation = 'none', animationDuration = 300, swipeEnabled = true, contentStyle, style, toolbar, children } = props;
345
356
  const v = useMemo(() => VARIANTS[variant](), [variant]);
346
357
  const titleColor = textColor ?? v.textColor;
347
358
  const titleIdleColor = idleTextColor ?? v.idleTextColor;
@@ -578,11 +589,11 @@ const TMTabControl = (props) => {
578
589
  tabIndex: isActive ? 0 : -1,
579
590
  // Con il titolo accorciato il nome per esteso lo mostra gia il tooltip
580
591
  title: canRename && !shortLabel ? 'Doppio clic per rinominare' : undefined, $active: isActive, $textColor: titleColor, $idleColor: titleIdleColor, disabled: p.props.disabled, onClick: () => handleSelect(p.props.id), onDoubleClick: () => canRename && setEditingId(p.props.id), children: [shortLabel ?? p.props.label, (p.props.badge ?? 0) > 0 && _jsx(TabBadge, { $color: p.props.badgeColor ?? '#E53935', children: p.props.badge })] }));
581
- return (_jsxs(React.Fragment, { children: [showSeparator && _jsx(TabSeparator, { $v: v }), _jsxs(TabItem, { "data-tab-id": p.props.id, $v: v, $active: isActive, $dragging: dragId === p.props.id, draggable: !!onTabReorder && editingId !== p.props.id,
592
+ return (_jsxs(React.Fragment, { children: [showSeparator && _jsx(TabSeparator, { $v: v }), _jsxs(TabItem, { "data-tab-id": p.props.id, $v: v, $active: isActive, $dragging: dragId === p.props.id, draggable: !!onTabReorder && p.props.movable !== false && editingId !== p.props.id,
582
593
  // Tipo MIME privato invece di 'text/plain': il tab non e rilasciabile fuori dalla barra
583
594
  // (barra indirizzi, campi di ricerca, altre applicazioni) scrivendoci dentro il suo id
584
- onDragStart: (e) => { setDragId(p.props.id); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData(TAB_DRAG_MIME, p.props.id); }, onDragOver: (e) => { if (!dragId || dragId === p.props.id)
585
- return; e.preventDefault(); e.dataTransfer.dropEffect = 'move'; setDropId(p.props.id); }, onDragLeave: () => setDropId(prev => prev === p.props.id ? undefined : prev), onDrop: (e) => { e.preventDefault(); if (dragId && dragId !== p.props.id)
595
+ onDragStart: (e) => { setDragId(p.props.id); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData(TAB_DRAG_MIME, p.props.id); }, onDragOver: (e) => { if (!dragId || dragId === p.props.id || p.props.movable === false)
596
+ return; e.preventDefault(); e.dataTransfer.dropEffect = 'move'; setDropId(p.props.id); }, onDragLeave: () => setDropId(prev => prev === p.props.id ? undefined : prev), onDrop: (e) => { e.preventDefault(); if (dragId && dragId !== p.props.id && p.props.movable !== false)
586
597
  onTabReorder?.(dragId, p.props.id); setDragId(undefined); setDropId(undefined); }, onDragEnd: () => { setDragId(undefined); setDropId(undefined); }, onContextMenu: (e) => openMenu(e, p.props.id), children: [editingId === p.props.id
587
598
  // In rinomina: Invio conferma, Esc annulla, uscendo dal campo si conferma
588
599
  ? _jsx(RenameInput, { autoFocus: true, defaultValue: String(p.props.label), onFocus: (e) => e.currentTarget.select(), onBlur: (e) => commitRename(p.props.id, e.currentTarget.value), onKeyDown: (e) => {
@@ -595,7 +606,7 @@ const TMTabControl = (props) => {
595
606
  // width: auto annulla il max-content del tooltip, che impedirebbe al titolo di restringersi
596
607
  ? _jsx(TMTooltip, { content: labelText, parentStyle: { display: 'flex', minWidth: 0 }, childStyle: { width: 'auto', minWidth: 0 }, children: tabButton })
597
608
  : tabButton, canClose && _jsx(CloseButton, { $color: isActive ? v.textColor : v.idleTextColor, type: 'button', tabIndex: isActive ? 0 : -1, title: closeCaption, onClick: () => onTabClose?.(p.props.id), children: "\u00D7" }), isDropTarget && _jsx(DropMark, { $side: draggedIndex > index ? 'left' : 'right' })] })] }, p.props.id));
598
- }), onTabAdd && _jsx(AddButton, { $v: v, type: 'button', title: addCaption, onClick: onTabAdd, children: _jsx(IconAdd, { fontSize: 18 }) })] }), arrows.right && _jsx(NavButton, { $v: v, type: 'button', tabIndex: -1, onClick: () => scrollTabs(1), children: _jsx(IconRight, { fontSize: 18 }) })] }), menu.visible && menuItems.length > 0 &&
609
+ }), onTabAdd && _jsx(AddButton, { $v: v, type: 'button', title: addCaption, onClick: onTabAdd, children: _jsx(IconAdd, { fontSize: 18 }) })] }), arrows.right && _jsx(NavButton, { $v: v, type: 'button', tabIndex: -1, onClick: () => scrollTabs(1), children: _jsx(IconRight, { fontSize: 18 }) }), toolbar && _jsx(TabsToolbar, { children: toolbar })] }), menu.visible && menuItems.length > 0 &&
599
610
  _jsx(TMContextMenu, { items: menuItems, externalControl: { visible: menu.visible, position: menu.position, onClose: closeMenu } }), _jsx(ContentArea, { onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, children: mountedPanels.map(p => {
600
611
  const isActive = p.props.id === activeId;
601
612
  return (_jsx(ContentPane, { role: 'tabpanel', "aria-hidden": !isActive, $active: isActive, $anim: animation, $duration: animationDuration, $phase: animPhase,
@@ -4,6 +4,17 @@ import { List } from 'react-window';
4
4
  import styled from 'styled-components';
5
5
  import { IconArrowLeft, IconArrowRight, IconChevronDown, IconChevronRight, SDKUI_Localizator } from '../../helper';
6
6
  import TMButton from './TMButton';
7
+ // Campi in cui frecce, +, - e spazio servono alla digitazione (checkbox e bottoni esclusi)
8
+ const NON_TEXT_INPUT_TYPES = new Set(['checkbox', 'radio', 'button', 'submit', 'reset', 'image', 'file', 'color', 'range']);
9
+ const isEditableElement = (el) => {
10
+ if (el.isContentEditable)
11
+ return true;
12
+ if (el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)
13
+ return true;
14
+ if (el instanceof HTMLInputElement)
15
+ return !NON_TEXT_INPUT_TYPES.has(el.type);
16
+ return false;
17
+ };
7
18
  // Componente riga virtualizzata memoizzato
8
19
  const VirtualRowComponent = memo(({ ariaAttributes, index, style, flattenedItems, focusedItemKey, selectedItemKeys, allowMultipleSelection, hasVisibleItems, handleNodeToggle, handleNodeClick, handleCheckboxChange, hasPartialChildSelection, itemRender, onItemContextMenu }) => {
9
20
  const item = flattenedItems[index];
@@ -184,6 +195,9 @@ const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMul
184
195
  const handleKeyDown = (event) => {
185
196
  if (!focusedItem)
186
197
  return;
198
+ // Non rubare i tasti ai campi editabili (es. input di un pannello sovrapposto)
199
+ if (event.target instanceof HTMLElement && isEditableElement(event.target))
200
+ return;
187
201
  let newFocusedItem = null;
188
202
  let handled = false;
189
203
  switch (event.key) {
@@ -459,6 +473,12 @@ const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMul
459
473
  return selectedCount > 0 && selectedCount < childKeys.length;
460
474
  }, [flattenTree]);
461
475
  const hasVisibleItems = useCallback((node) => {
476
+ // Nodo già caricato: conta i figli visibili (isExpandible è solo una stima a priori)
477
+ if (node.isLoaded) {
478
+ if (!node.items)
479
+ return false;
480
+ return node.items.filter(o => !o.hidden).length > 0;
481
+ }
462
482
  // If node has explicit isExpandible value, use that
463
483
  if (node.isExpandible !== undefined) {
464
484
  return node.isExpandible;
@@ -34,7 +34,7 @@ const StyledTitle = styled.h2 ` margin: 0; font-size: 1.5em; color: #333; `;
34
34
  const StyledProgressBarContainer = styled.div ` width: 100%; height: 20px; background: #e0e0e0; border-radius: 10px; overflow: hidden; margin: 20px 0; `;
35
35
  const StyledProgressBar = styled.div ` height: 100%; background: #4caf50; transition: width 0.3s ease; `;
36
36
  const StyledProgressText = styled.p ` font-weight: bold; color: #333; margin: 0; `;
37
- const StyledMessage = styled.p ` color: #666; font-size: 0.9em; margin-top: 10px; `;
37
+ const StyledMessage = styled.p ` color: #666; font-size: 0.9em; margin-top: 10px; white-space: pre-line; `;
38
38
  const StyledAbortButton = styled.button ` background: #ff4d4d; color: white; border: none; border-radius: 5px; padding: 10px 20px; font-size: 1em; cursor: pointer; margin-top: 20px; &:hover { background: #ff6666; } `;
39
39
  export const TMWaitPanel = (props) => {
40
40
  const { title, showPrimary, textPrimary, valuePrimary, maxValuePrimary, showSecondary, textSecondary, valueSecondary, maxValueSecondary, isCancelable, abortController, onAbortClick, useHighZIndex } = props;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ export interface ITMWaitPanelConfirmProps {
3
+ /** Deve stare sopra l'overlay del pannello: il manager passa il suo z-index + 1. */
4
+ zIndex?: number;
5
+ /** Conferma: l'operazione va interrotta. */
6
+ onYes?: () => void;
7
+ /** Annulla: l'operazione riprende. Usata anche dalla X. */
8
+ onNo?: () => void;
9
+ }
10
+ /** Il ref punta all'overlay, che il manager mostra e nasconde via `style.display`. */
11
+ declare const TMWaitPanelConfirm: React.ForwardRefExoticComponent<ITMWaitPanelConfirmProps & React.RefAttributes<HTMLDivElement>>;
12
+ export default TMWaitPanelConfirm;