@topconsultnpm/sdkui-react 6.19.0-dev2.35 → 6.19.0-dev2.36
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.d.ts +4 -0
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +187 -0
- package/lib/components/NewComponents/ContextMenu/hooks.d.ts +11 -0
- package/lib/components/NewComponents/ContextMenu/hooks.js +48 -0
- package/lib/components/NewComponents/ContextMenu/index.d.ts +2 -0
- package/lib/components/NewComponents/ContextMenu/index.js +1 -0
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +27 -0
- package/lib/components/NewComponents/ContextMenu/styles.js +308 -0
- package/lib/components/NewComponents/ContextMenu/types.d.ts +26 -0
- package/lib/components/NewComponents/ContextMenu/types.js +1 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.d.ts +4 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +370 -0
- package/lib/components/NewComponents/FloatingMenuBar/index.d.ts +2 -0
- package/lib/components/NewComponents/FloatingMenuBar/index.js +2 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +38 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +267 -0
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +30 -0
- package/lib/components/NewComponents/FloatingMenuBar/types.js +1 -0
- package/lib/components/NewComponents/Notification/Notification.d.ts +4 -0
- package/lib/components/NewComponents/Notification/Notification.js +60 -0
- package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +8 -0
- package/lib/components/NewComponents/Notification/NotificationContainer.js +33 -0
- package/lib/components/NewComponents/Notification/index.d.ts +2 -0
- package/lib/components/NewComponents/Notification/index.js +2 -0
- package/lib/components/NewComponents/Notification/styles.d.ts +21 -0
- package/lib/components/NewComponents/Notification/styles.js +180 -0
- package/lib/components/NewComponents/Notification/types.d.ts +18 -0
- package/lib/components/NewComponents/Notification/types.js +1 -0
- package/lib/components/features/documents/TMDcmtPreview.js +28 -7
- package/lib/components/grids/TMBlogsPost.js +3 -1
- package/lib/helper/helpers.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { ContextMenu } from '../ContextMenu';
|
|
4
|
+
import Notification from '../Notification';
|
|
5
|
+
import TMTooltip from '../../base/TMTooltip';
|
|
6
|
+
import * as S from './styles';
|
|
7
|
+
import { IconApply, IconMenuKebab, IconMenuVertical, IconSettings, IconStar } from '../../../helper';
|
|
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 = [], storageKey = 'floatingMenuBar-config', isConstrained = false, defaultPosition = { x: 100, y: 100 }, maxItems = 8, }) => {
|
|
10
|
+
const loadConfig = () => {
|
|
11
|
+
try {
|
|
12
|
+
const saved = localStorage.getItem(storageKey);
|
|
13
|
+
if (saved) {
|
|
14
|
+
const config = JSON.parse(saved);
|
|
15
|
+
return {
|
|
16
|
+
orientation: config.orientation || 'horizontal',
|
|
17
|
+
pinnedItemIds: new Set(config.pinnedItemIds || []),
|
|
18
|
+
savedItemIds: config.itemIds || [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('Failed to load FloatingMenuBar config:', error);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
orientation: 'horizontal',
|
|
27
|
+
pinnedItemIds: new Set(),
|
|
28
|
+
savedItemIds: [],
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const initialConfig = loadConfig();
|
|
32
|
+
const [state, setState] = useState({
|
|
33
|
+
position: defaultPosition,
|
|
34
|
+
isDragging: false,
|
|
35
|
+
isConfigMode: false,
|
|
36
|
+
orientation: initialConfig.orientation,
|
|
37
|
+
items: [],
|
|
38
|
+
draggedItemIndex: null,
|
|
39
|
+
});
|
|
40
|
+
const floatingRef = useRef(null);
|
|
41
|
+
const dragOffset = useRef({ x: 0, y: 0 });
|
|
42
|
+
const [pinnedItemIds, setPinnedItemIds] = useState(initialConfig.pinnedItemIds);
|
|
43
|
+
const [dragOverIndex, setDragOverIndex] = useState(null);
|
|
44
|
+
const [showMaxItemsNotification, setShowMaxItemsNotification] = useState(false);
|
|
45
|
+
// Use refs to track item IDs without causing re-renders
|
|
46
|
+
const floatingBarItemIds = useRef(new Set());
|
|
47
|
+
const floatingBarItemNames = useRef(new Set());
|
|
48
|
+
// Update refs when items change, but don't trigger re-renders
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
floatingBarItemIds.current = new Set(state.items.map(i => i.id));
|
|
51
|
+
floatingBarItemNames.current = new Set(state.items.map(i => i.name));
|
|
52
|
+
}, [state.items]);
|
|
53
|
+
// Convert menu items to flat list with pinned status
|
|
54
|
+
const flattenMenuItems = useCallback((items, parentPath = '') => {
|
|
55
|
+
const result = [];
|
|
56
|
+
items.forEach((item, index) => {
|
|
57
|
+
const itemId = `${parentPath}${item.name}-${index}`;
|
|
58
|
+
// Only add items that have onClick (final actions, not submenu parents)
|
|
59
|
+
if (item.onClick && !item.submenu) {
|
|
60
|
+
result.push({
|
|
61
|
+
id: itemId,
|
|
62
|
+
name: item.name,
|
|
63
|
+
icon: item.icon || _jsx(IconStar, {}),
|
|
64
|
+
onClick: item.onClick,
|
|
65
|
+
disabled: item.disabled,
|
|
66
|
+
isPinned: pinnedItemIds.has(itemId),
|
|
67
|
+
originalMenuItem: item,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Recursively process submenus
|
|
71
|
+
if (item.submenu) {
|
|
72
|
+
result.push(...flattenMenuItems(item.submenu, `${itemId}/`));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return result;
|
|
76
|
+
}, [pinnedItemIds]);
|
|
77
|
+
// Restore items on mount from savedItemIds
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (contextMenuItems.length > 0) {
|
|
80
|
+
const flatItems = flattenMenuItems(contextMenuItems);
|
|
81
|
+
// Restore items in the saved order from localStorage
|
|
82
|
+
const restoredItems = initialConfig.savedItemIds
|
|
83
|
+
.map((id) => flatItems.find(item => item.id === id))
|
|
84
|
+
.filter((item) => item !== undefined);
|
|
85
|
+
if (restoredItems.length > 0) {
|
|
86
|
+
setState(s => ({ ...s, items: restoredItems }));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, []); // Only run once on mount
|
|
90
|
+
const togglePin = useCallback((item) => {
|
|
91
|
+
setState(s => {
|
|
92
|
+
const isInFloatingBar = s.items.some(i => i.id === item.id || i.name === item.name);
|
|
93
|
+
if (isInFloatingBar) {
|
|
94
|
+
// Remove from floating bar
|
|
95
|
+
const newItems = s.items.filter(i => i.id !== item.id && i.name !== item.name);
|
|
96
|
+
return { ...s, items: newItems };
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Add to floating bar
|
|
100
|
+
if (s.items.length >= maxItems) {
|
|
101
|
+
setShowMaxItemsNotification(true);
|
|
102
|
+
setTimeout(() => setShowMaxItemsNotification(false), 3000);
|
|
103
|
+
return s;
|
|
104
|
+
}
|
|
105
|
+
return { ...s, items: [...s.items, item] };
|
|
106
|
+
}
|
|
107
|
+
});
|
|
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
|
+
}, [maxItems]);
|
|
120
|
+
// Get current item state (disabled and onClick) from contextMenuItems
|
|
121
|
+
const getCurrentItemState = useCallback((itemName) => {
|
|
122
|
+
const findInItems = (items) => {
|
|
123
|
+
for (const item of items) {
|
|
124
|
+
if (item.name === itemName)
|
|
125
|
+
return item;
|
|
126
|
+
if (item.submenu) {
|
|
127
|
+
const found = findInItems(item.submenu);
|
|
128
|
+
if (found)
|
|
129
|
+
return found;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
};
|
|
134
|
+
const foundItem = findInItems(contextMenuItems);
|
|
135
|
+
return {
|
|
136
|
+
disabled: foundItem?.disabled,
|
|
137
|
+
onClick: foundItem?.onClick
|
|
138
|
+
};
|
|
139
|
+
}, [contextMenuItems]); // Enhanced context menu items with pin functionality
|
|
140
|
+
const enhancedContextMenuItems = useCallback(() => {
|
|
141
|
+
const flatItems = flattenMenuItems(contextMenuItems);
|
|
142
|
+
// Calculate current pinned items directly from state.items (not refs)
|
|
143
|
+
const currentItemIds = new Set(state.items.map(i => i.id));
|
|
144
|
+
const currentItemNames = new Set(state.items.map(i => i.name));
|
|
145
|
+
const enhanceItems = (items) => {
|
|
146
|
+
return items.map(item => {
|
|
147
|
+
const flatItem = flatItems.find(fi => fi.name === item.name);
|
|
148
|
+
const itemId = flatItem?.id || '';
|
|
149
|
+
// Check if item is in the floating bar using current state
|
|
150
|
+
const isInFloatingBar = currentItemIds.has(itemId) || currentItemNames.has(item.name);
|
|
151
|
+
const enhanced = {
|
|
152
|
+
...item,
|
|
153
|
+
rightIcon: item.onClick && !item.submenu ? (isInFloatingBar ? _jsx(IconStar, { color: "#FFD700" }) : _jsx(IconStar, {})) : undefined,
|
|
154
|
+
onRightIconClick: item.onClick && !item.submenu ? () => {
|
|
155
|
+
if (flatItem) {
|
|
156
|
+
togglePin(flatItem);
|
|
157
|
+
}
|
|
158
|
+
} : undefined,
|
|
159
|
+
};
|
|
160
|
+
if (item.submenu) {
|
|
161
|
+
enhanced.submenu = enhanceItems(item.submenu);
|
|
162
|
+
}
|
|
163
|
+
return enhanced;
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
return enhanceItems(contextMenuItems);
|
|
167
|
+
}, [contextMenuItems, flattenMenuItems, togglePin, state.items]);
|
|
168
|
+
const handleMouseDown = (e) => {
|
|
169
|
+
if (state.isConfigMode)
|
|
170
|
+
return;
|
|
171
|
+
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
172
|
+
if (containerRect) {
|
|
173
|
+
// Calculate drag offset based on positioning mode
|
|
174
|
+
if (isConstrained) {
|
|
175
|
+
// For absolute positioning, offset is relative to container
|
|
176
|
+
dragOffset.current = {
|
|
177
|
+
x: e.clientX - containerRect.left - state.position.x,
|
|
178
|
+
y: e.clientY - containerRect.top - state.position.y,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// For fixed positioning, offset is relative to viewport
|
|
183
|
+
dragOffset.current = {
|
|
184
|
+
x: e.clientX - state.position.x,
|
|
185
|
+
y: e.clientY - state.position.y,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
setState(s => ({ ...s, isDragging: true }));
|
|
190
|
+
};
|
|
191
|
+
const handleMouseMove = useCallback((e) => {
|
|
192
|
+
if (!state.isDragging || !containerRef.current || !floatingRef.current)
|
|
193
|
+
return;
|
|
194
|
+
const container = containerRef.current.getBoundingClientRect();
|
|
195
|
+
const floating = floatingRef.current.getBoundingClientRect();
|
|
196
|
+
let newX, newY;
|
|
197
|
+
if (isConstrained) {
|
|
198
|
+
// For constrained (absolute positioning), use container coordinates
|
|
199
|
+
newX = e.clientX - container.left - dragOffset.current.x;
|
|
200
|
+
newY = e.clientY - container.top - dragOffset.current.y;
|
|
201
|
+
// Constrain to container bounds
|
|
202
|
+
newX = Math.max(0, Math.min(newX, container.width - floating.width));
|
|
203
|
+
newY = Math.max(0, Math.min(newY, container.height - floating.height));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// For unconstrained (fixed positioning), use viewport coordinates
|
|
207
|
+
newX = e.clientX - dragOffset.current.x;
|
|
208
|
+
newY = e.clientY - dragOffset.current.y;
|
|
209
|
+
// Constrain to viewport bounds
|
|
210
|
+
newX = Math.max(0, Math.min(newX, window.innerWidth - floating.width));
|
|
211
|
+
newY = Math.max(0, Math.min(newY, window.innerHeight - floating.height));
|
|
212
|
+
}
|
|
213
|
+
setState(s => ({
|
|
214
|
+
...s,
|
|
215
|
+
position: { x: newX, y: newY },
|
|
216
|
+
}));
|
|
217
|
+
}, [state.isDragging, containerRef, isConstrained]);
|
|
218
|
+
const handleMouseUp = useCallback(() => {
|
|
219
|
+
setState(s => ({ ...s, isDragging: false }));
|
|
220
|
+
}, []);
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
if (state.isDragging) {
|
|
223
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
224
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
225
|
+
return () => {
|
|
226
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
227
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}, [state.isDragging, handleMouseMove, handleMouseUp]);
|
|
232
|
+
// Save to localStorage whenever config changes (excluding position)
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
try {
|
|
235
|
+
const config = {
|
|
236
|
+
orientation: state.orientation,
|
|
237
|
+
pinnedItemIds: Array.from(pinnedItemIds),
|
|
238
|
+
itemIds: state.items.map(item => item.id), // Save only IDs, not functions
|
|
239
|
+
};
|
|
240
|
+
localStorage.setItem(storageKey, JSON.stringify(config));
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
console.error('Failed to save FloatingMenuBar config:', error);
|
|
244
|
+
}
|
|
245
|
+
}, [state.orientation, state.items, pinnedItemIds, storageKey]);
|
|
246
|
+
const toggleConfigMode = () => {
|
|
247
|
+
setState(s => ({ ...s, isConfigMode: !s.isConfigMode }));
|
|
248
|
+
};
|
|
249
|
+
const toggleOrientation = () => {
|
|
250
|
+
const newOrientation = state.orientation === 'horizontal' ? 'vertical' : 'horizontal';
|
|
251
|
+
// First, change the orientation
|
|
252
|
+
setState(s => ({
|
|
253
|
+
...s,
|
|
254
|
+
orientation: newOrientation,
|
|
255
|
+
}));
|
|
256
|
+
// Then, after DOM updates, adjust position to stay in bounds
|
|
257
|
+
requestAnimationFrame(() => {
|
|
258
|
+
requestAnimationFrame(() => {
|
|
259
|
+
if (containerRef.current && floatingRef.current) {
|
|
260
|
+
const container = containerRef.current.getBoundingClientRect();
|
|
261
|
+
const floating = floatingRef.current.getBoundingClientRect();
|
|
262
|
+
let newX, newY;
|
|
263
|
+
if (isConstrained) {
|
|
264
|
+
// For constrained mode, use container bounds
|
|
265
|
+
newX = Math.max(0, Math.min(state.position.x, container.width - floating.width));
|
|
266
|
+
newY = Math.max(0, Math.min(state.position.y, container.height - floating.height));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// For unconstrained mode, use viewport bounds
|
|
270
|
+
newX = Math.max(0, Math.min(state.position.x, window.innerWidth - floating.width));
|
|
271
|
+
newY = Math.max(0, Math.min(state.position.y, window.innerHeight - floating.height));
|
|
272
|
+
}
|
|
273
|
+
// Only update position if it changed (to avoid unnecessary re-render)
|
|
274
|
+
if (newX !== state.position.x || newY !== state.position.y) {
|
|
275
|
+
setState(s => ({
|
|
276
|
+
...s,
|
|
277
|
+
position: { x: newX, y: newY },
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
const removeItem = (itemId) => {
|
|
285
|
+
setState(s => ({
|
|
286
|
+
...s,
|
|
287
|
+
items: s.items.filter(item => item.id !== itemId),
|
|
288
|
+
}));
|
|
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
|
+
};
|
|
296
|
+
// Drag and drop for reordering
|
|
297
|
+
const handleDragStart = (e, index) => {
|
|
298
|
+
if (!state.isConfigMode)
|
|
299
|
+
return;
|
|
300
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
301
|
+
e.dataTransfer.setData('text/plain', index.toString());
|
|
302
|
+
setState(s => ({ ...s, draggedItemIndex: index }));
|
|
303
|
+
};
|
|
304
|
+
const handleDragEnter = (e, index) => {
|
|
305
|
+
if (!state.isConfigMode)
|
|
306
|
+
return;
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
if (state.draggedItemIndex !== index) {
|
|
309
|
+
setDragOverIndex(index);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
const handleDragOver = (e) => {
|
|
313
|
+
if (!state.isConfigMode)
|
|
314
|
+
return;
|
|
315
|
+
e.preventDefault();
|
|
316
|
+
e.dataTransfer.dropEffect = 'move';
|
|
317
|
+
};
|
|
318
|
+
const handleDragLeave = (_e, index) => {
|
|
319
|
+
if (!state.isConfigMode)
|
|
320
|
+
return;
|
|
321
|
+
// Only clear if we're actually leaving this specific item
|
|
322
|
+
if (dragOverIndex === index) {
|
|
323
|
+
setDragOverIndex(null);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
const handleDrop = (e, dropIndex) => {
|
|
327
|
+
if (!state.isConfigMode || state.draggedItemIndex === null)
|
|
328
|
+
return;
|
|
329
|
+
e.preventDefault();
|
|
330
|
+
e.stopPropagation();
|
|
331
|
+
const dragIndex = state.draggedItemIndex;
|
|
332
|
+
if (dragIndex === dropIndex) {
|
|
333
|
+
setDragOverIndex(null);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const newItems = [...state.items];
|
|
337
|
+
const [draggedItem] = newItems.splice(dragIndex, 1);
|
|
338
|
+
newItems.splice(dropIndex, 0, draggedItem);
|
|
339
|
+
setState(s => ({
|
|
340
|
+
...s,
|
|
341
|
+
items: newItems,
|
|
342
|
+
draggedItemIndex: null,
|
|
343
|
+
}));
|
|
344
|
+
setDragOverIndex(null);
|
|
345
|
+
};
|
|
346
|
+
const handleDragEnd = () => {
|
|
347
|
+
setState(s => ({ ...s, draggedItemIndex: null }));
|
|
348
|
+
setDragOverIndex(null);
|
|
349
|
+
};
|
|
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.ConfigButton, { onClick: toggleConfigMode, "$isActive": state.isConfigMode, children: state.isConfigMode ? _jsx(IconApply, {}) : _jsx(IconSettings, {}) }), state.items.map((item, index) => {
|
|
351
|
+
// Get current state (disabled and onClick) from contextMenuItems
|
|
352
|
+
const currentState = getCurrentItemState(item.name);
|
|
353
|
+
const isDisabled = currentState.disabled || false;
|
|
354
|
+
const currentOnClick = currentState.onClick || item.onClick; // Fallback to stored onClick if not found
|
|
355
|
+
return (_jsxs(S.DraggableItem, { "$isDragging": state.draggedItemIndex === index, "$isDragOver": dragOverIndex === index && state.draggedItemIndex !== index, draggable: state.isConfigMode, 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: [state.isConfigMode ? (_jsx(S.MenuButton, { onClick: () => {
|
|
356
|
+
if (state.isConfigMode || isDisabled)
|
|
357
|
+
return;
|
|
358
|
+
if (currentOnClick) {
|
|
359
|
+
currentOnClick();
|
|
360
|
+
}
|
|
361
|
+
}, disabled: isDisabled && !state.isConfigMode, children: item.icon })) : (_jsx(TMTooltip, { content: item.name, position: "top", children: _jsx(S.MenuButton, { onClick: () => {
|
|
362
|
+
if (state.isConfigMode || isDisabled)
|
|
363
|
+
return;
|
|
364
|
+
if (currentOnClick) {
|
|
365
|
+
currentOnClick();
|
|
366
|
+
}
|
|
367
|
+
}, 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.MenuButton, { children: _jsx(IconMenuVertical, {}) }) }, Array.from(pinnedItemIds).join(','))), !state.isConfigMode && (_jsx(S.OrientationToggle, { "$orientation": state.orientation, onClick: toggleOrientation, children: _jsx(IconMenuKebab, {}) }))] }), showMaxItemsNotification && (_jsx("div", { style: { position: 'fixed', top: '20px', left: '50%', transform: 'translateX(-50%)', zIndex: 100001 }, children: _jsx(Notification, { title: "Maximum Items Reached", message: `You have reached the maximum number of pinned items (${maxItems}). Please unpin an item before adding a new one.`, mode: "warning", position: "top-center", duration: 4000, closable: true, stopOnMouseEnter: true, hasProgress: true, onClose: () => setShowMaxItemsNotification(false) }) }))] }));
|
|
369
|
+
};
|
|
370
|
+
export default TMFloatingMenuBar;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const Overlay: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
2
|
+
$visible: boolean;
|
|
3
|
+
}>> & string;
|
|
4
|
+
export declare const FloatingContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("styled-components/dist/types").Substitute<import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
5
|
+
ref?: ((instance: HTMLDivElement | 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<HTMLDivElement> | null | undefined;
|
|
6
|
+
}>, {
|
|
7
|
+
$x: number;
|
|
8
|
+
$y: number;
|
|
9
|
+
$orientation: "horizontal" | "vertical";
|
|
10
|
+
$isDragging: boolean;
|
|
11
|
+
$isConfigMode: boolean;
|
|
12
|
+
$isConstrained?: boolean;
|
|
13
|
+
}>, {
|
|
14
|
+
$x: number;
|
|
15
|
+
$y: number;
|
|
16
|
+
$orientation: "horizontal" | "vertical";
|
|
17
|
+
$isDragging: boolean;
|
|
18
|
+
$isConfigMode: boolean;
|
|
19
|
+
$isConstrained?: boolean;
|
|
20
|
+
}>> & string;
|
|
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
|
+
$orientation: "horizontal" | "vertical";
|
|
23
|
+
}>> & string;
|
|
24
|
+
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
|
+
$isActive?: boolean;
|
|
26
|
+
}>> & string;
|
|
27
|
+
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
|
+
$isActive?: boolean;
|
|
29
|
+
}>> & string;
|
|
30
|
+
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
|
+
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
|
+
$orientation: "horizontal" | "vertical";
|
|
33
|
+
}>> & string;
|
|
34
|
+
export declare const DraggableItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
35
|
+
$isDragging: boolean;
|
|
36
|
+
$isDragOver: boolean;
|
|
37
|
+
}>> & string;
|
|
38
|
+
export declare const ContextMenuWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import styled, { keyframes, css } from 'styled-components';
|
|
2
|
+
const fadeIn = keyframes `
|
|
3
|
+
from {
|
|
4
|
+
opacity: 0;
|
|
5
|
+
transform: scale(0.95);
|
|
6
|
+
}
|
|
7
|
+
to {
|
|
8
|
+
opacity: 1;
|
|
9
|
+
transform: scale(1);
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
const shake = keyframes `
|
|
13
|
+
0%, 100% { transform: translateX(0); }
|
|
14
|
+
25% { transform: translateX(-2px); }
|
|
15
|
+
75% { transform: translateX(2px); }
|
|
16
|
+
`;
|
|
17
|
+
export const Overlay = styled.div `
|
|
18
|
+
position: fixed;
|
|
19
|
+
top: 0;
|
|
20
|
+
left: 0;
|
|
21
|
+
right: 0;
|
|
22
|
+
bottom: 0;
|
|
23
|
+
background: rgba(255, 255, 255, 0.8);
|
|
24
|
+
z-index: 9998;
|
|
25
|
+
display: ${props => props.$visible ? 'block' : 'none'};
|
|
26
|
+
animation: ${fadeIn} 0.2s ease-out;
|
|
27
|
+
backdrop-filter: blur(2px);
|
|
28
|
+
|
|
29
|
+
[data-theme='dark'] & {
|
|
30
|
+
background: rgba(0, 0, 0, 0.8);
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
export const FloatingContainer = styled.div.attrs(props => ({
|
|
34
|
+
style: {
|
|
35
|
+
left: `${props.$x}px`,
|
|
36
|
+
top: `${props.$y}px`,
|
|
37
|
+
},
|
|
38
|
+
})) `
|
|
39
|
+
position: ${props => props.$isConstrained ? 'absolute' : 'fixed'};
|
|
40
|
+
z-index: ${props => props.$isConfigMode ? 9999 : 10000};
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: ${props => props.$orientation === 'horizontal' ? 'row' : 'column'};
|
|
43
|
+
align-items: center;
|
|
44
|
+
background: ${props => props.$isDragging || props.$isConfigMode
|
|
45
|
+
? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
|
46
|
+
: 'rgba(102, 126, 234, 0.9)'};
|
|
47
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
48
|
+
border-radius: 14px;
|
|
49
|
+
padding: 6px;
|
|
50
|
+
gap: 3px;
|
|
51
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
|
52
|
+
cursor: ${props => props.$isDragging ? 'grabbing' : 'default'};
|
|
53
|
+
user-select: none;
|
|
54
|
+
animation: ${props => props.$isConfigMode && css `${shake} 0.3s ease-in-out`};
|
|
55
|
+
transition: none;
|
|
56
|
+
|
|
57
|
+
&:hover {
|
|
58
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
59
|
+
border: 1px solid #667eea;
|
|
60
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3),
|
|
61
|
+
0 6px 16px rgba(0, 0, 0, 0.2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[data-theme='dark'] & {
|
|
65
|
+
background: ${props => props.$isDragging || props.$isConfigMode
|
|
66
|
+
? 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)'
|
|
67
|
+
: 'rgba(26, 26, 46, 0.9)'};
|
|
68
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
69
|
+
|
|
70
|
+
&:hover {
|
|
71
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
72
|
+
border: 1px solid #1a1a2e;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
export const GripHandle = styled.div `
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
padding: ${props => props.$orientation === 'horizontal' ? '10px 6px' : '6px 10px'};
|
|
81
|
+
cursor: grab;
|
|
82
|
+
color: rgba(255, 255, 255, 0.7);
|
|
83
|
+
transition: all 0.2s ease;
|
|
84
|
+
border-radius: 6px;
|
|
85
|
+
|
|
86
|
+
&:hover {
|
|
87
|
+
background: rgba(255, 255, 255, 0.1);
|
|
88
|
+
color: rgba(255, 255, 255, 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&:active {
|
|
92
|
+
cursor: grabbing;
|
|
93
|
+
background: rgba(255, 255, 255, 0.15);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
svg {
|
|
97
|
+
width: 14px;
|
|
98
|
+
height: 14px;
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
export const MenuButton = styled.button `
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
width: 34px;
|
|
106
|
+
height: 34px;
|
|
107
|
+
background: transparent;
|
|
108
|
+
border: none;
|
|
109
|
+
border-radius: 8px;
|
|
110
|
+
color: white;
|
|
111
|
+
font-size: 16px;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
transition: all 0.2s ease;
|
|
114
|
+
position: relative;
|
|
115
|
+
|
|
116
|
+
&:hover:not(:disabled) {
|
|
117
|
+
background: rgba(255, 255, 255, 0.2);
|
|
118
|
+
transform: scale(1.1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&:active:not(:disabled) {
|
|
122
|
+
transform: scale(0.95);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&:disabled {
|
|
126
|
+
opacity: 0.3;
|
|
127
|
+
cursor: not-allowed;
|
|
128
|
+
background: rgba(255, 255, 255, 0.05);
|
|
129
|
+
color: rgba(255, 255, 255, 0.4);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
svg {
|
|
133
|
+
width: 18px;
|
|
134
|
+
height: 18px;
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
export const ConfigButton = styled.button `
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: center;
|
|
141
|
+
width: 34px;
|
|
142
|
+
height: 34px;
|
|
143
|
+
background: ${props => props.$isActive
|
|
144
|
+
? 'rgba(0, 0, 0, 0.2)'
|
|
145
|
+
: 'rgba(0, 0, 0, 0.1)'};
|
|
146
|
+
border: 1px solid ${props => props.$isActive
|
|
147
|
+
? 'rgba(255, 255, 255, 0.3)'
|
|
148
|
+
: 'rgba(255, 255, 255, 0.15)'};
|
|
149
|
+
border-radius: 8px;
|
|
150
|
+
color: white;
|
|
151
|
+
font-size: 16px;
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
transition: all 0.2s ease;
|
|
154
|
+
position: relative;
|
|
155
|
+
|
|
156
|
+
&:hover {
|
|
157
|
+
background: ${props => props.$isActive
|
|
158
|
+
? 'rgba(0, 0, 0, 0.25)'
|
|
159
|
+
: 'rgba(0, 0, 0, 0.15)'};
|
|
160
|
+
border-color: rgba(255, 255, 255, 0.35);
|
|
161
|
+
transform: scale(1.05);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&:active {
|
|
165
|
+
transform: scale(0.95);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
svg {
|
|
169
|
+
width: 20px;
|
|
170
|
+
height: 20px;
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
export const RemoveButton = styled.button `
|
|
174
|
+
position: absolute;
|
|
175
|
+
top: -6px;
|
|
176
|
+
right: -6px;
|
|
177
|
+
width: 20px;
|
|
178
|
+
height: 20px;
|
|
179
|
+
background: #ef4444;
|
|
180
|
+
border: 2px solid white;
|
|
181
|
+
border-radius: 50%;
|
|
182
|
+
color: white;
|
|
183
|
+
font-size: 12px;
|
|
184
|
+
font-weight: bold;
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
justify-content: center;
|
|
189
|
+
transition: all 0.2s ease;
|
|
190
|
+
z-index: 1;
|
|
191
|
+
|
|
192
|
+
&:hover {
|
|
193
|
+
background: #dc2626;
|
|
194
|
+
transform: scale(1.15);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
&:active {
|
|
198
|
+
transform: scale(0.9);
|
|
199
|
+
}
|
|
200
|
+
`;
|
|
201
|
+
export const OrientationToggle = styled.button `
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: center;
|
|
205
|
+
width: 20px;
|
|
206
|
+
height: 20px;
|
|
207
|
+
background: transparent;
|
|
208
|
+
border: none;
|
|
209
|
+
border-radius: 4px;
|
|
210
|
+
color: rgba(255, 255, 255, 0.5);
|
|
211
|
+
font-size: 10px;
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
transition: all 0.2s ease;
|
|
214
|
+
padding: 2px;
|
|
215
|
+
transform: ${props => props.$orientation === 'vertical' ? 'rotate(90deg)' : 'rotate(0deg)'};
|
|
216
|
+
|
|
217
|
+
&:hover {
|
|
218
|
+
background: rgba(255, 255, 255, 0.1);
|
|
219
|
+
color: rgba(255, 255, 255, 0.8);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&:active {
|
|
223
|
+
transform: ${props => props.$orientation === 'vertical' ? 'rotate(90deg) scale(0.9)' : 'scale(0.9)'};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
svg {
|
|
227
|
+
width: 10px;
|
|
228
|
+
height: 10px;
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
export const DraggableItem = styled.div `
|
|
232
|
+
position: relative;
|
|
233
|
+
opacity: ${props => props.$isDragging ? 0.3 : 1};
|
|
234
|
+
transform: ${props => {
|
|
235
|
+
if (props.$isDragging)
|
|
236
|
+
return 'scale(1.1) rotate(5deg)';
|
|
237
|
+
if (props.$isDragOver)
|
|
238
|
+
return 'scale(1.05)';
|
|
239
|
+
return 'scale(1)';
|
|
240
|
+
}};
|
|
241
|
+
transition: all 0.2s ease;
|
|
242
|
+
filter: ${props => props.$isDragging ? 'brightness(1.3)' : 'brightness(1)'};
|
|
243
|
+
|
|
244
|
+
${props => props.$isDragging && css `
|
|
245
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
|
246
|
+
z-index: 100;
|
|
247
|
+
`}
|
|
248
|
+
|
|
249
|
+
${props => props.$isDragOver && css `
|
|
250
|
+
&::before {
|
|
251
|
+
content: '';
|
|
252
|
+
position: absolute;
|
|
253
|
+
top: -4px;
|
|
254
|
+
left: 50%;
|
|
255
|
+
transform: translateX(-50%);
|
|
256
|
+
width: 40px;
|
|
257
|
+
height: 3px;
|
|
258
|
+
background: rgba(255, 255, 255, 0.8);
|
|
259
|
+
border-radius: 2px;
|
|
260
|
+
box-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
|
|
261
|
+
}
|
|
262
|
+
`}
|
|
263
|
+
`;
|
|
264
|
+
export const ContextMenuWrapper = styled.div `
|
|
265
|
+
position: static;
|
|
266
|
+
display: contents;
|
|
267
|
+
`;
|