@topconsultnpm/sdkui-react 6.22.0-dev2.4 → 6.22.0-dev2.41

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 (124) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
  2. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
  3. package/lib/components/base/TMButton.d.ts +13 -0
  4. package/lib/components/base/TMButton.js +137 -5
  5. package/lib/components/base/TMDataGrid.d.ts +2 -0
  6. package/lib/components/base/TMDataGrid.js +81 -8
  7. package/lib/components/base/TMEditorBase.d.ts +2 -0
  8. package/lib/components/base/TMFileManager.js +5 -3
  9. package/lib/components/base/TMFileManagerUtils.d.ts +1 -0
  10. package/lib/components/base/TMFileManagerUtils.js +16 -0
  11. package/lib/components/base/TMModal.d.ts +11 -0
  12. package/lib/components/base/TMModal.js +47 -13
  13. package/lib/components/base/TMPanel.d.ts +1 -0
  14. package/lib/components/base/TMPanel.js +79 -20
  15. package/lib/components/base/TMPopUp.js +3 -0
  16. package/lib/components/base/TMTooltip.d.ts +2 -1
  17. package/lib/components/base/TMTooltip.js +23 -6
  18. package/lib/components/base/TMTreeView.js +108 -67
  19. package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
  20. package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
  21. package/lib/components/choosers/TMDistinctValues.js +149 -142
  22. package/lib/components/choosers/TMMetadataChooser.js +1 -1
  23. package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
  24. package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
  25. package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
  26. package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
  27. package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
  28. package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
  29. package/lib/components/editors/TMDropDown.d.ts +3 -0
  30. package/lib/components/editors/TMDropDown.js +197 -5
  31. package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
  32. package/lib/components/editors/TMMetadataEditor.js +8 -3
  33. package/lib/components/editors/TMTextBox.js +2 -2
  34. package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
  35. package/lib/components/features/documents/TMDcmtBlog.d.ts +3 -0
  36. package/lib/components/features/documents/TMDcmtBlog.js +4 -4
  37. package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
  38. package/lib/components/features/documents/TMDcmtForm.js +112 -45
  39. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
  40. package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
  41. package/lib/components/features/documents/TMDcmtPreview.js +10 -6
  42. package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
  43. package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
  44. package/lib/components/features/documents/TMMasterInfoFields.js +186 -0
  45. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +1 -2
  46. package/lib/components/features/documents/TMMergeToPdfForm.js +30 -5
  47. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +45 -0
  48. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +323 -0
  49. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +370 -0
  50. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +283 -0
  51. package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
  52. package/lib/components/features/documents/TMRelationViewer.js +69 -20
  53. package/lib/components/features/search/TMSearch.js +31 -18
  54. package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
  55. package/lib/components/features/search/TMSearchQueryPanel.d.ts +2 -1
  56. package/lib/components/features/search/TMSearchQueryPanel.js +45 -9
  57. package/lib/components/features/search/TMSearchResult.d.ts +15 -0
  58. package/lib/components/features/search/TMSearchResult.js +509 -187
  59. package/lib/components/features/search/TMTreeSelector.js +18 -5
  60. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
  61. package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
  62. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  63. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
  64. package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
  65. package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
  66. package/lib/components/layout/panelManager/types.d.ts +3 -0
  67. package/lib/components/layout/panelManager/utils.js +13 -11
  68. package/lib/components/viewers/TMMidViewer.js +4 -1
  69. package/lib/components/viewers/TMTidViewer.js +25 -11
  70. package/lib/devextreme-license.d.ts +1 -0
  71. package/lib/devextreme-license.js +3 -0
  72. package/lib/helper/SDKUI_Globals.d.ts +109 -0
  73. package/lib/helper/SDKUI_Globals.js +150 -0
  74. package/lib/helper/SDKUI_Localizator.d.ts +34 -0
  75. package/lib/helper/SDKUI_Localizator.js +340 -0
  76. package/lib/helper/TMIcons.d.ts +1 -0
  77. package/lib/helper/TMIcons.js +3 -0
  78. package/lib/helper/dataGridSearchHelper.d.ts +28 -0
  79. package/lib/helper/dataGridSearchHelper.js +51 -0
  80. package/lib/helper/dcmtsHelper.d.ts +20 -0
  81. package/lib/helper/dcmtsHelper.js +58 -1
  82. package/lib/helper/helpers.d.ts +20 -0
  83. package/lib/helper/helpers.js +55 -1
  84. package/lib/helper/index.d.ts +1 -0
  85. package/lib/helper/index.js +1 -0
  86. package/lib/helper/queryHelper.d.ts +2 -0
  87. package/lib/helper/queryHelper.js +3 -1
  88. package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
  89. package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
  90. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  91. package/lib/hooks/useArchiveListForm.js +46 -0
  92. package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
  93. package/lib/hooks/useCaseFlowApprove.js +157 -0
  94. package/lib/hooks/useDataUserIdItem.d.ts +1 -0
  95. package/lib/hooks/useDataUserIdItem.js +1 -0
  96. package/lib/hooks/useDocumentOperations.d.ts +4 -0
  97. package/lib/hooks/useDocumentOperations.js +41 -7
  98. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +113 -0
  99. package/lib/hooks/useMultiMasterDetailDcmts.js +760 -0
  100. package/lib/hooks/useRelatedDocuments.js +2 -26
  101. package/lib/hooks/useShowDossiersV5.d.ts +12 -0
  102. package/lib/hooks/useShowDossiersV5.js +37 -0
  103. package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
  104. package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
  105. package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
  106. package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
  107. package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
  108. package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
  109. package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
  110. package/lib/hooks/useTMDistinctValuesSource.js +105 -0
  111. package/lib/index.d.ts +2 -0
  112. package/lib/index.js +5 -2
  113. package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
  114. package/lib/services/caseflow/CaseFlowService.js +693 -0
  115. package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
  116. package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
  117. package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
  118. package/lib/services/caseflow/caseFlowNameCache.js +116 -0
  119. package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
  120. package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
  121. package/lib/services/index.d.ts +4 -0
  122. package/lib/services/index.js +4 -0
  123. package/lib/services/platform_services.d.ts +1 -1
  124. package/package.json +9 -5
@@ -0,0 +1,370 @@
1
+ import React from 'react';
2
+ import { DataListViewModes, DcmtTypeDescriptor, MetadataDataTypes, MetadataDescriptor, RelationDescriptor } from '@topconsultnpm/sdk-ts';
3
+ import { DataGridTypes, IColumnProps } from 'devextreme-react/data-grid';
4
+ import { TMPanelDimensionsMap } from '../../layout/panelManager/types';
5
+ import { DcmtInfo, MetadataValueDescriptorEx } from '../../../ts';
6
+ /** Relazione di dettaglio con il relativo tipo documento */
7
+ export interface DetailRelationItem {
8
+ relation: RelationDescriptor;
9
+ detailDtd: DcmtTypeDescriptor | undefined;
10
+ }
11
+ export interface MultiDetailStep {
12
+ phase: number;
13
+ label: React.ReactNode;
14
+ }
15
+ export type AccordionSection = 'masterInfo' | 'archivedDetails' | 'archiveList';
16
+ /**
17
+ * Stato del pannello che ospita una sezione. Lo espone il panel manager, il cui contesto è leggibile
18
+ * solo dentro l'albero del provider: da qui passa alle testate (vedi TMSummarySectionSlot)
19
+ */
20
+ export type SectionPanelState = {
21
+ isMaximized: boolean;
22
+ canMaximize: boolean;
23
+ canCollapse: boolean;
24
+ toggleMaximize: () => void;
25
+ };
26
+ /** Valore di un metadato letto da un documento: una cella del risultato di ricerca */
27
+ export type MetadataScalarValue = string | number | boolean | Date | null | undefined;
28
+ /**
29
+ * Riga del riepilogo: oltre ai campi di servizio (prefisso `__`) porta i valori dei metadati
30
+ * indicizzati per mid (`String(mid)`), come richiesto dal dataField delle colonne della griglia
31
+ */
32
+ export interface ArchiveRow {
33
+ /** Chiave della riga in griglia */
34
+ id: number;
35
+ /** Tipo documento di dettaglio a cui appartiene la riga */
36
+ __tid: number;
37
+ /** Valori compilati nel form, nel formato richiesto dall'archiviazione */
38
+ __values: Array<MetadataValueDescriptorEx>;
39
+ /** File da archiviare, se il tipo documento lo prevede */
40
+ __file?: File;
41
+ /** Valore del metadato mostrato in colonna, indicizzato per mid */
42
+ [mid: string]: string | number | Array<MetadataValueDescriptorEx> | File | undefined;
43
+ }
44
+ /**
45
+ * Cella nel render delle colonne del riepilogo: i valori dei metadati arrivano dal form come
46
+ * stringhe, quindi il `value` generico di DevExtreme si restringe
47
+ */
48
+ export type ArchiveRowCell = Omit<DataGridTypes.ColumnCellTemplateData<ArchiveRow, number>, 'value'> & {
49
+ readonly value?: string | number;
50
+ };
51
+ /**
52
+ * Modifiche di una riga in arrivo dalla griglia (mid -> nuovo valore): in modifica diretta ci sono
53
+ * i metadati testuali e numerici (vedi isInlineEditableMetadata), quindi stringhe e numeri,
54
+ * e l'editor svuotato restituisce null
55
+ */
56
+ export type ArchiveRowChanges = Record<string, string | number | null | undefined>;
57
+ /** Aggiornamento di una riga della griglia, con le sole modifiche possibili in linea */
58
+ export type ArchiveRowUpdatingEvent = Omit<DataGridTypes.RowUpdatingEvent<ArchiveRow, number>, 'newData'> & {
59
+ newData: ArchiveRowChanges;
60
+ };
61
+ export declare const SECTION_WEIGHTS: Record<AccordionSection, number>;
62
+ export declare const SECTION_HEADER_HEIGHT_PX = 30;
63
+ export declare const PANEL_GUTTER_PX = 10;
64
+ /** Il master è modificabile solo se il tipo documento lo consente */
65
+ export declare const canUpdateMasterCallback: (dtd?: DcmtTypeDescriptor, masterDcmt?: DcmtInfo) => boolean;
66
+ /** Permesso di archiviazione sul tipo documento di dettaglio */
67
+ export declare const canArchive: (dtd?: DcmtTypeDescriptor) => boolean;
68
+ /**
69
+ * Marcatore delle celle modificabili direttamente in griglia: il doppio clic le porta in modifica,
70
+ * quindi non deve aprire anche il form della riga
71
+ */
72
+ export declare const INLINE_EDITABLE_CELL_CLASS = "tm-cell-inline-editable";
73
+ /**
74
+ * Modifica diretta in griglia: testo libero e numeri. Liste dati, calcolati, numeratori e speciali
75
+ * hanno editor e vincoli propri, le date una formattazione dedicata: restano al form
76
+ */
77
+ export declare const isInlineEditableMetadata: (md: MetadataDescriptor) => boolean;
78
+ /**
79
+ * Valore di una cella numerica: l'editor della griglia vuole un numero, non la stringa che
80
+ * la riga porta in `__values` per l'archiviazione. Vuoto o non numerico: cella vuota
81
+ */
82
+ export declare const toGridNumber: (value: unknown) => number | undefined;
83
+ /**
84
+ * Valore di un metadato letto da un documento archiviato, nel formato in cui il form della riga
85
+ * produce i suoi valori: stringa vuota se assente, data serializzata come la restituisce l'editor
86
+ * ('yyyy-MM-ddTHH:mm:ss', vedi TMDateBox), numero con il punto decimale. Solo così la riga copiata
87
+ * si rilegge nel form, si mostra in griglia e si archivia come una riga compilata a mano
88
+ */
89
+ export declare const toArchiveRowValue: (value: MetadataScalarValue, dataType: MetadataDataTypes | undefined) => string;
90
+ /** Colonne della griglia di riepilogo dai metadati (utente) del tipo documento */
91
+ export declare const buildColumnsFromDtd: (dtd: DcmtTypeDescriptor | undefined, renderDataListCell: (value: string | Date | number | undefined, dataListID: number, viewMode: DataListViewModes) => React.ReactElement) => Array<IColumnProps>;
92
+ export declare const getDetailDtdAsync: (detailTID: number | undefined) => Promise<DcmtTypeDescriptor | undefined>;
93
+ /** Nome visualizzato del tipo documento (localizzato se richiesto) */
94
+ export declare const getDtdDisplayName: (dtd: DcmtTypeDescriptor | undefined) => string;
95
+ /** Messaggio del blocco: con righe inserite il tipo documento non è più modificabile */
96
+ export declare const getBackLockedTitle: () => string;
97
+ /**
98
+ * Altezze dei pannelli impilati a partire dalle sezioni aperte:
99
+ * una sezione chiusa vale 0% e resta a schermo con la sola testata
100
+ * le aperte si spartiscono tutto lo spazio
101
+ */
102
+ export declare const sectionPanelHeights: (sections: Array<AccordionSection>, sectionsOpen: Record<AccordionSection, boolean>, currentDimensions?: TMPanelDimensionsMap) => TMPanelDimensionsMap;
103
+ export declare const multiDetailStyles: {
104
+ stepper: {
105
+ display: "flex";
106
+ alignItems: "center";
107
+ justifyContent: "center";
108
+ gap: string;
109
+ };
110
+ backArrow: {
111
+ display: "flex";
112
+ alignItems: "center";
113
+ justifyContent: "center";
114
+ width: string;
115
+ height: string;
116
+ borderRadius: string;
117
+ border: string;
118
+ color: string;
119
+ cursor: "pointer";
120
+ marginRight: string;
121
+ transition: "all 0.18s ease";
122
+ };
123
+ stepPill: {
124
+ display: "flex";
125
+ alignItems: "center";
126
+ gap: string;
127
+ padding: string;
128
+ borderRadius: string;
129
+ transition: "all 0.18s ease";
130
+ };
131
+ stepCircle: {
132
+ width: string;
133
+ height: string;
134
+ borderRadius: string;
135
+ color: "#ffffff";
136
+ display: "flex";
137
+ alignItems: "center";
138
+ justifyContent: "center";
139
+ fontSize: string;
140
+ fontWeight: number;
141
+ flexShrink: number;
142
+ transition: "all 0.18s ease";
143
+ };
144
+ stepLabel: {
145
+ fontSize: string;
146
+ whiteSpace: "nowrap";
147
+ display: "flex";
148
+ alignItems: "center";
149
+ };
150
+ stepConnector: {
151
+ width: string;
152
+ height: string;
153
+ margin: string;
154
+ borderRadius: string;
155
+ flexShrink: number;
156
+ };
157
+ stepTypeLabel: {
158
+ display: "flex";
159
+ alignItems: "center";
160
+ gap: string;
161
+ maxWidth: string;
162
+ };
163
+ stepTypeLabelText: {
164
+ overflow: "hidden";
165
+ textOverflow: "ellipsis";
166
+ whiteSpace: "nowrap";
167
+ };
168
+ selectContainer: {
169
+ display: "flex";
170
+ flexDirection: "column";
171
+ alignItems: "center";
172
+ flex: number;
173
+ gap: string;
174
+ overflow: "auto";
175
+ padding: string;
176
+ };
177
+ selectTitle: {
178
+ fontSize: string;
179
+ fontWeight: number;
180
+ color: string;
181
+ textAlign: "center";
182
+ marginBottom: string;
183
+ };
184
+ selectList: {
185
+ display: "flex";
186
+ flexDirection: "column";
187
+ gap: string;
188
+ width: string;
189
+ maxWidth: string;
190
+ };
191
+ typeCard: {
192
+ display: "flex";
193
+ alignItems: "center";
194
+ gap: string;
195
+ padding: string;
196
+ borderRadius: string;
197
+ cursor: "pointer";
198
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.06)";
199
+ transition: "all 0.15s ease";
200
+ };
201
+ typeCardIcon: {
202
+ width: string;
203
+ height: string;
204
+ borderRadius: string;
205
+ background: string;
206
+ display: "flex";
207
+ alignItems: "center";
208
+ justifyContent: "center";
209
+ flexShrink: number;
210
+ };
211
+ typeCardName: {
212
+ flex: number;
213
+ fontSize: string;
214
+ fontWeight: number;
215
+ color: string;
216
+ whiteSpace: "normal";
217
+ wordBreak: "break-word";
218
+ lineHeight: number;
219
+ };
220
+ };
221
+ export declare const accordionStyles: {
222
+ accordion: {
223
+ display: "flex";
224
+ flexDirection: "column";
225
+ minHeight: number;
226
+ borderRadius: string;
227
+ border: string;
228
+ background: string;
229
+ overflow: "hidden";
230
+ };
231
+ accordionInPanel: {
232
+ flex: number;
233
+ };
234
+ accordionHeader: {
235
+ display: "flex";
236
+ alignItems: "center";
237
+ gap: string;
238
+ padding: string;
239
+ cursor: "pointer";
240
+ userSelect: "none";
241
+ transition: "background 0.15s ease";
242
+ outline: string;
243
+ flexShrink: number;
244
+ minHeight: string;
245
+ };
246
+ accordionTitle: {
247
+ flex: number;
248
+ fontSize: string;
249
+ fontWeight: number;
250
+ color: string;
251
+ display: "flex";
252
+ alignItems: "center";
253
+ gap: string;
254
+ minWidth: number;
255
+ };
256
+ accordionBadge: {
257
+ display: "inline-flex";
258
+ alignItems: "center";
259
+ justifyContent: "center";
260
+ color: string;
261
+ fontSize: string;
262
+ fontWeight: number;
263
+ flexShrink: number;
264
+ };
265
+ accordionListBody: {
266
+ position: "relative";
267
+ flex: number;
268
+ minHeight: number;
269
+ display: "flex";
270
+ flexDirection: "column";
271
+ borderTop: string;
272
+ };
273
+ accordionListPlaceholder: {
274
+ display: "flex";
275
+ flex: number;
276
+ alignItems: "center";
277
+ justifyContent: "center";
278
+ gap: string;
279
+ padding: string;
280
+ color: string;
281
+ fontSize: string;
282
+ };
283
+ accordionExpandBtn: {
284
+ display: "inline-flex";
285
+ alignItems: "center";
286
+ justifyContent: "center";
287
+ width: string;
288
+ height: string;
289
+ padding: number;
290
+ borderRadius: string;
291
+ border: string;
292
+ background: string;
293
+ color: string;
294
+ cursor: "pointer";
295
+ flexShrink: number;
296
+ transition: "background 0.15s ease, color 0.15s ease";
297
+ };
298
+ };
299
+ export interface RenderStepperParams {
300
+ /** Passi da mostrare, in ordine di fase */
301
+ steps: Array<MultiDetailStep>;
302
+ /** Fase corrente: i passi con fase minore sono completati */
303
+ phase: number;
304
+ /** Con un solo tipo documento il numero dello step non si mostra */
305
+ hasMultipleTypes: boolean;
306
+ /** Con righe inserite il ritorno alla scelta del tipo è bloccato (freccia e step completato) */
307
+ isBackLocked: boolean;
308
+ /** La freccia di ritorno si mostra solo dove ha senso tornare indietro */
309
+ showBackArrow: boolean;
310
+ /** Ritorno indietro dalla freccia */
311
+ onBack: () => void;
312
+ /** Click su uno step completato: riceve la fase di destinazione */
313
+ onStepClick: (phase: number) => void;
314
+ }
315
+ export declare const renderStepper: ({ steps, phase, hasMultipleTypes, isBackLocked, showBackArrow, onBack, onStepClick }: RenderStepperParams) => import("react/jsx-runtime").JSX.Element;
316
+ export interface RenderSelectDcmtTypeParams {
317
+ /** Relazioni di dettaglio fra cui scegliere */
318
+ detailRelationItems: Array<DetailRelationItem>;
319
+ /** Relazione attualmente selezionata */
320
+ selectedRelation: RelationDescriptor | undefined;
321
+ /** Selezione di una relazione */
322
+ onSelectRelation: (item: DetailRelationItem) => void;
323
+ }
324
+ export declare const renderSelectDcmtType: ({ detailRelationItems, selectedRelation, onSelectRelation }: RenderSelectDcmtTypeParams) => import("react/jsx-runtime").JSX.Element;
325
+ export interface RenderAccordionHeaderParams {
326
+ /** Sezione a cui appartiene la testata */
327
+ section: AccordionSection;
328
+ /** Sezione aperta o collassata */
329
+ isOpen: boolean;
330
+ /** Tooltip del collasso */
331
+ tooltip: string;
332
+ /** Conteggio mostrato nel badge */
333
+ count: number;
334
+ /** Titolo della sezione (icona + testo) */
335
+ title: React.ReactNode;
336
+ /** Pulsanti specifici della sezione, mostrati prima di quello di ingrandimento */
337
+ actions?: React.ReactNode;
338
+ /** Stato del pannello che ospita la sezione: l'ingrandimento è quello del panel manager */
339
+ panel: SectionPanelState;
340
+ /** Apre/chiude la sezione */
341
+ onToggleOpen: (section: AccordionSection, isOpen: boolean) => void;
342
+ }
343
+ export declare const renderAccordionHeader: ({ section, isOpen, tooltip, count, title, actions, panel, onToggleOpen }: RenderAccordionHeaderParams) => import("react/jsx-runtime").JSX.Element;
344
+ /**
345
+ * Contenuto di un pannello: il contesto del panel manager è leggibile solo dentro l'albero del
346
+ * provider, quindi è qui che si prende lo stato di ingrandimento da passare alla sezione
347
+ */
348
+ export declare const TMSummarySectionSlot: ({ section, render, onBeforeMaximize }: {
349
+ section: AccordionSection;
350
+ render: (panel: SectionPanelState) => React.ReactNode;
351
+ onBeforeMaximize?: (section: AccordionSection) => void;
352
+ }) => import("react/jsx-runtime").JSX.Element;
353
+ /**
354
+ * Segnala che l'utente ha spostato un separatore: è l'unico caso in cui le altezze dei pannelli sono
355
+ * una sua scelta, quindi la sola da salvare. Le quote calcolate all'apertura del riepilogo o al
356
+ * collasso di una sezione non vanno scritte nel local storage.
357
+ * Vive dentro il provider perché il contesto è leggibile solo lì
358
+ */
359
+ export declare const TMSummaryResizeTracker: ({ onUserResize }: {
360
+ onUserResize: () => void;
361
+ }) => null;
362
+ /**
363
+ * Tiene allineate le altezze dei pannelli al collasso delle sezioni: il panel manager non
364
+ * conosce il collasso, quindi è qui che l'apertura di una sezione si traduce nelle nuove quote.
365
+ * Vive dentro il provider perché il contesto è leggibile solo lì
366
+ */
367
+ export declare const TMSectionPanelsHeights: ({ sections, sectionsOpen }: {
368
+ sections: Array<AccordionSection>;
369
+ sectionsOpen: Record<AccordionSection, boolean>;
370
+ }) => null;
@@ -0,0 +1,283 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React, { useEffect, useRef } from 'react';
3
+ import { AccessLevelsEx, DataListViewModes, DcmtTypeListCacheService, MetadataDataDomains, MetadataDataTypes, SDK_Globals } from '@topconsultnpm/sdk-ts';
4
+ import { hoverStyle, IconChevronRight, IconWindowMaximize, IconWindowMinimize, SDKUI_Localizator } from '../../../helper';
5
+ import { formatScalarValue, getUserMetadataSorted } from '../../../helper/dcmtsHelper';
6
+ import { TMColors } from '../../../utils/theme';
7
+ import { TMDcmtTypeIcon } from '../../viewers/TMTidViewer';
8
+ import TMTooltip from '../../base/TMTooltip';
9
+ import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
10
+ import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
11
+ // l'elenco da archiviare è l'informazione più importante, quindi prende la fetta maggiore
12
+ export const SECTION_WEIGHTS = {
13
+ masterInfo: 25,
14
+ archivedDetails: 30,
15
+ archiveList: 45
16
+ };
17
+ export const SECTION_HEADER_HEIGHT_PX = 30;
18
+ // Spessore dei separatori fra i pannelli
19
+ export const PANEL_GUTTER_PX = 10;
20
+ // =================================================================================================
21
+ // HELPER
22
+ // =================================================================================================
23
+ /** Il master è modificabile solo se il tipo documento lo consente */
24
+ export const canUpdateMasterCallback = (dtd, masterDcmt) => (dtd?.perm?.canUpdate === AccessLevelsEx.Yes || dtd?.perm?.canUpdate === AccessLevelsEx.Mixed)
25
+ && masterDcmt?.TID !== undefined && masterDcmt?.DID !== undefined;
26
+ /** Permesso di archiviazione sul tipo documento di dettaglio */
27
+ export const canArchive = (dtd) => dtd?.perm?.canArchive === AccessLevelsEx.Yes || dtd?.perm?.canArchive === AccessLevelsEx.Mixed;
28
+ /**
29
+ * Marcatore delle celle modificabili direttamente in griglia: il doppio clic le porta in modifica,
30
+ * quindi non deve aprire anche il form della riga
31
+ */
32
+ export const INLINE_EDITABLE_CELL_CLASS = 'tm-cell-inline-editable';
33
+ /**
34
+ * Modifica diretta in griglia: testo libero e numeri. Liste dati, calcolati, numeratori e speciali
35
+ * hanno editor e vincoli propri, le date una formattazione dedicata: restano al form
36
+ */
37
+ export const isInlineEditableMetadata = (md) => (md.dataType === MetadataDataTypes.Varchar || md.dataType === MetadataDataTypes.Number)
38
+ && (md.dataDomain === undefined || md.dataDomain === MetadataDataDomains.None)
39
+ && md.isSystem !== 1 && md.isSystemDerived !== 1;
40
+ /**
41
+ * Valore di una cella numerica: l'editor della griglia vuole un numero, non la stringa che
42
+ * la riga porta in `__values` per l'archiviazione. Vuoto o non numerico: cella vuota
43
+ */
44
+ export const toGridNumber = (value) => {
45
+ if (value === undefined || value === null || value === '')
46
+ return undefined;
47
+ const parsed = typeof value === 'number' ? value : Number(value);
48
+ return Number.isFinite(parsed) ? parsed : undefined;
49
+ };
50
+ /**
51
+ * Valore di un metadato letto da un documento archiviato, nel formato in cui il form della riga
52
+ * produce i suoi valori: stringa vuota se assente, data serializzata come la restituisce l'editor
53
+ * ('yyyy-MM-ddTHH:mm:ss', vedi TMDateBox), numero con il punto decimale. Solo così la riga copiata
54
+ * si rilegge nel form, si mostra in griglia e si archivia come una riga compilata a mano
55
+ */
56
+ export const toArchiveRowValue = (value, dataType) => {
57
+ if (value === undefined || value === null || value === '')
58
+ return '';
59
+ if (dataType === MetadataDataTypes.DateTime) {
60
+ const date = value instanceof Date ? value : new Date(String(value));
61
+ // Data non interpretabile: meglio la cella vuota di un valore che il form non saprebbe rileggere
62
+ if (Number.isNaN(date.getTime()))
63
+ return '';
64
+ const pad = (part) => String(part).padStart(2, '0');
65
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
66
+ }
67
+ if (dataType === MetadataDataTypes.Number) {
68
+ // L'archiviazione fa parseFloat del valore: serve il punto come separatore decimale
69
+ const parsed = typeof value === 'number' ? value : Number(String(value).trim());
70
+ return Number.isFinite(parsed) ? String(parsed) : '';
71
+ }
72
+ return String(value);
73
+ };
74
+ /** Colonne della griglia di riepilogo dai metadati (utente) del tipo documento */
75
+ export const buildColumnsFromDtd = (dtd, renderDataListCell) => getUserMetadataSorted(dtd).map(md => {
76
+ const dataListID = md.dataListID ?? 0;
77
+ const isDataList = md.dataDomain === MetadataDataDomains.DataList && dataListID > 0;
78
+ const viewMode = md.dataListViewMode ?? DataListViewModes.None;
79
+ const dataType = md.dataType;
80
+ const format = md.format?.format;
81
+ const formatCulture = md.format?.formatCulture;
82
+ const isInlineEditable = isInlineEditableMetadata(md);
83
+ const isNumber = dataType === MetadataDataTypes.Number;
84
+ // Date e numeri usano la formattazione scalare; le liste dati il render comune. La cella
85
+ // modificabile mostra sempre l'editor, quindi non ha un proprio render
86
+ const needsScalarFormat = !isDataList && !isInlineEditable && (dataType === MetadataDataTypes.DateTime || isNumber);
87
+ return {
88
+ dataField: String(md.id),
89
+ caption: (SDK_Globals.useLocalizedName ? md.nameLoc : md.name) ?? md.name ?? '',
90
+ // La colonna modificabile dichiara il tipo: senza valori DevExtreme non saprebbe che editor usare
91
+ ...(isInlineEditable
92
+ ? {
93
+ // I numeri arrivano in cella già come numeri (vedi buildArchiveRow): l'editor è la casella numerica
94
+ dataType: isNumber ? 'number' : 'string',
95
+ allowEditing: true,
96
+ showEditorAlways: true,
97
+ cssClass: INLINE_EDITABLE_CELL_CLASS,
98
+ // Lunghezza e obbligatorietà del metadato valgono anche in griglia; sui numeri
99
+ // i limiti restano all'API, in cella basta che sia un numero
100
+ editorOptions: !isNumber && md.length ? { maxLength: md.length } : undefined,
101
+ validationRules: String(md.isRequired) === '1' ? [{ type: 'required', message: SDKUI_Localizator.RequiredField }] : undefined,
102
+ }
103
+ : { allowEditing: false }),
104
+ cellRender: isDataList
105
+ ? (cellData) => renderDataListCell(cellData.value, dataListID, viewMode)
106
+ : (needsScalarFormat ? (cellData) => formatScalarValue(cellData.value, dataType, format, formatCulture) : undefined),
107
+ };
108
+ });
109
+ // Accesso al descrittore del tipo documento di dettaglio
110
+ export const getDetailDtdAsync = (detailTID) => detailTID === undefined ? Promise.resolve(undefined) : DcmtTypeListCacheService.GetWithNotGrantedAsync(detailTID, undefined);
111
+ /** Nome visualizzato del tipo documento (localizzato se richiesto) */
112
+ export const getDtdDisplayName = (dtd) => {
113
+ if (!dtd)
114
+ return '';
115
+ return (SDK_Globals.useLocalizedName ? dtd.nameLoc : dtd.name) ?? dtd.name ?? '';
116
+ };
117
+ /** Messaggio del blocco: con righe inserite il tipo documento non è più modificabile */
118
+ export const getBackLockedTitle = () => SDKUI_Localizator.DeleteAllRowsToChangeDcmtType;
119
+ /**
120
+ * Altezze dei pannelli impilati a partire dalle sezioni aperte:
121
+ * una sezione chiusa vale 0% e resta a schermo con la sola testata
122
+ * le aperte si spartiscono tutto lo spazio
123
+ */
124
+ export const sectionPanelHeights = (sections, sectionsOpen, currentDimensions) => {
125
+ const weightOf = (section) => {
126
+ if (!sectionsOpen[section])
127
+ return 0;
128
+ const current = parseFloat(currentDimensions?.[section]?.height ?? '');
129
+ // Sezione che si apre ora (era a 0%) o prima apertura: si riparte dalla quota nominale
130
+ return Number.isFinite(current) && current > 0 ? current : SECTION_WEIGHTS[section];
131
+ };
132
+ const total = sections.reduce((sum, section) => sum + weightOf(section), 0);
133
+ return sections.reduce((acc, section) => {
134
+ acc[section] = { width: '100%', height: total > 0 ? `${(weightOf(section) / total) * 100}%` : '0%' };
135
+ return acc;
136
+ }, {});
137
+ };
138
+ // =================================================================================================
139
+ // STILI
140
+ // =================================================================================================
141
+ export const multiDetailStyles = {
142
+ // Stepper
143
+ stepper: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '4px' },
144
+ backArrow: { display: 'flex', alignItems: 'center', justifyContent: 'center', width: '26px', height: '26px', borderRadius: '50%', border: `1.5px solid ${TMColors.primary}`, color: TMColors.primary, cursor: 'pointer', marginRight: '8px', transition: 'all 0.18s ease' },
145
+ stepPill: { display: 'flex', alignItems: 'center', gap: '7px', padding: '3px 10px 3px 4px', borderRadius: '16px', transition: 'all 0.18s ease' },
146
+ stepCircle: { width: '22px', height: '22px', borderRadius: '50%', color: '#ffffff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', fontWeight: 600, flexShrink: 0, transition: 'all 0.18s ease' },
147
+ stepLabel: { fontSize: '12px', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center' },
148
+ stepConnector: { width: '18px', height: '2px', margin: '0 5px', borderRadius: '1px', flexShrink: 0 },
149
+ stepTypeLabel: { display: 'flex', alignItems: 'center', gap: '6px', maxWidth: '260px' },
150
+ stepTypeLabelText: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
151
+ // Fase 1 - Selezione tipo documento
152
+ selectContainer: { display: 'flex', flexDirection: 'column', alignItems: 'center', flex: 1, gap: '8px', overflow: 'auto', padding: '8px 16px 24px' },
153
+ selectTitle: { fontSize: '18px', fontWeight: 700, color: TMColors.text_normal, textAlign: 'center', marginBottom: '16px' },
154
+ selectList: { display: 'flex', flexDirection: 'column', gap: '8px', width: '100%', maxWidth: '640px' },
155
+ typeCard: { display: 'flex', alignItems: 'center', gap: '12px', padding: '10px 14px', borderRadius: '9px', cursor: 'pointer', boxShadow: '0 1px 3px rgba(0, 0, 0, 0.06)', transition: 'all 0.15s ease' },
156
+ typeCardIcon: { width: '34px', height: '34px', borderRadius: '9px', background: TMColors.primary_container, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 },
157
+ typeCardName: { flex: 1, fontSize: '14px', fontWeight: 500, color: TMColors.text_normal, whiteSpace: 'normal', wordBreak: 'break-word', lineHeight: 1.3 },
158
+ };
159
+ // Stili delle sezioni collassabili
160
+ export const accordionStyles = {
161
+ accordion: { display: 'flex', flexDirection: 'column', minHeight: 0, borderRadius: '8px', border: `1px solid ${TMColors.card_background}`, background: TMColors.default_background, overflow: 'hidden' },
162
+ accordionInPanel: { flex: 1 },
163
+ accordionHeader: { display: 'flex', alignItems: 'center', gap: '8px', padding: '4px 8px', cursor: 'pointer', userSelect: 'none', transition: 'background 0.15s ease', outline: 'none', flexShrink: 0, minHeight: '28px' },
164
+ accordionTitle: { flex: 1, fontSize: '12px', fontWeight: 600, color: TMColors.text_normal, display: 'flex', alignItems: 'center', gap: '6px', minWidth: 0 },
165
+ accordionBadge: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: TMColors.primary, fontSize: '13px', fontWeight: 700, flexShrink: 0 },
166
+ accordionListBody: { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', borderTop: `1px solid ${TMColors.card_background}` },
167
+ accordionListPlaceholder: { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center', gap: '10px', padding: '16px', color: TMColors.label_normal, fontSize: '13px' },
168
+ accordionExpandBtn: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '20px', height: '20px', padding: 0, borderRadius: '6px', border: 'none', background: 'transparent', color: TMColors.label_normal, cursor: 'pointer', flexShrink: 0, transition: 'background 0.15s ease, color 0.15s ease' },
169
+ };
170
+ export const renderStepper = ({ steps, phase, hasMultipleTypes, isBackLocked, showBackArrow, onBack, onStepClick }) => (_jsxs("div", { style: multiDetailStyles.stepper, children: [showBackArrow && (_jsx(TMTooltip, { content: isBackLocked ? getBackLockedTitle() : SDKUI_Localizator.Back, parentStyle: { marginRight: '10px' }, children: _jsx("div", { onClick: isBackLocked ? undefined : onBack, style: {
171
+ ...multiDetailStyles.backArrow,
172
+ marginRight: 0,
173
+ ...(isBackLocked ? { borderColor: TMColors.disabled, color: TMColors.disabled, background: TMColors.card_background, opacity: 0.55, cursor: 'default' } : {}),
174
+ }, ...(isBackLocked ? {} : hoverStyle({ background: TMColors.primary, color: '#ffffff', transform: 'translateX(-2px)' }, { background: 'transparent', color: TMColors.primary, transform: 'translateX(0)' })), children: _jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", style: { display: 'block' }, children: _jsx("path", { d: "M15 5l-7 7 7 7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })), steps.map((step, index) => {
175
+ const isActive = step.phase === phase;
176
+ const isCompleted = step.phase < phase;
177
+ // Completato: verde con spunta. Corrente: blu. Da fare: grigio disabilitato
178
+ const accentColor = isCompleted ? TMColors.success : (isActive ? TMColors.primary : TMColors.disabled);
179
+ // Step completato cliccabile per tornare indietro, salvo il lock per righe presenti
180
+ const isClickable = isCompleted && !isBackLocked;
181
+ // Tooltip esplicativo solo sullo step completato quando il ritorno è bloccato dalle righe
182
+ const showLockTooltip = isCompleted && isBackLocked;
183
+ const stepPill = (_jsxs("div", { onClick: isClickable ? () => onStepClick(step.phase) : undefined, style: {
184
+ ...multiDetailStyles.stepPill,
185
+ background: isActive ? TMColors.primary_container : 'transparent',
186
+ boxShadow: isActive ? `inset 0 0 0 1.5px ${TMColors.primary}` : 'none',
187
+ cursor: isClickable ? 'pointer' : 'default',
188
+ ...(showLockTooltip ? { opacity: 0.5, filter: 'grayscale(0.6)' } : {}),
189
+ }, ...(isClickable && !isActive
190
+ ? hoverStyle({ background: TMColors.primary_container, transform: 'translateY(-1px)' }, { background: 'transparent', transform: 'translateY(0)' })
191
+ : {}), children: [hasMultipleTypes && (_jsx("div", { style: { ...multiDetailStyles.stepCircle, background: accentColor, boxShadow: isActive ? `0 2px 6px ${TMColors.primary}55` : 'none' }, children: isCompleted
192
+ ? (_jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", style: { display: 'block' }, children: _jsx("path", { d: "M5 13l4 4L19 7", stroke: "#ffffff", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) }))
193
+ : step.phase + 1 })), _jsx("span", { style: { ...multiDetailStyles.stepLabel, fontWeight: isActive ? 700 : 500, color: accentColor }, children: step.label })] }));
194
+ return (_jsxs(React.Fragment, { children: [showLockTooltip
195
+ ? _jsx(TMTooltip, { content: getBackLockedTitle(), children: stepPill })
196
+ : stepPill, index < steps.length - 1 && (_jsx("div", { style: { ...multiDetailStyles.stepConnector, background: isCompleted ? TMColors.success : TMColors.disabled } }))] }, step.phase));
197
+ })] }));
198
+ export const renderSelectDcmtType = ({ detailRelationItems, selectedRelation, onSelectRelation }) => (_jsxs("div", { style: multiDetailStyles.selectContainer, children: [_jsx("div", { style: multiDetailStyles.selectTitle, children: SDKUI_Localizator.SelectDcmtTypeForDetailArchiving }), _jsx("div", { style: multiDetailStyles.selectList, children: detailRelationItems.map((item) => {
199
+ const isSelected = item.relation === selectedRelation;
200
+ return (_jsxs("div", { onClick: () => onSelectRelation(item), style: {
201
+ ...multiDetailStyles.typeCard,
202
+ border: `1.5px solid ${isSelected ? TMColors.primary : TMColors.card_background}`,
203
+ background: isSelected ? TMColors.primary_container : TMColors.default_background,
204
+ }, ...hoverStyle({ borderColor: TMColors.primary, boxShadow: '0 4px 12px rgba(37, 89, 165, 0.18)' }, { borderColor: isSelected ? TMColors.primary : TMColors.card_background, boxShadow: '0 1px 3px rgba(0, 0, 0, 0.06)' }), children: [_jsx("div", { style: multiDetailStyles.typeCardIcon, children: _jsx(TMDcmtTypeIcon, { dtd: item.detailDtd }) }), _jsx("span", { style: multiDetailStyles.typeCardName, children: getDtdDisplayName(item.detailDtd) }), _jsx(IconChevronRight, { fontSize: 18, color: TMColors.label_normal })] }, item.relation.id ?? item.relation.detailTID));
205
+ }) })] }));
206
+ export const renderAccordionHeader = ({ section, isOpen, tooltip, count, title, actions, panel, onToggleOpen }) => {
207
+ const restBackground = isOpen ? TMColors.toolbar_background : 'transparent';
208
+ // Un pannello ingrandito non si collassa: resterebbe la sola testata al posto di tutto il riepilogo.
209
+ // Dove i pannelli si vedono uno alla volta (mobile) il collasso è precluso per lo stesso motivo
210
+ const canToggle = panel.canCollapse && !panel.isMaximized;
211
+ // Con un pannello alla volta l'ingrandimento non ha nulla da nascondere
212
+ const canMaximize = panel.canMaximize;
213
+ const onToggle = () => onToggleOpen(section, !isOpen);
214
+ const expandTooltip = panel.isMaximized ? SDKUI_Localizator.Minimize : SDKUI_Localizator.ZoomIn;
215
+ return (_jsxs("div", { role: canToggle ? 'button' : undefined, tabIndex: canToggle ? 0 : undefined, "aria-expanded": canToggle ? isOpen : undefined, style: { ...accordionStyles.accordionHeader, background: restBackground, ...(canToggle ? {} : { cursor: 'default' }) }, onClick: canToggle ? onToggle : undefined, onDoubleClick: canMaximize ? () => panel.toggleMaximize() : undefined, onKeyDown: canToggle ? (e) => { if (e.key === 'Enter' || e.key === ' ') {
216
+ e.preventDefault();
217
+ onToggle();
218
+ } } : undefined, ...(canToggle ? hoverStyle({ background: TMColors.primary_container }, { background: restBackground }) : {}), children: [_jsx(TMTooltip, { content: canToggle ? tooltip : undefined, parentStyle: { flex: 1, minWidth: 0 }, childStyle: { maxWidth: '100%' }, children: _jsxs("span", { style: accordionStyles.accordionTitle, children: [title, _jsx("span", { style: accordionStyles.accordionBadge, children: count })] }) }), actions, canMaximize && (_jsx(TMTooltip, { content: expandTooltip, children: _jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); panel.toggleMaximize(); }, style: accordionStyles.accordionExpandBtn, "aria-label": expandTooltip, ...hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal }), children: panel.isMaximized ? _jsx(IconWindowMinimize, { fontSize: 14 }) : _jsx(IconWindowMaximize, { fontSize: 14 }) }) })), canToggle && (_jsx(IconChevronRight, { fontSize: 16, color: TMColors.label_normal, style: { transform: isOpen ? 'rotate(90deg)' : 'none', transition: 'transform 0.2s ease', flexShrink: 0 } }))] }));
219
+ };
220
+ /**
221
+ * Contenuto di un pannello: il contesto del panel manager è leggibile solo dentro l'albero del
222
+ * provider, quindi è qui che si prende lo stato di ingrandimento da passare alla sezione
223
+ */
224
+ export const TMSummarySectionSlot = ({ section, render, onBeforeMaximize }) => {
225
+ const { maximizedPanels, toggleMaximize } = useTMPanelManagerContext();
226
+ const isMaximized = maximizedPanels.includes(section);
227
+ // Su mobile il panel manager mostra un pannello alla volta: la sezione è già a tutto schermo, e
228
+ // ingrandirla nasconderebbe la barra con cui si passa da una sezione all'altra; chiuderla lascerebbe
229
+ // a schermo la sola testata, perché non ci sono altre sezioni a cui cedere lo spazio
230
+ const isMobile = useDeviceType() === DeviceType.MOBILE;
231
+ // Una sezione collassata non ha corpo: ingrandirla mostrerebbe un pannello vuoto, senza
232
+ // nemmeno poterla riaprire (da ingrandita il collasso è bloccato), quindi la si apre prima
233
+ const toggleMaximizeSection = () => {
234
+ if (!isMaximized)
235
+ onBeforeMaximize?.(section);
236
+ toggleMaximize(section);
237
+ };
238
+ return _jsx(_Fragment, { children: render({ isMaximized, canMaximize: !isMobile, canCollapse: !isMobile, toggleMaximize: toggleMaximizeSection }) });
239
+ };
240
+ /**
241
+ * Segnala che l'utente ha spostato un separatore: è l'unico caso in cui le altezze dei pannelli sono
242
+ * una sua scelta, quindi la sola da salvare. Le quote calcolate all'apertura del riepilogo o al
243
+ * collasso di una sezione non vanno scritte nel local storage.
244
+ * Vive dentro il provider perché il contesto è leggibile solo lì
245
+ */
246
+ export const TMSummaryResizeTracker = ({ onUserResize }) => {
247
+ const { isResizingActive } = useTMPanelManagerContext();
248
+ useEffect(() => {
249
+ if (isResizingActive)
250
+ onUserResize();
251
+ }, [isResizingActive]);
252
+ return null;
253
+ };
254
+ /**
255
+ * Tiene allineate le altezze dei pannelli al collasso delle sezioni: il panel manager non
256
+ * conosce il collasso, quindi è qui che l'apertura di una sezione si traduce nelle nuove quote.
257
+ * Vive dentro il provider perché il contesto è leggibile solo lì
258
+ */
259
+ export const TMSectionPanelsHeights = ({ sections, sectionsOpen }) => {
260
+ const { panelDimensions, panelVisibility, setPanelDimensionsById, maximizedPanels } = useTMPanelManagerContext();
261
+ // Con un pannello ingrandito le quote sono quelle dell'ingrandimento (100% e 0%): riallinearle al
262
+ // collasso lo svuoterebbe, quindi si aspetta il ritorno alla vista normale per rifare i conti
263
+ const hasMaximizedPanel = maximizedPanels.length > 0;
264
+ // Le altezze correnti si leggono da un ref: fra le dipendenze rientrerebbero a ogni ridimensionamento,
265
+ // mentre qui interessa solo il collasso
266
+ const dimensionsRef = useRef(panelDimensions);
267
+ dimensionsRef.current = panelDimensions;
268
+ // Le prime altezze le imposta il provider da initialDimensions, già calcolate sul collasso corrente
269
+ const isFirstRun = useRef(true);
270
+ useEffect(() => {
271
+ if (isFirstRun.current) {
272
+ isFirstRun.current = false;
273
+ return;
274
+ }
275
+ if (hasMaximizedPanel)
276
+ return;
277
+ // Solo i pannelli a schermo si spartiscono l'altezza, altrimenti resterebbe vuoto lo spazio dei nascosti
278
+ const visibleSections = sections.filter(section => panelVisibility[section] !== false);
279
+ const heights = sectionPanelHeights(visibleSections, sectionsOpen, dimensionsRef.current);
280
+ visibleSections.forEach(section => setPanelDimensionsById(section, heights[section].width, heights[section].height));
281
+ }, [sectionsOpen, hasMaximizedPanel, panelVisibility]);
282
+ return null;
283
+ };