@topconsultnpm/sdkui-react 6.19.0-dev2.52 → 6.19.0-dev2.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/base/TMModal.js +1 -1
- package/lib/components/base/TMPopUp.js +4 -1
- package/lib/components/features/assistant/ToppyDraggableHelpCenter.d.ts +2 -0
- package/lib/components/features/assistant/ToppyDraggableHelpCenter.js +186 -51
- package/lib/components/features/assistant/ToppySpeechBubble.d.ts +1 -0
- package/lib/components/features/assistant/ToppySpeechBubble.js +68 -4
- package/lib/components/features/documents/TMDcmtForm.js +2 -2
- package/lib/components/features/search/TMSearchQueryPanel.js +2 -3
- package/lib/components/features/search/TMSearchResult.js +11 -12
- package/lib/components/index.d.ts +0 -1
- package/lib/components/index.js +0 -1
- package/lib/helper/TMToppyMessage.js +2 -1
- package/package.json +1 -1
- package/lib/components/features/assistant/ToppyHelpCenter.d.ts +0 -12
- package/lib/components/features/assistant/ToppyHelpCenter.js +0 -173
|
@@ -110,6 +110,6 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
110
110
|
onClick: () => setIsFullScreen(!isFullScreen)
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
] : undefined, children: _jsxs(TMLayoutContainer, { children: [toolbar && (_jsx(TMLayoutItem, { height: "40px", children: _jsx(StyledModalToolbar, { children: toolbar }) })), _jsx(TMLayoutItem, { height: toolbar ? 'calc(100% -
|
|
113
|
+
] : undefined, children: _jsxs(TMLayoutContainer, { children: [toolbar && (_jsx(TMLayoutItem, { height: "40px", children: _jsx(StyledModalToolbar, { children: toolbar }) })), _jsx(TMLayoutItem, { height: toolbar ? 'calc(100% - 40px)' : '100%', 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
114
|
};
|
|
115
115
|
export default TMModal;
|
|
@@ -33,7 +33,8 @@ const StyledExeptionToolbar = styled.div `
|
|
|
33
33
|
background-color: white;
|
|
34
34
|
`;
|
|
35
35
|
const StyledMessageToolbar = styled.div `
|
|
36
|
-
position:
|
|
36
|
+
position: sticky;
|
|
37
|
+
bottom: 0;
|
|
37
38
|
display: flex;
|
|
38
39
|
flex-direction: row;
|
|
39
40
|
justify-content: flex-end;
|
|
@@ -95,6 +96,7 @@ const ResponsiveMessageContainer = styled.div `
|
|
|
95
96
|
height: 100%;
|
|
96
97
|
width: 100%;
|
|
97
98
|
background-color: #ffffff;
|
|
99
|
+
overflow: hidden;
|
|
98
100
|
`;
|
|
99
101
|
const ResponsiveMessageContent = styled.div `
|
|
100
102
|
flex: 1;
|
|
@@ -105,6 +107,7 @@ const ResponsiveMessageContent = styled.div `
|
|
|
105
107
|
gap: clamp(8px, 2vw, 20px);
|
|
106
108
|
padding: clamp(8px, 3vw, 25px) clamp(5px, 2vw, 30px);
|
|
107
109
|
min-height: clamp(80px, 15vh, 120px);
|
|
110
|
+
overflow: auto;
|
|
108
111
|
|
|
109
112
|
@media (min-width: 300px) {
|
|
110
113
|
flex-direction: ${props => props.$isMobile ? 'column' : 'row'};
|
|
@@ -16,6 +16,8 @@ interface ToppyDraggableHelpCenterProps {
|
|
|
16
16
|
onToppyImageClick?: () => void;
|
|
17
17
|
/** Visibilità del componente */
|
|
18
18
|
isVisible?: boolean;
|
|
19
|
+
/** Se true, renderizza Toppy nel document.body tramite Portal invece che nel parent */
|
|
20
|
+
usePortal?: boolean;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Componente ToppyDraggableHelpCenter
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useState, useEffect } from 'react';
|
|
3
|
+
import ReactDOM from 'react-dom';
|
|
3
4
|
import styled from 'styled-components';
|
|
4
5
|
import Toppy from '../../../assets/Toppy-generico.png';
|
|
5
6
|
import { DeviceType } from '../../base/TMDeviceProvider';
|
|
6
7
|
import ToppySpeechBubble from './ToppySpeechBubble';
|
|
8
|
+
import { SDKUI_Localizator } from '../../../helper';
|
|
9
|
+
import { IconWindowMaximize } from '../../../helper/TMIcons';
|
|
7
10
|
/**
|
|
8
11
|
* Styled component per il contenitore di Toppy
|
|
9
12
|
* Gestisce il posizionamento, le dimensioni e le animazioni
|
|
@@ -25,7 +28,7 @@ const ToppyButton = styled.div.attrs((props) => ({
|
|
|
25
28
|
})) `
|
|
26
29
|
/* Visibilità controllata dalla prop */
|
|
27
30
|
display: ${(props) => (props.$isVisible ? 'flex' : 'none')};
|
|
28
|
-
position: absolute;
|
|
31
|
+
position: ${(props) => props.$usePortal ? 'fixed' : 'absolute'};
|
|
29
32
|
|
|
30
33
|
/* Posizionamento di default quando non è draggato (x e y non sono definiti) */
|
|
31
34
|
${(props) => props.$x === undefined || props.$y === undefined
|
|
@@ -106,6 +109,45 @@ const ToppyButton = styled.div.attrs((props) => ({
|
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
`;
|
|
112
|
+
/**
|
|
113
|
+
* Pulsante di espansione quando Toppy è minimizzato
|
|
114
|
+
*/
|
|
115
|
+
const ExpandButton = styled.button `
|
|
116
|
+
position: absolute;
|
|
117
|
+
top: -8px;
|
|
118
|
+
right: -8px;
|
|
119
|
+
width: 24px;
|
|
120
|
+
height: 24px;
|
|
121
|
+
border-radius: 50%;
|
|
122
|
+
border: 2px solid rgba(255, 255, 255, 0.9);
|
|
123
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
124
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 20px rgba(118, 75, 162, 0.2);
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
display: flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
justify-content: center;
|
|
129
|
+
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
130
|
+
z-index: 10001;
|
|
131
|
+
color: white;
|
|
132
|
+
font-size: 14px;
|
|
133
|
+
|
|
134
|
+
&:hover {
|
|
135
|
+
background: linear-gradient(135deg, #764ba2 0%, #f093fb 100%);
|
|
136
|
+
box-shadow: 0 6px 20px rgba(118, 75, 162, 0.6), 0 0 30px rgba(240, 147, 251, 0.4);
|
|
137
|
+
border-color: rgba(255, 255, 255, 1);
|
|
138
|
+
transform: scale(1.1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&:active {
|
|
142
|
+
transform: scale(0.95);
|
|
143
|
+
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
&:focus {
|
|
147
|
+
outline: none;
|
|
148
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 0 3px rgba(102, 126, 234, 0.2);
|
|
149
|
+
}
|
|
150
|
+
`;
|
|
109
151
|
/**
|
|
110
152
|
* Overlay trasparente per bloccare le interazioni durante il drag
|
|
111
153
|
* Previene problemi di performance con iframe e altri elementi interattivi
|
|
@@ -126,7 +168,7 @@ const DragOverlay = styled.div `
|
|
|
126
168
|
* in una speech bubble. Il componente può essere trascinato all'interno del suo
|
|
127
169
|
* contenitore e può essere collassato/espanso con un doppio click.
|
|
128
170
|
*/
|
|
129
|
-
const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onToppyImageClick, initialIsCollapsed, isVisible = true, }) => {
|
|
171
|
+
const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onToppyImageClick, initialIsCollapsed, isVisible = true, usePortal = false, }) => {
|
|
130
172
|
// Ref per il contenitore principale
|
|
131
173
|
const buttonRef = useRef(null);
|
|
132
174
|
// Stato per controllare se il componente è collassato o espanso
|
|
@@ -140,6 +182,9 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
140
182
|
// Ref e stato per tracciare le dimensioni della speech bubble
|
|
141
183
|
const bubbleRef = useRef(null);
|
|
142
184
|
const [bubbleSize, setBubbleSize] = useState({ width: 0, height: 0 });
|
|
185
|
+
// Ref per tracciare il dragging dell'ExpandButton
|
|
186
|
+
const isExpandButtonDraggingRef = useRef(false);
|
|
187
|
+
const expandButtonMouseDownPosRef = useRef(null);
|
|
143
188
|
const isMobile = deviceType === DeviceType.MOBILE;
|
|
144
189
|
/**
|
|
145
190
|
* Effect per aggiornare le dimensioni della bubble quando cambia il contenuto
|
|
@@ -153,57 +198,112 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
153
198
|
}
|
|
154
199
|
}, [content, isCollapsed]);
|
|
155
200
|
/**
|
|
156
|
-
* Effect per
|
|
157
|
-
* Questo previene che il componente finisca fuori dai bordi dopo un resize.
|
|
201
|
+
* Effect per verificare e aggiustare la posizione quando cambia lo stato di collassamento
|
|
158
202
|
*/
|
|
159
203
|
useEffect(() => {
|
|
160
|
-
if (!buttonRef.current)
|
|
204
|
+
if (!buttonRef.current || !position)
|
|
161
205
|
return;
|
|
162
|
-
const
|
|
163
|
-
|
|
206
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
207
|
+
// Spazio extra occupato dalla bubble quando non è collassato
|
|
208
|
+
const extraHeight = !isCollapsed ? bubbleSize.height : 0;
|
|
209
|
+
const extraWidth = !isCollapsed ? bubbleSize.width : 0;
|
|
210
|
+
let minX = 0;
|
|
211
|
+
let maxX;
|
|
212
|
+
let maxY;
|
|
213
|
+
const bubbleBuffer = 5;
|
|
214
|
+
if (usePortal) {
|
|
215
|
+
// Calcola i limiti usando le dimensioni del viewport
|
|
216
|
+
maxX = window.innerWidth - rect.width;
|
|
217
|
+
maxY = window.innerHeight - rect.height + 20;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
// Calcola i limiti usando le dimensioni del parent
|
|
221
|
+
const parent = buttonRef.current.offsetParent;
|
|
222
|
+
if (!parent)
|
|
223
|
+
return;
|
|
224
|
+
const parentRect = parent.getBoundingClientRect();
|
|
225
|
+
maxX = parentRect.width - rect.width;
|
|
226
|
+
maxY = parentRect.height - rect.height + 20;
|
|
227
|
+
}
|
|
228
|
+
if (!isCollapsed) {
|
|
229
|
+
if (align === 'right') {
|
|
230
|
+
minX = Math.max(0, extraWidth - rect.width);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
maxX = Math.min(maxX, (usePortal ? window.innerWidth : buttonRef.current.offsetParent?.getBoundingClientRect().width || 0) - extraWidth);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Verifica se la posizione corrente è fuori dai limiti
|
|
237
|
+
const isOutOfBounds = position.x < minX ||
|
|
238
|
+
position.x > maxX ||
|
|
239
|
+
position.y < extraHeight + bubbleBuffer ||
|
|
240
|
+
position.y > maxY;
|
|
241
|
+
// Se è fuori dai limiti, aggiusta la posizione
|
|
242
|
+
if (isOutOfBounds) {
|
|
243
|
+
const adjustedX = Math.max(minX, Math.min(position.x, maxX));
|
|
244
|
+
const adjustedY = Math.max(extraHeight + bubbleBuffer, Math.min(position.y, maxY));
|
|
245
|
+
setPosition({ x: adjustedX, y: adjustedY });
|
|
246
|
+
}
|
|
247
|
+
}, [isCollapsed, bubbleSize, usePortal, align]);
|
|
248
|
+
/**
|
|
249
|
+
* Effect per resettare la posizione quando il parent o la finestra vengono ridimensionati
|
|
250
|
+
*/
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
if (!buttonRef.current || !position)
|
|
164
253
|
return;
|
|
165
|
-
// Funzione per verificare e aggiustare la posizione dopo un resize
|
|
166
254
|
const handleResize = () => {
|
|
167
255
|
if (!buttonRef.current || !position)
|
|
168
256
|
return;
|
|
169
|
-
const parentRect = parent.getBoundingClientRect();
|
|
170
257
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
171
|
-
// Spazio extra occupato dalla bubble quando non è collassato
|
|
172
258
|
const extraHeight = !isCollapsed ? bubbleSize.height : 0;
|
|
173
259
|
const extraWidth = !isCollapsed ? bubbleSize.width : 0;
|
|
174
|
-
// Calcola i nuovi limiti
|
|
175
260
|
let minX = 0;
|
|
176
|
-
let maxX
|
|
261
|
+
let maxX;
|
|
262
|
+
let maxY;
|
|
263
|
+
const bubbleBuffer = 5;
|
|
264
|
+
if (usePortal) {
|
|
265
|
+
maxX = window.innerWidth - rect.width;
|
|
266
|
+
maxY = window.innerHeight - rect.height + 20;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
const parent = buttonRef.current.offsetParent;
|
|
270
|
+
if (!parent)
|
|
271
|
+
return;
|
|
272
|
+
const parentRect = parent.getBoundingClientRect();
|
|
273
|
+
maxX = parentRect.width - rect.width;
|
|
274
|
+
maxY = parentRect.height - rect.height + 20;
|
|
275
|
+
}
|
|
177
276
|
if (!isCollapsed) {
|
|
178
277
|
if (align === 'right') {
|
|
179
278
|
minX = Math.max(0, extraWidth - rect.width);
|
|
180
279
|
}
|
|
181
280
|
else {
|
|
182
|
-
maxX = Math.min(maxX,
|
|
281
|
+
maxX = Math.min(maxX, (usePortal ? window.innerWidth : buttonRef.current.offsetParent?.getBoundingClientRect().width || 0) - extraWidth);
|
|
183
282
|
}
|
|
184
283
|
}
|
|
185
|
-
const bubbleBuffer = 5; // Buffer per evitare che la bubble esca leggermente dai bordi
|
|
186
|
-
const maxY = parentRect.height - rect.height + 20; // +20px per permettere bottom: -20px
|
|
187
|
-
// Verifica se la posizione corrente è fuori dai limiti
|
|
188
284
|
const isOutOfBounds = position.x < minX ||
|
|
189
285
|
position.x > maxX ||
|
|
190
286
|
position.y < extraHeight + bubbleBuffer ||
|
|
191
287
|
position.y > maxY;
|
|
192
|
-
// Se è fuori dai limiti, resetta alla posizione default
|
|
193
288
|
if (isOutOfBounds) {
|
|
194
289
|
setPosition(null);
|
|
195
290
|
}
|
|
196
291
|
};
|
|
197
|
-
// Observer per
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
292
|
+
// Observer per il parent se non usa Portal
|
|
293
|
+
let resizeObserver;
|
|
294
|
+
if (!usePortal) {
|
|
295
|
+
const parent = buttonRef.current.offsetParent;
|
|
296
|
+
if (parent) {
|
|
297
|
+
resizeObserver = new ResizeObserver(handleResize);
|
|
298
|
+
resizeObserver.observe(parent);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
201
301
|
window.addEventListener('resize', handleResize);
|
|
202
302
|
return () => {
|
|
203
|
-
resizeObserver
|
|
303
|
+
resizeObserver?.disconnect();
|
|
204
304
|
window.removeEventListener('resize', handleResize);
|
|
205
305
|
};
|
|
206
|
-
}, [position, isCollapsed, bubbleSize, align]);
|
|
306
|
+
}, [position, isCollapsed, bubbleSize, align, usePortal]);
|
|
207
307
|
/**
|
|
208
308
|
* Effect per impostare automaticamente lo stato collassato su dispositivi mobile
|
|
209
309
|
* se non è stato specificato un valore iniziale.
|
|
@@ -218,7 +318,7 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
218
318
|
* Chiamato dal doppio click sul componente
|
|
219
319
|
*/
|
|
220
320
|
const toggleCollapse = (e) => {
|
|
221
|
-
e
|
|
321
|
+
e?.stopPropagation();
|
|
222
322
|
setIsCollapsed(!isCollapsed);
|
|
223
323
|
onToppyImageClick?.();
|
|
224
324
|
};
|
|
@@ -231,9 +331,6 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
231
331
|
if (!buttonRef.current)
|
|
232
332
|
return;
|
|
233
333
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
234
|
-
const parentRect = buttonRef.current.offsetParent?.getBoundingClientRect();
|
|
235
|
-
if (!parentRect)
|
|
236
|
-
return;
|
|
237
334
|
// Calcola l'offset tra il punto di click e l'angolo superiore sinistro del componente
|
|
238
335
|
dragOffset.current = {
|
|
239
336
|
x: e.clientX - rect.left,
|
|
@@ -244,42 +341,56 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
244
341
|
};
|
|
245
342
|
/**
|
|
246
343
|
* Gestisce il movimento durante il trascinamento
|
|
247
|
-
* Calcola la nuova posizione rispettando i limiti del parent
|
|
344
|
+
* Calcola la nuova posizione rispettando i limiti del parent o viewport
|
|
248
345
|
* e tenendo conto delle dimensioni della speech bubble quando espansa
|
|
249
346
|
*/
|
|
250
347
|
const handleMouseMove = (e) => {
|
|
251
348
|
if (!isDragging || !buttonRef.current)
|
|
252
349
|
return;
|
|
253
|
-
const parentRect = buttonRef.current.offsetParent?.getBoundingClientRect();
|
|
254
|
-
if (!parentRect)
|
|
255
|
-
return;
|
|
256
350
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
257
351
|
// Spazio extra occupato dalla bubble quando non è collassato
|
|
258
352
|
const extraHeight = !isCollapsed ? bubbleSize.height : 0;
|
|
259
353
|
const extraWidth = !isCollapsed ? bubbleSize.width : 0;
|
|
260
|
-
// Calcola i limiti orizzontali considerando la bubble
|
|
261
354
|
let minX = 0;
|
|
262
|
-
let maxX
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
355
|
+
let maxX;
|
|
356
|
+
let maxY;
|
|
357
|
+
let newX;
|
|
358
|
+
let newY;
|
|
359
|
+
const bubbleBuffer = 5;
|
|
360
|
+
if (usePortal) {
|
|
361
|
+
// Calcola i limiti usando il viewport
|
|
362
|
+
maxX = window.innerWidth - rect.width;
|
|
363
|
+
maxY = window.innerHeight - rect.height + 20;
|
|
364
|
+
if (!isCollapsed) {
|
|
365
|
+
if (align === 'right') {
|
|
366
|
+
minX = Math.max(0, extraWidth - rect.width);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
maxX = Math.min(maxX, window.innerWidth - extraWidth);
|
|
370
|
+
}
|
|
268
371
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
372
|
+
newX = Math.max(minX, Math.min(e.clientX - dragOffset.current.x, maxX));
|
|
373
|
+
newY = Math.max(extraHeight + bubbleBuffer, Math.min(e.clientY - dragOffset.current.y, maxY));
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
// Calcola i limiti usando il parent
|
|
377
|
+
const parent = buttonRef.current.offsetParent;
|
|
378
|
+
if (!parent)
|
|
379
|
+
return;
|
|
380
|
+
const parentRect = parent.getBoundingClientRect();
|
|
381
|
+
maxX = parentRect.width - rect.width;
|
|
382
|
+
maxY = parentRect.height - rect.height + 20;
|
|
383
|
+
if (!isCollapsed) {
|
|
384
|
+
if (align === 'right') {
|
|
385
|
+
minX = Math.max(0, extraWidth - rect.width);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
maxX = Math.min(maxX, parentRect.width - extraWidth);
|
|
389
|
+
}
|
|
273
390
|
}
|
|
391
|
+
newX = Math.max(minX, Math.min(e.clientX - parentRect.left - dragOffset.current.x, maxX));
|
|
392
|
+
newY = Math.max(extraHeight + bubbleBuffer, Math.min(e.clientY - parentRect.top - dragOffset.current.y, maxY));
|
|
274
393
|
}
|
|
275
|
-
// Calcola la nuova posizione X rispettando i limiti
|
|
276
|
-
const newX = Math.max(minX, Math.min(e.clientX - parentRect.left - dragOffset.current.x, maxX));
|
|
277
|
-
// Calcola la nuova posizione Y rispettando i limiti
|
|
278
|
-
// Il limite superiore tiene conto dell'altezza della bubble + buffer di sicurezza
|
|
279
|
-
// Il limite inferiore include 20px extra per permettere bottom: -20px
|
|
280
|
-
const bubbleBuffer = 5; // Buffer per evitare che la bubble esca leggermente dai bordi
|
|
281
|
-
const maxY = parentRect.height - rect.height + 20;
|
|
282
|
-
const newY = Math.max(extraHeight + bubbleBuffer, Math.min(e.clientY - parentRect.top - dragOffset.current.y, maxY));
|
|
283
394
|
setPosition({ x: newX, y: newY });
|
|
284
395
|
};
|
|
285
396
|
/**
|
|
@@ -319,6 +430,30 @@ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onTopp
|
|
|
319
430
|
}, [isDragging]);
|
|
320
431
|
// Renderizza l'overlay solo durante il drag
|
|
321
432
|
const renderDragOverlay = isDragging && _jsx(DragOverlay, {});
|
|
322
|
-
|
|
433
|
+
const toppyContent = (_jsxs(_Fragment, { children: [renderDragOverlay, _jsxs(ToppyButton, { ref: buttonRef, "$align": align, "$isDragging": isDragging, "$x": position?.x, "$y": position?.y, "$isVisible": isVisible, "$isCollapsed": isCollapsed, "$isMobile": isMobile, "$usePortal": usePortal, onMouseDown: !isMobile ? handleMouseDown : undefined, onContextMenu: (e) => e.preventDefault(), onDoubleClick: !isMobile ? toggleCollapse : undefined, children: [(content && !isCollapsed) && (_jsx(ToppySpeechBubble, { ref: bubbleRef, align: align, onClose: toggleCollapse, children: content })), (content && isCollapsed) && (_jsx(ExpandButton, { onMouseDown: (e) => {
|
|
434
|
+
isExpandButtonDraggingRef.current = false;
|
|
435
|
+
expandButtonMouseDownPosRef.current = { x: e.clientX, y: e.clientY };
|
|
436
|
+
}, onMouseMove: (e) => {
|
|
437
|
+
if (!expandButtonMouseDownPosRef.current)
|
|
438
|
+
return;
|
|
439
|
+
const deltaX = Math.abs(e.clientX - expandButtonMouseDownPosRef.current.x);
|
|
440
|
+
const deltaY = Math.abs(e.clientY - expandButtonMouseDownPosRef.current.y);
|
|
441
|
+
// Considera drag solo se il movimento supera 3px
|
|
442
|
+
if (deltaX > 3 || deltaY > 3) {
|
|
443
|
+
isExpandButtonDraggingRef.current = true;
|
|
444
|
+
}
|
|
445
|
+
}, onMouseUp: () => {
|
|
446
|
+
expandButtonMouseDownPosRef.current = null;
|
|
447
|
+
}, onClick: (e) => {
|
|
448
|
+
if (isExpandButtonDraggingRef.current) {
|
|
449
|
+
e.stopPropagation();
|
|
450
|
+
e.preventDefault();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
e.stopPropagation();
|
|
454
|
+
toggleCollapse(e);
|
|
455
|
+
}, onContextMenu: (e) => e.preventDefault(), "aria-label": SDKUI_Localizator.Maximize, title: SDKUI_Localizator.Maximize, type: "button", children: _jsx(IconWindowMaximize, {}) })), _jsx("img", { src: Toppy, alt: "Toppy Help", draggable: false, onClick: isMobile ? toggleCollapse : undefined })] })] }));
|
|
456
|
+
// Renderizza nel document.body usando un Portal se usePortal è true, altrimenti renderizza normalmente
|
|
457
|
+
return usePortal ? ReactDOM.createPortal(toppyContent, document.body) : toppyContent;
|
|
323
458
|
};
|
|
324
459
|
export default ToppyDraggableHelpCenter;
|
|
@@ -3,6 +3,7 @@ interface ToppySpeechBubbleProps {
|
|
|
3
3
|
align?: 'left' | 'right';
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
className?: string;
|
|
6
|
+
onClose?: () => void;
|
|
6
7
|
}
|
|
7
8
|
declare const ToppySpeechBubble: React.ForwardRefExoticComponent<ToppySpeechBubbleProps & React.RefAttributes<HTMLDivElement>>;
|
|
8
9
|
export default ToppySpeechBubble;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef } from 'react';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
|
+
import { SDKUI_Localizator } from '../../../helper';
|
|
5
|
+
import { IconCloseOutline } from '../../../helper/TMIcons';
|
|
4
6
|
// Styled component
|
|
5
7
|
const Bubble = styled.div `
|
|
6
8
|
position: absolute;
|
|
@@ -46,8 +48,70 @@ const Bubble = styled.div `
|
|
|
46
48
|
z-index: 2;
|
|
47
49
|
}
|
|
48
50
|
`;
|
|
51
|
+
const CloseButton = styled.button `
|
|
52
|
+
position: absolute;
|
|
53
|
+
top: -8px;
|
|
54
|
+
right: -8px;
|
|
55
|
+
width: 24px;
|
|
56
|
+
height: 24px;
|
|
57
|
+
border-radius: 50%;
|
|
58
|
+
border: 2px solid rgba(255, 255, 255, 0.9);
|
|
59
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
60
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 20px rgba(118, 75, 162, 0.2);
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
66
|
+
z-index: 10001;
|
|
67
|
+
color: white;
|
|
68
|
+
font-size: 14px;
|
|
69
|
+
|
|
70
|
+
&:hover {
|
|
71
|
+
background: linear-gradient(135deg, #764ba2 0%, #f093fb 100%);
|
|
72
|
+
box-shadow: 0 6px 20px rgba(118, 75, 162, 0.6), 0 0 30px rgba(240, 147, 251, 0.4);
|
|
73
|
+
border-color: rgba(255, 255, 255, 1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&:active {
|
|
77
|
+
transform: scale(0.95);
|
|
78
|
+
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&:focus {
|
|
82
|
+
outline: none;
|
|
83
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 0 3px rgba(102, 126, 234, 0.2);
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
49
86
|
// Componente con forwardRef
|
|
50
|
-
const ToppySpeechBubble = forwardRef(({ align = 'right', children, className }, ref) => {
|
|
51
|
-
|
|
87
|
+
const ToppySpeechBubble = forwardRef(({ align = 'right', children, className, onClose }, ref) => {
|
|
88
|
+
const isDraggingRef = React.useRef(false);
|
|
89
|
+
const mouseDownPosRef = React.useRef(null);
|
|
90
|
+
const handleMouseDown = (e) => {
|
|
91
|
+
isDraggingRef.current = false;
|
|
92
|
+
mouseDownPosRef.current = { x: e.clientX, y: e.clientY };
|
|
93
|
+
};
|
|
94
|
+
const handleMouseMove = (e) => {
|
|
95
|
+
if (!mouseDownPosRef.current)
|
|
96
|
+
return;
|
|
97
|
+
const deltaX = Math.abs(e.clientX - mouseDownPosRef.current.x);
|
|
98
|
+
const deltaY = Math.abs(e.clientY - mouseDownPosRef.current.y);
|
|
99
|
+
// Considera drag solo se il movimento supera 3px
|
|
100
|
+
if (deltaX > 3 || deltaY > 3) {
|
|
101
|
+
isDraggingRef.current = true;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const handleClick = (e) => {
|
|
105
|
+
if (isDraggingRef.current) {
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
onClose?.();
|
|
111
|
+
};
|
|
112
|
+
const handleMouseUp = () => {
|
|
113
|
+
mouseDownPosRef.current = null;
|
|
114
|
+
};
|
|
115
|
+
return (_jsxs(Bubble, { ref: ref, "$align": align, className: className, children: [onClose && (_jsx(CloseButton, { onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onClick: handleClick, "aria-label": SDKUI_Localizator.Minimize, title: SDKUI_Localizator.Minimize, type: "button", children: _jsx(IconCloseOutline, {}) })), children] }));
|
|
52
116
|
});
|
|
53
117
|
export default ToppySpeechBubble;
|
|
@@ -1353,7 +1353,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
|
|
|
1353
1353
|
isEditable: true,
|
|
1354
1354
|
value: FormulaHelper.addFormulaTag(newFormula.expression)
|
|
1355
1355
|
}));
|
|
1356
|
-
} }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, onClose: () => setShowReAssignPopup(false) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, onClose: () => setShowMoreInfoPopup(false) }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }),
|
|
1356
|
+
} }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, onClose: () => setShowReAssignPopup(false) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, onClose: () => setShowMoreInfoPopup(false) }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }), _jsx(ToppyDraggableHelpCenter, { initialIsCollapsed: false, deviceType: deviceType, isVisible: showToppyForApprove || showToppyForCompleteMoreInfo || showToppyForReferences, content: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: [showToppyForApprove && (workItems.length === 1 ?
|
|
1357
1357
|
_jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => setShowApprovePopup(true), onSignApprove: handleSignApprove, onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), onMoreInfo: () => setShowMoreInfoPopup(true), dtd: fromDTD })
|
|
1358
1358
|
:
|
|
1359
1359
|
_jsxs("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: [`Devi approvare ${workItems.length} workitem(s) per questo documento.`, `Vai alla sezione di approvazione.`] })), showToppyForCompleteMoreInfo && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: `${SDKUI_Localizator.MoreInfoCompleteRequestSentBy} ${taskMoreInfo?.fromName}!` }), _jsx(TMButton, { caption: SDKUI_Localizator.CommentAndComplete, color: 'success', showTooltip: false, onClick: () => {
|
|
@@ -1368,7 +1368,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
|
|
|
1368
1368
|
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
1369
1369
|
margin: '6px 0'
|
|
1370
1370
|
} })), _jsxs(StyledReferenceButton, { onClick: () => handleNavigateToReference(ref), children: [_jsx("span", { children: label }), _jsx("span", { children: `"${ref.objName}"` })] }, `ref-${index}-${ref.objID}`)] }, `ref-frag-${index}-${ref.objID}`));
|
|
1371
|
-
})] }) })
|
|
1371
|
+
})] }) })] }), (showCommentForm && TID && DID) &&
|
|
1372
1372
|
_jsx(TMBlogCommentForm, { context: { engine: 'SearchEngine', object: { tid: TID, did: DID } }, onClose: () => setShowCommentForm(false), refreshCallback: handleCompleteMoreInfo, participants: [], showAttachmentsSection: false, allArchivedDocumentsFileItems: [] }), isOpenDetails &&
|
|
1373
1373
|
_jsx(StyledModalContainer, { children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, isForMaster: false, inputDcmts: getSelectionDcmtInfo(), allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenDetails(false), allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }) }), isOpenMaster &&
|
|
1374
1374
|
_jsxs(StyledModalContainer, { children: [_jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: getSelectionDcmtInfo(), isForMaster: true, allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenMaster(false), appendMasterDcmts: handleAddItem, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
|
|
@@ -3,7 +3,6 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
3
3
|
import { PlatformObjectValidator, WhereItem, SDK_Localizator, OrderByItem, SelectItem, SelectItemVisibilities, SDK_Globals, SavedQueryCacheService, SearchEngine, QueryOperators } from '@topconsultnpm/sdk-ts';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import TMSearchQueryEditor from './TMSearchQueryEditor';
|
|
6
|
-
import Toppy from '../../../assets/Toppy-generico.png';
|
|
7
6
|
import { getDcmtTypesByQdAsync, SDKUI_Localizator, getQD, IconMenuVertical, IconAddCircleOutline, IconEdit, IconEasy, IconAdvanced, deepCompare, IconSearch, IconClear, getDefaultOperator, prepareQdForSearchAsync, IsParametricQuery, SDKUI_Globals, IconArrowRight, IconMenuCAArchive, getListMaxItems } from '../../../helper';
|
|
8
7
|
import { useQueryParametersDialog } from '../../../hooks/useQueryParametersDialog';
|
|
9
8
|
import { FormModes } from '../../../ts';
|
|
@@ -13,7 +12,6 @@ import ShowAlert from '../../base/TMAlert';
|
|
|
13
12
|
import TMButton from '../../base/TMButton';
|
|
14
13
|
import { useDeviceType, DeviceType } from '../../base/TMDeviceProvider';
|
|
15
14
|
import TMDropDownMenu from '../../base/TMDropDownMenu';
|
|
16
|
-
import TMLayoutContainer from '../../base/TMLayout';
|
|
17
15
|
import { TMExceptionBoxManager } from '../../base/TMPopUp';
|
|
18
16
|
import TMSpinner from '../../base/TMSpinner';
|
|
19
17
|
import TMPanel from '../../base/TMPanel';
|
|
@@ -22,6 +20,7 @@ import { TMMetadataChooserForm } from '../../choosers/TMMetadataChooser';
|
|
|
22
20
|
import TMQueryEditor from '../../query/TMQueryEditor';
|
|
23
21
|
import TMSavedQueryForm from './TMSavedQueryForm';
|
|
24
22
|
import { AdvancedMenuButtons } from '../../editors/TMMetadataValues';
|
|
23
|
+
import TMToppyMessage from '../../../helper/TMToppyMessage';
|
|
25
24
|
const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, inputMids, onSearchCompleted, onSqdSaved, onBack, onClosePanel, allowMaximize = true, onMaximizePanel, onBackToResult, passToArchiveCallback }) => {
|
|
26
25
|
const [confirmQueryParams, ConfirmQueryParamsDialog] = useQueryParametersDialog();
|
|
27
26
|
const [qd, setQd] = useState();
|
|
@@ -329,7 +328,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
329
328
|
_jsx(TMMetadataChooserForm, { allowMultipleSelection: true, height: '500px', width: '600px', allowSysMetadata: true, qd: qd, selectedIDs: qd?.select?.map((item) => ({ tid: item.tid, mid: item.mid })), onClose: handleCloseOutputConfig, onChoose: handleChooseOutput }), showOrderByConfig &&
|
|
330
329
|
_jsx(TMMetadataChooserForm, { allowMultipleSelection: true, height: '500px', width: '600px', allowSysMetadata: true, qd: qd, selectedIDs: qd?.orderBy?.map((item) => ({ tid: item.tid, mid: item.mid })), onClose: handleCloseOrderByConfig, onChoose: handleChooseOrderBy })] })
|
|
331
330
|
:
|
|
332
|
-
|
|
331
|
+
_jsx(TMToppyMessage, { message: SDKUI_Localizator.DcmtTypeSelectOrQuickSearch, titleTooltip: SDKUI_Localizator.DcmtTypeSelectOrQuickSearch }), showSqdForm &&
|
|
333
332
|
_jsx(StyledModalContainer, { style: { backgroundColor: `${TMColors.backgroundColorHeader}12` }, children: _jsx(TMSavedQueryForm, { height: '50%', width: '50%', id: formModeSqdForm === FormModes.Create ? -1 : SQD?.id, title: 'Ricerca rapida', formMode: formModeSqdForm, showBackButton: true, qd: qd, isAdvancedSearch: showAdvancedSearch, isModal: false, onClose: () => { setShowSqdForm(false); }, onSaved: onSqdSaved }) })] }), showDistinctValuesPanel &&
|
|
334
333
|
_jsx(TMDistinctValues, { isModal: true, tid: focusedTidMid?.tid, mid: focusedTidMid?.mid, separator: ',', onClosePanelCallback: () => setShowDistinctValuesPanel(false), onSelectionChanged: (e) => {
|
|
335
334
|
if (!e)
|
|
@@ -584,18 +584,17 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
|
|
|
584
584
|
setIsOpenBatchUpdate(false);
|
|
585
585
|
setIsModifiedBatchUpdate(false);
|
|
586
586
|
await refreshSelectionDataRowsAsync();
|
|
587
|
-
}, onStatusChanged: (isModified) => { setIsModifiedBatchUpdate(isModified); } }), (showToppyForApprove && !showApprovePopup && !showRejectPopup && !showReAssignPopup && !showMoreInfoPopup && !openS4TViewer && !showTodoDcmtForm)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}, approveDisable: selectedDocs.length === 0, signApproveDisable: disableSignApproveDisable, rejectDisable: selectedDocs.length === 0, reassignDisable: selectedDocs.length === 0, infoDisable: selectedDocs.length !== 1, dtd: fromDTD }) }) })] }), _jsx(ConfirmFormatDialog, {}), _jsx(ConfirmAttachmentsDialog, {}), customButton && _jsx(TMCustomButton, { button: customButton, formData: currentMetadataValues, selectedItems: selectedItems, onClose: () => setCustomButton(undefined) }), showRelatedDcmtsChooser &&
|
|
587
|
+
}, onStatusChanged: (isModified) => { setIsModifiedBatchUpdate(isModified); } }), _jsx(ToppyDraggableHelpCenter, { initialIsCollapsed: false, deviceType: deviceType, usePortal: true, isVisible: showToppyForApprove && !showApprovePopup && !showRejectPopup && !showReAssignPopup && !showMoreInfoPopup && !openS4TViewer && !showTodoDcmtForm, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => {
|
|
588
|
+
setShowApprovePopup(true);
|
|
589
|
+
}, onSignApprove: () => {
|
|
590
|
+
handleSignApprove();
|
|
591
|
+
}, onReject: () => {
|
|
592
|
+
setShowRejectPopup(true);
|
|
593
|
+
}, onReAssign: () => {
|
|
594
|
+
setShowReAssignPopup(true);
|
|
595
|
+
}, onMoreInfo: () => {
|
|
596
|
+
setShowMoreInfoPopup(true);
|
|
597
|
+
}, approveDisable: selectedDocs.length === 0, signApproveDisable: disableSignApproveDisable, rejectDisable: selectedDocs.length === 0, reassignDisable: selectedDocs.length === 0, infoDisable: selectedDocs.length !== 1, dtd: fromDTD }) }) })] }), _jsx(ConfirmFormatDialog, {}), _jsx(ConfirmAttachmentsDialog, {}), customButton && _jsx(TMCustomButton, { button: customButton, formData: currentMetadataValues, selectedItems: selectedItems, onClose: () => setCustomButton(undefined) }), showRelatedDcmtsChooser &&
|
|
599
598
|
_jsx(TMChooserForm, { dataSource: relatedDcmtsChooserDataSource, onChoose: async (selectedRelation) => {
|
|
600
599
|
try {
|
|
601
600
|
setShowRelatedDcmtsChooser(false);
|
|
@@ -59,7 +59,6 @@ export { default as TMBlogAttachments } from './grids/TMBlogAttachments';
|
|
|
59
59
|
export { default as TMBlogCommentForm } from './features/blog/TMBlogCommentForm';
|
|
60
60
|
export * from './query/TMQueryEditor';
|
|
61
61
|
export * from './query/TMQuerySummary';
|
|
62
|
-
export { default as ToppyHelpCenter } from './features/assistant/ToppyHelpCenter';
|
|
63
62
|
export { default as ToppyDraggableHelpCenter } from './features/assistant/ToppyDraggableHelpCenter';
|
|
64
63
|
export * from './features/documents/TMDcmtForm';
|
|
65
64
|
export * from './features/documents/TMDcmtIcon';
|
package/lib/components/index.js
CHANGED
|
@@ -66,7 +66,6 @@ export { default as TMBlogCommentForm } from './features/blog/TMBlogCommentForm'
|
|
|
66
66
|
export * from './query/TMQueryEditor';
|
|
67
67
|
export * from './query/TMQuerySummary';
|
|
68
68
|
//assistant
|
|
69
|
-
export { default as ToppyHelpCenter } from './features/assistant/ToppyHelpCenter';
|
|
70
69
|
export { default as ToppyDraggableHelpCenter } from './features/assistant/ToppyDraggableHelpCenter';
|
|
71
70
|
//documents
|
|
72
71
|
export * from './features/documents/TMDcmtForm';
|
|
@@ -34,9 +34,10 @@ const StyledToppyImage = styled.img `
|
|
|
34
34
|
max-width: 120px;
|
|
35
35
|
height: auto;
|
|
36
36
|
display: block;
|
|
37
|
+
user-select: none;
|
|
37
38
|
`;
|
|
38
39
|
const TMToppyMessage = (props) => {
|
|
39
40
|
const { message, titleTooltip } = props;
|
|
40
|
-
return (_jsxs(TMLayoutContainer, { gap: 30, alignItems: "center", justifyContent: "center", onContextMenu: (e) => e.preventDefault(), children: [_jsx(StyledToppyTextContainer, { children: _jsx(StyledToppyText, { title: titleTooltip || undefined, children: message }) }), _jsx(StyledToppyImage, { src: Toppy, alt: "Toppy" })] }));
|
|
41
|
+
return (_jsxs(TMLayoutContainer, { gap: 30, alignItems: "center", justifyContent: "center", onContextMenu: (e) => e.preventDefault(), children: [_jsx(StyledToppyTextContainer, { children: _jsx(StyledToppyText, { title: titleTooltip || undefined, children: message }) }), _jsx(StyledToppyImage, { src: Toppy, alt: "Toppy", draggable: false, onMouseDown: (e) => e.stopPropagation() })] }));
|
|
41
42
|
};
|
|
42
43
|
export default TMToppyMessage;
|
package/package.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { DeviceType } from '../../base/TMDeviceProvider';
|
|
3
|
-
interface ToppyHelpCenterProps {
|
|
4
|
-
content?: React.ReactNode;
|
|
5
|
-
deviceType?: DeviceType;
|
|
6
|
-
usePortal?: boolean;
|
|
7
|
-
align?: 'right' | 'left';
|
|
8
|
-
initialIsCollapsed?: boolean;
|
|
9
|
-
onToppyImageClick?: () => void;
|
|
10
|
-
}
|
|
11
|
-
declare const ToppyHelpCenter: React.FC<ToppyHelpCenterProps>;
|
|
12
|
-
export default ToppyHelpCenter;
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import ReactDOM from 'react-dom';
|
|
4
|
-
import styled, { keyframes, css } from 'styled-components';
|
|
5
|
-
import Toppy from '../../../assets/Toppy-generico.png';
|
|
6
|
-
import { DeviceType } from '../../base/TMDeviceProvider';
|
|
7
|
-
const toppyEntrance = keyframes `
|
|
8
|
-
0% { right: -200px; opacity: 0; }
|
|
9
|
-
60% { opacity: 1; }
|
|
10
|
-
100% { right: 10px; opacity: 1; }
|
|
11
|
-
`;
|
|
12
|
-
const pulseAnimation = keyframes `
|
|
13
|
-
0% { transform: scale(1); }
|
|
14
|
-
50% { transform: scale(1.05); }
|
|
15
|
-
100% { transform: scale(1); }
|
|
16
|
-
`;
|
|
17
|
-
const bounceAnimation = keyframes `
|
|
18
|
-
0%, 20%, 50%, 80%, 100% {
|
|
19
|
-
transform: translateY(0);
|
|
20
|
-
}
|
|
21
|
-
40% {
|
|
22
|
-
transform: translateY(-10px);
|
|
23
|
-
}
|
|
24
|
-
60% {
|
|
25
|
-
transform: translateY(-5px);
|
|
26
|
-
}
|
|
27
|
-
`;
|
|
28
|
-
const wiggle = keyframes `
|
|
29
|
-
0% { transform: rotate(-5deg) scale(1.1); }
|
|
30
|
-
10% { transform: rotate(5deg) scale(0.95); }
|
|
31
|
-
20% { transform: rotate(-5deg) scale(1.1); }
|
|
32
|
-
30% { transform: rotate(5deg) scale(1.05); }
|
|
33
|
-
40% { transform: rotate(-5deg) scale(1); }
|
|
34
|
-
50% { transform: rotate(5deg) scale(0.96); }
|
|
35
|
-
60% { transform: rotate(-5deg) scale(1.15); }
|
|
36
|
-
70% { transform: rotate(5deg) scale(0.99); }
|
|
37
|
-
80% { transform: rotate(-5deg) scale(1.05); }
|
|
38
|
-
90% { transform: rotate(5deg) scale(1); }
|
|
39
|
-
100% { transform: rotate(-5deg) scale(1.08); }
|
|
40
|
-
`;
|
|
41
|
-
const ToppyContainer = styled.div `
|
|
42
|
-
position: ${({ $fixed }) => ($fixed ? 'fixed' : 'absolute')};
|
|
43
|
-
bottom: ${({ $isCollapsed, $isMobile }) => $isMobile ? '5px' : $isCollapsed ? '5px' : '-20px'};
|
|
44
|
-
${({ $align, $isCollapsed }) => $align === 'left'
|
|
45
|
-
? `left: ${$isCollapsed ? '5px' : '10px'}; right: auto;`
|
|
46
|
-
: `right: ${$isCollapsed ? '5px' : '10px'}; left: auto;`}
|
|
47
|
-
display: flex;
|
|
48
|
-
flex-direction: column-reverse; /* Il contenuto è sopra l'immagine */
|
|
49
|
-
align-items: ${({ $align }) => $align === 'left' ? 'flex-start' : 'flex-end'};
|
|
50
|
-
animation: ${({ $fixed }) => $fixed && css `${toppyEntrance} 0.5s cubic-bezier(0.23, 1, 0.32, 1)`};
|
|
51
|
-
z-index: ${({ $fixed }) => ($fixed ? 2147483647 : 10)};
|
|
52
|
-
`;
|
|
53
|
-
const ToppyImage = styled.img `
|
|
54
|
-
width: ${({ $isMobile, $isCollapsed }) => $isCollapsed ? '60px' : $isMobile ? '90px' : '120px'};
|
|
55
|
-
height: ${({ $isMobile, $isCollapsed }) => $isCollapsed ? '70px' : $isMobile ? '105px' : '140px'};
|
|
56
|
-
cursor: pointer;
|
|
57
|
-
object-fit: cover;
|
|
58
|
-
object-position: top center;
|
|
59
|
-
clip-path: inset(0 0 22% 0 round 10px);
|
|
60
|
-
transform: ${({ $isCollapsed, $align }) => $isCollapsed
|
|
61
|
-
? 'none'
|
|
62
|
-
: $align === 'left'
|
|
63
|
-
? 'rotate(20deg)'
|
|
64
|
-
: 'rotate(-20deg)'};
|
|
65
|
-
|
|
66
|
-
${({ $isCollapsed }) => $isCollapsed &&
|
|
67
|
-
css `
|
|
68
|
-
/* animation: ${bounceAnimation} 2s infinite; */
|
|
69
|
-
/* animation: ${pulseAnimation} 2s infinite; */
|
|
70
|
-
animation: ${wiggle} 4s infinite;
|
|
71
|
-
`}
|
|
72
|
-
`;
|
|
73
|
-
const ToppyContent = styled.div `
|
|
74
|
-
margin-bottom: ${({ $isMobile, $align }) => $align === 'left'
|
|
75
|
-
? '30px' // Spazio tra ToppyContent e ToppyImage
|
|
76
|
-
: $isMobile
|
|
77
|
-
? '30px'
|
|
78
|
-
: '20px'};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
display: ${props => (props.$isCollapsed ? 'none' : 'block')};
|
|
82
|
-
width: max-content;
|
|
83
|
-
max-width: 250px;
|
|
84
|
-
background: linear-gradient(
|
|
85
|
-
180deg,
|
|
86
|
-
rgba(0, 113, 188, 0.45) 0%,
|
|
87
|
-
rgba(27, 20, 100, 0.65) 100%
|
|
88
|
-
);
|
|
89
|
-
color: white;
|
|
90
|
-
padding: 10px;
|
|
91
|
-
border-radius: 10px;
|
|
92
|
-
border: 1px solid #FFFFFF;
|
|
93
|
-
opacity: ${props => (props.$isCollapsed ? 0 : 1)};
|
|
94
|
-
transform: ${props => (props.$isCollapsed ? 'translateY(20px)' : 'translateY(0)')};
|
|
95
|
-
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
96
|
-
pointer-events: ${props => (props.$isCollapsed ? 'none' : 'auto')};
|
|
97
|
-
position: relative;
|
|
98
|
-
|
|
99
|
-
${({ $align, $isMobile }) => $align === 'left' &&
|
|
100
|
-
css `
|
|
101
|
-
position: absolute;
|
|
102
|
-
left: 50px;
|
|
103
|
-
right: auto;
|
|
104
|
-
bottom: 100%;
|
|
105
|
-
transform: translateY(-${$isMobile ? '10px' : '20px'});
|
|
106
|
-
`}
|
|
107
|
-
|
|
108
|
-
&::after {
|
|
109
|
-
transform: ${({ $align }) => $align === 'left' ? 'skewX(15deg)' : 'skewX(-15deg)'};
|
|
110
|
-
content: "";
|
|
111
|
-
position: absolute;
|
|
112
|
-
top: 100%;
|
|
113
|
-
|
|
114
|
-
${({ $align }) => $align === 'left' ? 'left: 20px; right: auto;' : 'right: 15px; left: auto;'}
|
|
115
|
-
border-width: 32px 32px 0 0;
|
|
116
|
-
border-style: solid;
|
|
117
|
-
border-color: #FFFFFF transparent;
|
|
118
|
-
display: block;
|
|
119
|
-
width: 0;
|
|
120
|
-
height: 0;
|
|
121
|
-
z-index: 1;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
&::before {
|
|
125
|
-
transform: ${({ $align }) => $align === 'left' ? 'skewX(15deg)' : 'skewX(-15deg)'};
|
|
126
|
-
content: "";
|
|
127
|
-
position: absolute;
|
|
128
|
-
top: 100%;
|
|
129
|
-
|
|
130
|
-
${({ $align }) => $align === 'left' ? 'left: 20px; right: auto;' : 'right: 15px; left: auto;'}
|
|
131
|
-
border-width: 32px 32px 0 0;
|
|
132
|
-
border-style: solid;
|
|
133
|
-
border-color: rgba(27, 20, 100, 0.65) transparent;
|
|
134
|
-
display: block;
|
|
135
|
-
width: 0;
|
|
136
|
-
height: 0;
|
|
137
|
-
z-index: 2;
|
|
138
|
-
}
|
|
139
|
-
`;
|
|
140
|
-
const ToppyHelpCenter = ({ content, deviceType, usePortal = true, align = 'right', initialIsCollapsed, onToppyImageClick }) => {
|
|
141
|
-
const [isCollapsed, setIsCollapsed] = useState(initialIsCollapsed ?? false);
|
|
142
|
-
const [portalContainer, setPortalContainer] = useState(null);
|
|
143
|
-
useEffect(() => {
|
|
144
|
-
if (initialIsCollapsed === undefined && deviceType === DeviceType.MOBILE) {
|
|
145
|
-
setIsCollapsed(true);
|
|
146
|
-
}
|
|
147
|
-
}, [deviceType, initialIsCollapsed]);
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
if (!usePortal)
|
|
150
|
-
return;
|
|
151
|
-
const portalRoot = document.createElement('div');
|
|
152
|
-
portalRoot.setAttribute('id', 'toppy-portal-root');
|
|
153
|
-
document.body.appendChild(portalRoot);
|
|
154
|
-
setPortalContainer(portalRoot);
|
|
155
|
-
return () => {
|
|
156
|
-
document.body.removeChild(portalRoot);
|
|
157
|
-
};
|
|
158
|
-
}, [usePortal]);
|
|
159
|
-
const toggleCollapse = (e) => {
|
|
160
|
-
e.stopPropagation();
|
|
161
|
-
setIsCollapsed(!isCollapsed);
|
|
162
|
-
onToppyImageClick?.();
|
|
163
|
-
};
|
|
164
|
-
const isMobile = deviceType === DeviceType.MOBILE;
|
|
165
|
-
const toppyComponent = (_jsxs(ToppyContainer, { "$isMobile": isMobile, "$isCollapsed": isCollapsed, "$fixed": usePortal, "$align": align, onContextMenu: (e) => e.preventDefault(), children: [_jsx(ToppyImage, { "$isMobile": isMobile, "$isCollapsed": isCollapsed, "$align": align, onClick: toggleCollapse, src: Toppy, alt: "Toppy" }), _jsx(ToppyContent, { "$isCollapsed": isCollapsed, "$isMobile": isMobile, "$align": align, children: content })] }));
|
|
166
|
-
if (usePortal) {
|
|
167
|
-
if (!portalContainer)
|
|
168
|
-
return null;
|
|
169
|
-
return ReactDOM.createPortal(toppyComponent, portalContainer);
|
|
170
|
-
}
|
|
171
|
-
return toppyComponent;
|
|
172
|
-
};
|
|
173
|
-
export default ToppyHelpCenter;
|