@topconsultnpm/sdkui-react 6.20.0-dev1.3 → 6.20.0-dev1.31
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/components/NewComponents/ContextMenu/TMContextMenu.js +29 -11
- package/lib/components/NewComponents/ContextMenu/hooks.d.ts +1 -0
- package/lib/components/NewComponents/ContextMenu/hooks.js +8 -4
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +5 -1
- package/lib/components/NewComponents/ContextMenu/styles.js +56 -28
- package/lib/components/NewComponents/ContextMenu/types.d.ts +10 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +39 -51
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +8 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +29 -19
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +0 -1
- package/lib/components/base/TMAccordion.js +2 -2
- package/lib/components/base/TMCustomButton.js +61 -17
- package/lib/components/base/TMDataGrid.d.ts +5 -2
- package/lib/components/base/TMDataGrid.js +98 -7
- package/lib/components/editors/TMHtmlEditor.js +1 -1
- package/lib/components/editors/TMMetadataValues.js +20 -2
- package/lib/components/features/documents/TMDcmtBlog.d.ts +1 -7
- package/lib/components/features/documents/TMDcmtBlog.js +29 -2
- package/lib/components/features/documents/TMDcmtForm.js +268 -168
- package/lib/components/features/documents/TMDcmtPreview.js +37 -66
- package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +8 -0
- package/lib/components/features/search/{TMSearchResultCheckoutInfoForm.js → TMDcmtCheckoutInfoForm.js} +6 -11
- package/lib/components/features/search/TMSearchQueryPanel.js +13 -12
- package/lib/components/features/search/TMSearchResult.js +66 -117
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +3 -3
- package/lib/components/features/search/TMSearchResultsMenuItems.js +163 -180
- package/lib/components/features/search/TMSignatureInfoContent.d.ts +6 -0
- package/lib/components/features/search/TMSignatureInfoContent.js +140 -0
- package/lib/components/forms/Login/LoginValidatorService.d.ts +2 -0
- package/lib/components/forms/Login/LoginValidatorService.js +7 -2
- package/lib/components/forms/Login/TMLoginForm.js +34 -6
- package/lib/css/tm-sdkui.css +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +12 -14
- package/lib/helper/SDKUI_Globals.js +8 -0
- package/lib/helper/SDKUI_Localizator.d.ts +8 -0
- package/lib/helper/SDKUI_Localizator.js +98 -0
- package/lib/helper/TMPdfViewer.d.ts +8 -0
- package/lib/helper/TMPdfViewer.js +187 -0
- package/lib/helper/TMUtils.d.ts +3 -1
- package/lib/helper/TMUtils.js +51 -0
- package/lib/helper/checkinCheckoutManager.d.ts +85 -0
- package/lib/helper/checkinCheckoutManager.js +348 -0
- package/lib/helper/devextremeCustomMessages.d.ts +30 -0
- package/lib/helper/devextremeCustomMessages.js +30 -0
- package/lib/helper/helpers.js +7 -1
- package/lib/helper/index.d.ts +2 -0
- package/lib/helper/index.js +2 -0
- package/lib/helper/queryHelper.js +29 -0
- package/lib/hooks/useCheckInOutOperations.d.ts +28 -0
- package/lib/hooks/useCheckInOutOperations.js +223 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +5 -2
- package/lib/components/NewComponents/Notification/Notification.d.ts +0 -4
- package/lib/components/NewComponents/Notification/Notification.js +0 -60
- package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +0 -8
- package/lib/components/NewComponents/Notification/NotificationContainer.js +0 -33
- package/lib/components/NewComponents/Notification/index.d.ts +0 -2
- package/lib/components/NewComponents/Notification/index.js +0 -2
- package/lib/components/NewComponents/Notification/styles.d.ts +0 -21
- package/lib/components/NewComponents/Notification/styles.js +0 -180
- package/lib/components/NewComponents/Notification/types.d.ts +0 -18
- package/lib/components/NewComponents/Notification/types.js +0 -1
- package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +0 -8
- package/lib/helper/cicoHelper.d.ts +0 -31
- package/lib/helper/cicoHelper.js +0 -155
|
@@ -2,7 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useState, useRef, useEffect } from 'react';
|
|
3
3
|
import * as S from './styles';
|
|
4
4
|
import { useIsMobile, useMenuPosition } from './hooks';
|
|
5
|
-
|
|
5
|
+
import { IconArrowLeft } from '../../../helper';
|
|
6
|
+
const TMContextMenu = ({ items, trigger = 'right', children, externalControl }) => {
|
|
6
7
|
const [menuState, setMenuState] = useState({
|
|
7
8
|
visible: false,
|
|
8
9
|
position: { x: 0, y: 0 },
|
|
@@ -14,16 +15,33 @@ const TMContextMenu = ({ items, trigger = 'right', children }) => {
|
|
|
14
15
|
const menuRef = useRef(null);
|
|
15
16
|
const triggerRef = useRef(null);
|
|
16
17
|
const submenuTimeoutRef = useRef(null);
|
|
17
|
-
const { openLeft, openUp } = useMenuPosition(menuRef, menuState.position);
|
|
18
|
+
const { openLeft, openUp, isCalculated } = useMenuPosition(menuRef, menuState.position);
|
|
18
19
|
const handleClose = () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
if (externalControl) {
|
|
21
|
+
externalControl.onClose();
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
setMenuState(prev => ({
|
|
25
|
+
...prev,
|
|
26
|
+
visible: false,
|
|
27
|
+
submenuStack: [items],
|
|
28
|
+
parentNames: [],
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
25
31
|
setHoveredSubmenus([]);
|
|
26
32
|
};
|
|
33
|
+
// Sync with external control when provided
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (externalControl) {
|
|
36
|
+
setMenuState(prev => ({
|
|
37
|
+
...prev,
|
|
38
|
+
visible: externalControl.visible,
|
|
39
|
+
position: externalControl.position,
|
|
40
|
+
submenuStack: [items],
|
|
41
|
+
parentNames: [],
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
}, [externalControl, items]);
|
|
27
45
|
useEffect(() => {
|
|
28
46
|
if (!menuState.visible)
|
|
29
47
|
return;
|
|
@@ -173,15 +191,15 @@ const TMContextMenu = ({ items, trigger = 'right', children }) => {
|
|
|
173
191
|
// if (item.disabled) return;
|
|
174
192
|
item.onRightIconClick?.();
|
|
175
193
|
};
|
|
176
|
-
return (_jsxs(S.MenuItem, { "$disabled": item.disabled, "$hasSubmenu": !!item.submenu && item.submenu.length > 0, "$beginGroup": item.beginGroup, onMouseDown: handleClick, onMouseEnter: (e) => !isMobile && handleMouseEnter(item, e, depth + 1), onMouseLeave: () => !isMobile && handleMouseLeave(depth + 1), children: [_jsxs(S.MenuItemContent, { children: [item.icon && _jsx(S.IconWrapper, { children: item.icon }), _jsx(S.MenuItemName, { children: item.name })] }), item.rightIcon && item.onRightIconClick && (_jsx(S.RightIconButton, { onClick: handleRightIconClick, onMouseDown: (e) => e.stopPropagation(), "aria-label": `Action for ${item.name}`, children: item.rightIcon })), item.submenu && item.submenu.length > 0 && (_jsx(S.SubmenuIndicator, { "$isMobile": isMobile, children: isMobile ? '›' : '▸' }))] }, itemKey));
|
|
194
|
+
return (_jsxs(S.MenuItem, { "$disabled": item.disabled, "$hasSubmenu": !!item.submenu && item.submenu.length > 0, "$beginGroup": item.beginGroup, onMouseDown: handleClick, onMouseEnter: (e) => !isMobile && handleMouseEnter(item, e, depth + 1), onMouseLeave: () => !isMobile && handleMouseLeave(depth + 1), title: item.tooltip, children: [_jsxs(S.MenuItemContent, { children: [item.icon && _jsx(S.IconWrapper, { children: item.icon }), _jsx(S.MenuItemName, { children: item.name })] }), item.rightIcon && item.onRightIconClick && (_jsx(S.RightIconButton, { onClick: handleRightIconClick, onMouseDown: (e) => e.stopPropagation(), "aria-label": `Action for ${item.name}`, children: item.rightIcon })), item.submenu && item.submenu.length > 0 && (_jsx(S.SubmenuIndicator, { "$isMobile": isMobile, children: isMobile ? '›' : '▸' }))] }, itemKey));
|
|
177
195
|
});
|
|
178
196
|
};
|
|
179
197
|
const currentMenu = menuState.submenuStack.at(-1) || items;
|
|
180
198
|
const currentParentName = menuState.parentNames.at(-1) || '';
|
|
181
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, onClick: handleClick, onKeyDown: (e) => {
|
|
199
|
+
return (_jsxs(_Fragment, { children: [!externalControl && children && (_jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, onClick: handleClick, onKeyDown: (e) => {
|
|
182
200
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
183
201
|
handleClick(e);
|
|
184
202
|
}
|
|
185
|
-
}, role: "button", tabIndex: 0, style: { display: 'inline-block' }, children: children }), menuState.visible && (_jsxs(_Fragment, { children: [_jsx(S.Overlay, { onClick: handleClose }), _jsxs(S.MenuContainer, { ref: menuRef, "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children:
|
|
203
|
+
}, role: "button", tabIndex: 0, style: { display: 'inline-block' }, children: children })), menuState.visible && (_jsxs(_Fragment, { children: [_jsx(S.Overlay, { onClick: handleClose }), _jsxs(S.MenuContainer, { ref: menuRef, "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }))] }));
|
|
186
204
|
};
|
|
187
205
|
export default TMContextMenu;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from 'react';
|
|
1
|
+
import { useState, useEffect, useLayoutEffect, useRef } from 'react';
|
|
2
2
|
export const useIsMobile = () => {
|
|
3
3
|
const [isMobile, setIsMobile] = useState(false);
|
|
4
4
|
useEffect(() => {
|
|
@@ -31,9 +31,12 @@ export const useClickOutside = (callback) => {
|
|
|
31
31
|
};
|
|
32
32
|
export const useMenuPosition = (menuRef, position) => {
|
|
33
33
|
const [adjustedPosition, setAdjustedPosition] = useState({ openLeft: false, openUp: false });
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const [isCalculated, setIsCalculated] = useState(false);
|
|
35
|
+
useLayoutEffect(() => {
|
|
36
|
+
if (!menuRef.current) {
|
|
37
|
+
setIsCalculated(false);
|
|
36
38
|
return;
|
|
39
|
+
}
|
|
37
40
|
const menuRect = menuRef.current.getBoundingClientRect();
|
|
38
41
|
const viewportWidth = window.innerWidth;
|
|
39
42
|
const viewportHeight = window.innerHeight;
|
|
@@ -43,6 +46,7 @@ export const useMenuPosition = (menuRef, position) => {
|
|
|
43
46
|
openLeft: spaceRight < menuRect.width + 20,
|
|
44
47
|
openUp: spaceBottom < menuRect.height + 20,
|
|
45
48
|
});
|
|
49
|
+
setIsCalculated(true);
|
|
46
50
|
}, [position, menuRef]);
|
|
47
|
-
return adjustedPosition;
|
|
51
|
+
return { ...adjustedPosition, isCalculated };
|
|
48
52
|
};
|
|
@@ -3,6 +3,8 @@ export declare const MenuContainer: import("styled-components/dist/types").IStyl
|
|
|
3
3
|
$y: number;
|
|
4
4
|
$openLeft: boolean;
|
|
5
5
|
$openUp: boolean;
|
|
6
|
+
$isPositioned: boolean;
|
|
7
|
+
$externalControl?: boolean;
|
|
6
8
|
}>> & string;
|
|
7
9
|
export declare const MenuItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
8
10
|
$disabled?: boolean;
|
|
@@ -12,7 +14,9 @@ export declare const MenuItem: import("styled-components/dist/types").IStyledCom
|
|
|
12
14
|
export declare const MenuItemContent: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
13
15
|
export declare const IconWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>> & string;
|
|
14
16
|
export declare const MenuItemName: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, never>> & string;
|
|
15
|
-
export declare const RightIconButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
|
|
17
|
+
export declare const RightIconButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("styled-components").FastOmit<import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
|
|
18
|
+
ref?: ((instance: HTMLButtonElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLButtonElement> | null | undefined;
|
|
19
|
+
}>, never>, never>> & string;
|
|
16
20
|
export declare const SubmenuIndicator: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, {
|
|
17
21
|
$isMobile?: boolean;
|
|
18
22
|
}>> & string;
|
|
@@ -36,6 +36,13 @@ export const MenuContainer = styled.div `
|
|
|
36
36
|
animation: ${fadeIn} 0.15s ease-out;
|
|
37
37
|
backdrop-filter: blur(10px);
|
|
38
38
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
39
|
+
opacity: ${props => props.$isPositioned ? 1 : 0};
|
|
40
|
+
transition: opacity 0.05s ease-in;
|
|
41
|
+
|
|
42
|
+
/* Reset color inheritance from parent with !important to override panel header styles */
|
|
43
|
+
& *:not(svg):not(.right-icon-btn):not(.right-icon-btn *) {
|
|
44
|
+
color: #1a1a1a !important;
|
|
45
|
+
}
|
|
39
46
|
|
|
40
47
|
[data-theme='dark'] & {
|
|
41
48
|
background: #2a2a2a;
|
|
@@ -43,6 +50,20 @@ export const MenuContainer = styled.div `
|
|
|
43
50
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4),
|
|
44
51
|
0 2px 8px rgba(0, 0, 0, 0.3);
|
|
45
52
|
}
|
|
53
|
+
|
|
54
|
+
[data-theme='dark'] & *:not(svg):not(.right-icon-btn):not(.right-icon-btn *) {
|
|
55
|
+
color: #e0e0e0 !important;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
${props => props.$externalControl && `
|
|
59
|
+
@media (max-width: 768px) {
|
|
60
|
+
left: 100px !important;
|
|
61
|
+
right: 100px !important;
|
|
62
|
+
max-width: calc(100vw - 200px);
|
|
63
|
+
width: auto;
|
|
64
|
+
min-width: auto;
|
|
65
|
+
}
|
|
66
|
+
`}
|
|
46
67
|
`;
|
|
47
68
|
export const MenuItem = styled.div `
|
|
48
69
|
display: flex;
|
|
@@ -50,7 +71,6 @@ export const MenuItem = styled.div `
|
|
|
50
71
|
justify-content: space-between;
|
|
51
72
|
padding: 4px 12px;
|
|
52
73
|
cursor: ${props => props.$disabled ? 'not-allowed' : 'pointer'};
|
|
53
|
-
opacity: ${props => props.$disabled ? 0.4 : 1};
|
|
54
74
|
transition: all 0.15s ease;
|
|
55
75
|
position: relative;
|
|
56
76
|
user-select: none;
|
|
@@ -63,12 +83,26 @@ export const MenuItem = styled.div `
|
|
|
63
83
|
padding-top: 8px;
|
|
64
84
|
`}
|
|
65
85
|
|
|
86
|
+
/* Apply opacity only to direct children except right-icon-btn */
|
|
87
|
+
& > *:not(.right-icon-btn) {
|
|
88
|
+
opacity: ${props => props.$disabled ? 0.4 : 1};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Right icon button hidden by default, shown on hover */
|
|
92
|
+
& .right-icon-btn {
|
|
93
|
+
cursor: pointer !important;
|
|
94
|
+
}
|
|
95
|
+
|
|
66
96
|
&:hover {
|
|
67
97
|
${props => !props.$disabled && `
|
|
68
98
|
background: linear-gradient(90deg, #f0f7ff 0%, #e6f2ff 100%);
|
|
69
99
|
color: #0066cc;
|
|
70
|
-
padding-left: 14px;
|
|
71
100
|
`}
|
|
101
|
+
|
|
102
|
+
/* Show right icon on hover */
|
|
103
|
+
& .right-icon-btn {
|
|
104
|
+
opacity: 1 !important;
|
|
105
|
+
}
|
|
72
106
|
}
|
|
73
107
|
|
|
74
108
|
&:active {
|
|
@@ -99,8 +133,8 @@ export const MenuItem = styled.div `
|
|
|
99
133
|
}
|
|
100
134
|
|
|
101
135
|
@media (max-width: 768px) {
|
|
102
|
-
padding:
|
|
103
|
-
font-size:
|
|
136
|
+
padding: 4px 10px;
|
|
137
|
+
font-size: 12px;
|
|
104
138
|
}
|
|
105
139
|
`;
|
|
106
140
|
export const MenuItemContent = styled.div `
|
|
@@ -124,23 +158,23 @@ export const MenuItemName = styled.span `
|
|
|
124
158
|
overflow-wrap: break-word;
|
|
125
159
|
line-height: 1.4;
|
|
126
160
|
`;
|
|
127
|
-
export const RightIconButton = styled.button
|
|
161
|
+
export const RightIconButton = styled.button.attrs({
|
|
162
|
+
className: 'right-icon-btn'
|
|
163
|
+
}) `
|
|
128
164
|
display: flex;
|
|
129
165
|
align-items: center;
|
|
130
166
|
justify-content: center;
|
|
131
167
|
background: transparent;
|
|
132
168
|
border: none;
|
|
133
|
-
cursor: pointer;
|
|
169
|
+
cursor: pointer !important;
|
|
134
170
|
padding: 4px 8px;
|
|
135
171
|
margin-left: 8px;
|
|
136
172
|
border-radius: 6px;
|
|
137
|
-
color: inherit;
|
|
138
173
|
font-size: 14px;
|
|
139
|
-
opacity: 0
|
|
140
|
-
transition:
|
|
174
|
+
opacity: 0 !important;
|
|
175
|
+
transition: opacity 0.15s ease, background 0.15s ease, transform 0.15s ease;
|
|
141
176
|
|
|
142
177
|
&:hover {
|
|
143
|
-
opacity: 1;
|
|
144
178
|
background: rgba(0, 0, 0, 0.05);
|
|
145
179
|
transform: scale(1.1);
|
|
146
180
|
}
|
|
@@ -223,17 +257,26 @@ export const Submenu = styled.div `
|
|
|
223
257
|
background: transparent;
|
|
224
258
|
}
|
|
225
259
|
|
|
260
|
+
/* Reset color inheritance from parent with !important to override panel header styles */
|
|
261
|
+
& *:not(svg):not(.right-icon-btn):not(.right-icon-btn *) {
|
|
262
|
+
color: #1a1a1a !important;
|
|
263
|
+
}
|
|
264
|
+
|
|
226
265
|
[data-theme='dark'] & {
|
|
227
266
|
background: #2a2a2a;
|
|
228
267
|
border-color: rgba(255, 255, 255, 0.1);
|
|
229
268
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4),
|
|
230
269
|
0 2px 8px rgba(0, 0, 0, 0.3);
|
|
231
270
|
}
|
|
271
|
+
|
|
272
|
+
[data-theme='dark'] & *:not(svg):not(.right-icon-btn):not(.right-icon-btn *) {
|
|
273
|
+
color: #e0e0e0 !important;
|
|
274
|
+
}
|
|
232
275
|
`;
|
|
233
276
|
export const MobileMenuHeader = styled.div `
|
|
234
277
|
display: flex;
|
|
235
278
|
align-items: center;
|
|
236
|
-
padding:
|
|
279
|
+
padding: 4px 8px;
|
|
237
280
|
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
238
281
|
margin-bottom: 8px;
|
|
239
282
|
gap: 12px;
|
|
@@ -249,33 +292,18 @@ export const BackButton = styled.button `
|
|
|
249
292
|
display: flex;
|
|
250
293
|
align-items: center;
|
|
251
294
|
justify-content: center;
|
|
252
|
-
background: #0066cc;
|
|
253
|
-
color: white;
|
|
254
295
|
border: none;
|
|
255
296
|
border-radius: 8px;
|
|
256
297
|
width: 32px;
|
|
257
298
|
height: 32px;
|
|
258
299
|
cursor: pointer;
|
|
259
|
-
font-size: 18px;
|
|
260
300
|
transition: all 0.15s ease;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
background: #0052a3;
|
|
264
|
-
transform: scale(1.05);
|
|
265
|
-
}
|
|
301
|
+
font-size: 16px;
|
|
302
|
+
transform: translateY(-2px);
|
|
266
303
|
|
|
267
304
|
&:active {
|
|
268
305
|
transform: scale(0.95);
|
|
269
306
|
}
|
|
270
|
-
|
|
271
|
-
[data-theme='dark'] & {
|
|
272
|
-
background: #4db8ff;
|
|
273
|
-
color: #1a1a1a;
|
|
274
|
-
|
|
275
|
-
&:hover {
|
|
276
|
-
background: #66c2ff;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
307
|
`;
|
|
280
308
|
export const HeaderTitle = styled.h3 `
|
|
281
309
|
margin: 0;
|
|
@@ -8,11 +8,21 @@ export interface TMContextMenuItemProps {
|
|
|
8
8
|
rightIcon?: React.ReactNode;
|
|
9
9
|
onRightIconClick?: () => void;
|
|
10
10
|
beginGroup?: boolean;
|
|
11
|
+
tooltip?: string;
|
|
12
|
+
operationType?: 'singleRow' | 'multiRow';
|
|
11
13
|
}
|
|
12
14
|
export interface TMContextMenuProps {
|
|
13
15
|
items: TMContextMenuItemProps[];
|
|
14
16
|
trigger?: 'right' | 'left';
|
|
15
17
|
children?: React.ReactNode;
|
|
18
|
+
externalControl?: {
|
|
19
|
+
visible: boolean;
|
|
20
|
+
position: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
};
|
|
24
|
+
onClose: () => void;
|
|
25
|
+
};
|
|
16
26
|
}
|
|
17
27
|
export interface Position {
|
|
18
28
|
x: number;
|
|
@@ -1,36 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { ContextMenu } from '../ContextMenu';
|
|
4
|
-
import
|
|
4
|
+
import ShowAlert from '../../base/TMAlert';
|
|
5
5
|
import TMTooltip from '../../base/TMTooltip';
|
|
6
6
|
import * as S from './styles';
|
|
7
|
-
import { IconApply, IconMenuKebab, IconMenuVertical,
|
|
7
|
+
import { IconApply, IconMenuKebab, IconMenuVertical, IconPencil, IconPin, SDKUI_Globals } 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
|
-
const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [],
|
|
9
|
+
const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained = false, defaultPosition = { x: 100, y: 100 }, maxItems = 8, }) => {
|
|
10
10
|
const loadConfig = () => {
|
|
11
11
|
try {
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
const settings = SDKUI_Globals.userSettings.searchSettings.floatingMenuBar;
|
|
13
|
+
// Check if position was actually saved (not just the default class value)
|
|
14
|
+
const hasSavedPosition = settings.position &&
|
|
15
|
+
(settings.position.x !== 100 || settings.position.y !== 100 ||
|
|
16
|
+
(settings.itemIds && settings.itemIds.length > 0));
|
|
17
|
+
return {
|
|
18
|
+
orientation: settings.orientation || 'horizontal',
|
|
19
|
+
savedItemIds: settings.itemIds || [],
|
|
20
|
+
position: hasSavedPosition ? settings.position : defaultPosition,
|
|
21
|
+
};
|
|
21
22
|
}
|
|
22
23
|
catch (error) {
|
|
23
24
|
console.error('Failed to load FloatingMenuBar config:', error);
|
|
25
|
+
return {
|
|
26
|
+
orientation: 'horizontal',
|
|
27
|
+
savedItemIds: [],
|
|
28
|
+
position: defaultPosition,
|
|
29
|
+
};
|
|
24
30
|
}
|
|
25
|
-
return {
|
|
26
|
-
orientation: 'horizontal',
|
|
27
|
-
pinnedItemIds: new Set(),
|
|
28
|
-
savedItemIds: [],
|
|
29
|
-
};
|
|
30
31
|
};
|
|
31
32
|
const initialConfig = loadConfig();
|
|
32
33
|
const [state, setState] = useState({
|
|
33
|
-
position:
|
|
34
|
+
position: initialConfig.position,
|
|
34
35
|
isDragging: false,
|
|
35
36
|
isConfigMode: false,
|
|
36
37
|
orientation: initialConfig.orientation,
|
|
@@ -39,9 +40,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
39
40
|
});
|
|
40
41
|
const floatingRef = useRef(null);
|
|
41
42
|
const dragOffset = useRef({ x: 0, y: 0 });
|
|
42
|
-
const [pinnedItemIds, setPinnedItemIds] = useState(initialConfig.pinnedItemIds);
|
|
43
43
|
const [dragOverIndex, setDragOverIndex] = useState(null);
|
|
44
|
-
const [showMaxItemsNotification, setShowMaxItemsNotification] = useState(false);
|
|
45
44
|
// Use refs to track item IDs without causing re-renders
|
|
46
45
|
const floatingBarItemIds = useRef(new Set());
|
|
47
46
|
const floatingBarItemNames = useRef(new Set());
|
|
@@ -57,13 +56,15 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
57
56
|
const itemId = `${parentPath}${item.name}-${index}`;
|
|
58
57
|
// Only add items that have onClick (final actions, not submenu parents)
|
|
59
58
|
if (item.onClick && !item.submenu) {
|
|
59
|
+
// Check if item is currently in the floating bar
|
|
60
|
+
const isPinned = state.items.some(i => i.id === itemId || i.name === item.name);
|
|
60
61
|
result.push({
|
|
61
62
|
id: itemId,
|
|
62
63
|
name: item.name,
|
|
63
|
-
icon: item.icon || _jsx(
|
|
64
|
+
icon: item.icon || _jsx(IconPin, {}),
|
|
64
65
|
onClick: item.onClick,
|
|
65
66
|
disabled: item.disabled,
|
|
66
|
-
isPinned:
|
|
67
|
+
isPinned: isPinned,
|
|
67
68
|
originalMenuItem: item,
|
|
68
69
|
});
|
|
69
70
|
}
|
|
@@ -73,7 +74,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
73
74
|
}
|
|
74
75
|
});
|
|
75
76
|
return result;
|
|
76
|
-
}, [
|
|
77
|
+
}, [state.items]);
|
|
77
78
|
// Restore items on mount from savedItemIds
|
|
78
79
|
useEffect(() => {
|
|
79
80
|
if (contextMenuItems.length > 0) {
|
|
@@ -98,24 +99,17 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
98
99
|
else {
|
|
99
100
|
// Add to floating bar
|
|
100
101
|
if (s.items.length >= maxItems) {
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
ShowAlert({
|
|
103
|
+
mode: 'warning',
|
|
104
|
+
title: 'Limite Massimo Raggiunto',
|
|
105
|
+
message: `Hai raggiunto il massimo di ${maxItems} elementi. Rimuovine uno prima di aggiungerne altri.`,
|
|
106
|
+
duration: 4000,
|
|
107
|
+
});
|
|
103
108
|
return s;
|
|
104
109
|
}
|
|
105
110
|
return { ...s, items: [...s.items, item] };
|
|
106
111
|
}
|
|
107
112
|
});
|
|
108
|
-
// Update pinned IDs for context menu items
|
|
109
|
-
setPinnedItemIds(prev => {
|
|
110
|
-
const newSet = new Set(prev);
|
|
111
|
-
if (newSet.has(item.id)) {
|
|
112
|
-
newSet.delete(item.id);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
newSet.add(item.id);
|
|
116
|
-
}
|
|
117
|
-
return newSet;
|
|
118
|
-
});
|
|
119
113
|
}, [maxItems]);
|
|
120
114
|
// Get current item state (disabled and onClick) from contextMenuItems
|
|
121
115
|
const getCurrentItemState = useCallback((itemName) => {
|
|
@@ -150,7 +144,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
150
144
|
const isInFloatingBar = currentItemIds.has(itemId) || currentItemNames.has(item.name);
|
|
151
145
|
const enhanced = {
|
|
152
146
|
...item,
|
|
153
|
-
rightIcon: item.onClick && !item.submenu ? (isInFloatingBar ? _jsx(
|
|
147
|
+
rightIcon: item.onClick && !item.submenu ? (isInFloatingBar ? _jsx(IconPin, { color: "#e12a2a" }) : _jsx(IconPin, {})) : undefined,
|
|
154
148
|
onRightIconClick: item.onClick && !item.submenu ? () => {
|
|
155
149
|
if (flatItem) {
|
|
156
150
|
togglePin(flatItem);
|
|
@@ -229,20 +223,20 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
229
223
|
}
|
|
230
224
|
return undefined;
|
|
231
225
|
}, [state.isDragging, handleMouseMove, handleMouseUp]);
|
|
232
|
-
// Save to
|
|
226
|
+
// Save to SDKUI_Globals.userSettings whenever config changes (including position)
|
|
233
227
|
useEffect(() => {
|
|
234
228
|
try {
|
|
235
|
-
|
|
229
|
+
// Replace the entire object to trigger the Proxy
|
|
230
|
+
SDKUI_Globals.userSettings.searchSettings.floatingMenuBar = {
|
|
236
231
|
orientation: state.orientation,
|
|
237
|
-
|
|
238
|
-
|
|
232
|
+
itemIds: state.items.map(item => item.id),
|
|
233
|
+
position: state.position,
|
|
239
234
|
};
|
|
240
|
-
localStorage.setItem(storageKey, JSON.stringify(config));
|
|
241
235
|
}
|
|
242
236
|
catch (error) {
|
|
243
237
|
console.error('Failed to save FloatingMenuBar config:', error);
|
|
244
238
|
}
|
|
245
|
-
}, [state.orientation, state.items,
|
|
239
|
+
}, [state.orientation, state.items, state.position]);
|
|
246
240
|
const toggleConfigMode = () => {
|
|
247
241
|
setState(s => ({ ...s, isConfigMode: !s.isConfigMode }));
|
|
248
242
|
};
|
|
@@ -286,12 +280,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
286
280
|
...s,
|
|
287
281
|
items: s.items.filter(item => item.id !== itemId),
|
|
288
282
|
}));
|
|
289
|
-
// Also remove from pinned items if it was pinned
|
|
290
|
-
setPinnedItemIds(prev => {
|
|
291
|
-
const newSet = new Set(prev);
|
|
292
|
-
newSet.delete(itemId);
|
|
293
|
-
return newSet;
|
|
294
|
-
});
|
|
295
283
|
};
|
|
296
284
|
// Drag and drop for reordering
|
|
297
285
|
const handleDragStart = (e, index) => {
|
|
@@ -347,7 +335,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
347
335
|
setState(s => ({ ...s, draggedItemIndex: null }));
|
|
348
336
|
setDragOverIndex(null);
|
|
349
337
|
};
|
|
350
|
-
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, children: [_jsx(S.GripHandle, { "$orientation": state.orientation, onMouseDown: handleMouseDown, children: _jsx(IconDraggableDots, {}) }), _jsx(S.
|
|
338
|
+
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, children: [_jsx(S.GripHandle, { "$orientation": state.orientation, onMouseDown: handleMouseDown, children: _jsx(IconDraggableDots, {}) }), _jsx(S.Separator, { "$orientation": state.orientation }), state.items.map((item, index) => {
|
|
351
339
|
// Get current state (disabled and onClick) from contextMenuItems
|
|
352
340
|
const currentState = getCurrentItemState(item.name);
|
|
353
341
|
const isDisabled = currentState.disabled || false;
|
|
@@ -365,6 +353,6 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], storageKey = '
|
|
|
365
353
|
currentOnClick();
|
|
366
354
|
}
|
|
367
355
|
}, disabled: isDisabled, children: item.icon }) })), state.isConfigMode && (_jsx(S.RemoveButton, { onClick: () => removeItem(item.id), children: "\u00D7" }))] }, item.id));
|
|
368
|
-
}), !state.isConfigMode && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: enhancedContextMenuItems(), trigger: "left", children: _jsx(S.
|
|
356
|
+
}), !state.isConfigMode && contextMenuItems.length > 0 && (_jsx(ContextMenu, { items: enhancedContextMenuItems(), trigger: "left", children: _jsx(S.ContextMenuButton, { children: _jsx(IconMenuVertical, {}) }) }, state.items.map(i => i.id).join(','))), _jsx(S.ConfigButton, { onClick: toggleConfigMode, "$isActive": state.isConfigMode, children: state.isConfigMode ? _jsx(IconApply, {}) : _jsx(IconPencil, {}) }), !state.isConfigMode && (_jsx(S.OrientationToggle, { "$orientation": state.orientation, onClick: toggleOrientation, children: _jsx(IconMenuKebab, {}) }))] })] }));
|
|
369
357
|
};
|
|
370
358
|
export default TMFloatingMenuBar;
|
|
@@ -21,12 +21,20 @@ export declare const FloatingContainer: import("styled-components/dist/types").I
|
|
|
21
21
|
export declare const GripHandle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
22
22
|
$orientation: "horizontal" | "vertical";
|
|
23
23
|
}>> & string;
|
|
24
|
+
export declare const Separator: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
25
|
+
$orientation: "horizontal" | "vertical";
|
|
26
|
+
}>> & string;
|
|
24
27
|
export declare const MenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
|
|
25
28
|
$isActive?: boolean;
|
|
26
29
|
}>> & string;
|
|
27
30
|
export declare const ConfigButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
|
|
28
31
|
$isActive?: boolean;
|
|
29
32
|
}>> & string;
|
|
33
|
+
export declare const ContextMenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
|
|
34
|
+
$isActive?: boolean;
|
|
35
|
+
}, "ref"> & {
|
|
36
|
+
ref?: ((instance: HTMLButtonElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLButtonElement> | null | undefined;
|
|
37
|
+
}, never>> & string;
|
|
30
38
|
export declare const RemoveButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>> & string;
|
|
31
39
|
export declare const OrientationToggle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
|
|
32
40
|
$orientation: "horizontal" | "vertical";
|