@topconsultnpm/sdkui-react 6.19.0-dev1.9 → 6.19.0-dev2.1
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/Styled.d.ts +1 -0
- package/lib/components/base/Styled.js +40 -0
- package/lib/components/base/TMCustomButton.d.ts +11 -0
- package/lib/components/base/TMCustomButton.js +63 -0
- package/lib/components/base/TMFileManagerDataGridView.js +4 -1
- package/lib/components/base/TMLayout.d.ts +2 -1
- package/lib/components/base/TMLayout.js +2 -2
- package/lib/components/base/TMPopUp.js +5 -18
- package/lib/components/base/TMTreeView.js +3 -2
- package/lib/components/editors/TMHtmlEditor.d.ts +5 -0
- package/lib/components/editors/TMHtmlEditor.js +72 -12
- package/lib/components/editors/TMMetadataValues.js +90 -40
- package/lib/components/features/archive/TMArchive.d.ts +10 -0
- package/lib/components/features/archive/TMArchive.js +56 -25
- package/lib/components/features/blog/TMBlogCommentForm.d.ts +4 -4
- package/lib/components/features/blog/TMBlogCommentForm.js +76 -51
- package/lib/components/features/documents/TMDcmtBlog.d.ts +15 -0
- package/lib/components/features/documents/TMDcmtBlog.js +21 -33
- package/lib/components/features/documents/TMDcmtForm.d.ts +17 -3
- package/lib/components/features/documents/TMDcmtForm.js +205 -46
- package/lib/components/features/documents/TMDcmtTasks.d.ts +13 -0
- package/lib/components/features/documents/TMDcmtTasks.js +24 -0
- package/lib/components/features/documents/TMDragDropOverlay.js +2 -1
- package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +8 -1
- package/lib/components/features/documents/TMMasterDetailDcmts.js +6 -6
- package/lib/components/features/documents/TMRelationViewer.d.ts +53 -3
- package/lib/components/features/documents/TMRelationViewer.js +232 -85
- package/lib/components/features/search/TMSearch.d.ts +10 -1
- package/lib/components/features/search/TMSearch.js +14 -5
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
- package/lib/components/features/search/TMSearchQueryPanel.js +36 -7
- package/lib/components/features/search/TMSearchResult.d.ts +10 -1
- package/lib/components/features/search/TMSearchResult.js +140 -422
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +2 -2
- package/lib/components/features/search/TMSearchResultsMenuItems.js +33 -8
- package/lib/components/features/tasks/TMTaskForm.d.ts +38 -0
- package/lib/components/features/tasks/TMTaskForm.js +386 -0
- package/lib/components/features/tasks/TMTasksAgenda.d.ts +17 -0
- package/lib/components/features/tasks/TMTasksAgenda.js +107 -0
- package/lib/components/features/tasks/TMTasksCalendar.d.ts +21 -0
- package/lib/components/features/tasks/TMTasksCalendar.js +240 -0
- package/lib/components/features/tasks/TMTasksHeader.d.ts +14 -0
- package/lib/components/features/tasks/TMTasksHeader.js +37 -0
- package/lib/components/features/tasks/TMTasksPanelContent.d.ts +20 -0
- package/lib/components/features/tasks/TMTasksPanelContent.js +65 -0
- package/lib/components/features/tasks/TMTasksUtils.d.ts +132 -0
- package/lib/components/features/tasks/TMTasksUtils.js +634 -0
- package/lib/components/features/tasks/TMTasksUtilsView.d.ts +39 -0
- package/lib/components/features/tasks/TMTasksUtilsView.js +118 -0
- package/lib/components/features/tasks/TMTasksView.d.ts +40 -0
- package/lib/components/features/tasks/TMTasksView.js +560 -0
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +3 -1
- package/lib/components/features/workflow/TMWorkflowPopup.js +19 -6
- package/lib/components/features/workflow/diagram/RecipientList.js +4 -3
- package/lib/components/forms/Login/Chooser.js +1 -1
- package/lib/components/forms/TMChooserForm.d.ts +1 -1
- package/lib/components/forms/TMChooserForm.js +2 -2
- package/lib/components/grids/TMBlogAttachments.d.ts +42 -0
- package/lib/components/grids/TMBlogAttachments.js +43 -0
- package/lib/components/grids/TMBlogHeader.d.ts +31 -0
- package/lib/components/grids/TMBlogHeader.js +41 -0
- package/lib/components/grids/{TMBlogs.d.ts → TMBlogsPost.d.ts} +42 -58
- package/lib/components/grids/TMBlogsPost.js +628 -0
- package/lib/components/grids/{TMBlogsUtils.d.ts → TMBlogsPostUtils.d.ts} +61 -47
- package/lib/components/grids/{TMBlogsUtils.js → TMBlogsPostUtils.js} +146 -124
- package/lib/components/index.d.ts +14 -1
- package/lib/components/index.js +15 -1
- package/lib/components/layout/panelManager/TMPanelManagerContext.js +7 -0
- package/lib/components/viewers/TMTidViewer.js +20 -2
- package/lib/css/tm-sdkui.css +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +2 -1
- package/lib/helper/SDKUI_Globals.js +1 -0
- package/lib/helper/SDKUI_Localizator.d.ts +62 -4
- package/lib/helper/SDKUI_Localizator.js +618 -25
- package/lib/helper/TMCustomSearchBar.d.ts +8 -0
- package/lib/helper/TMCustomSearchBar.js +54 -0
- package/lib/helper/TMIcons.d.ts +2 -0
- package/lib/helper/TMIcons.js +6 -0
- package/lib/helper/TMImageLibrary.d.ts +3 -2
- package/lib/helper/TMImageLibrary.js +230 -230
- package/lib/helper/TMToppyMessage.d.ts +7 -0
- package/lib/helper/TMToppyMessage.js +42 -0
- package/lib/helper/TMUtils.d.ts +10 -1
- package/lib/helper/TMUtils.js +42 -1
- package/lib/helper/dcmtsHelper.d.ts +2 -0
- package/lib/helper/dcmtsHelper.js +18 -0
- package/lib/helper/helpers.js +1 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/hooks/useRelatedDocuments.d.ts +72 -0
- package/lib/hooks/useRelatedDocuments.js +655 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/ts/types.d.ts +14 -0
- package/lib/ts/types.js +15 -0
- package/lib/utils/theme.d.ts +1 -0
- package/lib/utils/theme.js +1 -0
- package/package.json +7 -7
- package/lib/components/grids/TMBlogs.js +0 -721
- package/lib/stories/TMButton.stories.d.ts +0 -4
- package/lib/stories/TMButton.stories.js +0 -29
- package/lib/stories/TMDataGrid.stories.d.ts +0 -9
- package/lib/stories/TMDataGrid.stories.js +0 -310
- package/lib/stories/TMHtmlContentDisplay.stories.d.ts +0 -6
- package/lib/stories/TMHtmlContentDisplay.stories.js +0 -45
- package/lib/stories/TMHtmlEditor.stories.d.ts +0 -6
- package/lib/stories/TMHtmlEditor.stories.js +0 -49
- package/lib/stories/TMIcons.stories.d.ts +0 -4
- package/lib/stories/TMIcons.stories.js +0 -13
- package/lib/stories/TMSDKUI_Localizator.stories.d.ts +0 -4
- package/lib/stories/TMSDKUI_Localizator.stories.js +0 -123
- package/lib/stories/TMStoriesUtils.d.ts +0 -1
- package/lib/stories/TMStoriesUtils.js +0 -10
- package/lib/stories/TMUserAvatar.stories.d.ts +0 -6
- package/lib/stories/TMUserAvatar.stories.js +0 -20
|
@@ -7,6 +7,7 @@ export declare const StyledMultiViewPanel: import("styled-components/dist/types"
|
|
|
7
7
|
}>> & string;
|
|
8
8
|
export declare const StyledParagraph: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, never>> & string;
|
|
9
9
|
export declare const StyledToolbarForm: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
10
|
+
export declare const StyledReferenceButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
|
|
10
11
|
export declare const StyledPanelPage: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
11
12
|
$isOpen?: boolean;
|
|
12
13
|
$padding?: string;
|
|
@@ -36,6 +36,46 @@ export const StyledToolbarForm = styled.div `
|
|
|
36
36
|
gap: 2px;
|
|
37
37
|
background-color: ${TMColors.toolbar_background};
|
|
38
38
|
`;
|
|
39
|
+
export const StyledReferenceButton = styled.button `
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
padding: 10px 16px;
|
|
45
|
+
border-radius: 20px;
|
|
46
|
+
border: none;
|
|
47
|
+
background-color: ${TMColors.button_floating_background};
|
|
48
|
+
color: white;
|
|
49
|
+
font-size: 0.9rem;
|
|
50
|
+
font-weight: 500;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
transition: all 0.2s ease;
|
|
53
|
+
text-align: center;
|
|
54
|
+
max-width: 200px;
|
|
55
|
+
gap: 2px;
|
|
56
|
+
|
|
57
|
+
&:hover {
|
|
58
|
+
opacity: 0.9;
|
|
59
|
+
transform: translateY(-1px);
|
|
60
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&:active {
|
|
64
|
+
transform: translateY(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&:disabled {
|
|
68
|
+
opacity: 0.5;
|
|
69
|
+
cursor: not-allowed;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
span {
|
|
73
|
+
width: 100%;
|
|
74
|
+
white-space: nowrap;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
text-overflow: ellipsis;
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
39
79
|
export const StyledPanelPage = styled.div `
|
|
40
80
|
position: absolute;
|
|
41
81
|
width: calc(100% - 50px);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { LayoutCustomButtonDescriptor } from '@topconsultnpm/sdk-ts';
|
|
2
|
+
import { MetadataValueDescriptorEx } from '../..';
|
|
3
|
+
type TMCustomButtonProps = {
|
|
4
|
+
button: LayoutCustomButtonDescriptor;
|
|
5
|
+
isModal?: boolean;
|
|
6
|
+
formData?: MetadataValueDescriptorEx[];
|
|
7
|
+
selectedItems?: Array<any>;
|
|
8
|
+
onClose?: () => void;
|
|
9
|
+
};
|
|
10
|
+
declare const TMCustomButton: (props: TMCustomButtonProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export default TMCustomButton;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SDK_Globals } from '@topconsultnpm/sdk-ts';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import TMModal from './TMModal';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import { processButtonAttributes } from '../../helper/dcmtsHelper';
|
|
7
|
+
const IframeContainer = styled.div `
|
|
8
|
+
display: flex;
|
|
9
|
+
height: 100%;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
`;
|
|
12
|
+
const StyledIframe = styled.iframe `
|
|
13
|
+
border: none;
|
|
14
|
+
flex: 1;
|
|
15
|
+
`;
|
|
16
|
+
const TMCustomButton = (props) => {
|
|
17
|
+
const { button, isModal = true, formData, selectedItems, onClose } = props;
|
|
18
|
+
const { appName: scriptUrl, arguments: args } = button;
|
|
19
|
+
const iframeRef = useRef(null);
|
|
20
|
+
const attributes = processButtonAttributes(args, formData);
|
|
21
|
+
const [loading, setLoading] = useState(true);
|
|
22
|
+
const [error, setError] = useState(false);
|
|
23
|
+
const getTargetOrigin = (url) => {
|
|
24
|
+
if (!url)
|
|
25
|
+
return '*';
|
|
26
|
+
try {
|
|
27
|
+
const urlObj = new URL(url);
|
|
28
|
+
return urlObj.origin;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return '*';
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (iframeRef.current?.contentWindow) {
|
|
36
|
+
const mergedAttributes = { ...attributes, selectedItems: selectedItems };
|
|
37
|
+
iframeRef.current.contentWindow.postMessage({
|
|
38
|
+
"options": mergedAttributes,
|
|
39
|
+
"selectedItems": selectedItems,
|
|
40
|
+
"session": SDK_Globals.tmSession
|
|
41
|
+
}, getTargetOrigin(scriptUrl));
|
|
42
|
+
}
|
|
43
|
+
clearTimeout(timeoutIframe);
|
|
44
|
+
}, [loading, error, scriptUrl, attributes]);
|
|
45
|
+
const handleLoad = () => setLoading(false);
|
|
46
|
+
const handleError = () => {
|
|
47
|
+
setLoading(false);
|
|
48
|
+
setError(true);
|
|
49
|
+
};
|
|
50
|
+
// Timeout di sicurezza nel caso l'evento 'error' non venga chiamato
|
|
51
|
+
const timeoutIframe = setTimeout(() => {
|
|
52
|
+
if (loading)
|
|
53
|
+
handleError();
|
|
54
|
+
}, 5000); // 5 secondi
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!isModal && scriptUrl) {
|
|
57
|
+
window.open(scriptUrl, '_blank');
|
|
58
|
+
onClose?.();
|
|
59
|
+
}
|
|
60
|
+
}, [isModal, scriptUrl, onClose]);
|
|
61
|
+
return isModal ? (_jsx(TMModal, { title: button.title, width: '60%', height: '60%', resizable: true, onClose: onClose, children: _jsxs(IframeContainer, { children: [error && _jsx("div", { children: "Si \u00E8 verificato un errore nel caricamento del contenuto." }), !error && _jsx(StyledIframe, { ref: iframeRef, loading: 'lazy', onLoad: handleLoad, onError: handleError, src: scriptUrl })] }) })) : null;
|
|
62
|
+
};
|
|
63
|
+
export default TMCustomButton;
|
|
@@ -50,7 +50,8 @@ const TMFileManagerDataGridView = (props) => {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
const cellDefaultRender = useCallback((cellData) => {
|
|
53
|
-
|
|
53
|
+
const value = cellData.value ?? '';
|
|
54
|
+
return renderHighlightedText(value.toString(), searchText, false);
|
|
54
55
|
}, [searchText]);
|
|
55
56
|
const cellNameRender = useCallback((cellData) => {
|
|
56
57
|
const { checkoutDate, checkOutUserID, checkOutUserName, version, ext, creationTime, lastUpdateTime } = cellData.data;
|
|
@@ -66,6 +67,8 @@ const TMFileManagerDataGridView = (props) => {
|
|
|
66
67
|
return _jsx("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' }, children: _jsx(TMDcmtIcon, { tid: item.tid, did: item.did, fileExtension: item.ext, downloadMode: 'openInNewWindow', tooltipContent: tooltipContent }) });
|
|
67
68
|
}, []);
|
|
68
69
|
const cellDatetimeRender = useCallback((cellData) => {
|
|
70
|
+
if (!cellData.value)
|
|
71
|
+
return '-';
|
|
69
72
|
return renderHighlightedText(Globalization.getDateTimeDisplayValue(cellData.value).toString(), searchText, false);
|
|
70
73
|
}, [searchText]);
|
|
71
74
|
const cellSizeRender = useCallback((cellData) => {
|
|
@@ -48,7 +48,8 @@ export interface ITMLayoutContainerProps {
|
|
|
48
48
|
alignItems?: string;
|
|
49
49
|
direction?: 'vertical' | 'horizontal';
|
|
50
50
|
onClick?: () => void;
|
|
51
|
+
onContextMenu?: (e: React.MouseEvent) => void;
|
|
51
52
|
}
|
|
52
|
-
declare const TMLayoutContainer: ({ gap, onClick, justifyContent, alignItems, children, direction }: ITMLayoutContainerProps) => import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
declare const TMLayoutContainer: ({ gap, onClick, justifyContent, alignItems, children, direction, onContextMenu }: ITMLayoutContainerProps) => import("react/jsx-runtime").JSX.Element;
|
|
53
54
|
export { TMCard, TMLayoutItem, TMSplitterLayout };
|
|
54
55
|
export default TMLayoutContainer;
|
|
@@ -193,9 +193,9 @@ const TMSplitterLayout = ({ animation = false, showSeparator = true, separatorCo
|
|
|
193
193
|
const TMLayoutItem = ({ onClick, children, width = '100%', minWidth, maxWidth, maxHeight, height = '100%', minHeight }) => {
|
|
194
194
|
return (_jsx(StyledLayoutItem, { onClick: onClick, "$height": height, "$maxHeight": maxHeight, "$minHeight": minHeight, "$width": width, "$minWidth": minWidth, "$maxWidth": maxWidth, children: children }));
|
|
195
195
|
};
|
|
196
|
-
const TMLayoutContainer = ({ gap = 3, onClick, justifyContent = 'flex-start', alignItems = 'flex-start', children, direction = 'vertical' }) => {
|
|
196
|
+
const TMLayoutContainer = ({ gap = 3, onClick, justifyContent = 'flex-start', alignItems = 'flex-start', children, direction = 'vertical', onContextMenu }) => {
|
|
197
197
|
const renderedEls = () => { return (React.Children.map(children, child => (child))); };
|
|
198
|
-
return (_jsxs(StyledLayoutContainer, { "$alignItems": alignItems, "$justifyContent": justifyContent, onClick: onClick, "$direction": direction, style: { gap: gap }, children: [" ", renderedEls(), " "] }));
|
|
198
|
+
return (_jsxs(StyledLayoutContainer, { "$alignItems": alignItems, "$justifyContent": justifyContent, onClick: onClick, onContextMenu: onContextMenu, "$direction": direction, style: { gap: gap }, children: [" ", renderedEls(), " "] }));
|
|
199
199
|
};
|
|
200
200
|
export { TMCard, TMLayoutItem, TMSplitterLayout };
|
|
201
201
|
export default TMLayoutContainer;
|
|
@@ -152,24 +152,11 @@ const ResponsiveMessageText = styled.div `
|
|
|
152
152
|
line-height: 1.1;
|
|
153
153
|
}
|
|
154
154
|
`;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@media (max-width: 200px) {
|
|
162
|
-
padding: 2px 4px !important;
|
|
163
|
-
min-width: 25px !important;
|
|
164
|
-
font-size: 8px !important;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
@media (max-width: 150px) {
|
|
168
|
-
padding: 1px 3px !important;
|
|
169
|
-
min-width: 20px !important;
|
|
170
|
-
font-size: 7px !important;
|
|
171
|
-
}
|
|
172
|
-
`;
|
|
155
|
+
// ResponsiveButton wrapper component to avoid circular dependency
|
|
156
|
+
const ResponsiveButton = (props) => {
|
|
157
|
+
const { fontSize: customFontSize, ...otherProps } = props;
|
|
158
|
+
return _jsx(TMButton, { fontSize: customFontSize || 'clamp(12px, 2vw, 1.1rem)', padding: "clamp(2px, 1vw, 8px) clamp(4px, 2vw, 12px)", width: "clamp(30px, 10vw, 60px)", ...otherProps });
|
|
159
|
+
};
|
|
173
160
|
const ResponsiveMessageBody = ({ message, isMobile, MessageToolbar, showToppy }) => {
|
|
174
161
|
return (_jsxs(ResponsiveMessageContainer, { children: [_jsxs(ResponsiveMessageContent, { "$isMobile": isMobile, children: [showToppy && _jsx(ResponsiveToppyImage, { "$isMobile": isMobile, src: toppy, alt: "Toppy" }), _jsx(ResponsiveMessageText, { "$isMobile": isMobile, children: typeof message === 'string' ? _jsx(Message, { msg: message }) : message })] }), _jsx(MessageToolbar, {})] }));
|
|
175
162
|
};
|
|
@@ -332,15 +332,16 @@ const TMTreeView = ({ dataSource = [], focusedItem, selectedItems = [], allowMul
|
|
|
332
332
|
if (input) {
|
|
333
333
|
input.indeterminate = isIndeterminate(node);
|
|
334
334
|
}
|
|
335
|
-
} })), _jsx("div", { style: { display: 'flex', alignItems: 'center', flex: 1,
|
|
335
|
+
} })), _jsx("div", { style: { display: 'flex', alignItems: 'center', flex: 1, minWidth: 0 }, onClick: (e) => { handleNodeClick(node, e); }, children: itemRender(node) })] }), node.expanded && node.items && (_jsxs("div", { style: { paddingLeft: 20, width: '100%' }, children: [renderTree(getVisibleItems(node)), needsLoadMoreButton(node) && (_jsx("div", { style: { display: 'flex', justifyContent: 'center', margin: '5px 0' }, children: _jsx(TMButton, { onClick: () => handleLoadMoreItems(node.key), showTooltip: false, caption: `Carica altri ${itemsPerPage}... (${node.visibleItemsCount ?? itemsPerPage} / ${node.items?.length ?? 0})` }) }))] }))] }, node.key)));
|
|
336
336
|
}, [handleNodeClick, handleNodeToggle, handleCheckboxChange, focusedItem, selectedItems, allowMultipleSelection, getVisibleItems, needsLoadMoreButton, handleLoadMoreItems, itemsPerPage]);
|
|
337
|
-
return (_jsx("div", { style: { height: '100%', width: '100%', overflowY: 'auto', padding: '0px 5px 2px 2px' }, children: renderTree(dataSource) }));
|
|
337
|
+
return (_jsx("div", { style: { height: '100%', width: '100%', overflowY: 'auto', overflowX: 'hidden', padding: '0px 5px 2px 2px' }, children: renderTree(dataSource) }));
|
|
338
338
|
};
|
|
339
339
|
export default TMTreeView;
|
|
340
340
|
export const StyledTreeNode = styled.div `
|
|
341
341
|
display: flex;
|
|
342
342
|
flex-direction: row;
|
|
343
343
|
width: 100%;
|
|
344
|
+
min-width: 0;
|
|
344
345
|
min-height: 22px;
|
|
345
346
|
max-height: 30px;
|
|
346
347
|
gap: 5px;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ValidationItem } from '@topconsultnpm/sdk-ts';
|
|
2
|
+
export declare const sanitizeAndFormatComment: (raw?: string) => string;
|
|
2
3
|
export interface ITMHtmlEditor {
|
|
3
4
|
/** Width of the editor (e.g., '100%', '500px') */
|
|
4
5
|
width?: string;
|
|
@@ -28,6 +29,10 @@ export interface ITMHtmlEditor {
|
|
|
28
29
|
toolbarMode?: 'compact' | 'expanded';
|
|
29
30
|
/** If true, the editor will be focused on mount */
|
|
30
31
|
autoFocus?: boolean;
|
|
32
|
+
/** Maximum number of characters allowed in the editor */
|
|
33
|
+
maxLength?: number;
|
|
34
|
+
/** If true, displays a character count below the editor */
|
|
35
|
+
showCount?: boolean;
|
|
31
36
|
}
|
|
32
37
|
declare const TMHtmlEditor: (props: ITMHtmlEditor) => import("react/jsx-runtime").JSX.Element;
|
|
33
38
|
export default TMHtmlEditor;
|
|
@@ -4,8 +4,35 @@ import HtmlEditor, { Toolbar, Item } from 'devextreme-react/html-editor';
|
|
|
4
4
|
import ReactDOM from 'react-dom';
|
|
5
5
|
import { SDKUI_Localizator } from '../../helper';
|
|
6
6
|
import TMVilViewer from '../base/TMVilViewer';
|
|
7
|
+
import { TMMessageBoxManager, ButtonNames } from '../base/TMPopUp';
|
|
8
|
+
import TMTooltip from '../base/TMTooltip';
|
|
9
|
+
import { useDeviceType, DeviceType } from '../base/TMDeviceProvider';
|
|
10
|
+
export const sanitizeAndFormatComment = (raw = "") => {
|
|
11
|
+
if (!raw)
|
|
12
|
+
return "";
|
|
13
|
+
let cleanComment = raw
|
|
14
|
+
// Simplify dx-mention markup - replace with just @username
|
|
15
|
+
.replace(/<span class="dx-mention"[^>]*>\uFEFF?<span[^>]*><span>@<\/span>([^<]+)<\/span>\uFEFF?<\/span>/gi, '@$1')
|
|
16
|
+
// Replace </p> with '' only if followed by <ol> or <ul>
|
|
17
|
+
.replace(/<\/p>(?=\s*<(ol|ul)>)/gi, '')
|
|
18
|
+
// Replace all other </p> with '\r\n'
|
|
19
|
+
.replace(/<\/p>/gi, '\r\n')
|
|
20
|
+
// Remove all <p> tags
|
|
21
|
+
.replace(/<p>/gi, '')
|
|
22
|
+
// Remove all <br> tags
|
|
23
|
+
.replace(/<br>/gi, '')
|
|
24
|
+
// Trim whitespace
|
|
25
|
+
.trim();
|
|
26
|
+
// Remove dangerous HTML elements
|
|
27
|
+
cleanComment = cleanComment.replace(/<(script|iframe|embed|object|link|style|img|video|audio|svg|form|input|button|textarea|select|pre|function)[^>]*>/gi, '');
|
|
28
|
+
return cleanComment;
|
|
29
|
+
};
|
|
7
30
|
const TMHtmlEditor = (props) => {
|
|
8
|
-
const { width = "100%", height = "100%", initialMarkup = "", mentionsConfig, onValueChanged, validationItems = [], isEditorEnabled: isEditorEnabledProp = false, toolbarMode = 'compact', autoFocus = false } = props;
|
|
31
|
+
const { width = "100%", height = "100%", initialMarkup = "", mentionsConfig, onValueChanged, validationItems = [], isEditorEnabled: isEditorEnabledProp = false, toolbarMode = 'compact', autoFocus = false, maxLength = 1000, showCount = true, } = props;
|
|
32
|
+
// Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
|
|
33
|
+
const deviceType = useDeviceType();
|
|
34
|
+
// This avoids unnecessary re-renders by only recalculating when deviceType changes.
|
|
35
|
+
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
|
9
36
|
// Ref for HtmlEditor instance
|
|
10
37
|
const editorRef = useRef(null);
|
|
11
38
|
// State for editor enable/disable
|
|
@@ -14,13 +41,15 @@ const TMHtmlEditor = (props) => {
|
|
|
14
41
|
const [markup, setMarkup] = useState(initialMarkup);
|
|
15
42
|
// State to track the position (x, y coordinates) where the custom context menu should be displayed.
|
|
16
43
|
const [contextMenuPos, setContextMenuPos] = useState(null);
|
|
17
|
-
// State to
|
|
18
|
-
const [
|
|
44
|
+
// State to track remaining characters
|
|
45
|
+
const [charactersRemaining, setCharactersRemaining] = useState(maxLength - initialMarkup.length);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const cleanedMarkup = sanitizeAndFormatComment(markup);
|
|
48
|
+
setCharactersRemaining(maxLength - cleanedMarkup.length);
|
|
49
|
+
}, [markup, maxLength]);
|
|
19
50
|
// Function to read text from the clipboard, format it, and paste into the Quill editor
|
|
20
51
|
const pasteFromClipboard = async () => {
|
|
21
52
|
try {
|
|
22
|
-
// Set flag to indicate paste operation is in progress
|
|
23
|
-
setIsPasting(true);
|
|
24
53
|
// Read plain text from the clipboard asynchronously
|
|
25
54
|
let text = await navigator.clipboard.readText();
|
|
26
55
|
text = '<p>' + text.replace(/\r\n/g, '</p><p>') + '</p>';
|
|
@@ -52,10 +81,6 @@ const TMHtmlEditor = (props) => {
|
|
|
52
81
|
// Log any errors that might occur during clipboard access or pasting
|
|
53
82
|
console.warn('Clipboard paste failed:', err);
|
|
54
83
|
}
|
|
55
|
-
finally {
|
|
56
|
-
// Reset paste state after a short delay to restore UI state
|
|
57
|
-
setTimeout(() => setIsPasting(false), 500);
|
|
58
|
-
}
|
|
59
84
|
};
|
|
60
85
|
useEffect(() => {
|
|
61
86
|
const handleKeyDown = (e) => {
|
|
@@ -116,7 +141,7 @@ const TMHtmlEditor = (props) => {
|
|
|
116
141
|
}, [autoFocus]);
|
|
117
142
|
// Memoized check for validation errors
|
|
118
143
|
const hasValidationErrors = useMemo(() => {
|
|
119
|
-
return
|
|
144
|
+
return validationItems && validationItems.length > 0;
|
|
120
145
|
}, [validationItems]);
|
|
121
146
|
// Handler function triggered by the context menu's "Paste" action
|
|
122
147
|
const handlePaste = async () => {
|
|
@@ -125,9 +150,44 @@ const TMHtmlEditor = (props) => {
|
|
|
125
150
|
// Close the custom context menu after pasting
|
|
126
151
|
setContextMenuPos(null);
|
|
127
152
|
};
|
|
128
|
-
return (_jsxs("div", { style: {
|
|
153
|
+
return (_jsxs("div", { style: {
|
|
154
|
+
height: height,
|
|
155
|
+
width: width
|
|
156
|
+
}, children: [_jsx("div", { className: "custom-mentions-wrapper", onContextMenu: handleContextMenu, style: {
|
|
157
|
+
borderWidth: '1px',
|
|
158
|
+
borderStyle: 'solid',
|
|
159
|
+
borderColor: hasValidationErrors ? "red" : "#e0e0e0 #e0e0e0 #616161",
|
|
160
|
+
width: "100%",
|
|
161
|
+
height: `calc(100% - ${showCount ? 15 : 0}px - ${validationItems.length > 0 ? 15 : 0}px)`,
|
|
162
|
+
}, children: _jsx(HtmlEditor, { ref: editorRef, placeholder: SDKUI_Localizator.TypeAMessage + "...", width: "100%", height: "100%", value: markup, onValueChange: onValueChangeCallback, mentions: mentionsConfig, style: { overflow: 'hidden', outline: "none", fontSize: '1rem' }, children: isEditorEnabled && (toolbarMode === 'compact' ?
|
|
129
163
|
_jsxs(Toolbar, { multiline: false, children: [_jsx(Item, { name: "undo" }), _jsx(Item, { name: "redo" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "bold" }), _jsx(Item, { name: "italic" }), _jsx(Item, { name: "strike" }), _jsx(Item, { name: "underline" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "orderedList" }), _jsx(Item, { name: "bulletList" })] }) :
|
|
130
|
-
_jsxs(Toolbar, { children: [_jsx(Item, { name: "undo" }), _jsx(Item, { name: "redo" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "bold" }), _jsx(Item, { name: "italic" }), _jsx(Item, { name: "strike" }), _jsx(Item, { name: "underline" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "alignLeft" }), _jsx(Item, { name: "alignCenter" }), _jsx(Item, { name: "alignRight" }), _jsx(Item, { name: "alignJustify" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "color" }), _jsx(Item, { name: "background" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "link" }), _jsx(Item, { name: "image" }), _jsx(Item, { name: "separator" })] })) }) }),
|
|
164
|
+
_jsxs(Toolbar, { children: [_jsx(Item, { name: "undo" }), _jsx(Item, { name: "redo" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "bold" }), _jsx(Item, { name: "italic" }), _jsx(Item, { name: "strike" }), _jsx(Item, { name: "underline" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "alignLeft" }), _jsx(Item, { name: "alignCenter" }), _jsx(Item, { name: "alignRight" }), _jsx(Item, { name: "alignJustify" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "color" }), _jsx(Item, { name: "background" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "link" }), _jsx(Item, { name: "image" }), _jsx(Item, { name: "separator" })] })) }) }), showCount ? ((() => {
|
|
165
|
+
const cleanedMarkup = sanitizeAndFormatComment(markup);
|
|
166
|
+
const showInfoIcon = charactersRemaining !== maxLength;
|
|
167
|
+
return (_jsxs("div", { style: {
|
|
168
|
+
display: 'flex',
|
|
169
|
+
alignItems: 'center',
|
|
170
|
+
justifyContent: 'flex-end',
|
|
171
|
+
fontSize: 12,
|
|
172
|
+
color: '#6c757d',
|
|
173
|
+
marginTop: 4,
|
|
174
|
+
gap: 4,
|
|
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
|
+
TMMessageBoxManager.show({
|
|
177
|
+
title: 'Markup HTML',
|
|
178
|
+
initialWidth: !isMobile ? '700px' : undefined,
|
|
179
|
+
message: (_jsx("pre", { style: {
|
|
180
|
+
whiteSpace: 'pre-wrap',
|
|
181
|
+
background: '#f5f5f5',
|
|
182
|
+
padding: '12px',
|
|
183
|
+
borderRadius: '6px',
|
|
184
|
+
}, children: cleanedMarkup })),
|
|
185
|
+
resizable: true,
|
|
186
|
+
showToppy: false,
|
|
187
|
+
buttons: [ButtonNames.OK],
|
|
188
|
+
});
|
|
189
|
+
} }) }))] }));
|
|
190
|
+
})()) : null, validationItems.length > 0 && _jsx(TMVilViewer, { vil: validationItems }), contextMenuPos &&
|
|
131
191
|
ReactDOM.createPortal(_jsxs("div", { style: {
|
|
132
192
|
position: 'fixed',
|
|
133
193
|
top: contextMenuPos.y,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useEffect, useState } from "react";
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import styled from "styled-components";
|
|
4
4
|
import { AccessLevels, DcmtTypeListCacheService, LayoutItemTypes, LayoutModes, MetadataDataDomains, MetadataDataTypes, SDK_Globals, SystemMIDsAsNumber, SystemTIDs, TemplateTIDs, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
|
|
5
5
|
import { IconUndo, IconPencil, IconFunction, IconMenuVertical, IconDataList, SDKUI_Localizator, IconNull, stringIsNullOrEmpty, deepCompare, SDKUI_Globals, IconDcmtTypeSys } from "../../helper";
|
|
@@ -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 } from "../../ts";
|
|
13
|
+
import { DraftsMIDs, DSAttachsMIDs } from "../../ts";
|
|
14
14
|
import { TMNothingToShow } from "../features/documents/TMDcmtPreview";
|
|
15
15
|
import TMAccordion from "../base/TMAccordion";
|
|
16
16
|
export var ShowCheckBoxesMode;
|
|
@@ -33,7 +33,7 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
33
33
|
const [selectedItem, setSelectedItem] = useState(undefined);
|
|
34
34
|
const [prevMetadataValues, setPrevMetadataValues] = useState([]);
|
|
35
35
|
const [inputMidsApplied, setInputMidsApplied] = useState(false);
|
|
36
|
-
const onChangeHandler = (newValue, mid) => {
|
|
36
|
+
const onChangeHandler = useCallback((newValue, mid) => {
|
|
37
37
|
let newValues = structuredClone(metadataValues);
|
|
38
38
|
const item = newValues.find(value => value.mid === mid);
|
|
39
39
|
if (item) {
|
|
@@ -51,25 +51,25 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
onValueChanged?.(newValues);
|
|
54
|
-
};
|
|
55
|
-
const editorValidationHandler = (mid) => {
|
|
54
|
+
}, [metadataValues, showCheckBoxes, dynDataListsToBeRefreshed, onValueChanged]);
|
|
55
|
+
const editorValidationHandler = useCallback((mid) => {
|
|
56
56
|
const md = metadataValues?.find(m => m.mid === mid);
|
|
57
57
|
const validationItem = validationItems.find(vil => vil.PropertyName === md?.md?.nameLoc);
|
|
58
58
|
return validationItem ? [validationItem] : [];
|
|
59
|
-
};
|
|
60
|
-
const isEditable = (mid) => {
|
|
59
|
+
}, [metadataValues, validationItems]);
|
|
60
|
+
const isEditable = useCallback((mid) => {
|
|
61
61
|
let md = currentDTD?.metadata?.find(o => o.id == mid);
|
|
62
62
|
if (!md)
|
|
63
63
|
return false;
|
|
64
64
|
let isList = md.dataDomain == MetadataDataDomains.DataList || md.dataDomain == MetadataDataDomains.DynamicDataList || md.dataDomain == MetadataDataDomains.UserID;
|
|
65
65
|
return isList && isEditableList(mid);
|
|
66
|
-
};
|
|
67
|
-
const handleMetadataValueSelection = (item) => {
|
|
66
|
+
}, [currentDTD, isEditableList]);
|
|
67
|
+
const handleMetadataValueSelection = useCallback((item) => {
|
|
68
68
|
if (selectedItem?.mid !== item.mid) {
|
|
69
69
|
setSelectedItem(item);
|
|
70
70
|
onFocusedItemChanged?.(item);
|
|
71
71
|
}
|
|
72
|
-
};
|
|
72
|
+
}, [selectedItem, onFocusedItemChanged]);
|
|
73
73
|
useEffect(() => {
|
|
74
74
|
if (!TID) {
|
|
75
75
|
setCurrentDTD(undefined);
|
|
@@ -184,7 +184,7 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
184
184
|
}
|
|
185
185
|
return toBeRefreshed;
|
|
186
186
|
};
|
|
187
|
-
const getAdvancedMenuItems = (mvd) => {
|
|
187
|
+
const getAdvancedMenuItems = useCallback((mvd) => {
|
|
188
188
|
let md = mvd.md;
|
|
189
189
|
if (!md)
|
|
190
190
|
return [];
|
|
@@ -241,9 +241,9 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
241
241
|
...customMenuItems
|
|
242
242
|
];
|
|
243
243
|
return menu;
|
|
244
|
-
};
|
|
244
|
+
}, [checkPerms, isExpertMode, customMenuItems, metadataValues, onAdvancedMenuClick, onValueChanged]);
|
|
245
245
|
// Helper function to render a single metadata item
|
|
246
|
-
const renderMetadataItem = (item, isReadOnlyOverride = false) => (_jsxs(StyledRow, { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '6px' : '0', gap: '8px' }, onClick: () => { handleMetadataValueSelection(item); }, onFocus: () => { handleMetadataValueSelection(item); }, children: [showCheckBoxes !== ShowCheckBoxesMode.Never &&
|
|
246
|
+
const renderMetadataItem = useCallback((item, isReadOnlyOverride = false) => (_jsxs(StyledRow, { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '6px' : '0', gap: '8px' }, onClick: () => { handleMetadataValueSelection(item); }, onFocus: () => { handleMetadataValueSelection(item); }, children: [showCheckBoxes !== ShowCheckBoxesMode.Never &&
|
|
247
247
|
_jsx(TMCheckBox, { elementStyle: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '14px' : '20px' }, value: item.isSelected, disabled: showCheckBoxes === ShowCheckBoxesMode.AlwaysReadOnly, onValueChanged: (newValue) => {
|
|
248
248
|
let newValues = structuredClone(metadataValues);
|
|
249
249
|
const mvd = newValues.find(value => value.mid === item.mid);
|
|
@@ -296,8 +296,8 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
296
296
|
mvd.isSelected = !stringIsNullOrEmpty(mvd.value);
|
|
297
297
|
}
|
|
298
298
|
onValueChanged?.(newValues);
|
|
299
|
-
} }) }), !isReadOnly && _jsx("div", { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '12px' : '18px' }, onClick: () => { handleMetadataValueSelection(item); }, children: _jsx(TMDropDownMenu, { backgroundColor: 'white', color: TMColors.button_icon, borderRadius: '3px', content: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconMenuVertical, {}), showTooltip: false }), disabled: item.isLexProt === 1, items: getAdvancedMenuItems(item) }) })] }, item.mid));
|
|
300
|
-
const layoutWorkItem = () => {
|
|
299
|
+
} }) }), !isReadOnly && _jsx("div", { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '12px' : '18px' }, onClick: () => { handleMetadataValueSelection(item); }, children: _jsx(TMDropDownMenu, { backgroundColor: 'white', color: TMColors.button_icon, borderRadius: '3px', content: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconMenuVertical, {}), showTooltip: false }), disabled: item.isLexProt === 1, items: getAdvancedMenuItems(item) }) })] }, item.mid)), [TID, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, layoutMode, selectedMID, isOpenDistinctValues, openChooserBySingleClick, dynDataListsToBeRefreshed, metadataValues, metadataValuesOrig, validationItems, onValueChanged, handleMetadataValueSelection, getAdvancedMenuItems, onChangeHandler, editorValidationHandler, isEditable]);
|
|
300
|
+
const layoutWorkItem = useMemo(() => {
|
|
301
301
|
const workItemData = [];
|
|
302
302
|
const technicalWorkItemData = [];
|
|
303
303
|
const documentData = [];
|
|
@@ -328,8 +328,8 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
328
328
|
}
|
|
329
329
|
});
|
|
330
330
|
return (_jsxs("div", { style: { width: '100%' }, children: [documentData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.DocumentData, children: documentData.map(item => renderMetadataItem(item)) }), workItemData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.WorkItemData, children: workItemData.map(item => renderMetadataItem(item, true)) }), technicalWorkItemData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.WorkItemTechnicalData, defaultCollapsed: true, children: technicalWorkItemData.map(item => renderMetadataItem(item, true)) })] }));
|
|
331
|
-
};
|
|
332
|
-
const layoutDraft = () => {
|
|
331
|
+
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
332
|
+
const layoutDraft = useMemo(() => {
|
|
333
333
|
// Definiamo l'ordine desiderato per gli elementi
|
|
334
334
|
const desiredDraftOrder = [
|
|
335
335
|
DraftsMIDs.Name,
|
|
@@ -384,36 +384,73 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
384
384
|
checkOutData.push(tempCICODataMap[id]);
|
|
385
385
|
}
|
|
386
386
|
});
|
|
387
|
-
return (_jsxs("div", { style: { width: '100%' }, children: [draftData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.Draft, children: draftData.map(item => renderMetadataItem(item)) }), checkOutData.length > 0 && _jsx(TMAccordion, { title: `${SDKUI_Localizator.CheckIn}/${SDKUI_Localizator.CheckOut}`, children: checkOutData.map(item => renderMetadataItem(item, true)) })] }));
|
|
388
|
-
};
|
|
389
|
-
const
|
|
387
|
+
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)) })] }));
|
|
388
|
+
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
389
|
+
const layoutDsAttachs = useMemo(() => {
|
|
390
|
+
const dsAttachsData = [];
|
|
391
|
+
metadataValues.forEach(item => {
|
|
392
|
+
switch (item.md?.id) {
|
|
393
|
+
// case DSAttachsMIDs.DSID:
|
|
394
|
+
case DSAttachsMIDs.Name:
|
|
395
|
+
case DSAttachsMIDs.Description:
|
|
396
|
+
dsAttachsData.push(item);
|
|
397
|
+
break;
|
|
398
|
+
default:
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
return (_jsx("div", { style: { width: '100%' }, children: dsAttachsData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.Attachment, children: dsAttachsData.map(item => renderMetadataItem(item, isReadOnly)) }) }));
|
|
403
|
+
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
404
|
+
const layoutCustom = useMemo(() => {
|
|
390
405
|
if (!layout || !layout.items || layout.items.length === 0) {
|
|
391
406
|
return metadataValues.map((item) => renderMetadataItem(item));
|
|
392
407
|
}
|
|
393
|
-
// Build a map of items by ID for quick lookup
|
|
408
|
+
// Build a map of items by ID for quick lookup (include negative/zero IDs)
|
|
394
409
|
const itemsById = new Map();
|
|
395
410
|
layout.items.forEach(item => {
|
|
396
|
-
if (item.layoutItemID) {
|
|
411
|
+
if (item.layoutItemID !== undefined && item.layoutItemID !== null) {
|
|
397
412
|
itemsById.set(item.layoutItemID, item);
|
|
398
413
|
}
|
|
399
414
|
});
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
|
|
415
|
+
// Determine root items. Prefer explicit LayoutRoot items if present;
|
|
416
|
+
// otherwise fall back to items with no parent (parentID undefined or -1).
|
|
417
|
+
const hasExplicitRoot = layout.items.some(item => item.type === LayoutItemTypes.LayoutRoot);
|
|
418
|
+
const rootItems = hasExplicitRoot
|
|
419
|
+
? layout.items.filter(item => item.type === LayoutItemTypes.LayoutRoot)
|
|
420
|
+
: layout.items.filter(item => item.parentID === undefined || item.parentID === -1);
|
|
421
|
+
// Recursive function to get children of an item. Handle parentID === -1 and undefined
|
|
422
|
+
// because some layouts use -1 for root while children use undefined.
|
|
403
423
|
const getChildren = (parentID) => {
|
|
424
|
+
if (parentID === -1) {
|
|
425
|
+
return layout.items?.filter(item => item.parentID === -1 || item.parentID === undefined) || [];
|
|
426
|
+
}
|
|
427
|
+
if (parentID === undefined) {
|
|
428
|
+
return layout.items?.filter(item => item.parentID === undefined) || [];
|
|
429
|
+
}
|
|
404
430
|
return layout.items?.filter(item => item.parentID === parentID) || [];
|
|
405
431
|
};
|
|
406
432
|
// Recursive function to render layout items with depth tracking for indentation
|
|
407
|
-
|
|
433
|
+
// Prevent infinite recursion by tracking visited layoutItemIDs (handles malformed layouts where an item
|
|
434
|
+
// may reference itself as a child or cycles exist).
|
|
435
|
+
const renderLayoutItem = (layoutItem, depth = 0, visited = new Set()) => {
|
|
436
|
+
const id = layoutItem.layoutItemID ?? 0;
|
|
437
|
+
if (visited.has(id))
|
|
438
|
+
return null;
|
|
439
|
+
visited.add(id);
|
|
440
|
+
// Check if this is a LayoutRoot - just render its children
|
|
441
|
+
if (layoutItem.type === LayoutItemTypes.LayoutRoot) {
|
|
442
|
+
const children = getChildren(layoutItem.layoutItemID);
|
|
443
|
+
return (_jsx(React.Fragment, { children: children.map(child => renderLayoutItem(child, depth, visited)) }, `root-${layoutItem.layoutItemID}`));
|
|
444
|
+
}
|
|
408
445
|
// Check if this is a LayoutGroup
|
|
409
|
-
if (layoutItem.type === LayoutItemTypes.LayoutGroup && layoutItem.lgd) {
|
|
410
|
-
const children = getChildren(layoutItem.layoutItemID
|
|
446
|
+
else if (layoutItem.type === LayoutItemTypes.LayoutGroup && layoutItem.lgd) {
|
|
447
|
+
const children = getChildren(layoutItem.layoutItemID);
|
|
411
448
|
const groupDescriptor = layoutItem.lgd;
|
|
412
449
|
const groupTitle = groupDescriptor.caption || `Group ${layoutItem.layoutItemID}`;
|
|
413
450
|
const isCollapsed = false; // LayoutGroupDescriptor doesn't have collapsed property
|
|
414
451
|
// Apply indentation only to subgroups (depth > 0), not to root groups
|
|
415
452
|
const indentationPx = depth > 0 ? depth * 10 : 0;
|
|
416
|
-
return (_jsx("div", { style: { paddingLeft: `${indentationPx}px` }, children: _jsx(TMAccordion, { title: groupTitle, defaultCollapsed: isCollapsed, children: children.map(child => renderLayoutItem(child, depth + 1)) }) }, `group-wrapper-${layoutItem.layoutItemID}`));
|
|
453
|
+
return (_jsx("div", { style: { paddingLeft: `${indentationPx}px` }, children: _jsx(TMAccordion, { title: groupTitle, defaultCollapsed: isCollapsed, children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }) }, `group-wrapper-${layoutItem.layoutItemID}`));
|
|
417
454
|
}
|
|
418
455
|
// Check if this is a LayoutControlItem (metadata field)
|
|
419
456
|
else if (layoutItem.type === LayoutItemTypes.LayoutControlItem && layoutItem.lcid) {
|
|
@@ -425,8 +462,9 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
425
462
|
const metadataItem = metadataValues.find(m => m.mid === mid);
|
|
426
463
|
if (!metadataItem)
|
|
427
464
|
return null;
|
|
428
|
-
//
|
|
429
|
-
|
|
465
|
+
// Indent control items based on depth so they align under groups
|
|
466
|
+
const controlIndentPx = depth > 0 ? depth * 10 : 15;
|
|
467
|
+
return (_jsx("div", { style: { paddingLeft: `${controlIndentPx}px` }, children: renderMetadataItem(metadataItem) }, `control-${layoutItem.layoutItemID}`));
|
|
430
468
|
}
|
|
431
469
|
// Check if this is a SeparatorItem (horizontal line)
|
|
432
470
|
else if (layoutItem.type === LayoutItemTypes.SeparatorItem) {
|
|
@@ -434,27 +472,39 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
434
472
|
}
|
|
435
473
|
return null;
|
|
436
474
|
};
|
|
437
|
-
return (_jsx("div", { style: { width: '100%' }, children:
|
|
438
|
-
|
|
439
|
-
|
|
475
|
+
return (_jsx("div", { style: { width: '100%' }, children: (() => {
|
|
476
|
+
const visited = new Set();
|
|
477
|
+
return rootItems.map(item => renderLayoutItem(item, 0, visited));
|
|
478
|
+
})() }));
|
|
479
|
+
}, [layout, metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
480
|
+
const renderForm = useMemo(() => {
|
|
481
|
+
// Se currentDTD non è ancora stato caricato, non renderizzare nulla
|
|
482
|
+
if (!currentDTD) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
// Se tutti i metadata sono di sistema, renderizziamo tutti in sola lettura senza layout
|
|
486
|
+
const allSystem = metadataValues.length > 0 && metadataValues.every(item => item.md?.isSystem === 1);
|
|
487
|
+
if (allSystem) {
|
|
488
|
+
return metadataValues.map((item) => renderMetadataItem(item, true));
|
|
489
|
+
}
|
|
440
490
|
if (currentDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isReadOnly) {
|
|
441
|
-
return layoutWorkItem
|
|
491
|
+
return layoutWorkItem;
|
|
442
492
|
}
|
|
443
493
|
switch (currentDTD?.id) {
|
|
444
|
-
case SystemTIDs.Drafts: return
|
|
494
|
+
case SystemTIDs.Drafts: return layoutDraft;
|
|
445
495
|
// case SystemTIDs.Chronology: break;
|
|
446
|
-
|
|
496
|
+
case SystemTIDs.DSAttachs: return layoutDsAttachs;
|
|
447
497
|
default:
|
|
448
498
|
// Se è presente un layout personalizzato, usalo, altrimenti usa il rendering standard
|
|
449
499
|
if (layout && layout.items && layout.items.length > 0) {
|
|
450
|
-
return layoutCustom
|
|
500
|
+
return layoutCustom;
|
|
451
501
|
}
|
|
452
502
|
return metadataValues.map((item) => renderMetadataItem(item));
|
|
453
503
|
}
|
|
454
|
-
};
|
|
504
|
+
}, [currentDTD, metadataValues, layout, isReadOnly, showCheckBoxes, showNullValueCheckBoxes, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
455
505
|
return (_jsx(StyledMetadataValuesContainer, { children: !TID ?
|
|
456
506
|
_jsx(TMNothingToShow, { text: `${SDKUI_Localizator.NoDcmtSelected}.`, secondText: `${SDKUI_Localizator.MetadataSystem} - ${SDKUI_Localizator.NotAvailable}`, icon: _jsx(IconDcmtTypeSys, { fontSize: 96 }) }) :
|
|
457
|
-
_jsx(_Fragment, { children: renderForm
|
|
507
|
+
_jsx(_Fragment, { children: renderForm }) }));
|
|
458
508
|
};
|
|
459
509
|
export default TMMetadataValues;
|
|
460
510
|
//#region Styled Components
|