diff --git a/package.json b/package.json
index 770e145..a485b9a 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.12.0",
+ "sass": "^1.83.0",
"vite": "^6.0.1"
}
-}
\ No newline at end of file
+}
diff --git a/src/App.jsx b/src/App.jsx
index cc74cbb..44743ea 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,6 +1,6 @@
import { useState } from "react";
-import "./App.css";
-import { MenuBuilder } from "./Component/MenuBuilder";
+import "./app.scss";
+import MenuWrapper from "./Component/MenuWrapper";
const initMenus = [
{
@@ -59,18 +59,18 @@ const initMenus = [
},
];
-function App() {
+const App = () => {
const [menus, setMenus] = useState(initMenus);
return (
-
+
Wordpress like menu structure
-
+
);
-}
+};
export default App;
diff --git a/src/Component/CustomDragLayer.jsx b/src/Component/CustomDragLayer.jsx
index 259ac48..3b86158 100644
--- a/src/Component/CustomDragLayer.jsx
+++ b/src/Component/CustomDragLayer.jsx
@@ -51,8 +51,6 @@ const CustomDragLayer = ({ menuitems }) => {
depth={item.depth}
clone
childCount={getChildCount(menuitems, item.id) + 1}
- value={item.id.toString()}
- otherfields={item}
menu={item}
childs={getChildrens(menuitems, item.id)}
/>
diff --git a/src/Component/Menu/MenuItem.jsx b/src/Component/Menu/MenuItem.jsx
index 5011731..d1acc27 100644
--- a/src/Component/Menu/MenuItem.jsx
+++ b/src/Component/Menu/MenuItem.jsx
@@ -27,12 +27,11 @@ export const MenuItem = ({
const [{ handlerId }, setDroppableNodeRef] = useDrop({
accept: [ITEM_TYPE],
collect(monitor) {
- return { handlerId: monitor.getHandlerId() };
+ return { handlerId: monitor.getHandlerId()};
},
drop: (item) => {
onDragEnd({ id: item.id }, { id: menu.id });
},
-
hover: (item, monitor) => {
if (!dndRef.current) {
return;
@@ -55,7 +54,7 @@ export const MenuItem = ({
collect: (monitor) => {
const isDragging = monitor.isDragging();
if (isDragging) {
- onDragStart({ active: { id: menu.id } });
+ onDragStart(menu.id);
}
return { isDragging };
@@ -74,8 +73,9 @@ export const MenuItem = ({
data-handler-id={handlerId}
data-depth={depth}
className={classNames({
- Wrapper: true,
- dragging: isDragging,
+ [`${classPrefix}-item-wrapper`]: true,
+ [`${classPrefix}-item-dragging`]: isDragging,
+ // dragging: isDragging,
})}
style={{
...(!clone
@@ -83,9 +83,7 @@ export const MenuItem = ({
paddingLeft: `${INDENTATION_WIDTH * depth}px`,
}
: {}),
- }}
- {...props}
- >
+ }}>
+ }}>
+ display:
+ activeId || clone ? "none" : menu?.parentId ? "block" : "none",
+ }}>
{menu?.name}{" "}
+ }}>
{depth > 0 ? "sub item" : ""}
@@ -150,8 +146,7 @@ const RecursiveItem = (props) => {
paddingLeft: "0.5rem",
fontWeight: "600",
fontSize: "13px",
- }}
- >
+ }}>
{props.child.name}{" "}
{
fontStyle: "italic",
color: "#50575e",
marginLeft: "4px",
- }}
- >
+ }}>
sub item
diff --git a/src/Component/MenuBuilder.jsx b/src/Component/MenuWrapper.jsx
old mode 100755
new mode 100644
similarity index 79%
rename from src/Component/MenuBuilder.jsx
rename to src/Component/MenuWrapper.jsx
index 42d77d3..39a769b
--- a/src/Component/MenuBuilder.jsx
+++ b/src/Component/MenuWrapper.jsx
@@ -13,8 +13,17 @@ import {
import CustomDragLayer from "./CustomDragLayer";
import { MenuItem } from "./Menu/MenuItem";
-export function MenuBuilder({ items: itemsProps, setItems }) {
- const menuList = generateItemChildren(itemsProps);
+const generateItemChildren = (menuList) => {
+ return menuList.map((menu) => {
+ return {
+ ...menu,
+ children: menu?.children ? generateItemChildren(menu.children) : [],
+ };
+ });
+};
+
+const MenuWrapper = ({ menus: menuData, setMenus }) => {
+ const menuList = generateItemChildren(menuData);
const [activeId, setActiveId] = useState(null);
const [overId, setOverId] = useState(null);
const [offsetLeft, setOffsetLeft] = useState(0);
@@ -33,10 +42,8 @@ export function MenuBuilder({ items: itemsProps, setItems }) {
);
}, [activeId, menuList]);
- let projected =
- activeId && overId
- ? getProjection(flattenedMenus, activeId, overId, offsetLeft)
- : null;
+ // This is the projected position of the dragged item over the hovered item. Initially set to null
+ let projected = null;
const handleOnHover = (dragId, hoverId, deltaX) => {
const { depth, parentId } = getProjection(
@@ -59,7 +66,7 @@ export function MenuBuilder({ items: itemsProps, setItems }) {
const sortedItems = arrayMove(clonedItems, activeIndex, overIndex);
const newItems = buildTree(sortedItems);
- setItems(newItems);
+ setMenus(newItems);
};
const getBranchPathHeight = (menu) => {
@@ -75,60 +82,29 @@ export function MenuBuilder({ items: itemsProps, setItems }) {
return "0px";
};
- return (
-
- {flattenedMenus.map((menu, index) => (
-
- ))}
-
-
-
- );
-
- function handleDragStart({ active: { id: aId } }) {
- if (activeId === aId || overId === aId) return;
- setActiveId(aId);
- setOverId(aId);
- }
+ const handleDragStart = (menuId) => {
+ if (activeId === menuId || overId === menuId) return;
+ setActiveId(menuId);
+ setOverId(menuId);
+ };
- function handleDragOver(deltaX, id) {
+ const handleDragOver = (deltaX, id) => {
setOffsetLeft(deltaX);
if (overId === id) return;
setOverId(id ?? null);
- }
+ };
- function handleDragEnd() {
- const active = { id: activeId };
+ const handleDragEnd = () => {
const over = { id: overId };
+
if (projected && over) {
const { depth, parentId } = projected;
const clonedItems = JSON.parse(JSON.stringify(flattenTree(menuList)));
const overIndex = clonedItems.findIndex(({ id }) => id === over.id);
- const activeIndex = clonedItems.findIndex(({ id }) => id === active.id);
+ const activeIndex = clonedItems.findIndex(({ id }) => id === activeId);
const activeTreeItem = clonedItems[activeIndex];
clonedItems[activeIndex] = { ...activeTreeItem, depth, parentId };
@@ -136,24 +112,48 @@ export function MenuBuilder({ items: itemsProps, setItems }) {
const sortedItems = arrayMove(clonedItems, activeIndex, overIndex);
const newItems = buildTree(sortedItems);
- setItems(newItems);
+ setMenus(newItems);
}
resetState();
- }
+ };
- function resetState() {
+ const resetState = () => {
setOverId(null);
setActiveId(null);
setOffsetLeft(0);
+ };
+
+ // Get the projection of the dragged item over the hovered item
+ if (activeId && overId) {
+ projected = getProjection(flattenedMenus, activeId, overId, offsetLeft);
}
-}
-const generateItemChildren = (menuList) => {
- return menuList.map((menu) => {
- return {
- ...menu,
- children: menu?.children ? generateItemChildren(menu.children) : [],
- };
- });
+ return (
+
+ {flattenedMenus.map((menu, index) => (
+
+ ))}
+
+
+
+ );
};
+
+export default MenuWrapper;
diff --git a/src/App.css b/src/app.scss
similarity index 75%
rename from src/App.css
rename to src/app.scss
index e6f69d7..25401a6 100644
--- a/src/App.css
+++ b/src/app.scss
@@ -1,3 +1,5 @@
+$class-prefix: "lgmenu";
+
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
@@ -17,14 +19,14 @@ body {
}
/* Wrapper styles */
-.Wrapper {
+.#{$class-prefix}-item-wrapper {
list-style: none;
box-sizing: border-box;
margin-bottom: -1px;
-webkit-font-smoothing: subpixel-antialiased;
}
-.Wrapper.clone {
+.#{$class-prefix}-item-wrapper.clone {
display: inline-block;
pointer-events: none;
padding: 0;
@@ -32,7 +34,7 @@ body {
padding-top: 5px;
}
-.Wrapper.clone .TreeItem {
+.#{$class-prefix}-item-wrapper.clone .TreeItem {
--vertical-padding: 5px;
padding-right: 24px;
border-radius: 4px;
@@ -41,14 +43,14 @@ body {
min-width: 414px;
}
-.Wrapper.dragging {
+.#{$class-prefix}-item-wrapper.#{$class-prefix}-item-dragging {
opacity: 1;
position: relative;
z-index: 1;
margin-bottom: -1px;
}
-.Wrapper.dragging .TreeItem {
+.#{$class-prefix}-item-wrapper.#{$class-prefix}-item-dragging .TreeItem {
position: relative;
width: "100%";
max-width: 414px;
@@ -59,12 +61,12 @@ body {
background-color: #fff;
}
-.Wrapper.dragging .TreeItem > * {
+.#{$class-prefix}-item-wrapper.#{$class-prefix}-item-dragging .TreeItem > * {
opacity: 0;
height: 0;
}
-.Wrapper.dragging .TreeItem > * {
+.#{$class-prefix}-item-wrapper.#{$class-prefix}-item-dragging .TreeItem > * {
box-shadow: none;
background-color: transparent;
}
@@ -110,38 +112,6 @@ body {
flex-direction: column;
}
-/* Disable Selection styles */
-.disableSelection,
-.clone .Text,
-.clone .Count {
- user-select: none;
- -webkit-user-select: none;
-}
-
-/* Collapse styles */
-.Collapse svg {
- transition: transform 250ms ease;
-}
-
-.Collapse.collapsed svg {
- transform: rotate(-90deg);
-}
-
-@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
-
-@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
-}
-
.navigation-item-path {
display: block;
position: absolute;
diff --git a/src/main.jsx b/src/main.jsx
index 2bad55b..6e69881 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -3,6 +3,9 @@ import App from "./App.jsx";
import { HTML5Backend } from "react-dnd-html5-backend";
import { DndProvider } from "react-dnd";
+// global variables
+window.classPrefix = "lgmenu";
+
createRoot(document.getElementById("I-am-groot")).render(