@topconsultnpm/sdkui-react 6.20.0-dev1.46 → 6.20.0-dev1.48
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.
|
@@ -136,7 +136,7 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
|
|
|
136
136
|
};
|
|
137
137
|
// iOS-specific touch handlers for long press
|
|
138
138
|
const handleTouchStart = (e) => {
|
|
139
|
-
if (!isIOS
|
|
139
|
+
if (!isIOS)
|
|
140
140
|
return;
|
|
141
141
|
const touch = e.touches[0];
|
|
142
142
|
touchStartPos.current = { x: touch.clientX, y: touch.clientY };
|
|
@@ -176,7 +176,6 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
|
|
|
176
176
|
const handleTouchEnd = () => {
|
|
177
177
|
if (!isIOS)
|
|
178
178
|
return;
|
|
179
|
-
// Clear long press timeout if touch ended before long press completed
|
|
180
179
|
if (longPressTimeoutRef.current) {
|
|
181
180
|
clearTimeout(longPressTimeoutRef.current);
|
|
182
181
|
longPressTimeoutRef.current = null;
|
|
@@ -186,23 +185,19 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
|
|
|
186
185
|
const handleItemClick = (item) => {
|
|
187
186
|
if (item.disabled)
|
|
188
187
|
return;
|
|
189
|
-
// Always execute onClick if present
|
|
190
188
|
if (item.onClick) {
|
|
191
189
|
item.onClick();
|
|
192
190
|
}
|
|
193
191
|
if (item.submenu && item.submenu.length > 0) {
|
|
194
192
|
if (isMobile) {
|
|
195
|
-
// Mobile: Push submenu to stack
|
|
196
193
|
setMenuState(prev => ({
|
|
197
194
|
...prev,
|
|
198
195
|
submenuStack: [...prev.submenuStack, item.submenu],
|
|
199
196
|
parentNames: [...prev.parentNames, item.name],
|
|
200
197
|
}));
|
|
201
198
|
}
|
|
202
|
-
// Desktop: Submenus are handled by hover, don't close menu
|
|
203
199
|
}
|
|
204
200
|
else {
|
|
205
|
-
// No submenu: close menu after executing action (unless keepOpenOnClick is true)
|
|
206
201
|
if (!keepOpenOnClick) {
|
|
207
202
|
handleClose();
|
|
208
203
|
}
|
|
@@ -211,6 +211,29 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
211
211
|
};
|
|
212
212
|
return createPinItems(contextMenuItems);
|
|
213
213
|
}, [contextMenuItems, flattenMenuItems, togglePin, state.items]);
|
|
214
|
+
const getContextMenuItemsWithPinIcons = useCallback(() => {
|
|
215
|
+
const flatItems = flattenMenuItems(contextMenuItems);
|
|
216
|
+
const currentItemIds = new Set(state.items.map(i => i.id));
|
|
217
|
+
const addPinIcons = (items) => {
|
|
218
|
+
return items.map(item => {
|
|
219
|
+
const flatItem = flatItems.find(fi => fi.id === item.id);
|
|
220
|
+
const itemId = flatItem?.id || item.id || '';
|
|
221
|
+
const isPinned = currentItemIds.has(itemId);
|
|
222
|
+
const itemWithPin = {
|
|
223
|
+
...item,
|
|
224
|
+
rightIcon: flatItem ? _jsx(IconPin, { color: isPinned ? 'red' : 'black' }) : undefined,
|
|
225
|
+
onRightIconClick: flatItem ? () => {
|
|
226
|
+
togglePin(flatItem);
|
|
227
|
+
} : undefined,
|
|
228
|
+
};
|
|
229
|
+
if (item.submenu) {
|
|
230
|
+
itemWithPin.submenu = addPinIcons(item.submenu);
|
|
231
|
+
}
|
|
232
|
+
return itemWithPin;
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
return addPinIcons(contextMenuItems);
|
|
236
|
+
}, [contextMenuItems, flattenMenuItems, togglePin, state.items]);
|
|
214
237
|
const handleMouseDown = (e) => {
|
|
215
238
|
if (state.isConfigMode)
|
|
216
239
|
return;
|
|
@@ -269,17 +292,70 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
269
292
|
const handleMouseUp = useCallback(() => {
|
|
270
293
|
setState(s => ({ ...s, isDragging: false }));
|
|
271
294
|
}, []);
|
|
295
|
+
// Touch event handlers for tablet support
|
|
296
|
+
const handleTouchStart = (e) => {
|
|
297
|
+
if (state.isConfigMode)
|
|
298
|
+
return;
|
|
299
|
+
const touch = e.touches[0];
|
|
300
|
+
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
301
|
+
if (containerRect) {
|
|
302
|
+
if (isConstrained) {
|
|
303
|
+
dragOffset.current = {
|
|
304
|
+
x: touch.clientX - containerRect.left - state.position.x,
|
|
305
|
+
y: touch.clientY - containerRect.top - state.position.y,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
dragOffset.current = {
|
|
310
|
+
x: touch.clientX - state.position.x,
|
|
311
|
+
y: touch.clientY - state.position.y,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
setState(s => ({ ...s, isDragging: true }));
|
|
316
|
+
};
|
|
317
|
+
const handleTouchMove = useCallback((e) => {
|
|
318
|
+
if (!state.isDragging || !containerRef.current || !floatingRef.current)
|
|
319
|
+
return;
|
|
320
|
+
const touch = e.touches[0];
|
|
321
|
+
const container = containerRef.current.getBoundingClientRect();
|
|
322
|
+
const floating = floatingRef.current.getBoundingClientRect();
|
|
323
|
+
let newX, newY;
|
|
324
|
+
if (isConstrained) {
|
|
325
|
+
newX = touch.clientX - container.left - dragOffset.current.x;
|
|
326
|
+
newY = touch.clientY - container.top - dragOffset.current.y;
|
|
327
|
+
newX = Math.max(0, Math.min(newX, container.width - floating.width));
|
|
328
|
+
newY = Math.max(0, Math.min(newY, container.height - floating.height));
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
newX = touch.clientX - dragOffset.current.x;
|
|
332
|
+
newY = touch.clientY - dragOffset.current.y;
|
|
333
|
+
newX = Math.max(0, Math.min(newX, window.innerWidth - floating.width));
|
|
334
|
+
newY = Math.max(0, Math.min(newY, window.innerHeight - floating.height));
|
|
335
|
+
}
|
|
336
|
+
setState(s => ({
|
|
337
|
+
...s,
|
|
338
|
+
position: { x: newX, y: newY },
|
|
339
|
+
}));
|
|
340
|
+
}, [state.isDragging, containerRef, isConstrained]);
|
|
341
|
+
const handleTouchEnd = useCallback(() => {
|
|
342
|
+
setState(s => ({ ...s, isDragging: false }));
|
|
343
|
+
}, []);
|
|
272
344
|
useEffect(() => {
|
|
273
345
|
if (state.isDragging) {
|
|
274
346
|
document.addEventListener('mousemove', handleMouseMove);
|
|
275
347
|
document.addEventListener('mouseup', handleMouseUp);
|
|
348
|
+
document.addEventListener('touchmove', handleTouchMove);
|
|
349
|
+
document.addEventListener('touchend', handleTouchEnd);
|
|
276
350
|
return () => {
|
|
277
351
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
278
352
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
353
|
+
document.removeEventListener('touchmove', handleTouchMove);
|
|
354
|
+
document.removeEventListener('touchend', handleTouchEnd);
|
|
279
355
|
};
|
|
280
356
|
}
|
|
281
357
|
return undefined;
|
|
282
|
-
}, [state.isDragging, handleMouseMove, handleMouseUp]);
|
|
358
|
+
}, [state.isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
|
|
283
359
|
// Save to SDKUI_Globals.userSettings only when NOT in config mode (when applying changes)
|
|
284
360
|
useEffect(() => {
|
|
285
361
|
if (state.isConfigMode)
|
|
@@ -507,7 +583,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
507
583
|
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
508
584
|
onClick: toggleOrientation,
|
|
509
585
|
},
|
|
510
|
-
], trigger: "right", children: _jsx(S.GripHandle, { "$orientation": state.orientation, onMouseDown: handleMouseDown, onDoubleClick: handleGripDoubleClick, children: _jsx(IconDraggableDots, {}) }) })) : (_jsx(S.GripHandle, { "$orientation": state.orientation, onMouseDown: handleMouseDown, onDoubleClick: handleGripDoubleClick, children: _jsx(IconDraggableDots, {}) })), _jsx(S.Separator, { "$orientation": state.orientation }), state.items.map((item, index) => {
|
|
586
|
+
], 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) => {
|
|
511
587
|
// Get current state (disabled and onClick) from contextMenuItems
|
|
512
588
|
const currentState = getCurrentItemState(item.id);
|
|
513
589
|
const isDisabled = currentState.disabled || false;
|
|
@@ -519,6 +595,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
519
595
|
currentOnClick();
|
|
520
596
|
}
|
|
521
597
|
}, disabled: state.isConfigMode ? isDisabled && !state.isConfigMode : isDisabled, children: item.icon }) }), state.isConfigMode && (_jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" }))] }, item.id));
|
|
522
|
-
}), !state.isConfigMode && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items:
|
|
598
|
+
}), !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 ? 'Devi aggiungere almeno un item' : SDKUI_Localizator.ApplyAndClose, position: state.orientation === 'horizontal' ? 'right' : 'top', children: _jsx(S.ApplyButton, { onClick: toggleConfigMode, disabled: state.items.length === 0, children: _jsx(IconApply, { fontSize: 20 }) }) })] })] }))] })] }));
|
|
523
599
|
};
|
|
524
600
|
export default TMFloatingMenuBar;
|