@topconsultnpm/sdkui-react 6.22.0-dev2.2 → 6.22.0-dev2.21
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.
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
- package/lib/components/base/TMButton.d.ts +13 -0
- package/lib/components/base/TMButton.js +137 -5
- package/lib/components/base/TMDataGrid.d.ts +2 -0
- package/lib/components/base/TMDataGrid.js +68 -4
- package/lib/components/base/TMEditorBase.d.ts +2 -0
- package/lib/components/base/TMModal.d.ts +4 -0
- package/lib/components/base/TMModal.js +42 -11
- package/lib/components/base/TMPanel.d.ts +1 -0
- package/lib/components/base/TMPanel.js +79 -20
- package/lib/components/base/TMTooltip.d.ts +2 -1
- package/lib/components/base/TMTooltip.js +23 -6
- package/lib/components/base/TMTreeView.js +108 -67
- package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
- package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
- package/lib/components/choosers/TMDistinctValues.js +149 -142
- package/lib/components/choosers/TMMetadataChooser.js +1 -1
- package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
- package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
- package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
- package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
- package/lib/components/editors/TMDropDown.d.ts +3 -0
- package/lib/components/editors/TMDropDown.js +197 -5
- package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
- package/lib/components/editors/TMMetadataEditor.js +8 -3
- package/lib/components/editors/TMTextBox.js +15 -15
- package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
- package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
- package/lib/components/features/documents/TMDcmtForm.js +104 -42
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
- package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
- package/lib/components/features/documents/TMMasterInfoFields.js +172 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +277 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +354 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +234 -0
- package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
- package/lib/components/features/documents/TMRelationViewer.js +69 -20
- package/lib/components/features/search/TMSearch.js +29 -16
- package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
- package/lib/components/features/search/TMSearchQueryPanel.js +28 -7
- package/lib/components/features/search/TMSearchResult.d.ts +9 -0
- package/lib/components/features/search/TMSearchResult.js +323 -92
- package/lib/components/features/search/TMTreeSelector.js +18 -5
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
- package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
- package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
- package/lib/components/layout/panelManager/types.d.ts +3 -0
- package/lib/components/viewers/TMMidViewer.js +4 -1
- package/lib/components/viewers/TMTidViewer.js +25 -11
- package/lib/helper/SDKUI_Globals.d.ts +55 -0
- package/lib/helper/SDKUI_Globals.js +82 -0
- package/lib/helper/SDKUI_Localizator.d.ts +24 -0
- package/lib/helper/SDKUI_Localizator.js +240 -0
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +3 -0
- package/lib/helper/dataGridSearchHelper.d.ts +28 -0
- package/lib/helper/dataGridSearchHelper.js +51 -0
- package/lib/helper/dcmtsHelper.d.ts +20 -0
- package/lib/helper/dcmtsHelper.js +58 -1
- package/lib/helper/helpers.d.ts +20 -0
- package/lib/helper/helpers.js +40 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.d.ts +2 -0
- package/lib/helper/queryHelper.js +3 -1
- package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
- package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
- package/lib/hooks/useArchiveListForm.d.ts +39 -0
- package/lib/hooks/useArchiveListForm.js +46 -0
- package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
- package/lib/hooks/useCaseFlowApprove.js +157 -0
- package/lib/hooks/useDataUserIdItem.d.ts +1 -0
- package/lib/hooks/useDataUserIdItem.js +1 -0
- package/lib/hooks/useDocumentOperations.d.ts +2 -0
- package/lib/hooks/useDocumentOperations.js +28 -5
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +98 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +567 -0
- package/lib/hooks/useRelatedDocuments.js +2 -26
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
- package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
- package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
- package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
- package/lib/hooks/useTMDistinctValuesSource.js +105 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
- package/lib/services/caseflow/CaseFlowService.js +693 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
- package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
- package/lib/services/caseflow/caseFlowNameCache.js +116 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/index.js +4 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +66 -66
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { ResultTypes, SavedQueryCacheService, SDK_Localizator, ValidationItem } from '@topconsultnpm/sdk-ts';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { calcIsModified, calcResponsiveSizes, DEFAULT_QUICK_SEARCH_MAX_DCMTS, IconClear, IconDelete, IconMetadata, SDKUI_Localizator, SYS_ALL_DCMTS_SQD_ID } from '../../helper';
|
|
6
|
+
import { FormModes } from '../../ts';
|
|
7
|
+
import ShowAlert from '../base/TMAlert';
|
|
8
|
+
import TMButton from '../base/TMButton';
|
|
9
|
+
import { useDeviceType } from '../base/TMDeviceProvider';
|
|
10
|
+
import { TMExceptionBoxManager } from '../base/TMPopUp';
|
|
11
|
+
import TMSpinner from '../base/TMSpinner';
|
|
12
|
+
import TMDropDown from '../editors/TMDropDown';
|
|
13
|
+
import TMTextBox from '../editors/TMTextBox';
|
|
14
|
+
import TMSaveForm from '../forms/TMSaveForm';
|
|
15
|
+
import TMDcmtTypeChooser from './TMDcmtTypeChooser';
|
|
16
|
+
import TMMetadataChooser from './TMMetadataChooser';
|
|
17
|
+
import { useTMQuickSearchInfo } from './TMQuickSearchInfo';
|
|
18
|
+
const StyledFormContent = styled.div `display: flex; flex-direction: column; gap: 12px; padding: 10px 15px;`;
|
|
19
|
+
/** Impostazione vuota da cui parte la configurazione: solo il numero massimo di documenti ha un valore predefinito */
|
|
20
|
+
export const newQuickSearchSettings = () => ({
|
|
21
|
+
tid: undefined,
|
|
22
|
+
sqdId: undefined,
|
|
23
|
+
mid: undefined,
|
|
24
|
+
maxDcmtsToBeReturned: DEFAULT_QUICK_SEARCH_MAX_DCMTS,
|
|
25
|
+
});
|
|
26
|
+
const TMQuickSearchSettingsForm = ({ settings, onSave, onDelete, onClose }) => {
|
|
27
|
+
const initialSettings = useMemo(() => settings ?? newQuickSearchSettings(), [settings]);
|
|
28
|
+
const deviceType = useDeviceType();
|
|
29
|
+
const [formData, setFormData] = useState(initialSettings);
|
|
30
|
+
// Finché è undefined la cache delle ricerche rapide non è ancora stata caricata
|
|
31
|
+
const [allSQDs, setAllSQDs] = useState();
|
|
32
|
+
// Icona informativa della ricerca rapida scelta: sta fra i pulsanti del suo editor, il dettaglio si apre a parte
|
|
33
|
+
const { infoButton, detailForm } = useTMQuickSearchInfo({ tid: formData.tid, sqdId: formData.sqdId });
|
|
34
|
+
useEffect(() => { loadSQDsAsync(); }, []);
|
|
35
|
+
// L'avviso ha senso solo a cache caricata: prima le ricerche rapide risulterebbero assenti per tutti i tipi documento
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!formData.tid || !allSQDs)
|
|
38
|
+
return;
|
|
39
|
+
if (allSQDs.some(o => o.masterTID === formData.tid))
|
|
40
|
+
return;
|
|
41
|
+
ShowAlert({ mode: 'warning', title: SDK_Localizator.SavedQuery, message: 'Non ci sono ricerche rapide per il tipo documento selezionato', duration: 4000 });
|
|
42
|
+
}, [formData.tid, allSQDs]);
|
|
43
|
+
const loadSQDsAsync = async () => {
|
|
44
|
+
try {
|
|
45
|
+
TMSpinner.show({ description: `${SDKUI_Localizator.Loading} - ${SDK_Localizator.SavedQuery} ...` });
|
|
46
|
+
setAllSQDs(await SavedQueryCacheService.GetAllAsync() ?? []);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
TMExceptionBoxManager.show({ exception: e });
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
TMSpinner.hide();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
// Stessa logica di TMSearch: in testa all'elenco c'è la ricerca di sistema con tutti i documenti del tipo documento
|
|
56
|
+
const sqdsDataSource = useMemo(() => {
|
|
57
|
+
if (!formData.tid)
|
|
58
|
+
return [];
|
|
59
|
+
return [
|
|
60
|
+
{ value: SYS_ALL_DCMTS_SQD_ID, display: SDKUI_Localizator.AllDcmts },
|
|
61
|
+
...(allSQDs ?? []).filter(o => o.masterTID === formData.tid).map(o => ({ value: o.id, display: o.name ?? `ID ${o.id}` })),
|
|
62
|
+
];
|
|
63
|
+
}, [allSQDs, formData.tid]);
|
|
64
|
+
const validationItems = useMemo(() => {
|
|
65
|
+
const vil = [];
|
|
66
|
+
if (!formData.tid)
|
|
67
|
+
vil.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.DcmtType, SDKUI_Localizator.RequiredField));
|
|
68
|
+
if (!formData.sqdId)
|
|
69
|
+
vil.push(new ValidationItem(ResultTypes.ERROR, SDK_Localizator.SavedQuery, SDKUI_Localizator.RequiredField));
|
|
70
|
+
if (!formData.mid)
|
|
71
|
+
vil.push(new ValidationItem(ResultTypes.ERROR, SDK_Localizator.Metadata, SDKUI_Localizator.RequiredField));
|
|
72
|
+
if (!formData.maxDcmtsToBeReturned || formData.maxDcmtsToBeReturned <= 0)
|
|
73
|
+
vil.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.MaxDcmtsToBeReturned, SDKUI_Localizator.RequiredField));
|
|
74
|
+
return vil;
|
|
75
|
+
}, [formData]);
|
|
76
|
+
// Ogni editor mostra solo gli errori del proprio campo
|
|
77
|
+
const validationItemsOf = (propertyName) => validationItems.filter(o => o.PropertyName === propertyName);
|
|
78
|
+
const patchFormData = (changes) => setFormData(prev => ({ ...prev, ...changes }));
|
|
79
|
+
/** Cambiare tipo documento rende privi di senso la ricerca rapida e il metadato scelti prima */
|
|
80
|
+
const onTidChanged = (tids) => {
|
|
81
|
+
const tid = tids?.[0];
|
|
82
|
+
if (tid === formData.tid)
|
|
83
|
+
return;
|
|
84
|
+
patchFormData({ tid, sqdId: undefined, mid: undefined });
|
|
85
|
+
};
|
|
86
|
+
const onSaveAsync = async () => { onSave?.(formData); };
|
|
87
|
+
/* Ricomincia da capo la configurazione: cambiare tipo documento azzera già ricerca rapida e metadato, ma non
|
|
88
|
+
permette di tornare alla scelta vuota, da cui l'elenco delle ricerche rapide riparte senza filtri */
|
|
89
|
+
const isFormDataEmpty = !formData.tid && !formData.sqdId && !formData.mid && formData.maxDcmtsToBeReturned === DEFAULT_QUICK_SEARCH_MAX_DCMTS;
|
|
90
|
+
const onClearClick = () => setFormData(newQuickSearchSettings());
|
|
91
|
+
return (_jsx(TMSaveForm, { title: `${SDKUI_Localizator.DistinctValues} - ${SDK_Localizator.SavedQuery}`, showTitleFormMode: false, formMode: FormModes.Update, isModal: true, width: calcResponsiveSizes(deviceType, '600px', '90%', '95%'), height: calcResponsiveSizes(deviceType, '500px', '90%', '95%'), showBackButton: false, hasNavigation: false, skipIsModifiedCheck: true, validationItems: validationItems, isModified: calcIsModified(formData, initialSettings), customToolbarElements: _jsxs(_Fragment, { children: [_jsx(TMButton, { btnStyle: 'toolbar', caption: SDKUI_Localizator.Clear, icon: _jsx(IconClear, {}), disabled: isFormDataEmpty, onClick: onClearClick }), onDelete && settings &&
|
|
92
|
+
_jsx(TMButton, { btnStyle: 'toolbar', color: 'error', caption: SDKUI_Localizator.Delete, icon: _jsx(IconDelete, {}), onClick: onDelete })] }), onUndo: () => setFormData(initialSettings), onSaveAsync: onSaveAsync, onClose: onClose, children: _jsxs(StyledFormContent, { children: [_jsx(TMDcmtTypeChooser, { label: SDKUI_Localizator.DcmtType, placeHolder: SDKUI_Localizator.DcmtTypeSelect, accessFilter: 'canSearch', values: formData.tid ? [formData.tid] : [], isModifiedWhen: formData.tid !== initialSettings.tid, validationItems: validationItemsOf(SDKUI_Localizator.DcmtType), onValueChanged: onTidChanged }), _jsx(TMDropDown, { searchEnabled: true, usePortal: true, label: SDK_Localizator.SavedQuery, placeHolder: `<${SDKUI_Localizator.NoneSelection}>`, disabled: !formData.tid, value: formData.sqdId ?? '', dataSource: sqdsDataSource, buttons: infoButton ? [infoButton] : [], isModifiedWhen: formData.sqdId !== initialSettings.sqdId, validationItems: validationItemsOf(SDK_Localizator.SavedQuery), onValueChanged: (e) => patchFormData({ sqdId: Number(e.target.value) }) }), _jsx(TMMetadataChooser, { icon: _jsx(IconMetadata, {}), label: SDK_Localizator.Metadata, placeHolder: SDKUI_Localizator.SelectMetadata, disabled: !formData.tid || !formData.sqdId, allowSysMetadata: false, tids: formData.tid ? [formData.tid] : [], values: formData.mid ? [{ tid: formData.tid, mid: formData.mid }] : [], isModifiedWhen: formData.mid !== initialSettings.mid, validationItems: validationItemsOf(SDK_Localizator.Metadata), onValueChanged: (values) => patchFormData({ mid: values?.[0]?.mid }) }), _jsx(TMTextBox, { type: 'number', label: SDKUI_Localizator.MaxDcmtsToBeReturned, minValue: 1, value: formData.maxDcmtsToBeReturned, isModifiedWhen: formData.maxDcmtsToBeReturned !== initialSettings.maxDcmtsToBeReturned, validationItems: validationItemsOf(SDKUI_Localizator.MaxDcmtsToBeReturned), onValueChanged: (e) => patchFormData({ maxDcmtsToBeReturned: Number(e.target.value) || DEFAULT_QUICK_SEARCH_MAX_DCMTS }) }), detailForm] }) }));
|
|
93
|
+
};
|
|
94
|
+
export default TMQuickSearchSettingsForm;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/** Valore selezionato: value è quello che finisce nel campo del form, display quello mostrato all'utente */
|
|
3
|
+
export type SelectedValueItem = {
|
|
4
|
+
value: string;
|
|
5
|
+
display: string;
|
|
6
|
+
};
|
|
7
|
+
interface ITMSelectedValuesSummary {
|
|
8
|
+
items: Array<SelectedValueItem>;
|
|
9
|
+
onRemove: (value: string) => void;
|
|
10
|
+
onClear: () => void;
|
|
11
|
+
}
|
|
12
|
+
declare const TMSelectedValuesSummary: React.FC<ITMSelectedValuesSummary>;
|
|
13
|
+
export default TMSelectedValuesSummary;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { IconCloseOutline, IconEraser, IconSelected, SDKUI_Localizator } from '../../helper';
|
|
4
|
+
import TMButton from '../base/TMButton';
|
|
5
|
+
import TMTooltip from '../base/TMTooltip';
|
|
6
|
+
import { TMColors } from '../../utils/theme';
|
|
7
|
+
/* Riepilogo dei valori selezionati: sta sopra la griglia perché è il contenuto che finirà nel campo del form.
|
|
8
|
+
Titolo, chip e pulsante di pulizia stanno su un'unica riga per rubare il minor spazio possibile alla griglia */
|
|
9
|
+
const StyledSelectionSummary = styled.div `
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: row;
|
|
12
|
+
/* Titolo, chip e azioni centrati fra loro: con una sola riga di chip la barra non risulta schiacciata in alto */
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: 8px;
|
|
15
|
+
padding: 8px;
|
|
16
|
+
background-color: ${TMColors.primary_container};
|
|
17
|
+
border: 1px solid ${TMColors.primary};
|
|
18
|
+
border-radius: 6px;
|
|
19
|
+
`;
|
|
20
|
+
const StyledSelectionTitle = styled.div `
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: row;
|
|
23
|
+
align-items: center;
|
|
24
|
+
flex-shrink: 0;
|
|
25
|
+
gap: 4px;
|
|
26
|
+
min-height: 28px;
|
|
27
|
+
font-size: 0.8rem;
|
|
28
|
+
font-weight: 600;
|
|
29
|
+
text-transform: uppercase;
|
|
30
|
+
letter-spacing: 0.3px;
|
|
31
|
+
white-space: nowrap;
|
|
32
|
+
color: ${TMColors.primary};
|
|
33
|
+
|
|
34
|
+
svg { flex-shrink: 0; }
|
|
35
|
+
`;
|
|
36
|
+
const StyledSelectionCounter = styled.span `
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
min-width: 20px;
|
|
41
|
+
height: 20px;
|
|
42
|
+
padding: 0 5px;
|
|
43
|
+
border-radius: 10px;
|
|
44
|
+
background-color: ${TMColors.primary};
|
|
45
|
+
color: white;
|
|
46
|
+
font-size: 0.75rem;
|
|
47
|
+
font-weight: 600;
|
|
48
|
+
`;
|
|
49
|
+
/* max-height: due righe di chip (28px) più il gap: oltre si scorre invece di rubare spazio alla griglia */
|
|
50
|
+
const StyledSelectionChips = styled.div `display: flex; flex: 1; flex-direction: row; flex-wrap: wrap; align-content: flex-start; gap: 4px; max-height: 60px; overflow-y: auto;`;
|
|
51
|
+
const StyledChip = styled.div `
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
flex-direction: row;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: 4px;
|
|
56
|
+
max-width: 320px;
|
|
57
|
+
height: 28px;
|
|
58
|
+
padding: 0 4px 0 10px;
|
|
59
|
+
border: 1px solid ${TMColors.primary};
|
|
60
|
+
border-radius: 14px;
|
|
61
|
+
background-color: ${TMColors.default_background};
|
|
62
|
+
color: ${TMColors.text_normal};
|
|
63
|
+
font-size: 0.875rem;
|
|
64
|
+
`;
|
|
65
|
+
const StyledChipLabel = styled.span `overflow: hidden; text-overflow: ellipsis; white-space: nowrap;`;
|
|
66
|
+
const StyledChipRemove = styled.button `
|
|
67
|
+
display: inline-flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
justify-content: center;
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
width: 20px;
|
|
72
|
+
height: 20px;
|
|
73
|
+
padding: 0;
|
|
74
|
+
border: none;
|
|
75
|
+
border-radius: 50%;
|
|
76
|
+
background-color: transparent;
|
|
77
|
+
color: ${TMColors.button_icon};
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
|
|
80
|
+
&:hover { background-color: ${TMColors.primary}; color: white; }
|
|
81
|
+
`;
|
|
82
|
+
/* Il pulsante di pulizia non deve scorrere con i chip né stringersi */
|
|
83
|
+
const StyledSelectionActions = styled.div `display: flex; flex-shrink: 0; align-items: center; min-height: 28px;`;
|
|
84
|
+
const TMSelectedValuesSummary = ({ items, onRemove, onClear }) => {
|
|
85
|
+
if (items.length <= 0)
|
|
86
|
+
return null;
|
|
87
|
+
return (_jsxs(StyledSelectionSummary, { children: [_jsxs(StyledSelectionTitle, { children: [_jsx(IconSelected, { fontSize: 17, color: TMColors.primary }), _jsx("span", { children: SDKUI_Localizator.Selected }), _jsx(StyledSelectionCounter, { children: items.length })] }), _jsx(StyledSelectionChips, { children: items.map((item) =>
|
|
88
|
+
/* Il valore lungo viene troncato nel chip: il tooltip è l'unico modo per leggerlo per intero */
|
|
89
|
+
_jsx(TMTooltip, { content: item.display, children: _jsxs(StyledChip, { children: [_jsx(StyledChipLabel, { children: item.display }), _jsx(StyledChipRemove, { type: 'button', title: SDKUI_Localizator.Remove, onClick: () => onRemove(item.value), children: _jsx(IconCloseOutline, { fontSize: 13 }) })] }) }, item.value)) }), _jsx(StyledSelectionActions, { children: _jsx(TMButton, { btnStyle: 'toolbar', caption: SDKUI_Localizator.Clear, icon: _jsx(IconEraser, {}), onClick: onClear }) })] }));
|
|
90
|
+
};
|
|
91
|
+
export default TMSelectedValuesSummary;
|
|
@@ -4,6 +4,9 @@ interface ITMDropDown extends ITMEditorBase {
|
|
|
4
4
|
dataSource?: any[];
|
|
5
5
|
value?: string | number | undefined;
|
|
6
6
|
disabled?: boolean;
|
|
7
|
+
searchEnabled?: boolean;
|
|
8
|
+
usePortal?: boolean;
|
|
9
|
+
itemRender?: (item: any) => React.ReactNode;
|
|
7
10
|
onValueChanged?: (e: React.ChangeEvent<HTMLSelectElement>) => void;
|
|
8
11
|
}
|
|
9
12
|
declare const TMDropDown: React.FC<ITMDropDown>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import styled, { css } from "styled-components";
|
|
4
5
|
import { FontSize, TMColors } from '../../utils/theme';
|
|
5
6
|
import { StyledEditorContainer, StyledEditorIcon, StyledEditorLabel, TMEditorsDefaultBorderRadius, editorColorManager } from "./TMEditorStyled";
|
|
6
7
|
import TMLayoutContainer, { TMLayoutItem } from "../base/TMLayout";
|
|
7
8
|
import TMVilViewer from "../base/TMVilViewer";
|
|
8
9
|
import TMTooltip from "../base/TMTooltip";
|
|
10
|
+
import { SDKUI_Localizator } from "../../helper";
|
|
9
11
|
const StyledDropDownWrapper = styled.div `
|
|
10
12
|
position: relative;
|
|
11
13
|
width: ${props => props.$width};
|
|
@@ -54,9 +56,199 @@ const StyledDropDownEditorButton = styled.div `
|
|
|
54
56
|
align-items: center;
|
|
55
57
|
cursor: pointer;
|
|
56
58
|
`;
|
|
57
|
-
|
|
59
|
+
// Variante filtrabile: un input di testo con la stessa resa grafica della select nativa
|
|
60
|
+
const StyledSearchInput = styled.input `
|
|
61
|
+
width: 100%;
|
|
62
|
+
padding: 5px 35px 3px 9px;
|
|
63
|
+
border: 1px solid;
|
|
64
|
+
border-color: ${props => props.$isModified ? TMColors.isModified : TMColors.border_normal};
|
|
65
|
+
border-bottom: ${props => (props.$vil.length > 0) ? '3px' : '1px'} solid;
|
|
66
|
+
border-bottom-color: ${props => (props.$vil.length === 0) ? !props.$isModified ? TMColors.border_normal : TMColors.isModified : editorColorManager(props.$vil)};
|
|
67
|
+
color: ${props => !props.$disabled ? (props.$vil.length === 0 ? (!props.$isModified ? TMColors.text_normal : TMColors.isModified) : editorColorManager(props.$vil)) : TMColors.disabled};
|
|
68
|
+
border-radius: ${props => props.$borderRadius ?? TMEditorsDefaultBorderRadius};
|
|
69
|
+
font-size: ${props => props.$fontSize};
|
|
70
|
+
background-color: ${props => props.$disabled ? '#F5F5F5' : 'white'};
|
|
71
|
+
text-overflow: ellipsis;
|
|
72
|
+
|
|
73
|
+
&:focus {
|
|
74
|
+
outline: none;
|
|
75
|
+
border: ${props => props.$isModified ? `1px solid ${TMColors.isModified}` : '1px solid rgb(180,180,180)'};
|
|
76
|
+
border-bottom: 2px solid;
|
|
77
|
+
border-bottom-color: ${props => (props.$vil.length === 0) ? !props.$isModified ? TMColors.primary : TMColors.isModified : editorColorManager(props.$vil)};
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
const OPTIONS_PANEL_MAX_HEIGHT = 220;
|
|
81
|
+
const OPTIONS_PANEL_MIN_HEIGHT = 80;
|
|
82
|
+
const OPTIONS_PANEL_MARGIN = 4;
|
|
83
|
+
// L'elenco aperto con usePortal esce dal contenitore dell'editor: deve stare sopra tutto ciò che può trovarsi nella stessa pagina
|
|
84
|
+
const DROPDOWN_OPTIONS_PANEL_Z_INDEX = 100001;
|
|
85
|
+
const optionsPanelCss = css `
|
|
86
|
+
overflow-y: auto;
|
|
87
|
+
background-color: white;
|
|
88
|
+
border: 1px solid ${TMColors.border_normal};
|
|
89
|
+
border-radius: ${TMEditorsDefaultBorderRadius};
|
|
90
|
+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
|
|
91
|
+
`;
|
|
92
|
+
// Elenco ancorato all'editor: resta nel flusso del DOM e quindi soggetto al clipping dei contenitori con overflow
|
|
93
|
+
const StyledOptionsPanel = styled.div `
|
|
94
|
+
${optionsPanelCss}
|
|
95
|
+
position: absolute;
|
|
96
|
+
top: calc(100% + 2px);
|
|
97
|
+
left: 0;
|
|
98
|
+
right: 0;
|
|
99
|
+
z-index: 10001;
|
|
100
|
+
max-height: ${OPTIONS_PANEL_MAX_HEIGHT}px;
|
|
101
|
+
`;
|
|
102
|
+
// Variante usePortal: posizione calcolata rispetto alla finestra, così l'elenco non viene tagliato da nessun contenitore
|
|
103
|
+
const StyledPortalOptionsPanel = styled.div `
|
|
104
|
+
${optionsPanelCss}
|
|
105
|
+
position: fixed;
|
|
106
|
+
left: ${props => props.$left}px;
|
|
107
|
+
${props => props.$top !== undefined ? `top: ${props.$top}px;` : ''}
|
|
108
|
+
${props => props.$bottom !== undefined ? `bottom: ${props.$bottom}px;` : ''}
|
|
109
|
+
width: ${props => props.$width}px;
|
|
110
|
+
z-index: ${DROPDOWN_OPTIONS_PANEL_Z_INDEX};
|
|
111
|
+
max-height: ${props => props.$maxHeight}px;
|
|
112
|
+
`;
|
|
113
|
+
const StyledOption = styled.div `
|
|
114
|
+
padding: 5px 9px;
|
|
115
|
+
font-size: ${props => props.$fontSize};
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
text-overflow: ellipsis;
|
|
120
|
+
font-weight: ${props => props.$isSelected ? 600 : 'normal'};
|
|
121
|
+
background-color: ${props => props.$isHighlighted ? TMColors.primary_container : 'transparent'};
|
|
122
|
+
`;
|
|
123
|
+
const StyledNoOptions = styled.div `
|
|
124
|
+
padding: 5px 9px;
|
|
125
|
+
font-size: ${props => props.$fontSize};
|
|
126
|
+
color: ${TMColors.disabled};
|
|
127
|
+
`;
|
|
128
|
+
// L'elenco si apre sotto l'editor se c'è spazio, altrimenti sopra, e in ogni caso non esce dalla finestra
|
|
129
|
+
const calcOptionsPanelPosition = (rect) => {
|
|
130
|
+
const spaceBelow = window.innerHeight - rect.bottom - OPTIONS_PANEL_MARGIN;
|
|
131
|
+
const spaceAbove = rect.top - OPTIONS_PANEL_MARGIN;
|
|
132
|
+
const openUpwards = spaceBelow < OPTIONS_PANEL_MAX_HEIGHT && spaceAbove > spaceBelow;
|
|
133
|
+
const availableHeight = openUpwards ? spaceAbove : spaceBelow;
|
|
134
|
+
return {
|
|
135
|
+
left: Math.max(OPTIONS_PANEL_MARGIN, Math.min(rect.left, window.innerWidth - rect.width - OPTIONS_PANEL_MARGIN)),
|
|
136
|
+
top: openUpwards ? undefined : rect.bottom + OPTIONS_PANEL_MARGIN,
|
|
137
|
+
bottom: openUpwards ? window.innerHeight - rect.top + OPTIONS_PANEL_MARGIN : undefined,
|
|
138
|
+
width: rect.width,
|
|
139
|
+
maxHeight: Math.max(OPTIONS_PANEL_MIN_HEIGHT, Math.min(OPTIONS_PANEL_MAX_HEIGHT, availableHeight)),
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
const TMDropDown = ({ validationItems = [], disabled = false, elementStyle, isModifiedWhen, labelPosition = 'left', label, icon, width = '100%', fontSize = FontSize.defaultFontSize, dataSource, value, searchEnabled = false, usePortal = false, itemRender, onValueChanged, buttons = [], placeHolder, }) => {
|
|
58
143
|
const [isFocused, setIsFocused] = useState(false);
|
|
59
|
-
const
|
|
144
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
145
|
+
const [searchText, setSearchText] = useState('');
|
|
146
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
147
|
+
const [optionsPanelPosition, setOptionsPanelPosition] = useState();
|
|
148
|
+
const optionsPanelRef = useRef(null);
|
|
149
|
+
const dropDownWrapperRef = useRef(null);
|
|
150
|
+
const selectedItem = dataSource?.find(o => String(o.value) === String(value));
|
|
151
|
+
// A elenco chiuso l'input mostra la voce selezionata, ad elenco aperto il testo digitato
|
|
152
|
+
const filteredDataSource = (!isOpen || !searchText) ? (dataSource ?? []) : (dataSource ?? []).filter(o => String(o.display ?? '').toLocaleLowerCase().includes(searchText.toLocaleLowerCase()));
|
|
153
|
+
// Mantiene visibile la voce evidenziata durante la navigazione da tastiera
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
if (!isOpen || highlightedIndex < 0)
|
|
156
|
+
return;
|
|
157
|
+
const option = optionsPanelRef.current?.children[highlightedIndex];
|
|
158
|
+
option?.scrollIntoView({ block: 'nearest' });
|
|
159
|
+
}, [isOpen, highlightedIndex]);
|
|
160
|
+
// Solo nel portal la posizione va ricalcolata: essendo fissa non segue lo scorrimento o il ridimensionamento del contenitore
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (!isOpen || !usePortal)
|
|
163
|
+
return;
|
|
164
|
+
const updatePosition = () => updateOptionsPanelPosition();
|
|
165
|
+
window.addEventListener('scroll', updatePosition, true);
|
|
166
|
+
window.addEventListener('resize', updatePosition);
|
|
167
|
+
return () => {
|
|
168
|
+
window.removeEventListener('scroll', updatePosition, true);
|
|
169
|
+
window.removeEventListener('resize', updatePosition);
|
|
170
|
+
};
|
|
171
|
+
}, [isOpen, usePortal]);
|
|
172
|
+
const updateOptionsPanelPosition = () => {
|
|
173
|
+
const rect = dropDownWrapperRef.current?.getBoundingClientRect();
|
|
174
|
+
if (rect)
|
|
175
|
+
setOptionsPanelPosition(calcOptionsPanelPosition(rect));
|
|
176
|
+
};
|
|
177
|
+
const openOptions = () => {
|
|
178
|
+
if (disabled)
|
|
179
|
+
return;
|
|
180
|
+
if (usePortal)
|
|
181
|
+
updateOptionsPanelPosition();
|
|
182
|
+
setIsOpen(true);
|
|
183
|
+
setSearchText('');
|
|
184
|
+
setHighlightedIndex(dataSource?.findIndex(o => String(o.value) === String(value)) ?? -1);
|
|
185
|
+
};
|
|
186
|
+
const closeOptions = () => {
|
|
187
|
+
setIsOpen(false);
|
|
188
|
+
setSearchText('');
|
|
189
|
+
setHighlightedIndex(-1);
|
|
190
|
+
};
|
|
191
|
+
const selectItem = (item) => {
|
|
192
|
+
closeOptions();
|
|
193
|
+
// La select nativa notifica un ChangeEvent: qui viene ricostruito il solo dato letto dai consumatori (target.value)
|
|
194
|
+
onValueChanged?.({ target: { value: String(item.value) } });
|
|
195
|
+
};
|
|
196
|
+
const onSearchKeyDown = (e) => {
|
|
197
|
+
switch (e.key) {
|
|
198
|
+
case 'ArrowDown':
|
|
199
|
+
case 'ArrowUp':
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
if (!isOpen) {
|
|
202
|
+
openOptions();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (filteredDataSource.length <= 0)
|
|
206
|
+
return;
|
|
207
|
+
setHighlightedIndex(prev => {
|
|
208
|
+
const delta = e.key === 'ArrowDown' ? 1 : -1;
|
|
209
|
+
const next = prev + delta;
|
|
210
|
+
if (next < 0)
|
|
211
|
+
return filteredDataSource.length - 1;
|
|
212
|
+
if (next >= filteredDataSource.length)
|
|
213
|
+
return 0;
|
|
214
|
+
return next;
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
case 'Enter': {
|
|
218
|
+
if (!isOpen)
|
|
219
|
+
return;
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
const item = filteredDataSource[highlightedIndex] ?? filteredDataSource[0];
|
|
222
|
+
if (item)
|
|
223
|
+
selectItem(item);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case 'Escape':
|
|
227
|
+
if (isOpen) {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
closeOptions();
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const renderOptions = () => (filteredDataSource.length > 0
|
|
235
|
+
? filteredDataSource.map((data, index) => (_jsx(StyledOption, { "$fontSize": fontSize, "$isSelected": String(data.value) === String(value), "$isHighlighted": index === highlightedIndex, onMouseEnter: () => setHighlightedIndex(index), onClick: () => selectItem(data), children: itemRender ? itemRender(data) : data.display }, index)))
|
|
236
|
+
: _jsx(StyledNoOptions, { "$fontSize": fontSize, children: SDKUI_Localizator.NoDataToDisplay }));
|
|
237
|
+
// onMouseDown va bloccato: senza preventDefault il blur dell'input chiuderebbe l'elenco prima del click
|
|
238
|
+
const renderOptionsPanel = () => {
|
|
239
|
+
if (!isOpen)
|
|
240
|
+
return null;
|
|
241
|
+
if (!usePortal) {
|
|
242
|
+
return (_jsx(StyledOptionsPanel, { ref: optionsPanelRef, onMouseDown: (e) => e.preventDefault(), children: renderOptions() }));
|
|
243
|
+
}
|
|
244
|
+
if (!optionsPanelPosition)
|
|
245
|
+
return null;
|
|
246
|
+
return createPortal(_jsx(StyledPortalOptionsPanel, { ref: optionsPanelRef, "$left": optionsPanelPosition.left, "$top": optionsPanelPosition.top, "$bottom": optionsPanelPosition.bottom, "$width": optionsPanelPosition.width, "$maxHeight": optionsPanelPosition.maxHeight, onMouseDown: (e) => e.preventDefault(), children: renderOptions() }), document.body);
|
|
247
|
+
};
|
|
248
|
+
const renderSearchableDropDown = () => (_jsxs(_Fragment, { children: [_jsx(StyledSearchInput, { value: isOpen ? searchText : (selectedItem?.display ?? ''), placeholder: isOpen ? SDKUI_Localizator.Search : placeHolder, readOnly: disabled, disabled: disabled, "$disabled": disabled, "$fontSize": fontSize, "$isModified": isModifiedWhen, "$vil": validationItems, onFocus: () => { setIsFocused(true); openOptions(); }, onBlur: () => { setIsFocused(false); closeOptions(); }, onClick: () => { if (!isOpen)
|
|
249
|
+
openOptions(); }, onChange: (e) => { setSearchText(e.target.value); setIsOpen(true); setHighlightedIndex(0); }, onKeyDown: onSearchKeyDown }), renderOptionsPanel()] }));
|
|
250
|
+
const renderNativeDropDown = () => (_jsxs(StyledDropDown, { value: value, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), "$disabled": disabled, disabled: disabled, "$fontSize": fontSize, "$isModified": isModifiedWhen, "$vil": validationItems, onChange: onValueChanged, children: [placeHolder && _jsx("option", { value: "", disabled: true, hidden: true, children: placeHolder }), dataSource?.map((data, index) => (_jsx("option", { value: data.value, children: data.display }, index)))] }));
|
|
251
|
+
const renderedLeftLabelTextBox = () => (_jsxs(TMLayoutContainer, { direction: 'horizontal', children: [icon && _jsx(TMLayoutItem, { width: '20px', children: _jsx(StyledEditorIcon, { "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, children: icon }) }), _jsx(TMLayoutItem, { children: _jsxs(StyledEditorContainer, { "$width": width, children: [label && _jsx(StyledEditorLabel, { "$isFocused": isFocused, "$labelPosition": labelPosition, "$disabled": disabled, children: label }), _jsxs(StyledDropDownWrapper, { ref: dropDownWrapperRef, "$width": width, children: [searchEnabled ? renderSearchableDropDown() : renderNativeDropDown(), buttons.length > 0 && (_jsx(ButtonsContainer, { children: buttons.map((buttonItem) => (_jsx(StyledDropDownEditorButton, { onClick: buttonItem.onClick, children: _jsx(TMTooltip, { content: buttonItem.tooltipContent ?? buttonItem.text, children: buttonItem.icon }) }, buttonItem.text))) })), _jsx(CustomArrow, {})] }), _jsx(TMVilViewer, { vil: validationItems })] }) })] }));
|
|
60
252
|
return _jsx("div", { style: elementStyle, children: renderedLeftLabelTextBox() });
|
|
61
253
|
};
|
|
62
254
|
export default TMDropDown;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ValidationItem, LayoutModes, QueryOperators } from '@topconsultnpm/sdk-ts';
|
|
2
|
+
import { MetadataDescriptor, ValidationItem, LayoutModes, QueryOperators } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
import { DynDataListsToBeRefreshed, MIDsToBeUpdated } from '../choosers/TMDynDataListItemChooser';
|
|
4
4
|
interface ITMMetadataEditorProps {
|
|
5
5
|
tid: number | undefined;
|
|
6
6
|
mid?: number;
|
|
7
|
+
/** MetadataDescriptor esplicito. Se valorizzato, l'editor lo usa direttamente
|
|
8
|
+
* e salta la risoluzione interna via tid/mid (utile per metadati non legati
|
|
9
|
+
* a un tipo documento, es. copertina CaseFlow). */
|
|
10
|
+
md?: MetadataDescriptor;
|
|
7
11
|
value: string | undefined;
|
|
8
12
|
queryParamsDynDataList?: string[];
|
|
9
13
|
autoFocus?: boolean;
|
|
@@ -33,14 +33,19 @@ const getTextTransform = (format) => {
|
|
|
33
33
|
default: return undefined;
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
-
const TMMetadataEditor = ({ isSelected = false, customLabel, isReadOnly, isLexProt, layoutMode, queryOperator, isEditable, isModifiedWhen = false, tid, mid, value, queryParamsDynDataList, containerElement, autoFocus, validationItems = [], disabled = false, openChooserBySingleClick = true, onValueChanged, onValueChange, onCascadeRefreshDynDataLists, onCascadeUpdateMIDs, updateIsModalOpen }) => {
|
|
37
|
-
const [md, setMd] = useState();
|
|
36
|
+
const TMMetadataEditor = ({ isSelected = false, customLabel, isReadOnly, isLexProt, layoutMode, queryOperator, isEditable, isModifiedWhen = false, tid, mid, md: mdProp, value, queryParamsDynDataList, containerElement, autoFocus, validationItems = [], disabled = false, openChooserBySingleClick = true, onValueChanged, onValueChange, onCascadeRefreshDynDataLists, onCascadeUpdateMIDs, updateIsModalOpen }) => {
|
|
37
|
+
const [md, setMd] = useState(mdProp);
|
|
38
38
|
useEffect(() => {
|
|
39
|
+
// Se il descriptor è fornito dall'esterno lo usiamo direttamente (es. metadati di copertina CaseFlow, non legati a un tipo documento).
|
|
40
|
+
if (mdProp) {
|
|
41
|
+
setMd(mdProp);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
39
44
|
// Passiamo did = undefined, perché è già in cache
|
|
40
45
|
DcmtTypeListCacheService.GetWithNotGrantedAsync(tid, undefined).then((dtd) => {
|
|
41
46
|
setMd(dtd?.metadata?.find(o => o.id == mid));
|
|
42
47
|
});
|
|
43
|
-
}, [tid, mid]);
|
|
48
|
+
}, [tid, mid, mdProp]);
|
|
44
49
|
const isReadOnlyInternal = () => {
|
|
45
50
|
if (!md)
|
|
46
51
|
return false;
|
|
@@ -296,21 +296,21 @@ const TMTextBox = ({ autoComplete = 'off', autoFocus, maxLength, labelColor, pre
|
|
|
296
296
|
// Stima la larghezza del simbolo (circa 10px per carattere + margine)
|
|
297
297
|
return 6 + (currencySymbol.length * 10) + 4;
|
|
298
298
|
};
|
|
299
|
-
return (_jsx("div", { style: { width: '100%', height: 'fit-content', cursor: onClick ? 'pointer' : undefined, display: 'flex', alignItems: 'center' }, id: `text-${id}`, onClick: onClick, children:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
299
|
+
return (_jsx("div", { style: { width: '100%', height: 'fit-content', cursor: onClick ? 'pointer' : undefined, display: 'flex', alignItems: 'center' }, id: `text-${id}`, onClick: onClick, children: _jsxs("div", { style: { flex: 1 }, children: [_jsxs("div", { style: { position: 'relative' }, children: [currencySymbol && initialType !== 'password' && initialType !== 'secureText' && _jsx("span", { style: { position: 'absolute', left: '6px', top: '50%', transform: 'translateY(-50%)', color: disabled ? TMColors.disabled : TMColors.text_normal, fontSize: fontSize, pointerEvents: 'none', zIndex: 1 }, children: currencySymbol }), _jsx(StyledEditor, { ref: inputRef, onContextMenu: (e) => e.stopPropagation(), id: `text-${label}-${id}`, name: label, autoFocus: autoFocus, readOnly: readOnly, type: currentType, disabled: disabled, value: displayedValue, width: width || '100%', placeholder: placeHolder, maxLength: maxLength, autoComplete: autoComplete, spellCheck: false, onFocus: () => setIsFocused(true), onBlur: (e) => { setIsFocused(false); if (currentValue != value)
|
|
300
|
+
onBlur?.(currentValue); }, onChange: handleInputChange, onKeyDown: (e) => {
|
|
301
|
+
if (currentType === 'number') {
|
|
302
|
+
if (!scale && (e.key == "." || e.key == ","))
|
|
303
|
+
e.preventDefault();
|
|
304
|
+
}
|
|
305
|
+
onKeyDown?.(e);
|
|
306
|
+
}, "$isMobile": deviceType === DeviceType.MOBILE, "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, "$fontSize": fontSize, "$maxValue": maxValue, "$width": width, "$type": currentType, "$borderRadius": borderRadius, style: { paddingLeft: `${calculateLeftPadding()}px`, paddingRight: `${calculateRightPadding()}px`, cursor: onClick ? 'pointer' : undefined } }), (initialType === 'password' || initialType === 'secureText') && _jsx(StyledShowPasswordIcon, { onClick: toggleShowPassword, "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, children: showPasswordIcon() }), (formulaItems.length > 0 || (showClearButton && currentValue) || buttons.length > 0) &&
|
|
307
|
+
_jsxs("div", { style: { display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center', position: 'absolute', right: type === 'number' ? '25px' : (initialType === 'password' ? '34px' : '6px'), top: '50%', transform: 'translateY(-50%)', pointerEvents: disabled ? 'none' : 'auto', opacity: disabled ? 0.4 : 1 }, children: [formulaItems.length > 0 &&
|
|
308
|
+
_jsx(StyledTextBoxEditorButton, { onClick: () => {
|
|
309
|
+
setShowFormulaItemsChooser(true);
|
|
310
|
+
}, children: _jsx(IconDataList, {}) }), (showClearButton && currentValue) &&
|
|
311
|
+
_jsx(StyledTextBoxEditorButton, { onClick: () => { onValueChanged?.({ target: { value: undefined } }); onBlur?.(undefined); }, children: _jsx(IconClearButton, {}) }), buttons.map((buttonItem, index) => {
|
|
312
|
+
return (_jsx(StyledTextBoxEditorButton, { onClick: buttonItem.onClick, children: _jsx(TMTooltip, { content: buttonItem.text, children: buttonItem.icon }) }, buttonItem.text));
|
|
313
|
+
})] }), openFormulaItemsChooser(), formulaItems.length > 0 && (_jsx(TMContextMenu, { items: formulaMenuItems, target: `#text-${id}` }))] }), _jsx(TMVilViewer, { vil: validationItems })] }) }));
|
|
314
314
|
};
|
|
315
315
|
const renderedLeftLabelTextBox = () => {
|
|
316
316
|
return (_jsxs(TMLayoutContainer, { direction: 'horizontal', children: [icon && _jsx(TMLayoutItem, { width: '20px', children: _jsx(StyledEditorIcon, { "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, children: icon }) }), _jsx(TMLayoutItem, { children: _jsxs(StyledEditorContainer, { "$width": width, children: [label && _jsx(StyledEditorLabel, { "$color": labelColor, "$isFocused": isFocused, "$labelPosition": labelPosition, "$disabled": disabled, children: label }), renderInputField()] }) })] }));
|
|
@@ -7,7 +7,7 @@ import { DcmtOperationTypes, FormModes } from '../../../ts';
|
|
|
7
7
|
import { StyledToolbarCardContainer, StyledFormButtonsContainer } from '../../base/Styled';
|
|
8
8
|
import { useDeviceType, DeviceType } from '../../base/TMDeviceProvider';
|
|
9
9
|
import { TMSplitterLayout } from '../../base/TMLayout';
|
|
10
|
-
import { TMExceptionBoxManager } from '../../base/TMPopUp';
|
|
10
|
+
import { TMExceptionBoxManager, TMMessageBoxManager, ButtonNames } from '../../base/TMPopUp';
|
|
11
11
|
import TMShowAllOrMaxItemsButton from '../../base/TMShowAllOrMaxItemsButton';
|
|
12
12
|
import TMSpinner from '../../base/TMSpinner';
|
|
13
13
|
import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
|
|
@@ -114,10 +114,24 @@ const TMBatchUpdateForm = ({ inputDcmts, TID, DID, isExpertMode = SDKUI_Globals.
|
|
|
114
114
|
const totalItems = metadataValues.filter(o => o.mid && o.mid > 100).length;
|
|
115
115
|
const metadataValuesSource = metadataValues.filter(o => o.mid && o.mid > 100).slice(0, showAll ? metadataValues.filter(o => o.mid && o.mid > 100).length : listMaxItems);
|
|
116
116
|
const metadataValuesSourceOrig = metadataValuesOrig.filter(o => o.mid && o.mid > 100).slice(0, showAll ? metadataValuesOrig.filter(o => o.mid && o.mid > 100).length : listMaxItems);
|
|
117
|
+
/**
|
|
118
|
+
* Chiusura del form: con modifiche pendenti chiede conferma prima di scartarle.
|
|
119
|
+
* Il TMModal è montato con askClosingConfirm, quindi non si auto-nasconde: la chiusura
|
|
120
|
+
* effettiva avviene solo quando onBack smonta il componente.
|
|
121
|
+
*/
|
|
122
|
+
const handleClose = () => {
|
|
123
|
+
if (!isModified) {
|
|
124
|
+
onBack?.();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
TMMessageBoxManager.show({
|
|
128
|
+
title: SDKUI_Localizator.Cancel, message: SDKUI_Localizator.ConfirmOnCancel, buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
129
|
+
onButtonClick: (e) => { if (e === ButtonNames.YES)
|
|
130
|
+
onBack?.(); }
|
|
131
|
+
});
|
|
132
|
+
};
|
|
117
133
|
const renderContent = () => {
|
|
118
|
-
return (_jsx(TMLayoutWaitingContainer, { showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, children: _jsxs(TMSplitterLayout, { direction: 'horizontal', overflow: 'visible', separatorSize: Gutters.getGutters(), separatorColor: 'transparent', separatorActiveColor: 'transparent', showSeparator: showFormulaEditor, start: showFormulaEditor ? ['50%', '50%'] : ['100%', '0%'], min: ['0', '0'], children: [_jsx(TMPanel, { title: isModal ? 'Modifica i metadati' : `${SDKUI_Localizator.BatchUpdate} (${inputDcmts.length} documenti selezionati)`, onBack: deviceType === DeviceType.MOBILE ?
|
|
119
|
-
// onClose={deviceType !== DeviceType.MOBILE ? onBack : undefined}
|
|
120
|
-
allowMaximize: !isModal, showHeader: !isModal, children: _jsxs(StyledToolbarCardContainer, { children: [_jsx(TMMetadataValues, { TID: TID, isExpertMode: isExpertMode, showCheckBoxes: ShowCheckBoxesMode.Always, showNullValueCheckBoxes: true, metadataValues: metadataValuesSource, metadataValuesOrig: metadataValuesSourceOrig, validationItems: validationItems, isOpenDistinctValues: showDistinctValuesPanel, selectedMID: focusedMetadataValue?.mid, onFocusedItemChanged: (item) => { setFocusedMetadataValue(item); }, onValueChanged: (newItems) => {
|
|
134
|
+
return (_jsx(TMLayoutWaitingContainer, { showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, children: _jsxs(TMSplitterLayout, { direction: 'horizontal', overflow: 'visible', separatorSize: Gutters.getGutters(), separatorColor: 'transparent', separatorActiveColor: 'transparent', showSeparator: showFormulaEditor, start: showFormulaEditor ? ['50%', '50%'] : ['100%', '0%'], min: ['0', '0'], children: [_jsx(TMPanel, { title: isModal ? 'Modifica i metadati' : `${SDKUI_Localizator.BatchUpdate} (${inputDcmts.length} documenti selezionati)`, onBack: deviceType === DeviceType.MOBILE ? handleClose : undefined, allowMaximize: !isModal, showHeader: !isModal, children: _jsxs(StyledToolbarCardContainer, { children: [_jsx(TMMetadataValues, { TID: TID, isExpertMode: isExpertMode, showCheckBoxes: ShowCheckBoxesMode.Always, showNullValueCheckBoxes: true, metadataValues: metadataValuesSource, metadataValuesOrig: metadataValuesSourceOrig, validationItems: validationItems, isOpenDistinctValues: showDistinctValuesPanel, selectedMID: focusedMetadataValue?.mid, onFocusedItemChanged: (item) => { setFocusedMetadataValue(item); }, onValueChanged: (newItems) => {
|
|
121
135
|
setMetadataValues((prevItems) => prevItems.map((item) => {
|
|
122
136
|
const newItem = newItems.find((newItem) => newItem.tid === item.tid && newItem.mid === item.mid);
|
|
123
137
|
return newItem ? { ...item, ...newItem } : item;
|
|
@@ -153,7 +167,7 @@ const TMBatchUpdateForm = ({ inputDcmts, TID, DID, isExpertMode = SDKUI_Globals.
|
|
|
153
167
|
} })] }) })] }) }));
|
|
154
168
|
};
|
|
155
169
|
return (_jsx(_Fragment, { children: (isModal && onBack)
|
|
156
|
-
? _jsx(TMModal, { title: titleModal, onClose:
|
|
170
|
+
? _jsx(TMModal, { title: titleModal, onClose: handleClose, width: widthModal ?? '100%', height: heightModal ?? '100%', hidePopup: false, askClosingConfirm: true, children: _jsx("div", { style: { width: "100%", height: "100%", display: 'block', padding: "4px" }, children: renderContent() }) })
|
|
157
171
|
: renderContent() }));
|
|
158
172
|
};
|
|
159
173
|
export default TMBatchUpdateForm;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { DcmtTypeDescriptor, HomeBlogPost, LayoutModes, ObjectRef, SearchResultDescriptor, TaskDescriptor, ValidationItem } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
import { DcmtInfo, FormModes, MetadataValueDescriptorEx, TaskContext, IGraphometricManagerProp } from '../../../ts';
|
|
4
4
|
import { IntesiCertificateData } from '../../../helper';
|
|
5
|
+
import { ArchiveListMode } from '../../../hooks/useArchiveListForm';
|
|
5
6
|
/**
|
|
6
7
|
* Definisce il contesto da cui è stato invocato il TMDcmtForm.
|
|
7
8
|
* Permette di gestire logiche diverse in base alla provenienza.
|
|
@@ -77,6 +78,12 @@ interface ITMDcmtFormProps {
|
|
|
77
78
|
mid: number;
|
|
78
79
|
value: string;
|
|
79
80
|
}>, tid?: number) => void;
|
|
81
|
+
/**
|
|
82
|
+
* Modalità "aggiungi a elenco" (es. archiviazione dettagli multipli): se valorizzata, in layout di archiviazione
|
|
83
|
+
* il form non archivia ma emette i metadati compilati al chiamante (pulsanti "Aggiungi"/"Modifica" + "Pulisci").
|
|
84
|
+
* Tutta la logica è incapsulata in useArchiveListForm; usata solo da TMMultiMasterDetailDcmts.
|
|
85
|
+
*/
|
|
86
|
+
archiveListMode?: ArchiveListMode;
|
|
80
87
|
fetchRemoteCertificates?: (email: string) => Promise<IntesiCertificateData[]>;
|
|
81
88
|
datagridUtility?: {
|
|
82
89
|
onRefreshSearchAsyncDatagrid?: () => Promise<void>;
|