@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 || trigger !== 'right')
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: contextMenuItems, trigger: "left", 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 }) }) })] })] }))] })] }));
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.20.0-dev1.46",
3
+ "version": "6.20.0-dev1.48",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",