@topconsultnpm/sdkui-react-beta 6.13.42 → 6.13.43
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/features/documents/TMDcmtForm.d.ts +6 -0
- package/lib/components/features/documents/TMDcmtForm.js +33 -8
- package/lib/components/features/documents/TMMasterDetailDcmts.js +2 -1
- package/lib/components/features/search/TMSearchResult.js +9 -6
- package/lib/components/features/workflow/TMWorkflowPopup.js +11 -4
- package/lib/components/index.d.ts +3 -0
- package/lib/components/index.js +4 -0
- package/lib/components/layout/TMPanelLayout.js +29 -31
- package/lib/components/layout/panel/TMPanelContext.d.ts +45 -0
- package/lib/components/layout/panel/TMPanelContext.js +314 -0
- package/lib/components/layout/panel/TMPanelToolbar.d.ts +6 -0
- package/lib/components/layout/panel/TMPanelToolbar.js +66 -0
- package/lib/components/layout/panel/useResizablePanels.d.ts +3 -0
- package/lib/components/layout/panel/useResizablePanels.js +67 -0
- package/package.json +1 -1
@@ -1,6 +1,7 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { LayoutModes } from '@topconsultnpm/sdk-ts-beta';
|
3
3
|
import { FormModes } from '../../../ts';
|
4
|
+
import { DeviceType } from '../../base/TMDeviceProvider';
|
4
5
|
import { TMCommandItemProps } from '../../sidebar/TMCommandsPanel';
|
5
6
|
interface ITMDcmtFormProps {
|
6
7
|
TID?: number;
|
@@ -37,3 +38,8 @@ interface ITMDcmtFormProps {
|
|
37
38
|
}
|
38
39
|
declare const TMDcmtForm: React.FC<ITMDcmtFormProps>;
|
39
40
|
export default TMDcmtForm;
|
41
|
+
export declare const ToppyHelpCenter: ({ content, onClick, deviceType }: {
|
42
|
+
content?: any;
|
43
|
+
onClick?: () => void;
|
44
|
+
deviceType?: DeviceType;
|
45
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
3
|
-
import styled from 'styled-components';
|
3
|
+
import styled, { keyframes } from 'styled-components';
|
4
4
|
import TMDcmtPreview from './TMDcmtPreview';
|
5
5
|
import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutModes, MetadataDataTypes, ResultTypes, SDK_Globals, SystemMIDsAsNumber, TemplateTIDs, UpdateEngineByID, ValidationItem } from '@topconsultnpm/sdk-ts-beta';
|
6
6
|
import { ContextMenu } from 'devextreme-react';
|
@@ -31,6 +31,7 @@ import { useInputAttachmentsDialog } from '../../../hooks/useInputDialog';
|
|
31
31
|
import TMModal from '../../base/TMModal';
|
32
32
|
import TMPanel from '../../base/TMPanel';
|
33
33
|
import TMCommandsPanel from '../../sidebar/TMCommandsPanel';
|
34
|
+
import toppy from '../../../assets/Toppy-generico.png';
|
34
35
|
let abortControllerLocal = new AbortController();
|
35
36
|
//#endregion
|
36
37
|
const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, onClose, onSavedAsyncCallback, TID, DID, formMode = FormModes.Update, canNext, canPrev, count, itemIndex, onNext, onPrev, customRightSidebarItems = [], allowNavigation = true, allowRelations = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, showDcmtForm = true, showDcmtFormSidebar = true, showPreview = false, showBoard = false, showSysMetadata = false, onClosePreviewPanel, onCloseBlogPanel, onCloseSysMetadataPanel, titleModal, isModal = false, widthModal = "100%", heightModal = "100%" }) => {
|
@@ -467,8 +468,8 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
467
468
|
setIsOpenFormulaEditor(!isOpenFormulaEditor);
|
468
469
|
break;
|
469
470
|
}
|
470
|
-
} }), _jsxs(StyledFormButtonsContainer, { children: [
|
471
|
-
|
471
|
+
} }), _jsxs(StyledFormButtonsContainer, { children: [_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 10 }, children: _jsx("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '8px' }, children: layoutMode === LayoutModes.Update ? _jsxs(_Fragment, { children: [_jsx(TMSaveFormButtonSave, { showTooltip: false, btnStyle: 'advanced', advancedColor: '#f09c0a', isModified: isModified, formMode: formMode, errorsCount: validationItems.filter(o => o.ResultType == ResultTypes.ERROR).length, onSaveAsync: confirmActionPopup }), _jsx(TMSaveFormButtonUndo, { btnStyle: 'toolbar', showTooltip: true, color: 'primary', isModified: isModified, formMode: formMode, onUndo: onUndoHandler })] }) :
|
472
|
+
_jsxs(_Fragment, { children: [_jsx(TMButton, { disabled: archiveBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconBoxArchiveIn, {}), showTooltip: false, caption: 'Archivia', advancedColor: TMColors.success, onClick: confirmActionPopup }), _jsx(TMButton, { disabled: !clearFormBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconClear, {}), showTooltip: false, caption: 'Pulisci', advancedColor: TMColors.tertiary, onClick: clearFormHandler }), DID && _jsx(TMButton, { disabled: undoBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconUndo, {}), width: '150px', showTooltip: false, caption: 'Annulla modifiche', advancedColor: TMColors.tertiary, onClick: onUndoHandler })] }) }) }), totalItems > listMaxItems && _jsx(TMShowAllOrMaxItemsButton, { showAll: showAll, dataSourceLength: totalItems, onClick: () => { setShowAll(!showAll); } })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, TID: TID, DID: DID, op: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, TID: TID, DID: DID, op: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, TID: TID, DID: DID, onClose: () => setShowReAssignPopup(false) }), _jsx(ConfirmAttachmentsDialog, {})] }) }) }), isOpenPreview || isOpenMiddlePanel() ?
|
472
473
|
_jsx(TMLayoutItem, { children: _jsxs(TMSplitterLayout, { direction: 'horizontal', overflow: 'visible', separatorSize: SDKUI_Globals.userSettings.themeSettings.gutters, showSeparator: deviceType !== DeviceType.MOBILE && (isOpenPreview && isOpenMiddlePanel()), start: getSecondarySplitterStartLayout(), min: ['0', '0'], separatorColor: 'transparent', children: [isOpenMiddlePanel()
|
473
474
|
? _jsx(TMLayoutItem, { children: _jsxs(TMPanel, { showHeader: !(isOpenDetails && layoutMode === LayoutModes.Update), title: titleText(), onClose: () => { closeMiddlePanel(); }, children: [isOpenBoard && layoutMode === LayoutModes.Update &&
|
474
475
|
_jsx(TMDcmtBlog, { tid: TID, did: DID }), isOpenSysMetadata && layoutMode === LayoutModes.Update &&
|
@@ -514,7 +515,10 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
514
515
|
...(allowRelations && currentTIDHasDetailRelations ? [{ icon: _jsx(IconDetailDcmts, { transform: 'scale(-1, 1)' }), selected: isOpenDetails, disabled: isDetailsDisabled, onClick: () => { if (!isDetailsDisabled)
|
515
516
|
setIsOpenDetails(!isOpenDetails); } }] : []),
|
516
517
|
...customRightSidebarItems
|
517
|
-
] }), isOpenDetails &&
|
518
|
+
] }), (fromDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isOpenDetails && !isOpenMaster) &&
|
519
|
+
_jsx(ToppyHelpCenter, { deviceType: deviceType,
|
520
|
+
// onClick={() => isMobile ? openConfigureMode?.() : undefined}
|
521
|
+
content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { onApprove: () => setShowApprovePopup(true), onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), isInDcmtForm: deviceType === DeviceType.MOBILE }) }) }), isOpenDetails &&
|
518
522
|
_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, isForMaster: false, inputDcmts: getSelectionDcmtInfo(), allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenDetails(false) }) }), isOpenMaster &&
|
519
523
|
_jsxs(StyledModalContainer, { style: { backgroundColor: 'white' }, children: [_jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: getSelectionDcmtInfo(), isForMaster: true, allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenMaster(false), appendMasterDcmts: handleAddItem }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
|
520
524
|
return (_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: [dcmt], isForMaster: true, allowNavigation: false, onBack: () => handleRemoveItem(dcmt.TID, dcmt.DID), appendMasterDcmts: handleAddItem }) }, `${index}-${dcmt.DID}`));
|
@@ -546,10 +550,6 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
546
550
|
: renderDcmtForm() }));
|
547
551
|
};
|
548
552
|
export default TMDcmtForm;
|
549
|
-
//#region Styled Components
|
550
|
-
const StyledSectionContainer = styled.div ` width: 100%; height: 100%; display:flex; flex-direction: column; `;
|
551
|
-
const StyledSidebarItemsContentContainer = styled.div `width: 100%; height: 100%; overflow:auto;`;
|
552
|
-
//#endregion Styled Components
|
553
553
|
//#region Validaion
|
554
554
|
const validateMetadataList = (mvdList = []) => {
|
555
555
|
if (!Array.isArray(mvdList)) {
|
@@ -580,3 +580,28 @@ const validateMaxLength = (mvd, value, validationItems) => {
|
|
580
580
|
validationItems.push(new ValidationItem(ResultTypes.ERROR, mvd.md.nameLoc ?? "", message));
|
581
581
|
}
|
582
582
|
};
|
583
|
+
//#endregion Validation
|
584
|
+
const toppyEntrance = keyframes `
|
585
|
+
0% { right: -200px; opacity: 0; }
|
586
|
+
60% { opacity: 1; }
|
587
|
+
100% { right: -70px; opacity: 1; }
|
588
|
+
`;
|
589
|
+
const ToppyContainer = styled.div `
|
590
|
+
position: absolute;
|
591
|
+
bottom: ${props => props.$isMobile ? '-70px' : '-50px'};
|
592
|
+
right: ${props => props.$isMobile ? '-70px' : '-50px'};
|
593
|
+
transform: rotate(-20deg);
|
594
|
+
display: flex;
|
595
|
+
align-items: center;
|
596
|
+
justify-content: center;
|
597
|
+
animation: ${toppyEntrance} 0.5s cubic-bezier(0.23, 1, 0.32, 1);
|
598
|
+
z-index: 1000;
|
599
|
+
`;
|
600
|
+
const ToppyImage = styled.img `
|
601
|
+
width: ${props => props.$isMobile ? '120px' : '170px'};
|
602
|
+
height: ${props => props.$isMobile ? '140px' : '200px'};
|
603
|
+
cursor: ${props => props.$isMobile ? 'pointer' : 'default'};
|
604
|
+
`;
|
605
|
+
export const ToppyHelpCenter = ({ content, onClick, deviceType }) => {
|
606
|
+
return (_jsxs(ToppyContainer, { children: [_jsx(ToppyImage, { "$isMobile": deviceType === DeviceType.MOBILE, onClick: onClick, src: toppy, alt: "Toppy" }), _jsx("div", { style: { top: deviceType === DeviceType.MOBILE ? -180 : -200, right: deviceType === DeviceType.MOBILE ? 20 : 1, transform: 'rotate(20deg)', position: 'absolute', width: 'max-content', height: 'max-content' }, children: content })] }));
|
607
|
+
};
|
@@ -387,7 +387,7 @@ const TMMasterDetailDcmts = ({ deviceType, inputDcmts, isForMaster, showCurrentD
|
|
387
387
|
const renderTMFormOrResult = useMemo(() => (handleTogglePanel) => {
|
388
388
|
return (_jsx(_Fragment, { children: focusedItem?.isDcmt ?
|
389
389
|
_jsx(TMDcmtForm, { TID: focusedItem?.tid, DID: focusedItem.did, isClosable: deviceType !== DeviceType.MOBILE, allowNavigation: false, allowRelations: deviceType !== DeviceType.MOBILE, showDcmtFormSidebar: deviceType === DeviceType.MOBILE, showPreview: showPreview, showBoard: showBoard, showSysMetadata: showSysMetadata, showDcmtForm: showDcmtForm, onClose: () => { setShowDcmtForm(false); }, onClosePreviewPanel: () => { setShowPreview(false); } }) :
|
390
|
-
_jsx(TMSearchResult, { context: SearchResultContext.METADATA_SEARCH, allowFloatingBar: false, searchResults: focusedItem?.searchResult ?? [], showPreview: showPreview, showSearchResultSidebar: false, onClose: () => { setShowDcmtForm(false); }, onClosePreviewPanel: () => {
|
390
|
+
_jsx(TMSearchResult, { context: SearchResultContext.METADATA_SEARCH, allowFloatingBar: false, searchResults: focusedItem?.searchResult ?? [], showPreview: showPreview, showBoard: showBoard, showSysMetadata: showSysMetadata, showSearchResultSidebar: false, onClose: () => { setShowDcmtForm(false); }, onClosePreviewPanel: () => {
|
391
391
|
setShowPreview(false);
|
392
392
|
handleTogglePanel("commandPreview");
|
393
393
|
}, onTaskCreateRequest: onTaskCreateRequest }) }));
|
@@ -482,6 +482,7 @@ const TMMasterDetailDcmts = ({ deviceType, inputDcmts, isForMaster, showCurrentD
|
|
482
482
|
visible: true,
|
483
483
|
isActive: showZeroDcmts,
|
484
484
|
orderNumber: 3,
|
485
|
+
beginGroup: true,
|
485
486
|
},
|
486
487
|
type: 'button',
|
487
488
|
buttonOptions: {
|
@@ -30,7 +30,7 @@ import TMFloatingToolbar from '../../base/TMFloatingToolbar';
|
|
30
30
|
import { WorkFlowApproveRejectPopUp, WorkFlowOperationButtons, WorkFlowReAssignPopUp } from '../workflow/TMWorkflowPopup';
|
31
31
|
import TMMasterDetailDcmts from '../documents/TMMasterDetailDcmts';
|
32
32
|
import TMBatchUpdateForm from '../../features/documents/TMBatchUpdateForm';
|
33
|
-
import TMDcmtForm from '../documents/TMDcmtForm';
|
33
|
+
import TMDcmtForm, { ToppyHelpCenter } from '../documents/TMDcmtForm';
|
34
34
|
import TMDcmtBlog from '../documents/TMDcmtBlog';
|
35
35
|
import TMDcmtIcon from '../documents/TMDcmtIcon';
|
36
36
|
import TMPanelManager from '../../base/TMPanelManager';
|
@@ -325,7 +325,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
325
325
|
break;
|
326
326
|
}
|
327
327
|
};
|
328
|
-
const searchResutlToolbar = _jsxs(_Fragment, { children: [
|
328
|
+
const searchResutlToolbar = _jsxs(_Fragment, { children: [(dcmtsReturned != dcmtsFound) && _jsx("p", { style: { backgroundColor: `white`, color: TMColors.primaryColor, textAlign: 'center', padding: '1px 4px', borderRadius: '3px', display: 'flex' }, children: `${dcmtsReturned}/${dcmtsFound} restituiti` }), context === SearchResultContext.FAVORITES_AND_RECENTS &&
|
329
329
|
_jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: onRefreshSearchAsync }), _jsx(IconMenuVertical, { id: `commands-header-${id}`, color: 'white', cursor: 'pointer' }), _jsx(CommandsContextMenu, { target: `#commands-header-${id}`, menuItems: getCommandsMenuItems(fromDTD, selectedItems, focusedItem, context, showFloatingBar, setShowFloatingBar, openFormHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler) })] });
|
330
330
|
const middlePanelToolbar = _jsxs("div", { style: { width: 'max-content', display: 'flex', alignItems: 'center', gap: '10px' }, children: [_jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', isModified: false, iconColor: TMColors.default_background, formMode: FormModes.ReadOnly, canPrev: canNavigateHandler('prev'), onPrev: () => onNavigateHandler('prev') }), _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', isModified: false, iconColor: TMColors.default_background, formMode: FormModes.ReadOnly, canNext: canNavigateHandler('next'), onNext: () => onNavigateHandler('next') })] });
|
331
331
|
const handleAddItem = (tid, did) => {
|
@@ -346,7 +346,10 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
346
346
|
, {
|
347
347
|
// allowMultipleSelection={allowMultipleSelection}
|
348
348
|
inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], lastUpdateSearchTime: lastUpdateSearchTime, onDblClick: () => openFormHandler(LayoutModes.Update), onContextMenuPreparing: onContextMenuPreparing, onSelectionChanged: (items) => { setSelectedItems(items); }, onVisibleItemChanged: setVisibleItems, onFocusedItemChanged: setFocusedItem }), allowFloatingBar && showFloatingBar && deviceType !== DeviceType.MOBILE &&
|
349
|
-
_jsxs(TMFloatingToolbar, { backgroundColor: TMColors.primaryColor, initialLeft: '10px', initialTop: 'calc(100% - 75px)', children: [fromDTD?.perm?.canRetrieveFile === AccessLevels.Yes && _jsx(TMButton, { btnStyle: 'icon', caption: "Download file", disabled: fromDTD?.perm?.canRetrieveFile !== AccessLevels.Yes, icon: _jsx(IconDownload, { color: 'white' }), onClick: () => { downloadDcmtsAsync(getSelectedDcmtsOrFocused(selectedItems, focusedItem), DownloadTypes.Dcmt); } }), _jsx(TMButton, { btnStyle: 'icon', caption: 'Firma e marca', icon: _jsx(IconSignature, { color: 'white' }), onClick: () => { ShowAlert({ message: "TODO Firma e marca ", mode: 'info', title: `${"TODO"}`, duration: 3000 }); } }), _jsx(IconMenuVertical, { id: `commands-floating-${id}`, color: 'white', cursor: 'pointer' }), _jsx(CommandsContextMenu, { target: `#commands-floating-${id}`, menuItems: getCommandsMenuItems(fromDTD, selectedItems, focusedItem, context, showFloatingBar, setShowFloatingBar, openFormHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler) })] })] })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onUpdate: onUpdate, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), op: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onUpdate: onUpdate, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), op: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onUpdate: onUpdate, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) })
|
349
|
+
_jsxs(TMFloatingToolbar, { backgroundColor: TMColors.primaryColor, initialLeft: '10px', initialTop: 'calc(100% - 75px)', children: [fromDTD?.perm?.canRetrieveFile === AccessLevels.Yes && _jsx(TMButton, { btnStyle: 'icon', caption: "Download file", disabled: fromDTD?.perm?.canRetrieveFile !== AccessLevels.Yes, icon: _jsx(IconDownload, { color: 'white' }), onClick: () => { downloadDcmtsAsync(getSelectedDcmtsOrFocused(selectedItems, focusedItem), DownloadTypes.Dcmt); } }), _jsx(TMButton, { btnStyle: 'icon', caption: 'Firma e marca', icon: _jsx(IconSignature, { color: 'white' }), onClick: () => { ShowAlert({ message: "TODO Firma e marca ", mode: 'info', title: `${"TODO"}`, duration: 3000 }); } }), _jsx(IconMenuVertical, { id: `commands-floating-${id}`, color: 'white', cursor: 'pointer' }), _jsx(CommandsContextMenu, { target: `#commands-floating-${id}`, menuItems: getCommandsMenuItems(fromDTD, selectedItems, focusedItem, context, showFloatingBar, setShowFloatingBar, openFormHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler) })] })] })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onUpdate: onUpdate, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), op: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onUpdate: onUpdate, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), op: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onUpdate: onUpdate, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), (isVisible && fromDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isOpenDcmtForm && !isOpenDetails && !isOpenMaster) &&
|
350
|
+
_jsx(ToppyHelpCenter, { deviceType: deviceType,
|
351
|
+
// onClick={() => isMobile ? openConfigureMode?.() : undefined}
|
352
|
+
content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => setShowApprovePopup(true), onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), approveDisable: disable, rejectDisable: disable, reassignDisable: disable, infoDisable: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length !== 1 }) }) })] }), _jsx(ConfirmFormatDialog, {}), _jsx(ConfirmAttachmentsDialog, {})] });
|
350
353
|
const renderTMBlog = (handleTogglePanel) => _jsx(TMDcmtBlog, { tid: focusedItem?.TID, did: focusedItem?.DID });
|
351
354
|
const renderTMSysMetadata = (handleTogglePanel) => _jsx(TMMetadataValues, { layoutMode: LayoutModes.Update, openChooserBySingleClick: true, TID: focusedItem?.TID, isReadOnly: true, deviceType: deviceType, metadataValues: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [] });
|
352
355
|
const renderTMDcmtPreview = (handleTogglePanel) => _jsx(TMDcmtPreview, { onClose: () => {
|
@@ -373,7 +376,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
373
376
|
title: getTitleHeader(),
|
374
377
|
showHeader: showToolbarHeader,
|
375
378
|
allowMaximize: !isMobile,
|
376
|
-
onBack: !isClosable ? onBack : undefined,
|
379
|
+
onBack: (!isClosable && context === SearchResultContext.METADATA_SEARCH) ? onBack : undefined,
|
377
380
|
onClose: isClosable ? onBack : undefined,
|
378
381
|
toolbar: searchResutlToolbar
|
379
382
|
},
|
@@ -456,7 +459,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
456
459
|
},
|
457
460
|
},
|
458
461
|
{
|
459
|
-
id: '
|
462
|
+
id: 'commandMaster',
|
460
463
|
name: SDKUI_Localizator.DcmtsMaster,
|
461
464
|
toolbarOptions: {
|
462
465
|
icon: _jsx(IconDetailDcmts, { fontSize: 24 }),
|
@@ -472,7 +475,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
472
475
|
},
|
473
476
|
},
|
474
477
|
{
|
475
|
-
id: '
|
478
|
+
id: 'commandDetails',
|
476
479
|
name: SDKUI_Localizator.DcmtsDetail,
|
477
480
|
toolbarOptions: {
|
478
481
|
icon: _jsx(IconDetailDcmts, { transform: 'scale(-1, 1)', fontSize: 24 }),
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
2
|
import { useMemo, useState } from "react";
|
3
3
|
import { SDK_Globals } from '@topconsultnpm/sdk-ts-beta';
|
4
|
-
import { Menu } from "devextreme-react";
|
5
4
|
import styled from "styled-components";
|
6
5
|
import { SDKUI_Localizator, IconApply, IconCloseOutline, IconUser, IconInfo } from "../../../helper";
|
7
6
|
import { TMColors } from "../../../utils/theme";
|
@@ -16,6 +15,7 @@ const StyledWorkFlowOperationButtonsContainer = styled.div `
|
|
16
15
|
align-items: center;
|
17
16
|
gap: 15px;
|
18
17
|
margin-left: ${props => props.$isInDcmtForm ? '0' : '50px'};
|
18
|
+
flex-direction: column;
|
19
19
|
`;
|
20
20
|
const StyledTextArea = styled.textarea `
|
21
21
|
width: 100%;
|
@@ -41,9 +41,16 @@ export const WorkFlowOperationButtons = ({ isInDcmtForm = false, deviceType = De
|
|
41
41
|
],
|
42
42
|
}]);
|
43
43
|
}, []);
|
44
|
-
return (
|
45
|
-
|
46
|
-
|
44
|
+
return (
|
45
|
+
// deviceType === DeviceType.DESKTOP ?
|
46
|
+
_jsxs(StyledWorkFlowOperationButtonsContainer, { "$isInDcmtForm": isInDcmtForm, children: [_jsx(TMButton, { btnStyle: isInDcmtForm ? 'toolbar' : 'advanced', showTooltip: isInDcmtForm, icon: _jsx(IconApply, {}), caption: SDKUI_Localizator.Approve, disabled: approveDisable, onClick: () => !approveDisable && onApprove && onApprove(), advancedColor: TMColors.success, color: 'success' }), _jsx(TMButton, { btnStyle: isInDcmtForm ? 'toolbar' : 'advanced', showTooltip: isInDcmtForm, icon: _jsx(IconCloseOutline, {}), caption: SDKUI_Localizator.Reject, disabled: rejectDisable, onClick: () => !rejectDisable && onReject && onReject(), advancedColor: TMColors.error, color: 'error' }), _jsx(TMButton, { btnStyle: isInDcmtForm ? 'toolbar' : 'advanced', showTooltip: isInDcmtForm, icon: _jsx(IconUser, { fontSize: 16 }), caption: SDKUI_Localizator.Reassign, disabled: reassignDisable, onClick: () => !reassignDisable && onReAssign && onReAssign(), advancedColor: TMColors.tertiary, color: 'tertiary' }), _jsx(TMButton, { btnStyle: isInDcmtForm ? 'toolbar' : 'advanced', showTooltip: isInDcmtForm, icon: _jsx(IconInfo, { fontSize: 16 }), caption: SDKUI_Localizator.MoreInformation, width: '180px', disabled: infoDisable, onClick: () => alert('TODO!!!'), advancedColor: TMColors.info, color: 'info' })] })
|
47
|
+
// : <Menu
|
48
|
+
// elementAttr={{ class: 'custom-dx-menu' }}
|
49
|
+
// disabled={approveDisable && reassignDisable && rejectDisable && infoDisable}
|
50
|
+
// dataSource={operations}
|
51
|
+
// displayExpr="name"
|
52
|
+
// />
|
53
|
+
);
|
47
54
|
};
|
48
55
|
export const WorkFlowApproveRejectPopUp = ({ TID = 0, DID = 0, deviceType = DeviceType.DESKTOP, op, onClose, selectedItems = [], onUpdate }) => {
|
49
56
|
const [commentValue, setCommentValue] = useState('');
|
@@ -81,3 +81,6 @@ export { default as TMFileManagerThumbnailItems } from "./base/TMFileManagerThum
|
|
81
81
|
export { default as TMCounterContainer } from "./base/TMCounterContainer";
|
82
82
|
export * from "./base/TMCounterContainer";
|
83
83
|
export { default as TMAreaManager } from "./base/TMAreaManager";
|
84
|
+
export * from "./layout/panel/TMPanelContext";
|
85
|
+
export * from "./layout/panel/TMPanelToolbar";
|
86
|
+
export * from "./layout/panel/useResizablePanels";
|
package/lib/components/index.js
CHANGED
@@ -96,3 +96,7 @@ export { default as TMFileManagerThumbnailItems } from "./base/TMFileManagerThum
|
|
96
96
|
export { default as TMCounterContainer } from "./base/TMCounterContainer";
|
97
97
|
export * from "./base/TMCounterContainer";
|
98
98
|
export { default as TMAreaManager } from "./base/TMAreaManager";
|
99
|
+
// layout
|
100
|
+
export * from "./layout/panel/TMPanelContext";
|
101
|
+
export * from "./layout/panel/TMPanelToolbar";
|
102
|
+
export * from "./layout/panel/useResizablePanels";
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
2
|
import React, { useRef, useState, Children, cloneElement, isValidElement, useEffect, useMemo, } from 'react';
|
3
3
|
import { SDKUI_Globals } from '../../helper';
|
4
|
+
import { useDeviceType } from '../base/TMDeviceProvider';
|
4
5
|
export const TMPanelItem = (props) => (_jsx("div", { style: {
|
5
6
|
flexBasis: props.width || props.height || 'auto',
|
6
7
|
minWidth: props.minWidth,
|
@@ -9,7 +10,7 @@ export const TMPanelItem = (props) => (_jsx("div", { style: {
|
|
9
10
|
flexShrink: 0,
|
10
11
|
height: '100%',
|
11
12
|
width: '100%',
|
12
|
-
overflow: 'auto',
|
13
|
+
// overflow: 'auto',
|
13
14
|
position: 'relative',
|
14
15
|
boxSizing: 'border-box',
|
15
16
|
display: 'flex',
|
@@ -100,35 +101,32 @@ export const TMPanelGroup = ({ orientation = 'horizontal', allowItemResize = fal
|
|
100
101
|
filteredAccHidden += renderedSizes[i];
|
101
102
|
}
|
102
103
|
}
|
103
|
-
// --- CORRECTED MERGING LOGIC ---
|
104
|
+
// --- CORRECTED MERGING LOGIC: merge hidden into previous visible, or into the first visible if at the start ---
|
104
105
|
// Merge hidden panel sizes into the nearest visible sibling (prefer previous, else next)
|
105
106
|
const mergedPanels = [];
|
106
107
|
const mergedSizes = [];
|
107
108
|
const mergedPaths = [];
|
109
|
+
let lastVisibleIdx = -1;
|
108
110
|
for (let i = 0; i < filteredPanels.length; i++) {
|
109
111
|
if (filteredPanels[i] !== null && filteredPanels[i] !== undefined) {
|
112
|
+
// If there were hidden panels before the first visible, merge their sizes into this first visible
|
113
|
+
if (lastVisibleIdx === -1 && i > 0) {
|
114
|
+
let acc = 0;
|
115
|
+
for (let j = 0; j < i; j++)
|
116
|
+
acc += filteredSizes[j];
|
117
|
+
mergedSizes.push(filteredSizes[i] + acc);
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
mergedSizes.push(filteredSizes[i]);
|
121
|
+
}
|
110
122
|
mergedPanels.push(filteredPanels[i]);
|
111
|
-
mergedSizes.push(filteredSizes[i]);
|
112
123
|
mergedPaths.push(filteredPaths[i]);
|
124
|
+
lastVisibleIdx = mergedSizes.length - 1;
|
113
125
|
}
|
114
126
|
else {
|
115
|
-
// Hidden panel: merge its size into
|
116
|
-
if (
|
117
|
-
mergedSizes[
|
118
|
-
}
|
119
|
-
else {
|
120
|
-
// No previous, merge into next visible
|
121
|
-
let j = i + 1;
|
122
|
-
while (j < filteredPanels.length &&
|
123
|
-
(filteredPanels[j] === null || filteredPanels[j] === undefined)) {
|
124
|
-
j++;
|
125
|
-
}
|
126
|
-
if (j < filteredPanels.length) {
|
127
|
-
// Next visible found
|
128
|
-
if (filteredSizes[j] !== undefined) {
|
129
|
-
filteredSizes[j] += filteredSizes[i];
|
130
|
-
}
|
131
|
-
}
|
127
|
+
// Hidden panel: merge its size into the last visible panel (if any)
|
128
|
+
if (lastVisibleIdx >= 0) {
|
129
|
+
mergedSizes[lastVisibleIdx] += filteredSizes[i];
|
132
130
|
}
|
133
131
|
}
|
134
132
|
}
|
@@ -268,6 +266,8 @@ const TMPanelLayout = ({ children, showToolbar = true, emptyContent, }) => {
|
|
268
266
|
};
|
269
267
|
// For nested: you can extend this to track nested visibility state if needed
|
270
268
|
const allHidden = isAllPanelsHidden();
|
269
|
+
const deviceType = useDeviceType();
|
270
|
+
const isMobile = useMemo(() => deviceType === 'mobile', [deviceType]);
|
271
271
|
return (_jsx("div", { style: {
|
272
272
|
width: '100%',
|
273
273
|
height: '100%',
|
@@ -275,23 +275,21 @@ const TMPanelLayout = ({ children, showToolbar = true, emptyContent, }) => {
|
|
275
275
|
overflow: 'hidden',
|
276
276
|
display: 'flex',
|
277
277
|
flexDirection: 'column',
|
278
|
-
}, children: _jsxs("div", { style: {
|
278
|
+
}, children: _jsxs("div", { style: { display: 'flex', flexDirection: isMobile ? 'column' : 'row', height: '100%', width: '100%', gap: !isMobile ? SDKUI_Globals.userSettings.themeSettings.gutters : 0 }, children: [!allHidden &&
|
279
279
|
cloneElement(group, {
|
280
280
|
panelVisibility,
|
281
281
|
onTogglePanel: handleTogglePanel,
|
282
282
|
panelLabels: allPanels.map(p => p.label),
|
283
283
|
}), showToolbar && (_jsx("div", { style: {
|
284
|
-
position: 'absolute',
|
285
|
-
top: 10,
|
286
|
-
right: 10,
|
287
|
-
zIndex: 1000,
|
288
|
-
background: 'rgba(255,255,255,0.9)',
|
289
|
-
borderRadius: 8,
|
290
|
-
boxShadow: '0 2px 8px #0002',
|
291
|
-
padding: 8,
|
292
284
|
display: 'flex',
|
293
|
-
flexDirection: 'column',
|
294
|
-
|
285
|
+
flexDirection: isMobile ? 'row' : 'column',
|
286
|
+
alignItems: 'center',
|
287
|
+
width: isMobile ? '100%' : '50px',
|
288
|
+
height: isMobile ? '50px' : 'max-content',
|
289
|
+
background: 'transparent linear-gradient(90deg, #CCE0F4 0%, #7EC1E7 14%, #39A6DB 28%, #1E9CD7 35%, #0075BE 78%, #005B97 99%) 0% 0% no-repeat padding-box',
|
290
|
+
borderRadius: isMobile ? '10px' : '10px 0px 0px 10px',
|
291
|
+
padding: '10px',
|
292
|
+
gap: '10px'
|
295
293
|
}, children: _jsx(TMPanelLayoutToolbar, { panelLabels: allPanels.map(p => p.label), panelVisibility: panelVisibility, onTogglePanel: handleTogglePanel }) })), allHidden && (_jsx("div", { style: {
|
296
294
|
position: 'absolute',
|
297
295
|
left: 0,
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { ReactNode } from 'react';
|
2
|
+
export interface TMPanelManagerItemContext {
|
3
|
+
id: string;
|
4
|
+
name: string;
|
5
|
+
contentOptions: {
|
6
|
+
width: string;
|
7
|
+
height: string;
|
8
|
+
};
|
9
|
+
toolbarOptions: {
|
10
|
+
icon: string | JSX.Element;
|
11
|
+
visible: boolean;
|
12
|
+
isActive: boolean;
|
13
|
+
disabled?: boolean;
|
14
|
+
orderNumber?: number;
|
15
|
+
beginGroup?: boolean;
|
16
|
+
alwaysActiveColor?: boolean;
|
17
|
+
};
|
18
|
+
children: Array<TMPanelManagerItemContext>;
|
19
|
+
}
|
20
|
+
interface ITMPanelManagerContext {
|
21
|
+
panelTree: Array<TMPanelManagerItemContext>;
|
22
|
+
visibility: Record<string, boolean>;
|
23
|
+
calculateEffectiveVisibility: Record<string, boolean>;
|
24
|
+
setPanelVisibility: (id: string, visible: boolean) => void;
|
25
|
+
togglePanel: (id: string) => void;
|
26
|
+
findPanelById: (id: string) => TMPanelManagerItemContext | undefined;
|
27
|
+
getPanelDimensions: (id: string) => {
|
28
|
+
width: string;
|
29
|
+
height: string;
|
30
|
+
};
|
31
|
+
toggleMaximizePanel: (id: string) => void;
|
32
|
+
maximizedPanelId: string | null;
|
33
|
+
updatePanelSize: (id: string, width: string | number, height: string | number) => void;
|
34
|
+
setPanelVisibleById: (id: string, visible: boolean) => void;
|
35
|
+
setPanelDisabledById: (id: string, disabled: boolean) => void;
|
36
|
+
hasVisiblePanels: () => boolean;
|
37
|
+
}
|
38
|
+
export declare function useTMPanelConext(): ITMPanelManagerContext;
|
39
|
+
interface TMPanelContextProviderProps {
|
40
|
+
panels: Array<TMPanelManagerItemContext>;
|
41
|
+
initialMobilePanelId: string;
|
42
|
+
children: ReactNode;
|
43
|
+
}
|
44
|
+
export declare const TMPanelManagerContextProvider: (props: TMPanelContextProviderProps) => import("react/jsx-runtime").JSX.Element;
|
45
|
+
export {};
|
@@ -0,0 +1,314 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { createContext, useContext, useMemo, useState, useEffect, } from 'react';
|
3
|
+
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
4
|
+
// Creazione del context
|
5
|
+
const TMPanelManagerContext = createContext(undefined);
|
6
|
+
// Hook custom per accedere al context
|
7
|
+
export function useTMPanelConext() {
|
8
|
+
const context = useContext(TMPanelManagerContext);
|
9
|
+
if (!context) {
|
10
|
+
throw new Error('useTMPanelConext must be used within a TMPanelContextProvider');
|
11
|
+
}
|
12
|
+
return context;
|
13
|
+
}
|
14
|
+
// Provider del context
|
15
|
+
export const TMPanelManagerContextProvider = (props) => {
|
16
|
+
const { panels, initialMobilePanelId, children } = props;
|
17
|
+
const [panelTree, setPanelTree] = useState(panels);
|
18
|
+
const [maximizedPanelId, setMaximizedPanelId] = useState(null);
|
19
|
+
const deviceType = useDeviceType();
|
20
|
+
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
21
|
+
useEffect(() => {
|
22
|
+
if (isMobile) {
|
23
|
+
setVisibility(prev => updatePanelVisibility(initialMobilePanelId, true, prev));
|
24
|
+
}
|
25
|
+
}, [isMobile]);
|
26
|
+
// Stato iniziale per dimensioni pannelli
|
27
|
+
const [panelSizes, setPanelSizes] = useState(() => {
|
28
|
+
const sizes = {};
|
29
|
+
const flattenPanels = (panels) => panels.reduce((acc, p) => [...acc, p, ...flattenPanels(p.children)], []);
|
30
|
+
const allPanels = flattenPanels(panelTree);
|
31
|
+
allPanels.forEach(panel => {
|
32
|
+
sizes[panel.id] = { width: panel.contentOptions.width, height: panel.contentOptions.height };
|
33
|
+
});
|
34
|
+
return sizes;
|
35
|
+
});
|
36
|
+
// Trova un pannello tramite ID
|
37
|
+
const findPanelById = (id) => {
|
38
|
+
const findRecursive = (panels) => {
|
39
|
+
for (const panel of panels) {
|
40
|
+
if (panel.id === id)
|
41
|
+
return panel;
|
42
|
+
const found = findRecursive(panel.children);
|
43
|
+
if (found)
|
44
|
+
return found;
|
45
|
+
}
|
46
|
+
return undefined;
|
47
|
+
};
|
48
|
+
return findRecursive(panelTree);
|
49
|
+
};
|
50
|
+
// Mappa figlio -> genitori
|
51
|
+
const parentMap = useMemo(() => {
|
52
|
+
const map = new Map();
|
53
|
+
const buildMap = (panels, parents = []) => {
|
54
|
+
for (const panel of panels) {
|
55
|
+
map.set(panel.id, [...parents]);
|
56
|
+
buildMap(panel.children, [...parents, panel.id]);
|
57
|
+
}
|
58
|
+
};
|
59
|
+
buildMap(panelTree);
|
60
|
+
return map;
|
61
|
+
}, [panelTree]);
|
62
|
+
// Mappa genitore -> figli
|
63
|
+
const parentToChildrenMap = useMemo(() => {
|
64
|
+
const map = new Map();
|
65
|
+
const buildMap = (panels) => {
|
66
|
+
for (const panel of panels) {
|
67
|
+
map.set(panel.id, panel.children.map(c => c.id));
|
68
|
+
buildMap(panel.children);
|
69
|
+
}
|
70
|
+
};
|
71
|
+
buildMap(panelTree);
|
72
|
+
return map;
|
73
|
+
}, [panelTree]);
|
74
|
+
// Stato visibilità iniziale
|
75
|
+
const getInitialVisibility = (panels) => {
|
76
|
+
const vis = {};
|
77
|
+
const collect = (panels) => {
|
78
|
+
for (const panel of panels) {
|
79
|
+
vis[panel.id] = panel.toolbarOptions?.isActive ?? false;
|
80
|
+
collect(panel.children);
|
81
|
+
}
|
82
|
+
};
|
83
|
+
collect(panels);
|
84
|
+
return vis;
|
85
|
+
};
|
86
|
+
const [visibility, setVisibility] = useState(() => getInitialVisibility(panelTree));
|
87
|
+
const getParentsOfPanel = (id) => parentMap.get(id) || [];
|
88
|
+
// Visibilità effettiva tenendo conto della gerarchia
|
89
|
+
const calculateEffectiveVisibility = useMemo(() => {
|
90
|
+
if (maximizedPanelId) {
|
91
|
+
return { ...visibility };
|
92
|
+
}
|
93
|
+
const updatedVisibility = { ...visibility };
|
94
|
+
const propagateToParents = (id) => {
|
95
|
+
for (const parent of parentMap.get(id) || []) {
|
96
|
+
if (!updatedVisibility[parent]) {
|
97
|
+
updatedVisibility[parent] = true;
|
98
|
+
propagateToParents(parent);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
};
|
102
|
+
Object.entries(updatedVisibility).forEach(([id, isVisible]) => {
|
103
|
+
if (isVisible)
|
104
|
+
propagateToParents(id);
|
105
|
+
});
|
106
|
+
const deactivateIfNoVisibleChildren = (parentId) => {
|
107
|
+
const children = parentToChildrenMap.get(parentId) || [];
|
108
|
+
if (children.length === 0)
|
109
|
+
return;
|
110
|
+
const anyVisible = children.some(id => updatedVisibility[id]);
|
111
|
+
if (!anyVisible && updatedVisibility[parentId]) {
|
112
|
+
updatedVisibility[parentId] = false;
|
113
|
+
for (const grand of parentMap.get(parentId) || []) {
|
114
|
+
deactivateIfNoVisibleChildren(grand);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
};
|
118
|
+
for (const parentId of parentToChildrenMap.keys()) {
|
119
|
+
deactivateIfNoVisibleChildren(parentId);
|
120
|
+
}
|
121
|
+
return updatedVisibility;
|
122
|
+
}, [visibility, parentMap, parentToChildrenMap, maximizedPanelId]);
|
123
|
+
// Calcolo delle dimensioni dinamiche in base alla visibilità
|
124
|
+
const dynamicSizesMap = useMemo(() => {
|
125
|
+
const flattenPanels = (panels) => panels.reduce((acc, p) => [...acc, p, ...flattenPanels(p.children)], []);
|
126
|
+
const allPanels = flattenPanels(panelTree);
|
127
|
+
const originalHeights = new Map();
|
128
|
+
const originalWidths = new Map();
|
129
|
+
allPanels.forEach(panel => {
|
130
|
+
originalHeights.set(panel.id, parseFloat(panel.contentOptions.height) || 0);
|
131
|
+
originalWidths.set(panel.id, parseFloat(panel.contentOptions.width) || 0);
|
132
|
+
});
|
133
|
+
if (maximizedPanelId) {
|
134
|
+
const parents = getParentsOfPanel(maximizedPanelId);
|
135
|
+
const allowed = new Set([maximizedPanelId, ...parents]);
|
136
|
+
const sizesMap = {};
|
137
|
+
allPanels.forEach(panel => {
|
138
|
+
sizesMap[panel.id] = allowed.has(panel.id)
|
139
|
+
? { width: '100%', height: '100%' }
|
140
|
+
: { width: '0%', height: '0%' };
|
141
|
+
});
|
142
|
+
return sizesMap;
|
143
|
+
}
|
144
|
+
const calcSiblingSizes = (siblings, originalSizes) => {
|
145
|
+
const visibleSiblings = siblings.filter(s => calculateEffectiveVisibility[s.id]);
|
146
|
+
const totalVisible = visibleSiblings.reduce((sum, s) => sum + (originalSizes.get(s.id) || 0), 0);
|
147
|
+
const totalAll = siblings.reduce((sum, s) => sum + (originalSizes.get(s.id) || 0), 0);
|
148
|
+
const map = {};
|
149
|
+
visibleSiblings.forEach(s => {
|
150
|
+
const orig = originalSizes.get(s.id) || 0;
|
151
|
+
map[s.id] = totalVisible === 0 ? 0 : (orig / totalVisible) * totalAll;
|
152
|
+
});
|
153
|
+
siblings.forEach(s => {
|
154
|
+
if (!calculateEffectiveVisibility[s.id])
|
155
|
+
map[s.id] = 0;
|
156
|
+
});
|
157
|
+
return map;
|
158
|
+
};
|
159
|
+
const widthSizesMap = {};
|
160
|
+
const heightSizesMap = {};
|
161
|
+
Object.assign(widthSizesMap, calcSiblingSizes(panelTree, originalWidths));
|
162
|
+
Object.assign(heightSizesMap, calcSiblingSizes(panelTree, originalHeights));
|
163
|
+
const processChildrenSizes = (panels) => {
|
164
|
+
panels.forEach(panel => {
|
165
|
+
if (panel.children.length > 0) {
|
166
|
+
Object.assign(widthSizesMap, calcSiblingSizes(panel.children, originalWidths));
|
167
|
+
Object.assign(heightSizesMap, calcSiblingSizes(panel.children, originalHeights));
|
168
|
+
processChildrenSizes(panel.children);
|
169
|
+
}
|
170
|
+
});
|
171
|
+
};
|
172
|
+
processChildrenSizes(panelTree);
|
173
|
+
const sizesMap = {};
|
174
|
+
allPanels.forEach(panel => {
|
175
|
+
const width = Math.min(widthSizesMap[panel.id] ?? 0, 100);
|
176
|
+
const height = Math.min(heightSizesMap[panel.id] ?? 0, 100);
|
177
|
+
sizesMap[panel.id] = {
|
178
|
+
width: calculateEffectiveVisibility[panel.id] ? `${width.toFixed(2)}%` : '0%',
|
179
|
+
height: calculateEffectiveVisibility[panel.id] ? `${height.toFixed(2)}%` : '0%',
|
180
|
+
};
|
181
|
+
});
|
182
|
+
return sizesMap;
|
183
|
+
}, [calculateEffectiveVisibility, panelTree, maximizedPanelId]);
|
184
|
+
// Ottieni dimensioni di un pannello (override -> dinamico -> default)
|
185
|
+
const getPanelDimensions = (id) => {
|
186
|
+
if (panelSizes[id])
|
187
|
+
return panelSizes[id];
|
188
|
+
if (dynamicSizesMap[id])
|
189
|
+
return dynamicSizesMap[id];
|
190
|
+
const panel = findPanelById(id);
|
191
|
+
return panel ? { width: panel.contentOptions.width, height: panel.contentOptions.height } : { width: 'auto', height: 'auto' };
|
192
|
+
};
|
193
|
+
// Imposta manualmente le dimensioni di un pannello
|
194
|
+
const updatePanelSize = (id, width, height) => {
|
195
|
+
setPanelSizes(prev => ({
|
196
|
+
...prev,
|
197
|
+
[id]: {
|
198
|
+
width: typeof width === 'number' ? `${width}%` : width,
|
199
|
+
height: typeof height === 'number' ? `${height}%` : height,
|
200
|
+
},
|
201
|
+
}));
|
202
|
+
};
|
203
|
+
// Quando cambia visibilità o massimizzazione, resetta override
|
204
|
+
useEffect(() => {
|
205
|
+
setPanelSizes({});
|
206
|
+
}, [visibility, maximizedPanelId]);
|
207
|
+
// Massimizza o ripristina un pannello
|
208
|
+
const toggleMaximizePanel = (id) => {
|
209
|
+
setMaximizedPanelId(prev => (prev === id ? null : id));
|
210
|
+
};
|
211
|
+
const updatePanelVisibility = (id, visible, currentVisibility) => {
|
212
|
+
let next = { ...currentVisibility };
|
213
|
+
if (!visible && maximizedPanelId === id) {
|
214
|
+
setMaximizedPanelId(null);
|
215
|
+
}
|
216
|
+
// Se siamo su mobile, resettiamo tutto tranne il pannello selezionato e i suoi parent
|
217
|
+
if (isMobile && visible) {
|
218
|
+
const parents = parentMap.get(id) || [];
|
219
|
+
// Imposta tutti i pannelli come invisibili tranne il selezionato e i suoi parent
|
220
|
+
Object.keys(next).forEach(panelId => {
|
221
|
+
next[panelId] = (panelId === id || parents.includes(panelId));
|
222
|
+
});
|
223
|
+
// Applichiamo propagazione verso l’alto (attivazione dei genitori)
|
224
|
+
const activateParents = (childId) => {
|
225
|
+
for (const parent of parentMap.get(childId) || []) {
|
226
|
+
if (!next[parent]) {
|
227
|
+
next[parent] = true;
|
228
|
+
activateParents(parent);
|
229
|
+
}
|
230
|
+
}
|
231
|
+
};
|
232
|
+
activateParents(id);
|
233
|
+
return next;
|
234
|
+
}
|
235
|
+
// Normal behavior (desktop)
|
236
|
+
next[id] = visible;
|
237
|
+
if (visible) {
|
238
|
+
const activateParents = (childId) => {
|
239
|
+
for (const parent of parentMap.get(childId) || []) {
|
240
|
+
if (!next[parent]) {
|
241
|
+
next[parent] = true;
|
242
|
+
activateParents(parent);
|
243
|
+
}
|
244
|
+
}
|
245
|
+
};
|
246
|
+
activateParents(id);
|
247
|
+
}
|
248
|
+
else {
|
249
|
+
const deactivateParents = (childId) => {
|
250
|
+
for (const parent of parentMap.get(childId) || []) {
|
251
|
+
const children = parentToChildrenMap.get(parent) || [];
|
252
|
+
const anyVisible = children.some(cId => next[cId]);
|
253
|
+
if (!anyVisible && next[parent]) {
|
254
|
+
next[parent] = false;
|
255
|
+
deactivateParents(parent);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
};
|
259
|
+
deactivateParents(id);
|
260
|
+
}
|
261
|
+
return next;
|
262
|
+
};
|
263
|
+
// Attiva/disattiva visibilità di un pannello
|
264
|
+
const togglePanel = (id) => {
|
265
|
+
setVisibility(prev => updatePanelVisibility(id, !prev[id], prev));
|
266
|
+
};
|
267
|
+
// Imposta visibilità esplicita di un pannello
|
268
|
+
const setPanelVisibility = (id, visible) => {
|
269
|
+
setVisibility(prev => updatePanelVisibility(id, visible, prev));
|
270
|
+
};
|
271
|
+
// Cambia la visibilità direttamente nel pannello (opzionale, ma richiesto da te)
|
272
|
+
const setPanelVisibleById = (id, visible) => {
|
273
|
+
setPanelTree(prevTree => {
|
274
|
+
const update = (panels) => panels.map(panel => {
|
275
|
+
if (panel.id === id) {
|
276
|
+
return { ...panel, toolbarOptions: { ...panel.toolbarOptions, visible } };
|
277
|
+
}
|
278
|
+
return { ...panel, children: update(panel.children) };
|
279
|
+
});
|
280
|
+
return update(prevTree);
|
281
|
+
});
|
282
|
+
};
|
283
|
+
// Cambia il valore di disabled direttamente nel pannello
|
284
|
+
const setPanelDisabledById = (id, disabled) => {
|
285
|
+
setPanelTree(prevTree => {
|
286
|
+
const update = (panels) => panels.map(panel => {
|
287
|
+
if (panel.id === id) {
|
288
|
+
return { ...panel, toolbarOptions: { ...panel.toolbarOptions, disabled, } };
|
289
|
+
}
|
290
|
+
return { ...panel, children: update(panel.children) };
|
291
|
+
});
|
292
|
+
return update(prevTree);
|
293
|
+
});
|
294
|
+
};
|
295
|
+
const hasVisiblePanels = () => {
|
296
|
+
return Object.values(calculateEffectiveVisibility).some(visible => visible);
|
297
|
+
};
|
298
|
+
// Valori esposti dal context
|
299
|
+
return (_jsx(TMPanelManagerContext.Provider, { value: {
|
300
|
+
panelTree,
|
301
|
+
visibility,
|
302
|
+
calculateEffectiveVisibility,
|
303
|
+
setPanelVisibility,
|
304
|
+
togglePanel,
|
305
|
+
findPanelById,
|
306
|
+
getPanelDimensions,
|
307
|
+
toggleMaximizePanel,
|
308
|
+
maximizedPanelId,
|
309
|
+
updatePanelSize,
|
310
|
+
setPanelVisibleById,
|
311
|
+
setPanelDisabledById,
|
312
|
+
hasVisiblePanels
|
313
|
+
}, children: children }));
|
314
|
+
};
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
export declare const TMPanelToolbar: () => import("react/jsx-runtime").JSX.Element;
|
3
|
+
export declare const StyledToolbarButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
|
4
|
+
$isActive?: boolean;
|
5
|
+
$isDisabled?: boolean;
|
6
|
+
}>> & string;
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useMemo } from 'react';
|
3
|
+
import { useTMPanelConext } from './TMPanelContext';
|
4
|
+
import styled from 'styled-components';
|
5
|
+
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
6
|
+
import TMTooltip from '../../base/TMTooltip';
|
7
|
+
export const TMPanelToolbar = () => {
|
8
|
+
const { panelTree, togglePanel, calculateEffectiveVisibility } = useTMPanelConext();
|
9
|
+
const deviceType = useDeviceType();
|
10
|
+
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
11
|
+
// Ricorsivamente trova tutti i pannelli parent (hanno figli)
|
12
|
+
const getParentIds = (panels, parents = new Set()) => {
|
13
|
+
for (const panel of panels) {
|
14
|
+
if (panel.children.length > 0) {
|
15
|
+
parents.add(panel.id);
|
16
|
+
getParentIds(panel.children, parents);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
return parents;
|
20
|
+
};
|
21
|
+
// Ricorsivamente trova tutti i pannelli foglia (senza figli)
|
22
|
+
const getLeafPanels = (panels, leaves = []) => {
|
23
|
+
for (const panel of panels) {
|
24
|
+
if (panel.children.length === 0) {
|
25
|
+
leaves.push(panel);
|
26
|
+
}
|
27
|
+
else {
|
28
|
+
getLeafPanels(panel.children, leaves);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
return leaves;
|
32
|
+
};
|
33
|
+
const parentIds = getParentIds(panelTree);
|
34
|
+
const leafPanels = getLeafPanels(panelTree);
|
35
|
+
return (_jsx("div", { style: {
|
36
|
+
display: 'flex',
|
37
|
+
flexDirection: isMobile ? 'row' : 'column',
|
38
|
+
gap: '6px',
|
39
|
+
alignItems: 'center',
|
40
|
+
justifyContent: isMobile ? 'center' : 'flex-start',
|
41
|
+
width: '100%',
|
42
|
+
height: '100%'
|
43
|
+
}, children: leafPanels.filter(panel => panel.toolbarOptions.visible).map((item) => {
|
44
|
+
const isActive = calculateEffectiveVisibility[item.id];
|
45
|
+
return _jsx(TMTooltip, { content: item.name, position: isMobile ? 'top' : 'left', children: _jsx(StyledToolbarButton, { "$isActive": isActive || item.toolbarOptions?.alwaysActiveColor, "$isDisabled": item.toolbarOptions?.disabled, disabled: item.toolbarOptions?.disabled, onClick: () => { togglePanel(item.id); }, children: typeof item.toolbarOptions?.icon === 'string' ? (_jsx("i", { className: `dx-icon dx-icon-${item.toolbarOptions?.icon}` })) : (item.toolbarOptions?.icon) }) }, item.id);
|
46
|
+
}) }));
|
47
|
+
};
|
48
|
+
export const StyledToolbarButton = styled.button `
|
49
|
+
display: flex;
|
50
|
+
align-items: center;
|
51
|
+
justify-content: center;
|
52
|
+
height: 32px;
|
53
|
+
width: 32px;
|
54
|
+
border: none;
|
55
|
+
border-radius: 8px;
|
56
|
+
font-size: 18px;
|
57
|
+
padding: 0px;
|
58
|
+
color: #fff;
|
59
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
60
|
+
cursor: ${({ $isDisabled }) => ($isDisabled ? 'not-allowed' : 'pointer')};
|
61
|
+
opacity: ${({ $isDisabled }) => ($isDisabled ? 0.6 : 1)};
|
62
|
+
background: ${({ $isActive }) => $isActive ? 'rgba(255,255,255,0.35)' : 'transparent'};
|
63
|
+
&:hover {
|
64
|
+
background: ${({ $isDisabled }) => !$isDisabled ? 'rgba(255,255,255,0.35)' : undefined};
|
65
|
+
}
|
66
|
+
`;
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import { useRef, useCallback } from 'react';
|
2
|
+
import { useTMPanelConext } from './TMPanelContext';
|
3
|
+
export function useResizablePanels(containerRef) {
|
4
|
+
const { getPanelDimensions, updatePanelSize } = useTMPanelConext();
|
5
|
+
const resizingRef = useRef(null);
|
6
|
+
const getSizePercent = useCallback((panelId, isRow) => {
|
7
|
+
const dim = getPanelDimensions(panelId);
|
8
|
+
return isRow
|
9
|
+
? parseFloat(dim.width.replace('%', ''))
|
10
|
+
: parseFloat(dim.height.replace('%', ''));
|
11
|
+
}, [getPanelDimensions]);
|
12
|
+
const getContainerSize = useCallback((isRow) => {
|
13
|
+
if (!containerRef.current)
|
14
|
+
return 0;
|
15
|
+
return isRow ? containerRef.current.clientWidth : containerRef.current.clientHeight;
|
16
|
+
}, [containerRef]);
|
17
|
+
const updateSize = useCallback((panelId, sizePercent, otherDimension, isRow) => {
|
18
|
+
if (isRow) {
|
19
|
+
updatePanelSize(panelId, `${sizePercent.toFixed(2)}%`, otherDimension);
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
updatePanelSize(panelId, otherDimension, `${sizePercent.toFixed(2)}%`);
|
23
|
+
}
|
24
|
+
}, [updatePanelSize]);
|
25
|
+
const onMouseMove = useCallback((e) => {
|
26
|
+
const data = resizingRef.current;
|
27
|
+
if (!data || !containerRef.current)
|
28
|
+
return;
|
29
|
+
const { panelBeforeId, panelAfterId, startPos, startSizeBefore, startSizeAfter, isRow } = data;
|
30
|
+
const containerSize = getContainerSize(isRow);
|
31
|
+
const currentPos = isRow ? e.clientX : e.clientY;
|
32
|
+
const delta = currentPos - startPos;
|
33
|
+
const sizeBeforePx = (startSizeBefore / 100) * containerSize + delta;
|
34
|
+
const sizeAfterPx = (startSizeAfter / 100) * containerSize - delta;
|
35
|
+
if (sizeBeforePx < 50 || sizeAfterPx < 50)
|
36
|
+
return;
|
37
|
+
const newSizeBefore = (sizeBeforePx / containerSize) * 100;
|
38
|
+
const newSizeAfter = (sizeAfterPx / containerSize) * 100;
|
39
|
+
const dimBefore = getPanelDimensions(panelBeforeId);
|
40
|
+
const dimAfter = getPanelDimensions(panelAfterId);
|
41
|
+
const otherDimBefore = isRow ? dimBefore.height : dimBefore.width;
|
42
|
+
const otherDimAfter = isRow ? dimAfter.height : dimAfter.width;
|
43
|
+
updateSize(panelBeforeId, newSizeBefore, otherDimBefore, isRow);
|
44
|
+
updateSize(panelAfterId, newSizeAfter, otherDimAfter, isRow);
|
45
|
+
}, [getContainerSize, getPanelDimensions, updateSize, containerRef]);
|
46
|
+
const onMouseUp = useCallback(() => {
|
47
|
+
resizingRef.current = null;
|
48
|
+
window.removeEventListener('mousemove', onMouseMove);
|
49
|
+
window.removeEventListener('mouseup', onMouseUp);
|
50
|
+
}, [onMouseMove]);
|
51
|
+
const onMouseDown = useCallback((e, panelBeforeId, panelAfterId, isRow) => {
|
52
|
+
e.preventDefault();
|
53
|
+
if (!containerRef.current)
|
54
|
+
return;
|
55
|
+
resizingRef.current = {
|
56
|
+
panelBeforeId,
|
57
|
+
panelAfterId,
|
58
|
+
isRow,
|
59
|
+
startPos: isRow ? e.clientX : e.clientY,
|
60
|
+
startSizeBefore: getSizePercent(panelBeforeId, isRow),
|
61
|
+
startSizeAfter: getSizePercent(panelAfterId, isRow),
|
62
|
+
};
|
63
|
+
window.addEventListener('mousemove', onMouseMove);
|
64
|
+
window.addEventListener('mouseup', onMouseUp);
|
65
|
+
}, [getSizePercent, onMouseMove, onMouseUp, containerRef]);
|
66
|
+
return { onMouseDown };
|
67
|
+
}
|