@topconsultnpm/sdkui-react 6.20.0-dev1.123 → 6.20.0-dev1.124

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.
@@ -239,11 +239,11 @@ export const Submenu = styled.div `
239
239
  position: fixed;
240
240
  left: ${props => {
241
241
  const spaceOnRight = globalThis.innerWidth - props.$parentRect.right;
242
- return spaceOnRight > 240 ? `${props.$parentRect.right - 8}px` : 'auto';
242
+ return spaceOnRight > 240 ? `${props.$parentRect.right - 4}px` : 'auto';
243
243
  }};
244
244
  right: ${props => {
245
245
  const spaceOnRight = globalThis.innerWidth - props.$parentRect.right;
246
- return spaceOnRight > 240 ? 'auto' : `${globalThis.innerWidth - props.$parentRect.left + 8}px`;
246
+ return spaceOnRight > 240 ? 'auto' : `${globalThis.innerWidth - props.$parentRect.left + 4}px`;
247
247
  }};
248
248
  /* Vertical positioning: Each submenu independently decides direction based on its own space */
249
249
  top: ${props => {
@@ -266,25 +266,25 @@ export const Submenu = styled.div `
266
266
  backdrop-filter: blur(10px);
267
267
  border: 1px solid rgba(0, 0, 0, 0.06);
268
268
 
269
- /* Add invisible padding to bridge the gap - works for both sides */
269
+ /* Invisible hover bridge on the LEFT side (for submenus opening left) */
270
270
  &::before {
271
271
  content: '';
272
272
  position: absolute;
273
273
  right: 100%;
274
- top: 0;
275
- bottom: 0;
276
- width: 15px;
274
+ top: -20px;
275
+ bottom: -20px;
276
+ width: 25px;
277
277
  background: transparent;
278
278
  }
279
279
 
280
- /* Bridge on the right side for nested submenus */
280
+ /* Invisible hover bridge on the RIGHT side (for submenus opening right) */
281
281
  &::after {
282
282
  content: '';
283
283
  position: absolute;
284
284
  left: 100%;
285
- top: 0;
286
- bottom: 0;
287
- width: 15px;
285
+ top: -20px;
286
+ bottom: -20px;
287
+ width: 25px;
288
288
  background: transparent;
289
289
  }
290
290
 
@@ -328,30 +328,7 @@ export const Submenu = styled.div `
328
328
  &::-webkit-scrollbar-thumb:hover {
329
329
  background: rgba(0, 0, 0, 0.3);
330
330
  }
331
- ` : `
332
- /* Fallback max-height for submenus that fit */
333
- max-height: calc(100vh - 40px);
334
- overflow-y: auto;
335
- overflow-x: hidden;
336
-
337
- &::-webkit-scrollbar {
338
- width: 8px;
339
- }
340
-
341
- &::-webkit-scrollbar-track {
342
- background: rgba(0, 0, 0, 0.05);
343
- border-radius: 4px;
344
- }
345
-
346
- &::-webkit-scrollbar-thumb {
347
- background: rgba(0, 0, 0, 0.2);
348
- border-radius: 4px;
349
- }
350
-
351
- &::-webkit-scrollbar-thumb:hover {
352
- background: rgba(0, 0, 0, 0.3);
353
- }
354
- `}
331
+ ` : ``}
355
332
 
356
333
  [data-theme='dark'] & {
357
334
  &::-webkit-scrollbar-track {
@@ -7,7 +7,7 @@ import * as S from './styles';
7
7
  import { IconDelete, IconMenuVertical, IconPin, IconRotate, IconSeparator, SDKUI_Globals } from '../../../helper';
8
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" }) }));
9
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" }) }));
10
- const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 1, y: 90 }, defaultOrientation = 'horizontal', maxItems = 100, contextMenuDefaultPinnedIds = [], defaultItems = [], enableConfigMode = true, bgColor = undefined, hasContextMenu = true, pinnedItemIds: externalPinnedItemIds, onPinChange, }) => {
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, }) => {
11
11
  const percentToPixels = (percent, containerSize) => {
12
12
  return (percent / 100) * containerSize;
13
13
  };
@@ -28,7 +28,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
28
28
  };
29
29
  const getDefaultConfig = () => ({
30
30
  orientation: defaultOrientation,
31
- savedItemIds: contextMenuDefaultPinnedIds,
31
+ savedItemIds: defaultPinnedItems,
32
32
  position: defaultPosition,
33
33
  });
34
34
  const resetFloatingBarSettings = () => {
@@ -44,14 +44,14 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
44
44
  if (!enableConfigMode) {
45
45
  return {
46
46
  orientation: defaultOrientation,
47
- savedItemIds: contextMenuDefaultPinnedIds,
47
+ savedItemIds: defaultPinnedItems,
48
48
  position: defaultPosition,
49
49
  };
50
50
  }
51
51
  try {
52
52
  const settings = SDKUI_Globals.userSettings.searchSettings.floatingMenuBar;
53
- // If localStorage is empty (first time), use props as defaults
54
- 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) {
55
55
  return getDefaultConfig();
56
56
  }
57
57
  // Validate position
@@ -145,6 +145,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
145
145
  const isSyncingFromExternal = useRef(false);
146
146
  const isLocalChange = useRef(false);
147
147
  const dragStartPosition = useRef(null);
148
+ const isItemsInitialized = useRef(false);
148
149
  useEffect(() => {
149
150
  floatingBarItemIds.current = new Set(state.items.map(i => i.id));
150
151
  floatingBarItemNames.current = new Set(state.items.map(i => i.name));
@@ -204,49 +205,56 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
204
205
  });
205
206
  return result;
206
207
  }, [state.items]);
207
- // Restore items on mount from savedItemIds
208
+ // Restore items on mount (and when contextMenuItems become available) from savedItemIds
208
209
  useEffect(() => {
209
- if (contextMenuItems.length > 0 || initialConfig.savedItemIds.length > 0 || defaultItems.length > 0) {
210
- const flatItems = flattenMenuItems(contextMenuItems);
211
- let itemsToSet = [];
212
- // If enableConfigMode is false and defaultItems provided, use only defaultItems
213
- if (!enableConfigMode && defaultItems.length > 0) {
214
- itemsToSet = defaultItems;
215
- }
216
- else if (enableConfigMode && initialConfig.savedItemIds.length > 0) {
217
- // Restore items in the saved order from localStorage (only if config mode is enabled)
218
- const restoredItems = initialConfig.savedItemIds
219
- .map((id) => {
220
- if (id.startsWith('separator-')) {
221
- return {
222
- id,
223
- name: 'Separator',
224
- icon: _jsx(Separator, {}),
225
- onClick: () => { },
226
- isSeparator: true,
227
- };
228
- }
229
- return flatItems.find(item => item.id === id);
230
- })
231
- .filter((item) => item !== undefined);
232
- itemsToSet = restoredItems;
233
- }
234
- else if (contextMenuDefaultPinnedIds.length > 0) {
235
- // First time: Use contextMenuDefaultPinnedIds from props to find items by ID
236
- const defaultPinnedItems = contextMenuDefaultPinnedIds
237
- .map((id) => flatItems.find(item => item.id === id))
238
- .filter((item) => item !== undefined);
239
- itemsToSet = defaultPinnedItems;
240
- }
241
- else if (defaultItems.length > 0) {
242
- // Use defaultItems as fallback
243
- itemsToSet = defaultItems;
244
- }
245
- if (itemsToSet.length > 0) {
246
- setState(s => ({ ...s, items: itemsToSet }));
210
+ // For enableConfigMode=false: always sync with fixedItems
211
+ if (!enableConfigMode) {
212
+ if (fixedItems.length > 0) {
213
+ setState(s => ({ ...s, items: fixedItems }));
247
214
  }
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;
248
251
  }
249
- }, enableConfigMode ? [] : [defaultItems]); // Update when defaultItems change if config mode is disabled
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]);
250
258
  // Sync with external pinnedItemIds when they change (from parent component)
251
259
  useEffect(() => {
252
260
  // Skip sync if a local change was just made (e.g. right-click remove/add separator)
@@ -256,6 +264,10 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
256
264
  }
257
265
  if (externalPinnedItemIds === undefined || !enableConfigMode)
258
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;
259
271
  const flatItems = flattenMenuItems(contextMenuItems);
260
272
  // Build items from external pinned IDs
261
273
  const itemsFromExternal = externalPinnedItemIds
@@ -16,9 +16,9 @@ export interface TMFloatingMenuBarProps {
16
16
  isConstrained?: boolean;
17
17
  defaultPosition?: Position;
18
18
  maxItems?: number;
19
- contextMenuDefaultPinnedIds?: string[];
19
+ defaultPinnedItems?: string[];
20
20
  defaultOrientation?: 'horizontal' | 'vertical';
21
- defaultItems?: TMFloatingMenuItem[];
21
+ fixedItems?: TMFloatingMenuItem[];
22
22
  enableConfigMode?: boolean;
23
23
  bgColor?: string;
24
24
  hasContextMenu?: boolean;
@@ -614,7 +614,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
614
614
  _jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile || currentSearchResults.length === 1, onSelectionChanged: onSearchResultSelectionChanged }) })
615
615
  :
616
616
  _jsx(_Fragment, {}), _jsx(TMLayoutItem, { children: _jsx(TMSearchResultGrid, { showSearch: showSearch, fromDTD: fromDTD, allUsers: allUsers, inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], lastUpdateSearchTime: lastUpdateSearchTime, openInOffice: openInOffice, onDblClick: () => openFormHandler(LayoutModes.Update), floatingMenuItems: floatingMenuItems, onSelectionChanged: (items) => { setSelectedItems(items); }, onVisibleItemChanged: setVisibleItems, onFocusedItemChanged: setFocusedItem, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync(inputDcmts, downloadType, downloadMode, onFileOpened, confirmAttachments), showExportForm: showExportForm, onCloseExportForm: onCloseExportForm }) })] }), allowFloatingBar && showFloatingBar && deviceType !== DeviceType.MOBILE &&
617
- _jsx(TMFloatingMenuBar, { containerRef: floatingBarContainerRef, contextMenuItems: floatingMenuItems, isConstrained: true, defaultPosition: { x: 1, y: 88 }, contextMenuDefaultPinnedIds: ['rel-det', 'rel-mst', 'dl'], defaultOrientation: 'horizontal', hasContextMenu: false, pinnedItemIds: pinnedItemIds, onPinChange: setPinnedItemIds })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { TID: focusedItem?.TID, DID: focusedItem?.DID, deviceType: deviceType, onCompleted: onWFOperationCompleted, onClose: () => setShowMoreInfoPopup(false) }), isOpenBatchUpdate && _jsx(TMBatchUpdateForm, { isModal: true, titleModal: `${SDKUI_Localizator.BatchUpdate} (${getSelectionDcmtInfo().length} documenti selezionati)`, inputDcmts: getSelectionDcmtInfo(), TID: focusedItem ? focusedItem?.TID : selectedItems[0]?.TID, DID: focusedItem ? focusedItem?.DID : selectedItems[0]?.DID, onBack: () => {
617
+ _jsx(TMFloatingMenuBar, { containerRef: floatingBarContainerRef, contextMenuItems: floatingMenuItems, isConstrained: true, defaultPosition: { x: 1, y: 88 }, defaultPinnedItems: ['rel-det', 'rel-mst', 'dl'], defaultOrientation: 'horizontal', hasContextMenu: false, pinnedItemIds: pinnedItemIds, onPinChange: setPinnedItemIds })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { TID: focusedItem?.TID, DID: focusedItem?.DID, deviceType: deviceType, onCompleted: onWFOperationCompleted, onClose: () => setShowMoreInfoPopup(false) }), isOpenBatchUpdate && _jsx(TMBatchUpdateForm, { isModal: true, titleModal: `${SDKUI_Localizator.BatchUpdate} (${getSelectionDcmtInfo().length} documenti selezionati)`, inputDcmts: getSelectionDcmtInfo(), TID: focusedItem ? focusedItem?.TID : selectedItems[0]?.TID, DID: focusedItem ? focusedItem?.DID : selectedItems[0]?.DID, onBack: () => {
618
618
  setIsOpenBatchUpdate(false);
619
619
  }, onSavedCallbackAsync: async () => {
620
620
  setIsOpenBatchUpdate(false);
@@ -1822,7 +1822,7 @@ const WFDiagram = ({ xmlDiagramString, currentSetID, allowEdit = true, onDiagram
1822
1822
  }, [isFullScreen]);
1823
1823
  const diagramContent = (_jsxs(CanvasContainer, { onDoubleClick: handleCanvasDoubleClick, children: [_jsx("input", { ref: fileInputRef, type: "file", accept: ".xml" // Filtra per file XML
1824
1824
  , onChange: handleFileChange, style: { display: 'none' } }), SDK_Globals.tmSession?.SessionDescr?.appModuleID === AppModules.SURFER ?
1825
- _jsx(TMFloatingMenuBar, { containerRef: diagramRef, defaultPosition: { x: 45, y: 85 }, enableConfigMode: false, defaultItems: [
1825
+ _jsx(TMFloatingMenuBar, { containerRef: diagramRef, defaultPosition: { x: 45, y: 85 }, enableConfigMode: false, fixedItems: [
1826
1826
  { icon: _jsx(IconZoomIn, {}), name: SDKUI_Localizator.ZoomIn, disabled: isAutoZoomEnabled, onClick: () => { handleZoomIn(); }, id: 'zoom-in', isPinned: true },
1827
1827
  { icon: _jsx(IconZoomOut, {}), name: SDKUI_Localizator.ZoomOut, disabled: isAutoZoomEnabled, onClick: () => { handleZoomOut(); }, id: 'zoom-out', isPinned: true },
1828
1828
  { icon: _jsx(IconZoomAuto, {}), name: 'AutoZoom', onClick: () => { handleToggleAutoZoom(); }, id: 'zoom-auto', isPinned: true, isToggle: isAutoZoomEnabled },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.20.0-dev1.123",
3
+ "version": "6.20.0-dev1.124",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",