diff --git a/frontend/src/AppBuilder/AppCanvas/Container.jsx b/frontend/src/AppBuilder/AppCanvas/Container.jsx index e622e1a2cd..fcc0a95d4e 100644 --- a/frontend/src/AppBuilder/AppCanvas/Container.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Container.jsx @@ -20,6 +20,7 @@ import NoComponentCanvasContainer from './NoComponentCanvasContainer'; import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants'; import { isPDFSupported } from '@/_helpers/appUtils'; import toast from 'react-hot-toast'; +import useSortedComponents from '../_hooks/useSortedComponents'; //TODO: Revisit the logic of height (dropRef) @@ -146,6 +147,8 @@ export const Container = React.memo( [setLastCanvasClickPosition] ); + const sortedComponents = useSortedComponents(components, currentLayout, id); + return (
- {components.map((id) => ( + {sortedComponents.map((id) => ( state.setReorderContainerChildren, shallow); useEffect(() => { const selectedSet = new Set(selectedComponents); @@ -536,6 +537,7 @@ export default function Grid({ gridWidth, currentLayout }) { }) ); } + setReorderContainerChildren(draggedOverElemId ?? 'canvas'); } catch (error) { console.error('Error dragging group', error); } @@ -696,6 +698,7 @@ export default function Grid({ gridWidth, currentLayout }) { resizeData.gw = _gridWidth; } handleResizeStop([resizeData]); + setReorderContainerChildren(currentWidget?.parent ?? 'canvas'); } catch (error) { console.error('ResizeEnd error ->', error); } @@ -775,6 +778,11 @@ export default function Grid({ gridWidth, currentLayout }) { ev.target.style.transform = `translate(${posX}px, ${posY}px)`; }); } + + const groupParentId = + boxList.find(({ id }) => id === groupResizeDataRef.current[0].target.id)?.parent ?? 'canvas'; + setReorderContainerChildren(groupParentId); + groupResizeDataRef.current = []; reloadGrid(); } catch (error) { @@ -841,6 +849,8 @@ export default function Grid({ gridWidth, currentLayout }) { useStore.getState().setDraggingComponentId(null); isDraggingRef.current = false; } + + const oldParentId = boxList.find((b) => b.id === e.target.id)?.parent ?? 'canvas'; prevDragParentId.current = null; newDragParentId.current = null; setDragParentId(null); @@ -880,6 +890,12 @@ export default function Grid({ gridWidth, currentLayout }) { // Apply transform for smooth transition e.target.style.transform = `translate(${left}px, ${top}px)`; + // Force reordering of conatiner if the parent has not changed + const newParentId = target.slotId === 'real-canvas' ? 'canvas' : target.slotId; + if (oldParentId === newParentId) { + setReorderContainerChildren(newParentId); + } + // Select the dragged component after drop setTimeout(() => setSelectedComponents([dragged.id])); } catch (error) { diff --git a/frontend/src/AppBuilder/_hooks/useSortedComponents.js b/frontend/src/AppBuilder/_hooks/useSortedComponents.js new file mode 100644 index 0000000000..9877206444 --- /dev/null +++ b/frontend/src/AppBuilder/_hooks/useSortedComponents.js @@ -0,0 +1,49 @@ +import { useMemo, useRef } from 'react'; +import useStore from '@/AppBuilder/_stores/store'; +import { shallow } from 'zustand/shallow'; + +const useSortedComponents = (components, currentLayout, id) => { + const getCurrentPageComponents = useStore((state) => state.getCurrentPageComponents, shallow); + const reorderContainerChildren = useStore((state) => state.reorderContainerChildren, shallow); + const prevForceUpdateRef = useRef(0); + const prevComponentsOrder = useRef(components); + + // Function to sort the components based on position in container for tab navigation + const sortedComponents = useMemo(() => { + const { triggerUpdate, containerId } = reorderContainerChildren; + + // If a forced update occurred for a different container, return the previous order + const isForcedUpdate = prevForceUpdateRef.current !== triggerUpdate; + if (isForcedUpdate) { + prevForceUpdateRef.current = triggerUpdate; + if (containerId !== id) { + return prevComponentsOrder.current; + } + } + + const currentPageComponents = getCurrentPageComponents(); + + const newComponentsOrder = [...components].sort((a, b) => { + const aTop = currentPageComponents?.[a]?.layouts?.[currentLayout]?.top; + const bTop = currentPageComponents?.[b]?.layouts?.[currentLayout]?.top; + if (aTop !== bTop) { + return aTop - bTop; + } else { + const aLeft = currentPageComponents?.[a]?.layouts?.[currentLayout]?.left; + const bLeft = currentPageComponents?.[b]?.layouts?.[currentLayout]?.left; + if (aLeft !== bLeft) { + return aLeft - bLeft; + } + return 0; + } + }); + + prevComponentsOrder.current = newComponentsOrder; + return newComponentsOrder; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [components, currentLayout, reorderContainerChildren.triggerUpdate, id]); + + return sortedComponents; +}; + +export default useSortedComponents; diff --git a/frontend/src/AppBuilder/_stores/slices/gridSlice.js b/frontend/src/AppBuilder/_stores/slices/gridSlice.js index 642266a32b..37de5cf81a 100644 --- a/frontend/src/AppBuilder/_stores/slices/gridSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/gridSlice.js @@ -8,6 +8,10 @@ const initialState = { lastCanvasIdClick: '', lastCanvasClickPosition: null, draggingComponentId: null, + reorderContainerChildren: { + containerId: null, + triggerUpdate: 0, + }, }; export const createGridSlice = (set, get) => ({ @@ -73,4 +77,10 @@ export const createGridSlice = (set, get) => ({ setLastCanvasClickPosition: (position) => { set({ lastCanvasClickPosition: position }); }, + setReorderContainerChildren: (containerId) => { + // Function to trigger reordering of specific container for tab navigation + set((state) => ({ + reorderContainerChildren: { containerId, triggerUpdate: state.reorderContainerChildren.triggerUpdate + 1 }, + })); + }, });