@topconsultnpm/sdkui-react 6.22.0-dev2.2 → 6.22.0-dev2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
- package/lib/components/base/TMButton.d.ts +13 -0
- package/lib/components/base/TMButton.js +137 -5
- package/lib/components/base/TMDataGrid.d.ts +2 -0
- package/lib/components/base/TMDataGrid.js +68 -4
- package/lib/components/base/TMEditorBase.d.ts +2 -0
- package/lib/components/base/TMModal.d.ts +4 -0
- package/lib/components/base/TMModal.js +42 -11
- package/lib/components/base/TMPanel.d.ts +1 -0
- package/lib/components/base/TMPanel.js +79 -20
- package/lib/components/base/TMTooltip.d.ts +2 -1
- package/lib/components/base/TMTooltip.js +23 -6
- package/lib/components/base/TMTreeView.js +108 -67
- package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
- package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
- package/lib/components/choosers/TMDistinctValues.js +149 -142
- package/lib/components/choosers/TMMetadataChooser.js +1 -1
- package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
- package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
- package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
- package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
- package/lib/components/editors/TMDropDown.d.ts +3 -0
- package/lib/components/editors/TMDropDown.js +197 -5
- package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
- package/lib/components/editors/TMMetadataEditor.js +8 -3
- package/lib/components/editors/TMTextBox.js +15 -15
- package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
- package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
- package/lib/components/features/documents/TMDcmtForm.js +104 -42
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
- package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
- package/lib/components/features/documents/TMMasterInfoFields.js +172 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +256 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +352 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +221 -0
- package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
- package/lib/components/features/documents/TMRelationViewer.js +69 -20
- package/lib/components/features/search/TMSearch.js +29 -16
- package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
- package/lib/components/features/search/TMSearchQueryPanel.js +28 -7
- package/lib/components/features/search/TMSearchResult.d.ts +9 -0
- package/lib/components/features/search/TMSearchResult.js +323 -92
- package/lib/components/features/search/TMTreeSelector.js +18 -5
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
- package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
- package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
- package/lib/components/layout/panelManager/types.d.ts +3 -0
- package/lib/components/viewers/TMMidViewer.js +4 -1
- package/lib/components/viewers/TMTidViewer.js +25 -11
- package/lib/helper/SDKUI_Globals.d.ts +55 -0
- package/lib/helper/SDKUI_Globals.js +82 -0
- package/lib/helper/SDKUI_Localizator.d.ts +23 -0
- package/lib/helper/SDKUI_Localizator.js +230 -0
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +3 -0
- package/lib/helper/dataGridSearchHelper.d.ts +28 -0
- package/lib/helper/dataGridSearchHelper.js +51 -0
- package/lib/helper/dcmtsHelper.d.ts +20 -0
- package/lib/helper/dcmtsHelper.js +58 -1
- package/lib/helper/helpers.d.ts +20 -0
- package/lib/helper/helpers.js +40 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.d.ts +2 -0
- package/lib/helper/queryHelper.js +3 -1
- package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
- package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
- package/lib/hooks/useArchiveListForm.d.ts +39 -0
- package/lib/hooks/useArchiveListForm.js +46 -0
- package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
- package/lib/hooks/useCaseFlowApprove.js +157 -0
- package/lib/hooks/useDataUserIdItem.d.ts +1 -0
- package/lib/hooks/useDataUserIdItem.js +1 -0
- package/lib/hooks/useDocumentOperations.d.ts +2 -0
- package/lib/hooks/useDocumentOperations.js +28 -5
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +97 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +555 -0
- package/lib/hooks/useRelatedDocuments.js +2 -26
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
- package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
- package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
- package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
- package/lib/hooks/useTMDistinctValuesSource.js +105 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
- package/lib/services/caseflow/CaseFlowService.js +693 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
- package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
- package/lib/services/caseflow/caseFlowNameCache.js +116 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/index.js +4 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +66 -66
|
@@ -500,6 +500,6 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
|
|
|
500
500
|
// display: 'inline-block',
|
|
501
501
|
WebkitTouchCallout: isIOS ? 'none' : undefined,
|
|
502
502
|
WebkitUserSelect: isIOS ? 'none' : undefined,
|
|
503
|
-
}, children: children })), menuState.visible && createPortal(_jsxs(_Fragment, { children: [_jsxs(S.MenuContainer, { ref: menuRef, "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, "$needsScroll": needsScroll, "$maxHeight": maxHeight, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", "data-open-up": submenu.openUp ? 'true' : 'false', onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }), document.body)] }));
|
|
503
|
+
}, children: children })), menuState.visible && createPortal(_jsxs(_Fragment, { children: [_jsxs(S.MenuContainer, { ref: menuRef, className: "notranslate", translate: "no", "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, "$needsScroll": needsScroll, "$maxHeight": maxHeight, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { className: "notranslate", translate: "no", "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", "data-open-up": submenu.openUp ? 'true' : 'false', onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }), document.body)] }));
|
|
504
504
|
};
|
|
505
505
|
export default TMContextMenu;
|
|
@@ -837,7 +837,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
837
837
|
setState(s => ({ ...s, draggedItemIndex: null }));
|
|
838
838
|
setDragOverIndex(null);
|
|
839
839
|
};
|
|
840
|
-
return (_jsxs(S.FloatingContainer, { ref: floatingRef, "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": false, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: undefined, children: [_jsx(ContextMenu, { items: [
|
|
840
|
+
return (_jsxs(S.FloatingContainer, { ref: floatingRef, className: "notranslate", translate: "no", "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": false, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: undefined, children: [_jsx(ContextMenu, { items: [
|
|
841
841
|
{
|
|
842
842
|
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
843
843
|
icon: _jsx(IconRotate, { fontSize: 16, style: { transform: state.orientation === 'horizontal' ? 'rotate(90deg)' : 'rotate(0deg)' } }),
|
|
@@ -3,6 +3,17 @@ import { ITMEditorBase } from './TMEditorBase';
|
|
|
3
3
|
export type ButtonColors = 'error' | 'warning' | 'success' | 'info' | 'primary' | 'secondary' | 'tertiary' | 'standard' | 'primaryOutline' | 'secondaryOutline' | 'tertiaryOutline' | 'errorOutline' | 'warningOutline' | 'successOutline' | 'infoOutline';
|
|
4
4
|
export type ButtonStyle = 'normal' | 'toolbar' | 'icon' | 'text' | 'advanced';
|
|
5
5
|
export type AdvancedButtonType = 'success' | 'error' | 'tertiary' | 'primary';
|
|
6
|
+
/** Azione secondaria mostrata nel dropdown "split button" (freccia a destra) */
|
|
7
|
+
export interface ITMButtonSecondaryAction {
|
|
8
|
+
/** Etichetta mostrata nella voce di menu */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Icona opzionale a sinistra della voce */
|
|
11
|
+
icon?: React.ReactNode;
|
|
12
|
+
/** Callback al click della voce */
|
|
13
|
+
onClick?: () => void;
|
|
14
|
+
/** Disabilita la singola voce */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
6
17
|
export interface ITMButton extends ITMEditorBase {
|
|
7
18
|
color?: ButtonColors;
|
|
8
19
|
btnStyle?: ButtonStyle;
|
|
@@ -17,6 +28,8 @@ export interface ITMButton extends ITMEditorBase {
|
|
|
17
28
|
showTooltip?: boolean;
|
|
18
29
|
onClick?: VoidFunction;
|
|
19
30
|
onMouseDown?: (e: any) => any;
|
|
31
|
+
/** Azioni secondarie: se valorizzate, il bottone mostra una freccia a destra che apre un dropdown con queste azioni */
|
|
32
|
+
secondaryActions?: ITMButtonSecondaryAction[];
|
|
20
33
|
}
|
|
21
34
|
declare const TMButton: React.ForwardRefExoticComponent<ITMButton & React.RefAttributes<HTMLButtonElement>>;
|
|
22
35
|
export default TMButton;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, forwardRef } from 'react';
|
|
2
|
+
import { useState, forwardRef, useRef, useEffect } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
3
4
|
import styled from 'styled-components';
|
|
4
|
-
import { getColor } from '../../helper';
|
|
5
|
+
import { getColor, IconChevronDown } from '../../helper';
|
|
5
6
|
import { FontSize, TMColors } from '../../utils/theme';
|
|
6
7
|
import TMTooltip from './TMTooltip';
|
|
7
8
|
const StyledNormalButton = styled.button.withConfig({ shouldForwardProp: prop => !['text'].includes(prop) }) `
|
|
@@ -149,10 +150,132 @@ const StyledAdvancedButtonText = styled.div `
|
|
|
149
150
|
border-top-right-radius: 10px;
|
|
150
151
|
border-bottom-right-radius: 10px;
|
|
151
152
|
padding: 5px;
|
|
153
|
+
padding-right: ${props => props.$reserveArrow ? '26px' : '5px'};
|
|
154
|
+
`;
|
|
155
|
+
const StyledSplitContainer = styled.div `
|
|
156
|
+
display: inline-flex;
|
|
157
|
+
align-items: stretch;
|
|
158
|
+
position: relative;
|
|
159
|
+
`;
|
|
160
|
+
const StyledSplitArrow = styled.button `
|
|
161
|
+
position: absolute;
|
|
162
|
+
right: 4px;
|
|
163
|
+
top: 50%;
|
|
164
|
+
transform: translateY(-50%);
|
|
165
|
+
z-index: 2;
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
justify-content: center;
|
|
169
|
+
width: 20px;
|
|
170
|
+
height: 20px;
|
|
171
|
+
padding: 0;
|
|
172
|
+
border: none;
|
|
173
|
+
border-radius: 6px;
|
|
174
|
+
cursor: ${props => props.$isDisabled ? 'default' : 'pointer'};
|
|
175
|
+
color: white;
|
|
176
|
+
background-color: rgba(255, 255, 255, 0.22);
|
|
177
|
+
transition: background-color ease 150ms;
|
|
178
|
+
&:hover { background-color: ${props => props.$isDisabled ? 'rgba(255, 255, 255, 0.22)' : 'rgba(255, 255, 255, 0.38)'}; }
|
|
179
|
+
& svg {
|
|
180
|
+
transition: transform ease 150ms;
|
|
181
|
+
transform: rotate(${props => props.$isOpen ? '180deg' : '0deg'});
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
const StyledDropdown = styled.div `
|
|
185
|
+
position: fixed;
|
|
186
|
+
left: ${props => props.$left}px;
|
|
187
|
+
top: ${props => props.$top}px;
|
|
188
|
+
transform: translateY(-100%);
|
|
189
|
+
transform-origin: bottom left;
|
|
190
|
+
min-width: ${props => props.$minWidth}px;
|
|
191
|
+
background: #ffffff;
|
|
192
|
+
border-radius: 14px;
|
|
193
|
+
box-shadow: 0 10px 30px rgba(20, 40, 80, 0.20);
|
|
194
|
+
border: 1px solid rgba(74, 150, 210, 0.18);
|
|
195
|
+
padding: 5px;
|
|
196
|
+
z-index: 100000;
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
gap: 2px;
|
|
200
|
+
animation: tmSplitDropdownIn cubic-bezier(0.2, 0.8, 0.2, 1) 150ms;
|
|
201
|
+
@keyframes tmSplitDropdownIn {
|
|
202
|
+
from { opacity: 0; transform: translateY(calc(-100% + 8px)); }
|
|
203
|
+
to { opacity: 1; transform: translateY(-100%); }
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
const StyledDropdownItem = styled.button `
|
|
207
|
+
display: flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
gap: 9px;
|
|
210
|
+
width: 100%;
|
|
211
|
+
padding: 5px 9px;
|
|
212
|
+
border: none;
|
|
213
|
+
border-radius: 8px;
|
|
214
|
+
background: transparent;
|
|
215
|
+
text-align: left;
|
|
216
|
+
font-size: ${FontSize.defaultFontSize};
|
|
217
|
+
font-weight: 500;
|
|
218
|
+
color: ${props => props.$disabled ? '#b4b4b4' : '#2c3e50'};
|
|
219
|
+
cursor: ${props => props.$disabled ? 'default' : 'pointer'};
|
|
220
|
+
transition: background ease 150ms, color ease 150ms;
|
|
221
|
+
&:hover {
|
|
222
|
+
background: ${props => props.$disabled ? 'transparent' : 'linear-gradient(135deg, rgba(74, 150, 210, 0.16) 0%, rgba(37, 89, 165, 0.16) 100%)'};
|
|
223
|
+
color: ${props => props.$disabled ? '#b4b4b4' : '#2559A5'};
|
|
224
|
+
}
|
|
225
|
+
& > .tm-split-item-icon {
|
|
226
|
+
display: flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
width: 22px;
|
|
230
|
+
height: 22px;
|
|
231
|
+
flex-shrink: 0;
|
|
232
|
+
border-radius: 6px;
|
|
233
|
+
background: rgba(74, 150, 210, 0.14);
|
|
234
|
+
color: #4A96D2;
|
|
235
|
+
transition: background ease 150ms, color ease 150ms;
|
|
236
|
+
}
|
|
237
|
+
&:hover > .tm-split-item-icon {
|
|
238
|
+
background: ${props => props.$disabled ? 'rgba(74, 150, 210, 0.14)' : '#4A96D2'};
|
|
239
|
+
color: ${props => props.$disabled ? '#4A96D2' : '#ffffff'};
|
|
240
|
+
}
|
|
152
241
|
`;
|
|
153
242
|
const TMButton = forwardRef((props, ref) => {
|
|
154
|
-
const { width, height, keyGesture, btnStyle = 'normal', advancedColor, advancedType = 'primary', color = 'primary', fontSize = FontSize.defaultFontSize, disabled = false, showTooltip = true, caption, icon, description, padding, elementStyle, onClick = () => { }, onMouseDown = () => { } } = props;
|
|
243
|
+
const { width, height, keyGesture, btnStyle = 'normal', advancedColor, advancedType = 'primary', color = 'primary', fontSize = FontSize.defaultFontSize, disabled = false, showTooltip = true, caption, icon, description, padding, elementStyle, onClick = () => { }, onMouseDown = () => { }, secondaryActions } = props;
|
|
155
244
|
const [isHovered, setIsHovered] = useState(false);
|
|
245
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
246
|
+
const [dropdownPos, setDropdownPos] = useState({ left: 0, top: 0, minWidth: 200 });
|
|
247
|
+
const splitContainerRef = useRef(null);
|
|
248
|
+
const dropdownRef = useRef(null);
|
|
249
|
+
const hasSecondaryActions = !!secondaryActions && secondaryActions.length > 0;
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (!isDropdownOpen)
|
|
252
|
+
return;
|
|
253
|
+
const handlePointerDown = (e) => {
|
|
254
|
+
const target = e.target;
|
|
255
|
+
if (splitContainerRef.current?.contains(target) || dropdownRef.current?.contains(target))
|
|
256
|
+
return;
|
|
257
|
+
setIsDropdownOpen(false);
|
|
258
|
+
};
|
|
259
|
+
const handleKeyDown = (e) => { if (e.key === 'Escape')
|
|
260
|
+
setIsDropdownOpen(false); };
|
|
261
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
262
|
+
document.addEventListener('touchstart', handlePointerDown);
|
|
263
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
264
|
+
return () => {
|
|
265
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
266
|
+
document.removeEventListener('touchstart', handlePointerDown);
|
|
267
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
268
|
+
};
|
|
269
|
+
}, [isDropdownOpen]);
|
|
270
|
+
const toggleDropdown = () => {
|
|
271
|
+
if (disabled)
|
|
272
|
+
return;
|
|
273
|
+
if (!isDropdownOpen && splitContainerRef.current) {
|
|
274
|
+
const rect = splitContainerRef.current.getBoundingClientRect();
|
|
275
|
+
setDropdownPos({ left: rect.left, top: rect.top - 6, minWidth: Math.max(rect.width, 200) });
|
|
276
|
+
}
|
|
277
|
+
setIsDropdownOpen(prev => !prev);
|
|
278
|
+
};
|
|
156
279
|
const renderedButton = () => {
|
|
157
280
|
if (btnStyle === 'normal') {
|
|
158
281
|
return (_jsx(StyledNormalButton, { ref: ref, width: width, height: height, color: color, caption: caption, disabled: disabled, fontSize: fontSize, onClick: onClick, onMouseDown: (e) => !disabled && onMouseDown?.(e), children: caption }));
|
|
@@ -164,7 +287,7 @@ const TMButton = forwardRef((props, ref) => {
|
|
|
164
287
|
return (_jsx(StyledIconButton, { ref: ref, color: color, caption: caption, disabled: disabled, fontSize: fontSize, padding: padding, onClick: onClick, onMouseDown: (e) => !disabled && onMouseDown?.(e), children: icon }));
|
|
165
288
|
}
|
|
166
289
|
else if (btnStyle === 'advanced') {
|
|
167
|
-
return (_jsxs(StyledAdvancedButton, { ref: ref, "$width": width ?? '90px', "$height": height ?? '30px', onMouseOver: () => !disabled && setIsHovered(true), onMouseOut: () => setIsHovered(false), "$isDisabled": disabled, "$isPrimaryOutline": advancedType === 'primary' && (advancedColor === 'primaryOutline' || color === 'primaryOutline'), onClick: () => !disabled && onClick?.(), onMouseDown: (e) => !disabled && onMouseDown?.(e), children: [_jsx(StyledAdvancedButtonIcon, { "$color": advancedColor, "$isVisible": isHovered, "$isDisabled": disabled, "$type": advancedType, children: icon }), _jsx(StyledAdvancedButtonText, { "$color": advancedColor, "$isDisabled": disabled, "$type": advancedType, children: caption })] }));
|
|
290
|
+
return (_jsxs(StyledAdvancedButton, { ref: ref, "$width": width ?? '90px', "$height": height ?? '30px', onMouseOver: () => !disabled && setIsHovered(true), onMouseOut: () => setIsHovered(false), "$isDisabled": disabled, "$isPrimaryOutline": advancedType === 'primary' && (advancedColor === 'primaryOutline' || color === 'primaryOutline'), onClick: () => !disabled && onClick?.(), onMouseDown: (e) => !disabled && onMouseDown?.(e), children: [_jsx(StyledAdvancedButtonIcon, { "$color": advancedColor, "$isVisible": isHovered, "$isDisabled": disabled, "$type": advancedType, children: icon }), _jsx(StyledAdvancedButtonText, { "$color": advancedColor, "$isDisabled": disabled, "$type": advancedType, "$reserveArrow": hasSecondaryActions, children: caption })] }));
|
|
168
291
|
}
|
|
169
292
|
else {
|
|
170
293
|
return (_jsx(StyledTextButton, { ref: ref, color: color, caption: caption, disabled: disabled, fontSize: fontSize, onClick: onClick, onMouseDown: (e) => !disabled && onMouseDown?.(e), children: caption }));
|
|
@@ -175,6 +298,15 @@ const TMButton = forwardRef((props, ref) => {
|
|
|
175
298
|
_jsxs(_Fragment, { children: [caption &&
|
|
176
299
|
_jsx(StyledButtonTooltipHeader, { "$description": description, children: _jsx(StyledButtonTooltipItem, { "$color": color, children: caption }) }), description && _jsx(StyledButtonTooltipItem, { children: description })] }));
|
|
177
300
|
};
|
|
178
|
-
|
|
301
|
+
const buttonWithTooltip = showTooltip
|
|
302
|
+
? _jsx(TMTooltip, { hideAfterDelay: true, content: renderTooltip(), children: renderedButton() })
|
|
303
|
+
: renderedButton();
|
|
304
|
+
const renderSplitButton = () => (_jsxs(StyledSplitContainer, { ref: splitContainerRef, children: [buttonWithTooltip, _jsx(StyledSplitArrow, { type: 'button', "$isDisabled": disabled, "$isOpen": isDropdownOpen, "aria-haspopup": 'menu', "aria-expanded": isDropdownOpen, onClick: (e) => { e.stopPropagation(); toggleDropdown(); }, onMouseDown: (e) => e.stopPropagation(), children: _jsx(IconChevronDown, { fontSize: 14 }) }), isDropdownOpen && createPortal(_jsx(StyledDropdown, { ref: dropdownRef, role: 'menu', "$left": dropdownPos.left, "$top": dropdownPos.top, "$minWidth": dropdownPos.minWidth, children: secondaryActions.map((action, idx) => (_jsxs(StyledDropdownItem, { type: 'button', role: 'menuitem', "$disabled": action.disabled, disabled: action.disabled, onClick: () => {
|
|
305
|
+
if (action.disabled)
|
|
306
|
+
return;
|
|
307
|
+
setIsDropdownOpen(false);
|
|
308
|
+
action.onClick?.();
|
|
309
|
+
}, children: [action.icon && _jsx("span", { className: 'tm-split-item-icon', children: action.icon }), _jsx("span", { children: action.name })] }, `${action.name}-${idx}`))) }), document.body)] }));
|
|
310
|
+
return (_jsx("div", { style: elementStyle, children: hasSecondaryActions ? renderSplitButton() : buttonWithTooltip }));
|
|
179
311
|
});
|
|
180
312
|
export default TMButton;
|
|
@@ -46,6 +46,8 @@ export interface TMDataGridProps<T> extends IDataGridOptions {
|
|
|
46
46
|
showSearchPanel?: boolean;
|
|
47
47
|
/** Show the group panel */
|
|
48
48
|
showGroupPanel?: boolean;
|
|
49
|
+
/** Rende il paginatore compatto (meno alto, font e pulsanti ridotti, page numbers sostituiti da "pagina / totale"): utile quando la griglia è dentro pannelli bassi */
|
|
50
|
+
compactPager?: boolean;
|
|
49
51
|
/** Counter configuration */
|
|
50
52
|
counterConfig?: ITMCounterContainerProps;
|
|
51
53
|
/** Master Detail */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import styled, { css } from 'styled-components';
|
|
3
4
|
import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail, Position, ColumnChooserSearch, ColumnChooserSelection } from 'devextreme-react/data-grid';
|
|
4
5
|
import DataSource from 'devextreme/data/data_source';
|
|
5
6
|
import { IconAll, IconSelected, IconVisible, SDKUI_Globals, SDKUI_Localizator } from '../../helper';
|
|
@@ -18,7 +19,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
18
19
|
// main properties
|
|
19
20
|
keyExpr = 'id', dataSource, focusedRowEnabled = true, hoverStateEnabled = true, focusedRowKey, selectedRowKeys = [],
|
|
20
21
|
// custom options
|
|
21
|
-
dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showHeaderColumnChooser = false, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusTrigger = 0, counterConfig = { show: false, items: new Map() }, onHasFiltersChange, customContextMenuItems,
|
|
22
|
+
dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showHeaderColumnChooser = false, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusTrigger = 0, counterConfig = { show: false, items: new Map() }, compactPager = false, onHasFiltersChange, customContextMenuItems,
|
|
22
23
|
// events and callbacks
|
|
23
24
|
onSelectionChanged, onFocusedRowChanged, onRowDblClick, onRowClick, onCellClick, onCellDblClick, onOptionChanged, onContentReady, onContextMenuPreparing, onInitialized, onEditorPreparing, onCellPrepared, onRowPrepared, onRowUpdating, onRowExpanded, onRowCollapsed, onRowUpdated, onSaved, onEditCanceled, onEditingStart, onEditingChange, customizeColumns, onKeyDown, scrolling = { mode: 'standard', useNative: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1 }, paging = { enabled: true, pageSize: pageSize }, pager = { visible: true, showInfo: true, showNavigationButtons: true }, selection = { mode: 'multiple', showCheckBoxesMode: "always", selectAllMode: "allPages" }, sorting, summary, stateStoring, grouping, groupPanel, filterRow, headerFilter, editing, rowDragging, masterDetail,
|
|
24
25
|
// other properties
|
|
@@ -77,6 +78,13 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
77
78
|
setCustomContextMenuRowKey(rowKey);
|
|
78
79
|
}
|
|
79
80
|
else {
|
|
81
|
+
// Right-click su spazio vuoto: ricade sulla riga focalizzata solo se esiste ancora.
|
|
82
|
+
// Dopo un'eliminazione focusedRowKey può puntare a una riga non più presente:
|
|
83
|
+
// in tal caso non si mostra il menu (evita di riproporre le azioni della riga eliminata)
|
|
84
|
+
const grid = internalRef.current?.instance();
|
|
85
|
+
const focusedRowExists = focusedRowKey !== undefined && !!grid && grid.getRowIndexByKey(focusedRowKey) >= 0;
|
|
86
|
+
if (!focusedRowExists)
|
|
87
|
+
return;
|
|
80
88
|
setCustomContextMenuRowKey(focusedRowKey);
|
|
81
89
|
}
|
|
82
90
|
setCustomContextMenuVisible(true);
|
|
@@ -358,7 +366,14 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
358
366
|
// Propaga l'evento originale
|
|
359
367
|
onOptionChanged?.(e);
|
|
360
368
|
}, [onOptionChanged, onHasFiltersChange]);
|
|
361
|
-
|
|
369
|
+
// In modalità compatta il pager usa il displayMode 'compact' di DevExtreme (page numbers sostituiti da "pagina / totale"),
|
|
370
|
+
// salvo displayMode esplicito passato dal chiamante
|
|
371
|
+
const effectivePager = useMemo(() => {
|
|
372
|
+
if (!pager || !compactPager)
|
|
373
|
+
return pager;
|
|
374
|
+
return { ...pager, displayMode: pager.displayMode ?? 'compact' };
|
|
375
|
+
}, [pager, compactPager]);
|
|
376
|
+
return _jsxs(StyledDataGridContainer, { "$compactPager": compactPager, style: { width: "100%", height: "100%" }, children: [_jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
|
|
362
377
|
// main properties
|
|
363
378
|
keyExpr: keyExpr, dataSource: dataSource, selectedRowKeys: selectedRowKeys, focusedRowEnabled: focusedRowEnabled, hoverStateEnabled: hoverStateEnabled,
|
|
364
379
|
// events and callbacks
|
|
@@ -371,13 +386,62 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
371
386
|
// Temporary: iOS-specific styles to prevent default long-press menu and text selection
|
|
372
387
|
WebkitTouchCallout: 'none',
|
|
373
388
|
WebkitUserSelect: 'none'
|
|
374
|
-
}, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }),
|
|
389
|
+
}, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), effectivePager && _jsx(Pager, { ...effectivePager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { target: ".dx-data-row", items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
|
|
375
390
|
visible: customContextMenuVisible,
|
|
376
391
|
position: customContextMenuPosition,
|
|
377
392
|
onClose: () => setCustomContextMenuVisible(false)
|
|
378
393
|
} }))] });
|
|
379
394
|
});
|
|
380
395
|
export default TMDataGrid;
|
|
396
|
+
// Contenitore della griglia: in modalità compatta riduce l'ingombro verticale del pager DevExtreme
|
|
397
|
+
// (padding, altezza minima, font e dimensione dei pulsanti di pagina).
|
|
398
|
+
// Con compactPager = false non emette nessuna regola: width/height restano inline come in precedenza,
|
|
399
|
+
// così il rendering è identico a quello del div originale.
|
|
400
|
+
const StyledDataGridContainer = styled.div `
|
|
401
|
+
${({ $compactPager }) => $compactPager && css `
|
|
402
|
+
& .dx-datagrid-pager {
|
|
403
|
+
padding: 2px 4px !important;
|
|
404
|
+
min-height: 0 !important;
|
|
405
|
+
font-size: 11px;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
& .dx-datagrid-pager .dx-page,
|
|
409
|
+
& .dx-datagrid-pager .dx-page-size,
|
|
410
|
+
& .dx-datagrid-pager .dx-selection,
|
|
411
|
+
& .dx-datagrid-pager .dx-navigate-button {
|
|
412
|
+
min-width: 18px !important;
|
|
413
|
+
min-height: 18px !important;
|
|
414
|
+
height: auto !important;
|
|
415
|
+
padding: 1px 5px !important;
|
|
416
|
+
line-height: 16px !important;
|
|
417
|
+
font-size: 11px;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
& .dx-datagrid-pager .dx-pages,
|
|
421
|
+
& .dx-datagrid-pager .dx-page-sizes,
|
|
422
|
+
& .dx-datagrid-pager .dx-info,
|
|
423
|
+
& .dx-datagrid-pager .dx-page-index,
|
|
424
|
+
& .dx-datagrid-pager .dx-pages .dx-info {
|
|
425
|
+
font-size: 11px;
|
|
426
|
+
margin-top: 0 !important;
|
|
427
|
+
margin-bottom: 0 !important;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
& .dx-datagrid-pager .dx-info {
|
|
431
|
+
margin-left: 4px !important;
|
|
432
|
+
margin-right: 4px !important;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* Editor del numero di pagina (displayMode compact) */
|
|
436
|
+
& .dx-datagrid-pager .dx-page-index .dx-texteditor-input,
|
|
437
|
+
& .dx-datagrid-pager .dx-page-sizes .dx-texteditor-input {
|
|
438
|
+
min-height: 18px !important;
|
|
439
|
+
height: 18px !important;
|
|
440
|
+
padding: 0 4px !important;
|
|
441
|
+
font-size: 11px;
|
|
442
|
+
}
|
|
443
|
+
`}
|
|
444
|
+
`;
|
|
381
445
|
const getRecordCount = (dataSource) => {
|
|
382
446
|
if (Array.isArray(dataSource)) {
|
|
383
447
|
return dataSource.length; // For array
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { ValidationItem } from "@topconsultnpm/sdk-ts";
|
|
2
3
|
export type Colors = 'error' | 'warning' | 'success' | 'info' | 'primary' | 'secondary' | 'tertiary' | 'standard';
|
|
3
4
|
type IconModes = 'outside' | 'inside' | 'none';
|
|
@@ -6,6 +7,7 @@ export type Sizes = 'small' | 'medium' | 'large';
|
|
|
6
7
|
export type TMEditorButtonData = {
|
|
7
8
|
icon: any;
|
|
8
9
|
text?: string;
|
|
10
|
+
tooltipContent?: React.ReactNode;
|
|
9
11
|
onClick?: () => void;
|
|
10
12
|
};
|
|
11
13
|
export interface ITMEditorBase {
|
|
@@ -14,6 +14,10 @@ interface ITMModal {
|
|
|
14
14
|
askClosingConfirm?: boolean;
|
|
15
15
|
showCloseButton?: boolean;
|
|
16
16
|
showHeader?: boolean;
|
|
17
|
+
/** Se true, ricentra il popup ogni volta che cambiano width/height, annullando lo spostamento manuale */
|
|
18
|
+
recenterOnResize?: boolean;
|
|
19
|
+
/** Contenuto aggiuntivo nella barra del titolo, prima del pulsante di chiusura (es. un menu di opzioni) */
|
|
20
|
+
titleActions?: React.ReactNode;
|
|
17
21
|
}
|
|
18
22
|
declare const TMModal: React.FC<ITMModal>;
|
|
19
23
|
export default TMModal;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { Popup } from 'devextreme-react';
|
|
4
|
+
import { ToolbarItem } from 'devextreme-react/popup';
|
|
4
5
|
import styled from 'styled-components';
|
|
5
6
|
import { TMCard } from './TMLayout';
|
|
6
7
|
import { FontSize, TMColors } from '../../utils/theme';
|
|
7
8
|
import { IconWindowMaximize, IconWindowMinimize, svgToString } from '../../helper';
|
|
9
|
+
/** Valori di `height` con cui il popup si dimensiona sul contenuto (vedi applyContentFillLayout) */
|
|
10
|
+
const CONTENT_DRIVEN_HEIGHTS = ['auto', 'max-content', 'min-content', 'fit-content'];
|
|
8
11
|
const StyledModal = styled.div `
|
|
9
12
|
width: ${props => props.$width};
|
|
10
13
|
height: ${props => props.$height};
|
|
@@ -39,10 +42,13 @@ const StyledModalContext = styled.div `
|
|
|
39
42
|
overflow: auto;
|
|
40
43
|
height: 100%;
|
|
41
44
|
`;
|
|
42
|
-
const TMModal = ({ resizable = true, expandable = false, isModal = true, title = '', toolbar, onClose, children, width = '100%', height = '100%', fontSize = FontSize.defaultFontSize, hidePopup = true, askClosingConfirm = false, showCloseButton = true, showHeader = true }) => {
|
|
45
|
+
const TMModal = ({ resizable = true, expandable = false, isModal = true, title = '', toolbar, onClose, children, width = '100%', height = '100%', fontSize = FontSize.defaultFontSize, hidePopup = true, askClosingConfirm = false, showCloseButton = true, showHeader = true, recenterOnResize = false, titleActions }) => {
|
|
43
46
|
const popupRef = useRef(null);
|
|
44
47
|
const [initialWidth, setInitialWidth] = useState(width);
|
|
45
48
|
const [initialHeight, setInitialHeight] = useState(height);
|
|
49
|
+
// Posizione controllata: un nuovo riferimento oggetto forza DevExtreme a riposizionare (ricentrare).
|
|
50
|
+
// Lasciata invariata durante il drag, così lo spostamento manuale viene preservato
|
|
51
|
+
const [position, setPosition] = useState(undefined);
|
|
46
52
|
const [showPopup, setShowPopup] = useState(false);
|
|
47
53
|
const [isResizing, setIsResizing] = useState(false);
|
|
48
54
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
@@ -51,6 +57,26 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
51
57
|
const DEFAULT_MIN_HEIGHT = 400;
|
|
52
58
|
const [minWidth, setMinWidth] = useState(0);
|
|
53
59
|
const [minHeight, setMinHeight] = useState(0);
|
|
60
|
+
/**
|
|
61
|
+
* DevExtreme fissa in px l'altezza del contenuto del popup: se le dimensioni cambiano a popup
|
|
62
|
+
* già aperto (es. cambio step) resta quella del passo precedente e sotto rimane spazio vuoto.
|
|
63
|
+
* Come figlio flex il contenuto riempie il popup senza dipendere da quel calcolo.
|
|
64
|
+
* Non si applica alle altezze dimensionate sul contenuto (`auto`, `max-content`, ...):
|
|
65
|
+
* lì è il contenuto a dare l'altezza al popup e non c'è spazio da riempire.
|
|
66
|
+
*/
|
|
67
|
+
const applyContentFillLayout = () => {
|
|
68
|
+
const content = popupRef.current?.instance()?.content();
|
|
69
|
+
const overlayContent = content?.parentElement;
|
|
70
|
+
if (!content || !overlayContent)
|
|
71
|
+
return;
|
|
72
|
+
const fill = !CONTENT_DRIVEN_HEIGHTS.some(value => initialHeight.trim().startsWith(value));
|
|
73
|
+
overlayContent.style.display = fill ? 'flex' : '';
|
|
74
|
+
overlayContent.style.flexDirection = fill ? 'column' : '';
|
|
75
|
+
content.style.flex = fill ? '1 1 auto' : '';
|
|
76
|
+
// Testata e toolbar inferiore restano alla loro altezza naturale
|
|
77
|
+
overlayContent.querySelectorAll(':scope > .dx-popup-title, :scope > .dx-popup-bottom')
|
|
78
|
+
.forEach(el => { el.style.flexShrink = fill ? '0' : ''; });
|
|
79
|
+
};
|
|
54
80
|
/**
|
|
55
81
|
* Gestore chiamato quando il popup viene mostrato.
|
|
56
82
|
* Calcola e imposta le dimensioni minime del popup in base allo spazio disponibile.
|
|
@@ -59,6 +85,7 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
59
85
|
const handleShown = () => {
|
|
60
86
|
// Usa requestAnimationFrame per attendere il completamento del rendering
|
|
61
87
|
requestAnimationFrame(() => {
|
|
88
|
+
applyContentFillLayout();
|
|
62
89
|
if (popupRef.current && popupRef.current.instance) {
|
|
63
90
|
const instance = popupRef.current.instance();
|
|
64
91
|
if (instance) {
|
|
@@ -85,7 +112,17 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
85
112
|
useEffect(() => {
|
|
86
113
|
setInitialWidth(width);
|
|
87
114
|
setInitialHeight(height);
|
|
88
|
-
|
|
115
|
+
// Al cambio di dimensioni (es. cambio step) ricentra il popup con un nuovo riferimento oggetto,
|
|
116
|
+
// annullando l'eventuale spostamento manuale precedente
|
|
117
|
+
if (recenterOnResize)
|
|
118
|
+
setPosition({ my: 'center', at: 'center', of: window });
|
|
119
|
+
}, [width, height, recenterOnResize]);
|
|
120
|
+
// Dimensioni, testata e schermo pieno fanno rigenerare geometria ed elementi del popup:
|
|
121
|
+
// il layout flex del contenuto va riapplicato dopo il ridisegno (vedi applyContentFillLayout)
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
const rafId = requestAnimationFrame(() => applyContentFillLayout());
|
|
124
|
+
return () => cancelAnimationFrame(rafId);
|
|
125
|
+
}, [initialWidth, initialHeight, title, isFullScreen, showPopup]);
|
|
89
126
|
const handleResizeStart = () => {
|
|
90
127
|
setIsResizing(true);
|
|
91
128
|
};
|
|
@@ -101,15 +138,9 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
101
138
|
setShowPopup(false);
|
|
102
139
|
onClose && onClose();
|
|
103
140
|
};
|
|
104
|
-
return (_jsx(_Fragment, { children: isModal ? (
|
|
105
|
-
{
|
|
106
|
-
widget: 'dxButton',
|
|
107
|
-
location: 'after',
|
|
108
|
-
options: {
|
|
141
|
+
return (_jsx(_Fragment, { children: isModal ? (_jsxs(Popup, { ref: popupRef, showCloseButton: showHeader ? showCloseButton : false, showTitle: showHeader, animation: undefined, minWidth: minWidth, minHeight: minHeight, maxHeight: '95%', maxWidth: '95%', dragEnabled: !isResizing && !isFullScreen, resizeEnabled: resizable && !isFullScreen, position: expandable && isFullScreen ? { my: 'center', at: 'center', of: window } : position, width: expandable && isFullScreen ? '95%' : initialWidth, height: expandable && isFullScreen ? '95%' : initialHeight, title: title, visible: showPopup, onShown: handleShown, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd, onHiding: onHiding, children: [showHeader && titleActions && (_jsx(ToolbarItem, { toolbar: 'top', location: 'after', render: () => _jsx(_Fragment, { children: titleActions }) })), showHeader && expandable && (_jsx(ToolbarItem, { toolbar: 'top', location: 'after', widget: 'dxButton', options: {
|
|
109
142
|
icon: isFullScreen ? svgToString(_jsx(IconWindowMinimize, {})) : svgToString(_jsx(IconWindowMaximize, {})),
|
|
110
143
|
onClick: () => setIsFullScreen(!isFullScreen)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
] : undefined, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }, children: [toolbar && (_jsx(StyledModalToolbar, { children: toolbar })), _jsx("div", { style: { flex: 1, overflow: 'hidden', minHeight: 0 }, children: _jsx(TMCard, { showBorder: false, padding: false, scrollY: true, children: children }) })] }) })) : (_jsxs(StyledModal, { "$isModal": isModal, className: "temp-modal", "$fontSize": fontSize, "$width": initialWidth, "$height": initialHeight, children: [toolbar ? _jsx(StyledModalToolbar, { children: toolbar }) : _jsx(_Fragment, {}), _jsx(StyledModalContext, { children: children })] })) }));
|
|
144
|
+
} })), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }, children: [toolbar && (_jsx(StyledModalToolbar, { children: toolbar })), _jsx("div", { style: { flex: 1, overflow: 'hidden', minHeight: 0 }, children: _jsx(TMCard, { showBorder: false, padding: false, scrollY: true, children: children }) })] })] })) : (_jsxs(StyledModal, { "$isModal": isModal, className: "temp-modal", "$fontSize": fontSize, "$width": initialWidth, "$height": initialHeight, children: [toolbar ? _jsx(StyledModalToolbar, { children: toolbar }) : _jsx(_Fragment, {}), _jsx(StyledModalContext, { children: children })] })) }));
|
|
114
145
|
};
|
|
115
146
|
export default TMModal;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useRef, useState, forwardRef, useImperativeHandle } from 'react';
|
|
3
|
-
import styled from 'styled-components';
|
|
3
|
+
import styled, { css, keyframes } from 'styled-components';
|
|
4
4
|
import { getAppModuleGradient, IconArrowLeft, IconClearButton, IconWindowMaximize, IconWindowMinimize, isPositiveNumber, SDKUI_Localizator } from '../../helper';
|
|
5
5
|
import TMButton from './TMButton';
|
|
6
6
|
import { Gutters } from '../../utils/theme';
|
|
@@ -96,6 +96,77 @@ const StyledPanelContent = styled.div `
|
|
|
96
96
|
outline: none;
|
|
97
97
|
}
|
|
98
98
|
`;
|
|
99
|
+
// Allineata al TABLET_BREAKPOINT (768) di TMDeviceProvider
|
|
100
|
+
const MOBILE_MEDIA_QUERY = '(max-width: 767.98px)';
|
|
101
|
+
// L'eccedenza da scorrere è "larghezza viewport - larghezza testo", ma nessuna delle due
|
|
102
|
+
// è nota al CSS. La si ottiene componendo due percentuali che si risolvono su box diversi:
|
|
103
|
+
// la track (larga quanto il viewport) va a +100% della PROPRIA larghezza, il testo a -100%
|
|
104
|
+
// della SUA. Le due animazioni hanno stessa durata ed easing, quindi lo scostamento netto
|
|
105
|
+
// in ogni istante è esattamente e(t) * (viewport - testo).
|
|
106
|
+
const panelTitleTrackMarquee = keyframes `
|
|
107
|
+
0%, 15% { transform: translateX(0); }
|
|
108
|
+
45%, 55% { transform: translateX(100%); }
|
|
109
|
+
85%, 100% { transform: translateX(0); }
|
|
110
|
+
`;
|
|
111
|
+
const panelTitleTextMarquee = keyframes `
|
|
112
|
+
0%, 15% { transform: translateX(0); }
|
|
113
|
+
45%, 55% { transform: translateX(-100%); }
|
|
114
|
+
85%, 100% { transform: translateX(0); }
|
|
115
|
+
`;
|
|
116
|
+
// Ritaglia il testo in eccesso: è l'unico box con overflow hidden, la track ci scorre dentro
|
|
117
|
+
const StyledPanelTitleViewport = styled.div `
|
|
118
|
+
flex: 1;
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
justify-content: flex-start;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
padding: 0 8px;
|
|
125
|
+
`;
|
|
126
|
+
// Larga quanto il viewport: è il metro con cui il -100% del testo viene compensato.
|
|
127
|
+
const StyledPanelTitleTrack = styled.div `
|
|
128
|
+
flex: 1;
|
|
129
|
+
min-width: 0;
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
|
|
133
|
+
${({ $marquee }) => $marquee && css `
|
|
134
|
+
@media ${MOBILE_MEDIA_QUERY} {
|
|
135
|
+
animation: ${panelTitleTrackMarquee} 10s ease-in-out infinite;
|
|
136
|
+
|
|
137
|
+
@media (prefers-reduced-motion: reduce) {
|
|
138
|
+
animation: none;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
`}
|
|
142
|
+
`;
|
|
143
|
+
// Senza $marquee resta il troncamento con i puntini di sempre
|
|
144
|
+
const StyledPanelTitle = styled.div `
|
|
145
|
+
margin: 0;
|
|
146
|
+
white-space: nowrap;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
text-overflow: ellipsis;
|
|
149
|
+
|
|
150
|
+
${({ $marquee }) => $marquee && css `
|
|
151
|
+
@media ${MOBILE_MEDIA_QUERY} {
|
|
152
|
+
flex: 0 0 auto;
|
|
153
|
+
width: max-content;
|
|
154
|
+
min-width: 100%;
|
|
155
|
+
max-width: none;
|
|
156
|
+
overflow: visible;
|
|
157
|
+
text-overflow: clip;
|
|
158
|
+
animation: ${panelTitleTextMarquee} 10s ease-in-out infinite;
|
|
159
|
+
|
|
160
|
+
@media (prefers-reduced-motion: reduce) {
|
|
161
|
+
animation: none;
|
|
162
|
+
width: auto;
|
|
163
|
+
min-width: 0;
|
|
164
|
+
overflow: hidden;
|
|
165
|
+
text-overflow: ellipsis;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`}
|
|
169
|
+
`;
|
|
99
170
|
/**
|
|
100
171
|
* TMPanel - Pannello con supporto Controlled/Uncontrolled
|
|
101
172
|
*
|
|
@@ -103,7 +174,7 @@ const StyledPanelContent = styled.div `
|
|
|
103
174
|
* - Se NON passata → Uncontrolled: gestione interna dello stato attivo (plug-and-play)
|
|
104
175
|
* - Se passata → Controlled: il parent gestisce lo stato tramite onActivate
|
|
105
176
|
*/
|
|
106
|
-
const TMPanel = forwardRef(({ allowMaximize = true, color, backgroundColor, backgroundColorContainer, children, showHeader = true, title, totalItems, displayedItemsCount, toolbar, padding = '5px', isVisible = true, panelID = 'tmpanel', isActive, // Questa prop determinerà la modalità
|
|
177
|
+
const TMPanel = forwardRef(({ allowMaximize = true, color, backgroundColor, backgroundColorContainer, children, showHeader = true, title, enableTitleMarquee = false, totalItems, displayedItemsCount, toolbar, padding = '5px', isVisible = true, panelID = 'tmpanel', isActive, // Questa prop determinerà la modalità
|
|
107
178
|
onActivate, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged }, ref) => {
|
|
108
179
|
const [isMaximized, setIsMaximized] = useState(false);
|
|
109
180
|
// Detect device type (e.g., mobile or desktop)
|
|
@@ -157,6 +228,7 @@ onActivate, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged },
|
|
|
157
228
|
handleFocusUncontrolled();
|
|
158
229
|
}
|
|
159
230
|
};
|
|
231
|
+
const titleMarqueeKey = enableTitleMarquee && (typeof title === 'string' || typeof title === 'number') ? String(title) : undefined;
|
|
160
232
|
// handler for external maximize management
|
|
161
233
|
const handleMaximize = () => {
|
|
162
234
|
setIsMaximized(prevState => {
|
|
@@ -188,24 +260,11 @@ onActivate, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged },
|
|
|
188
260
|
flexDirection: 'row',
|
|
189
261
|
alignItems: 'center',
|
|
190
262
|
flexShrink: 0
|
|
191
|
-
}, children: _jsxs(StyledBackButton, { "$isActive": currentIsActive, onClick: () => onBack ? onBack() : null, title: SDKUI_Localizator.Back, children: [_jsx(IconArrowLeft, {}), !isMobile && _jsx("span", { children: SDKUI_Localizator.Back })] }) }), _jsx("
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
minWidth: 0,
|
|
197
|
-
overflow: 'hidden',
|
|
198
|
-
padding: '0 8px'
|
|
199
|
-
}, children: _jsxs("div", { style: {
|
|
200
|
-
whiteSpace: 'nowrap',
|
|
201
|
-
overflow: 'hidden',
|
|
202
|
-
textOverflow: 'ellipsis',
|
|
203
|
-
margin: 0
|
|
204
|
-
}, children: [title, isPositiveNumber(displayedItemsCount) && isPositiveNumber(totalItems)
|
|
205
|
-
? ` (${displayedItemsCount} / ${totalItems})`
|
|
206
|
-
: isPositiveNumber(totalItems)
|
|
207
|
-
? ` (${totalItems})`
|
|
208
|
-
: ''] }) }), _jsxs("div", { style: {
|
|
263
|
+
}, children: _jsxs(StyledBackButton, { "$isActive": currentIsActive, onClick: () => onBack ? onBack() : null, title: SDKUI_Localizator.Back, children: [_jsx(IconArrowLeft, {}), !isMobile && _jsx("span", { children: SDKUI_Localizator.Back })] }) }), _jsx(StyledPanelTitleViewport, { children: _jsx(StyledPanelTitleTrack, { "$marquee": enableTitleMarquee, children: _jsxs(StyledPanelTitle, { "$marquee": enableTitleMarquee, children: [title, isPositiveNumber(displayedItemsCount) && isPositiveNumber(totalItems)
|
|
264
|
+
? ` (${displayedItemsCount} / ${totalItems})`
|
|
265
|
+
: isPositiveNumber(totalItems)
|
|
266
|
+
? ` (${totalItems})`
|
|
267
|
+
: ''] }) }, titleMarqueeKey) }), _jsxs("div", { style: {
|
|
209
268
|
display: 'flex',
|
|
210
269
|
flexDirection: 'row',
|
|
211
270
|
alignItems: 'center',
|
|
@@ -9,6 +9,7 @@ interface ITMTooltipProps extends ITooltipOptions {
|
|
|
9
9
|
hideAfterDelay?: boolean;
|
|
10
10
|
parentStyle?: React.CSSProperties;
|
|
11
11
|
childStyle?: React.CSSProperties;
|
|
12
|
+
allowContentInteraction?: boolean;
|
|
12
13
|
}
|
|
13
|
-
declare const TMTooltip: ({ children, position, content, hideAfterDelay, parentStyle, childStyle }: ITMTooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare const TMTooltip: ({ children, position, content, hideAfterDelay, parentStyle, childStyle, allowContentInteraction }: ITMTooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
15
|
export default TMTooltip;
|