diff --git a/src/Components/Layout/SidebarMenu.jsx b/src/Components/Layout/SidebarMenu.jsx
index 87b61e2..70058ed 100644
--- a/src/Components/Layout/SidebarMenu.jsx
+++ b/src/Components/Layout/SidebarMenu.jsx
@@ -6,6 +6,7 @@ import {
IconButton,
Tooltip,
Box,
+ alpha
} from "@mui/material";
import SidebarFooter from "./SidebarMenuComponents/SidebarFooter";
import useSidebarResize from "../hooks/useSidebarResize";
@@ -20,7 +21,8 @@ import {
PointerSensor,
useSensor,
useSensors,
- DragOverlay
+ DragOverlay,
+ MeasuringStrategy
} from "@dnd-kit/core";
import {
SortableContext,
@@ -38,13 +40,15 @@ const SidebarMenu = ({
user,
}) => {
const [collapsed, setCollapsed] = useState(false);
- const { sidebarWidth, startResizing } = useSidebarResize(290);
+ const { sidebarWidth, startResizing } = useSidebarResize(320); // Увеличил минимальную ширину
const [menuItems, setMenuItems] = useState(data.items || []);
const [activeItem, setActiveItem] = useState(null);
+ const [hoveredItem, setHoveredItem] = useState(null);
+ const [dropIndicator, setDropIndicator] = useState({ show: false, position: null, targetId: null });
const sensors = useSensors(useSensor(PointerSensor, {
activationConstraint: {
- distance: 8,
+ distance: 4,
},
}));
@@ -61,9 +65,12 @@ const SidebarMenu = ({
}
}, [data]);
- const handleToggleCollapse = () => setCollapsed(!collapsed);
+ const handleToggleCollapse = () => {
+ setCollapsed(!collapsed);
+ setHoveredItem(null);
+ };
- // Функция для поиска элемента по ID во всем дереве
+ // Функции для работы с деревом (остаются без изменений)
const findItemInTree = (items, id) => {
for (const item of items) {
if (item.id === id) return item;
@@ -75,7 +82,6 @@ const SidebarMenu = ({
return null;
};
- // Функция для удаления элемента из дерева
const removeItemFromTree = (items, id) => {
return items.filter(item => {
if (item.id === id) return false;
@@ -86,7 +92,6 @@ const SidebarMenu = ({
});
};
- // Функция для добавления элемента в конкретную папку
const addItemToFolder = (items, folderId, newItem) => {
return items.map(item => {
if (item.id === folderId) {
@@ -105,7 +110,6 @@ const SidebarMenu = ({
});
};
- // Функция для поиска родителя элемента
const findParent = (items, childId, parent = null) => {
for (const item of items) {
if (item.id === childId) return parent;
@@ -117,7 +121,6 @@ const SidebarMenu = ({
return null;
};
- // Функция для добавления элемента на тот же уровень
const addItemAtSameLevel = (items, parentId, newItem, afterId = null) => {
return items.map(item => {
if (item.id === parentId) {
@@ -143,11 +146,13 @@ const SidebarMenu = ({
const { active } = event;
const item = findItemInTree(menuItems, active.id);
setActiveItem(item);
+ setDropIndicator({ show: false, position: null, targetId: null });
};
-
const handleDragEnd = (event) => {
const { active, over } = event;
setActiveItem(null);
+ setHoveredItem(null);
+ setDropIndicator({ show: false, position: null, targetId: null });
if (!over) return;
if (active.id === over.id) return;
@@ -155,69 +160,190 @@ const SidebarMenu = ({
const draggedItem = findItemInTree(menuItems, active.id);
if (!draggedItem) return;
- // Определяем тип целевого элемента (папка или элемент)
const overItem = findItemInTree(menuItems, over.id);
- const isOverFolder = overItem && Array.isArray(overItem.items);
+
+ // Проверяем, не пытаемся ли переместить элемент в его же потомка
+ if (isDescendant(draggedItem, overItem)) {
+ return;
+ }
let newTree;
- if (isOverFolder) {
- // Перемещаем в папку
+ if (dropIndicator.position === 'inside' && overItem && Array.isArray(overItem.items)) {
+ // Вставка внутрь папки
newTree = removeItemFromTree([...menuItems], active.id);
newTree = addItemToFolder(newTree, over.id, draggedItem);
} else {
- // Перемещаем на тот же уровень
+ // Вставка на том же уровне
const overParent = findParent(menuItems, over.id);
if (!overParent) return;
- // Удаляем из старого места
newTree = removeItemFromTree([...menuItems], active.id);
- // Добавляем на новый уровень
- newTree = addItemAtSameLevel(newTree, overParent.id, draggedItem, over.id);
+ // Определяем позицию для вставки
+ let insertAfterId = null;
+ if (dropIndicator.position === 'below') {
+ insertAfterId = over.id;
+ } else if (dropIndicator.position === 'above') {
+ const siblings = overParent.items || [];
+ const overIndex = siblings.findIndex(item => item.id === over.id);
+ if (overIndex > 0) {
+ insertAfterId = siblings[overIndex - 1].id;
+ }
+ }
+
+ newTree = addItemAtSameLevel(newTree, overParent.id, draggedItem, insertAfterId);
}
setMenuItems(newTree);
localStorage.setItem("menuTree", JSON.stringify(newTree));
};
+ const handleDragOver = (event) => {
+ const { active, over } = event;
+
+ if (!over) {
+ setDropIndicator({ show: false, position: null, targetId: null });
+ return;
+ }
+
+ const overItem = findItemInTree(menuItems, over.id);
+ const activeItem = findItemInTree(menuItems, active.id);
+
+ if (!overItem || !activeItem || active.id === over.id) {
+ setDropIndicator({ show: false, position: null, targetId: null });
+ return;
+ }
+
+ // Проверяем, можно ли перемещать элемент
+ if (isDescendant(activeItem, overItem)) {
+ setDropIndicator({ show: false, position: null, targetId: null });
+ return;
+ }
+
+ const overRect = over.rect.current;
+ if (!overRect) return;
+
+ const relativeY = event.delta.y;
+ const isOverFolder = overItem && Array.isArray(overItem.items);
+ const isTopHalf = relativeY < overRect.height * 0.4;
+ const isBottomHalf = relativeY > overRect.height * 0.6;
+
+ if (isOverFolder && !isTopHalf && !isBottomHalf) {
+ // Показываем индикатор для вставки в папку
+ setDropIndicator({
+ show: true,
+ position: 'inside',
+ targetId: over.id
+ });
+ setHoveredItem(over.id);
+ } else if (isTopHalf) {
+ // Показываем индикатор для вставки выше
+ setDropIndicator({
+ show: true,
+ position: 'above',
+ targetId: over.id
+ });
+ setHoveredItem(null);
+ } else if (isBottomHalf) {
+ // Показываем индикатор для вставки ниже
+ setDropIndicator({
+ show: true,
+ position: 'below',
+ targetId: over.id
+ });
+ setHoveredItem(null);
+ } else {
+ setDropIndicator({ show: false, position: null, targetId: null });
+ setHoveredItem(null);
+ }
+ };
+
+ const isDescendant = (parent, child) => {
+ if (!parent || !child || !parent.items) return false;
+
+ const checkChildren = (items, targetId) => {
+ for (const item of items) {
+ if (item.id === targetId) return true;
+ if (item.items && checkChildren(item.items, targetId)) return true;
+ }
+ return false;
+ };
+
+ return checkChildren(parent.items, child.id);
+ };
+
const SidebarResizer = styled("div")(({ theme }) => ({
- width: "4px",
- cursor: "ew-resize",
- backgroundColor: "transparent",
+ width: "3px",
+ cursor: "col-resize",
+ backgroundColor: alpha(theme.palette.primary.main, 0.3),
"&:hover": {
- backgroundColor: theme.palette.action.hover,
+ backgroundColor: theme.palette.primary.main,
},
height: "100%",
position: "absolute",
top: 0,
right: 0,
zIndex: 1000,
+ transition: "background-color 0.2s ease",
}));
+ const DropIndicator = ({ position, targetId }) => {
+ if (!targetId) return null;
+
+ return (
+
+
{menuItems.map((item) => (
-