This commit is contained in:
Nakul Nagargade 2025-07-02 14:44:21 +05:30
parent 74dada642c
commit 7198427ff9
5 changed files with 113 additions and 58 deletions

View file

@ -12,8 +12,7 @@ import NoComponentCanvasContainer from './NoComponentCanvasContainer';
import { ModuleContainerBlank } from '@/modules/Modules/components';
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
import useSortedComponents from '../_hooks/useSortedComponents';
import { noop } from 'lodash';
import { useGhostMoveable } from '@/AppBuilder/_hooks/useGhostMoveable';
import { useDropVirtualMoveableGhost } from '@/AppBuilder/_hooks/useDropVirtualMoveableGhost';
import { useCanvasDropHandler } from './useCanvasDropHandler';
import { findNewParentIdFromMousePosition } from './Grid/gridUtils';
@ -37,33 +36,25 @@ export const Container = React.memo(
columns,
darkMode,
canvasMaxWidth,
isViewerSidebarPinned,
pageSidebarStyle,
pagePositionType,
componentType,
appType,
}) => {
const { moduleId } = useModuleContext();
const realCanvasRef = useRef(null);
const components = useStore((state) => state.getContainerChildrenMapping(id, moduleId), shallow);
const addComponentToCurrentPage = useStore((state) => state.addComponentToCurrentPage, shallow);
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab, shallow);
const setLastCanvasClickPosition = useStore((state) => state.setLastCanvasClickPosition, shallow);
const canvasBgColor = useStore(
(state) => (id === 'canvas' ? state.getCanvasBackgroundColor('canvas', darkMode) : ''),
shallow
);
const isPagesSidebarHidden = useStore((state) => state.getPagesSidebarVisibility('canvas'), shallow);
const currentMode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow);
const currentLayout = useStore((state) => state.currentLayout, shallow);
const setFocusedParentId = useStore((state) => state.setFocusedParentId, shallow);
const setShowModuleBorder = useStore((state) => state.setShowModuleBorder, shallow) || noop;
// Initialize ghost moveable hook
const { activateGhost, deactivateGhost } = useGhostMoveable(id);
const { activateMoveableGhost, deactivateMoveableGhost } = useDropVirtualMoveableGhost();
// Monitor drag layer to update ghost position continuously
// // Monitor drag layer to update ghost position continuously
const { isDragging } = useDragLayer((monitor) => ({
isDragging: monitor.isDragging(),
}));
@ -71,9 +62,9 @@ export const Container = React.memo(
// // // Cleanup ghost when drag ends
useEffect(() => {
if (!isDragging) {
deactivateGhost();
deactivateMoveableGhost();
}
}, [id, isDragging, deactivateGhost]);
}, [id, isDragging, deactivateMoveableGhost]);
const isContainerReadOnly = useMemo(() => {
return (index !== 0 && (componentType === 'Listview' || componentType === 'Kanban')) || currentMode === 'view';
@ -81,8 +72,7 @@ export const Container = React.memo(
const setCurrentDragCanvasId = useGridStore((state) => state.actions.setCurrentDragCanvasId);
// Get the drop handler from the new hook
const handleDrop = useCanvasDropHandler({
const { handleDrop } = useCanvasDropHandler({
appType,
});
@ -106,7 +96,7 @@ export const Container = React.memo(
height: item.component?.defaultSize?.height,
};
if (clientOffset && id === 'canvas') {
activateGhost(componentSize, clientOffset, realCanvasRef);
activateMoveableGhost(componentSize, clientOffset, realCanvasRef);
}
},
drop: (item, monitor) => {
@ -136,29 +126,6 @@ export const Container = React.memo(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [canvasWidth, listViewMode, columns]);
const getCanvasWidth = useCallback(() => {
// if (
// id === 'canvas' &&
// !isPagesSidebarHidden &&
// isViewerSidebarPinned &&
// currentLayout !== 'mobile' &&
// pagePositionType == 'side' &&
// appType !== 'module'
// ) {
// return `calc(100% - ${pageSidebarStyle === 'icon' ? '85px' : '226px'})`;
// }
// if (
// id === 'canvas' &&
// !isPagesSidebarHidden &&
// !isViewerSidebarPinned &&
// currentLayout !== 'mobile' &&
// pagePositionType == 'side'
// ) {
// return `calc(100% - ${'44px'})`;
// }
return '100%';
}, [id, isPagesSidebarHidden, isViewerSidebarPinned, currentLayout, pagePositionType, pageSidebarStyle]);
const handleCanvasClick = useCallback(
(e) => {
const realCanvas = e.target.closest('.real-canvas');

View file

@ -29,7 +29,6 @@ export function snapToGrid(canvasWidth, x, y) {
//TODO: componentTypes should be a key value pair and get the definition directly by passing the componentType
export const addNewWidgetToTheEditor = (
componentType,
eventMonitorObject,
currentLayout,
realCanvasRef,
parentId,
@ -46,13 +45,12 @@ export const addNewWidgetToTheEditor = (
const { e } = useGridStore.getState().getGhostDragPosition();
const subContainerWidth = canvasBoundingRect?.width;
const { left: left3, top: top3 } = getMouseDistanceFromParentDiv(
const { left: _left, top: _top } = getMouseDistanceFromParentDiv(
e,
parentId === 'canvas' ? 'real-canvas' : parentId,
parentCanvasType
);
// [left, top] = snapToGrid(subContainerWidth, left, top);
let [left, top] = snapToGrid(subContainerWidth, left3, top3);
let [left, top] = snapToGrid(subContainerWidth, _left, _top);
const gridWidth = subContainerWidth / NO_OF_GRIDS;
left = Math.round(left / gridWidth);

View file

@ -12,7 +12,6 @@ import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants';
import { isPDFSupported } from '@/_helpers/appUtils';
import toast from 'react-hot-toast';
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
import { useGhostMoveable } from '../_hooks/useGhostMoveable';
import { handleDeactivateTargets, hideGridLines } from '../AppCanvas/Grid/gridUtils';
export const useCanvasDropHandler = ({ appType }) => {
@ -24,15 +23,13 @@ export const useCanvasDropHandler = ({ appType }) => {
const currentMode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow);
const currentLayout = useStore((state) => state.currentLayout, shallow);
const setCurrentDragCanvasId = useGridStore((state) => state.actions.setCurrentDragCanvasId);
const { deactivateGhost } = useGhostMoveable();
const handleDrop = async ({ componentType: draggedComponentType, component }, monitor, canvasId) => {
const realCanvasRef =
!canvasId || canvasId === 'canvas'
? document.getElementById(`real-canvas`)
: document.getElementById(`canvas-${canvasId}`);
// Ensure ghost is deactivated before processing drop
deactivateGhost();
handleDeactivateTargets();
hideGridLines();
@ -42,11 +39,6 @@ export const useCanvasDropHandler = ({ appType }) => {
return;
}
// const didDrop = monitor.didDrop();
// if (didDrop) {
// return;
// }
if (draggedComponentType === 'PDF' && !isPDFSupported()) {
toast.error(
'PDF is not supported in this version of browser. We recommend upgrading to the latest version for full support.'
@ -70,7 +62,6 @@ export const useCanvasDropHandler = ({ appType }) => {
if (WIDGETS_WITH_DEFAULT_CHILDREN.includes(draggedComponentType)) {
let parentComponent = addNewWidgetToTheEditor(
draggedComponentType,
monitor,
currentLayout,
realCanvasRef,
canvasId,
@ -85,7 +76,6 @@ export const useCanvasDropHandler = ({ appType }) => {
} else {
const newComponent = addNewWidgetToTheEditor(
draggedComponentType,
monitor,
currentLayout,
realCanvasRef,
canvasId,
@ -119,5 +109,5 @@ export const useCanvasDropHandler = ({ appType }) => {
setCurrentDragCanvasId(null);
};
return handleDrop;
return { handleDrop };
};

View file

@ -20,7 +20,7 @@ export const DragLayer = ({ index, component, isModuleTab = false }) => {
const isRightSidebarPinned = useStore((state) => state.isRightSidebarPinned);
const { isModuleEditor } = useModuleContext();
const setShowModuleBorder = useStore((state) => state.setShowModuleBorder, shallow) || noop;
const handleDrop = useCanvasDropHandler({ appType: isModuleTab ? 'module' : 'app' }) || noop;
const { handleDrop } = useCanvasDropHandler({ appType: isModuleTab ? 'module' : 'app' }) || noop;
const [{ isDragging }, drag, preview] = useDrag(
() => ({
@ -59,7 +59,6 @@ export const DragLayer = ({ index, component, isModuleTab = false }) => {
return (
<>
{/* {isDragging && <CustomDragLayer size={size} />} */}
<div ref={drag} className="draggable-box" style={{ height: '100%', width: isModuleTab && '100%' }}>
{isModuleTab ? <ModuleWidgetBox module={component} /> : <WidgetBox index={index} component={component} />}
</div>

View file

@ -0,0 +1,101 @@
import { useRef } from 'react';
import { useGridStore } from '@/_stores/gridStore';
export const useDropVirtualMoveableGhost = () => {
const ghostElementRef = useRef(null);
const isActiveRef = useRef(false);
const getMoveableRef = useGridStore((state) => state.moveableRef);
const setVirtualTarget = useGridStore((state) => state.actions.setVirtualTarget);
const createGhostMoveElement = (componentSize) => {
if (ghostElementRef.current) return;
const ghost = document.createElement('div');
ghost.id = 'moveable-ghost-element';
ghost.className = 'moveable-ghost target';
ghost.style.cssText = `
position: absolute;
width: ${componentSize.width || 100}px;
height: ${componentSize.height || 40}px;
background: #D9E2FC;
opacity: 0.7;
pointer-events: none;
z-index: 9998;
box-sizing: border-box;
top: 0;
left: 0;
`;
const container = document.getElementById('real-canvas');
container.appendChild(ghost);
ghostElementRef.current = ghost;
return ghost;
};
const updateMoveableGhostPosition = (mousePosition, canvasRef) => {
if (!ghostElementRef.current || !canvasRef?.current || !mousePosition) return;
const canvasRect = canvasRef.current.getBoundingClientRect();
const relativeX = mousePosition.x - canvasRect.left;
const relativeY = mousePosition.y - canvasRect.top;
ghostElementRef.current.style.transform = `translate(${relativeX}px, ${relativeY}px)`;
};
const activateMoveableGhost = (componentSize, mousePosition, canvasRef) => {
if (isActiveRef.current) return;
isActiveRef.current = true;
const ghost = createGhostMoveElement(componentSize, canvasRef);
if (ghost && mousePosition) {
updateMoveableGhostPosition(mousePosition, canvasRef);
// Trigger moveable drag on the ghost element to show guidelines
const moveableInstance = getMoveableRef;
if (moveableInstance && ghost) {
try {
const fakeEvent = new MouseEvent('mousedown', {
clientX: mousePosition.x,
clientY: mousePosition.y,
bubbles: true,
cancelable: true,
view: window,
button: 0,
buttons: 1,
});
moveableInstance.waitToChangeTarget().then((e) => {
moveableInstance.dragStart(fakeEvent, ghost);
});
setVirtualTarget(ghost);
} catch (error) {
console.warn('Failed to trigger moveable dragStart:', error);
}
}
}
};
const deactivateMoveableGhost = () => {
if (!isActiveRef.current) return;
isActiveRef.current = false;
const moveableInstance = getMoveableRef;
if (moveableInstance && ghostElementRef.current) {
try {
setVirtualTarget(null);
ghostElementRef.current.remove();
ghostElementRef.current = null;
} catch (error) {
console.warn('Failed to trigger moveable dragEnd:', error);
}
}
};
return {
activateMoveableGhost,
deactivateMoveableGhost,
};
};