@topconsultnpm/sdkui-react 6.19.0-test2 → 6.20.0-dev1.10
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/base/TMAccordion.js +2 -2
- package/lib/components/choosers/TMDynDataListItemChooser.js +5 -4
- package/lib/components/editors/TMHtmlEditor.js +1 -1
- package/lib/components/editors/TMMetadataValues.js +34 -12
- package/lib/components/features/assistant/ToppyDraggableHelpCenter.js +74 -63
- package/lib/components/features/documents/TMDcmtBlog.d.ts +1 -7
- package/lib/components/features/documents/TMDcmtBlog.js +29 -2
- package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
- package/lib/components/features/documents/TMDcmtForm.js +24 -34
- package/lib/components/features/documents/TMDcmtPreview.js +93 -64
- package/lib/components/features/search/TMSavedQuerySelector.js +1 -1
- package/lib/components/features/search/TMSearchQueryPanel.js +1 -1
- package/lib/components/features/search/TMSearchResult.js +249 -58
- package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +8 -0
- package/lib/components/features/search/TMSearchResultCheckoutInfoForm.js +129 -0
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +2 -2
- package/lib/components/features/search/TMSearchResultsMenuItems.js +41 -63
- package/lib/components/features/search/TMTreeSelector.js +1 -1
- package/lib/components/features/search/TMViewHistoryDcmt.d.ts +18 -0
- package/lib/components/features/search/TMViewHistoryDcmt.js +285 -0
- package/lib/components/grids/TMRecentsManager.js +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +3 -7
- package/lib/helper/SDKUI_Globals.js +1 -0
- package/lib/helper/SDKUI_Localizator.d.ts +16 -0
- package/lib/helper/SDKUI_Localizator.js +209 -6
- package/lib/helper/TMIcons.d.ts +3 -1
- package/lib/helper/TMIcons.js +9 -1
- package/lib/helper/TMUtils.d.ts +3 -1
- package/lib/helper/TMUtils.js +51 -0
- package/lib/helper/checkinCheckoutManager.d.ts +55 -0
- package/lib/helper/checkinCheckoutManager.js +266 -0
- package/lib/helper/helpers.d.ts +7 -0
- package/lib/helper/helpers.js +37 -5
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.js +13 -1
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +52 -52
|
@@ -47,12 +47,12 @@ const StyledGroupTemplate = styled.div `
|
|
|
47
47
|
&::after {
|
|
48
48
|
content: '';
|
|
49
49
|
display: block;
|
|
50
|
-
width:
|
|
50
|
+
width: calc(100% - 35px);
|
|
51
51
|
margin: 0 auto;
|
|
52
52
|
border-bottom: 1px solid #00A99D;
|
|
53
53
|
margin-top: 8px;
|
|
54
54
|
position: absolute;
|
|
55
|
-
left:
|
|
55
|
+
left: 35px;
|
|
56
56
|
bottom: 0;
|
|
57
57
|
}
|
|
58
58
|
`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { DataColumnTypes, SDK_Localizator, DataListCacheService, SDK_Globals, LayoutModes } from '@topconsultnpm/sdk-ts';
|
|
4
|
-
import { IconDetails, IconSearch,
|
|
4
|
+
import { IconDetails, IconSearch, generateUniqueColumnKeys, Globalization, SDKUI_Localizator, searchResultDescriptorToSimpleArray, IsParametricQuery, IconWarning } from '../../helper';
|
|
5
5
|
import { StyledDivHorizontal } from '../base/Styled';
|
|
6
6
|
import TMSpinner from '../base/TMSpinner';
|
|
7
7
|
import TMSummary from '../editors/TMSummary';
|
|
@@ -125,8 +125,9 @@ const TMDynDataListItemChooser = ({ tid, md, width = '100%', titleForm, openChoo
|
|
|
125
125
|
export default TMDynDataListItemChooser;
|
|
126
126
|
const cellRenderIcon = () => _jsx(IconDetails, {});
|
|
127
127
|
export const TMDynDataListItemChooserForm = (props) => {
|
|
128
|
+
// Generate unique keys for all columns
|
|
129
|
+
const uniqueKeys = generateUniqueColumnKeys(props.searchResult?.dtdResult?.columns, props.searchResult?.fromTID);
|
|
128
130
|
const dataColumns = props.searchResult?.dtdResult?.columns?.map((col, index) => {
|
|
129
|
-
let keyField = getDataColumnName(props.searchResult?.fromTID, col);
|
|
130
131
|
const isVisible = col.extendedProperties?.["Visibility"] != "Hidden";
|
|
131
132
|
const dataType = () => {
|
|
132
133
|
switch (col.dataType) {
|
|
@@ -136,9 +137,9 @@ export const TMDynDataListItemChooserForm = (props) => {
|
|
|
136
137
|
default: return "string";
|
|
137
138
|
}
|
|
138
139
|
};
|
|
139
|
-
return { dataField:
|
|
140
|
+
return { dataField: uniqueKeys[index], caption: col.caption, visible: isVisible, dataType: dataType(), format: col.dataType === DataColumnTypes.DateTime ? Globalization.getDateDisplayFormat() : "" };
|
|
140
141
|
});
|
|
141
|
-
const keyValue =
|
|
142
|
+
const keyValue = uniqueKeys[props.dynDL?.selectItemForValue ?? 0] ?? '';
|
|
142
143
|
const getItems = async (refreshCache) => {
|
|
143
144
|
if (!props.searchResult)
|
|
144
145
|
return [];
|
|
@@ -170,7 +170,7 @@ const TMHtmlEditor = (props) => {
|
|
|
170
170
|
justifyContent: 'flex-end',
|
|
171
171
|
fontSize: 12,
|
|
172
172
|
color: '#6c757d',
|
|
173
|
-
marginTop: 4,
|
|
173
|
+
marginTop: showInfoIcon ? 0 : 4,
|
|
174
174
|
gap: 4,
|
|
175
175
|
}, children: [`${Math.max(charactersRemaining, 0)} ${SDKUI_Localizator.CharactersRemaining}`, showInfoIcon && (_jsx(TMTooltip, { content: 'Markup HTML', children: _jsx("span", { className: "dx-icon-codeblock", style: { fontSize: 22, cursor: 'pointer' }, onClick: () => {
|
|
176
176
|
TMMessageBoxManager.show({
|
|
@@ -10,7 +10,7 @@ import TMTooltip from "../base/TMTooltip";
|
|
|
10
10
|
import TMCheckBox from "./TMCheckBox";
|
|
11
11
|
import TMMetadataEditor, { useMetadataEditableList } from "./TMMetadataEditor";
|
|
12
12
|
import { FormulaHelper } from "./TMFormulaEditor";
|
|
13
|
-
import { DraftsMIDs, DSAttachsMIDs } from "../../ts";
|
|
13
|
+
import { ChronologyMIDs, DraftsMIDs, DSAttachsMIDs } from "../../ts";
|
|
14
14
|
import { TMNothingToShow } from "../features/documents/TMDcmtPreview";
|
|
15
15
|
import TMAccordion from "../base/TMAccordion";
|
|
16
16
|
import TabPanel, { Item } from 'devextreme-react/tab-panel';
|
|
@@ -388,6 +388,20 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
388
388
|
});
|
|
389
389
|
return (_jsxs("div", { style: { width: '100%' }, children: [draftData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.Draft, children: draftData.map(item => renderMetadataItem(item, isReadOnly)) }), checkOutData.length > 0 && _jsx(TMAccordion, { title: `${SDKUI_Localizator.CheckIn}/${SDKUI_Localizator.CheckOut}`, children: checkOutData.map(item => renderMetadataItem(item, true)) })] }));
|
|
390
390
|
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
391
|
+
const layoutChronology = useMemo(() => {
|
|
392
|
+
const chronologyData = [];
|
|
393
|
+
metadataValues.forEach(item => {
|
|
394
|
+
switch (item.md?.id) {
|
|
395
|
+
case ChronologyMIDs.Ver:
|
|
396
|
+
case ChronologyMIDs.AuthorID:
|
|
397
|
+
chronologyData.push(item);
|
|
398
|
+
break;
|
|
399
|
+
default:
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return (_jsx("div", { style: { width: '100%' }, children: chronologyData.length > 0 && chronologyData.map(item => renderMetadataItem(item, isReadOnly)) }));
|
|
404
|
+
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
391
405
|
const layoutDsAttachs = useMemo(() => {
|
|
392
406
|
const dsAttachsData = [];
|
|
393
407
|
metadataValues.forEach(item => {
|
|
@@ -443,10 +457,10 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
443
457
|
if (layoutItem.type === LayoutItemTypes.LayoutRoot) {
|
|
444
458
|
const children = getChildren(layoutItem.layoutItemID);
|
|
445
459
|
if (layout.showTab) {
|
|
446
|
-
return (_jsx(React.Fragment, { children: _jsx(TabPanel, { width: "100%", height: "100%", children: children.map(child => renderLayoutItem(child, depth, visited)) }) }, `root-${layoutItem.layoutItemID}`));
|
|
460
|
+
return (_jsx(React.Fragment, { children: _jsx(TabPanel, { width: "100%", height: "100%", children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth, visited) }, `child-${child.layoutItemID}`))) }) }, `root-${layoutItem.layoutItemID}`));
|
|
447
461
|
}
|
|
448
462
|
else {
|
|
449
|
-
return (_jsx(React.Fragment, { children: children.map(child => renderLayoutItem(child, depth, visited)) }, `root-${layoutItem.layoutItemID}`));
|
|
463
|
+
return (_jsx(React.Fragment, { children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth, visited) }, `child-${child.layoutItemID}`))) }, `root-${layoutItem.layoutItemID}`));
|
|
450
464
|
}
|
|
451
465
|
}
|
|
452
466
|
// Check if this is a LayoutGroup
|
|
@@ -458,25 +472,32 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
458
472
|
const isCollapsed = false; // LayoutGroupDescriptor doesn't have collapsed property
|
|
459
473
|
//Layout Grou da trasformare in tab
|
|
460
474
|
if (groupDescriptor.orientation == LayoutGroupOrientations.Vertical && layout.showTab && layoutItem.parentID === 0) {
|
|
461
|
-
return (_jsx(Item, { title: groupTitle, children: _jsx("div", { style: { width: '100%' }, children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }) }, `group-tab-${layoutItem.layoutItemID}`));
|
|
475
|
+
return (_jsx(Item, { title: groupTitle, children: _jsx("div", { style: { width: '100%' }, children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`))) }) }, `group-tab-${layoutItem.layoutItemID}`));
|
|
462
476
|
}
|
|
463
477
|
if (groupIsNoBorder && groupDescriptor.orientation !== LayoutGroupOrientations.Horizontal && children.length == 0) {
|
|
464
478
|
return (_jsx("br", {}));
|
|
465
479
|
}
|
|
480
|
+
if (!groupIsNoBorder && groupDescriptor.orientation === LayoutGroupOrientations.Horizontal && children.length > 0 && groupTitle.length > 0) {
|
|
481
|
+
return (
|
|
482
|
+
// <div key={`group-horizontal-${layoutItem.layoutItemID}`} style={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', gap: '3px' }}>
|
|
483
|
+
_jsxs("div", { style: { position: 'relative', border: '2px solid #CAD9EB', borderRadius: '3px', padding: '12px 6px 6px', margin: '10px', display: 'flex', flexDirection: 'row', flexWrap: 'wrap', gap: '3px' }, children: [_jsx("div", { style: { position: 'absolute', top: '-10px', left: '10px', padding: '0 8px', backgroundColor: '#fff', fontWeight: 600, fontSize: '1rem', color: TMColors.primaryColor, }, children: groupTitle }), children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`))), " "] }, `group-horizontal-${layoutItem.layoutItemID}`)
|
|
484
|
+
// </div>
|
|
485
|
+
);
|
|
486
|
+
}
|
|
466
487
|
if (groupIsNoBorder && groupDescriptor.orientation === LayoutGroupOrientations.Vertical) {
|
|
467
|
-
return (_jsx("div", { style: { width: '100%', border: 'none', padding: 0 }, children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }));
|
|
488
|
+
return (_jsx("div", { style: { width: '100%', border: 'none', padding: 0 }, children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`))) }));
|
|
468
489
|
}
|
|
469
490
|
if (!groupIsNoBorder && groupDescriptor.orientation === LayoutGroupOrientations.Vertical) {
|
|
470
491
|
if (groupDescriptor.borderStyle == LayoutGroupBorderStyles.Group) {
|
|
471
|
-
return (_jsxs("div", { style: { position: 'relative', border: '2px solid #CAD9EB', borderRadius: '3px', padding: '12px 6px 6px', margin: '10px', display: 'flex', flexDirection: 'column', flexWrap: 'wrap', gap: '3px', }, children: [_jsx("div", { style: { position: 'absolute', top: '-10px', left: '10px', padding: '0 8px', backgroundColor: '#fff', fontWeight: 600, fontSize: '1rem', color: TMColors.primaryColor, }, children: groupTitle }), children.map(child => renderLayoutItem(child, depth + 1, visited))] }));
|
|
492
|
+
return (_jsxs("div", { style: { position: 'relative', border: '2px solid #CAD9EB', borderRadius: '3px', padding: '12px 6px 6px', margin: '10px', display: 'flex', flexDirection: 'column', flexWrap: 'wrap', gap: '3px', }, children: [_jsx("div", { style: { position: 'absolute', top: '-10px', left: '10px', padding: '0 8px', backgroundColor: '#fff', fontWeight: 600, fontSize: '1rem', color: TMColors.primaryColor, }, children: groupTitle }), children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`)))] }));
|
|
472
493
|
}
|
|
473
494
|
if (groupDescriptor.borderStyle == LayoutGroupBorderStyles.GroupBox || groupDescriptor.borderStyle == LayoutGroupBorderStyles.Tabbed) {
|
|
474
|
-
return (_jsx(TMAccordion, { title: groupTitle, titleSize: "Small", children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }));
|
|
495
|
+
return (_jsx(TMAccordion, { title: groupTitle, titleSize: "Small", children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`))) }));
|
|
475
496
|
}
|
|
476
497
|
}
|
|
477
498
|
if (groupDescriptor.orientation === LayoutGroupOrientations.Horizontal) {
|
|
478
499
|
return (_jsx("div", { style: { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', gap: '3px' }, children: groupDescriptor.borderStyle == LayoutGroupBorderStyles.GroupBox || groupDescriptor.borderStyle == LayoutGroupBorderStyles.Tabbed ?
|
|
479
|
-
_jsx(TMAccordion, { title: groupTitle, titleSize: "Small", children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }) :
|
|
500
|
+
_jsx(TMAccordion, { title: groupTitle, titleSize: "Small", children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`))) }) :
|
|
480
501
|
groupDescriptor.borderStyle == LayoutGroupBorderStyles.Group ?
|
|
481
502
|
_jsxs("div", { style: { position: 'relative', border: '2px solid #CAD9EB', borderRadius: '3px', padding: '12px 6px 6px', margin: '10px', display: 'flex', flexDirection: 'column', flexWrap: 'wrap', gap: '3px', }, children: [_jsx("div", { style: { position: 'absolute', top: '-10px', left: '10px', padding: '0 8px', backgroundColor: '#fff', fontWeight: 600, fontSize: '1rem', color: TMColors.primaryColor, }, children: groupTitle }), children.map(child => (_jsx("div", { style: { flex: '1 1 0', minWidth: 0 }, children: renderLayoutItem(child, depth + 1, visited) }, child.layoutItemID)))] })
|
|
482
503
|
:
|
|
@@ -484,7 +505,7 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
484
505
|
}
|
|
485
506
|
// Apply indentation only to subgroups (depth > 0), not to root groups
|
|
486
507
|
const indentationPx = depth > 0 ? depth * 10 : 0;
|
|
487
|
-
return (_jsx("div", { style: { paddingLeft: `${indentationPx}px` }, children: _jsx(TMAccordion, { title: groupTitle, defaultCollapsed: isCollapsed, titleSize: "Small", children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }) }, `group-wrapper-${layoutItem.layoutItemID}`));
|
|
508
|
+
return (_jsx("div", { style: { paddingLeft: `${indentationPx}px` }, children: _jsx(TMAccordion, { title: groupTitle, defaultCollapsed: isCollapsed, titleSize: "Small", children: children.map(child => (_jsx(React.Fragment, { children: renderLayoutItem(child, depth + 1, visited) }, `child-${child.layoutItemID}`))) }) }, `group-wrapper-${layoutItem.layoutItemID}`));
|
|
488
509
|
}
|
|
489
510
|
// Check if this is a LayoutControlItem (metadata field)
|
|
490
511
|
else if (layoutItem.type === LayoutItemTypes.LayoutControlItem && layoutItem.lcid) {
|
|
@@ -496,7 +517,8 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
496
517
|
const parent = layout.items?.filter(p => p.layoutItemID === layoutItem.parentID);
|
|
497
518
|
if (parent && parent.length > 0 && parent[0].type === LayoutItemTypes.LayoutGroup && parent[0].lgd) {
|
|
498
519
|
const groupDescriptor = parent[0].lgd;
|
|
499
|
-
if (groupDescriptor.borderStyle == LayoutGroupBorderStyles.Group
|
|
520
|
+
if (groupDescriptor.borderStyle == LayoutGroupBorderStyles.Group
|
|
521
|
+
|| groupDescriptor.borderStyle == LayoutGroupBorderStyles.NoBorder)
|
|
500
522
|
isAccordion = false;
|
|
501
523
|
}
|
|
502
524
|
// Find the metadata value for this MID
|
|
@@ -515,7 +537,7 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
515
537
|
};
|
|
516
538
|
return (_jsx("div", { style: { width: '100%' }, children: (() => {
|
|
517
539
|
const visited = new Set();
|
|
518
|
-
return rootItems.map(item => renderLayoutItem(item, 0, visited));
|
|
540
|
+
return rootItems.map(item => (_jsx(React.Fragment, { children: renderLayoutItem(item, 0, visited) }, `root-item-${item.layoutItemID}`)));
|
|
519
541
|
})() }));
|
|
520
542
|
}, [layout, metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
521
543
|
const renderForm = useMemo(() => {
|
|
@@ -533,7 +555,7 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
533
555
|
}
|
|
534
556
|
switch (currentDTD?.id) {
|
|
535
557
|
case SystemTIDs.Drafts: return layoutDraft;
|
|
536
|
-
|
|
558
|
+
case SystemTIDs.Chronology: return layoutChronology;
|
|
537
559
|
case SystemTIDs.DSAttachs: return layoutDsAttachs;
|
|
538
560
|
default:
|
|
539
561
|
// Se è presente un layout personalizzato, usalo, altrimenti usa il rendering standard
|
|
@@ -33,7 +33,7 @@ const ToppyButton = styled.div.attrs((props) => ({
|
|
|
33
33
|
/* Posizionamento di default quando non è draggato (x e y non sono definiti) */
|
|
34
34
|
${(props) => props.$x === undefined || props.$y === undefined
|
|
35
35
|
? `
|
|
36
|
-
bottom: ${props.$isMobile ? '
|
|
36
|
+
bottom: ${props.$isMobile ? (props.$isCollapsed ? '-20px' : '-50px') : '-20px'};
|
|
37
37
|
${props.$align === 'left' ? 'left: 10px;' : 'right: 10px;'};
|
|
38
38
|
`
|
|
39
39
|
: ''}
|
|
@@ -48,15 +48,9 @@ const ToppyButton = styled.div.attrs((props) => ({
|
|
|
48
48
|
/* Dimensioni dinamiche in base allo stato collassato e al tipo di dispositivo
|
|
49
49
|
Usa min() per adattarsi su schermi piccoli */
|
|
50
50
|
width: ${(props) => {
|
|
51
|
-
if (props.$isMobile) {
|
|
52
|
-
return props.$isCollapsed ? 'min(40px, 100%)' : '80px';
|
|
53
|
-
}
|
|
54
51
|
return props.$isCollapsed ? 'min(60px, 100%)' : '120px';
|
|
55
52
|
}};
|
|
56
53
|
height: ${(props) => {
|
|
57
|
-
if (props.$isMobile) {
|
|
58
|
-
return props.$isCollapsed ? 'min(45px, 100%)' : '95px';
|
|
59
|
-
}
|
|
60
54
|
return props.$isCollapsed ? 'min(70px, 100%)' : '140px';
|
|
61
55
|
}};
|
|
62
56
|
max-width: 100%;
|
|
@@ -66,18 +60,8 @@ const ToppyButton = styled.div.attrs((props) => ({
|
|
|
66
60
|
|
|
67
61
|
img {
|
|
68
62
|
/* Dimensioni dell'immagine in base allo stato collassato e al tipo di dispositivo */
|
|
69
|
-
width: ${(props) =>
|
|
70
|
-
|
|
71
|
-
return props.$isCollapsed ? '40px' : '80px';
|
|
72
|
-
}
|
|
73
|
-
return props.$isCollapsed ? '60px' : '120px';
|
|
74
|
-
}};
|
|
75
|
-
height: ${(props) => {
|
|
76
|
-
if (props.$isMobile) {
|
|
77
|
-
return props.$isCollapsed ? '40px' : '95px';
|
|
78
|
-
}
|
|
79
|
-
return props.$isCollapsed ? '60px' : '140px';
|
|
80
|
-
}};
|
|
63
|
+
width: ${(props) => props.$isCollapsed ? (props.$isMobile ? '40px' : '60px') : (props.$isMobile ? '80px' : '120px')};
|
|
64
|
+
height: ${(props) => props.$isCollapsed ? (props.$isMobile ? '40px' : '60px') : (props.$isMobile ? '100px' : '140px')};
|
|
81
65
|
pointer-events: ${(props) => (props.$isMobile ? 'auto' : 'none')};
|
|
82
66
|
border-radius: 50%; /* Rende l'immagine circolare */
|
|
83
67
|
/* Rotazione leggera in base all'allineamento */
|
|
@@ -169,6 +153,16 @@ const DragOverlay = styled.div `
|
|
|
169
153
|
* contenitore e può essere collassato/espanso con un doppio click.
|
|
170
154
|
*/
|
|
171
155
|
const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onToppyImageClick, initialIsCollapsed, isVisible = true, usePortal = false, }) => {
|
|
156
|
+
// Configurazione del layout e dei limiti di posizionamento di Toppy
|
|
157
|
+
const LAYOUT_CONFIG = {
|
|
158
|
+
bottomOffsetDesktop: 20, // Quanto Toppy può uscire dal bordo inferiore su desktop
|
|
159
|
+
bottomOffsetMobileCollapsed: 20, // Quanto Toppy può uscire dal bordo inferiore su mobile quando è collassato
|
|
160
|
+
bottomOffsetMobileExpanded: 50, // Quanto Toppy può uscire dal bordo inferiore su mobile quando è espanso
|
|
161
|
+
buttonOffset: 8, // Spazio extra per i bottoni posizionati a -8px (ExpandButton, CloseButton)
|
|
162
|
+
closeButtonOffset: 8, // Offset dei bottoni che sporgono dalla bubble (CloseButton sulla bubble)
|
|
163
|
+
bubbleBuffer: 5, // Buffer di sicurezza per evitare sovrapposizioni con la bubble
|
|
164
|
+
minTopOffset: 8 // Limite minimo dall'alto per evitare che i bottoni superiori escano
|
|
165
|
+
};
|
|
172
166
|
// Ref per il contenitore principale
|
|
173
167
|
const buttonRef = useRef(null);
|
|
174
168
|
// Stato per controllare se il componente è collassato o espanso
|
|
@@ -205,20 +199,16 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
205
199
|
return;
|
|
206
200
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
207
201
|
// Spazio extra occupato dalla bubble quando non è collassato
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
const extraHeight = !isCollapsed ? bubbleSize.height + closeButtonOffset : 0;
|
|
211
|
-
const extraWidth = !isCollapsed ? bubbleSize.width + closeButtonOffset : 0;
|
|
202
|
+
const extraHeight = !isCollapsed ? bubbleSize.height + LAYOUT_CONFIG.closeButtonOffset : 0;
|
|
203
|
+
const extraWidth = !isCollapsed ? bubbleSize.width + LAYOUT_CONFIG.closeButtonOffset : 0;
|
|
212
204
|
let minX = 0;
|
|
213
205
|
let maxX;
|
|
214
206
|
let maxY;
|
|
215
|
-
const
|
|
216
|
-
const buttonOffset = 8; // Spazio extra per i bottoni posizionati a -8px
|
|
217
|
-
const minY = buttonOffset; // Limite minimo per evitare che i bottoni superiori escano
|
|
207
|
+
const minY = LAYOUT_CONFIG.minTopOffset;
|
|
218
208
|
if (usePortal) {
|
|
219
209
|
// Calcola i limiti usando le dimensioni del viewport
|
|
220
|
-
maxX = window.innerWidth - rect.width - buttonOffset;
|
|
221
|
-
maxY = window.innerHeight - rect.height +
|
|
210
|
+
maxX = window.innerWidth - rect.width - LAYOUT_CONFIG.buttonOffset;
|
|
211
|
+
maxY = window.innerHeight - rect.height + (isMobile ? (isCollapsed ? LAYOUT_CONFIG.bottomOffsetMobileCollapsed : LAYOUT_CONFIG.bottomOffsetMobileExpanded) : LAYOUT_CONFIG.bottomOffsetDesktop);
|
|
222
212
|
}
|
|
223
213
|
else {
|
|
224
214
|
// Calcola i limiti usando le dimensioni del parent
|
|
@@ -226,8 +216,8 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
226
216
|
if (!parent)
|
|
227
217
|
return;
|
|
228
218
|
const parentRect = parent.getBoundingClientRect();
|
|
229
|
-
maxX = parentRect.width - rect.width - buttonOffset;
|
|
230
|
-
maxY = parentRect.height - rect.height +
|
|
219
|
+
maxX = parentRect.width - rect.width - LAYOUT_CONFIG.buttonOffset;
|
|
220
|
+
maxY = parentRect.height - rect.height + (isMobile ? (isCollapsed ? LAYOUT_CONFIG.bottomOffsetMobileCollapsed : LAYOUT_CONFIG.bottomOffsetMobileExpanded) : LAYOUT_CONFIG.bottomOffsetDesktop);
|
|
231
221
|
}
|
|
232
222
|
if (!isCollapsed) {
|
|
233
223
|
if (align === 'right') {
|
|
@@ -240,12 +230,12 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
240
230
|
// Verifica se la posizione corrente è fuori dai limiti
|
|
241
231
|
const isOutOfBounds = position.x < minX ||
|
|
242
232
|
position.x > maxX ||
|
|
243
|
-
position.y < Math.max(minY, extraHeight + bubbleBuffer) ||
|
|
233
|
+
position.y < Math.max(minY, extraHeight + LAYOUT_CONFIG.bubbleBuffer) ||
|
|
244
234
|
position.y > maxY;
|
|
245
235
|
// Se è fuori dai limiti, aggiusta la posizione
|
|
246
236
|
if (isOutOfBounds) {
|
|
247
237
|
const adjustedX = Math.max(minX, Math.min(position.x, maxX));
|
|
248
|
-
const adjustedY = Math.max(Math.max(minY, extraHeight + bubbleBuffer), Math.min(position.y, maxY));
|
|
238
|
+
const adjustedY = Math.max(Math.max(minY, extraHeight + LAYOUT_CONFIG.bubbleBuffer), Math.min(position.y, maxY));
|
|
249
239
|
setPosition({ x: adjustedX, y: adjustedY });
|
|
250
240
|
}
|
|
251
241
|
}, [isCollapsed, bubbleSize, usePortal, align]);
|
|
@@ -259,27 +249,24 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
259
249
|
if (!buttonRef.current || !position)
|
|
260
250
|
return;
|
|
261
251
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
262
|
-
//
|
|
263
|
-
const
|
|
264
|
-
const
|
|
265
|
-
const extraWidth = !isCollapsed ? bubbleSize.width + closeButtonOffset : 0;
|
|
252
|
+
// Spazio extra occupato dalla bubble quando non è collassato
|
|
253
|
+
const extraHeight = !isCollapsed ? bubbleSize.height + LAYOUT_CONFIG.closeButtonOffset : 0;
|
|
254
|
+
const extraWidth = !isCollapsed ? bubbleSize.width + LAYOUT_CONFIG.closeButtonOffset : 0;
|
|
266
255
|
let minX = 0;
|
|
267
256
|
let maxX;
|
|
268
257
|
let maxY;
|
|
269
|
-
const
|
|
270
|
-
const buttonOffset = 8; // Spazio extra per i bottoni posizionati a -8px
|
|
271
|
-
const minY = buttonOffset; // Limite minimo per evitare che i bottoni superiori escano
|
|
258
|
+
const minY = LAYOUT_CONFIG.minTopOffset;
|
|
272
259
|
if (usePortal) {
|
|
273
|
-
maxX = window.innerWidth - rect.width - buttonOffset;
|
|
274
|
-
maxY = window.innerHeight - rect.height +
|
|
260
|
+
maxX = window.innerWidth - rect.width - LAYOUT_CONFIG.buttonOffset;
|
|
261
|
+
maxY = window.innerHeight - rect.height + (isMobile ? (isCollapsed ? LAYOUT_CONFIG.bottomOffsetMobileCollapsed : LAYOUT_CONFIG.bottomOffsetMobileExpanded) : LAYOUT_CONFIG.bottomOffsetDesktop);
|
|
275
262
|
}
|
|
276
263
|
else {
|
|
277
264
|
const parent = buttonRef.current.offsetParent;
|
|
278
265
|
if (!parent)
|
|
279
266
|
return;
|
|
280
267
|
const parentRect = parent.getBoundingClientRect();
|
|
281
|
-
maxX = parentRect.width - rect.width - buttonOffset;
|
|
282
|
-
maxY = parentRect.height - rect.height +
|
|
268
|
+
maxX = parentRect.width - rect.width - LAYOUT_CONFIG.buttonOffset;
|
|
269
|
+
maxY = parentRect.height - rect.height + (isMobile ? (isCollapsed ? LAYOUT_CONFIG.bottomOffsetMobileCollapsed : LAYOUT_CONFIG.bottomOffsetMobileExpanded) : LAYOUT_CONFIG.bottomOffsetDesktop);
|
|
283
270
|
}
|
|
284
271
|
if (!isCollapsed) {
|
|
285
272
|
if (align === 'right') {
|
|
@@ -291,7 +278,7 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
291
278
|
}
|
|
292
279
|
const isOutOfBounds = position.x < minX ||
|
|
293
280
|
position.x > maxX ||
|
|
294
|
-
position.y < Math.max(minY, extraHeight + bubbleBuffer) ||
|
|
281
|
+
position.y < Math.max(minY, extraHeight + LAYOUT_CONFIG.bubbleBuffer) ||
|
|
295
282
|
position.y > maxY;
|
|
296
283
|
if (isOutOfBounds) {
|
|
297
284
|
setPosition(null);
|
|
@@ -347,6 +334,22 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
347
334
|
setIsDragging(true);
|
|
348
335
|
e.preventDefault();
|
|
349
336
|
};
|
|
337
|
+
/**
|
|
338
|
+
* Gestisce l'inizio del trascinamento per eventi touch
|
|
339
|
+
*/
|
|
340
|
+
const handleTouchStart = (e) => {
|
|
341
|
+
if (!buttonRef.current)
|
|
342
|
+
return;
|
|
343
|
+
const touch = e.touches[0];
|
|
344
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
345
|
+
// Calcola l'offset tra il punto di touch e l'angolo superiore sinistro del componente
|
|
346
|
+
dragOffset.current = {
|
|
347
|
+
x: touch.clientX - rect.left,
|
|
348
|
+
y: touch.clientY - rect.top,
|
|
349
|
+
};
|
|
350
|
+
setIsDragging(true);
|
|
351
|
+
e.preventDefault();
|
|
352
|
+
};
|
|
350
353
|
/**
|
|
351
354
|
* Gestisce il movimento durante il trascinamento
|
|
352
355
|
* Calcola la nuova posizione rispettando i limiti del parent o viewport
|
|
@@ -355,24 +358,23 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
355
358
|
const handleMouseMove = (e) => {
|
|
356
359
|
if (!isDragging || !buttonRef.current)
|
|
357
360
|
return;
|
|
361
|
+
// Estrae le coordinate dal tipo di evento appropriato
|
|
362
|
+
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
363
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
358
364
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
359
365
|
// Spazio extra occupato dalla bubble quando non è collassato
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
const extraHeight = !isCollapsed ? bubbleSize.height + closeButtonOffset : 0;
|
|
363
|
-
const extraWidth = !isCollapsed ? bubbleSize.width + closeButtonOffset : 0;
|
|
366
|
+
const extraHeight = !isCollapsed ? bubbleSize.height + LAYOUT_CONFIG.closeButtonOffset : 0;
|
|
367
|
+
const extraWidth = !isCollapsed ? bubbleSize.width + LAYOUT_CONFIG.closeButtonOffset : 0;
|
|
364
368
|
let minX = 0;
|
|
365
369
|
let maxX;
|
|
366
370
|
let maxY;
|
|
367
371
|
let newX;
|
|
368
372
|
let newY;
|
|
369
|
-
const
|
|
370
|
-
const buttonOffset = 8; // Spazio extra per i bottoni posizionati a -8px
|
|
371
|
-
const minY = buttonOffset; // Limite minimo per evitare che i bottoni superiori escano
|
|
373
|
+
const minY = LAYOUT_CONFIG.minTopOffset;
|
|
372
374
|
if (usePortal) {
|
|
373
375
|
// Calcola i limiti usando il viewport
|
|
374
|
-
maxX = window.innerWidth - rect.width - buttonOffset;
|
|
375
|
-
maxY = window.innerHeight - rect.height +
|
|
376
|
+
maxX = window.innerWidth - rect.width - LAYOUT_CONFIG.buttonOffset;
|
|
377
|
+
maxY = window.innerHeight - rect.height + (isMobile ? (isCollapsed ? LAYOUT_CONFIG.bottomOffsetMobileCollapsed : LAYOUT_CONFIG.bottomOffsetMobileExpanded) : LAYOUT_CONFIG.bottomOffsetDesktop);
|
|
376
378
|
if (!isCollapsed) {
|
|
377
379
|
if (align === 'right') {
|
|
378
380
|
minX = Math.max(0, extraWidth - rect.width);
|
|
@@ -381,8 +383,8 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
381
383
|
maxX = Math.min(maxX, window.innerWidth - extraWidth);
|
|
382
384
|
}
|
|
383
385
|
}
|
|
384
|
-
newX = Math.max(minX, Math.min(
|
|
385
|
-
newY = Math.max(Math.max(minY, extraHeight + bubbleBuffer), Math.min(
|
|
386
|
+
newX = Math.max(minX, Math.min(clientX - dragOffset.current.x, maxX));
|
|
387
|
+
newY = Math.max(Math.max(minY, extraHeight + LAYOUT_CONFIG.bubbleBuffer), Math.min(clientY - dragOffset.current.y, maxY));
|
|
386
388
|
}
|
|
387
389
|
else {
|
|
388
390
|
// Calcola i limiti usando il parent
|
|
@@ -390,8 +392,8 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
390
392
|
if (!parent)
|
|
391
393
|
return;
|
|
392
394
|
const parentRect = parent.getBoundingClientRect();
|
|
393
|
-
maxX = parentRect.width - rect.width - buttonOffset;
|
|
394
|
-
maxY = parentRect.height - rect.height +
|
|
395
|
+
maxX = parentRect.width - rect.width - LAYOUT_CONFIG.buttonOffset;
|
|
396
|
+
maxY = parentRect.height - rect.height + (isMobile ? (isCollapsed ? LAYOUT_CONFIG.bottomOffsetMobileCollapsed : LAYOUT_CONFIG.bottomOffsetMobileExpanded) : LAYOUT_CONFIG.bottomOffsetDesktop);
|
|
395
397
|
if (!isCollapsed) {
|
|
396
398
|
if (align === 'right') {
|
|
397
399
|
minX = Math.max(0, extraWidth - rect.width);
|
|
@@ -400,8 +402,8 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
400
402
|
maxX = Math.min(maxX, parentRect.width - extraWidth);
|
|
401
403
|
}
|
|
402
404
|
}
|
|
403
|
-
newX = Math.max(minX, Math.min(
|
|
404
|
-
newY = Math.max(Math.max(minY, extraHeight + bubbleBuffer), Math.min(
|
|
405
|
+
newX = Math.max(minX, Math.min(clientX - parentRect.left - dragOffset.current.x, maxX));
|
|
406
|
+
newY = Math.max(Math.max(minY, extraHeight + LAYOUT_CONFIG.bubbleBuffer), Math.min(clientY - parentRect.top - dragOffset.current.y, maxY));
|
|
405
407
|
}
|
|
406
408
|
setPosition({ x: newX, y: newY });
|
|
407
409
|
};
|
|
@@ -413,10 +415,13 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
413
415
|
const handleMouseUp = (e) => {
|
|
414
416
|
if (isDragging) {
|
|
415
417
|
setIsDragging(false);
|
|
418
|
+
// Estrae le coordinate dal tipo di evento appropriato
|
|
419
|
+
const clientX = 'changedTouches' in e ? e.changedTouches[0].clientX : e.clientX;
|
|
420
|
+
const clientY = 'changedTouches' in e ? e.changedTouches[0].clientY : e.clientY;
|
|
416
421
|
const rect = buttonRef.current?.getBoundingClientRect();
|
|
417
422
|
if (rect) {
|
|
418
423
|
// Calcola la distanza totale del movimento usando il teorema di Pitagora
|
|
419
|
-
const moveDistance = Math.hypot(
|
|
424
|
+
const moveDistance = Math.hypot(clientX - (rect.left + dragOffset.current.x), clientY - (rect.top + dragOffset.current.y));
|
|
420
425
|
// Se il movimento è stato minimo, trattalo come un click
|
|
421
426
|
if (moveDistance < 5 && onToppyImageClick) {
|
|
422
427
|
onToppyImageClick();
|
|
@@ -427,22 +432,28 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
427
432
|
/**
|
|
428
433
|
* Effect per gestire gli event listener durante il trascinamento
|
|
429
434
|
* Gli eventi sono registrati sul document per catturare il movimento
|
|
430
|
-
* anche quando il mouse esce dal componente
|
|
435
|
+
* anche quando il mouse o il touch esce dal componente
|
|
431
436
|
*/
|
|
432
437
|
useEffect(() => {
|
|
433
438
|
if (isDragging) {
|
|
439
|
+
// Eventi mouse per desktop
|
|
434
440
|
document.addEventListener('mousemove', handleMouseMove);
|
|
435
441
|
document.addEventListener('mouseup', handleMouseUp);
|
|
442
|
+
// Eventi touch per mobile
|
|
443
|
+
document.addEventListener('touchmove', handleMouseMove);
|
|
444
|
+
document.addEventListener('touchend', handleMouseUp);
|
|
436
445
|
return () => {
|
|
437
446
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
438
447
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
448
|
+
document.removeEventListener('touchmove', handleMouseMove);
|
|
449
|
+
document.removeEventListener('touchend', handleMouseUp);
|
|
439
450
|
};
|
|
440
451
|
}
|
|
441
452
|
return undefined;
|
|
442
453
|
}, [isDragging]);
|
|
443
454
|
// Renderizza l'overlay solo durante il drag
|
|
444
455
|
const renderDragOverlay = isDragging && _jsx(DragOverlay, {});
|
|
445
|
-
const toppyContent = (_jsxs(_Fragment, { children: [renderDragOverlay, _jsxs(ToppyButton, { ref: buttonRef, "$align": align, "$isDragging": isDragging, "$x": position?.x, "$y": position?.y, "$isVisible": isVisible, "$isCollapsed": isCollapsed, "$isMobile": isMobile, "$usePortal": usePortal, onMouseDown: !isMobile ? handleMouseDown : undefined, onContextMenu: (e) => e.preventDefault(), onDoubleClick: !isMobile ? toggleCollapse : undefined, children: [(content && !isCollapsed) && (_jsx(ToppySpeechBubble, { ref: bubbleRef, align: align, onClose: toggleCollapse, children: content })), (content && isCollapsed) && (_jsx(ExpandButton, { onMouseDown: (e) => {
|
|
456
|
+
const toppyContent = (_jsxs(_Fragment, { children: [renderDragOverlay, _jsxs(ToppyButton, { ref: buttonRef, "$align": align, "$isDragging": isDragging, "$x": position?.x, "$y": position?.y, "$isVisible": isVisible, "$isCollapsed": isCollapsed, "$isMobile": isMobile, "$usePortal": usePortal, onMouseDown: !isMobile ? handleMouseDown : undefined, onTouchStart: isMobile ? handleTouchStart : undefined, onContextMenu: (e) => e.preventDefault(), onDoubleClick: !isMobile ? toggleCollapse : undefined, children: [(content && !isCollapsed) && (_jsx(ToppySpeechBubble, { ref: bubbleRef, align: align, onClose: toggleCollapse, children: content })), (content && isCollapsed) && (_jsx(ExpandButton, { onMouseDown: (e) => {
|
|
446
457
|
isExpandButtonDraggingRef.current = false;
|
|
447
458
|
expandButtonMouseDownPosRef.current = { x: e.clientX, y: e.clientY };
|
|
448
459
|
}, onMouseMove: (e) => {
|
|
@@ -464,7 +475,7 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
464
475
|
}
|
|
465
476
|
e.stopPropagation();
|
|
466
477
|
toggleCollapse(e);
|
|
467
|
-
}, onContextMenu: (e) => e.preventDefault(), "aria-label": SDKUI_Localizator.Maximize, title: SDKUI_Localizator.Maximize, type: "button", children: _jsx(IconWindowMaximize, {}) })), _jsx("img", { src: Toppy, alt: "Toppy Help", draggable: false
|
|
478
|
+
}, onContextMenu: (e) => e.preventDefault(), "aria-label": SDKUI_Localizator.Maximize, title: SDKUI_Localizator.Maximize, type: "button", children: _jsx(IconWindowMaximize, {}) })), _jsx("img", { src: Toppy, alt: "Toppy Help", draggable: false })] })] }));
|
|
468
479
|
// Renderizza nel document.body usando un Portal se usePortal è true, altrimenti renderizza normalmente
|
|
469
480
|
return usePortal ? ReactDOM.createPortal(toppyContent, document.body) : toppyContent;
|
|
470
481
|
};
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { HomeBlogPost, TaskDescriptor } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
interface ITMDcmtBlogProps {
|
|
4
|
-
blogsDatasource: HomeBlogPost[];
|
|
5
|
-
setBlogsDatasource: (posts: HomeBlogPost[]) => void;
|
|
6
|
-
hasLoadedDataOnce: boolean;
|
|
7
|
-
setHasLoadedDataOnce: (loaded: boolean) => void;
|
|
8
|
-
lastLoadedDid: number | undefined;
|
|
9
|
-
setLastLoadedDid: (did: number | undefined) => void;
|
|
10
4
|
tid: number | undefined;
|
|
11
5
|
did: number | undefined;
|
|
12
|
-
fetchBlogDataAsync: (tid: number | undefined, did: number | undefined) => Promise<void>;
|
|
13
6
|
isVisible?: boolean;
|
|
7
|
+
fetchBlogDataTrigger?: number;
|
|
14
8
|
allTasks?: Array<TaskDescriptor>;
|
|
15
9
|
getAllTasks?: () => Promise<void>;
|
|
16
10
|
deleteTaskByIdsCallback?: (deletedTaskIds: Array<number>) => Promise<void>;
|
|
@@ -6,13 +6,40 @@ import { TMNothingToShow } from './TMDcmtPreview';
|
|
|
6
6
|
import { IconBoard, SDKUI_Localizator } from '../../../helper';
|
|
7
7
|
import TMBlogCommentForm from '../blog/TMBlogCommentForm';
|
|
8
8
|
import TMBlogsPost from '../../grids/TMBlogsPost';
|
|
9
|
-
|
|
9
|
+
import TMSpinner from '../../base/TMSpinner';
|
|
10
|
+
import { TMExceptionBoxManager } from '../../base/TMPopUp';
|
|
11
|
+
const TMDcmtBlog = ({ tid, did, isVisible, fetchBlogDataTrigger, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers }) => {
|
|
12
|
+
const [blogsDatasource, setBlogsDatasource] = useState([]);
|
|
13
|
+
const [hasLoadedDataOnce, setHasLoadedDataOnce] = useState(false); //traccia se *qualsiasi* dato è stato caricato per la prima volta
|
|
14
|
+
const [lastLoadedDid, setLastLoadedDid] = useState(undefined); // `lastLoadedDid` tiene traccia dell'ultimo `did` per cui abbiamo caricato i dati
|
|
10
15
|
// State to manage show comment form selected file
|
|
11
16
|
const [showCommentForm, setShowCommentForm] = useState(false);
|
|
12
17
|
const [externalBlogPost, setExternalBlogPost] = useState(undefined);
|
|
18
|
+
const fetchBlogDataAsync = useCallback(async (tid, did) => {
|
|
19
|
+
try {
|
|
20
|
+
TMSpinner.show({ description: 'Caricamento - Bacheca...' });
|
|
21
|
+
const res = await SDK_Globals.tmSession?.NewSearchEngine().BlogRetrieveAsync(tid, did);
|
|
22
|
+
setBlogsDatasource(res ?? []);
|
|
23
|
+
setHasLoadedDataOnce(true);
|
|
24
|
+
setLastLoadedDid(did);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
let err = e;
|
|
28
|
+
TMExceptionBoxManager.show({ exception: err });
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
TMSpinner.hide();
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
13
34
|
const showCommentFormCallback = useCallback(() => {
|
|
14
35
|
setShowCommentForm(true);
|
|
15
36
|
}, []);
|
|
37
|
+
// useEffect per triggerare il fetch dall'esterno tramite props
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (fetchBlogDataTrigger !== undefined && fetchBlogDataTrigger > 0) {
|
|
40
|
+
fetchBlogDataAsync(tid, did);
|
|
41
|
+
}
|
|
42
|
+
}, [fetchBlogDataTrigger, fetchBlogDataAsync, tid, did]);
|
|
16
43
|
useEffect(() => {
|
|
17
44
|
if (!tid || !did) {
|
|
18
45
|
setBlogsDatasource([]);
|
|
@@ -21,7 +48,7 @@ const TMDcmtBlog = ({ blogsDatasource, setBlogsDatasource, hasLoadedDataOnce, se
|
|
|
21
48
|
}
|
|
22
49
|
// Condizione per eseguire il fetch:
|
|
23
50
|
// 1. Il pannello è visibile
|
|
24
|
-
// 2. E (non abbiamo ancora caricato dati
|
|
51
|
+
// 2. E (non abbiamo ancora caricato dati o il `did` è cambiato rispetto all'ultima volta)
|
|
25
52
|
const shouldFetch = isVisible && (!hasLoadedDataOnce || did !== lastLoadedDid);
|
|
26
53
|
// Esegui la chiamata API solo se il pannello è visibile E i dati non sono già stati caricati
|
|
27
54
|
// O, se vuoi ricaricare ogni volta che diventa visibile (ma è meno efficiente per "pesante")
|
|
@@ -46,6 +46,7 @@ interface ITMDcmtFormProps {
|
|
|
46
46
|
mid: number;
|
|
47
47
|
value: string;
|
|
48
48
|
}>;
|
|
49
|
+
openS4TViewer?: boolean;
|
|
49
50
|
onOpenS4TViewerRequest?: (dcmtInfo: Array<DcmtInfo>, onRefreshSearchAsync?: (() => Promise<void>)) => void;
|
|
50
51
|
s4TViewerDialogComponent?: React.ReactNode;
|
|
51
52
|
enableDragDropOverlay?: boolean;
|