@topconsultnpm/sdkui-react 6.20.0-dev1.112 → 6.20.0-dev1.114

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 (34) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +42 -7
  2. package/lib/components/NewComponents/ContextMenu/styles.js +2 -0
  3. package/lib/components/NewComponents/ContextMenu/types.d.ts +8 -2
  4. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +159 -24
  5. package/lib/components/NewComponents/FloatingMenuBar/styles.js +5 -2
  6. package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +3 -0
  7. package/lib/components/base/TMButton.js +6 -0
  8. package/lib/components/base/TMClosableList.js +4 -0
  9. package/lib/components/base/TMDropDownMenu.js +2 -0
  10. package/lib/components/base/TMListView.js +1 -1
  11. package/lib/components/base/TMPanel.js +2 -0
  12. package/lib/components/base/TMPopUp.js +6 -0
  13. package/lib/components/base/TMToolbarCard.js +2 -0
  14. package/lib/components/editors/TMCheckBox.js +2 -0
  15. package/lib/components/editors/TMEditorStyled.js +4 -0
  16. package/lib/components/editors/TMRadioButton.js +2 -0
  17. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -0
  18. package/lib/components/features/documents/TMDcmtBlog.js +1 -1
  19. package/lib/components/features/documents/TMDcmtPreview.js +23 -27
  20. package/lib/components/features/search/TMSearchQueryPanel.js +2 -0
  21. package/lib/components/features/search/TMSearchResult.js +5 -3
  22. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +1 -1
  23. package/lib/components/features/search/TMSearchResultsMenuItems.js +24 -4
  24. package/lib/components/features/workflow/diagram/DiagramItemComponent.js +2 -0
  25. package/lib/components/grids/TMValidationItemsList.js +6 -0
  26. package/lib/components/query/TMQueryEditor.js +2 -0
  27. package/lib/components/sidebar/TMHeader.js +4 -0
  28. package/lib/components/sidebar/TMSidebarItem.js +2 -0
  29. package/lib/helper/TMToppyMessage.js +4 -0
  30. package/lib/helper/helpers.d.ts +2 -1
  31. package/lib/helper/helpers.js +1 -0
  32. package/lib/hooks/useFloatingBarPinnedItems.d.ts +11 -0
  33. package/lib/hooks/useFloatingBarPinnedItems.js +54 -0
  34. package/package.json +1 -1
@@ -12,6 +12,8 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
12
12
  parentNames: [],
13
13
  });
14
14
  const [hoveredSubmenus, setHoveredSubmenus] = useState([]);
15
+ // Tracks items whose rightIconProps have been clicked, so the icon color flips immediately
16
+ const [toggledItemIds, setToggledItemIds] = useState(new Set());
15
17
  const isMobile = useIsMobile();
16
18
  const isIOS = useIsIOS();
17
19
  const menuRef = useRef(null);
@@ -35,6 +37,7 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
35
37
  }));
36
38
  }
37
39
  setHoveredSubmenus([]);
40
+ setToggledItemIds(new Set());
38
41
  };
39
42
  // Sync with external control when provided
40
43
  useEffect(() => {
@@ -247,8 +250,17 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
247
250
  const handleContextMenu = (e) => {
248
251
  if (trigger === 'right') {
249
252
  e.preventDefault();
250
- e.stopPropagation(); // Prevent event from bubbling to close other menus prematurely
251
- // Small delay to ensure other menus receive the contextmenu event and close first
253
+ // Close any other open context menus by dispatching a synthetic contextmenu event to document
254
+ // This triggers the document listener in other ContextMenu instances to close them
255
+ const syntheticEvent = new MouseEvent('contextmenu', {
256
+ bubbles: true,
257
+ cancelable: true,
258
+ clientX: -1000, // Position outside viewport so it doesn't interfere
259
+ clientY: -1000,
260
+ });
261
+ document.dispatchEvent(syntheticEvent);
262
+ e.stopPropagation(); // Prevent event from bubbling after closing others
263
+ // Small delay to ensure other menus close first
252
264
  setTimeout(() => {
253
265
  setMenuState({
254
266
  visible: true,
@@ -421,13 +433,36 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
421
433
  e.stopPropagation();
422
434
  handleItemClick(item);
423
435
  };
424
- const handleRightIconClick = (e) => {
436
+ const handleRightIconPropsClick = (e) => {
437
+ e.preventDefault();
425
438
  e.stopPropagation();
426
- // if (item.disabled) return;
427
- item.onRightIconClick?.();
428
- handleClose();
439
+ e.nativeEvent.stopImmediatePropagation();
440
+ item.rightIconProps?.onClick?.();
441
+ if (item.id) {
442
+ setToggledItemIds(prev => {
443
+ const next = new Set(prev);
444
+ if (next.has(item.id))
445
+ next.delete(item.id);
446
+ else
447
+ next.add(item.id);
448
+ return next;
449
+ });
450
+ }
429
451
  };
430
- return (_jsxs(S.MenuItem, { "$disabled": item.disabled, "$hasSubmenu": !!item.submenu && item.submenu.length > 0, "$beginGroup": item.beginGroup, "data-disabled": item.disabled ? "true" : undefined, onMouseDown: handleClick, onMouseEnter: (e) => !isMobile && handleMouseEnter(item, e, depth + 1), onMouseLeave: () => !isMobile && handleMouseLeave(depth + 1), title: item.tooltip, children: [_jsxs(S.MenuItemContent, { children: [item.icon && _jsx(S.IconWrapper, { children: item.icon }), _jsx(S.MenuItemName, { children: item.name })] }), item.rightIcon && item.onRightIconClick && (_jsx(S.RightIconButton, { onClick: handleRightIconClick, onMouseDown: (e) => e.stopPropagation(), "aria-label": `Action for ${item.name}`, children: item.rightIcon })), item.submenu && item.submenu.length > 0 && (_jsx(S.SubmenuIndicator, { "$isMobile": isMobile, children: isMobile ? '›' : '▸' }))] }, itemKey));
452
+ // rightIconProps takes precedence over rightIcon
453
+ let rightIconElement = null;
454
+ let rightIconClickHandler = null;
455
+ if (item.rightIconProps) {
456
+ const rip = item.rightIconProps;
457
+ const wasToggled = item.id ? toggledItemIds.has(item.id) : false;
458
+ const isActive = wasToggled ? !rip.isActive : rip.isActive;
459
+ const color = isActive ? (rip.activeColor || '#d32f2f') : (rip.inactiveColor || '#626262');
460
+ rightIconElement = _jsx("span", { style: { color, display: 'flex', alignItems: 'center' }, children: rip.icon });
461
+ if (rip.onClick) {
462
+ rightIconClickHandler = handleRightIconPropsClick;
463
+ }
464
+ }
465
+ return (_jsxs(S.MenuItem, { "$disabled": item.disabled, "$hasSubmenu": !!item.submenu && item.submenu.length > 0, "$beginGroup": item.beginGroup, "data-disabled": item.disabled ? "true" : undefined, onMouseDown: handleClick, onMouseEnter: (e) => !isMobile && handleMouseEnter(item, e, depth + 1), onMouseLeave: () => !isMobile && handleMouseLeave(depth + 1), title: item.tooltip, children: [_jsxs(S.MenuItemContent, { children: [item.icon && _jsx(S.IconWrapper, { children: item.icon }), _jsx(S.MenuItemName, { children: item.name })] }), rightIconElement && rightIconClickHandler ? (_jsx(S.RightIconButton, { onClick: rightIconClickHandler, onMouseDown: (e) => e.stopPropagation(), "aria-label": `Action for ${item.name}`, children: rightIconElement })) : rightIconElement ? (_jsx(S.IconWrapper, { children: rightIconElement })) : null, item.submenu && item.submenu.length > 0 && (_jsx(S.SubmenuIndicator, { "$isMobile": isMobile, children: isMobile ? '›' : '▸' }))] }, itemKey));
431
466
  });
432
467
  };
433
468
  const currentParentName = menuState.parentNames.at(-1) || '';
@@ -103,6 +103,8 @@ export const MenuItem = styled.div `
103
103
  transition: all 0.15s ease;
104
104
  position: relative;
105
105
  user-select: none;
106
+ -webkit-touch-callout: none;
107
+ -webkit-user-select: none;
106
108
  font-size: var(--base-font-size, 13px);
107
109
  color: ${props => props.$disabled ? '#999' : '#1a1a1a'};
108
110
  font-weight: 500;
@@ -1,3 +1,10 @@
1
+ export interface RightIconProps {
2
+ icon: React.ReactNode;
3
+ activeColor?: string;
4
+ inactiveColor?: string;
5
+ isActive?: boolean;
6
+ onClick?: () => void;
7
+ }
1
8
  export interface TMContextMenuItemProps {
2
9
  id?: string;
3
10
  name: string;
@@ -6,8 +13,7 @@ export interface TMContextMenuItemProps {
6
13
  onClick?: (data?: any) => void;
7
14
  submenu?: TMContextMenuItemProps[];
8
15
  visible?: boolean;
9
- rightIcon?: React.ReactNode;
10
- onRightIconClick?: () => void;
16
+ rightIconProps?: RightIconProps;
11
17
  beginGroup?: boolean;
12
18
  tooltip?: string;
13
19
  operationType?: 'singleRow' | 'multiRow';
@@ -4,11 +4,11 @@ import { ContextMenu } from '../ContextMenu';
4
4
  import ShowAlert from '../../base/TMAlert';
5
5
  import TMTooltip from '../../base/TMTooltip';
6
6
  import * as S from './styles';
7
- import { IconAdd, IconCloseOutline, IconMenuVertical, IconPin, IconSave, IconSeparator, IconUndo, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
7
+ import { IconAdd, IconCloseOutline, IconDelete, IconMenuVertical, IconPin, IconRotate, IconSave, IconSeparator, IconSettings, IconUndo, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
8
8
  import { ButtonNames, TMMessageBoxManager } from '../../base/TMPopUp';
9
9
  const Separator = (props) => (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", height: "1em", width: "1em", ...props, children: _jsx("path", { d: "M12 2v20", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" }) }));
10
10
  const IconDraggableDots = (props) => (_jsx("svg", { fontSize: 18, viewBox: "0 0 24 24", fill: "currentColor", height: "1em", width: "1em", ...props, children: _jsx("path", { d: "M9 3a2 2 0 11-4 0 2 2 0 014 0zm0 9a2 2 0 11-4 0 2 2 0 014 0zm0 9a2 2 0 11-4 0 2 2 0 014 0zm10-18a2 2 0 11-4 0 2 2 0 014 0zm0 9a2 2 0 11-4 0 2 2 0 014 0zm0 9a2 2 0 11-4 0 2 2 0 014 0z" }) }));
11
- const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 1, y: 90 }, defaultOrientation = 'horizontal', maxItems = 100, contextMenuDefaultPinnedIds = [], defaultItems = [], disbaleConfigMode = false, bgColor = undefined, }) => {
11
+ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 1, y: 90 }, defaultOrientation = 'horizontal', maxItems = 100, contextMenuDefaultPinnedIds = [], defaultItems = [], disbaleConfigMode = false, bgColor = undefined, hasContextMenu = true, pinnedItemIds: externalPinnedItemIds, onPinChange, }) => {
12
12
  const percentToPixels = (percent, containerSize) => {
13
13
  return (percent / 100) * containerSize;
14
14
  };
@@ -145,6 +145,10 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
145
145
  const positionBeforeOrientationChange = useRef(null);
146
146
  const floatingBarItemIds = useRef(new Set());
147
147
  const floatingBarItemNames = useRef(new Set());
148
+ const isSyncingFromExternal = useRef(false);
149
+ const isExitingConfigMode = useRef(false);
150
+ const isLocalChange = useRef(false);
151
+ const dragStartPosition = useRef(null);
148
152
  useEffect(() => {
149
153
  floatingBarItemIds.current = new Set(state.items.map(i => i.id));
150
154
  floatingBarItemNames.current = new Set(state.items.map(i => i.name));
@@ -247,6 +251,44 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
247
251
  }
248
252
  }
249
253
  }, disbaleConfigMode ? [defaultItems] : []); // Update when defaultItems change if config mode is disabled
254
+ // Sync with external pinnedItemIds when they change (from parent component)
255
+ useEffect(() => {
256
+ // Skip sync if exiting config mode - let save effect update external state first
257
+ if (isExitingConfigMode.current) {
258
+ isExitingConfigMode.current = false;
259
+ return;
260
+ }
261
+ // Skip sync if a local change was just made (e.g. right-click remove/add separator)
262
+ if (isLocalChange.current) {
263
+ isLocalChange.current = false;
264
+ return;
265
+ }
266
+ if (externalPinnedItemIds === undefined || disbaleConfigMode || state.isConfigMode)
267
+ return;
268
+ const flatItems = flattenMenuItems(contextMenuItems);
269
+ // Build items from external pinned IDs
270
+ const itemsFromExternal = externalPinnedItemIds
271
+ .map((id) => {
272
+ if (id.startsWith('separator-')) {
273
+ return {
274
+ id,
275
+ name: 'Separator',
276
+ icon: _jsx(Separator, {}),
277
+ onClick: () => { },
278
+ isSeparator: true,
279
+ };
280
+ }
281
+ return flatItems.find(item => item.id === id);
282
+ })
283
+ .filter((item) => item !== undefined);
284
+ // Only update if different
285
+ const currentIds = state.items.map(i => i.id).join(',');
286
+ const newIds = itemsFromExternal.map(i => i.id).join(',');
287
+ if (currentIds !== newIds) {
288
+ isSyncingFromExternal.current = true; // Mark that we're syncing from external
289
+ setState(s => ({ ...s, items: itemsFromExternal }));
290
+ }
291
+ }, [externalPinnedItemIds, contextMenuItems, disbaleConfigMode, state.isConfigMode, flattenMenuItems]);
250
292
  const togglePin = useCallback((item) => {
251
293
  setState(s => {
252
294
  const isInFloatingBar = s.items.some(i => i.id === item.id);
@@ -336,6 +378,8 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
336
378
  const isAlreadyPinned = currentItemIds.has(itemId);
337
379
  const pinItem = {
338
380
  ...item,
381
+ // Remove rightIconProps in config mode - clicking the item itself pins it
382
+ rightIconProps: undefined,
339
383
  onClick: item.onClick && !item.submenu ? () => {
340
384
  if (flatItem && !isAlreadyPinned) {
341
385
  togglePin(flatItem);
@@ -370,9 +414,12 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
370
414
  const isPinned = currentItemIds.has(itemId);
371
415
  const itemWithPin = {
372
416
  ...item,
373
- rightIcon: flatItem ? _jsx(IconPin, { color: isPinned ? 'red' : 'black' }) : undefined,
374
- onRightIconClick: flatItem ? () => {
375
- togglePin(flatItem);
417
+ rightIconProps: flatItem ? {
418
+ icon: _jsx(IconPin, {}),
419
+ activeColor: 'red',
420
+ inactiveColor: 'black',
421
+ isActive: isPinned,
422
+ onClick: () => togglePin(flatItem),
376
423
  } : undefined,
377
424
  };
378
425
  if (item.submenu) {
@@ -404,6 +451,8 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
404
451
  };
405
452
  }
406
453
  }
454
+ // Save starting position to detect if user actually moved the bar
455
+ dragStartPosition.current = { ...pixelPosition };
407
456
  setState(s => ({ ...s, isDragging: true }));
408
457
  };
409
458
  const handleGripDoubleClick = () => {
@@ -443,6 +492,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
443
492
  x: pixelsToPercent(pixelPosition.x, containerSizeRef.current.width),
444
493
  y: pixelsToPercent(pixelPosition.y, containerSizeRef.current.height),
445
494
  };
495
+ // Only clear saved position if user actually moved the bar significantly (more than 5 pixels)
496
+ if (dragStartPosition.current) {
497
+ const dx = Math.abs(pixelPosition.x - dragStartPosition.current.x);
498
+ const dy = Math.abs(pixelPosition.y - dragStartPosition.current.y);
499
+ if (dx > 5 || dy > 5) {
500
+ positionBeforeOrientationChange.current = null;
501
+ }
502
+ }
503
+ dragStartPosition.current = null;
446
504
  setState(s => ({
447
505
  ...s,
448
506
  isDragging: false,
@@ -473,6 +531,8 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
473
531
  };
474
532
  }
475
533
  }
534
+ // Save starting position to detect if user actually moved the bar
535
+ dragStartPosition.current = { ...pixelPosition };
476
536
  setState(s => ({ ...s, isDragging: true }));
477
537
  };
478
538
  const handleTouchMove = useCallback((e) => {
@@ -502,6 +562,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
502
562
  x: pixelsToPercent(pixelPosition.x, containerSizeRef.current.width),
503
563
  y: pixelsToPercent(pixelPosition.y, containerSizeRef.current.height),
504
564
  };
565
+ // Only clear saved position if user actually moved the bar significantly (more than 5 pixels)
566
+ if (dragStartPosition.current) {
567
+ const dx = Math.abs(pixelPosition.x - dragStartPosition.current.x);
568
+ const dy = Math.abs(pixelPosition.y - dragStartPosition.current.y);
569
+ if (dx > 5 || dy > 5) {
570
+ positionBeforeOrientationChange.current = null;
571
+ }
572
+ }
573
+ dragStartPosition.current = null;
505
574
  setState(s => ({
506
575
  ...s,
507
576
  isDragging: false,
@@ -533,11 +602,12 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
533
602
  return; // Don't save during edit mode
534
603
  if (disbaleConfigMode)
535
604
  return; // Don't save if config mode is disabled
605
+ const pinnedIds = state.items.map(item => item.id);
536
606
  try {
537
607
  // Replace the entire object to trigger the Proxy
538
608
  SDKUI_Globals.userSettings.searchSettings.floatingMenuBar = {
539
609
  orientation: state.orientation,
540
- itemIds: state.items.map(item => item.id),
610
+ itemIds: pinnedIds,
541
611
  position: state.position,
542
612
  positionFormat: 'percentage',
543
613
  };
@@ -545,7 +615,13 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
545
615
  catch (error) {
546
616
  console.error('Failed to save FloatingMenuBar config:', error);
547
617
  }
548
- }, [state.orientation, state.items, state.position, state.isConfigMode, disbaleConfigMode]);
618
+ // Notify parent about pin changes only if not syncing from external
619
+ // This prevents infinite loop: external change -> sync -> save -> onPinChange -> external change
620
+ if (!isSyncingFromExternal.current) {
621
+ onPinChange?.(pinnedIds);
622
+ }
623
+ isSyncingFromExternal.current = false; // Reset the flag
624
+ }, [state.orientation, state.items, state.position, state.isConfigMode, disbaleConfigMode, onPinChange]);
549
625
  const toggleConfigMode = () => {
550
626
  setState(s => {
551
627
  if (!s.isConfigMode) {
@@ -560,6 +636,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
560
636
  else {
561
637
  // Exiting edit mode (applying changes) - clean up trailing separators and clear snapshot
562
638
  stateSnapshot.current = null;
639
+ isExitingConfigMode.current = true; // Prevent sync effect from overwriting changes
563
640
  const cleanedItems = removeTrailingSeparators(s.items);
564
641
  return { ...s, isConfigMode: false, items: cleanedItems };
565
642
  }
@@ -679,11 +756,11 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
679
756
  };
680
757
  const toggleOrientation = () => {
681
758
  const newOrientation = state.orientation === 'horizontal' ? 'vertical' : 'horizontal';
682
- // Hide the bar during transition to prevent flicker
683
- setIsOrientationChanging(true);
684
759
  // When switching from vertical back to horizontal, restore the original position
760
+ // Use visibility hiding only for this case to prevent flicker during position restoration
685
761
  if (state.orientation === 'vertical' && newOrientation === 'horizontal') {
686
762
  if (positionBeforeOrientationChange.current) {
763
+ setIsOrientationChanging(true); // Hide only when restoring position
687
764
  const savedPosition = positionBeforeOrientationChange.current.position;
688
765
  const savedPixelPosition = positionBeforeOrientationChange.current.pixelPosition;
689
766
  setPixelPosition(savedPixelPosition);
@@ -702,7 +779,9 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
702
779
  }
703
780
  }
704
781
  // When switching to vertical, save current position and check if we need to expand upward
782
+ // Use opacity hiding (doesn't affect focus unlike visibility:hidden)
705
783
  if (state.orientation === 'horizontal' && newOrientation === 'vertical') {
784
+ setIsOrientationChanging(true);
706
785
  // Save the current position before changing orientation
707
786
  positionBeforeOrientationChange.current = {
708
787
  position: { ...state.position },
@@ -750,7 +829,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
750
829
  ...s,
751
830
  position: { ...s.position, y: newPercentY },
752
831
  }));
753
- setIsOrientationChanging(false); // Show bar after position is set
832
+ setIsOrientationChanging(false);
754
833
  });
755
834
  });
756
835
  return;
@@ -787,16 +866,70 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
787
866
  }));
788
867
  }
789
868
  }
790
- setIsOrientationChanging(false); // Show bar after position is set
869
+ setIsOrientationChanging(false);
791
870
  });
792
871
  });
793
872
  };
794
873
  const removeItem = (itemId) => {
874
+ isLocalChange.current = true;
795
875
  setState(s => ({
796
876
  ...s,
797
877
  items: s.items.filter(item => item.id !== itemId),
798
878
  }));
799
879
  };
880
+ const getItemRightClickMenuItems = useCallback((item, index) => {
881
+ const hasSeparatorOnRight = index < state.items.length - 1 && state.items[index + 1]?.isSeparator;
882
+ const hasSeparatorOnLeft = index > 0 && state.items[index - 1]?.isSeparator;
883
+ return [
884
+ {
885
+ name: 'Rimuovi',
886
+ icon: _jsx(IconDelete, { fontSize: 16 }),
887
+ onClick: () => removeItem(item.id),
888
+ },
889
+ {
890
+ name: 'Aggiungi separatore a destra',
891
+ icon: _jsx(IconSeparator, { style: { transform: 'rotate(-90deg)' }, fontSize: 16 }),
892
+ disabled: hasSeparatorOnRight,
893
+ onClick: () => {
894
+ isLocalChange.current = true;
895
+ const separator = createSeparator();
896
+ setState(s => {
897
+ const newItems = [...s.items];
898
+ newItems.splice(index + 1, 0, separator);
899
+ return { ...s, items: newItems };
900
+ });
901
+ },
902
+ },
903
+ {
904
+ name: 'Aggiungi separatore a sinistra',
905
+ icon: _jsx(IconSeparator, { fontSize: 16 }),
906
+ disabled: hasSeparatorOnLeft,
907
+ onClick: () => {
908
+ isLocalChange.current = true;
909
+ const separator = createSeparator();
910
+ setState(s => {
911
+ const newItems = [...s.items];
912
+ newItems.splice(index, 0, separator);
913
+ return { ...s, items: newItems };
914
+ });
915
+ },
916
+ },
917
+ ...(!disbaleConfigMode ? [{
918
+ name: SDKUI_Localizator.Configure,
919
+ icon: _jsx(IconSettings, { fontSize: 16 }),
920
+ onClick: () => {
921
+ if (!state.isConfigMode) {
922
+ toggleConfigMode();
923
+ }
924
+ },
925
+ }] : []),
926
+ {
927
+ name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
928
+ icon: _jsx(IconRotate, { fontSize: 16 }),
929
+ onClick: toggleOrientation,
930
+ },
931
+ ];
932
+ }, [state.items, state.isConfigMode, state.orientation, removeItem, createSeparator, toggleConfigMode, toggleOrientation, disbaleConfigMode]);
800
933
  // Drag and drop for reordering
801
934
  const handleDragStart = (e, index) => {
802
935
  if (!state.isConfigMode)
@@ -851,9 +984,10 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
851
984
  setState(s => ({ ...s, draggedItemIndex: null }));
852
985
  setDragOverIndex(null);
853
986
  };
854
- return (_jsxs(_Fragment, { children: [_jsx(S.Overlay, { "$visible": state.isConfigMode }), _jsxs(S.FloatingContainer, { ref: floatingRef, "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": state.isConfigMode, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: (e) => e.preventDefault(), children: [!state.isConfigMode ? (_jsx(ContextMenu, { items: [
987
+ return (_jsxs(_Fragment, { children: [_jsx(S.Overlay, { "$visible": state.isConfigMode }), _jsxs(S.FloatingContainer, { ref: floatingRef, "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": state.isConfigMode, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: state.isConfigMode ? (e) => e.preventDefault() : undefined, children: [!state.isConfigMode ? (_jsx(ContextMenu, { items: [
855
988
  ...(!disbaleConfigMode ? [{
856
989
  name: SDKUI_Localizator.Configure,
990
+ icon: _jsx(IconSettings, { fontSize: 16 }),
857
991
  onClick: () => {
858
992
  if (!state.isConfigMode) {
859
993
  toggleConfigMode();
@@ -862,6 +996,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
862
996
  }] : []),
863
997
  {
864
998
  name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
999
+ icon: _jsx(IconRotate, { fontSize: 16, style: { transform: state.orientation === 'horizontal' ? 'rotate(90deg)' : 'rotate(0deg)' } }),
865
1000
  onClick: toggleOrientation,
866
1001
  },
867
1002
  ], trigger: "right", children: _jsx(S.GripHandle, { "$orientation": state.orientation, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, onDoubleClick: handleGripDoubleClick, children: _jsx(IconDraggableDots, {}) }) })) : (_jsx(S.GripHandle, { "$orientation": state.orientation, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, onDoubleClick: handleGripDoubleClick, children: _jsx(IconDraggableDots, {}) })), _jsx(S.Separator, { "$orientation": state.orientation }), state.items.map((item, index) => {
@@ -876,18 +1011,18 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
876
1011
  ? currentState.disabled === true
877
1012
  : item.disabled === true;
878
1013
  const currentOnClick = currentState.onClick || item.onClick; // Fallback to stored onClick if not found
879
- return (_jsx(S.DraggableItem, { "$isDragging": state.draggedItemIndex === index, "$isDragOver": dragOverIndex === index && state.draggedItemIndex !== index, draggable: state.isConfigMode, onDragStart: (e) => handleDragStart(e, index), onDragEnter: (e) => handleDragEnter(e, index), onDragOver: handleDragOver, onDragLeave: (e) => handleDragLeave(e, index), onDrop: (e) => handleDrop(e, index), onDragEnd: handleDragEnd, children: item.staticItem ? (
880
- // Visualizza l'elemento statico personalizzato
881
- _jsxs(_Fragment, { children: [item.staticItem, state.isConfigMode && (_jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" }))] })) : (
882
- // Visualizza il pulsante standard
883
- _jsxs(_Fragment, { children: [_jsx(TMTooltip, { content: item.name, position: "bottom", children: _jsx(S.MenuButton, { onClick: () => {
884
- if (state.isConfigMode || isDisabled)
885
- return;
886
- if (currentOnClick) {
887
- currentOnClick();
888
- }
889
- }, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) }), state.isConfigMode && (_jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" }))] })) }, item.id));
890
- }), !state.isConfigMode && !disbaleConfigMode && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getContextMenuItemsWithPinIcons(), trigger: "left", keepOpenOnClick: false, children: _jsx(S.ContextMenuButton, { children: _jsx(IconMenuVertical, {}) }) })), state.isConfigMode && state.items.length < maxItems && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getPinContextMenuItems(), trigger: "left", keepOpenOnClick: true, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Add, children: _jsx(S.AddButton, { children: _jsx(IconAdd, {}) }) }) })), state.isConfigMode && (_jsxs(_Fragment, { children: [_jsx(S.Separator, { "$orientation": state.orientation }), _jsxs(S.ButtonGroup, { "$orientation": state.orientation, children: [_jsx(TMTooltip, { content: SDKUI_Localizator.Undo, position: state.orientation === 'horizontal' ? 'right' : 'top', children: _jsx(S.UndoButton, { onClick: handleUndo, disabled: !hasChanges(), children: _jsx(IconUndo, { fontSize: 18 }) }) }), _jsx(TMTooltip, { content: state.items.length === 0
1014
+ return (_jsx(S.DraggableItem, { "$isDragging": state.draggedItemIndex === index, "$isDragOver": dragOverIndex === index && state.draggedItemIndex !== index, draggable: state.isConfigMode, onDragStart: (e) => handleDragStart(e, index), onDragEnter: (e) => handleDragEnter(e, index), onDragOver: handleDragOver, onDragLeave: (e) => handleDragLeave(e, index), onDrop: (e) => handleDrop(e, index), onDragEnd: handleDragEnd, children: state.isConfigMode ? (
1015
+ // Config mode: show remove button, no right-click menu
1016
+ _jsxs(_Fragment, { children: [item.staticItem ? (item.staticItem) : (_jsx(TMTooltip, { content: item.name, position: "bottom", children: _jsx(S.MenuButton, { onClick: () => { }, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) })), _jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" })] })) : (
1017
+ // Normal mode: wrap in right-click context menu
1018
+ _jsx(ContextMenu, { items: getItemRightClickMenuItems(item, index), trigger: "right", children: item.staticItem ? (_jsx("div", { children: item.staticItem })) : (_jsx(TMTooltip, { content: item.name, position: "bottom", children: _jsx(S.MenuButton, { onClick: () => {
1019
+ if (isDisabled)
1020
+ return;
1021
+ if (currentOnClick) {
1022
+ currentOnClick();
1023
+ }
1024
+ }, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) })) })) }, item.id));
1025
+ }), !state.isConfigMode && !disbaleConfigMode && hasContextMenu && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getContextMenuItemsWithPinIcons(), trigger: "left", keepOpenOnClick: false, children: _jsx(S.ContextMenuButton, { children: _jsx(IconMenuVertical, {}) }) })), state.isConfigMode && state.items.length < maxItems && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getPinContextMenuItems(), trigger: "left", keepOpenOnClick: true, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Add, children: _jsx(S.AddButton, { children: _jsx(IconAdd, {}) }) }) })), state.isConfigMode && (_jsxs(_Fragment, { children: [_jsx(S.Separator, { "$orientation": state.orientation }), _jsxs(S.ButtonGroup, { "$orientation": state.orientation, children: [_jsx(TMTooltip, { content: SDKUI_Localizator.Undo, position: state.orientation === 'horizontal' ? 'right' : 'top', children: _jsx(S.UndoButton, { onClick: handleUndo, disabled: !hasChanges(), children: _jsx(IconUndo, { fontSize: 18 }) }) }), _jsx(TMTooltip, { content: state.items.length === 0
891
1026
  ? 'Devi aggiungere almeno un item'
892
1027
  : SDKUI_Localizator.Save, position: state.orientation === 'horizontal' ? 'right' : 'top', children: _jsx(S.ApplyButton, { onClick: toggleConfigMode, disabled: state.items.length === 0 || !hasChanges(), children: _jsx(IconSave, { fontSize: 20 }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Close, position: state.orientation === 'horizontal' ? 'right' : 'top', children: _jsx(S.CloseButton, { onClick: handleClose, children: _jsx(IconCloseOutline, { fontSize: 20 }) }) })] })] }))] })] }));
893
1028
  };
@@ -39,7 +39,8 @@ export const FloatingContainer = styled.div.attrs(props => ({
39
39
  position: ${props => props.$isConstrained ? 'absolute' : 'fixed'};
40
40
  z-index: ${props => props.$isConfigMode ? 9999 : 1500};
41
41
  display: flex;
42
- visibility: ${props => props.$isHidden ? 'hidden' : 'visible'};
42
+ opacity: ${props => props.$isHidden ? 0 : 1};
43
+ pointer-events: ${props => props.$isHidden ? 'none' : 'auto'};
43
44
  flex-direction: ${props => props.$orientation === 'horizontal' ? 'row' : (props.$verticalDirection === 'up' ? 'column-reverse' : 'column')};
44
45
  align-items: center;
45
46
  background: ${props => props.$bgColor || 'linear-gradient(135deg, #0071BC 0%, #1B1464 100%)'};
@@ -51,6 +52,8 @@ export const FloatingContainer = styled.div.attrs(props => ({
51
52
  0 6px 16px rgba(0, 0, 0, 0.2);
52
53
  cursor: ${props => props.$isDragging ? 'grabbing' : 'default'};
53
54
  user-select: none;
55
+ -webkit-touch-callout: none;
56
+ -webkit-user-select: none;
54
57
  animation: ${props => props.$isConfigMode && css `${shake} 0.3s ease-in-out`};
55
58
  transition: none;
56
59
 
@@ -137,7 +140,7 @@ export const ItemSeparator = styled.div `
137
140
  ` : `
138
141
  /* Normal mode: simple line with tight spacing */
139
142
  background: rgba(255, 255, 255, 0.25);
140
- width: ${props.$orientation === 'horizontal' ? '1px' : '100%'};
143
+ width: ${props.$orientation === 'horizontal' ? '1px' : '20px'};
141
144
  height: ${props.$orientation === 'horizontal' ? '20px' : '1px'};
142
145
  margin: ${props.$orientation === 'horizontal' ? '0 2px' : '2px 0'};
143
146
  `}
@@ -21,6 +21,9 @@ export interface TMFloatingMenuBarProps {
21
21
  defaultItems?: TMFloatingMenuItem[];
22
22
  disbaleConfigMode?: boolean;
23
23
  bgColor?: string;
24
+ hasContextMenu?: boolean;
25
+ pinnedItemIds?: string[];
26
+ onPinChange?: (pinnedIds: string[]) => void;
24
27
  }
25
28
  export interface Position {
26
29
  x: number;
@@ -61,6 +61,8 @@ const StyledIconButton = styled.button.withConfig({ shouldForwardProp: prop => !
61
61
  background-color: transparent;
62
62
  color: ${props => !props.disabled ? getColor(props.color) : 'rgb(180, 180, 180)'};
63
63
  user-select: none;
64
+ -webkit-touch-callout: none;
65
+ -webkit-user-select: none;
64
66
  `;
65
67
  const StyledTextButton = styled.button.withConfig({ shouldForwardProp: prop => !['text'].includes(prop) }) `
66
68
  font-size: ${props => props.fontSize};
@@ -79,6 +81,8 @@ const StyledTextButton = styled.button.withConfig({ shouldForwardProp: prop => !
79
81
  background-color: transparent;
80
82
  color: ${props => !props.disabled ? getColor(props.color) : 'rgb(231, 231, 231)'};
81
83
  user-select: none;
84
+ -webkit-touch-callout: none;
85
+ -webkit-user-select: none;
82
86
  transition: ease 200ms;
83
87
  &:hover {
84
88
  background-color: rgb(241, 241, 241);
@@ -118,6 +122,8 @@ const StyledAdvancedButton = styled.button `
118
122
  overflow: hidden;
119
123
  color: ${({ $isPrimaryOutline }) => $isPrimaryOutline ? TMColors.primaryColor : 'white'};
120
124
  user-select: none;
125
+ -webkit-touch-callout: none;
126
+ -webkit-user-select: none;
121
127
  border: ${({ $isPrimaryOutline }) => $isPrimaryOutline ? `1px solid ${TMColors.primaryColor}` : 'none'};
122
128
  background: ${({ $isPrimaryOutline }) => $isPrimaryOutline ? 'white' : 'unset'};
123
129
  `;
@@ -49,6 +49,8 @@ const StyledClosabelIcon = styled.div `
49
49
  justify-content: flex-start;
50
50
  padding: 2px;
51
51
  user-select: none;
52
+ -webkit-touch-callout: none;
53
+ -webkit-user-select: none;
52
54
  `;
53
55
  const StyledClosableItemsCount = styled.div `
54
56
  /* color: ${Colors.primary}; */
@@ -56,6 +58,8 @@ const StyledClosableItemsCount = styled.div `
56
58
  cursor: pointer;
57
59
  margin-right: 5px;
58
60
  user-select: none;
61
+ -webkit-touch-callout: none;
62
+ -webkit-user-select: none;
59
63
  `;
60
64
  const TMClosableList = ({ dataSource, visibility = false, label, inline = false, hasPadding = true }) => {
61
65
  const [status, setStatus] = useState(visibility);
@@ -12,6 +12,8 @@ const StyledContent = styled.div `
12
12
  opacity: ${props => props.$disabled ? 0.4 : 1};
13
13
  pointer-events: ${props => props.$disabled ? 'none' : ''};
14
14
  user-select: none;
15
+ -webkit-touch-callout: none;
16
+ -webkit-user-select: none;
15
17
  display: flex;
16
18
  align-items: center;
17
19
 
@@ -246,7 +246,7 @@ const TMListView = ({ customGroupingHeaders, headerBackGroundColor, header, show
246
246
  onClick: () => { setOrderedBy(key); setShowOrderOptions(false); setOrderMenuVisible(false); },
247
247
  icon: _jsx(IconColumns, { fontSize: 16 }),
248
248
  name: key,
249
- rightIcon: key === orderedBy ? _jsx(IconApply, { fontSize: 14, color: 'gray' }) : undefined
249
+ rightIconProps: key === orderedBy ? { icon: _jsx(IconApply, { fontSize: 14, color: 'gray' }) } : undefined
250
250
  });
251
251
  }
252
252
  }
@@ -65,6 +65,8 @@ const StyledPanelContent = styled.div `
65
65
  justify-content: space-between;
66
66
  position: relative;
67
67
  user-select: none;
68
+ -webkit-touch-callout: none;
69
+ -webkit-user-select: none;
68
70
  outline: none;
69
71
  &:focus {
70
72
  outline: none;
@@ -31,6 +31,8 @@ const StyledExeptionToolbar = styled.div `
31
31
  gap: 5px;
32
32
  padding: 5px;
33
33
  user-select: none;
34
+ -webkit-touch-callout: none;
35
+ -webkit-user-select: none;
34
36
  border-top: 1px solid #f3f3f3;
35
37
  /* background-color: ${TMColors.primary_container}; */
36
38
  background-color: white;
@@ -45,6 +47,8 @@ const StyledMessageToolbar = styled.div `
45
47
  gap: clamp(2px, 1vw, 5px);
46
48
  padding: clamp(3px, 1vw, 5px);
47
49
  user-select: none;
50
+ -webkit-touch-callout: none;
51
+ -webkit-user-select: none;
48
52
  border-top: 1px solid #f3f3f3;
49
53
  background-color: #ffffff;
50
54
  z-index: 1;
@@ -69,6 +73,8 @@ const TabContextContainer = styled.div `
69
73
  border-radius: 5px;
70
74
  overflow: auto;
71
75
  user-select: none;
76
+ -webkit-touch-callout: none;
77
+ -webkit-user-select: none;
72
78
  position: relative;
73
79
  `;
74
80
  const StyledAppVersionText = styled.p `
@@ -42,6 +42,8 @@ const StyledSearchCardContent = styled.div `
42
42
 
43
43
  position: relative;
44
44
  user-select: none;
45
+ -webkit-touch-callout: none;
46
+ -webkit-user-select: none;
45
47
  `;
46
48
  const TMToolbarCard = ({ items = [], onItemClick, selectedItem, showPanel, color = TMColors.colorHeader, backgroundColor = TMColors.backgroundColorHeader, backgroundColorContainer, children, showHeader = true, title, totalItems, displayedItemsCount, toolbar, padding = '3px', onBack, onClose, onHeaderDoubleClick }) => {
47
49
  return (_jsxs(StyledSearchCard, { children: [showHeader && _jsx(StyledSearchCardHeader, { "$backgroundColor": backgroundColor, "$color": color, onDoubleClick: () => { if (onHeaderDoubleClick)
@@ -16,6 +16,8 @@ const StyledCheckBoxLabel = styled.div `
16
16
  color: ${props => !props.$disabled ? props.$isModifiedWhen ? TMColors.isModified : props.$labelColor ? props.$labelColor : TMColors.text_normal : TMColors.disabled};
17
17
  font-size: ${props => props.$fontSize};
18
18
  user-select: none;
19
+ -webkit-touch-callout: none;
20
+ -webkit-user-select: none;
19
21
  &:focus{
20
22
  outline: none;
21
23
  background-image: linear-gradient(white, #E4E9F7);
@@ -48,6 +48,8 @@ export const StyledEditorIcon = styled.div `
48
48
  margin-right: 2px;
49
49
  font-size: 18px;
50
50
  user-select: none;
51
+ -webkit-touch-callout: none;
52
+ -webkit-user-select: none;
51
53
  transform: translateY(18px);
52
54
  `;
53
55
  export const StyledEditorLabel = styled.div `
@@ -55,6 +57,8 @@ export const StyledEditorLabel = styled.div `
55
57
  color:${props => !props.$disabled ? props.$isFocused ? TMColors.primary : props.$color ? props.$color : TMColors.label_normal : TMColors.disabled};
56
58
  padding: 0 3px;
57
59
  user-select: none;
60
+ -webkit-touch-callout: none;
61
+ -webkit-user-select: none;
58
62
  transform: ${props => props.$labelPosition === 'left' && 'translateY(5px)'};
59
63
  width: max-content;
60
64
  transform: translate(10px, 6px);
@@ -39,6 +39,8 @@ const StyledradioButtonLabel = styled.label `
39
39
  font-size: ${props => props.$fontSize};
40
40
  color: ${props => !props.$disabled ? props.$isModifiedWhen ? TMColors.isModified : TMColors.text_normal : TMColors.disabled};
41
41
  user-select: none;
42
+ -webkit-touch-callout: none;
43
+ -webkit-user-select: none;
42
44
  width: 100%;
43
45
  cursor: ${props => !props.$disabled && 'pointer'};
44
46
  &:focus{
@@ -67,6 +67,8 @@ const TMToppyButton = styled.div.attrs((props) => ({
67
67
  padding: 0;
68
68
  outline: none;
69
69
  user-select: none;
70
+ -webkit-touch-callout: none;
71
+ -webkit-user-select: none;
70
72
  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
73
 
72
74
  img {
@@ -87,6 +87,6 @@ const TMDcmtBlog = ({ tid, did, isVisible, fetchBlogDataTrigger, allTasks = [],
87
87
  }, 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 })] }));
88
88
  };
89
89
  export default TMDcmtBlog;
90
- const StyledContainer = styled.div ` user-select: none; overflow: hidden; background-color: #ffffff; width: calc(100%); height: calc(100%); display: flex; gap: 10px; `;
90
+ const StyledContainer = styled.div ` user-select: none; -webkit-touch-callout: none; -webkit-user-select: none; overflow: hidden; background-color: #ffffff; width: calc(100%); height: calc(100%); display: flex; gap: 10px; `;
91
91
  const StyledSectionContainer = styled.div ` width: 100%; height: 100%; display:flex; flex-direction: column; `;
92
92
  const StyledBoardContainer = styled.div `width: 100%; height: 100%;`;
@@ -341,32 +341,28 @@ const ImageViewer = ({ fileBlob, alt = 'Image', className }) => {
341
341
  const doc = iframe.contentWindow?.document;
342
342
  if (!doc)
343
343
  return;
344
- doc.open();
345
- doc.write(`
346
- <html>
347
- <head>
348
- <title>Print Image</title>
349
- <style>
350
- body, html {
351
- margin: 0;
352
- padding: 0;
353
- height: 100%;
354
- display: flex;
355
- justify-content: center;
356
- align-items: center;
357
- }
358
- img {
359
- max-width: 100%;
360
- max-height: 100%;
361
- }
362
- </style>
363
- </head>
364
- <body>
365
- <img src="${dataUrl}" onload="setTimeout(() => { window.print(); window.close(); }, 100);" />
366
- </body>
367
- </html>
368
- `);
369
- doc.close();
344
+ doc.documentElement.innerHTML = `
345
+ <head>
346
+ <title>Print Image</title>
347
+ <style>
348
+ body, html {
349
+ margin: 0;
350
+ padding: 0;
351
+ height: 100%;
352
+ display: flex;
353
+ justify-content: center;
354
+ align-items: center;
355
+ }
356
+ img {
357
+ max-width: 100%;
358
+ max-height: 100%;
359
+ }
360
+ </style>
361
+ </head>
362
+ <body>
363
+ <img src="${dataUrl}" onload="setTimeout(() => { window.print(); window.close(); }, 100);" />
364
+ </body>
365
+ `;
370
366
  iframe.contentWindow?.addEventListener('afterprint', () => {
371
367
  document.body.removeChild(iframe);
372
368
  });
@@ -405,7 +401,7 @@ const StyledPreviewContainer = styled.div `display: flex; justify-content: cente
405
401
  export const StyledHeaderIcon = styled.div ` color: ${props => props.$color}; cursor: pointer; display: flex; align-items: center; justify-content: center; &:hover{ color: white ; transition: 200ms ease; } `;
406
402
  export const StyledPanelStatusContainer = styled.div ` width: 100%; height: 100%; padding: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; `;
407
403
  const StyledPreviewNotAvailable = styled.div ` display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; `;
408
- const ImageContainer = styled.div ` width: calc(100% - 20px); height: 100%; left:10px; position: absolute; top:50px; overflow: hidden; background: #f5f5f5; touch-action: none; user-select: none; cursor: ${({ $cursor }) => $cursor ?? 'default'}; `;
404
+ const ImageContainer = styled.div ` width: calc(100% - 20px); height: 100%; left:10px; position: absolute; top:50px; overflow: hidden; background: #f5f5f5; touch-action: none; user-select: none; -webkit-touch-callout: none; -webkit-user-select: none; cursor: ${({ $cursor }) => $cursor ?? 'default'}; `;
409
405
  const TopToolbar = styled.div ` position: absolute; top: 0; width: 100%; height: 40px; background-color: #e4e4e4; display: flex; justify-content: center; align-items: center; z-index: 10; `;
410
406
  const ToolbarCenter = styled.div ` display: flex; align-items: center; gap: 12px;`;
411
407
  const ToolbarIconButton = styled.button ` background: #f0f0f0; border: none; color: #313131; font-size: 18px; cursor: pointer; display: flex; align-items: center; justify-content: center; height: 32px; width: 32px; padding: 0; border-radius: 50px; &:hover{ background-color: #c4c4c4; } &:disabled { color: #c2c2c2; cursor: not-allowed; } `;
@@ -427,6 +427,8 @@ export const StyledToppyText = styled.p `
427
427
  color: #2559A5;
428
428
  font-size: 1rem;
429
429
  user-select: none;
430
+ -webkit-touch-callout: none;
431
+ -webkit-user-select: none;
430
432
  margin: 0;
431
433
  display: -webkit-box;
432
434
  -webkit-box-orient: vertical;
@@ -44,6 +44,7 @@ import { getDcmtCicoStatus } from '../../../helper/checkinCheckoutManager';
44
44
  import TMViewHistoryDcmt from './TMViewHistoryDcmt';
45
45
  import TMBlogCommentForm from '../blog/TMBlogCommentForm';
46
46
  import { useCheckInOutOperations } from '../../../hooks/useCheckInOutOperations';
47
+ import { useFloatingBarPinnedItems } from '../../../hooks/useFloatingBarPinnedItems';
47
48
  import TMDcmtCheckoutInfoForm from './TMDcmtCheckoutInfoForm';
48
49
  import { useDataListItem } from '../../../hooks/useDataListItem';
49
50
  import { useDataUserIdItem } from '../../../hooks/useDataUserIdItem';
@@ -107,6 +108,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
107
108
  const [customButton, setCustomButton] = useState();
108
109
  const deviceType = useDeviceType();
109
110
  const isMobile = deviceType === DeviceType.MOBILE;
111
+ const { pinnedItemIds, togglePin, setPinnedItemIds } = useFloatingBarPinnedItems();
110
112
  const selectedDocs = getSelectedDcmtsOrFocused(selectedItems, focusedItem);
111
113
  const allFieldSelectedDocs = useMemo(() => getAllFieldSelectedDcmtsOrFocused(selectedItems, focusedItem), [selectedItems, focusedItem]);
112
114
  // Disable the "Sign/Approve" button if:
@@ -594,14 +596,14 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
594
596
  && !showTodoDcmtForm);
595
597
  }, [showToppyForApprove, showToppyDraggableHelpCenter, selectedDocs, showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, editPdfForm, openS4TViewer, showTodoDcmtForm]);
596
598
  const floatingMenuItems = useMemo(() => {
597
- const baseMenuItems = getCommandsMenuItems(isMobile, fromDTD, allUsers, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, showSearch, setShowFloatingBar, openFormHandler, openSharedArchiveHandler, showSharedDcmtsHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, openExportForm, handleToggleSearch, handleSignApprove, openSignSettingsForm, handleCheckOutOperationCallback, handleCheckInOperationCallback, showCheckoutInformationFormCallback, showHistoryCallback, copyCheckoutPathToClipboardOperationCallback, openWGsCopyMoveForm, openCommentFormCallback, openEditPdf, openAddDocumentForm, passToArchiveCallback, archiveMasterDocuments, archiveDetailDocuments, currentTIDHasMasterRelations, currentTIDHasDetailRelations, canArchiveMasterRelation, canArchiveDetailRelation, pairManyToMany, hasManyToManyRelation);
599
+ const baseMenuItems = getCommandsMenuItems(isMobile, fromDTD, allUsers, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, showSearch, setShowFloatingBar, openFormHandler, openSharedArchiveHandler, showSharedDcmtsHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, openExportForm, handleToggleSearch, handleSignApprove, openSignSettingsForm, handleCheckOutOperationCallback, handleCheckInOperationCallback, showCheckoutInformationFormCallback, showHistoryCallback, copyCheckoutPathToClipboardOperationCallback, openWGsCopyMoveForm, openCommentFormCallback, openEditPdf, openAddDocumentForm, passToArchiveCallback, archiveMasterDocuments, archiveDetailDocuments, currentTIDHasMasterRelations, currentTIDHasDetailRelations, canArchiveMasterRelation, canArchiveDetailRelation, pairManyToMany, hasManyToManyRelation, pinnedItemIds, togglePin);
598
600
  const customButtons = customButtonMenuItems();
599
601
  return customButtons.name ? baseMenuItems.concat([customButtons]) : baseMenuItems;
600
602
  }, [
601
603
  isMobile, fromDTD, allUsers, selectedItems, focusedItem, context,
602
604
  showFloatingBar, workingGroupContext, showSearch, currentTIDHasMasterRelations,
603
605
  currentTIDHasDetailRelations, canArchiveMasterRelation, canArchiveDetailRelation,
604
- hasManyToManyRelation, customButtonsLayout
606
+ hasManyToManyRelation, customButtonsLayout, pinnedItemIds, togglePin
605
607
  ]);
606
608
  const searchResutlToolbar = _jsxs(_Fragment, { children: [(dcmtsReturned != dcmtsFound) && _jsx("p", { style: { textAlign: 'center', padding: '1px 4px', borderRadius: '3px', display: 'flex' }, children: `${dcmtsReturned}/${dcmtsFound} restituiti` }), context === SearchResultContext.FAVORITES_AND_RECENTS &&
607
609
  _jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: onRefreshSearchAsync }), _jsx(TMContextMenu, { items: floatingMenuItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) })] });
@@ -612,7 +614,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
612
614
  _jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile || currentSearchResults.length === 1, onSelectionChanged: onSearchResultSelectionChanged }) })
613
615
  :
614
616
  _jsx(_Fragment, {}), _jsx(TMLayoutItem, { children: _jsx(TMSearchResultGrid, { showSearch: showSearch, fromDTD: fromDTD, allUsers: allUsers, inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], lastUpdateSearchTime: lastUpdateSearchTime, openInOffice: openInOffice, onDblClick: () => openFormHandler(LayoutModes.Update), floatingMenuItems: floatingMenuItems, onSelectionChanged: (items) => { setSelectedItems(items); }, onVisibleItemChanged: setVisibleItems, onFocusedItemChanged: setFocusedItem, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync(inputDcmts, downloadType, downloadMode, onFileOpened, confirmAttachments), showExportForm: showExportForm, onCloseExportForm: onCloseExportForm }) })] }), allowFloatingBar && showFloatingBar && deviceType !== DeviceType.MOBILE &&
615
- _jsx(TMFloatingMenuBar, { containerRef: floatingBarContainerRef, contextMenuItems: floatingMenuItems, isConstrained: true, defaultPosition: { x: 1, y: 88 }, contextMenuDefaultPinnedIds: ['rel-det', 'rel-mst', 'dl'], defaultOrientation: 'horizontal' })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { TID: focusedItem?.TID, DID: focusedItem?.DID, deviceType: deviceType, onCompleted: onWFOperationCompleted, onClose: () => setShowMoreInfoPopup(false) }), isOpenBatchUpdate && _jsx(TMBatchUpdateForm, { isModal: true, titleModal: `${SDKUI_Localizator.BatchUpdate} (${getSelectionDcmtInfo().length} documenti selezionati)`, inputDcmts: getSelectionDcmtInfo(), TID: focusedItem ? focusedItem?.TID : selectedItems[0]?.TID, DID: focusedItem ? focusedItem?.DID : selectedItems[0]?.DID, onBack: () => {
617
+ _jsx(TMFloatingMenuBar, { containerRef: floatingBarContainerRef, contextMenuItems: floatingMenuItems, isConstrained: true, defaultPosition: { x: 1, y: 88 }, contextMenuDefaultPinnedIds: ['rel-det', 'rel-mst', 'dl'], defaultOrientation: 'horizontal', hasContextMenu: false, pinnedItemIds: pinnedItemIds, onPinChange: setPinnedItemIds })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { TID: focusedItem?.TID, DID: focusedItem?.DID, deviceType: deviceType, onCompleted: onWFOperationCompleted, onClose: () => setShowMoreInfoPopup(false) }), isOpenBatchUpdate && _jsx(TMBatchUpdateForm, { isModal: true, titleModal: `${SDKUI_Localizator.BatchUpdate} (${getSelectionDcmtInfo().length} documenti selezionati)`, inputDcmts: getSelectionDcmtInfo(), TID: focusedItem ? focusedItem?.TID : selectedItems[0]?.TID, DID: focusedItem ? focusedItem?.DID : selectedItems[0]?.DID, onBack: () => {
616
618
  setIsOpenBatchUpdate(false);
617
619
  }, onSavedCallbackAsync: async () => {
618
620
  setIsOpenBatchUpdate(false);
@@ -8,4 +8,4 @@ export declare const signatureInformationCallback: (isMobile: boolean, inputDcmt
8
8
  export declare const getCommandsMenuItems: (isMobile: boolean, dtd: DcmtTypeDescriptor | undefined, allUsers: Array<UserDescriptor>, selectedItems: Array<any>, focusedItem: any, context: SearchResultContext, showFloatingBar: boolean, workingGroupContext: WorkingGroupDescriptor | undefined, showSearch: boolean, setShowFloatingBar: React.Dispatch<React.SetStateAction<boolean>>, openFormHandler: (layoutMode: LayoutModes) => void, openSharedArchiveHandler: () => Promise<void>, showSharedDcmtsHandler: () => Promise<void>, downloadDcmtsAsync: (inputDcmts: DcmtInfo[] | undefined, downloadType: DownloadTypes, downloadMode: DownloadModes, onFileDownloaded?: (dcmtFile: File | undefined) => void, confirmAttachments?: (list: FileDescriptor[]) => Promise<string[] | undefined>, skipConfirmation?: boolean) => Promise<void>, runOperationAsync: (inputDcmts: DcmtInfo[] | undefined, dcmtOperationType: DcmtOperationTypes, actionAfterOperationAsync?: () => Promise<void>) => Promise<void>, onRefreshSearchAsync: (() => Promise<void>) | undefined, onRefreshDataRowsAsync: (() => Promise<void>) | undefined, onRefreshAfterAddDcmtToFavs: (() => void) | undefined, confirmFormat: () => Promise<FileFormats>, confirmAttachments: (list: FileDescriptor[]) => Promise<string[] | undefined>, openTaskFormHandler: () => void, openDetailDcmtsFormHandler: (value: boolean) => void, openMasterDcmtsFormHandler: (value: boolean) => void, openBatchUpdateFormHandler: (value: boolean) => void, openExportForm: () => void, handleToggleSearch: () => void, handleSignApprove: () => void, openSignSettingsForm: () => void, handleCheckOutOperationCallback: (checkout: boolean) => Promise<void>, handleCheckInOperationCallback: () => void, showCheckoutInformationFormCallback: () => void, viewHistoryCallback: () => void, copyCheckoutPathToClipboardOperationCallback: () => void, openWGsCopyMoveForm?: ((mode: "copyToWgDraft" | "copyToWgArchivedDoc", dcmtTypeDescriptor: DcmtTypeDescriptor, documents: Array<DcmtInfo>) => void), openCommentFormCallback?: ((documents: Array<DcmtInfo>) => void), openEditPdf?: ((documents: Array<DcmtInfo>) => void), openAddDocumentForm?: () => void, passToArchiveCallback?: (outputMids: Array<{
9
9
  mid: number;
10
10
  value: string;
11
- }>, tid?: number) => void, archiveMasterDocuments?: (tid: number | undefined) => Promise<void>, archiveDetailDocuments?: (tid: number | undefined) => Promise<void>, hasMasterRelation?: boolean, hasDetailRelation?: boolean, canArchiveMasterRelation?: boolean, canArchiveDetailRelation?: boolean, pairManyToManyDocuments?: (isPairing: boolean) => Promise<void>, hasManyToManyRelation?: boolean) => Array<TMContextMenuItemProps>;
11
+ }>, tid?: number) => void, archiveMasterDocuments?: (tid: number | undefined) => Promise<void>, archiveDetailDocuments?: (tid: number | undefined) => Promise<void>, hasMasterRelation?: boolean, hasDetailRelation?: boolean, canArchiveMasterRelation?: boolean, canArchiveDetailRelation?: boolean, pairManyToManyDocuments?: (isPairing: boolean) => Promise<void>, hasManyToManyRelation?: boolean, pinnedItemIds?: string[], onTogglePin?: (id: string) => void) => Array<TMContextMenuItemProps>;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { AccessLevels, AccessLevelsEx, AppModules, FileFormats, LayoutModes, SDK_Globals, DcmtTypeListCacheService, LicenseModuleStatus } from '@topconsultnpm/sdk-ts';
3
- import { IconActivity, IconArchiveDoc, IconBatchUpdate, IconCheckFile, IconCheckIn, IconCircleInfo, IconCloseCircle, IconConvertFilePdf, IconDelete, IconDotsVerticalCircleOutline, IconDownload, IconEdit, IconExportTo, IconFileDots, IconHide, IconInfo, IconMenuCAArchive, IconPlatform, IconPreview, IconRelation, IconSearch, IconShow, IconStar, IconSubstFile, IconUndo, IconUserGroupOutline, SDKUI_Localizator, searchResultToMetadataValues, IconSignaturePencil, IconArchiveMaster, IconArchiveDetail, IconDetailDcmts, isPdfEditorEnabled, IconPair, IconUnpair, IconSharedDcmt, IconShare, IconCopy, IconMoveToFolder } from '../../../helper';
3
+ import { IconActivity, IconArchiveDoc, IconBatchUpdate, IconCheckFile, IconCheckIn, IconCircleInfo, IconCloseCircle, IconConvertFilePdf, IconDelete, IconDotsVerticalCircleOutline, IconDownload, IconEdit, IconExportTo, IconFileDots, IconHide, IconInfo, IconMenuCAArchive, IconPlatform, IconPreview, IconRelation, IconSearch, IconShow, IconStar, IconSubstFile, IconUndo, IconUserGroupOutline, SDKUI_Localizator, searchResultToMetadataValues, IconSignaturePencil, IconArchiveMaster, IconArchiveDetail, IconDetailDcmts, isPdfEditorEnabled, IconPair, IconUnpair, IconSharedDcmt, IconShare, IconCopy, IconMoveToFolder, IconPin } from '../../../helper';
4
4
  import ShowAlert from '../../base/TMAlert';
5
5
  import { TMMessageBoxManager, ButtonNames, TMExceptionBoxManager } from '../../base/TMPopUp';
6
6
  import TMSpinner from '../../base/TMSpinner';
@@ -80,8 +80,28 @@ export const signatureInformationCallback = async (isMobile, inputDcmts) => {
80
80
  TMSpinner.hide();
81
81
  }
82
82
  };
83
- export const getCommandsMenuItems = (isMobile, dtd, allUsers, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, showSearch, setShowFloatingBar, openFormHandler, openSharedArchiveHandler, showSharedDcmtsHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, onRefreshDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, confirmAttachments, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, openExportForm, handleToggleSearch, handleSignApprove, openSignSettingsForm, handleCheckOutOperationCallback, handleCheckInOperationCallback, showCheckoutInformationFormCallback, viewHistoryCallback, copyCheckoutPathToClipboardOperationCallback, openWGsCopyMoveForm, openCommentFormCallback, openEditPdf, openAddDocumentForm, passToArchiveCallback, archiveMasterDocuments, archiveDetailDocuments, hasMasterRelation, hasDetailRelation, canArchiveMasterRelation, canArchiveDetailRelation, pairManyToManyDocuments, hasManyToManyRelation) => {
83
+ export const getCommandsMenuItems = (isMobile, dtd, allUsers, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, showSearch, setShowFloatingBar, openFormHandler, openSharedArchiveHandler, showSharedDcmtsHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, onRefreshDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, confirmAttachments, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, openExportForm, handleToggleSearch, handleSignApprove, openSignSettingsForm, handleCheckOutOperationCallback, handleCheckInOperationCallback, showCheckoutInformationFormCallback, viewHistoryCallback, copyCheckoutPathToClipboardOperationCallback, openWGsCopyMoveForm, openCommentFormCallback, openEditPdf, openAddDocumentForm, passToArchiveCallback, archiveMasterDocuments, archiveDetailDocuments, hasMasterRelation, hasDetailRelation, canArchiveMasterRelation, canArchiveDetailRelation, pairManyToManyDocuments, hasManyToManyRelation, pinnedItemIds, onTogglePin) => {
84
84
  const isPdfEditorLicensed = SDK_Globals?.license?.dcmtArchiveLicenses?.[0]?.siX_60007?.status === LicenseModuleStatus.Licensed;
85
+ const addPinIconToItems = (items) => {
86
+ if (isMobile || !onTogglePin)
87
+ return items;
88
+ return items.map(item => {
89
+ const newItem = { ...item };
90
+ if (item.id && item.onClick && !item.submenu) {
91
+ newItem.rightIconProps = {
92
+ icon: _jsx(IconPin, {}),
93
+ activeColor: 'red',
94
+ inactiveColor: 'black',
95
+ isActive: pinnedItemIds?.includes(item.id) ?? false,
96
+ onClick: () => onTogglePin(item.id),
97
+ };
98
+ }
99
+ if (item.submenu && item.submenu.length > 0) {
100
+ newItem.submenu = addPinIconToItems(item.submenu);
101
+ }
102
+ return newItem;
103
+ });
104
+ };
85
105
  let pdfEditorAvailable = false;
86
106
  if (dtd && dtd.widgets && dtd.widgets.length > 0) {
87
107
  pdfEditorAvailable = isPdfEditorEnabled(dtd.widgets);
@@ -744,7 +764,7 @@ export const getCommandsMenuItems = (isMobile, dtd, allUsers, selectedItems, foc
744
764
  ];
745
765
  };
746
766
  if (context === SearchResultContext.ARCHIVED_WORKGROUP) {
747
- return getArchivedWorkgroupMenuItems();
767
+ return addPinIconToItems(getArchivedWorkgroupMenuItems());
748
768
  }
749
- return getDefaultMenuItems();
769
+ return addPinIconToItems(getDefaultMenuItems());
750
770
  };
@@ -70,6 +70,8 @@ const StyledDiagramItem = styled.g `
70
70
  fill: #333;
71
71
  text-anchor: middle;
72
72
  user-select: none;
73
+ -webkit-touch-callout: none;
74
+ -webkit-user-select: none;
73
75
  pointer-events: none;
74
76
  }
75
77
  `;
@@ -14,6 +14,8 @@ const StyledValidationItemsTabs = styled.div `
14
14
  border-bottom: 1px solid $primary;
15
15
  width: 100%;
16
16
  user-select: none;
17
+ -webkit-touch-callout: none;
18
+ -webkit-user-select: none;
17
19
  background-color: ${TMColors.primary};
18
20
  z-index: 20;
19
21
  border-top: 1px solid #122C4C;
@@ -44,12 +46,16 @@ const StyledMessageIcon = styled.div `
44
46
  const StyledMessageResultType = styled.div `
45
47
  width: 50px;
46
48
  user-select: none;
49
+ -webkit-touch-callout: none;
50
+ -webkit-user-select: none;
47
51
  margin-right: 20px;
48
52
  `;
49
53
  const StyledMessageElement = styled.div `
50
54
  width: 150px;
51
55
  text-align: left;
52
56
  user-select: none;
57
+ -webkit-touch-callout: none;
58
+ -webkit-user-select: none;
53
59
  margin-right: 20px;
54
60
  `;
55
61
  const StyledMessageText = styled.div `
@@ -985,6 +985,8 @@ const StyledContent = styled.div `
985
985
  opacity: ${props => props.$disabled ? 0.4 : 1};
986
986
  pointer-events: ${props => props.$disabled ? 'none' : ''};
987
987
  user-select: none;
988
+ -webkit-touch-callout: none;
989
+ -webkit-user-select: none;
988
990
  display: flex;
989
991
  align-items: center;
990
992
 
@@ -400,6 +400,8 @@ const AppMenuButton = styled.div `
400
400
  cursor: pointer;
401
401
  box-shadow: 0 2px 8px #2459a41a;
402
402
  user-select: none;
403
+ -webkit-touch-callout: none;
404
+ -webkit-user-select: none;
403
405
  gap: 5px;
404
406
  transition:
405
407
  transform 0.18s cubic-bezier(.4,0,.2,1),
@@ -445,6 +447,8 @@ const UserAvatar = styled.div `
445
447
  font-weight: 600;
446
448
  letter-spacing: 0.5px;
447
449
  user-select: none;
450
+ -webkit-touch-callout: none;
451
+ -webkit-user-select: none;
448
452
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
449
453
  `;
450
454
  const UserAvatarLarge = styled(UserAvatar) `
@@ -21,6 +21,8 @@ const StyledTMSidebarItemContainer = styled.div `
21
21
  position: relative;
22
22
  transition: background-color 200ms ease;
23
23
  user-select: none;
24
+ -webkit-touch-callout: none;
25
+ -webkit-user-select: none;
24
26
  background-color: ${props => props.$isSelected ? 'rgba(255,255,255,0.35)' : 'transparent'};
25
27
 
26
28
  p {
@@ -19,6 +19,8 @@ const StyledToppyText = styled.div `
19
19
  color: #2559A5;
20
20
  font-size: 1rem;
21
21
  user-select: none;
22
+ -webkit-touch-callout: none;
23
+ -webkit-user-select: none;
22
24
  margin: 0;
23
25
  display: -webkit-box;
24
26
  -webkit-box-orient: vertical;
@@ -35,6 +37,8 @@ const StyledToppyImage = styled.img `
35
37
  height: auto;
36
38
  display: block;
37
39
  user-select: none;
40
+ -webkit-touch-callout: none;
41
+ -webkit-user-select: none;
38
42
  `;
39
43
  const TMToppyMessage = (props) => {
40
44
  const { message, titleTooltip, maxWidth } = props;
@@ -67,7 +67,8 @@ export declare enum LandingPages {
67
67
  RECENT = "recent",
68
68
  AREA_MANAGER = "areaManager",
69
69
  NOTIFICATIONS = "notifications",
70
- ACTIVITIES = "activities"
70
+ ACTIVITIES = "activities",
71
+ WORKFLOW_CTRL = "workflowCtrl"
71
72
  }
72
73
  export declare function getExceptionMessage(ex: any): string;
73
74
  export declare function getContrastColor(inputColor: string): {
@@ -728,6 +728,7 @@ export var LandingPages;
728
728
  LandingPages["AREA_MANAGER"] = "areaManager";
729
729
  LandingPages["NOTIFICATIONS"] = "notifications";
730
730
  LandingPages["ACTIVITIES"] = "activities";
731
+ LandingPages["WORKFLOW_CTRL"] = "workflowCtrl";
731
732
  })(LandingPages || (LandingPages = {}));
732
733
  // #endregion
733
734
  // #region Exception
@@ -0,0 +1,11 @@
1
+ interface UseFloatingBarPinnedItemsOptions {
2
+ defaultPinnedIds?: string[];
3
+ }
4
+ interface UseFloatingBarPinnedItemsReturn {
5
+ pinnedItemIds: string[];
6
+ isPinned: (id: string) => boolean;
7
+ togglePin: (id: string) => void;
8
+ setPinnedItemIds: (ids: string[]) => void;
9
+ }
10
+ export declare const useFloatingBarPinnedItems: (options?: UseFloatingBarPinnedItemsOptions) => UseFloatingBarPinnedItemsReturn;
11
+ export default useFloatingBarPinnedItems;
@@ -0,0 +1,54 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { SDKUI_Globals } from '../helper';
3
+ export const useFloatingBarPinnedItems = (options = {}) => {
4
+ const { defaultPinnedIds = [] } = options;
5
+ const loadPinnedIds = useCallback(() => {
6
+ try {
7
+ const settings = SDKUI_Globals.userSettings?.searchSettings?.floatingMenuBar || {};
8
+ if (settings.itemIds && Array.isArray(settings.itemIds) && settings.itemIds.length > 0) {
9
+ return settings.itemIds;
10
+ }
11
+ return defaultPinnedIds;
12
+ }
13
+ catch (error) {
14
+ console.error('Failed to load pinned items from localStorage:', error);
15
+ return defaultPinnedIds;
16
+ }
17
+ }, [defaultPinnedIds]);
18
+ const [internalPinnedItemIds, setInternalPinnedItemIds] = useState(loadPinnedIds);
19
+ const savePinnedIds = useCallback((ids) => {
20
+ try {
21
+ const currentSettings = SDKUI_Globals.userSettings?.searchSettings?.floatingMenuBar || {};
22
+ SDKUI_Globals.userSettings.searchSettings.floatingMenuBar = {
23
+ ...currentSettings,
24
+ itemIds: ids,
25
+ };
26
+ }
27
+ catch (error) {
28
+ console.error('Failed to save pinned items to localStorage:', error);
29
+ }
30
+ }, []);
31
+ const setPinnedItemIds = useCallback((ids) => {
32
+ setInternalPinnedItemIds(ids);
33
+ savePinnedIds(ids);
34
+ }, [savePinnedIds]);
35
+ const isPinned = useCallback((id) => {
36
+ return internalPinnedItemIds.includes(id);
37
+ }, [internalPinnedItemIds]);
38
+ const togglePin = useCallback((id) => {
39
+ setInternalPinnedItemIds(prev => {
40
+ const newIds = prev.includes(id)
41
+ ? prev.filter(pinnedId => pinnedId !== id)
42
+ : [...prev, id];
43
+ savePinnedIds(newIds);
44
+ return newIds;
45
+ });
46
+ }, [savePinnedIds]);
47
+ return {
48
+ pinnedItemIds: internalPinnedItemIds,
49
+ isPinned,
50
+ togglePin,
51
+ setPinnedItemIds,
52
+ };
53
+ };
54
+ export default useFloatingBarPinnedItems;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.20.0-dev1.112",
3
+ "version": "6.20.0-dev1.114",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",