@topconsultnpm/sdkui-react 6.20.0-dev1.113 → 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.
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +42 -7
- package/lib/components/NewComponents/ContextMenu/types.d.ts +8 -2
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +159 -24
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +3 -2
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +3 -0
- package/lib/components/base/TMListView.js +1 -1
- package/lib/components/features/documents/TMDcmtPreview.js +22 -26
- package/lib/components/features/search/TMSearchResult.js +5 -3
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +1 -1
- package/lib/components/features/search/TMSearchResultsMenuItems.js +24 -4
- package/lib/helper/helpers.d.ts +2 -1
- package/lib/helper/helpers.js +1 -0
- package/lib/hooks/useFloatingBarPinnedItems.d.ts +11 -0
- package/lib/hooks/useFloatingBarPinnedItems.js +54 -0
- 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
|
-
|
|
251
|
-
//
|
|
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
|
|
436
|
+
const handleRightIconPropsClick = (e) => {
|
|
437
|
+
e.preventDefault();
|
|
425
438
|
e.stopPropagation();
|
|
426
|
-
|
|
427
|
-
item.
|
|
428
|
-
|
|
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
|
-
|
|
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) || '';
|
|
@@ -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
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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:
|
|
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
|
-
|
|
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);
|
|
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);
|
|
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:
|
|
880
|
-
//
|
|
881
|
-
_jsxs(_Fragment, { children: [item.staticItem,
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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
|
-
|
|
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%)'};
|
|
@@ -139,7 +140,7 @@ export const ItemSeparator = styled.div `
|
|
|
139
140
|
` : `
|
|
140
141
|
/* Normal mode: simple line with tight spacing */
|
|
141
142
|
background: rgba(255, 255, 255, 0.25);
|
|
142
|
-
width: ${props.$orientation === 'horizontal' ? '1px' : '
|
|
143
|
+
width: ${props.$orientation === 'horizontal' ? '1px' : '20px'};
|
|
143
144
|
height: ${props.$orientation === 'horizontal' ? '20px' : '1px'};
|
|
144
145
|
margin: ${props.$orientation === 'horizontal' ? '0 2px' : '2px 0'};
|
|
145
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;
|
|
@@ -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
|
-
|
|
249
|
+
rightIconProps: key === orderedBy ? { icon: _jsx(IconApply, { fontSize: 14, color: 'gray' }) } : undefined
|
|
250
250
|
});
|
|
251
251
|
}
|
|
252
252
|
}
|
|
@@ -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.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
<
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
});
|
|
@@ -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
|
};
|
package/lib/helper/helpers.d.ts
CHANGED
|
@@ -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): {
|
package/lib/helper/helpers.js
CHANGED
|
@@ -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;
|