@topconsultnpm/sdkui-react 6.20.0-dev1.56 → 6.20.0-dev1.57
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.
|
@@ -4,7 +4,7 @@ import { createPortal } from 'react-dom';
|
|
|
4
4
|
import * as S from './styles';
|
|
5
5
|
import { useIsMobile, useMenuPosition, useIsIOS } from './hooks';
|
|
6
6
|
import { IconArrowLeft } from '../../../helper';
|
|
7
|
-
const TMContextMenu = ({ items, trigger = 'right', children, externalControl, keepOpenOnClick = false }) => {
|
|
7
|
+
const TMContextMenu = ({ items, trigger = 'right', children, target, externalControl, keepOpenOnClick = false }) => {
|
|
8
8
|
const [menuState, setMenuState] = useState({
|
|
9
9
|
visible: false,
|
|
10
10
|
position: { x: 0, y: 0 },
|
|
@@ -46,6 +46,91 @@ const TMContextMenu = ({ items, trigger = 'right', children, externalControl, ke
|
|
|
46
46
|
}));
|
|
47
47
|
}
|
|
48
48
|
}, [externalControl, items]);
|
|
49
|
+
// iOS long-press support: attach touch listeners to target elements
|
|
50
|
+
// On long-press, dispatch synthetic contextmenu event to trigger existing handlers
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!target || !isIOS)
|
|
53
|
+
return;
|
|
54
|
+
const elements = document.querySelectorAll(target);
|
|
55
|
+
if (elements.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
const touchStateMap = new WeakMap();
|
|
58
|
+
const handleTouchStart = (e) => {
|
|
59
|
+
const touchEvent = e;
|
|
60
|
+
const element = e.currentTarget;
|
|
61
|
+
const touch = touchEvent.touches[0];
|
|
62
|
+
let state = touchStateMap.get(element);
|
|
63
|
+
if (!state) {
|
|
64
|
+
state = { timeout: null, startX: 0, startY: 0 };
|
|
65
|
+
touchStateMap.set(element, state);
|
|
66
|
+
}
|
|
67
|
+
state.startX = touch.clientX;
|
|
68
|
+
state.startY = touch.clientY;
|
|
69
|
+
if (state.timeout)
|
|
70
|
+
clearTimeout(state.timeout);
|
|
71
|
+
state.timeout = setTimeout(() => {
|
|
72
|
+
// Haptic feedback
|
|
73
|
+
if ('vibrate' in navigator)
|
|
74
|
+
navigator.vibrate(50);
|
|
75
|
+
// Dispatch synthetic contextmenu event to trigger existing onContextMenu handlers
|
|
76
|
+
const syntheticEvent = new MouseEvent('contextmenu', {
|
|
77
|
+
bubbles: true,
|
|
78
|
+
cancelable: true,
|
|
79
|
+
clientX: touch.clientX,
|
|
80
|
+
clientY: touch.clientY,
|
|
81
|
+
});
|
|
82
|
+
element.dispatchEvent(syntheticEvent);
|
|
83
|
+
if (state)
|
|
84
|
+
state.timeout = null;
|
|
85
|
+
}, 500);
|
|
86
|
+
};
|
|
87
|
+
const handleTouchMove = (e) => {
|
|
88
|
+
const touchEvent = e;
|
|
89
|
+
const element = e.currentTarget;
|
|
90
|
+
const state = touchStateMap.get(element);
|
|
91
|
+
if (!state?.timeout)
|
|
92
|
+
return;
|
|
93
|
+
const touch = touchEvent.touches[0];
|
|
94
|
+
const dx = Math.abs(touch.clientX - state.startX);
|
|
95
|
+
const dy = Math.abs(touch.clientY - state.startY);
|
|
96
|
+
if (dx > 10 || dy > 10) {
|
|
97
|
+
clearTimeout(state.timeout);
|
|
98
|
+
state.timeout = null;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const handleTouchEnd = (e) => {
|
|
102
|
+
const element = e.currentTarget;
|
|
103
|
+
const state = touchStateMap.get(element);
|
|
104
|
+
if (state?.timeout) {
|
|
105
|
+
clearTimeout(state.timeout);
|
|
106
|
+
state.timeout = null;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
// Attach listeners to all matching elements
|
|
110
|
+
elements.forEach(element => {
|
|
111
|
+
const el = element;
|
|
112
|
+
// Prevent iOS native callout
|
|
113
|
+
const style = el.style;
|
|
114
|
+
style.webkitTouchCallout = 'none';
|
|
115
|
+
style.webkitUserSelect = 'none';
|
|
116
|
+
el.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
117
|
+
el.addEventListener('touchmove', handleTouchMove, { passive: true });
|
|
118
|
+
el.addEventListener('touchend', handleTouchEnd);
|
|
119
|
+
el.addEventListener('touchcancel', handleTouchEnd);
|
|
120
|
+
});
|
|
121
|
+
return () => {
|
|
122
|
+
elements.forEach(element => {
|
|
123
|
+
const el = element;
|
|
124
|
+
const style = el.style;
|
|
125
|
+
style.webkitTouchCallout = '';
|
|
126
|
+
style.webkitUserSelect = '';
|
|
127
|
+
el.removeEventListener('touchstart', handleTouchStart);
|
|
128
|
+
el.removeEventListener('touchmove', handleTouchMove);
|
|
129
|
+
el.removeEventListener('touchend', handleTouchEnd);
|
|
130
|
+
el.removeEventListener('touchcancel', handleTouchEnd);
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
}, [target, isIOS]);
|
|
49
134
|
// Update items when they change while menu is visible (for keepOpenOnClick behavior)
|
|
50
135
|
useEffect(() => {
|
|
51
136
|
if (!keepOpenOnClick)
|
|
@@ -180,7 +180,7 @@ const TMSavedQuerySelector = React.memo(({ items, selectedId, allowShowSearch =
|
|
|
180
180
|
overflow: 'auto'
|
|
181
181
|
}, children: dataSource.slice(0, showAllRoot || searchText.length > 0 ? dataSource.length : initialSQDsMaxItems).filter(o => searchText.length <= 0 || (searchText.length > 0 && o.name?.toLocaleLowerCase().includes(searchText.toLocaleLowerCase())) || o.description?.toLocaleLowerCase().includes(searchText.toLocaleLowerCase())).map((sqd, index) => {
|
|
182
182
|
const isCurrent = selectedItem?.id == sqd.id;
|
|
183
|
-
return (_jsx(StyledSqdItem, { "$isMobile": isMobile, onClick: () => {
|
|
183
|
+
return (_jsx(StyledSqdItem, { id: `sqd-item-${sqd.id}`, "$isMobile": isMobile, onClick: () => {
|
|
184
184
|
setSelectedItem(sqd);
|
|
185
185
|
onItemClick?.(sqd);
|
|
186
186
|
}, onContextMenu: (e) => {
|
|
@@ -222,10 +222,10 @@ const TMSavedQuerySelector = React.memo(({ items, selectedId, allowShowSearch =
|
|
|
222
222
|
visibility: isCurrent ? 'visible' : 'hidden'
|
|
223
223
|
}, children: _jsx(IconApply, { fontSize: 24, color: 'green' }) })] }) }, sqd.id));
|
|
224
224
|
}) }), dataSource.length > initialSQDsMaxItems && searchText.length <= 0 &&
|
|
225
|
-
_jsx("div", { style: { display: 'flex', justifyContent: 'flex-end', padding: '10px', position: 'relative' }, children: _jsx(TMShowAllOrMaxItemsButton, { showAll: showAllRoot, dataSourceLength: dataSource.length, onClick: () => { setShowAllRoot(!showAllRoot); } }) }),
|
|
225
|
+
_jsx("div", { style: { display: 'flex', justifyContent: 'flex-end', padding: '10px', position: 'relative' }, children: _jsx(TMShowAllOrMaxItemsButton, { showAll: showAllRoot, dataSourceLength: dataSource.length, onClick: () => { setShowAllRoot(!showAllRoot); } }) }), _jsx(TMContextMenu, { target: "[id^='sqd-item-']", items: contextMenuState.sqd ? getContextMenuItems(contextMenuState.sqd, manageDefault, isMobile, deleteSQDAsync, setDefaultSQDAsync, favManageSQDAsync, setInfoSQD) : [], externalControl: {
|
|
226
226
|
visible: contextMenuState.visible,
|
|
227
227
|
position: contextMenuState.position,
|
|
228
228
|
onClose: () => setContextMenuState(prev => ({ ...prev, visible: false, sqd: null }))
|
|
229
|
-
} })
|
|
229
|
+
} }), _jsxs(StyledOffCanvasPanel, { ref: panelRef, "$isOpen": isMobile && infoSQD !== undefined, children: [_jsxs(StyledDivHorizontal, { style: { gap: 10, padding: '10px 8px', width: '100%', alignItems: 'center' }, children: [_jsx("p", { style: { fontSize: '1.1rem', fontWeight: 'bold' }, children: `${SDK_Localizator.SavedQuery} - ${SDKUI_Localizator.About}` }), _jsx(IconCloseOutline, { style: { marginLeft: 'auto', cursor: 'pointer' }, onClick: () => setInfoSQD(undefined) })] }), getTooltipBySqd(infoSQD)] })] }));
|
|
230
230
|
});
|
|
231
231
|
export default TMSavedQuerySelector;
|