@topconsultnpm/sdkui-react 6.20.0-dev1.49 → 6.20.0-dev1.50

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 || trigger !== 'right')
140
140
  return;
141
141
  const touch = e.touches[0];
142
142
  touchStartPos.current = { x: touch.clientX, y: touch.clientY };
@@ -158,7 +158,7 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
158
158
  }, 500);
159
159
  };
160
160
  const handleTouchMove = (e) => {
161
- if (!isIOS || !touchStartPos.current)
161
+ if (!isIOS || trigger !== 'right' || !touchStartPos.current)
162
162
  return;
163
163
  const touch = e.touches[0];
164
164
  const moveThreshold = 10; // pixels
@@ -174,7 +174,7 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
174
174
  }
175
175
  };
176
176
  const handleTouchEnd = () => {
177
- if (!isIOS)
177
+ if (!isIOS || trigger !== 'right')
178
178
  return;
179
179
  if (longPressTimeoutRef.current) {
180
180
  clearTimeout(longPressTimeoutRef.current);
@@ -198,9 +198,6 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
198
198
  }
199
199
  }
200
200
  else {
201
- // Close menu if item has onClick (actual action) or keepOpenOnClick is false
202
- // This allows pin icon clicks (onRightIconClick) to keep menu open while
203
- // clicking the item itself closes it
204
201
  if (item.onClick || !keepOpenOnClick) {
205
202
  handleClose();
206
203
  }
@@ -7,6 +7,24 @@ import * as S from './styles';
7
7
  import { IconAdd, IconApply, IconMenuVertical, IconPin, IconUndo, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
8
8
  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" }) }));
9
9
  const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 100, y: 100 }, maxItems = 100, }) => {
10
+ const percentToPixels = (percent, containerSize) => {
11
+ return (percent / 100) * containerSize;
12
+ };
13
+ const pixelsToPercent = (pixels, containerSize) => {
14
+ return (pixels / containerSize) * 100;
15
+ };
16
+ const isPixelFormat = (pos) => {
17
+ return pos.x > 100 || pos.y > 100;
18
+ };
19
+ const migrateToPercentage = (pixelPos) => {
20
+ const container = containerRef.current?.getBoundingClientRect();
21
+ const containerWidth = isConstrained && container ? container.width : window.innerWidth;
22
+ const containerHeight = isConstrained && container ? container.height : window.innerHeight;
23
+ return {
24
+ x: pixelsToPercent(pixelPos.x, containerWidth),
25
+ y: pixelsToPercent(pixelPos.y, containerHeight),
26
+ };
27
+ };
10
28
  const getDefaultConfig = () => ({
11
29
  orientation: 'horizontal',
12
30
  savedItemIds: [],
@@ -33,10 +51,10 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
33
51
  const hasValidPosition = settings.position &&
34
52
  typeof settings.position.x === 'number' &&
35
53
  typeof settings.position.y === 'number' &&
36
- !isNaN(settings.position.x) &&
37
- !isNaN(settings.position.y) &&
38
- isFinite(settings.position.x) &&
39
- isFinite(settings.position.y);
54
+ !Number.isNaN(settings.position.x) &&
55
+ !Number.isNaN(settings.position.y) &&
56
+ Number.isFinite(settings.position.x) &&
57
+ Number.isFinite(settings.position.y);
40
58
  if (!hasValidPosition) {
41
59
  console.warn('FloatingMenuBar: Invalid position, resetting to defaults');
42
60
  resetFloatingBarSettings();
@@ -66,12 +84,23 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
66
84
  return getDefaultConfig();
67
85
  }
68
86
  }
69
- // Check if position was actually saved (not just the default class value)
70
- const hasSavedPosition = settings.position.x !== 100 || settings.position.y !== 100 || validItemIds.length > 0;
87
+ // Migrate old pixel-based position to percentage-based
88
+ let finalPosition = settings.position;
89
+ if (isPixelFormat(settings.position) || settings.positionFormat === 'pixels') {
90
+ console.log('FloatingMenuBar: Migrating pixel-based position to percentage-based');
91
+ finalPosition = migrateToPercentage(settings.position);
92
+ // Save migrated position immediately
93
+ SDKUI_Globals.userSettings.searchSettings.floatingMenuBar = {
94
+ orientation: validOrientation,
95
+ itemIds: validItemIds,
96
+ position: finalPosition,
97
+ positionFormat: 'percentage',
98
+ };
99
+ }
71
100
  return {
72
101
  orientation: validOrientation,
73
102
  savedItemIds: validItemIds,
74
- position: hasSavedPosition ? settings.position : defaultPosition,
103
+ position: finalPosition,
75
104
  };
76
105
  }
77
106
  catch (error) {
@@ -88,7 +117,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
88
117
  };
89
118
  const initialConfig = loadConfig();
90
119
  const [state, setState] = useState({
91
- position: initialConfig.position,
120
+ position: initialConfig.position, // Stored as percentage
92
121
  isDragging: false,
93
122
  isConfigMode: false,
94
123
  orientation: initialConfig.orientation,
@@ -98,6 +127,8 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
98
127
  const floatingRef = useRef(null);
99
128
  const dragOffset = useRef({ x: 0, y: 0 });
100
129
  const [dragOverIndex, setDragOverIndex] = useState(null);
130
+ const [pixelPosition, setPixelPosition] = useState({ x: 0, y: 0 }); // Calculated pixel position
131
+ const containerSizeRef = useRef({ width: 0, height: 0 });
101
132
  const stateSnapshot = useRef(null);
102
133
  const floatingBarItemIds = useRef(new Set());
103
134
  const floatingBarItemNames = useRef(new Set());
@@ -105,6 +136,39 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
105
136
  floatingBarItemIds.current = new Set(state.items.map(i => i.id));
106
137
  floatingBarItemNames.current = new Set(state.items.map(i => i.name));
107
138
  }, [state.items]);
139
+ // Calculate pixel position from percentage when container size or position changes
140
+ useEffect(() => {
141
+ const updatePixelPosition = () => {
142
+ if (!containerRef.current || !floatingRef.current)
143
+ return;
144
+ const container = containerRef.current.getBoundingClientRect();
145
+ const floating = floatingRef.current.getBoundingClientRect();
146
+ const containerWidth = isConstrained ? container.width : window.innerWidth;
147
+ const containerHeight = isConstrained ? container.height : window.innerHeight;
148
+ containerSizeRef.current = { width: containerWidth, height: containerHeight };
149
+ let newX = percentToPixels(state.position.x, containerWidth);
150
+ let newY = percentToPixels(state.position.y, containerHeight);
151
+ newX = Math.max(0, Math.min(newX, containerWidth - floating.width));
152
+ newY = Math.max(0, Math.min(newY, containerHeight - floating.height));
153
+ setPixelPosition({ x: newX, y: newY });
154
+ };
155
+ updatePixelPosition();
156
+ const resizeObserver = new ResizeObserver(() => {
157
+ updatePixelPosition();
158
+ });
159
+ if (containerRef.current) {
160
+ resizeObserver.observe(containerRef.current);
161
+ }
162
+ if (!isConstrained) {
163
+ window.addEventListener('resize', updatePixelPosition);
164
+ }
165
+ return () => {
166
+ resizeObserver.disconnect();
167
+ if (!isConstrained) {
168
+ window.removeEventListener('resize', updatePixelPosition);
169
+ }
170
+ };
171
+ }, [state.position, isConstrained]);
108
172
  const flattenMenuItems = useCallback((items, parentPath = '') => {
109
173
  const result = [];
110
174
  items.forEach((item, index) => {
@@ -243,15 +307,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
243
307
  if (isConstrained) {
244
308
  // For absolute positioning, offset is relative to container
245
309
  dragOffset.current = {
246
- x: e.clientX - containerRect.left - state.position.x,
247
- y: e.clientY - containerRect.top - state.position.y,
310
+ x: e.clientX - containerRect.left - pixelPosition.x,
311
+ y: e.clientY - containerRect.top - pixelPosition.y,
248
312
  };
249
313
  }
250
314
  else {
251
315
  // For fixed positioning, offset is relative to viewport
252
316
  dragOffset.current = {
253
- x: e.clientX - state.position.x,
254
- y: e.clientY - state.position.y,
317
+ x: e.clientX - pixelPosition.x,
318
+ y: e.clientY - pixelPosition.y,
255
319
  };
256
320
  }
257
321
  }
@@ -284,14 +348,26 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
284
348
  newX = Math.max(0, Math.min(newX, window.innerWidth - floating.width));
285
349
  newY = Math.max(0, Math.min(newY, window.innerHeight - floating.height));
286
350
  }
287
- setState(s => ({
288
- ...s,
289
- position: { x: newX, y: newY },
290
- }));
351
+ // Update pixel position directly during drag
352
+ setPixelPosition({ x: newX, y: newY });
291
353
  }, [state.isDragging, containerRef, isConstrained]);
292
354
  const handleMouseUp = useCallback(() => {
293
- setState(s => ({ ...s, isDragging: false }));
294
- }, []);
355
+ if (state.isDragging && containerSizeRef.current.width > 0) {
356
+ // Convert final pixel position to percentage before updating state
357
+ const percentagePosition = {
358
+ x: pixelsToPercent(pixelPosition.x, containerSizeRef.current.width),
359
+ y: pixelsToPercent(pixelPosition.y, containerSizeRef.current.height),
360
+ };
361
+ setState(s => ({
362
+ ...s,
363
+ isDragging: false,
364
+ position: percentagePosition,
365
+ }));
366
+ }
367
+ else {
368
+ setState(s => ({ ...s, isDragging: false }));
369
+ }
370
+ }, [state.isDragging, pixelPosition]);
295
371
  // Touch event handlers for tablet support
296
372
  const handleTouchStart = (e) => {
297
373
  if (state.isConfigMode)
@@ -301,14 +377,14 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
301
377
  if (containerRect) {
302
378
  if (isConstrained) {
303
379
  dragOffset.current = {
304
- x: touch.clientX - containerRect.left - state.position.x,
305
- y: touch.clientY - containerRect.top - state.position.y,
380
+ x: touch.clientX - containerRect.left - pixelPosition.x,
381
+ y: touch.clientY - containerRect.top - pixelPosition.y,
306
382
  };
307
383
  }
308
384
  else {
309
385
  dragOffset.current = {
310
- x: touch.clientX - state.position.x,
311
- y: touch.clientY - state.position.y,
386
+ x: touch.clientX - pixelPosition.x,
387
+ y: touch.clientY - pixelPosition.y,
312
388
  };
313
389
  }
314
390
  }
@@ -333,14 +409,24 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
333
409
  newX = Math.max(0, Math.min(newX, window.innerWidth - floating.width));
334
410
  newY = Math.max(0, Math.min(newY, window.innerHeight - floating.height));
335
411
  }
336
- setState(s => ({
337
- ...s,
338
- position: { x: newX, y: newY },
339
- }));
412
+ setPixelPosition({ x: newX, y: newY });
340
413
  }, [state.isDragging, containerRef, isConstrained]);
341
414
  const handleTouchEnd = useCallback(() => {
342
- setState(s => ({ ...s, isDragging: false }));
343
- }, []);
415
+ if (state.isDragging && containerSizeRef.current.width > 0) {
416
+ const percentagePosition = {
417
+ x: pixelsToPercent(pixelPosition.x, containerSizeRef.current.width),
418
+ y: pixelsToPercent(pixelPosition.y, containerSizeRef.current.height),
419
+ };
420
+ setState(s => ({
421
+ ...s,
422
+ isDragging: false,
423
+ position: percentagePosition,
424
+ }));
425
+ }
426
+ else {
427
+ setState(s => ({ ...s, isDragging: false }));
428
+ }
429
+ }, [state.isDragging, pixelPosition]);
344
430
  useEffect(() => {
345
431
  if (state.isDragging) {
346
432
  document.addEventListener('mousemove', handleMouseMove);
@@ -366,6 +452,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
366
452
  orientation: state.orientation,
367
453
  itemIds: state.items.map(item => item.id),
368
454
  position: state.position,
455
+ positionFormat: 'percentage',
369
456
  };
370
457
  }
371
458
  catch (error) {
@@ -375,7 +462,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
375
462
  const toggleConfigMode = () => {
376
463
  setState(s => {
377
464
  if (!s.isConfigMode) {
378
- // Entering edit mode - save snapshot
379
465
  stateSnapshot.current = {
380
466
  items: [...s.items],
381
467
  orientation: s.orientation,
@@ -570,7 +656,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
570
656
  setState(s => ({ ...s, draggedItemIndex: null }));
571
657
  setDragOverIndex(null);
572
658
  };
573
- return (_jsxs(_Fragment, { children: [_jsx(S.Overlay, { "$visible": state.isConfigMode }), _jsxs(S.FloatingContainer, { ref: floatingRef, "$x": state.position.x, "$y": state.position.y, "$orientation": state.orientation, "$isDragging": state.isDragging, "$isConfigMode": state.isConfigMode, "$isConstrained": isConstrained, onContextMenu: (e) => e.preventDefault(), children: [!state.isConfigMode ? (_jsx(ContextMenu, { items: [
659
+ return (_jsxs(_Fragment, { children: [_jsx(S.Overlay, { "$visible": state.isConfigMode }), _jsxs(S.FloatingContainer, { ref: floatingRef, "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$isDragging": state.isDragging, "$isConfigMode": state.isConfigMode, "$isConstrained": isConstrained, onContextMenu: (e) => e.preventDefault(), children: [!state.isConfigMode ? (_jsx(ContextMenu, { items: [
574
660
  {
575
661
  name: SDKUI_Localizator.Configure,
576
662
  onClick: () => {
@@ -70,6 +70,7 @@ export declare class FloatingMenuBarSettings {
70
70
  x: number;
71
71
  y: number;
72
72
  };
73
+ positionFormat?: 'pixels' | 'percentage';
73
74
  }
74
75
  export declare class ArchivingSettings {
75
76
  mruTIDs: number[];
@@ -106,7 +106,7 @@ export class FloatingMenuBarSettings {
106
106
  constructor() {
107
107
  this.orientation = 'horizontal';
108
108
  this.itemIds = ['rel-det', 'rel-mst', 'dl'];
109
- this.position = { x: 10, y: globalThis.window?.innerHeight ? globalThis.window.innerHeight - 215 : 100 };
109
+ this.position = { x: 1, y: 90 };
110
110
  }
111
111
  }
112
112
  export class ArchivingSettings {
@@ -204,7 +204,7 @@ export declare class SDKUI_Localizator {
204
204
  static get DownloadXMLAttachments(): string;
205
205
  static get Draft(): string;
206
206
  static get Drafts(): string;
207
- static get DropFileHere(): "Ziehen Sie Ihre Datei hierher" | "Drop your file here" | "Suelta tu archivo aquí" | "Déposez votre fichier ici" | "Solte seu arquivo aqui";
207
+ static get DropFileHere(): "Ziehen Sie Ihre Datei hierher" | "Drop your file here" | "Suelta tu archivo aquí" | "Déposez votre fichier ici" | "Solte seu arquivo aqui" | "Trascina qui il tuo file";
208
208
  static get DropFileToShare(): string;
209
209
  static get Duplicate(): "Duplikat" | "Duplicate" | "Duplicar" | "Dupliquer" | "Duplicado" | "Duplica";
210
210
  static get Duplicate_ConfirmFor1(): "Möchten Sie '{{0}}' duplizieren?" | "Are you sure you want to duplicate '{{0}}'?" | "¿Estás seguro de que deseas duplicar '{{0}}'?" | "Êtes-vous sûr de vouloir dupliquer '{{0}}'?" | "Você tem certeza que deseja duplicar '{{0}}'?" | "Sei sicuro di voler duplicare '{{0}}'?";
@@ -1996,7 +1996,7 @@ export class SDKUI_Localizator {
1996
1996
  case CultureIDs.Es_ES: return "Suelta tu archivo aquí";
1997
1997
  case CultureIDs.Fr_FR: return "Déposez votre fichier ici";
1998
1998
  case CultureIDs.Pt_PT: return "Solte seu arquivo aqui";
1999
- default: return "Drop your file here";
1999
+ default: return "Trascina qui il tuo file";
2000
2000
  }
2001
2001
  }
2002
2002
  static get DropFileToShare() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.20.0-dev1.49",
3
+ "version": "6.20.0-dev1.50",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",