@topconsultnpm/sdkui-react 6.20.0-dev1.10 → 6.20.0-dev1.101
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/assets/Toppy-help-center.png +0 -0
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.d.ts +4 -0
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +441 -0
- package/lib/components/NewComponents/ContextMenu/hooks.d.ts +18 -0
- package/lib/components/NewComponents/ContextMenu/hooks.js +120 -0
- package/lib/components/NewComponents/ContextMenu/index.d.ts +5 -0
- package/lib/components/NewComponents/ContextMenu/index.js +3 -0
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +35 -0
- package/lib/components/NewComponents/ContextMenu/styles.js +428 -0
- package/lib/components/NewComponents/ContextMenu/types.d.ts +39 -0
- package/lib/components/NewComponents/ContextMenu/types.js +1 -0
- package/lib/components/NewComponents/ContextMenu/useLongPress.d.ts +21 -0
- package/lib/components/NewComponents/ContextMenu/useLongPress.js +112 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.d.ts +4 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +821 -0
- package/lib/components/NewComponents/FloatingMenuBar/index.d.ts +2 -0
- package/lib/components/NewComponents/FloatingMenuBar/index.js +2 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +54 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +419 -0
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +36 -0
- package/lib/components/NewComponents/FloatingMenuBar/types.js +1 -0
- package/lib/components/base/TMAccordionNew.js +35 -14
- package/lib/components/base/TMCustomButton.js +61 -17
- package/lib/components/base/TMDataGrid.d.ts +7 -4
- package/lib/components/base/TMDataGrid.js +153 -11
- package/lib/components/base/TMDropDownMenu.js +19 -18
- package/lib/components/base/TMFileManager.d.ts +4 -3
- package/lib/components/base/TMFileManager.js +32 -24
- package/lib/components/base/TMFileManagerDataGridView.d.ts +3 -2
- package/lib/components/base/TMFileManagerDataGridView.js +1 -11
- package/lib/components/base/TMFileManagerThumbnailItems.d.ts +7 -1
- package/lib/components/base/TMFileManagerThumbnailItems.js +5 -2
- package/lib/components/base/TMFileManagerThumbnailsView.d.ts +17 -4
- package/lib/components/base/TMFileManagerThumbnailsView.js +18 -6
- package/lib/components/base/TMFileManagerUtils.d.ts +0 -12
- package/lib/components/base/TMListView.js +33 -15
- package/lib/components/base/TMPanel.d.ts +1 -1
- package/lib/components/base/TMPanel.js +1 -1
- package/lib/components/choosers/TMDistinctValues.js +1 -1
- package/lib/components/choosers/TMInvoiceRetrieveFormats.js +1 -1
- package/lib/components/choosers/TMMetadataChooser.js +8 -1
- package/lib/components/choosers/TMOrderRetrieveFormats.js +1 -1
- package/lib/components/choosers/TMUserChooser.d.ts +0 -5
- package/lib/components/choosers/TMUserChooser.js +25 -45
- package/lib/components/editors/TMDateBox.js +18 -9
- package/lib/components/editors/TMMetadataValues.js +23 -5
- package/lib/components/editors/TMTextArea.js +18 -30
- package/lib/components/editors/TMTextBox.js +6 -3
- package/lib/components/features/archive/TMArchive.js +2 -2
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.d.ts +15 -0
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +460 -0
- package/lib/components/features/assistant/TMToppySpeechBubble.d.ts +11 -0
- package/lib/components/features/assistant/TMToppySpeechBubble.js +126 -0
- package/lib/components/features/documents/TMDcmtForm.d.ts +14 -2
- package/lib/components/features/documents/TMDcmtForm.js +457 -206
- package/lib/components/features/documents/TMDcmtPreview.js +45 -108
- package/lib/components/features/documents/TMDcmtTasks.js +9 -9
- package/lib/components/features/documents/TMMasterDetailDcmts.js +38 -53
- package/lib/components/features/documents/TMRelationViewer.d.ts +1 -1
- package/lib/components/features/documents/TMRelationViewer.js +2 -2
- package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +8 -0
- package/lib/components/features/search/{TMSearchResultCheckoutInfoForm.js → TMDcmtCheckoutInfoForm.js} +2 -2
- package/lib/components/features/search/TMSavedQuerySelector.js +72 -67
- package/lib/components/features/search/TMSearch.d.ts +3 -0
- package/lib/components/features/search/TMSearch.js +50 -11
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -0
- package/lib/components/features/search/TMSearchQueryPanel.js +29 -21
- package/lib/components/features/search/TMSearchResult.d.ts +3 -0
- package/lib/components/features/search/TMSearchResult.js +208 -250
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +3 -3
- package/lib/components/features/search/TMSearchResultsMenuItems.js +205 -169
- package/lib/components/features/search/TMSignSettingsForm.js +1 -1
- package/lib/components/features/search/TMSignatureInfoContent.d.ts +6 -0
- package/lib/components/features/search/TMSignatureInfoContent.js +140 -0
- package/lib/components/features/search/TMViewHistoryDcmt.js +2 -2
- package/lib/components/features/tasks/TMTaskForm.js +20 -1
- package/lib/components/features/tasks/TMTasksAgenda.d.ts +3 -1
- package/lib/components/features/tasks/TMTasksAgenda.js +48 -9
- package/lib/components/features/tasks/TMTasksCalendar.d.ts +2 -0
- package/lib/components/features/tasks/TMTasksCalendar.js +19 -7
- package/lib/components/features/tasks/TMTasksUtils.d.ts +2 -2
- package/lib/components/features/tasks/TMTasksUtils.js +43 -36
- package/lib/components/features/tasks/TMTasksView.js +28 -19
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +33 -2
- package/lib/components/features/workflow/TMWorkflowPopup.js +139 -34
- package/lib/components/features/workflow/diagram/DiagramItemComponent.d.ts +2 -0
- package/lib/components/features/workflow/diagram/DiagramItemComponent.js +12 -7
- package/lib/components/features/workflow/diagram/RecipientList.js +3 -2
- package/lib/components/features/workflow/diagram/WFDiagram.d.ts +4 -0
- package/lib/components/features/workflow/diagram/WFDiagram.js +164 -13
- package/lib/components/forms/Login/LoginValidatorService.d.ts +2 -0
- package/lib/components/forms/Login/LoginValidatorService.js +7 -2
- package/lib/components/forms/Login/TMLoginForm.js +34 -6
- package/lib/components/forms/TMChooserForm.js +1 -1
- package/lib/components/grids/TMBlogsPost.js +56 -31
- package/lib/components/grids/TMRecentsManager.js +20 -10
- package/lib/components/index.d.ts +5 -3
- package/lib/components/index.js +5 -3
- package/lib/components/query/TMQueryEditor.d.ts +2 -1
- package/lib/components/query/TMQueryEditor.js +92 -92
- package/lib/components/settings/SettingsAppearance.d.ts +2 -1
- package/lib/components/settings/SettingsAppearance.js +99 -30
- package/lib/components/viewers/TMDataListItemViewer.d.ts +2 -1
- package/lib/components/viewers/TMDataListItemViewer.js +35 -71
- package/lib/components/viewers/TMDataUserIdItemViewer.d.ts +8 -0
- package/lib/components/viewers/TMDataUserIdItemViewer.js +39 -0
- package/lib/css/tm-sdkui.css +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +22 -0
- package/lib/helper/SDKUI_Globals.js +10 -1
- package/lib/helper/SDKUI_Localizator.d.ts +17 -1
- package/lib/helper/SDKUI_Localizator.js +167 -1
- package/lib/helper/TMCommandsContextMenu.d.ts +4 -2
- package/lib/helper/TMCommandsContextMenu.js +15 -4
- package/lib/helper/TMIcons.d.ts +4 -0
- package/lib/helper/TMIcons.js +13 -3
- package/lib/helper/TMPdfViewer.d.ts +8 -0
- package/lib/helper/TMPdfViewer.js +373 -0
- package/lib/helper/checkinCheckoutManager.d.ts +31 -1
- package/lib/helper/checkinCheckoutManager.js +112 -30
- package/lib/helper/devextremeCustomMessages.d.ts +30 -0
- package/lib/helper/devextremeCustomMessages.js +30 -0
- package/lib/helper/helpers.d.ts +3 -1
- package/lib/helper/helpers.js +25 -3
- package/lib/helper/index.d.ts +2 -0
- package/lib/helper/index.js +2 -0
- package/lib/helper/queryHelper.d.ts +1 -1
- package/lib/helper/queryHelper.js +33 -3
- package/lib/helper/workItemsHelper.d.ts +6 -0
- package/lib/helper/workItemsHelper.js +230 -0
- package/lib/hooks/useCheckInOutOperations.d.ts +28 -0
- package/lib/hooks/useCheckInOutOperations.js +223 -0
- package/lib/hooks/useDataListItem.d.ts +12 -0
- package/lib/hooks/useDataListItem.js +132 -0
- package/lib/hooks/useDataUserIdItem.d.ts +10 -0
- package/lib/hooks/useDataUserIdItem.js +96 -0
- package/lib/hooks/useSettingsFeedback.d.ts +11 -0
- package/lib/hooks/useSettingsFeedback.js +38 -0
- package/lib/hooks/useWorkflowApprove.d.ts +4 -0
- package/lib/hooks/useWorkflowApprove.js +14 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -2
- package/lib/services/platform_services.d.ts +3 -3
- package/lib/ts/types.d.ts +61 -1
- package/lib/utils/theme.d.ts +1 -1
- package/lib/utils/theme.js +1 -1
- package/package.json +7 -4
- package/lib/components/base/TMContextMenu.d.ts +0 -25
- package/lib/components/base/TMContextMenu.js +0 -109
- package/lib/components/base/TMContextMenuOLD.d.ts +0 -26
- package/lib/components/base/TMContextMenuOLD.js +0 -56
- package/lib/components/base/TMFloatingToolbar.d.ts +0 -9
- package/lib/components/base/TMFloatingToolbar.js +0 -101
- package/lib/components/features/assistant/ToppyDraggableHelpCenter.d.ts +0 -30
- package/lib/components/features/assistant/ToppyDraggableHelpCenter.js +0 -482
- package/lib/components/features/assistant/ToppySpeechBubble.d.ts +0 -9
- package/lib/components/features/assistant/ToppySpeechBubble.js +0 -117
- package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +0 -8
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { StyledEditorContainer, StyledEditorIcon, StyledEditorLabel, StyledTextareaEditor } from './TMEditorStyled';
|
|
4
4
|
import { FontSize, TMColors } from '../../utils/theme';
|
|
5
|
-
import TMContextMenu
|
|
5
|
+
import TMContextMenu from '../NewComponents/ContextMenu/TMContextMenu';
|
|
6
6
|
import { TMExceptionBoxManager } from '../base/TMPopUp';
|
|
7
7
|
import TMLayoutContainer, { TMLayoutItem } from '../base/TMLayout';
|
|
8
8
|
import TMVilViewer from '../base/TMVilViewer';
|
|
@@ -35,14 +35,10 @@ const TMTextArea = (props) => {
|
|
|
35
35
|
const [isFocused, setIsFocused] = useState(false);
|
|
36
36
|
// Stores the current value of the textarea
|
|
37
37
|
const [currentValue, setCurrentValue] = useState(value);
|
|
38
|
-
// Stores the items for the context menu
|
|
39
|
-
const [formulaMenuItems, setFormulaMenuItems] = useState([]);
|
|
40
38
|
// Stores the calculated number of rows for the textarea
|
|
41
39
|
const [calculatedRows, setCalculatedRows] = useState(rows ?? 1);
|
|
42
40
|
//Show chooserForm formulaItems
|
|
43
41
|
const [showFormulaItemsChooser, setShowFormulaItemsChooser] = useState(false);
|
|
44
|
-
// Manages the state for the custom context menu
|
|
45
|
-
const { clicked, setClicked, points, setPoints } = useContextMenu();
|
|
46
42
|
const deviceType = useDeviceType();
|
|
47
43
|
// Attach a `ResizeObserver` to the textarea to monitor changes in width: dynamically updates rows based on textarea content and width
|
|
48
44
|
useEffect(() => {
|
|
@@ -69,16 +65,15 @@ const TMTextArea = (props) => {
|
|
|
69
65
|
setCalculatedRows(newRowCount);
|
|
70
66
|
}
|
|
71
67
|
}, [currentValue, autoCalculateRows]);
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
if (formulaItems
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}, [formulaItems]);
|
|
68
|
+
// Build context menu items for formulas
|
|
69
|
+
const getFormulaMenuItems = () => {
|
|
70
|
+
if (!formulaItems || formulaItems.length === 0)
|
|
71
|
+
return [];
|
|
72
|
+
return formulaItems.map(formula => ({
|
|
73
|
+
name: formula,
|
|
74
|
+
onClick: () => insertText(formula)
|
|
75
|
+
}));
|
|
76
|
+
};
|
|
82
77
|
function getFormulaDataSorce() {
|
|
83
78
|
let fiarray = [];
|
|
84
79
|
if (!formulaItems)
|
|
@@ -153,20 +148,8 @@ const TMTextArea = (props) => {
|
|
|
153
148
|
};
|
|
154
149
|
// Renders the textarea
|
|
155
150
|
const renderTextArea = () => {
|
|
156
|
-
|
|
157
|
-
onBlur?.(currentValue); }, onChange: (e) => { setCurrentValue(e.target.value); onValueChanged?.(e); },
|
|
158
|
-
if (formulaItems.length <= 0)
|
|
159
|
-
return;
|
|
160
|
-
// prevent default right click behavior
|
|
161
|
-
e.preventDefault();
|
|
162
|
-
// set our click state to true when a user right clicks
|
|
163
|
-
setClicked(true);
|
|
164
|
-
const bounds = e.currentTarget.getBoundingClientRect();
|
|
165
|
-
const x = e.clientX - bounds.left;
|
|
166
|
-
const y = e.clientY - bounds.top;
|
|
167
|
-
// set the x and y coordinates of our users right click
|
|
168
|
-
setPoints({ x, y });
|
|
169
|
-
}, "$isMobile": deviceType === DeviceType.MOBILE, "$maxHeight": maxHeight, "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, "$fontSize": fontSize, "$width": width, "$resize": resize }), _jsxs("div", { style: { display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center', position: 'absolute', right: '6px', top: calculatedRows === 1 ? (label.length > 0 ? '20px' : '7px') : (label.length > 0 ? '22px' : '7px'), pointerEvents: disabled ? 'none' : 'auto', opacity: disabled ? 0.4 : 1 }, children: [formulaItems.length > 0 &&
|
|
151
|
+
const textareaElement = _jsxs(_Fragment, { children: [_jsx(StyledTextareaEditor, { ref: inputRef, autoFocus: autoFocus, readOnly: readOnly, disabled: disabled, value: currentValue, placeholder: placeHolder, rows: calculatedRows, maxLength: maxLength, spellCheck: false, onFocus: () => setIsFocused(true), onBlur: (e) => { setIsFocused(false); if (currentValue != value)
|
|
152
|
+
onBlur?.(currentValue); }, onChange: (e) => { setCurrentValue(e.target.value); onValueChanged?.(e); }, "$isMobile": deviceType === DeviceType.MOBILE, "$maxHeight": maxHeight, "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, "$fontSize": fontSize, "$width": width, "$resize": resize }), _jsxs("div", { style: { display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center', position: 'absolute', right: '6px', top: calculatedRows === 1 ? (label.length > 0 ? '20px' : '7px') : (label.length > 0 ? '22px' : '7px'), pointerEvents: disabled ? 'none' : 'auto', opacity: disabled ? 0.4 : 1 }, children: [formulaItems.length > 0 &&
|
|
170
153
|
_jsx(StyledTextAreaEditorButton, { onClick: () => {
|
|
171
154
|
setShowFormulaItemsChooser(true);
|
|
172
155
|
}, children: _jsx(IconDataList, {}) }), showClearButton && currentValue &&
|
|
@@ -178,7 +161,12 @@ const TMTextArea = (props) => {
|
|
|
178
161
|
onBlur?.(undefined);
|
|
179
162
|
}, children: _jsx(IconClearButton, {}) }), buttons.map((buttonItem, index) => {
|
|
180
163
|
return (_jsx(StyledTextAreaEditorButton, { onClick: buttonItem.onClick, children: _jsx(TMTooltip, { content: buttonItem.text, children: buttonItem.icon }) }, buttonItem.text));
|
|
181
|
-
})] }), openFormulaItemsChooser(),
|
|
164
|
+
})] }), openFormulaItemsChooser(), _jsx(TMVilViewer, { vil: validationItems })] });
|
|
165
|
+
// Wrap with context menu if formula items exist
|
|
166
|
+
if (formulaItems.length > 0) {
|
|
167
|
+
return (_jsx(TMContextMenu, { items: getFormulaMenuItems(), trigger: "right", children: textareaElement }));
|
|
168
|
+
}
|
|
169
|
+
return textareaElement;
|
|
182
170
|
};
|
|
183
171
|
// Layout for the textarea with a left-aligned label
|
|
184
172
|
const renderedLeftLabelTextArea = () => {
|
|
@@ -9,7 +9,7 @@ import { TMExceptionBoxManager } from '../base/TMPopUp';
|
|
|
9
9
|
import TMLayoutContainer, { TMLayoutItem } from '../base/TMLayout';
|
|
10
10
|
import TMVilViewer from '../base/TMVilViewer';
|
|
11
11
|
import TMTooltip from '../base/TMTooltip';
|
|
12
|
-
import
|
|
12
|
+
import TMContextMenu from '../NewComponents/ContextMenu/TMContextMenu';
|
|
13
13
|
import { DeviceType, useDeviceType } from '../base/TMDeviceProvider';
|
|
14
14
|
import TMChooserForm from '../forms/TMChooserForm';
|
|
15
15
|
import { FormulaItemHelper } from './TMTextExpression';
|
|
@@ -94,7 +94,10 @@ const TMTextBox = ({ autoFocus, maxLength, labelColor, precision, fromModal = fa
|
|
|
94
94
|
if (formulaItems && formulaItems.length > 0) {
|
|
95
95
|
let menuItems = [];
|
|
96
96
|
for (const formula of formulaItems) {
|
|
97
|
-
menuItems.push({
|
|
97
|
+
menuItems.push({
|
|
98
|
+
name: formula,
|
|
99
|
+
onClick: () => insertText(formula)
|
|
100
|
+
});
|
|
98
101
|
}
|
|
99
102
|
setFormulaMenuItems(menuItems);
|
|
100
103
|
}
|
|
@@ -269,7 +272,7 @@ const TMTextBox = ({ autoFocus, maxLength, labelColor, precision, fromModal = fa
|
|
|
269
272
|
onBlur?.(undefined);
|
|
270
273
|
}, children: _jsx(IconClearButton, {}) }), buttons.map((buttonItem, index) => {
|
|
271
274
|
return (_jsx(StyledTextBoxEditorButton, { onClick: buttonItem.onClick, children: _jsx(TMTooltip, { content: buttonItem.text, children: buttonItem.icon }) }, buttonItem.text));
|
|
272
|
-
})] }), openFormulaItemsChooser(), formulaItems.length > 0 && (_jsx(
|
|
275
|
+
})] }), openFormulaItemsChooser(), formulaItems.length > 0 && (_jsx(TMContextMenu, { items: formulaMenuItems, target: `#text-${id}` })), _jsx(TMVilViewer, { vil: validationItems })] }));
|
|
273
276
|
};
|
|
274
277
|
const renderedLeftLabelTextBox = () => {
|
|
275
278
|
return (_jsxs(TMLayoutContainer, { direction: 'horizontal', children: [icon && _jsx(TMLayoutItem, { width: '20px', children: _jsx(StyledEditorIcon, { "$disabled": disabled, "$vil": validationItems, "$isModified": isModifiedWhen, children: icon }) }), _jsx(TMLayoutItem, { children: _jsxs(StyledEditorContainer, { "$width": width, children: [label && _jsx(StyledEditorLabel, { "$color": labelColor, "$isFocused": isFocused, "$labelPosition": labelPosition, "$disabled": disabled, children: label }), renderInputField()] }) })] }));
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import Logo from '../../../assets/Toppy-generico.png';
|
|
4
4
|
import { DcmtTypeListCacheService, LayoutModes, SDK_Localizator } from '@topconsultnpm/sdk-ts';
|
|
5
|
-
import { IconTree, SDKUI_Globals, SDKUI_Localizator, IconRecentlyViewed, IconPreview, IconShow, IconBoard, IconDcmtTypeSys, removeMruTid } from '../../../helper';
|
|
5
|
+
import { IconTree, SDKUI_Globals, SDKUI_Localizator, IconRecentlyViewed, IconPreview, IconShow, IconBoard, IconDcmtTypeSys, removeMruTid, getMoreInfoTasksForDocument } from '../../../helper';
|
|
6
6
|
import { useDeviceType, DeviceType } from '../../base/TMDeviceProvider';
|
|
7
7
|
import TMLayoutContainer from '../../base/TMLayout';
|
|
8
8
|
import TMRecentsManager from '../../grids/TMRecentsManager';
|
|
@@ -77,7 +77,7 @@ const TMArchive = ({ onDcmtTypeSelect = undefined, inputTID, inputFile = null, c
|
|
|
77
77
|
if (onDcmtTypeSelect)
|
|
78
78
|
onDcmtTypeSelect(tidToUse);
|
|
79
79
|
passToSearch(tidToUse, outputMids);
|
|
80
|
-
} : undefined, isSharedDcmt: isSharedArchive, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }, currentTID)
|
|
80
|
+
} : undefined, isSharedDcmt: isSharedArchive, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, moreInfoTasks: getMoreInfoTasksForDocument(allTasks, currentTID, currentTID === inputTID ? inputDID : undefined) }, currentTID)
|
|
81
81
|
:
|
|
82
82
|
_jsx(TMPanel, { title: 'Archiviazione', allowMaximize: false, children: _jsxs(TMLayoutContainer, { gap: 30, alignItems: 'center', justifyContent: 'center', children: [_jsx(StyledToppyTextContainer, { children: _jsx(StyledToppyText, { children: SDKUI_Localizator.DcmtTypeSelect }) }), _jsx(StyledToppyImage, { src: Logo, alt: 'Toppy' })] }) }), [currentTID, deviceType, mruTIDs, inputFile, currentInputMids, enableDragDropOverlay, isSharedArchive, allTasks]);
|
|
83
83
|
const allInitialPanelVisibility = {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface TMToppyDraggableHelpCenterProps {
|
|
3
|
+
/** Contenuto da visualizzare nella speech bubble */
|
|
4
|
+
content?: React.ReactNode;
|
|
5
|
+
/** Se true, renderizza Toppy nel document.body tramite Portal invece che nel parent */
|
|
6
|
+
usePortal?: boolean;
|
|
7
|
+
/** Allineamento iniziale del componente (destra o sinistra) */
|
|
8
|
+
align?: 'right' | 'left';
|
|
9
|
+
/** Visibilità del componente */
|
|
10
|
+
isVisible?: boolean;
|
|
11
|
+
/** Callback chiamato quando si clicca sull'immagine di Toppy */
|
|
12
|
+
onToppyImageClick?: () => void;
|
|
13
|
+
}
|
|
14
|
+
declare const TMToppyDraggableHelpCenter: (props: TMToppyDraggableHelpCenterProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export default TMToppyDraggableHelpCenter;
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import ReactDOM from 'react-dom';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
|
6
|
+
import TMToppySpeechBubble from './TMToppySpeechBubble';
|
|
7
|
+
import ToppyHelpCenterImage from '../../../assets/Toppy-help-center.png';
|
|
8
|
+
import { SDKUI_Localizator } from '../../../helper';
|
|
9
|
+
import { IconWindowMaximize } from '../../../helper/TMIcons';
|
|
10
|
+
// Keyframes per l'animazione pulse sull'immagine quando Toppy è collassato
|
|
11
|
+
const pulseAnimation = `
|
|
12
|
+
@keyframes toppyImagePulse {
|
|
13
|
+
0% {
|
|
14
|
+
transform: translateY(-2px) scale(1);
|
|
15
|
+
filter: drop-shadow(0 0 0 rgba(59, 130, 246, 0));
|
|
16
|
+
}
|
|
17
|
+
25% {
|
|
18
|
+
transform: translateY(-2px) scale(1.08);
|
|
19
|
+
filter: drop-shadow(0 0 8px rgba(59, 130, 246, 0.6));
|
|
20
|
+
}
|
|
21
|
+
50% {
|
|
22
|
+
transform: translateY(-2px) scale(1);
|
|
23
|
+
filter: drop-shadow(0 0 12px rgba(118, 75, 162, 0.5));
|
|
24
|
+
}
|
|
25
|
+
75% {
|
|
26
|
+
transform: translateY(-2px) scale(1.05);
|
|
27
|
+
filter: drop-shadow(0 0 6px rgba(240, 147, 251, 0.4));
|
|
28
|
+
}
|
|
29
|
+
100% {
|
|
30
|
+
transform: translateY(-2px) scale(1);
|
|
31
|
+
filter: drop-shadow(0 0 0 rgba(59, 130, 246, 0));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
const TMToppyButton = styled.div.attrs((props) => ({
|
|
36
|
+
style: {
|
|
37
|
+
// Applica left/top come stili inline per evitare la generazione di troppe classi CSS
|
|
38
|
+
...(props.$x !== undefined && props.$y !== undefined
|
|
39
|
+
? {
|
|
40
|
+
left: `${props.$x}px`,
|
|
41
|
+
top: `${props.$y}px`,
|
|
42
|
+
bottom: 'auto',
|
|
43
|
+
right: 'auto',
|
|
44
|
+
}
|
|
45
|
+
: {}),
|
|
46
|
+
// Cursore applicato come stile inline
|
|
47
|
+
cursor: props.$isDragging ? 'grabbing' : 'grab',
|
|
48
|
+
},
|
|
49
|
+
})) `
|
|
50
|
+
${pulseAnimation}
|
|
51
|
+
display: ${(props) => (props.$isVisible ? 'flex' : 'none')};
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
position: ${(props) => props.$usePortal ? 'fixed' : 'absolute'};
|
|
55
|
+
/* Posizionamento di default quando non è draggato (x e y non sono definiti) */
|
|
56
|
+
${(props) => props.$x === undefined || props.$y === undefined ?
|
|
57
|
+
`bottom: 0px;
|
|
58
|
+
${props.$align === 'left' ? 'left: 10px;' : 'right: 0px;'};` : ''}
|
|
59
|
+
width: ${(props) => props.$isCollapsed ? '56px' : '110px'};
|
|
60
|
+
height: ${(props) => props.$isCollapsed ? '56px' : '110px'};
|
|
61
|
+
max-width: 100%;
|
|
62
|
+
max-height: 100%;
|
|
63
|
+
z-index: 2147483647;
|
|
64
|
+
background-color: ${(props) => props.$isCollapsed ? '#ffffff' : 'transparent'};
|
|
65
|
+
border: ${(props) => props.$isCollapsed ? '3px solid #3b82f6' : 'none'};
|
|
66
|
+
border-radius: ${(props) => props.$isCollapsed ? '50%' : '0'};
|
|
67
|
+
padding: 0;
|
|
68
|
+
outline: none;
|
|
69
|
+
user-select: none;
|
|
70
|
+
transition: background-color 0.3s ease, border 0.3s ease, border-radius 0.3s ease, box-shadow 0.3s ease, width 0.3s ease, height 0.3s ease;
|
|
71
|
+
|
|
72
|
+
img {
|
|
73
|
+
width: ${(props) => props.$isCollapsed ? '40px' : '110px'};
|
|
74
|
+
height: ${(props) => props.$isCollapsed ? '40px' : '110px'};
|
|
75
|
+
pointer-events: ${(props) => (props.$isMobile ? 'auto' : 'none')};
|
|
76
|
+
transform: ${(props) => props.$isCollapsed ? 'rotate(0deg)' : props.$align === 'left' ? 'rotate(20deg)' : 'rotate(-20deg)'};
|
|
77
|
+
transition: transform 0.3s ease, width 0.3s ease, height 0.3s ease, filter 0.3s ease;
|
|
78
|
+
object-fit: contain;
|
|
79
|
+
${(props) => props.$isCollapsed && `animation: toppyImagePulse 2.5s ease-in-out infinite;`}
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
/**
|
|
83
|
+
* Pulsante di espansione quando Toppy è minimizzato
|
|
84
|
+
*/
|
|
85
|
+
const ExpandButton = styled.button `
|
|
86
|
+
position: absolute;
|
|
87
|
+
top: -8px;
|
|
88
|
+
right: -8px;
|
|
89
|
+
width: 24px;
|
|
90
|
+
height: 24px;
|
|
91
|
+
border-radius: 50%;
|
|
92
|
+
border: 2px solid rgba(255, 255, 255, 0.9);
|
|
93
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
94
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 20px rgba(118, 75, 162, 0.2);
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
100
|
+
z-index: 10001;
|
|
101
|
+
color: white;
|
|
102
|
+
font-size: 14px;
|
|
103
|
+
|
|
104
|
+
&:hover {
|
|
105
|
+
background: linear-gradient(135deg, #764ba2 0%, #f093fb 100%);
|
|
106
|
+
box-shadow: 0 6px 20px rgba(118, 75, 162, 0.6), 0 0 30px rgba(240, 147, 251, 0.4);
|
|
107
|
+
border-color: rgba(255, 255, 255, 1);
|
|
108
|
+
transform: scale(1.1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
&:active {
|
|
112
|
+
transform: scale(0.95);
|
|
113
|
+
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
&:focus {
|
|
117
|
+
outline: none;
|
|
118
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 0 3px rgba(102, 126, 234, 0.2);
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
/**
|
|
122
|
+
* Overlay trasparente per bloccare le interazioni durante il drag
|
|
123
|
+
* Previene problemi di performance con iframe e altri elementi interattivi
|
|
124
|
+
*/
|
|
125
|
+
const DragOverlay = styled.div `
|
|
126
|
+
position: fixed;
|
|
127
|
+
top: 0;
|
|
128
|
+
left: 0;
|
|
129
|
+
right: 0;
|
|
130
|
+
bottom: 0;
|
|
131
|
+
z-index: 2147483646;
|
|
132
|
+
cursor: grabbing;
|
|
133
|
+
`;
|
|
134
|
+
const TMToppyDraggableHelpCenter = (props) => {
|
|
135
|
+
const { content, usePortal, align = 'right', isVisible = true, onToppyImageClick } = props;
|
|
136
|
+
// Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
|
|
137
|
+
const deviceType = useDeviceType();
|
|
138
|
+
// This avoids unnecessary re-renders by only recalculating when deviceType changes.
|
|
139
|
+
const isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
|
140
|
+
// Ref per il contenitore principale
|
|
141
|
+
const toppyButtonRef = useRef(null);
|
|
142
|
+
// Ref per la speech bubble per calcolare le sue dimensioni
|
|
143
|
+
const bubbleRef = useRef(null);
|
|
144
|
+
// Dimensioni della bubble (aggiornate dinamicamente)
|
|
145
|
+
const bubbleDimensions = useRef({ width: 0, height: 0 });
|
|
146
|
+
// Posizione corrente del componente (null = posizione di default)
|
|
147
|
+
const [position, setPosition] = useState(null);
|
|
148
|
+
// Stato per tracciare se il componente è in fase di trascinamento
|
|
149
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
150
|
+
// Stato per controllare se il componente è collassato o espanso
|
|
151
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
152
|
+
// Refs per gestire il drag
|
|
153
|
+
const dragStartPos = useRef(null);
|
|
154
|
+
const dragOffset = useRef(null);
|
|
155
|
+
const hasMoved = useRef(false);
|
|
156
|
+
// Ref per tracciare il dragging dell'ExpandButton
|
|
157
|
+
const isExpandButtonDraggingRef = useRef(false);
|
|
158
|
+
const expandButtonMouseDownPosRef = useRef(null);
|
|
159
|
+
// Soglia minima di movimento in pixel per attivare il drag
|
|
160
|
+
const DRAG_THRESHOLD = 5;
|
|
161
|
+
// Calcola le dimensioni della bubble quando il contenuto cambia
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (bubbleRef.current) {
|
|
164
|
+
const rect = bubbleRef.current.getBoundingClientRect();
|
|
165
|
+
bubbleDimensions.current = { width: rect.width, height: rect.height };
|
|
166
|
+
}
|
|
167
|
+
}, [content, isCollapsed]);
|
|
168
|
+
// Handler per l'inizio del drag (mouse)
|
|
169
|
+
const handleMouseDown = (e) => {
|
|
170
|
+
if (isMobile)
|
|
171
|
+
return;
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
const rect = toppyButtonRef.current?.getBoundingClientRect();
|
|
174
|
+
if (!rect)
|
|
175
|
+
return;
|
|
176
|
+
// Salva la posizione iniziale del mouse e l'offset rispetto al componente
|
|
177
|
+
dragStartPos.current = { x: e.clientX, y: e.clientY };
|
|
178
|
+
dragOffset.current = {
|
|
179
|
+
x: e.clientX - rect.left,
|
|
180
|
+
y: e.clientY - rect.top,
|
|
181
|
+
};
|
|
182
|
+
hasMoved.current = false;
|
|
183
|
+
// Aggiungi listener globali per il movimento e il rilascio
|
|
184
|
+
document.addEventListener('mousemove', handleDragMove);
|
|
185
|
+
document.addEventListener('mouseup', handleDragEnd);
|
|
186
|
+
};
|
|
187
|
+
// Handler per l'inizio del drag (touch)
|
|
188
|
+
const handleTouchStart = (e) => {
|
|
189
|
+
if (!isMobile)
|
|
190
|
+
return;
|
|
191
|
+
const touch = e.touches[0];
|
|
192
|
+
if (!touch)
|
|
193
|
+
return;
|
|
194
|
+
const rect = toppyButtonRef.current?.getBoundingClientRect();
|
|
195
|
+
if (!rect)
|
|
196
|
+
return;
|
|
197
|
+
// Salva la posizione iniziale del touch e l'offset rispetto al componente
|
|
198
|
+
dragStartPos.current = { x: touch.clientX, y: touch.clientY };
|
|
199
|
+
dragOffset.current = {
|
|
200
|
+
x: touch.clientX - rect.left,
|
|
201
|
+
y: touch.clientY - rect.top,
|
|
202
|
+
};
|
|
203
|
+
hasMoved.current = false;
|
|
204
|
+
// Aggiungi listener globali per il movimento e il rilascio
|
|
205
|
+
document.addEventListener('touchmove', handleDragMove, { passive: false });
|
|
206
|
+
document.addEventListener('touchend', handleDragEnd);
|
|
207
|
+
};
|
|
208
|
+
// Handler unificato per il movimento durante il drag (mouse e touch)
|
|
209
|
+
const handleDragMove = (e) => {
|
|
210
|
+
if (!dragStartPos.current || !dragOffset.current || !toppyButtonRef.current)
|
|
211
|
+
return;
|
|
212
|
+
// Estrae le coordinate dal tipo di evento appropriato
|
|
213
|
+
const clientX = 'touches' in e ? e.touches[0]?.clientX : e.clientX;
|
|
214
|
+
const clientY = 'touches' in e ? e.touches[0]?.clientY : e.clientY;
|
|
215
|
+
if (clientX === undefined || clientY === undefined)
|
|
216
|
+
return;
|
|
217
|
+
// Calcola la distanza dal punto iniziale
|
|
218
|
+
const deltaX = clientX - dragStartPos.current.x;
|
|
219
|
+
const deltaY = clientY - dragStartPos.current.y;
|
|
220
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
221
|
+
// Attiva il drag solo se il movimento supera la soglia
|
|
222
|
+
if (!hasMoved.current && distance < DRAG_THRESHOLD)
|
|
223
|
+
return;
|
|
224
|
+
if (!hasMoved.current) {
|
|
225
|
+
hasMoved.current = true;
|
|
226
|
+
setIsDragging(true);
|
|
227
|
+
}
|
|
228
|
+
// Previeni lo scroll durante il drag (solo per touch)
|
|
229
|
+
if ('touches' in e) {
|
|
230
|
+
e.preventDefault();
|
|
231
|
+
}
|
|
232
|
+
// Calcola la nuova posizione
|
|
233
|
+
let newX = clientX - dragOffset.current.x;
|
|
234
|
+
let newY = clientY - dragOffset.current.y;
|
|
235
|
+
// Se non usa portal, converti le coordinate da viewport a relative al parent
|
|
236
|
+
if (!usePortal) {
|
|
237
|
+
const parentElement = toppyButtonRef.current.offsetParent;
|
|
238
|
+
if (parentElement) {
|
|
239
|
+
const parentRect = parentElement.getBoundingClientRect();
|
|
240
|
+
newX -= parentRect.left;
|
|
241
|
+
newY -= parentRect.top;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Ottieni le dimensioni del componente
|
|
245
|
+
const componentRect = toppyButtonRef.current.getBoundingClientRect();
|
|
246
|
+
const componentWidth = componentRect.width;
|
|
247
|
+
const componentHeight = componentRect.height;
|
|
248
|
+
// Limiti del parent considerando anche la bubble
|
|
249
|
+
let maxX = 0;
|
|
250
|
+
let maxY = 0;
|
|
251
|
+
let minX = 0;
|
|
252
|
+
let minY = 0;
|
|
253
|
+
// Considera le dimensioni della bubble per i limiti solo se non è collassato
|
|
254
|
+
const bubbleWidth = isCollapsed ? 0 : (bubbleDimensions.current.width || 0);
|
|
255
|
+
const bubbleHeight = isCollapsed ? 0 : (bubbleDimensions.current.height || 0);
|
|
256
|
+
// Offset dell'ExpandButton quando Toppy è collassato (top: -8px, right: -8px)
|
|
257
|
+
const expandButtonOffset = isCollapsed ? 8 : 0;
|
|
258
|
+
if (usePortal) {
|
|
259
|
+
// Se usa portal, i limiti sono quelli della viewport
|
|
260
|
+
// Limite superiore: deve lasciare spazio per la bubble sopra o per l'ExpandButton se collassato
|
|
261
|
+
minY = isCollapsed ? expandButtonOffset : bubbleHeight;
|
|
262
|
+
// Limite inferiore: il componente non può uscire dalla viewport
|
|
263
|
+
maxY = window.innerHeight - componentHeight;
|
|
264
|
+
// Limiti orizzontali dipendono dall'allineamento della bubble
|
|
265
|
+
if (align === 'right') {
|
|
266
|
+
// Bubble a sinistra del componente: serve spazio a sinistra
|
|
267
|
+
minX = Math.max(0, bubbleWidth - componentWidth);
|
|
268
|
+
// Quando collassato, l'ExpandButton sporge a destra di 8px
|
|
269
|
+
maxX = window.innerWidth - componentWidth - expandButtonOffset;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// Bubble a destra del componente: serve spazio a destra
|
|
273
|
+
minX = 0;
|
|
274
|
+
maxX = window.innerWidth - Math.max(componentWidth, bubbleWidth) - expandButtonOffset;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
// Se non usa portal, le coordinate devono essere relative al parent
|
|
279
|
+
const parentElement = toppyButtonRef.current.offsetParent;
|
|
280
|
+
if (parentElement) {
|
|
281
|
+
// Usa le dimensioni del parent come riferimento
|
|
282
|
+
const parentWidth = parentElement.clientWidth;
|
|
283
|
+
const parentHeight = parentElement.clientHeight;
|
|
284
|
+
// Limite superiore: deve lasciare spazio per la bubble sopra o per l'ExpandButton se collassato
|
|
285
|
+
minY = isCollapsed ? expandButtonOffset : bubbleHeight;
|
|
286
|
+
// Limite inferiore: il componente non può uscire dal parent
|
|
287
|
+
maxY = parentHeight - componentHeight;
|
|
288
|
+
// Limiti orizzontali dipendono dall'allineamento della bubble
|
|
289
|
+
if (align === 'right') {
|
|
290
|
+
// Bubble a sinistra del componente: serve spazio a sinistra
|
|
291
|
+
minX = Math.max(0, bubbleWidth - componentWidth);
|
|
292
|
+
// Quando collassato, l'ExpandButton sporge a destra di 8px
|
|
293
|
+
maxX = parentWidth - componentWidth - expandButtonOffset;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// Bubble a destra del componente: serve spazio a destra
|
|
297
|
+
minX = 0;
|
|
298
|
+
maxX = parentWidth - Math.max(componentWidth, bubbleWidth) - expandButtonOffset;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Applica i limiti
|
|
303
|
+
newX = Math.max(minX, Math.min(newX, maxX));
|
|
304
|
+
newY = Math.max(minY, Math.min(newY, maxY));
|
|
305
|
+
setPosition({ x: newX, y: newY });
|
|
306
|
+
};
|
|
307
|
+
// Handler unificato per il rilascio del drag (mouse e touch)
|
|
308
|
+
const handleDragEnd = () => {
|
|
309
|
+
dragStartPos.current = null;
|
|
310
|
+
dragOffset.current = null;
|
|
311
|
+
setIsDragging(false);
|
|
312
|
+
// Rimuovi tutti i listener globali
|
|
313
|
+
document.removeEventListener('mousemove', handleDragMove);
|
|
314
|
+
document.removeEventListener('mouseup', handleDragEnd);
|
|
315
|
+
document.removeEventListener('touchmove', handleDragMove);
|
|
316
|
+
document.removeEventListener('touchend', handleDragEnd);
|
|
317
|
+
};
|
|
318
|
+
// Handler per il doppio click (collassa/espande Toppy su desktop)
|
|
319
|
+
const toggleCollapse = () => {
|
|
320
|
+
setIsCollapsed(prev => {
|
|
321
|
+
const newIsCollapsed = !prev;
|
|
322
|
+
// Se stiamo espandendo (da collassato a espanso), dobbiamo verificare i limiti
|
|
323
|
+
if (!newIsCollapsed && position && toppyButtonRef.current && content) {
|
|
324
|
+
// Usa setTimeout per permettere al DOM di aggiornarsi con le nuove dimensioni
|
|
325
|
+
setTimeout(() => {
|
|
326
|
+
if (!toppyButtonRef.current)
|
|
327
|
+
return;
|
|
328
|
+
// Dimensioni espanse del componente (110px come da styled-component)
|
|
329
|
+
const expandedSize = 110;
|
|
330
|
+
// Ottieni le dimensioni della bubble (usa valori di default se non disponibili)
|
|
331
|
+
const bubbleWidth = bubbleDimensions.current.width || 250;
|
|
332
|
+
const bubbleHeight = bubbleDimensions.current.height || 100;
|
|
333
|
+
let newX = position.x;
|
|
334
|
+
let newY = position.y;
|
|
335
|
+
let needsUpdate = false;
|
|
336
|
+
if (usePortal) {
|
|
337
|
+
// Limiti della viewport considerando la bubble
|
|
338
|
+
const minY = bubbleHeight;
|
|
339
|
+
const maxY = window.innerHeight - expandedSize;
|
|
340
|
+
let minX;
|
|
341
|
+
let maxX;
|
|
342
|
+
if (align === 'right') {
|
|
343
|
+
minX = Math.max(0, bubbleWidth - expandedSize);
|
|
344
|
+
maxX = window.innerWidth - expandedSize;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
minX = 0;
|
|
348
|
+
maxX = window.innerWidth - Math.max(expandedSize, bubbleWidth);
|
|
349
|
+
}
|
|
350
|
+
// Verifica e correggi la posizione
|
|
351
|
+
if (newX < minX) {
|
|
352
|
+
newX = minX;
|
|
353
|
+
needsUpdate = true;
|
|
354
|
+
}
|
|
355
|
+
if (newX > maxX) {
|
|
356
|
+
newX = maxX;
|
|
357
|
+
needsUpdate = true;
|
|
358
|
+
}
|
|
359
|
+
if (newY < minY) {
|
|
360
|
+
newY = minY;
|
|
361
|
+
needsUpdate = true;
|
|
362
|
+
}
|
|
363
|
+
if (newY > maxY) {
|
|
364
|
+
newY = maxY;
|
|
365
|
+
needsUpdate = true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
// Limiti del parent
|
|
370
|
+
const offsetParent = toppyButtonRef.current.offsetParent;
|
|
371
|
+
if (offsetParent) {
|
|
372
|
+
const minY = bubbleHeight;
|
|
373
|
+
const maxY = offsetParent.clientHeight - expandedSize;
|
|
374
|
+
let minX;
|
|
375
|
+
let maxX;
|
|
376
|
+
if (align === 'right') {
|
|
377
|
+
minX = Math.max(0, bubbleWidth - expandedSize);
|
|
378
|
+
maxX = offsetParent.clientWidth - expandedSize;
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
minX = 0;
|
|
382
|
+
maxX = offsetParent.clientWidth - Math.max(expandedSize, bubbleWidth);
|
|
383
|
+
}
|
|
384
|
+
// Verifica e correggi la posizione
|
|
385
|
+
if (newX < minX) {
|
|
386
|
+
newX = minX;
|
|
387
|
+
needsUpdate = true;
|
|
388
|
+
}
|
|
389
|
+
if (newX > maxX) {
|
|
390
|
+
newX = maxX;
|
|
391
|
+
needsUpdate = true;
|
|
392
|
+
}
|
|
393
|
+
if (newY < minY) {
|
|
394
|
+
newY = minY;
|
|
395
|
+
needsUpdate = true;
|
|
396
|
+
}
|
|
397
|
+
if (newY > maxY) {
|
|
398
|
+
newY = maxY;
|
|
399
|
+
needsUpdate = true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// Aggiorna la posizione solo se necessario
|
|
404
|
+
if (needsUpdate) {
|
|
405
|
+
setPosition({ x: newX, y: newY });
|
|
406
|
+
}
|
|
407
|
+
}, 50); // Piccolo delay per permettere l'aggiornamento delle dimensioni
|
|
408
|
+
}
|
|
409
|
+
onToppyImageClick?.();
|
|
410
|
+
return newIsCollapsed;
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
// Cleanup degli event listener al dismount
|
|
414
|
+
useEffect(() => {
|
|
415
|
+
return () => {
|
|
416
|
+
document.removeEventListener('mousemove', handleDragMove);
|
|
417
|
+
document.removeEventListener('mouseup', handleDragEnd);
|
|
418
|
+
document.removeEventListener('touchmove', handleDragMove);
|
|
419
|
+
document.removeEventListener('touchend', handleDragEnd);
|
|
420
|
+
};
|
|
421
|
+
}, []);
|
|
422
|
+
// Reset della posizione quando cambia la dimensione della finestra/parent
|
|
423
|
+
// Questo evita che Toppy rimanga nascosto oltre i limiti dopo un resize
|
|
424
|
+
useEffect(() => {
|
|
425
|
+
const handleResize = () => {
|
|
426
|
+
// Resetta alla posizione iniziale (bottom-right o bottom-left in base ad align)
|
|
427
|
+
setPosition(null);
|
|
428
|
+
};
|
|
429
|
+
window.addEventListener('resize', handleResize);
|
|
430
|
+
return () => {
|
|
431
|
+
window.removeEventListener('resize', handleResize);
|
|
432
|
+
};
|
|
433
|
+
}, []);
|
|
434
|
+
const toppyContent = (_jsxs(_Fragment, { children: [isDragging && _jsx(DragOverlay, {}), _jsxs(TMToppyButton, { ref: toppyButtonRef, "$align": align, "$isDragging": isDragging, "$isVisible": isVisible, "$isCollapsed": isCollapsed, "$isMobile": isMobile, "$usePortal": usePortal, onContextMenu: (e) => e.preventDefault(), onMouseDown: !isMobile ? handleMouseDown : undefined, onTouchStart: isMobile ? handleTouchStart : undefined, onDoubleClick: !isMobile ? toggleCollapse : undefined, "$x": position?.x, "$y": position?.y, children: [(content && !isCollapsed) && _jsx(TMToppySpeechBubble, { ref: bubbleRef, align: align, onClose: toggleCollapse, children: content }), (content && isCollapsed) && (_jsx(ExpandButton, { onMouseDown: (e) => {
|
|
435
|
+
isExpandButtonDraggingRef.current = false;
|
|
436
|
+
expandButtonMouseDownPosRef.current = { x: e.clientX, y: e.clientY };
|
|
437
|
+
}, onMouseMove: (e) => {
|
|
438
|
+
if (!expandButtonMouseDownPosRef.current)
|
|
439
|
+
return;
|
|
440
|
+
const deltaX = Math.abs(e.clientX - expandButtonMouseDownPosRef.current.x);
|
|
441
|
+
const deltaY = Math.abs(e.clientY - expandButtonMouseDownPosRef.current.y);
|
|
442
|
+
// Considera drag solo se il movimento supera 3px
|
|
443
|
+
if (deltaX > 3 || deltaY > 3) {
|
|
444
|
+
isExpandButtonDraggingRef.current = true;
|
|
445
|
+
}
|
|
446
|
+
}, onMouseUp: () => {
|
|
447
|
+
expandButtonMouseDownPosRef.current = null;
|
|
448
|
+
}, onClick: (e) => {
|
|
449
|
+
if (isExpandButtonDraggingRef.current) {
|
|
450
|
+
e.stopPropagation();
|
|
451
|
+
e.preventDefault();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
e.stopPropagation();
|
|
455
|
+
toggleCollapse();
|
|
456
|
+
}, onContextMenu: (e) => e.preventDefault(), "aria-label": SDKUI_Localizator.Maximize, title: SDKUI_Localizator.Maximize, type: "button", children: _jsx(IconWindowMaximize, {}) })), _jsx("img", { src: ToppyHelpCenterImage, alt: "Toppy Help", draggable: false })] })] }));
|
|
457
|
+
// Renderizza nel document.body usando un Portal se usePortal è true, altrimenti renderizza normalmente
|
|
458
|
+
return usePortal ? ReactDOM.createPortal(toppyContent, document.body) : toppyContent;
|
|
459
|
+
};
|
|
460
|
+
export default TMToppyDraggableHelpCenter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface TMToppySpeechBubbleProps {
|
|
3
|
+
/** Contenuto da visualizzare nella speech bubble */
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/** Allineamento della speech bubble (destra o sinistra) */
|
|
6
|
+
align?: 'right' | 'left';
|
|
7
|
+
/** Callback per chiudere/minimizzare la bubble */
|
|
8
|
+
onClose?: () => void;
|
|
9
|
+
}
|
|
10
|
+
declare const TMToppySpeechBubble: React.ForwardRefExoticComponent<TMToppySpeechBubbleProps & React.RefAttributes<HTMLDivElement>>;
|
|
11
|
+
export default TMToppySpeechBubble;
|