@topconsultnpm/sdkui-react 6.20.0-dev1.67 → 6.20.0-dev1.69
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 +13 -1
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +44 -2
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +1 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +45 -11
- package/package.json +1 -1
|
@@ -59,6 +59,8 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
|
|
|
59
59
|
const touchEvent = e;
|
|
60
60
|
const element = e.currentTarget;
|
|
61
61
|
const touch = touchEvent.touches[0];
|
|
62
|
+
// Prevent text selection during long press
|
|
63
|
+
e.preventDefault();
|
|
62
64
|
let state = touchStateMap.get(element);
|
|
63
65
|
if (!state) {
|
|
64
66
|
state = { timeout: null, startX: 0, startY: 0, longPressTriggered: false };
|
|
@@ -119,17 +121,25 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
|
|
|
119
121
|
state.longPressTriggered = false;
|
|
120
122
|
}
|
|
121
123
|
};
|
|
124
|
+
// Prevent default iOS context menu
|
|
125
|
+
const handleContextMenu = (e) => {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
return false;
|
|
129
|
+
};
|
|
122
130
|
// Attach listeners to all matching elements
|
|
123
131
|
elements.forEach(element => {
|
|
124
132
|
const el = element;
|
|
125
|
-
// Prevent iOS native callout
|
|
133
|
+
// Prevent iOS native callout and text selection
|
|
126
134
|
const style = el.style;
|
|
127
135
|
style.webkitTouchCallout = 'none';
|
|
128
136
|
style.webkitUserSelect = 'none';
|
|
137
|
+
style.userSelect = 'none';
|
|
129
138
|
el.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
130
139
|
el.addEventListener('touchmove', handleTouchMove, { passive: true });
|
|
131
140
|
el.addEventListener('touchend', handleTouchEnd);
|
|
132
141
|
el.addEventListener('touchcancel', handleTouchEnd);
|
|
142
|
+
el.addEventListener('contextmenu', handleContextMenu);
|
|
133
143
|
el.addEventListener('click', handleClick, { capture: true });
|
|
134
144
|
});
|
|
135
145
|
return () => {
|
|
@@ -138,10 +148,12 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
|
|
|
138
148
|
const style = el.style;
|
|
139
149
|
style.webkitTouchCallout = '';
|
|
140
150
|
style.webkitUserSelect = '';
|
|
151
|
+
style.userSelect = '';
|
|
141
152
|
el.removeEventListener('touchstart', handleTouchStart);
|
|
142
153
|
el.removeEventListener('touchmove', handleTouchMove);
|
|
143
154
|
el.removeEventListener('touchend', handleTouchEnd);
|
|
144
155
|
el.removeEventListener('touchcancel', handleTouchEnd);
|
|
156
|
+
el.removeEventListener('contextmenu', handleContextMenu);
|
|
145
157
|
el.removeEventListener('click', handleClick, { capture: true });
|
|
146
158
|
});
|
|
147
159
|
};
|
|
@@ -4,7 +4,8 @@ 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,
|
|
7
|
+
import { IconAdd, IconCloseOutline, IconMenuVertical, IconPin, IconSave, IconSeparator, IconUndo, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
|
|
8
|
+
import { ButtonNames, TMMessageBoxManager } from '../../base/TMPopUp';
|
|
8
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" }) }));
|
|
9
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" }) }));
|
|
10
11
|
const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 100, y: 100 }, maxItems = 100, }) => {
|
|
@@ -603,6 +604,45 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
603
604
|
const snapshotIds = stateSnapshot.current.items.map(i => i.id).join(',');
|
|
604
605
|
return currentIds !== snapshotIds;
|
|
605
606
|
};
|
|
607
|
+
const handleClose = () => {
|
|
608
|
+
// If all items removed, exit without asking and restore last items
|
|
609
|
+
if (state.items.length === 0 && stateSnapshot.current) {
|
|
610
|
+
setState(s => ({
|
|
611
|
+
...s,
|
|
612
|
+
items: [...stateSnapshot.current.items],
|
|
613
|
+
orientation: stateSnapshot.current.orientation,
|
|
614
|
+
position: { ...stateSnapshot.current.position },
|
|
615
|
+
isConfigMode: false,
|
|
616
|
+
}));
|
|
617
|
+
stateSnapshot.current = null;
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
// If no changes, simply exit config mode
|
|
621
|
+
if (!hasChanges()) {
|
|
622
|
+
stateSnapshot.current = null;
|
|
623
|
+
const cleanedItems = removeTrailingSeparators(state.items);
|
|
624
|
+
setState(s => ({ ...s, isConfigMode: false, items: cleanedItems }));
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
// If there are changes, ask for confirmation
|
|
628
|
+
TMMessageBoxManager.show({
|
|
629
|
+
message: 'Perderai le tue modifiche, sei sicuro?',
|
|
630
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
631
|
+
onButtonClick: (buttonName) => {
|
|
632
|
+
if (buttonName === ButtonNames.YES && stateSnapshot.current) {
|
|
633
|
+
// Restore snapshot and exit config mode
|
|
634
|
+
setState(s => ({
|
|
635
|
+
...s,
|
|
636
|
+
items: [...stateSnapshot.current.items],
|
|
637
|
+
orientation: stateSnapshot.current.orientation,
|
|
638
|
+
position: { ...stateSnapshot.current.position },
|
|
639
|
+
isConfigMode: false,
|
|
640
|
+
}));
|
|
641
|
+
stateSnapshot.current = null;
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
};
|
|
606
646
|
const toggleOrientation = () => {
|
|
607
647
|
const newOrientation = state.orientation === 'horizontal' ? 'vertical' : 'horizontal';
|
|
608
648
|
if (state.orientation === 'horizontal' && newOrientation === 'vertical') {
|
|
@@ -740,6 +780,8 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
740
780
|
currentOnClick();
|
|
741
781
|
}
|
|
742
782
|
}, disabled: state.isConfigMode ? isDisabled && !state.isConfigMode : isDisabled, children: item.icon }) }), state.isConfigMode && (_jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" }))] }, item.id));
|
|
743
|
-
}), !state.isConfigMode && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getContextMenuItemsWithPinIcons(), trigger: "left", keepOpenOnClick: true, 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
|
|
783
|
+
}), !state.isConfigMode && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getContextMenuItemsWithPinIcons(), trigger: "left", keepOpenOnClick: true, 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
|
|
784
|
+
? 'Devi aggiungere almeno un item'
|
|
785
|
+
: 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 }) }) })] })] }))] })] }));
|
|
744
786
|
};
|
|
745
787
|
export default TMFloatingMenuBar;
|
|
@@ -33,6 +33,7 @@ export declare const MenuButton: import("styled-components/dist/types").IStyledC
|
|
|
33
33
|
}>> & string;
|
|
34
34
|
export declare const ConfigButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
|
|
35
35
|
export declare const ApplyButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
|
|
36
|
+
export declare const CloseButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
|
|
36
37
|
export declare const ContextMenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
|
|
37
38
|
$isActive?: boolean;
|
|
38
39
|
}, "ref"> & {
|
|
@@ -74,7 +74,7 @@ export const GripHandle = styled.div `
|
|
|
74
74
|
display: flex;
|
|
75
75
|
align-items: center;
|
|
76
76
|
justify-content: center;
|
|
77
|
-
padding: ${props => props.$orientation === 'horizontal' ? '
|
|
77
|
+
padding: ${props => props.$orientation === 'horizontal' ? '6px 4px' : '4px 6px'};
|
|
78
78
|
cursor: grab;
|
|
79
79
|
color: rgba(255, 255, 255, 0.7);
|
|
80
80
|
transition: all 0.2s ease;
|
|
@@ -98,7 +98,7 @@ export const GripHandle = styled.div `
|
|
|
98
98
|
export const Separator = styled.div `
|
|
99
99
|
background: rgba(255, 255, 255, 0.25);
|
|
100
100
|
width: ${props => props.$orientation === 'horizontal' ? '1px' : '100%'};
|
|
101
|
-
height: ${props => props.$orientation === 'horizontal' ? '
|
|
101
|
+
height: ${props => props.$orientation === 'horizontal' ? '20px' : '1px'};
|
|
102
102
|
margin: ${props => props.$orientation === 'horizontal' ? '0 4px' : '4px 0'};
|
|
103
103
|
flex-shrink: 0;
|
|
104
104
|
`;
|
|
@@ -111,8 +111,8 @@ export const ItemSeparator = styled.div `
|
|
|
111
111
|
display: flex;
|
|
112
112
|
align-items: center;
|
|
113
113
|
justify-content: center;
|
|
114
|
-
width:
|
|
115
|
-
height:
|
|
114
|
+
width: 30px;
|
|
115
|
+
height: 30px;
|
|
116
116
|
background: transparent;
|
|
117
117
|
border-radius: 8px;
|
|
118
118
|
cursor: grab;
|
|
@@ -122,7 +122,7 @@ export const ItemSeparator = styled.div `
|
|
|
122
122
|
content: '';
|
|
123
123
|
background: rgba(255, 255, 255, 0.25);
|
|
124
124
|
width: ${props.$orientation === 'horizontal' ? '2px' : '100%'};
|
|
125
|
-
height: ${props.$orientation === 'horizontal' ? '
|
|
125
|
+
height: ${props.$orientation === 'horizontal' ? '18px' : '2px'};
|
|
126
126
|
border-radius: 1px;
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -137,7 +137,7 @@ export const ItemSeparator = styled.div `
|
|
|
137
137
|
/* Normal mode: simple line with tight spacing */
|
|
138
138
|
background: rgba(255, 255, 255, 0.25);
|
|
139
139
|
width: ${props.$orientation === 'horizontal' ? '1px' : '100%'};
|
|
140
|
-
height: ${props.$orientation === 'horizontal' ? '
|
|
140
|
+
height: ${props.$orientation === 'horizontal' ? '20px' : '1px'};
|
|
141
141
|
margin: ${props.$orientation === 'horizontal' ? '0 2px' : '2px 0'};
|
|
142
142
|
`}
|
|
143
143
|
`;
|
|
@@ -145,8 +145,8 @@ export const MenuButton = styled.button `
|
|
|
145
145
|
display: flex;
|
|
146
146
|
align-items: center;
|
|
147
147
|
justify-content: center;
|
|
148
|
-
width:
|
|
149
|
-
height:
|
|
148
|
+
width: 30px;
|
|
149
|
+
height: 30px;
|
|
150
150
|
background: transparent;
|
|
151
151
|
border: none;
|
|
152
152
|
border-radius: 8px;
|
|
@@ -243,6 +243,40 @@ export const ApplyButton = styled.button `
|
|
|
243
243
|
height: 20px;
|
|
244
244
|
}
|
|
245
245
|
`;
|
|
246
|
+
export const CloseButton = styled.button `
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
justify-content: center;
|
|
250
|
+
width: 24px;
|
|
251
|
+
height: 24px;
|
|
252
|
+
background: transparent;
|
|
253
|
+
border: none;
|
|
254
|
+
border-radius: 4px;
|
|
255
|
+
color: rgba(239, 68, 68, 1);
|
|
256
|
+
font-size: 10px;
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
transition: all 0.2s ease;
|
|
259
|
+
padding: 3px;
|
|
260
|
+
|
|
261
|
+
&:hover:not(:disabled) {
|
|
262
|
+
background: rgba(255, 255, 255, 0.1);
|
|
263
|
+
color: rgba(239, 68, 68, 1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
&:active:not(:disabled) {
|
|
267
|
+
transform: scale(0.9);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
&:disabled {
|
|
271
|
+
opacity: 0.3;
|
|
272
|
+
cursor: default;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
svg {
|
|
276
|
+
width: 20px;
|
|
277
|
+
height: 20px;
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
246
280
|
export const ContextMenuButton = styled(MenuButton) `
|
|
247
281
|
svg {
|
|
248
282
|
transform: translateY(0);
|
|
@@ -252,13 +286,13 @@ export const AddButton = styled.button `
|
|
|
252
286
|
display: flex;
|
|
253
287
|
align-items: center;
|
|
254
288
|
justify-content: center;
|
|
255
|
-
width:
|
|
256
|
-
height:
|
|
289
|
+
width: 26px;
|
|
290
|
+
height: 26px;
|
|
257
291
|
background: rgba(255, 255, 255, 0.15);
|
|
258
292
|
border: 1px dashed rgba(255, 255, 255, 0.4);
|
|
259
293
|
border-radius: 8px;
|
|
260
294
|
color: white;
|
|
261
|
-
font-size:
|
|
295
|
+
font-size: 18px;
|
|
262
296
|
font-weight: bold;
|
|
263
297
|
line-height: 0;
|
|
264
298
|
cursor: pointer;
|