@topconsultnpm/sdkui-react 6.20.0-dev1.98 → 6.20.0-dev2.1
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/assets/headergradient.svg +87 -0
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +56 -18
- package/lib/components/NewComponents/ContextMenu/styles.js +13 -34
- package/lib/components/NewComponents/ContextMenu/types.d.ts +8 -2
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +315 -271
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +4 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +6 -2
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +7 -4
- package/lib/components/base/TMButton.js +6 -0
- package/lib/components/base/TMClosableList.js +4 -0
- package/lib/components/base/TMDropDownMenu.js +2 -0
- package/lib/components/base/TMListView.js +1 -1
- package/lib/components/base/TMPanel.d.ts +1 -1
- package/lib/components/base/TMPanel.js +9 -5
- package/lib/components/base/TMPopUp.js +6 -0
- package/lib/components/base/TMToolbarCard.js +2 -0
- package/lib/components/base/TMTreeView.d.ts +2 -1
- package/lib/components/base/TMTreeView.js +33 -26
- package/lib/components/choosers/TMDataListItemChooser.d.ts +2 -0
- package/lib/components/choosers/TMDataListItemChooser.js +8 -2
- package/lib/components/choosers/TMDcmtTypeChooser.d.ts +1 -0
- package/lib/components/choosers/TMDcmtTypeChooser.js +11 -3
- package/lib/components/choosers/TMDistinctValues.js +2 -2
- package/lib/components/choosers/TMDynDataListItemChooser.d.ts +2 -0
- package/lib/components/choosers/TMDynDataListItemChooser.js +8 -2
- package/lib/components/choosers/TMMetadataChooser.d.ts +2 -0
- package/lib/components/choosers/TMMetadataChooser.js +11 -3
- package/lib/components/choosers/TMUserChooser.d.ts +2 -0
- package/lib/components/choosers/TMUserChooser.js +8 -2
- package/lib/components/editors/TMCheckBox.js +2 -0
- package/lib/components/editors/TMDateBox.js +18 -9
- package/lib/components/editors/TMEditorStyled.js +7 -0
- package/lib/components/editors/TMLocalizedTextBox.d.ts +3 -1
- package/lib/components/editors/TMLocalizedTextBox.js +16 -14
- package/lib/components/editors/TMMetadataEditor.d.ts +1 -0
- package/lib/components/editors/TMMetadataEditor.js +4 -4
- package/lib/components/editors/TMMetadataTextBox.d.ts +9 -0
- package/lib/components/editors/TMMetadataTextBox.js +92 -0
- package/lib/components/editors/TMMetadataValues.d.ts +2 -0
- package/lib/components/editors/TMMetadataValues.js +3 -3
- package/lib/components/editors/TMRadioButton.js +2 -0
- package/lib/components/editors/TMTextBox.d.ts +1 -1
- package/lib/components/editors/TMTextBox.js +23 -1
- package/lib/components/editors/TMTextExpression.js +6 -91
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -0
- package/lib/components/features/documents/TMDcmtBlog.js +1 -1
- package/lib/components/features/documents/TMDcmtForm.js +121 -88
- package/lib/components/features/documents/TMDcmtPreview.js +27 -30
- package/lib/components/features/search/TMSearch.d.ts +1 -0
- package/lib/components/features/search/TMSearch.js +7 -3
- package/lib/components/features/search/TMSearchQueryEditor.d.ts +1 -0
- package/lib/components/features/search/TMSearchQueryEditor.js +10 -10
- package/lib/components/features/search/TMSearchQueryPanel.js +24 -10
- package/lib/components/features/search/TMSearchResult.d.ts +1 -0
- package/lib/components/features/search/TMSearchResult.js +171 -11
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +1 -1
- package/lib/components/features/search/TMSearchResultsMenuItems.js +24 -4
- package/lib/components/features/search/TMViewHistoryDcmt.js +45 -50
- package/lib/components/features/tasks/TMTaskForm.js +55 -24
- package/lib/components/features/tasks/TMTasksUtils.js +14 -1
- package/lib/components/features/workflow/TMWorkflowPopup.js +5 -4
- package/lib/components/features/workflow/diagram/DiagramItemComponent.js +2 -0
- package/lib/components/features/workflow/diagram/DiagramItemForm.js +1 -1
- package/lib/components/features/workflow/diagram/WFDiagram.js +1 -1
- package/lib/components/forms/Login/TMLoginForm.js +1 -1
- package/lib/components/grids/TMValidationItemsList.js +6 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContext.js +13 -5
- package/lib/components/query/TMQueryEditor.d.ts +4 -0
- package/lib/components/query/TMQueryEditor.js +14 -10
- package/lib/components/sidebar/TMHeader.js +11 -7
- package/lib/components/sidebar/TMSidebar.d.ts +0 -1
- package/lib/components/sidebar/TMSidebar.js +16 -44
- package/lib/components/sidebar/TMSidebarItem.js +36 -17
- package/lib/helper/SDKUI_Globals.d.ts +3 -0
- package/lib/helper/SDKUI_Globals.js +6 -3
- package/lib/helper/SDKUI_Localizator.d.ts +4 -16
- package/lib/helper/SDKUI_Localizator.js +37 -157
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +3 -0
- package/lib/helper/TMToppyMessage.js +4 -0
- package/lib/helper/TMUtils.d.ts +2 -1
- package/lib/helper/TMUtils.js +13 -1
- package/lib/helper/helpers.d.ts +27 -1
- package/lib/helper/helpers.js +107 -1
- package/lib/helper/queryHelper.d.ts +1 -1
- package/lib/helper/queryHelper.js +24 -1
- package/lib/hooks/useFloatingBarPinnedItems.d.ts +11 -0
- package/lib/hooks/useFloatingBarPinnedItems.js +54 -0
- package/lib/hooks/useMetadataExpression.d.ts +19 -0
- package/lib/hooks/useMetadataExpression.js +99 -0
- package/package.json +56 -56
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
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,
|
|
10
|
+
const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 1, y: 90 }, defaultOrientation = 'horizontal', maxItems = 100, defaultPinnedItems = [], fixedItems = [], enableConfigMode = true, bgColor = undefined, hasContextMenu = true, pinnedItemIds: externalPinnedItemIds, onPinChange, }) => {
|
|
12
11
|
const percentToPixels = (percent, containerSize) => {
|
|
13
12
|
return (percent / 100) * containerSize;
|
|
14
13
|
};
|
|
@@ -29,7 +28,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
29
28
|
};
|
|
30
29
|
const getDefaultConfig = () => ({
|
|
31
30
|
orientation: defaultOrientation,
|
|
32
|
-
savedItemIds:
|
|
31
|
+
savedItemIds: defaultPinnedItems,
|
|
33
32
|
position: defaultPosition,
|
|
34
33
|
});
|
|
35
34
|
const resetFloatingBarSettings = () => {
|
|
@@ -42,17 +41,17 @@ 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
|
-
savedItemIds:
|
|
47
|
+
savedItemIds: defaultPinnedItems,
|
|
49
48
|
position: defaultPosition,
|
|
50
49
|
};
|
|
51
50
|
}
|
|
52
51
|
try {
|
|
53
52
|
const settings = SDKUI_Globals.userSettings.searchSettings.floatingMenuBar;
|
|
54
|
-
// If localStorage is empty (first time), use props as defaults
|
|
55
|
-
if (!settings || !settings.position || !settings.itemIds) {
|
|
53
|
+
// If localStorage is empty (first time) or itemIds is empty array, use props as defaults
|
|
54
|
+
if (!settings || !settings.position || !settings.itemIds || settings.itemIds.length === 0) {
|
|
56
55
|
return getDefaultConfig();
|
|
57
56
|
}
|
|
58
57
|
// Validate position
|
|
@@ -129,8 +128,8 @@ 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,
|
|
132
|
+
verticalDirection: 'down',
|
|
134
133
|
items: [],
|
|
135
134
|
draggedItemIndex: null,
|
|
136
135
|
});
|
|
@@ -138,10 +137,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
138
137
|
const dragOffset = useRef({ x: 0, y: 0 });
|
|
139
138
|
const [dragOverIndex, setDragOverIndex] = useState(null);
|
|
140
139
|
const [pixelPosition, setPixelPosition] = useState({ x: 0, y: 0 }); // Calculated pixel position
|
|
140
|
+
const [isOrientationChanging, setIsOrientationChanging] = useState(false); // Hide bar during orientation transition
|
|
141
141
|
const containerSizeRef = useRef({ width: 0, height: 0 });
|
|
142
|
-
const
|
|
142
|
+
const positionBeforeOrientationChange = useRef(null);
|
|
143
143
|
const floatingBarItemIds = useRef(new Set());
|
|
144
144
|
const floatingBarItemNames = useRef(new Set());
|
|
145
|
+
const isSyncingFromExternal = useRef(false);
|
|
146
|
+
const isLocalChange = useRef(false);
|
|
147
|
+
const dragStartPosition = useRef(null);
|
|
148
|
+
const isItemsInitialized = useRef(false);
|
|
145
149
|
useEffect(() => {
|
|
146
150
|
floatingBarItemIds.current = new Set(state.items.map(i => i.id));
|
|
147
151
|
floatingBarItemNames.current = new Set(state.items.map(i => i.name));
|
|
@@ -201,49 +205,93 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
201
205
|
});
|
|
202
206
|
return result;
|
|
203
207
|
}, [state.items]);
|
|
204
|
-
// Restore items on mount from savedItemIds
|
|
208
|
+
// Restore items on mount (and when contextMenuItems become available) from savedItemIds
|
|
205
209
|
useEffect(() => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (disbaleConfigMode && defaultItems.length > 0) {
|
|
211
|
-
itemsToSet = defaultItems;
|
|
210
|
+
// For enableConfigMode=false: always sync with fixedItems
|
|
211
|
+
if (!enableConfigMode) {
|
|
212
|
+
if (fixedItems.length > 0) {
|
|
213
|
+
setState(s => ({ ...s, items: fixedItems }));
|
|
212
214
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// For enableConfigMode=true: only initialize once
|
|
218
|
+
if (isItemsInitialized.current)
|
|
219
|
+
return;
|
|
220
|
+
// Wait until contextMenuItems is populated
|
|
221
|
+
if (contextMenuItems.length === 0)
|
|
222
|
+
return;
|
|
223
|
+
const flatItems = flattenMenuItems(contextMenuItems);
|
|
224
|
+
if (flatItems.length === 0)
|
|
225
|
+
return;
|
|
226
|
+
let itemsToSet = [];
|
|
227
|
+
if (initialConfig.savedItemIds.length > 0) {
|
|
228
|
+
// Restore items in the saved order from localStorage
|
|
229
|
+
const restoredItems = initialConfig.savedItemIds
|
|
230
|
+
.map((id) => {
|
|
231
|
+
if (id.startsWith('separator-')) {
|
|
232
|
+
return {
|
|
233
|
+
id,
|
|
234
|
+
name: 'Separator',
|
|
235
|
+
icon: _jsx(Separator, {}),
|
|
236
|
+
onClick: () => { },
|
|
237
|
+
isSeparator: true,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return flatItems.find(item => item.id === id);
|
|
241
|
+
})
|
|
242
|
+
.filter((item) => item !== undefined);
|
|
243
|
+
itemsToSet = restoredItems;
|
|
244
|
+
}
|
|
245
|
+
// If still empty, try defaultPinnedItems
|
|
246
|
+
if (itemsToSet.length === 0 && defaultPinnedItems.length > 0) {
|
|
247
|
+
const resolvedDefaultItems = defaultPinnedItems
|
|
248
|
+
.map((id) => flatItems.find(item => item.id === id))
|
|
249
|
+
.filter((item) => item !== undefined);
|
|
250
|
+
itemsToSet = resolvedDefaultItems;
|
|
251
|
+
}
|
|
252
|
+
if (itemsToSet.length > 0) {
|
|
253
|
+
isItemsInitialized.current = true;
|
|
254
|
+
setState(s => ({ ...s, items: itemsToSet }));
|
|
255
|
+
}
|
|
256
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
257
|
+
}, [contextMenuItems, fixedItems, enableConfigMode]);
|
|
258
|
+
// Sync with external pinnedItemIds when they change (from parent component)
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
// Skip sync if a local change was just made (e.g. right-click remove/add separator)
|
|
261
|
+
if (isLocalChange.current) {
|
|
262
|
+
isLocalChange.current = false;
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (externalPinnedItemIds === undefined || !enableConfigMode)
|
|
266
|
+
return;
|
|
267
|
+
// Skip sync if external is empty but we have initialized items (first login case)
|
|
268
|
+
// The parent hook starts with [] but we've initialized with defaults
|
|
269
|
+
if (externalPinnedItemIds.length === 0 && state.items.length > 0)
|
|
270
|
+
return;
|
|
271
|
+
const flatItems = flattenMenuItems(contextMenuItems);
|
|
272
|
+
// Build items from external pinned IDs
|
|
273
|
+
const itemsFromExternal = externalPinnedItemIds
|
|
274
|
+
.map((id) => {
|
|
275
|
+
if (id.startsWith('separator-')) {
|
|
276
|
+
return {
|
|
277
|
+
id,
|
|
278
|
+
name: 'Separator',
|
|
279
|
+
icon: _jsx(Separator, {}),
|
|
280
|
+
onClick: () => { },
|
|
281
|
+
isSeparator: true,
|
|
282
|
+
};
|
|
244
283
|
}
|
|
284
|
+
return flatItems.find(item => item.id === id);
|
|
285
|
+
})
|
|
286
|
+
.filter((item) => item !== undefined);
|
|
287
|
+
// Only update if different
|
|
288
|
+
const currentIds = state.items.map(i => i.id).join(',');
|
|
289
|
+
const newIds = itemsFromExternal.map(i => i.id).join(',');
|
|
290
|
+
if (currentIds !== newIds) {
|
|
291
|
+
isSyncingFromExternal.current = true; // Mark that we're syncing from external
|
|
292
|
+
setState(s => ({ ...s, items: itemsFromExternal }));
|
|
245
293
|
}
|
|
246
|
-
},
|
|
294
|
+
}, [externalPinnedItemIds, contextMenuItems, enableConfigMode, flattenMenuItems]);
|
|
247
295
|
const togglePin = useCallback((item) => {
|
|
248
296
|
setState(s => {
|
|
249
297
|
const isInFloatingBar = s.items.some(i => i.id === item.id);
|
|
@@ -323,40 +371,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
323
371
|
const separator = createSeparator();
|
|
324
372
|
setState(s => ({ ...s, items: [...s.items, separator] }));
|
|
325
373
|
}, [state.items.length, maxItems, createSeparator]);
|
|
326
|
-
const getPinContextMenuItems = useCallback(() => {
|
|
327
|
-
const flatItems = flattenMenuItems(contextMenuItems);
|
|
328
|
-
const currentItemIds = new Set(state.items.map(i => i.id));
|
|
329
|
-
const createPinItems = (items) => {
|
|
330
|
-
return items.map(item => {
|
|
331
|
-
const flatItem = flatItems.find(fi => fi.id === item.id);
|
|
332
|
-
const itemId = flatItem?.id || item.id || '';
|
|
333
|
-
const isAlreadyPinned = currentItemIds.has(itemId);
|
|
334
|
-
const pinItem = {
|
|
335
|
-
...item,
|
|
336
|
-
onClick: item.onClick && !item.submenu ? () => {
|
|
337
|
-
if (flatItem && !isAlreadyPinned) {
|
|
338
|
-
togglePin(flatItem);
|
|
339
|
-
}
|
|
340
|
-
} : undefined,
|
|
341
|
-
disabled: isAlreadyPinned,
|
|
342
|
-
};
|
|
343
|
-
if (item.submenu) {
|
|
344
|
-
pinItem.submenu = createPinItems(item.submenu);
|
|
345
|
-
}
|
|
346
|
-
return pinItem;
|
|
347
|
-
});
|
|
348
|
-
};
|
|
349
|
-
const pinItems = createPinItems(contextMenuItems);
|
|
350
|
-
// Add separator option at the end
|
|
351
|
-
pinItems.push({
|
|
352
|
-
id: 'add-separator',
|
|
353
|
-
name: SDKUI_Localizator.Add + ' separatore',
|
|
354
|
-
icon: _jsx(IconSeparator, {}),
|
|
355
|
-
onClick: addSeparator,
|
|
356
|
-
beginGroup: true
|
|
357
|
-
});
|
|
358
|
-
return pinItems;
|
|
359
|
-
}, [contextMenuItems, flattenMenuItems, togglePin, state.items, addSeparator]);
|
|
360
374
|
const getContextMenuItemsWithPinIcons = useCallback(() => {
|
|
361
375
|
const flatItems = flattenMenuItems(contextMenuItems);
|
|
362
376
|
const currentItemIds = new Set(state.items.map(i => i.id));
|
|
@@ -367,9 +381,12 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
367
381
|
const isPinned = currentItemIds.has(itemId);
|
|
368
382
|
const itemWithPin = {
|
|
369
383
|
...item,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
384
|
+
rightIconProps: flatItem ? {
|
|
385
|
+
icon: _jsx(IconPin, {}),
|
|
386
|
+
activeColor: 'red',
|
|
387
|
+
inactiveColor: 'black',
|
|
388
|
+
isActive: isPinned,
|
|
389
|
+
onClick: () => togglePin(flatItem),
|
|
373
390
|
} : undefined,
|
|
374
391
|
};
|
|
375
392
|
if (item.submenu) {
|
|
@@ -381,8 +398,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
381
398
|
return addPinIcons(contextMenuItems);
|
|
382
399
|
}, [contextMenuItems, flattenMenuItems, togglePin, state.items]);
|
|
383
400
|
const handleMouseDown = (e) => {
|
|
384
|
-
if (state.isConfigMode)
|
|
385
|
-
return;
|
|
386
401
|
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
387
402
|
if (containerRect) {
|
|
388
403
|
// Calculate drag offset based on positioning mode
|
|
@@ -401,11 +416,11 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
401
416
|
};
|
|
402
417
|
}
|
|
403
418
|
}
|
|
419
|
+
// Save starting position to detect if user actually moved the bar
|
|
420
|
+
dragStartPosition.current = { ...pixelPosition };
|
|
404
421
|
setState(s => ({ ...s, isDragging: true }));
|
|
405
422
|
};
|
|
406
423
|
const handleGripDoubleClick = () => {
|
|
407
|
-
if (state.isConfigMode)
|
|
408
|
-
return;
|
|
409
424
|
toggleOrientation();
|
|
410
425
|
};
|
|
411
426
|
const handleMouseMove = useCallback((e) => {
|
|
@@ -440,6 +455,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
440
455
|
x: pixelsToPercent(pixelPosition.x, containerSizeRef.current.width),
|
|
441
456
|
y: pixelsToPercent(pixelPosition.y, containerSizeRef.current.height),
|
|
442
457
|
};
|
|
458
|
+
// Only clear saved position if user actually moved the bar significantly (more than 5 pixels)
|
|
459
|
+
if (dragStartPosition.current) {
|
|
460
|
+
const dx = Math.abs(pixelPosition.x - dragStartPosition.current.x);
|
|
461
|
+
const dy = Math.abs(pixelPosition.y - dragStartPosition.current.y);
|
|
462
|
+
if (dx > 5 || dy > 5) {
|
|
463
|
+
positionBeforeOrientationChange.current = null;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
dragStartPosition.current = null;
|
|
443
467
|
setState(s => ({
|
|
444
468
|
...s,
|
|
445
469
|
isDragging: false,
|
|
@@ -452,8 +476,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
452
476
|
}, [state.isDragging, pixelPosition]);
|
|
453
477
|
// Touch event handlers for tablet support
|
|
454
478
|
const handleTouchStart = (e) => {
|
|
455
|
-
if (state.isConfigMode)
|
|
456
|
-
return;
|
|
457
479
|
const touch = e.touches[0];
|
|
458
480
|
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
459
481
|
if (containerRect) {
|
|
@@ -470,6 +492,8 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
470
492
|
};
|
|
471
493
|
}
|
|
472
494
|
}
|
|
495
|
+
// Save starting position to detect if user actually moved the bar
|
|
496
|
+
dragStartPosition.current = { ...pixelPosition };
|
|
473
497
|
setState(s => ({ ...s, isDragging: true }));
|
|
474
498
|
};
|
|
475
499
|
const handleTouchMove = useCallback((e) => {
|
|
@@ -499,6 +523,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
499
523
|
x: pixelsToPercent(pixelPosition.x, containerSizeRef.current.width),
|
|
500
524
|
y: pixelsToPercent(pixelPosition.y, containerSizeRef.current.height),
|
|
501
525
|
};
|
|
526
|
+
// Only clear saved position if user actually moved the bar significantly (more than 5 pixels)
|
|
527
|
+
if (dragStartPosition.current) {
|
|
528
|
+
const dx = Math.abs(pixelPosition.x - dragStartPosition.current.x);
|
|
529
|
+
const dy = Math.abs(pixelPosition.y - dragStartPosition.current.y);
|
|
530
|
+
if (dx > 5 || dy > 5) {
|
|
531
|
+
positionBeforeOrientationChange.current = null;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
dragStartPosition.current = null;
|
|
502
535
|
setState(s => ({
|
|
503
536
|
...s,
|
|
504
537
|
isDragging: false,
|
|
@@ -524,17 +557,16 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
524
557
|
}
|
|
525
558
|
return undefined;
|
|
526
559
|
}, [state.isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
|
|
527
|
-
// Save to SDKUI_Globals.userSettings
|
|
560
|
+
// Save to SDKUI_Globals.userSettings
|
|
528
561
|
useEffect(() => {
|
|
529
|
-
if (
|
|
530
|
-
return; // Don't save during edit mode
|
|
531
|
-
if (disbaleConfigMode)
|
|
562
|
+
if (!enableConfigMode)
|
|
532
563
|
return; // Don't save if config mode is disabled
|
|
564
|
+
const pinnedIds = state.items.map(item => item.id);
|
|
533
565
|
try {
|
|
534
566
|
// Replace the entire object to trigger the Proxy
|
|
535
567
|
SDKUI_Globals.userSettings.searchSettings.floatingMenuBar = {
|
|
536
568
|
orientation: state.orientation,
|
|
537
|
-
itemIds:
|
|
569
|
+
itemIds: pinnedIds,
|
|
538
570
|
position: state.position,
|
|
539
571
|
positionFormat: 'percentage',
|
|
540
572
|
};
|
|
@@ -542,154 +574,100 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
542
574
|
catch (error) {
|
|
543
575
|
console.error('Failed to save FloatingMenuBar config:', error);
|
|
544
576
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
stateSnapshot.current = {
|
|
550
|
-
items: [...s.items],
|
|
551
|
-
orientation: s.orientation,
|
|
552
|
-
position: { ...s.position },
|
|
553
|
-
};
|
|
554
|
-
return { ...s, isConfigMode: true };
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
// Exiting edit mode (applying changes) - clean up trailing separators and clear snapshot
|
|
558
|
-
stateSnapshot.current = null;
|
|
559
|
-
const cleanedItems = removeTrailingSeparators(s.items);
|
|
560
|
-
return { ...s, isConfigMode: false, items: cleanedItems };
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
};
|
|
564
|
-
// Auto-reposition when entering edit mode to ensure Apply/Undo buttons are visible
|
|
565
|
-
useEffect(() => {
|
|
566
|
-
if (!state.isConfigMode || !floatingRef.current)
|
|
567
|
-
return;
|
|
568
|
-
// Use double requestAnimationFrame to ensure the DOM has fully updated with new buttons
|
|
569
|
-
requestAnimationFrame(() => {
|
|
570
|
-
requestAnimationFrame(() => {
|
|
571
|
-
if (!floatingRef.current)
|
|
572
|
-
return;
|
|
573
|
-
const floating = floatingRef.current.getBoundingClientRect();
|
|
574
|
-
const containerWidth = isConstrained && containerRef.current
|
|
575
|
-
? containerRef.current.getBoundingClientRect().width
|
|
576
|
-
: window.innerWidth;
|
|
577
|
-
const containerHeight = isConstrained && containerRef.current
|
|
578
|
-
? containerRef.current.getBoundingClientRect().height
|
|
579
|
-
: window.innerHeight;
|
|
580
|
-
// Use current pixel position
|
|
581
|
-
let newPixelX = pixelPosition.x;
|
|
582
|
-
let newPixelY = pixelPosition.y;
|
|
583
|
-
let needsUpdate = false;
|
|
584
|
-
// Check horizontal overflow
|
|
585
|
-
if (newPixelX + floating.width > containerWidth) {
|
|
586
|
-
newPixelX = Math.max(0, containerWidth - floating.width);
|
|
587
|
-
needsUpdate = true;
|
|
588
|
-
}
|
|
589
|
-
// Check vertical overflow
|
|
590
|
-
if (newPixelY + floating.height > containerHeight) {
|
|
591
|
-
newPixelY = Math.max(0, containerHeight - floating.height);
|
|
592
|
-
needsUpdate = true;
|
|
593
|
-
}
|
|
594
|
-
if (needsUpdate) {
|
|
595
|
-
// Update pixel position immediately
|
|
596
|
-
setPixelPosition({ x: newPixelX, y: newPixelY });
|
|
597
|
-
// Convert to percentage for state
|
|
598
|
-
const newPercentagePosition = {
|
|
599
|
-
x: pixelsToPercent(newPixelX, containerWidth),
|
|
600
|
-
y: pixelsToPercent(newPixelY, containerHeight),
|
|
601
|
-
};
|
|
602
|
-
setState(s => ({
|
|
603
|
-
...s,
|
|
604
|
-
position: newPercentagePosition,
|
|
605
|
-
}));
|
|
606
|
-
// Update snapshot position to the corrected position so Undo restores to visible position
|
|
607
|
-
if (stateSnapshot.current) {
|
|
608
|
-
stateSnapshot.current.position = newPercentagePosition;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
});
|
|
613
|
-
}, [state.isConfigMode, state.orientation, isConstrained, state.items, pixelPosition.x, pixelPosition.y]);
|
|
614
|
-
const handleUndo = () => {
|
|
615
|
-
if (stateSnapshot.current) {
|
|
616
|
-
setState(s => ({
|
|
617
|
-
...s,
|
|
618
|
-
items: [...stateSnapshot.current.items],
|
|
619
|
-
orientation: stateSnapshot.current.orientation,
|
|
620
|
-
position: { ...stateSnapshot.current.position },
|
|
621
|
-
isConfigMode: true,
|
|
622
|
-
}));
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
// Check if current state has changed from snapshot
|
|
626
|
-
const hasChanges = () => {
|
|
627
|
-
if (!stateSnapshot.current)
|
|
628
|
-
return false;
|
|
629
|
-
// Check if items have changed (different IDs or different order)
|
|
630
|
-
const currentIds = state.items.map(i => i.id).join(',');
|
|
631
|
-
const snapshotIds = stateSnapshot.current.items.map(i => i.id).join(',');
|
|
632
|
-
return currentIds !== snapshotIds;
|
|
633
|
-
};
|
|
634
|
-
const handleClose = () => {
|
|
635
|
-
// If all items removed, exit without asking and restore last items
|
|
636
|
-
if (state.items.length === 0 && stateSnapshot.current) {
|
|
637
|
-
setState(s => ({
|
|
638
|
-
...s,
|
|
639
|
-
items: [...stateSnapshot.current.items],
|
|
640
|
-
orientation: stateSnapshot.current.orientation,
|
|
641
|
-
position: { ...stateSnapshot.current.position },
|
|
642
|
-
isConfigMode: false,
|
|
643
|
-
}));
|
|
644
|
-
stateSnapshot.current = null;
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
// If no changes, simply exit config mode
|
|
648
|
-
if (!hasChanges()) {
|
|
649
|
-
stateSnapshot.current = null;
|
|
650
|
-
const cleanedItems = removeTrailingSeparators(state.items);
|
|
651
|
-
setState(s => ({ ...s, isConfigMode: false, items: cleanedItems }));
|
|
652
|
-
return;
|
|
577
|
+
// Notify parent about pin changes only if not syncing from external
|
|
578
|
+
// This prevents infinite loop: external change -> sync -> save -> onPinChange -> external change
|
|
579
|
+
if (!isSyncingFromExternal.current) {
|
|
580
|
+
onPinChange?.(pinnedIds);
|
|
653
581
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
message: 'Perderai le tue modifiche, sei sicuro?',
|
|
657
|
-
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
658
|
-
onButtonClick: (buttonName) => {
|
|
659
|
-
if (buttonName === ButtonNames.YES && stateSnapshot.current) {
|
|
660
|
-
// Restore snapshot and exit config mode
|
|
661
|
-
setState(s => ({
|
|
662
|
-
...s,
|
|
663
|
-
items: [...stateSnapshot.current.items],
|
|
664
|
-
orientation: stateSnapshot.current.orientation,
|
|
665
|
-
position: { ...stateSnapshot.current.position },
|
|
666
|
-
isConfigMode: false,
|
|
667
|
-
}));
|
|
668
|
-
stateSnapshot.current = null;
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
});
|
|
672
|
-
};
|
|
582
|
+
isSyncingFromExternal.current = false; // Reset the flag
|
|
583
|
+
}, [state.orientation, state.items, state.position, enableConfigMode, onPinChange]);
|
|
673
584
|
const toggleOrientation = () => {
|
|
674
585
|
const newOrientation = state.orientation === 'horizontal' ? 'vertical' : 'horizontal';
|
|
586
|
+
// When switching from vertical back to horizontal, restore the original position
|
|
587
|
+
// Use visibility hiding only for this case to prevent flicker during position restoration
|
|
588
|
+
if (state.orientation === 'vertical' && newOrientation === 'horizontal') {
|
|
589
|
+
if (positionBeforeOrientationChange.current) {
|
|
590
|
+
setIsOrientationChanging(true); // Hide only when restoring position
|
|
591
|
+
const savedPosition = positionBeforeOrientationChange.current.position;
|
|
592
|
+
const savedPixelPosition = positionBeforeOrientationChange.current.pixelPosition;
|
|
593
|
+
setPixelPosition(savedPixelPosition);
|
|
594
|
+
setState(s => ({
|
|
595
|
+
...s,
|
|
596
|
+
orientation: newOrientation,
|
|
597
|
+
verticalDirection: 'down',
|
|
598
|
+
position: savedPosition,
|
|
599
|
+
}));
|
|
600
|
+
positionBeforeOrientationChange.current = null;
|
|
601
|
+
// Show the bar after the state has been applied
|
|
602
|
+
requestAnimationFrame(() => {
|
|
603
|
+
setIsOrientationChanging(false);
|
|
604
|
+
});
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// When switching to vertical, save current position and check if we need to expand upward
|
|
609
|
+
// Use opacity hiding (doesn't affect focus unlike visibility:hidden)
|
|
675
610
|
if (state.orientation === 'horizontal' && newOrientation === 'vertical') {
|
|
611
|
+
setIsOrientationChanging(true);
|
|
612
|
+
// Save the current position before changing orientation
|
|
613
|
+
positionBeforeOrientationChange.current = {
|
|
614
|
+
position: { ...state.position },
|
|
615
|
+
pixelPosition: { ...pixelPosition },
|
|
616
|
+
};
|
|
676
617
|
if (floatingRef.current) {
|
|
677
618
|
const floating = floatingRef.current.getBoundingClientRect();
|
|
678
|
-
const
|
|
679
|
-
|
|
619
|
+
const containerHeight = isConstrained && containerRef.current
|
|
620
|
+
? containerRef.current.getBoundingClientRect().height
|
|
621
|
+
: window.innerHeight;
|
|
622
|
+
// Estimate vertical height (horizontal width becomes vertical height)
|
|
623
|
+
const estimatedVerticalHeight = floating.width;
|
|
624
|
+
if (estimatedVerticalHeight > containerHeight - 70) {
|
|
680
625
|
ShowAlert({
|
|
681
626
|
mode: 'warning',
|
|
682
627
|
title: 'Troppi elementi',
|
|
683
628
|
message: 'Ci sono troppi elementi nella barra mobile per la modalità verticale.',
|
|
684
629
|
duration: 4000,
|
|
685
630
|
});
|
|
631
|
+
positionBeforeOrientationChange.current = null; // Clear saved position since we're not changing
|
|
632
|
+
setIsOrientationChanging(false); // Show bar again since we're not changing
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
// Check if we're in the bottom part of the screen and don't have enough space below
|
|
636
|
+
const spaceBelow = containerHeight - floating.bottom;
|
|
637
|
+
const spaceAbove = floating.top;
|
|
638
|
+
const needsVerticalSpace = estimatedVerticalHeight - floating.height; // Additional space needed
|
|
639
|
+
// If not enough space below but enough space above, expand upward
|
|
640
|
+
if (spaceBelow < needsVerticalSpace && spaceAbove >= needsVerticalSpace) {
|
|
641
|
+
// Calculate the new Y position so the bottom of the bar stays at the same place
|
|
642
|
+
const currentBottom = pixelPosition.y + floating.height;
|
|
643
|
+
const newPixelY = currentBottom - estimatedVerticalHeight;
|
|
644
|
+
setState(s => ({
|
|
645
|
+
...s,
|
|
646
|
+
orientation: newOrientation,
|
|
647
|
+
verticalDirection: 'up',
|
|
648
|
+
}));
|
|
649
|
+
// Update pixel position and percentage after orientation change
|
|
650
|
+
requestAnimationFrame(() => {
|
|
651
|
+
requestAnimationFrame(() => {
|
|
652
|
+
const newY = Math.max(0, newPixelY);
|
|
653
|
+
setPixelPosition(prev => ({ ...prev, y: newY }));
|
|
654
|
+
const newPercentY = pixelsToPercent(newY, containerHeight);
|
|
655
|
+
setState(s => ({
|
|
656
|
+
...s,
|
|
657
|
+
position: { ...s.position, y: newPercentY },
|
|
658
|
+
}));
|
|
659
|
+
setIsOrientationChanging(false);
|
|
660
|
+
});
|
|
661
|
+
});
|
|
686
662
|
return;
|
|
687
663
|
}
|
|
688
664
|
}
|
|
689
665
|
}
|
|
666
|
+
// Default case: just change orientation without special positioning
|
|
690
667
|
setState(s => ({
|
|
691
668
|
...s,
|
|
692
669
|
orientation: newOrientation,
|
|
670
|
+
verticalDirection: 'down',
|
|
693
671
|
}));
|
|
694
672
|
requestAnimationFrame(() => {
|
|
695
673
|
requestAnimationFrame(() => {
|
|
@@ -715,25 +693,97 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
715
693
|
}));
|
|
716
694
|
}
|
|
717
695
|
}
|
|
696
|
+
setIsOrientationChanging(false);
|
|
718
697
|
});
|
|
719
698
|
});
|
|
720
699
|
};
|
|
721
700
|
const removeItem = (itemId) => {
|
|
701
|
+
isLocalChange.current = true;
|
|
722
702
|
setState(s => ({
|
|
723
703
|
...s,
|
|
724
704
|
items: s.items.filter(item => item.id !== itemId),
|
|
725
705
|
}));
|
|
726
706
|
};
|
|
707
|
+
const getItemRightClickMenuItems = useCallback((item, index) => {
|
|
708
|
+
const hasSeparatorOnRight = index < state.items.length - 1 && state.items[index + 1]?.isSeparator;
|
|
709
|
+
const hasSeparatorOnLeft = index > 0 && state.items[index - 1]?.isSeparator;
|
|
710
|
+
return [
|
|
711
|
+
{
|
|
712
|
+
name: 'Rimuovi',
|
|
713
|
+
icon: _jsx(IconDelete, { fontSize: 16 }),
|
|
714
|
+
onClick: () => removeItem(item.id),
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
name: 'Aggiungi separatore a sinistra',
|
|
718
|
+
icon: _jsx(IconSeparator, { fontSize: 16 }),
|
|
719
|
+
disabled: hasSeparatorOnLeft,
|
|
720
|
+
onClick: () => {
|
|
721
|
+
isLocalChange.current = true;
|
|
722
|
+
const separator = createSeparator();
|
|
723
|
+
setState(s => {
|
|
724
|
+
const newItems = [...s.items];
|
|
725
|
+
newItems.splice(index, 0, separator);
|
|
726
|
+
return { ...s, items: newItems };
|
|
727
|
+
});
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
name: 'Aggiungi separatore a destra',
|
|
732
|
+
icon: _jsx(IconSeparator, { style: { transform: 'rotate(-90deg)' }, fontSize: 16 }),
|
|
733
|
+
disabled: hasSeparatorOnRight,
|
|
734
|
+
onClick: () => {
|
|
735
|
+
isLocalChange.current = true;
|
|
736
|
+
const separator = createSeparator();
|
|
737
|
+
setState(s => {
|
|
738
|
+
const newItems = [...s.items];
|
|
739
|
+
newItems.splice(index + 1, 0, separator);
|
|
740
|
+
return { ...s, items: newItems };
|
|
741
|
+
});
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
beginGroup: true,
|
|
746
|
+
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
747
|
+
icon: _jsx(IconRotate, { fontSize: 16 }),
|
|
748
|
+
onClick: toggleOrientation,
|
|
749
|
+
},
|
|
750
|
+
];
|
|
751
|
+
}, [state.items, state.orientation, removeItem, createSeparator, toggleOrientation]);
|
|
752
|
+
const getSeparatorRightClickMenuItems = useCallback((index) => {
|
|
753
|
+
return [
|
|
754
|
+
{
|
|
755
|
+
name: 'Rimuovi',
|
|
756
|
+
icon: _jsx(IconDelete, { fontSize: 16 }),
|
|
757
|
+
onClick: () => removeItem(state.items[index].id),
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: 'Aggiungi separatore a sinistra',
|
|
761
|
+
icon: _jsx(IconSeparator, { fontSize: 16 }),
|
|
762
|
+
disabled: true,
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
name: 'Aggiungi separatore a destra',
|
|
766
|
+
icon: _jsx(IconSeparator, { style: { transform: 'rotate(-90deg)' }, fontSize: 16 }),
|
|
767
|
+
disabled: true,
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
beginGroup: true,
|
|
771
|
+
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
772
|
+
icon: _jsx(IconRotate, { fontSize: 16 }),
|
|
773
|
+
onClick: toggleOrientation,
|
|
774
|
+
},
|
|
775
|
+
];
|
|
776
|
+
}, [state.items, state.orientation, removeItem, toggleOrientation]);
|
|
727
777
|
// Drag and drop for reordering
|
|
728
778
|
const handleDragStart = (e, index) => {
|
|
729
|
-
if (!
|
|
779
|
+
if (!enableConfigMode)
|
|
730
780
|
return;
|
|
731
781
|
e.dataTransfer.effectAllowed = 'move';
|
|
732
782
|
e.dataTransfer.setData('text/plain', index.toString());
|
|
733
783
|
setState(s => ({ ...s, draggedItemIndex: index }));
|
|
734
784
|
};
|
|
735
785
|
const handleDragEnter = (e, index) => {
|
|
736
|
-
if (!
|
|
786
|
+
if (!enableConfigMode)
|
|
737
787
|
return;
|
|
738
788
|
e.preventDefault();
|
|
739
789
|
if (state.draggedItemIndex !== index) {
|
|
@@ -741,13 +791,13 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
741
791
|
}
|
|
742
792
|
};
|
|
743
793
|
const handleDragOver = (e) => {
|
|
744
|
-
if (!
|
|
794
|
+
if (!enableConfigMode)
|
|
745
795
|
return;
|
|
746
796
|
e.preventDefault();
|
|
747
797
|
e.dataTransfer.dropEffect = 'move';
|
|
748
798
|
};
|
|
749
799
|
const handleDragLeave = (_e, index) => {
|
|
750
|
-
if (!
|
|
800
|
+
if (!enableConfigMode)
|
|
751
801
|
return;
|
|
752
802
|
// Only clear if we're actually leaving this specific item
|
|
753
803
|
if (dragOverIndex === index) {
|
|
@@ -755,7 +805,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
755
805
|
}
|
|
756
806
|
};
|
|
757
807
|
const handleDrop = (e, dropIndex) => {
|
|
758
|
-
if (!
|
|
808
|
+
if (!enableConfigMode || state.draggedItemIndex === null)
|
|
759
809
|
return;
|
|
760
810
|
e.preventDefault();
|
|
761
811
|
e.stopPropagation();
|
|
@@ -764,6 +814,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
764
814
|
setDragOverIndex(null);
|
|
765
815
|
return;
|
|
766
816
|
}
|
|
817
|
+
isLocalChange.current = true;
|
|
767
818
|
const newItems = [...state.items];
|
|
768
819
|
const [draggedItem] = newItems.splice(dragIndex, 1);
|
|
769
820
|
newItems.splice(dropIndex, 0, draggedItem);
|
|
@@ -778,44 +829,37 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
|
|
|
778
829
|
setState(s => ({ ...s, draggedItemIndex: null }));
|
|
779
830
|
setDragOverIndex(null);
|
|
780
831
|
};
|
|
781
|
-
return (_jsxs(
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
if (currentOnClick) {
|
|
814
|
-
currentOnClick();
|
|
815
|
-
}
|
|
816
|
-
}, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) }), state.isConfigMode && (_jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" }))] })) }, item.id));
|
|
817
|
-
}), !state.isConfigMode && !disbaleConfigMode && 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
|
|
818
|
-
? 'Devi aggiungere almeno un item'
|
|
819
|
-
: 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 }) }) })] })] }))] })] }));
|
|
832
|
+
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: [
|
|
833
|
+
{
|
|
834
|
+
name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
|
|
835
|
+
icon: _jsx(IconRotate, { fontSize: 16, style: { transform: state.orientation === 'horizontal' ? 'rotate(90deg)' : 'rotate(0deg)' } }),
|
|
836
|
+
onClick: toggleOrientation,
|
|
837
|
+
},
|
|
838
|
+
], 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) => {
|
|
839
|
+
// Handle separator items
|
|
840
|
+
if (item.isSeparator) {
|
|
841
|
+
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));
|
|
842
|
+
}
|
|
843
|
+
// Get current state (disabled and onClick) from contextMenuItems
|
|
844
|
+
const currentState = getCurrentItemState(item.id);
|
|
845
|
+
// Prefer currentState.disabled if contextMenuItems has items, otherwise use item.disabled
|
|
846
|
+
const isDisabled = (contextMenuItems.length > 0 && currentState.disabled !== undefined)
|
|
847
|
+
? currentState.disabled === true
|
|
848
|
+
: item.disabled === true;
|
|
849
|
+
const currentOnClick = currentState.onClick || item.onClick; // Fallback to stored onClick if not found
|
|
850
|
+
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: () => {
|
|
851
|
+
if (isDisabled)
|
|
852
|
+
return;
|
|
853
|
+
if (currentOnClick) {
|
|
854
|
+
currentOnClick();
|
|
855
|
+
}
|
|
856
|
+
}, 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: () => {
|
|
857
|
+
if (isDisabled)
|
|
858
|
+
return;
|
|
859
|
+
if (currentOnClick) {
|
|
860
|
+
currentOnClick();
|
|
861
|
+
}
|
|
862
|
+
}, disabled: isDisabled, "$isActive": item.isToggle, children: item.icon }) }))) }, item.id));
|
|
863
|
+
}), enableConfigMode && hasContextMenu && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: getContextMenuItemsWithPinIcons(), trigger: "left", keepOpenOnClick: false, children: _jsx(S.ContextMenuButton, { children: _jsx(IconMenuVertical, {}) }) }))] }));
|
|
820
864
|
};
|
|
821
865
|
export default TMFloatingMenuBar;
|