@topconsultnpm/sdkui-react 6.20.0-dev1.120 → 6.20.0-dev1.121
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.
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
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 {
|
|
8
|
-
import { ButtonNames, TMMessageBoxManager } from '../../base/TMPopUp';
|
|
7
|
+
import { IconDelete, IconMenuVertical, IconPin, IconRotate, IconSeparator, SDKUI_Globals } from '../../../helper';
|
|
9
8
|
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
9
|
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 = [],
|
|
10
|
+
const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 1, y: 90 }, defaultOrientation = 'horizontal', maxItems = 100, contextMenuDefaultPinnedIds = [], defaultItems = [], enableConfigMode = true, bgColor = undefined, hasContextMenu = true, pinnedItemIds: externalPinnedItemIds, onPinChange, }) => {
|
|
12
11
|
const percentToPixels = (percent, containerSize) => {
|
|
13
12
|
return (percent / 100) * containerSize;
|
|
14
13
|
};
|
|
@@ -42,7 +41,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
42
41
|
};
|
|
43
42
|
const loadConfig = () => {
|
|
44
43
|
// If config mode is disabled, use default position and orientation
|
|
45
|
-
if (
|
|
44
|
+
if (!enableConfigMode) {
|
|
46
45
|
return {
|
|
47
46
|
orientation: defaultOrientation,
|
|
48
47
|
savedItemIds: contextMenuDefaultPinnedIds,
|
|
@@ -129,7 +128,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
129
128
|
const [state, setState] = useState({
|
|
130
129
|
position: initialConfig.position, // Stored as percentage
|
|
131
130
|
isDragging: false,
|
|
132
|
-
isConfigMode: false,
|
|
133
131
|
orientation: initialConfig.orientation,
|
|
134
132
|
verticalDirection: 'down',
|
|
135
133
|
items: [],
|
|
@@ -141,12 +139,10 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
141
139
|
const [pixelPosition, setPixelPosition] = useState({ x: 0, y: 0 }); // Calculated pixel position
|
|
142
140
|
const [isOrientationChanging, setIsOrientationChanging] = useState(false); // Hide bar during orientation transition
|
|
143
141
|
const containerSizeRef = useRef({ width: 0, height: 0 });
|
|
144
|
-
const stateSnapshot = useRef(null);
|
|
145
142
|
const positionBeforeOrientationChange = useRef(null);
|
|
146
143
|
const floatingBarItemIds = useRef(new Set());
|
|
147
144
|
const floatingBarItemNames = useRef(new Set());
|
|
148
145
|
const isSyncingFromExternal = useRef(false);
|
|
149
|
-
const isExitingConfigMode = useRef(false);
|
|
150
146
|
const isLocalChange = useRef(false);
|
|
151
147
|
const dragStartPosition = useRef(null);
|
|
152
148
|
useEffect(() => {
|
|
@@ -213,11 +209,11 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
213
209
|
if (contextMenuItems.length > 0 || initialConfig.savedItemIds.length > 0 || defaultItems.length > 0) {
|
|
214
210
|
const flatItems = flattenMenuItems(contextMenuItems);
|
|
215
211
|
let itemsToSet = [];
|
|
216
|
-
// If
|
|
217
|
-
if (
|
|
212
|
+
// If enableConfigMode is false and defaultItems provided, use only defaultItems
|
|
213
|
+
if (!enableConfigMode && defaultItems.length > 0) {
|
|
218
214
|
itemsToSet = defaultItems;
|
|
219
215
|
}
|
|
220
|
-
else if (
|
|
216
|
+
else if (enableConfigMode && initialConfig.savedItemIds.length > 0) {
|
|
221
217
|
// Restore items in the saved order from localStorage (only if config mode is enabled)
|
|
222
218
|
const restoredItems = initialConfig.savedItemIds
|
|
223
219
|
.map((id) => {
|
|
@@ -250,20 +246,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
250
246
|
setState(s => ({ ...s, items: itemsToSet }));
|
|
251
247
|
}
|
|
252
248
|
}
|
|
253
|
-
},
|
|
249
|
+
}, enableConfigMode ? [] : [defaultItems]); // Update when defaultItems change if config mode is disabled
|
|
254
250
|
// Sync with external pinnedItemIds when they change (from parent component)
|
|
255
251
|
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
252
|
// Skip sync if a local change was just made (e.g. right-click remove/add separator)
|
|
262
253
|
if (isLocalChange.current) {
|
|
263
254
|
isLocalChange.current = false;
|
|
264
255
|
return;
|
|
265
256
|
}
|
|
266
|
-
if (externalPinnedItemIds === undefined ||
|
|
257
|
+
if (externalPinnedItemIds === undefined || !enableConfigMode)
|
|
267
258
|
return;
|
|
268
259
|
const flatItems = flattenMenuItems(contextMenuItems);
|
|
269
260
|
// Build items from external pinned IDs
|
|
@@ -288,7 +279,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
288
279
|
isSyncingFromExternal.current = true; // Mark that we're syncing from external
|
|
289
280
|
setState(s => ({ ...s, items: itemsFromExternal }));
|
|
290
281
|
}
|
|
291
|
-
}, [externalPinnedItemIds, contextMenuItems,
|
|
282
|
+
}, [externalPinnedItemIds, contextMenuItems, enableConfigMode, flattenMenuItems]);
|
|
292
283
|
const togglePin = useCallback((item) => {
|
|
293
284
|
setState(s => {
|
|
294
285
|
const isInFloatingBar = s.items.some(i => i.id === item.id);
|
|
@@ -368,42 +359,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
368
359
|
const separator = createSeparator();
|
|
369
360
|
setState(s => ({ ...s, items: [...s.items, separator] }));
|
|
370
361
|
}, [state.items.length, maxItems, createSeparator]);
|
|
371
|
-
const getPinContextMenuItems = useCallback(() => {
|
|
372
|
-
const flatItems = flattenMenuItems(contextMenuItems);
|
|
373
|
-
const currentItemIds = new Set(state.items.map(i => i.id));
|
|
374
|
-
const createPinItems = (items) => {
|
|
375
|
-
return items.map(item => {
|
|
376
|
-
const flatItem = flatItems.find(fi => fi.id === item.id);
|
|
377
|
-
const itemId = flatItem?.id || item.id || '';
|
|
378
|
-
const isAlreadyPinned = currentItemIds.has(itemId);
|
|
379
|
-
const pinItem = {
|
|
380
|
-
...item,
|
|
381
|
-
// Remove rightIconProps in config mode - clicking the item itself pins it
|
|
382
|
-
rightIconProps: undefined,
|
|
383
|
-
onClick: item.onClick && !item.submenu ? () => {
|
|
384
|
-
if (flatItem && !isAlreadyPinned) {
|
|
385
|
-
togglePin(flatItem);
|
|
386
|
-
}
|
|
387
|
-
} : undefined,
|
|
388
|
-
disabled: isAlreadyPinned,
|
|
389
|
-
};
|
|
390
|
-
if (item.submenu) {
|
|
391
|
-
pinItem.submenu = createPinItems(item.submenu);
|
|
392
|
-
}
|
|
393
|
-
return pinItem;
|
|
394
|
-
});
|
|
395
|
-
};
|
|
396
|
-
const pinItems = createPinItems(contextMenuItems);
|
|
397
|
-
// Add separator option at the end
|
|
398
|
-
pinItems.push({
|
|
399
|
-
id: 'add-separator',
|
|
400
|
-
name: SDKUI_Localizator.Add + ' separatore',
|
|
401
|
-
icon: _jsx(IconSeparator, {}),
|
|
402
|
-
onClick: addSeparator,
|
|
403
|
-
beginGroup: true
|
|
404
|
-
});
|
|
405
|
-
return pinItems;
|
|
406
|
-
}, [contextMenuItems, flattenMenuItems, togglePin, state.items, addSeparator]);
|
|
407
362
|
const getContextMenuItemsWithPinIcons = useCallback(() => {
|
|
408
363
|
const flatItems = flattenMenuItems(contextMenuItems);
|
|
409
364
|
const currentItemIds = new Set(state.items.map(i => i.id));
|
|
@@ -431,8 +386,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
431
386
|
return addPinIcons(contextMenuItems);
|
|
432
387
|
}, [contextMenuItems, flattenMenuItems, togglePin, state.items]);
|
|
433
388
|
const handleMouseDown = (e) => {
|
|
434
|
-
if (state.isConfigMode)
|
|
435
|
-
return;
|
|
436
389
|
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
437
390
|
if (containerRect) {
|
|
438
391
|
// Calculate drag offset based on positioning mode
|
|
@@ -456,8 +409,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
456
409
|
setState(s => ({ ...s, isDragging: true }));
|
|
457
410
|
};
|
|
458
411
|
const handleGripDoubleClick = () => {
|
|
459
|
-
if (state.isConfigMode)
|
|
460
|
-
return;
|
|
461
412
|
toggleOrientation();
|
|
462
413
|
};
|
|
463
414
|
const handleMouseMove = useCallback((e) => {
|
|
@@ -513,8 +464,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
513
464
|
}, [state.isDragging, pixelPosition]);
|
|
514
465
|
// Touch event handlers for tablet support
|
|
515
466
|
const handleTouchStart = (e) => {
|
|
516
|
-
if (state.isConfigMode)
|
|
517
|
-
return;
|
|
518
467
|
const touch = e.touches[0];
|
|
519
468
|
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
520
469
|
if (containerRect) {
|
|
@@ -596,11 +545,9 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
596
545
|
}
|
|
597
546
|
return undefined;
|
|
598
547
|
}, [state.isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
|
|
599
|
-
// Save to SDKUI_Globals.userSettings
|
|
548
|
+
// Save to SDKUI_Globals.userSettings
|
|
600
549
|
useEffect(() => {
|
|
601
|
-
if (
|
|
602
|
-
return; // Don't save during edit mode
|
|
603
|
-
if (disbaleConfigMode)
|
|
550
|
+
if (!enableConfigMode)
|
|
604
551
|
return; // Don't save if config mode is disabled
|
|
605
552
|
const pinnedIds = state.items.map(item => item.id);
|
|
606
553
|
try {
|
|
@@ -621,139 +568,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
621
568
|
onPinChange?.(pinnedIds);
|
|
622
569
|
}
|
|
623
570
|
isSyncingFromExternal.current = false; // Reset the flag
|
|
624
|
-
}, [state.orientation, state.items, state.position,
|
|
625
|
-
const toggleConfigMode = () => {
|
|
626
|
-
setState(s => {
|
|
627
|
-
if (!s.isConfigMode) {
|
|
628
|
-
stateSnapshot.current = {
|
|
629
|
-
items: [...s.items],
|
|
630
|
-
orientation: s.orientation,
|
|
631
|
-
verticalDirection: s.verticalDirection,
|
|
632
|
-
position: { ...s.position },
|
|
633
|
-
};
|
|
634
|
-
return { ...s, isConfigMode: true };
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
// Exiting edit mode (applying changes) - clean up trailing separators and clear snapshot
|
|
638
|
-
stateSnapshot.current = null;
|
|
639
|
-
isExitingConfigMode.current = true; // Prevent sync effect from overwriting changes
|
|
640
|
-
const cleanedItems = removeTrailingSeparators(s.items);
|
|
641
|
-
return { ...s, isConfigMode: false, items: cleanedItems };
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
};
|
|
645
|
-
// Auto-reposition when entering edit mode to ensure Apply/Undo buttons are visible
|
|
646
|
-
useEffect(() => {
|
|
647
|
-
if (!state.isConfigMode || !floatingRef.current)
|
|
648
|
-
return;
|
|
649
|
-
// Use double requestAnimationFrame to ensure the DOM has fully updated with new buttons
|
|
650
|
-
requestAnimationFrame(() => {
|
|
651
|
-
requestAnimationFrame(() => {
|
|
652
|
-
if (!floatingRef.current)
|
|
653
|
-
return;
|
|
654
|
-
const floating = floatingRef.current.getBoundingClientRect();
|
|
655
|
-
const containerWidth = isConstrained && containerRef.current
|
|
656
|
-
? containerRef.current.getBoundingClientRect().width
|
|
657
|
-
: window.innerWidth;
|
|
658
|
-
const containerHeight = isConstrained && containerRef.current
|
|
659
|
-
? containerRef.current.getBoundingClientRect().height
|
|
660
|
-
: window.innerHeight;
|
|
661
|
-
// Use current pixel position
|
|
662
|
-
let newPixelX = pixelPosition.x;
|
|
663
|
-
let newPixelY = pixelPosition.y;
|
|
664
|
-
let needsUpdate = false;
|
|
665
|
-
// Check horizontal overflow
|
|
666
|
-
if (newPixelX + floating.width > containerWidth) {
|
|
667
|
-
newPixelX = Math.max(0, containerWidth - floating.width);
|
|
668
|
-
needsUpdate = true;
|
|
669
|
-
}
|
|
670
|
-
// Check vertical overflow
|
|
671
|
-
if (newPixelY + floating.height > containerHeight) {
|
|
672
|
-
newPixelY = Math.max(0, containerHeight - floating.height);
|
|
673
|
-
needsUpdate = true;
|
|
674
|
-
}
|
|
675
|
-
if (needsUpdate) {
|
|
676
|
-
// Update pixel position immediately
|
|
677
|
-
setPixelPosition({ x: newPixelX, y: newPixelY });
|
|
678
|
-
// Convert to percentage for state
|
|
679
|
-
const newPercentagePosition = {
|
|
680
|
-
x: pixelsToPercent(newPixelX, containerWidth),
|
|
681
|
-
y: pixelsToPercent(newPixelY, containerHeight),
|
|
682
|
-
};
|
|
683
|
-
setState(s => ({
|
|
684
|
-
...s,
|
|
685
|
-
position: newPercentagePosition,
|
|
686
|
-
}));
|
|
687
|
-
// Update snapshot position to the corrected position so Undo restores to visible position
|
|
688
|
-
if (stateSnapshot.current) {
|
|
689
|
-
stateSnapshot.current.position = newPercentagePosition;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
});
|
|
694
|
-
}, [state.isConfigMode, state.orientation, isConstrained, state.items, pixelPosition.x, pixelPosition.y]);
|
|
695
|
-
const handleUndo = () => {
|
|
696
|
-
if (stateSnapshot.current) {
|
|
697
|
-
setState(s => ({
|
|
698
|
-
...s,
|
|
699
|
-
items: [...stateSnapshot.current.items],
|
|
700
|
-
orientation: stateSnapshot.current.orientation,
|
|
701
|
-
verticalDirection: stateSnapshot.current.verticalDirection,
|
|
702
|
-
position: { ...stateSnapshot.current.position },
|
|
703
|
-
isConfigMode: true,
|
|
704
|
-
}));
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
// Check if current state has changed from snapshot
|
|
708
|
-
const hasChanges = () => {
|
|
709
|
-
if (!stateSnapshot.current)
|
|
710
|
-
return false;
|
|
711
|
-
// Check if items have changed (different IDs or different order)
|
|
712
|
-
const currentIds = state.items.map(i => i.id).join(',');
|
|
713
|
-
const snapshotIds = stateSnapshot.current.items.map(i => i.id).join(',');
|
|
714
|
-
return currentIds !== snapshotIds;
|
|
715
|
-
};
|
|
716
|
-
const handleClose = () => {
|
|
717
|
-
// If all items removed, exit without asking and restore last items
|
|
718
|
-
if (state.items.length === 0 && stateSnapshot.current) {
|
|
719
|
-
setState(s => ({
|
|
720
|
-
...s,
|
|
721
|
-
items: [...stateSnapshot.current.items],
|
|
722
|
-
orientation: stateSnapshot.current.orientation,
|
|
723
|
-
verticalDirection: stateSnapshot.current.verticalDirection,
|
|
724
|
-
position: { ...stateSnapshot.current.position },
|
|
725
|
-
isConfigMode: false,
|
|
726
|
-
}));
|
|
727
|
-
stateSnapshot.current = null;
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
// If no changes, simply exit config mode
|
|
731
|
-
if (!hasChanges()) {
|
|
732
|
-
stateSnapshot.current = null;
|
|
733
|
-
const cleanedItems = removeTrailingSeparators(state.items);
|
|
734
|
-
setState(s => ({ ...s, isConfigMode: false, items: cleanedItems }));
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
// If there are changes, ask for confirmation
|
|
738
|
-
TMMessageBoxManager.show({
|
|
739
|
-
message: 'Perderai le tue modifiche, sei sicuro?',
|
|
740
|
-
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
741
|
-
onButtonClick: (buttonName) => {
|
|
742
|
-
if (buttonName === ButtonNames.YES && stateSnapshot.current) {
|
|
743
|
-
// Restore snapshot and exit config mode
|
|
744
|
-
setState(s => ({
|
|
745
|
-
...s,
|
|
746
|
-
items: [...stateSnapshot.current.items],
|
|
747
|
-
orientation: stateSnapshot.current.orientation,
|
|
748
|
-
verticalDirection: stateSnapshot.current.verticalDirection,
|
|
749
|
-
position: { ...stateSnapshot.current.position },
|
|
750
|
-
isConfigMode: false,
|
|
751
|
-
}));
|
|
752
|
-
stateSnapshot.current = null;
|
|
753
|
-
}
|
|
754
|
-
},
|
|
755
|
-
});
|
|
756
|
-
};
|
|
571
|
+
}, [state.orientation, state.items, state.position, enableConfigMode, onPinChange]);
|
|
757
572
|
const toggleOrientation = () => {
|
|
758
573
|
const newOrientation = state.orientation === 'horizontal' ? 'vertical' : 'horizontal';
|
|
759
574
|
// When switching from vertical back to horizontal, restore the original position
|
|
@@ -887,50 +702,41 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
887
702
|
onClick: () => removeItem(item.id),
|
|
888
703
|
},
|
|
889
704
|
{
|
|
890
|
-
name: 'Aggiungi separatore a
|
|
891
|
-
icon: _jsx(IconSeparator, {
|
|
892
|
-
disabled:
|
|
705
|
+
name: 'Aggiungi separatore a sinistra',
|
|
706
|
+
icon: _jsx(IconSeparator, { fontSize: 16 }),
|
|
707
|
+
disabled: hasSeparatorOnLeft,
|
|
893
708
|
onClick: () => {
|
|
894
709
|
isLocalChange.current = true;
|
|
895
710
|
const separator = createSeparator();
|
|
896
711
|
setState(s => {
|
|
897
712
|
const newItems = [...s.items];
|
|
898
|
-
newItems.splice(index
|
|
713
|
+
newItems.splice(index, 0, separator);
|
|
899
714
|
return { ...s, items: newItems };
|
|
900
715
|
});
|
|
901
716
|
},
|
|
902
717
|
},
|
|
903
718
|
{
|
|
904
|
-
name: 'Aggiungi separatore a
|
|
905
|
-
icon: _jsx(IconSeparator, { fontSize: 16 }),
|
|
906
|
-
disabled:
|
|
719
|
+
name: 'Aggiungi separatore a destra',
|
|
720
|
+
icon: _jsx(IconSeparator, { style: { transform: 'rotate(-90deg)' }, fontSize: 16 }),
|
|
721
|
+
disabled: hasSeparatorOnRight,
|
|
907
722
|
onClick: () => {
|
|
908
723
|
isLocalChange.current = true;
|
|
909
724
|
const separator = createSeparator();
|
|
910
725
|
setState(s => {
|
|
911
726
|
const newItems = [...s.items];
|
|
912
|
-
newItems.splice(index, 0, separator);
|
|
727
|
+
newItems.splice(index + 1, 0, separator);
|
|
913
728
|
return { ...s, items: newItems };
|
|
914
729
|
});
|
|
915
730
|
},
|
|
916
731
|
},
|
|
917
|
-
...(!disbaleConfigMode ? [{
|
|
918
|
-
beginGroup: true,
|
|
919
|
-
name: SDKUI_Localizator.Configure,
|
|
920
|
-
icon: _jsx(IconSettings, { fontSize: 16 }),
|
|
921
|
-
onClick: () => {
|
|
922
|
-
if (!state.isConfigMode) {
|
|
923
|
-
toggleConfigMode();
|
|
924
|
-
}
|
|
925
|
-
},
|
|
926
|
-
}] : []),
|
|
927
732
|
{
|
|
733
|
+
beginGroup: true,
|
|
928
734
|
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
929
735
|
icon: _jsx(IconRotate, { fontSize: 16 }),
|
|
930
736
|
onClick: toggleOrientation,
|
|
931
737
|
},
|
|
932
738
|
];
|
|
933
|
-
}, [state.items, state.
|
|
739
|
+
}, [state.items, state.orientation, removeItem, createSeparator, toggleOrientation]);
|
|
934
740
|
const getSeparatorRightClickMenuItems = useCallback((index) => {
|
|
935
741
|
return [
|
|
936
742
|
{
|
|
@@ -939,42 +745,33 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
939
745
|
onClick: () => removeItem(state.items[index].id),
|
|
940
746
|
},
|
|
941
747
|
{
|
|
942
|
-
name: 'Aggiungi separatore a
|
|
943
|
-
icon: _jsx(IconSeparator, {
|
|
748
|
+
name: 'Aggiungi separatore a sinistra',
|
|
749
|
+
icon: _jsx(IconSeparator, { fontSize: 16 }),
|
|
944
750
|
disabled: true,
|
|
945
751
|
},
|
|
946
752
|
{
|
|
947
|
-
name: 'Aggiungi separatore a
|
|
948
|
-
icon: _jsx(IconSeparator, { fontSize: 16 }),
|
|
753
|
+
name: 'Aggiungi separatore a destra',
|
|
754
|
+
icon: _jsx(IconSeparator, { style: { transform: 'rotate(-90deg)' }, fontSize: 16 }),
|
|
949
755
|
disabled: true,
|
|
950
756
|
},
|
|
951
|
-
...(!disbaleConfigMode ? [{
|
|
952
|
-
beginGroup: true,
|
|
953
|
-
name: SDKUI_Localizator.Configure,
|
|
954
|
-
icon: _jsx(IconSettings, { fontSize: 16 }),
|
|
955
|
-
onClick: () => {
|
|
956
|
-
if (!state.isConfigMode) {
|
|
957
|
-
toggleConfigMode();
|
|
958
|
-
}
|
|
959
|
-
},
|
|
960
|
-
}] : []),
|
|
961
757
|
{
|
|
758
|
+
beginGroup: true,
|
|
962
759
|
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
963
760
|
icon: _jsx(IconRotate, { fontSize: 16 }),
|
|
964
761
|
onClick: toggleOrientation,
|
|
965
762
|
},
|
|
966
763
|
];
|
|
967
|
-
}, [state.items, state.
|
|
764
|
+
}, [state.items, state.orientation, removeItem, toggleOrientation]);
|
|
968
765
|
// Drag and drop for reordering
|
|
969
766
|
const handleDragStart = (e, index) => {
|
|
970
|
-
if (!
|
|
767
|
+
if (!enableConfigMode)
|
|
971
768
|
return;
|
|
972
769
|
e.dataTransfer.effectAllowed = 'move';
|
|
973
770
|
e.dataTransfer.setData('text/plain', index.toString());
|
|
974
771
|
setState(s => ({ ...s, draggedItemIndex: index }));
|
|
975
772
|
};
|
|
976
773
|
const handleDragEnter = (e, index) => {
|
|
977
|
-
if (!
|
|
774
|
+
if (!enableConfigMode)
|
|
978
775
|
return;
|
|
979
776
|
e.preventDefault();
|
|
980
777
|
if (state.draggedItemIndex !== index) {
|
|
@@ -982,13 +779,13 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
982
779
|
}
|
|
983
780
|
};
|
|
984
781
|
const handleDragOver = (e) => {
|
|
985
|
-
if (!
|
|
782
|
+
if (!enableConfigMode)
|
|
986
783
|
return;
|
|
987
784
|
e.preventDefault();
|
|
988
785
|
e.dataTransfer.dropEffect = 'move';
|
|
989
786
|
};
|
|
990
787
|
const handleDragLeave = (_e, index) => {
|
|
991
|
-
if (!
|
|
788
|
+
if (!enableConfigMode)
|
|
992
789
|
return;
|
|
993
790
|
// Only clear if we're actually leaving this specific item
|
|
994
791
|
if (dragOverIndex === index) {
|
|
@@ -996,7 +793,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
996
793
|
}
|
|
997
794
|
};
|
|
998
795
|
const handleDrop = (e, dropIndex) => {
|
|
999
|
-
if (!
|
|
796
|
+
if (!enableConfigMode || state.draggedItemIndex === null)
|
|
1000
797
|
return;
|
|
1001
798
|
e.preventDefault();
|
|
1002
799
|
e.stopPropagation();
|
|
@@ -1005,6 +802,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
1005
802
|
setDragOverIndex(null);
|
|
1006
803
|
return;
|
|
1007
804
|
}
|
|
805
|
+
isLocalChange.current = true;
|
|
1008
806
|
const newItems = [...state.items];
|
|
1009
807
|
const [draggedItem] = newItems.splice(dragIndex, 1);
|
|
1010
808
|
newItems.splice(dropIndex, 0, draggedItem);
|
|
@@ -1019,46 +817,37 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
1019
817
|
setState(s => ({ ...s, draggedItemIndex: null }));
|
|
1020
818
|
setDragOverIndex(null);
|
|
1021
819
|
};
|
|
1022
|
-
return (_jsxs(
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (isDisabled)
|
|
1055
|
-
return;
|
|
1056
|
-
if (currentOnClick) {
|
|
1057
|
-
currentOnClick();
|
|
1058
|
-
}
|
|
1059
|
-
}, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) })) })) }, item.id));
|
|
1060
|
-
}), !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
|
|
1061
|
-
? 'Devi aggiungere almeno un item'
|
|
1062
|
-
: 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 }) }) })] })] }))] })] }));
|
|
820
|
+
return (_jsxs(S.FloatingContainer, { ref: floatingRef, "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": false, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: undefined, children: [_jsx(ContextMenu, { items: [
|
|
821
|
+
{
|
|
822
|
+
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
823
|
+
icon: _jsx(IconRotate, { fontSize: 16, style: { transform: state.orientation === 'horizontal' ? 'rotate(90deg)' : 'rotate(0deg)' } }),
|
|
824
|
+
onClick: toggleOrientation,
|
|
825
|
+
},
|
|
826
|
+
], trigger: "right", children: _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) => {
|
|
827
|
+
// Handle separator items
|
|
828
|
+
if (item.isSeparator) {
|
|
829
|
+
return (_jsx(S.DraggableItem, { "$isDragging": state.draggedItemIndex === index, "$isDragOver": dragOverIndex === index && state.draggedItemIndex !== index, draggable: enableConfigMode, 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: enableConfigMode ? (_jsx(ContextMenu, { items: getSeparatorRightClickMenuItems(index), trigger: "right", children: _jsx(S.ItemSeparator, { "$orientation": state.orientation, "$isConfigMode": false }) })) : (_jsx(S.ItemSeparator, { "$orientation": state.orientation, "$isConfigMode": false })) }, item.id));
|
|
830
|
+
}
|
|
831
|
+
// Get current state (disabled and onClick) from contextMenuItems
|
|
832
|
+
const currentState = getCurrentItemState(item.id);
|
|
833
|
+
// Prefer currentState.disabled if contextMenuItems has items, otherwise use item.disabled
|
|
834
|
+
const isDisabled = (contextMenuItems.length > 0 && currentState.disabled !== undefined)
|
|
835
|
+
? currentState.disabled === true
|
|
836
|
+
: item.disabled === true;
|
|
837
|
+
const currentOnClick = currentState.onClick || item.onClick; // Fallback to stored onClick if not found
|
|
838
|
+
return (_jsx(S.DraggableItem, { "$isDragging": state.draggedItemIndex === index, "$isDragOver": dragOverIndex === index && state.draggedItemIndex !== index, draggable: enableConfigMode, 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: enableConfigMode ? (_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: () => {
|
|
839
|
+
if (isDisabled)
|
|
840
|
+
return;
|
|
841
|
+
if (currentOnClick) {
|
|
842
|
+
currentOnClick();
|
|
843
|
+
}
|
|
844
|
+
}, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) })) })) : (item.staticItem ? (_jsx("div", { children: item.staticItem })) : (_jsx(TMTooltip, { content: item.name, position: "bottom", children: _jsx(S.MenuButton, { onClick: () => {
|
|
845
|
+
if (isDisabled)
|
|
846
|
+
return;
|
|
847
|
+
if (currentOnClick) {
|
|
848
|
+
currentOnClick();
|
|
849
|
+
}
|
|
850
|
+
}, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) }))) }, item.id));
|
|
851
|
+
}), enableConfigMode && hasContextMenu && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getContextMenuItemsWithPinIcons(), trigger: "left", keepOpenOnClick: false, children: _jsx(S.ContextMenuButton, { children: _jsx(IconMenuVertical, {}) }) }))] }));
|
|
1063
852
|
};
|
|
1064
853
|
export default TMFloatingMenuBar;
|
|
@@ -19,7 +19,7 @@ export interface TMFloatingMenuBarProps {
|
|
|
19
19
|
contextMenuDefaultPinnedIds?: string[];
|
|
20
20
|
defaultOrientation?: 'horizontal' | 'vertical';
|
|
21
21
|
defaultItems?: TMFloatingMenuItem[];
|
|
22
|
-
|
|
22
|
+
enableConfigMode?: boolean;
|
|
23
23
|
bgColor?: string;
|
|
24
24
|
hasContextMenu?: boolean;
|
|
25
25
|
pinnedItemIds?: string[];
|
|
@@ -32,7 +32,6 @@ export interface Position {
|
|
|
32
32
|
export interface TMFloatingMenuBarState {
|
|
33
33
|
position: Position;
|
|
34
34
|
isDragging: boolean;
|
|
35
|
-
isConfigMode: boolean;
|
|
36
35
|
orientation: 'horizontal' | 'vertical';
|
|
37
36
|
verticalDirection: 'down' | 'up';
|
|
38
37
|
items: TMFloatingMenuItem[];
|
|
@@ -1822,7 +1822,7 @@ const WFDiagram = ({ xmlDiagramString, currentSetID, allowEdit = true, onDiagram
|
|
|
1822
1822
|
}, [isFullScreen]);
|
|
1823
1823
|
const diagramContent = (_jsxs(CanvasContainer, { onDoubleClick: handleCanvasDoubleClick, children: [_jsx("input", { ref: fileInputRef, type: "file", accept: ".xml" // Filtra per file XML
|
|
1824
1824
|
, onChange: handleFileChange, style: { display: 'none' } }), SDK_Globals.tmSession?.SessionDescr?.appModuleID === AppModules.SURFER ?
|
|
1825
|
-
_jsx(TMFloatingMenuBar, { containerRef: diagramRef, defaultPosition: { x: 45, y: 85 },
|
|
1825
|
+
_jsx(TMFloatingMenuBar, { containerRef: diagramRef, defaultPosition: { x: 45, y: 85 }, enableConfigMode: false, defaultItems: [
|
|
1826
1826
|
{ icon: _jsx(IconZoomIn, {}), name: SDKUI_Localizator.ZoomIn, disabled: isAutoZoomEnabled, onClick: () => { handleZoomIn(); }, id: 'zoom-in', isPinned: true },
|
|
1827
1827
|
{ icon: _jsx(IconZoomOut, {}), name: SDKUI_Localizator.ZoomOut, disabled: isAutoZoomEnabled, onClick: () => { handleZoomOut(); }, id: 'zoom-out', isPinned: true },
|
|
1828
1828
|
{ icon: _jsx(IconZoomAuto, {}), name: 'AutoZoom', onClick: () => { handleToggleAutoZoom(); }, id: 'zoom-auto', isPinned: true, isToggle: isAutoZoomEnabled },
|