setShow(false)}
- />
- )}
- {show && (
-
{renderChildren()}
- )}
+ {show &&
setShow(false)} />}
+ {show &&
{renderChildren()}
}
);
}
diff --git a/app/containers/MassEnergizeSuperAdmin/Pages/CustomNavigationConfiguration.js b/app/containers/MassEnergizeSuperAdmin/Pages/CustomNavigationConfiguration.js
index 4c843bcbb..fc828feca 100644
--- a/app/containers/MassEnergizeSuperAdmin/Pages/CustomNavigationConfiguration.js
+++ b/app/containers/MassEnergizeSuperAdmin/Pages/CustomNavigationConfiguration.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useMemo, useState } from "react";
import MEPaperBlock from "../ME Tools/paper block/MEPaperBlock";
import { Button, Link, Paper, TextField, Tooltip, Typography } from "@mui/material";
import BrandCustomization from "./BrandCustomization";
@@ -14,12 +14,16 @@ import Loading from "dan-components/Loading";
import MEDropdown from "../ME Tools/dropdown/MEDropdown";
import { apiCall } from "../../../utils/messenger";
import { fetchParamsFromURL, smartString } from "../../../utils/common";
+import { FLAGS } from "../../../components/FeatureFlags/flags";
+import Feature from "../../../components/FeatureFlags/Feature";
const NAVIGATION = "navigation";
const FOOTER = "footer";
const BRAND = "brand";
const INIT = "INIT";
const RESET = "RESET";
+const UP = "up";
+const DOWN = "down";
const ACTIVITIES = {
edit: { key: "edit", description: "This item was edited, and unsaved!", color: "#fffcf3" },
@@ -52,9 +56,18 @@ function CustomNavigationConfiguration() {
const [menuProfileStash, stashMenuProfiles] = useState([]);
const [error, setError] = useState(null);
const [brandForm, setBrandForm] = useState({});
+ // ----- For Dragging and Dropping ------
+ const [dragged, setBeingDragged] = useState(null);
+ const [mouse, setMouse] = useState([]);
+ const [dropZone, setDropZone] = useState(null);
const menuHeap = useSelector((state) => state.getIn(["menuConfigurations"]));
+ const communities = useSelector((state) => state.getIn(["communities"]));
const { comId: community_id } = fetchParamsFromURL(window.location, "comId");
+ const community = useMemo(() => communities?.find((c) => c?.id?.toString() === community_id?.toString(), []), [
+ communities
+ ]);
+ // const community = communities?.find((c) => c?.id?.toString() === community_id?.toString(), []);
const dispatch = useDispatch();
const keepInRedux = (menuProfiles, options) =>
dispatch(
@@ -105,6 +118,113 @@ function CustomNavigationConfiguration() {
placeDetails(menuObj);
}, []);
+ // Add mouse move handler
+ useEffect(() => {
+ const handler = (e) => {
+ setMouse([e.x, e.y]);
+ };
+ document.addEventListener("mousemove", handler);
+ return () => document.removeEventListener("mousemove", handler);
+ }, []);
+
+ // Track closest drop-zone to dragged item
+ useEffect(() => {
+ if (dragged !== null) {
+ // get all drop-zones
+ const elements = Array.from(document.getElementsByClassName("nav-drop-zone"));
+ const parentIds = elements.map((e) => e.getAttribute("data-parent-ids"));
+ const indexes = elements.map((e) => e.getAttribute("data-index"));
+ const where = elements.map((e) => e.getAttribute("data-position"));
+ const idsOfItems = elements.map((e) => e.getAttribute("data-id"));
+ // get all drop-zones' y-axis position
+ const positions = elements.map((e) => e.getBoundingClientRect().top);
+ // get the difference with the mouse's y position
+ const absDifferences = positions.map((v) => Math.abs(v - mouse[1]));
+ // get the index of the dropzone closest to the mouse
+ let result = absDifferences.indexOf(Math.min(...absDifferences));
+ const placement = where[result];
+ setDropZone({
+ parentIds: parentIds[result],
+ index: indexes[result], // The index of the item that is currently at the position that we are trying to drop the dragged item
+ uniqueId: `${parentIds[result]}->${indexes[result]}->${placement}`,
+ id: idsOfItems[result],
+ placement //up or down : Helps determine whether to add or subtract from index
+ });
+ }
+ }, [dragged, mouse]);
+
+ useEffect(() => {
+ const handler = (e) => {
+ if (!dragged) return;
+ e.preventDefault();
+ setBeingDragged(null);
+ reorder();
+ };
+ document.addEventListener("mouseup", handler);
+ return () => document.removeEventListener("mouseup", handler);
+ });
+
+ /**
+ *
+ * @param {*} dragObject - Item that is set in the state when drage starts
+ * @param {*} list - original list of menu items
+ * @returns
+ */
+ const removeDraggedItem = (dragObject, list) => {
+ if (!dragObject) return list;
+ const { item, parents } = dragObject;
+ const parentsArr = Object.values(parents);
+ const isTopLevelItem = parentsArr?.length === 0;
+ if (isTopLevelItem) return list.filter((m) => m?.id !== item?.id);
+ const immediateParent = parentsArr[parentsArr.length - 1];
+ let sibblings = immediateParent?.children || [];
+ sibblings = sibblings.filter((s) => s?.id !== item?.id);
+ parents[(immediateParent?.id)] = { ...immediateParent, children: sibblings };
+ const newObj = rollUp(Object.entries(parents));
+ return insertIntoTopLevelList(newObj, list);
+ };
+ const unWrapTo = (parentIdList, mother) => {
+ const tracker = { [mother?.id]: { ...mother, children: [...(mother?.children || [])] } };
+ parentIdList = parentIdList.slice(1);
+ let current = mother;
+ for (let i = 0; i < parentIdList.length; i++) {
+ const key = parentIdList[i];
+ const found = current?.children?.find((m) => m?.id === key);
+ tracker[(found?.id)] = found;
+ current = found;
+ }
+ return tracker;
+ };
+
+ const dragIntoNewPosition = (positionInformation, topLevelList) => {
+ const { parentIds, index, placement } = positionInformation;
+ const newPosition = placement === UP ? index : Number(index) + 1;
+ const pIds = (parentIds?.split(":") || []).filter(Boolean);
+ const isTopLevelItem = pIds.length === 0;
+ if (isTopLevelItem) {
+ const sibblings = [...topLevelList];
+ sibblings.splice(newPosition, 0, dragged?.item);
+ return sibblings;
+ }
+ const mother = topLevelList?.find((m) => m?.id === pIds[0]);
+ const parentsAsObject = unWrapTo(pIds, mother);
+ const immediateParent = Object.values(parentsAsObject)[pIds.length - 1];
+ const sibblings = immediateParent?.children || [];
+ sibblings.splice(newPosition, 0, dragged?.item);
+ immediateParent.children = sibblings;
+ parentsAsObject[(immediateParent?.id)] = immediateParent;
+ const newObj = rollUp(Object.entries(parentsAsObject));
+ return insertIntoTopLevelList(newObj, topLevelList);
+ };
+
+ const reorder = () => {
+ if (!dropZone || dropZone?.id === dragged?.item?.id) return;
+ // Dragged item has been removed, top level list is modified, and ready for insertion
+ const listAfterDraggedIsRemoved = removeDraggedItem(dragged, [...menuItems]);
+ const listAfterDragInsertion = dragIntoNewPosition(dropZone, listAfterDraggedIsRemoved);
+ setStateAndExport(listAfterDragInsertion, trackEdited);
+ };
+
const updateForm = (key, value, reset = false) => {
if (reset) return setForm({});
setForm({ ...form, [key]: value });
@@ -186,15 +306,22 @@ function CustomNavigationConfiguration() {
}
return acc;
};
- const addToTopLevelMenu = (obj, changeTree = null) => {
- const ind = menuItems.findIndex((m) => m?.id === obj?.id);
- const copied = [...menuItems];
- if (ind === -1) copied.push(obj);
- else copied[ind] = obj;
- setMenu(copied);
- const profileList = recreateProfileFromList(copied);
+ const insertIntoTopLevelList = (obj, array) => {
+ array = [...array];
+ const ind = array.findIndex((m) => m?.id === obj?.id);
+ if (ind === -1) array.push(obj);
+ else array[ind] = obj;
+ return array;
+ };
+ const setStateAndExport = (newState, changeTree = null) => {
+ setMenu(newState);
+ const profileList = recreateProfileFromList(newState);
keepInRedux(profileList, { changeTree });
};
+ const addToTopLevelMenu = (obj, changeTree = null) => {
+ const copied = insertIntoTopLevelList(obj, menuItems);
+ setStateAndExport(copied, changeTree);
+ };
const removeItem = (itemObj, parents, options = {}) => {
closeModal();
@@ -248,19 +375,28 @@ function CustomNavigationConfiguration() {
});
};
+ const combineKeysWithDelimiter = (keys, delimiter = ":") => keys.join(delimiter);
const renderMenuItems = (items, margin = 0, parents = {}, options = {}) => {
if (!items?.length) return [];
- // items = items.sort((a, b) => a?.order - b?.order); //If you want to sort the items by order, uncomment this line
return items.map(({ children, ...rest }, index) => {
const { parentTraits } = options || {};
const editTrail = trackEdited[(rest?.id)];
let activity = editTrail ? ACTIVITIES[(editTrail?.activity)] : null;
const isRemoved = activity?.key === ACTIVITIES.remove.key;
+ const isBeingDragged = dragged?.item?.id === rest?.id;
+
+ const parentKeys = combineKeysWithDelimiter(Object.keys(parents));
+ const isFirstItem = index === 0;
return (
-
+
{margin ?
: <>>}
moveUp(up, { ...rest, children }, parents, { index, sibblings: items })}
@@ -425,7 +562,29 @@ function CustomNavigationConfiguration() {
background: "#fafafa"
}}
>
- {renderMenuItems(menuItems)}
+
+ {dragged !== null && (
+
+
+ {dragged?.item?.name}
+
+ )}
+
+ {renderMenuItems(menuItems)}
+