@topconsultnpm/sdkui-react 6.19.0-dev2.5 → 6.19.0-dev2.51

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.
Files changed (95) hide show
  1. package/lib/components/base/TMButton.d.ts +1 -0
  2. package/lib/components/base/TMButton.js +6 -6
  3. package/lib/components/base/TMCustomButton.d.ts +1 -1
  4. package/lib/components/base/TMCustomButton.js +83 -28
  5. package/lib/components/base/TMDataGridExportForm.d.ts +1 -1
  6. package/lib/components/base/TMDataGridExportForm.js +9 -3
  7. package/lib/components/base/TMFileManager.js +12 -3
  8. package/lib/components/base/TMFileManagerDataGridView.d.ts +2 -0
  9. package/lib/components/base/TMFileManagerDataGridView.js +12 -3
  10. package/lib/components/base/TMFileManagerThumbnailItems.d.ts +2 -0
  11. package/lib/components/base/TMFileManagerThumbnailItems.js +12 -2
  12. package/lib/components/base/TMFileManagerThumbnailsView.d.ts +2 -0
  13. package/lib/components/base/TMFileManagerThumbnailsView.js +2 -2
  14. package/lib/components/base/TMModal.d.ts +2 -0
  15. package/lib/components/base/TMModal.js +13 -2
  16. package/lib/components/base/TMTooltip.d.ts +1 -1
  17. package/lib/components/base/TMTooltip.js +1 -1
  18. package/lib/components/base/TMWaitPanel.js +8 -2
  19. package/lib/components/choosers/TMDataListItemChooser.js +1 -1
  20. package/lib/components/choosers/TMDcmtTypeChooser.js +2 -2
  21. package/lib/components/choosers/TMMetadataChooser.d.ts +4 -1
  22. package/lib/components/choosers/TMMetadataChooser.js +31 -8
  23. package/lib/components/choosers/TMUserChooser.d.ts +4 -0
  24. package/lib/components/choosers/TMUserChooser.js +21 -5
  25. package/lib/components/editors/TMDateBox.d.ts +1 -1
  26. package/lib/components/editors/TMTextArea.d.ts +1 -0
  27. package/lib/components/editors/TMTextArea.js +43 -9
  28. package/lib/components/editors/TMTextBox.js +33 -3
  29. package/lib/components/editors/TMTextExpression.js +36 -28
  30. package/lib/components/features/assistant/ToppyDraggableHelpCenter.d.ts +28 -0
  31. package/lib/components/features/assistant/ToppyDraggableHelpCenter.js +324 -0
  32. package/lib/components/features/assistant/ToppyHelpCenter.js +1 -1
  33. package/lib/components/features/assistant/ToppySpeechBubble.d.ts +8 -0
  34. package/lib/components/features/assistant/ToppySpeechBubble.js +53 -0
  35. package/lib/components/features/blog/TMBlogCommentForm.d.ts +2 -0
  36. package/lib/components/features/blog/TMBlogCommentForm.js +18 -6
  37. package/lib/components/features/documents/TMDcmtBlog.js +1 -1
  38. package/lib/components/features/documents/TMDcmtForm.js +313 -57
  39. package/lib/components/features/documents/TMDcmtPreview.js +45 -8
  40. package/lib/components/features/documents/TMRelationViewer.js +56 -23
  41. package/lib/components/features/search/TMSavedQuerySelector.js +1 -1
  42. package/lib/components/features/search/TMSearch.js +2 -2
  43. package/lib/components/features/search/TMSearchQueryEditor.js +1 -1
  44. package/lib/components/features/search/TMSearchQueryPanel.js +8 -25
  45. package/lib/components/features/search/TMSearchResult.js +94 -13
  46. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +2 -1
  47. package/lib/components/features/search/TMSearchResultsMenuItems.js +97 -51
  48. package/lib/components/features/tasks/TMTaskForm.js +11 -5
  49. package/lib/components/features/tasks/TMTasksAgenda.js +4 -4
  50. package/lib/components/features/tasks/TMTasksCalendar.js +2 -2
  51. package/lib/components/features/tasks/TMTasksHeader.js +1 -1
  52. package/lib/components/features/tasks/TMTasksUtils.d.ts +2 -1
  53. package/lib/components/features/tasks/TMTasksUtils.js +18 -3
  54. package/lib/components/features/tasks/TMTasksUtilsView.js +26 -4
  55. package/lib/components/features/tasks/TMTasksView.js +12 -6
  56. package/lib/components/features/workflow/TMWorkflowPopup.js +3 -3
  57. package/lib/components/features/workflow/diagram/DiagramItemForm.js +8 -3
  58. package/lib/components/forms/TMResultDialog.js +8 -2
  59. package/lib/components/forms/TMSaveForm.js +2 -2
  60. package/lib/components/grids/TMBlogsPost.d.ts +8 -3
  61. package/lib/components/grids/TMBlogsPost.js +73 -11
  62. package/lib/components/grids/TMBlogsPostUtils.d.ts +1 -0
  63. package/lib/components/grids/TMBlogsPostUtils.js +15 -3
  64. package/lib/components/grids/TMRecentsManager.js +1 -1
  65. package/lib/components/index.d.ts +2 -1
  66. package/lib/components/index.js +2 -1
  67. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  68. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +2 -2
  69. package/lib/components/layout/panelManager/TMPanelManagerContext.js +0 -1
  70. package/lib/components/layout/panelManager/TMPanelManagerToolbar.js +2 -1
  71. package/lib/components/layout/panelManager/types.d.ts +1 -0
  72. package/lib/components/pages/TMPage.js +1 -1
  73. package/lib/components/settings/SettingsAppearance.js +5 -5
  74. package/lib/components/viewers/TMDataListItemViewer.d.ts +1 -1
  75. package/lib/components/viewers/TMMidViewer.d.ts +1 -1
  76. package/lib/components/viewers/TMTidViewer.d.ts +1 -1
  77. package/lib/helper/GlobalStyles.d.ts +2 -0
  78. package/lib/helper/GlobalStyles.js +10 -0
  79. package/lib/helper/Globalization.d.ts +1 -0
  80. package/lib/helper/Globalization.js +30 -0
  81. package/lib/helper/SDKUI_Localizator.d.ts +39 -2
  82. package/lib/helper/SDKUI_Localizator.js +392 -22
  83. package/lib/helper/TMCustomSearchBar.js +1 -1
  84. package/lib/helper/TMIcons.d.ts +4 -1
  85. package/lib/helper/TMIcons.js +13 -1
  86. package/lib/helper/TMUtils.d.ts +1 -4
  87. package/lib/helper/TMUtils.js +18 -23
  88. package/lib/helper/dcmtsHelper.d.ts +2 -1
  89. package/lib/helper/dcmtsHelper.js +56 -17
  90. package/lib/helper/helpers.js +5 -1
  91. package/lib/helper/index.d.ts +1 -0
  92. package/lib/helper/index.js +1 -0
  93. package/lib/hooks/useRelatedDocuments.js +35 -26
  94. package/lib/ts/types.d.ts +3 -1
  95. package/package.json +8 -8
@@ -0,0 +1,324 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef, useState, useEffect } from 'react';
3
+ import styled from 'styled-components';
4
+ import Toppy from '../../../assets/Toppy-generico.png';
5
+ import { DeviceType } from '../../base/TMDeviceProvider';
6
+ import ToppySpeechBubble from './ToppySpeechBubble';
7
+ /**
8
+ * Styled component per il contenitore di Toppy
9
+ * Gestisce il posizionamento, le dimensioni e le animazioni
10
+ */
11
+ const ToppyButton = styled.div.attrs((props) => ({
12
+ style: {
13
+ // Applica left/top come stili inline per evitare la generazione di troppe classi CSS
14
+ ...(props.$x !== undefined && props.$y !== undefined
15
+ ? {
16
+ left: `${props.$x}px`,
17
+ top: `${props.$y}px`,
18
+ bottom: 'auto',
19
+ right: 'auto',
20
+ }
21
+ : {}),
22
+ // Cursore applicato come stile inline
23
+ cursor: props.$isDragging ? 'grabbing' : 'grab',
24
+ },
25
+ })) `
26
+ /* Visibilità controllata dalla prop */
27
+ display: ${(props) => (props.$isVisible ? 'flex' : 'none')};
28
+ position: absolute;
29
+
30
+ /* Posizionamento di default quando non è draggato (x e y non sono definiti) */
31
+ ${(props) => props.$x === undefined || props.$y === undefined
32
+ ? `
33
+ bottom: ${props.$isMobile ? '0px' : '-20px'};
34
+ ${props.$align === 'left' ? 'left: 10px;' : 'right: 10px;'};
35
+ `
36
+ : ''}
37
+
38
+ /* Z-index alto per assicurare che sia sempre in primo piano */
39
+ z-index: 2147483647;
40
+ background-color: transparent;
41
+ border: none;
42
+ padding: 0;
43
+ outline: none;
44
+
45
+ /* Dimensioni dinamiche in base allo stato collassato e al tipo di dispositivo
46
+ Usa min() per adattarsi su schermi piccoli */
47
+ width: ${(props) => {
48
+ if (props.$isMobile) {
49
+ return props.$isCollapsed ? 'min(40px, 100%)' : '80px';
50
+ }
51
+ return props.$isCollapsed ? 'min(60px, 100%)' : '120px';
52
+ }};
53
+ height: ${(props) => {
54
+ if (props.$isMobile) {
55
+ return props.$isCollapsed ? 'min(45px, 100%)' : '95px';
56
+ }
57
+ return props.$isCollapsed ? 'min(70px, 100%)' : '140px';
58
+ }};
59
+ max-width: 100%;
60
+ max-height: 100%;
61
+
62
+ user-select: none;
63
+
64
+ img {
65
+ /* Dimensioni dell'immagine in base allo stato collassato e al tipo di dispositivo */
66
+ width: ${(props) => {
67
+ if (props.$isMobile) {
68
+ return props.$isCollapsed ? '40px' : '80px';
69
+ }
70
+ return props.$isCollapsed ? '60px' : '120px';
71
+ }};
72
+ height: ${(props) => {
73
+ if (props.$isMobile) {
74
+ return props.$isCollapsed ? '40px' : '95px';
75
+ }
76
+ return props.$isCollapsed ? '60px' : '140px';
77
+ }};
78
+ pointer-events: ${(props) => (props.$isMobile ? 'auto' : 'none')};
79
+ border-radius: 50%; /* Rende l'immagine circolare */
80
+ /* Rotazione leggera in base all'allineamento */
81
+ transform: ${(props) => props.$isCollapsed
82
+ ? 'rotate(0deg)'
83
+ : props.$align === 'left' ? 'rotate(20deg)' : 'rotate(-20deg)'};
84
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
85
+
86
+ /* Animazione di pulsazione quando è collassato per attirare l'attenzione */
87
+ ${(props) => props.$isCollapsed && `animation: toppyPulse 1.5s infinite;`}
88
+ }
89
+
90
+ /* Keyframes per l'animazione di pulsazione */
91
+ @keyframes toppyPulse {
92
+ 0% {
93
+ /* Dimensione normale all'inizio */
94
+ transform: scale(1) rotate(0deg);
95
+ box-shadow: 0 0 0 rgba(0, 113, 188, 0.5);
96
+ }
97
+ 50% {
98
+ /* Ingrandimento e glow al 50% dell'animazione */
99
+ transform: scale(1.1) rotate(0deg);
100
+ box-shadow: 0 0 15px 5px rgba(0, 113, 188, 0.6);
101
+ }
102
+ 100% {
103
+ /* Ritorno alla dimensione normale */
104
+ transform: scale(1) rotate(0deg);
105
+ box-shadow: 0 0 0 rgba(0, 113, 188, 0.5);
106
+ }
107
+ }
108
+ `;
109
+ /**
110
+ * Overlay trasparente per bloccare le interazioni durante il drag
111
+ * Previene problemi di performance con iframe e altri elementi interattivi
112
+ */
113
+ const DragOverlay = styled.div `
114
+ position: fixed;
115
+ top: 0;
116
+ left: 0;
117
+ right: 0;
118
+ bottom: 0;
119
+ z-index: 2147483646;
120
+ cursor: grabbing;
121
+ `;
122
+ /**
123
+ * Componente ToppyDraggableHelpCenter
124
+ *
125
+ * Renderizza un assistente virtuale (Toppy) draggable che può mostrare contenuti
126
+ * in una speech bubble. Il componente può essere trascinato all'interno del suo
127
+ * contenitore e può essere collassato/espanso con un doppio click.
128
+ */
129
+ const ToppyDraggableHelpCenter = ({ content, deviceType, align = 'right', onToppyImageClick, initialIsCollapsed, isVisible = true, }) => {
130
+ // Ref per il contenitore principale
131
+ const buttonRef = useRef(null);
132
+ // Stato per controllare se il componente è collassato o espanso
133
+ const [isCollapsed, setIsCollapsed] = useState(initialIsCollapsed ?? false);
134
+ // Stato per tracciare se il componente è in fase di trascinamento
135
+ const [isDragging, setIsDragging] = useState(false);
136
+ // Posizione corrente del componente (null = posizione di default)
137
+ const [position, setPosition] = useState(null);
138
+ // Offset del mouse rispetto all'angolo superiore sinistro del componente durante il drag
139
+ const dragOffset = useRef({ x: 0, y: 0 });
140
+ // Ref e stato per tracciare le dimensioni della speech bubble
141
+ const bubbleRef = useRef(null);
142
+ const [bubbleSize, setBubbleSize] = useState({ width: 0, height: 0 });
143
+ const isMobile = deviceType === DeviceType.MOBILE;
144
+ /**
145
+ * Effect per aggiornare le dimensioni della bubble quando cambia il contenuto
146
+ * o lo stato di collassamento. Necessario per calcolare correttamente i limiti
147
+ * di trascinamento.
148
+ */
149
+ useEffect(() => {
150
+ if (bubbleRef.current) {
151
+ const rect = bubbleRef.current.getBoundingClientRect();
152
+ setBubbleSize({ width: rect.width, height: rect.height });
153
+ }
154
+ }, [content, isCollapsed]);
155
+ /**
156
+ * Effect per resettare la posizione quando il parent o la finestra vengono ridimensionati.
157
+ * Questo previene che il componente finisca fuori dai bordi dopo un resize.
158
+ */
159
+ useEffect(() => {
160
+ if (!buttonRef.current)
161
+ return;
162
+ const parent = buttonRef.current.offsetParent;
163
+ if (!parent)
164
+ return;
165
+ // Funzione per verificare e aggiustare la posizione dopo un resize
166
+ const handleResize = () => {
167
+ if (!buttonRef.current || !position)
168
+ return;
169
+ const parentRect = parent.getBoundingClientRect();
170
+ const rect = buttonRef.current.getBoundingClientRect();
171
+ // Spazio extra occupato dalla bubble quando non è collassato
172
+ const extraHeight = !isCollapsed ? bubbleSize.height : 0;
173
+ const extraWidth = !isCollapsed ? bubbleSize.width : 0;
174
+ // Calcola i nuovi limiti
175
+ let minX = 0;
176
+ let maxX = parentRect.width - rect.width;
177
+ if (!isCollapsed) {
178
+ if (align === 'right') {
179
+ minX = Math.max(0, extraWidth - rect.width);
180
+ }
181
+ else {
182
+ maxX = Math.min(maxX, parentRect.width - extraWidth);
183
+ }
184
+ }
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
+ const isOutOfBounds = position.x < minX ||
189
+ position.x > maxX ||
190
+ position.y < extraHeight + bubbleBuffer ||
191
+ position.y > maxY;
192
+ // Se è fuori dai limiti, resetta alla posizione default
193
+ if (isOutOfBounds) {
194
+ setPosition(null);
195
+ }
196
+ };
197
+ // Observer per monitorare i cambiamenti di dimensione del parent
198
+ const resizeObserver = new ResizeObserver(handleResize);
199
+ resizeObserver.observe(parent);
200
+ // Listener per il resize della finestra
201
+ window.addEventListener('resize', handleResize);
202
+ return () => {
203
+ resizeObserver.disconnect();
204
+ window.removeEventListener('resize', handleResize);
205
+ };
206
+ }, [position, isCollapsed, bubbleSize, align]);
207
+ /**
208
+ * Effect per impostare automaticamente lo stato collassato su dispositivi mobile
209
+ * se non è stato specificato un valore iniziale.
210
+ */
211
+ useEffect(() => {
212
+ if (initialIsCollapsed === undefined && deviceType === DeviceType.MOBILE) {
213
+ setIsCollapsed(true);
214
+ }
215
+ }, [deviceType, initialIsCollapsed]);
216
+ /**
217
+ * Gestisce il toggle dello stato collassato/espanso
218
+ * Chiamato dal doppio click sul componente
219
+ */
220
+ const toggleCollapse = (e) => {
221
+ e.stopPropagation();
222
+ setIsCollapsed(!isCollapsed);
223
+ onToppyImageClick?.();
224
+ };
225
+ /**
226
+ * Gestisce l'inizio del trascinamento
227
+ * Salva la posizione relativa del mouse rispetto al componente (offset)
228
+ * per mantenere il punto di presa durante il trascinamento
229
+ */
230
+ const handleMouseDown = (e) => {
231
+ if (!buttonRef.current)
232
+ return;
233
+ const rect = buttonRef.current.getBoundingClientRect();
234
+ const parentRect = buttonRef.current.offsetParent?.getBoundingClientRect();
235
+ if (!parentRect)
236
+ return;
237
+ // Calcola l'offset tra il punto di click e l'angolo superiore sinistro del componente
238
+ dragOffset.current = {
239
+ x: e.clientX - rect.left,
240
+ y: e.clientY - rect.top,
241
+ };
242
+ setIsDragging(true);
243
+ e.preventDefault();
244
+ };
245
+ /**
246
+ * Gestisce il movimento durante il trascinamento
247
+ * Calcola la nuova posizione rispettando i limiti del parent container
248
+ * e tenendo conto delle dimensioni della speech bubble quando espansa
249
+ */
250
+ const handleMouseMove = (e) => {
251
+ if (!isDragging || !buttonRef.current)
252
+ return;
253
+ const parentRect = buttonRef.current.offsetParent?.getBoundingClientRect();
254
+ if (!parentRect)
255
+ return;
256
+ const rect = buttonRef.current.getBoundingClientRect();
257
+ // Spazio extra occupato dalla bubble quando non è collassato
258
+ const extraHeight = !isCollapsed ? bubbleSize.height : 0;
259
+ const extraWidth = !isCollapsed ? bubbleSize.width : 0;
260
+ // Calcola i limiti orizzontali considerando la bubble
261
+ let minX = 0;
262
+ let maxX = parentRect.width - rect.width;
263
+ if (!isCollapsed) {
264
+ if (align === 'right') {
265
+ // La bubble si estende verso sinistra: aumenta il limite minimo
266
+ // per evitare che la bubble esca dal bordo sinistro
267
+ minX = Math.max(0, extraWidth - rect.width);
268
+ }
269
+ else {
270
+ // La bubble si estende verso destra: riduce il limite massimo
271
+ // per evitare che la bubble esca dal bordo destro
272
+ maxX = Math.min(maxX, parentRect.width - extraWidth);
273
+ }
274
+ }
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
+ setPosition({ x: newX, y: newY });
284
+ };
285
+ /**
286
+ * Gestisce il rilascio del mouse alla fine del trascinamento
287
+ * Se il movimento è stato minimo (< 5px), viene interpretato come un click
288
+ * e viene chiamato il callback onToppyImageClick
289
+ */
290
+ const handleMouseUp = (e) => {
291
+ if (isDragging) {
292
+ setIsDragging(false);
293
+ const rect = buttonRef.current?.getBoundingClientRect();
294
+ if (rect) {
295
+ // Calcola la distanza totale del movimento usando il teorema di Pitagora
296
+ const moveDistance = Math.hypot(e.clientX - (rect.left + dragOffset.current.x), e.clientY - (rect.top + dragOffset.current.y));
297
+ // Se il movimento è stato minimo, trattalo come un click
298
+ if (moveDistance < 5 && onToppyImageClick) {
299
+ onToppyImageClick();
300
+ }
301
+ }
302
+ }
303
+ };
304
+ /**
305
+ * Effect per gestire gli event listener durante il trascinamento
306
+ * Gli eventi sono registrati sul document per catturare il movimento
307
+ * anche quando il mouse esce dal componente
308
+ */
309
+ useEffect(() => {
310
+ if (isDragging) {
311
+ document.addEventListener('mousemove', handleMouseMove);
312
+ document.addEventListener('mouseup', handleMouseUp);
313
+ return () => {
314
+ document.removeEventListener('mousemove', handleMouseMove);
315
+ document.removeEventListener('mouseup', handleMouseUp);
316
+ };
317
+ }
318
+ return undefined;
319
+ }, [isDragging]);
320
+ // Renderizza l'overlay solo durante il drag
321
+ const renderDragOverlay = isDragging && _jsx(DragOverlay, {});
322
+ return (_jsxs(_Fragment, { children: [renderDragOverlay, _jsxs(ToppyButton, { ref: buttonRef, "$align": align, "$isDragging": isDragging, "$x": position?.x, "$y": position?.y, "$isVisible": isVisible, "$isCollapsed": isCollapsed, "$isMobile": isMobile, onMouseDown: !isMobile ? handleMouseDown : undefined, onContextMenu: (e) => e.preventDefault(), onDoubleClick: !isMobile ? toggleCollapse : undefined, children: [(content && !isCollapsed) && (_jsx(ToppySpeechBubble, { ref: bubbleRef, align: align, children: content })), _jsx("img", { src: Toppy, alt: "Toppy Help", draggable: false, onClick: isMobile ? toggleCollapse : undefined })] })] }));
323
+ };
324
+ export default ToppyDraggableHelpCenter;
@@ -162,7 +162,7 @@ const ToppyHelpCenter = ({ content, deviceType, usePortal = true, align = 'right
162
162
  onToppyImageClick?.();
163
163
  };
164
164
  const isMobile = deviceType === DeviceType.MOBILE;
165
- const toppyComponent = (_jsxs(ToppyContainer, { "$isMobile": isMobile, "$isCollapsed": isCollapsed, "$fixed": usePortal, "$align": align, 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 })] }));
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
166
  if (usePortal) {
167
167
  if (!portalContainer)
168
168
  return null;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface ToppySpeechBubbleProps {
3
+ align?: 'left' | 'right';
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ }
7
+ declare const ToppySpeechBubble: React.ForwardRefExoticComponent<ToppySpeechBubbleProps & React.RefAttributes<HTMLDivElement>>;
8
+ export default ToppySpeechBubble;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import styled from 'styled-components';
4
+ // Styled component
5
+ const Bubble = styled.div `
6
+ position: absolute;
7
+ bottom: 145px;
8
+ ${({ $align }) => ($align === 'left' ? 'left: 0px;' : 'right: 0px;')}
9
+ width: max-content;
10
+ padding: 10px;
11
+ background: linear-gradient(180deg, rgba(0, 113, 188, 0.45) 0%,rgba(27, 20, 100, 0.65) 100%);
12
+ border-radius: 18px;
13
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
14
+ font-size: 14px;
15
+ line-height: 1.4;
16
+ color: #333;
17
+ z-index: 10000;
18
+
19
+ &::after {
20
+ transform: ${({ $align }) => ($align === 'left' ? 'skewX(15deg)' : 'skewX(-15deg)')};
21
+ content: "";
22
+ position: absolute;
23
+ top: 100%;
24
+ ${({ $align }) => ($align === 'left' ? 'left: 20px;' : 'right: 15px;')}
25
+ border-width: 32px 32px 0 0;
26
+ border-style: solid;
27
+ border-color: #FFFFFF transparent;
28
+ display: block;
29
+ width: 0;
30
+ height: 0;
31
+ z-index: 1;
32
+ }
33
+
34
+ &::before {
35
+ transform: ${({ $align }) => ($align === 'left' ? 'skewX(15deg)' : 'skewX(-15deg)')};
36
+ content: "";
37
+ position: absolute;
38
+ top: 100%;
39
+ ${({ $align }) => ($align === 'left' ? 'left: 20px;' : 'right: 15px;')}
40
+ border-width: 32px 32px 0 0;
41
+ border-style: solid;
42
+ border-color: rgba(27, 20, 100, 0.65) transparent;
43
+ display: block;
44
+ width: 0;
45
+ height: 0;
46
+ z-index: 2;
47
+ }
48
+ `;
49
+ // Componente con forwardRef
50
+ const ToppySpeechBubble = forwardRef(({ align = 'right', children, className }, ref) => {
51
+ return (_jsx(Bubble, { ref: ref, "$align": align, className: className, children: children }));
52
+ });
53
+ export default ToppySpeechBubble;
@@ -6,12 +6,14 @@ interface TMBlogCommentFormProps {
6
6
  participants: Array<UserDescriptor>;
7
7
  onClose: () => void;
8
8
  showAttachmentsSection?: boolean;
9
+ removeAndEditAttachment?: boolean;
9
10
  selectedAttachments?: Array<FileItem>;
10
11
  selectedAttachmentDid?: Array<number>;
11
12
  allFileItems?: FileItem;
12
13
  allArchivedDocumentsFileItems?: Array<FileItem>;
13
14
  onFilterCreated?: (predicate: (post: BlogPost) => boolean) => void;
14
15
  refreshCallback?: () => Promise<void>;
16
+ isCommentRequired?: boolean;
15
17
  }
16
18
  declare const TMBlogCommentForm: (props: TMBlogCommentFormProps) => import("react/jsx-runtime").JSX.Element;
17
19
  export default TMBlogCommentForm;
@@ -29,7 +29,7 @@ const getNonDirectoryFiles = (items, exclude) => {
29
29
  };
30
30
  const TMBlogCommentForm = (props) => {
31
31
  const maxLength = 1000;
32
- const { participants, selectedAttachments, selectedAttachmentDid, allFileItems, allArchivedDocumentsFileItems = [], onClose, context, showAttachmentsSection = true, onFilterCreated, refreshCallback } = props;
32
+ const { participants, selectedAttachments, selectedAttachmentDid, allFileItems, allArchivedDocumentsFileItems = [], onClose, context, showAttachmentsSection = true, removeAndEditAttachment = true, onFilterCreated, refreshCallback, isCommentRequired = false } = props;
33
33
  // Initialize state with combined array
34
34
  const [dataSource, setDataSource] = useState(() => [...getNonDirectoryFiles(allFileItems?.items || [], []), ...allArchivedDocumentsFileItems]);
35
35
  const [isEditorEnabled, setIsEditorEnabled] = useState(true);
@@ -216,8 +216,20 @@ const TMBlogCommentForm = (props) => {
216
216
  // Update the state with selected draft items
217
217
  setCurrentDraftAttachments(selectedDraftItems);
218
218
  };
219
- return _jsx(TMSaveForm, { id: 1, title: SDKUI_Localizator.AddNewComment, showTitleFormMode: false, showErrorCount: false, customSaveButton: _jsx("i", { className: 'dx-icon-send' }), customTooltipSaveButton: SDKUI_Localizator.Send, showUndoButton: false, hasNavigation: false, skipIsModifiedCheck: true, isModal: true, width: calcResponsiveSizes(deviceType, '800px', '800px', '95%'), height: '550px', formMode: FormModes.Create, validationItems: validationItems, exception: exception, isModified: calcIsModified(formData, formDataOrig), onSaveAsync: onSaveAsync, onClose: onCloseCallback, customToolbarElements: _jsx("div", { style: { display: 'flex', gap: '2px' }, children: _jsx(TMButton, { btnStyle: "toolbar", icon: isEditorEnabled ? _jsx("i", { className: 'dx-icon-font' }) : _jsx("i", { className: 'dx-icon-background' }), caption: isEditorEnabled ? SDKUI_Localizator.HideFormattingOptions : SDKUI_Localizator.ShowFormattingOptions, onClick: toggleEditorMode }) }), children: _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { style: { width: "100%", height: showAttachmentsSection ? "calc(100% - 60px)" : "100%" }, children: _jsx(TMHtmlEditor, { width: '100%', height: '100%', isEditorEnabled: isEditorEnabled, validationItems: validationItems, onValueChanged: onValueChanged, mentionsConfig: mentionsConfig, autoFocus: true, maxLength: maxLength }) }), showAttachmentsSection && _jsxs("div", { style: { display: 'flex', alignItems: 'center', height: '60px', marginTop: '10px' }, children: [_jsx("div", { style: {
220
- width: 'calc(100% - 60px)',
219
+ return _jsx(TMSaveForm, { id: 1, title: SDKUI_Localizator.AddNewComment, showTitleFormMode: false, showErrorCount: false, customSaveButton: _jsx("i", { className: 'dx-icon-send' }), customTooltipSaveButton: SDKUI_Localizator.Send, showUndoButton: false, hasNavigation: false, skipIsModifiedCheck: true, isModal: true, width: calcResponsiveSizes(deviceType, '800px', '800px', '95%'), height: '550px', formMode: FormModes.Create, validationItems: validationItems, exception: exception, isModified: calcIsModified(formData, formDataOrig), onSaveAsync: onSaveAsync, onClose: onCloseCallback, showCloseButton: isCommentRequired ? false : true, customToolbarElements: _jsx("div", { style: { display: 'flex', gap: '2px' }, children: _jsx(TMButton, { btnStyle: "toolbar", icon: isEditorEnabled ? _jsx("i", { className: 'dx-icon-font' }) : _jsx("i", { className: 'dx-icon-background' }), caption: isEditorEnabled ? SDKUI_Localizator.HideFormattingOptions : SDKUI_Localizator.ShowFormattingOptions, onClick: toggleEditorMode }) }), children: _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsxs("div", { style: { width: "100%", height: showAttachmentsSection ? `calc(100% - 60px)` : '100%' }, children: [isCommentRequired && _jsx("div", { style: {
220
+ padding: '10px',
221
+ width: '100%',
222
+ height: '30px',
223
+ backgroundColor: '#e8f5e9',
224
+ border: '1px solid #81c784',
225
+ borderRadius: '4px',
226
+ fontSize: '13px',
227
+ marginBottom: '10px',
228
+ color: '#2e7d32',
229
+ display: 'flex',
230
+ alignItems: 'center',
231
+ }, children: SDKUI_Localizator.InsertCommentToCompleteOperation }), _jsx("div", { style: { width: "100%", height: isCommentRequired ? `calc(100% - 40px)` : '100%' }, children: _jsx(TMHtmlEditor, { width: '100%', height: '100%', isEditorEnabled: isEditorEnabled, validationItems: validationItems, onValueChanged: onValueChanged, mentionsConfig: mentionsConfig, autoFocus: true, maxLength: maxLength }) })] }), showAttachmentsSection && _jsxs("div", { style: { display: 'flex', alignItems: 'center', height: '60px', marginTop: '10px' }, children: [_jsx("div", { style: {
232
+ width: `calc(100% - ${removeAndEditAttachment ? 60 : 0}px)`,
221
233
  overflowX: 'auto',
222
234
  whiteSpace: 'nowrap',
223
235
  height: '50px',
@@ -231,7 +243,7 @@ const TMBlogCommentForm = (props) => {
231
243
  const tooltipContent = (_jsxs("div", { style: { textAlign: 'left' }, children: [_jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Name, ":"] }), " ", draft.name ?? '-'] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Author, ":"] }), " ", draft.updaterName ?? '-'] }), _jsx("hr", {}), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Version, ":"] }), " ", draft.version] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Size, ":"] }), " ", formatBytes(draft.size ?? 0)] }), _jsx("hr", {}), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.CreationTime, ":"] }), " ", Globalization.getDateTimeDisplayValue(draft.creationTime)] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.LastUpdateTime, ":"] }), " ", Globalization.getDateTimeDisplayValue(draft.lastUpdateTime)] })] }));
232
244
  return _jsxs("div", { style: {
233
245
  display: 'inline-flex',
234
- alignItems: 'center', // <-- this centers content vertically
246
+ alignItems: 'center',
235
247
  padding: '4px 8px',
236
248
  margin: '4px',
237
249
  border: '1px solid #ddd',
@@ -258,7 +270,7 @@ const TMBlogCommentForm = (props) => {
258
270
  fontWeight: 'bold',
259
271
  marginRight: '8px',
260
272
  boxShadow: '1px 1px 2px #00000020',
261
- }, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Version, children: draft.version }) })), _jsx(TMTooltip, { content: SDKUI_Localizator.RemoveAttachment, children: _jsx("span", { onClick: () => removeAttachment(draft), style: {
273
+ }, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Version, children: draft.version }) })), removeAndEditAttachment && _jsx(TMTooltip, { content: SDKUI_Localizator.RemoveAttachment, children: _jsx("span", { onClick: () => removeAttachment(draft), style: {
262
274
  display: 'inline-flex',
263
275
  width: '20px',
264
276
  height: '20px',
@@ -270,7 +282,7 @@ const TMBlogCommentForm = (props) => {
270
282
  cursor: 'pointer',
271
283
  boxShadow: '1px 1px 2px #00000020',
272
284
  }, children: _jsx("span", { style: { fontSize: '15px' }, children: "\u00D7" }) }) })] }, draft.did);
273
- })) : (_jsx("div", { style: { color: '#999', width: '100%', textAlign: 'center' }, children: SDKUI_Localizator.NoAttachments })) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Attachments + ": " + currentDraftAttachments.length, children: _jsxs("div", { style: { position: 'relative', display: 'inline-block' }, children: [_jsx("i", { className: "dx-icon-attach", style: {
285
+ })) : (_jsx("div", { style: { color: '#999', width: '100%', textAlign: 'center' }, children: SDKUI_Localizator.NoAttachments })) }), removeAndEditAttachment && _jsx(TMTooltip, { content: SDKUI_Localizator.Attachments + ": " + currentDraftAttachments.length, children: _jsxs("div", { style: { position: 'relative', display: 'inline-block' }, children: [_jsx("i", { className: "dx-icon-attach", style: {
274
286
  width: '50px',
275
287
  height: '50px',
276
288
  marginLeft: '10px',
@@ -57,7 +57,7 @@ const TMDcmtBlog = ({ blogsDatasource, setBlogsDatasource, hasLoadedDataOnce, se
57
57
  isRefreshEnabled: true,
58
58
  isRestoreEnabled: true,
59
59
  isCreateContextualTask: false
60
- }, externalBlogPost: externalBlogPost, resetExternalBlogPost: resetExternalBlogPost, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }) }) }) }), (showCommentForm && tid && did) && _jsx(TMBlogCommentForm, { context: { engine: 'SearchEngine', object: { tid, did } }, onClose: () => setShowCommentForm(false), refreshCallback: refreshCallback, participants: [], showAttachmentsSection: false, allArchivedDocumentsFileItems: [], onFilterCreated: handleFilterCreated })] }));
60
+ }, externalBlogPost: externalBlogPost, resetExternalBlogPost: resetExternalBlogPost, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, afterTaskSaved: refreshCallback }) }) }) }), (showCommentForm && tid && did) && _jsx(TMBlogCommentForm, { context: { engine: 'SearchEngine', object: { tid, did } }, onClose: () => setShowCommentForm(false), refreshCallback: refreshCallback, participants: [], showAttachmentsSection: false, allArchivedDocumentsFileItems: [], onFilterCreated: handleFilterCreated })] }));
61
61
  };
62
62
  export default TMDcmtBlog;
63
63
  const StyledContainer = styled.div ` user-select: none; overflow: hidden; background-color: #ffffff; width: calc(100%); height: calc(100%); display: flex; gap: 10px; `;