mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Merge pull request #13272 from ToolJet/revert-13089-snapping-react-moveable
Revert "Add support to show guidelines on component drop"
This commit is contained in:
commit
146bf83e67
16 changed files with 301 additions and 630 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -10,9 +10,6 @@
|
|||
],
|
||||
"eslint.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
|
|
|
|||
11
frontend/package-lock.json
generated
11
frontend/package-lock.json
generated
|
|
@ -121,7 +121,7 @@
|
|||
"react-loading-skeleton": "^3.1.1",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-mentions": "^4.4.7",
|
||||
"react-moveable": "^0.56.0",
|
||||
"react-moveable": "^0.54.1",
|
||||
"react-multi-select-component": "^4.3.4",
|
||||
"react-pdf": "^6.2.2",
|
||||
"react-phone-input-2": "^2.15.1",
|
||||
|
|
@ -30697,9 +30697,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-moveable": {
|
||||
"version": "0.56.0",
|
||||
"resolved": "https://registry.npmjs.org/react-moveable/-/react-moveable-0.56.0.tgz",
|
||||
"integrity": "sha512-FmJNmIOsOA36mdxbrc/huiE4wuXSRlmon/o+/OrfNhSiYYYL0AV5oObtPluEhb2Yr/7EfYWBHTxF5aWAvjg1SA==",
|
||||
"version": "0.54.2",
|
||||
"resolved": "https://registry.npmjs.org/react-moveable/-/react-moveable-0.54.2.tgz",
|
||||
"integrity": "sha512-NGaVLbn0i9pb3+BWSKGWFqI/Mgm4+WMeWHxXXQ4Qi1tHxWCXrUrbGvpxEpt69G/hR7dez+/m68ex+fabjnvcUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.13.0",
|
||||
"@egjs/agent": "^2.2.1",
|
||||
|
|
@ -30710,7 +30711,7 @@
|
|||
"@scena/matrix": "^1.1.1",
|
||||
"css-to-mat": "^1.1.1",
|
||||
"framework-utils": "^1.1.0",
|
||||
"gesto": "^1.19.3",
|
||||
"gesto": "^1.19.0",
|
||||
"overlap-area": "^1.1.0",
|
||||
"react-css-styled": "^1.1.9",
|
||||
"react-selecto": "^1.25.0"
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@
|
|||
"react-loading-skeleton": "^3.1.1",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-mentions": "^4.4.7",
|
||||
"react-moveable": "^0.56.0",
|
||||
"react-moveable": "^0.54.1",
|
||||
"react-multi-select-component": "^4.3.4",
|
||||
"react-pdf": "^6.2.2",
|
||||
"react-phone-input-2": "^2.15.1",
|
||||
|
|
|
|||
|
|
@ -4,17 +4,32 @@ import cx from 'classnames';
|
|||
import WidgetWrapper from './WidgetWrapper';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { useDrop, useDragLayer } from 'react-dnd';
|
||||
import { computeViewerBackgroundColor, getSubContainerWidthAfterPadding } from './appCanvasUtils';
|
||||
import { CANVAS_WIDTHS, NO_OF_GRIDS, GRID_HEIGHT } from './appCanvasConstants';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import {
|
||||
addChildrenWidgetsToParent,
|
||||
addNewWidgetToTheEditor,
|
||||
computeViewerBackgroundColor,
|
||||
getSubContainerWidthAfterPadding,
|
||||
addDefaultButtonIdToForm,
|
||||
} from './appCanvasUtils';
|
||||
import {
|
||||
CANVAS_WIDTHS,
|
||||
NO_OF_GRIDS,
|
||||
WIDGETS_WITH_DEFAULT_CHILDREN,
|
||||
GRID_HEIGHT,
|
||||
CONTAINER_FORM_CANVAS_PADDING,
|
||||
SUBCONTAINER_CANVAS_BORDER_WIDTH,
|
||||
BOX_PADDING,
|
||||
} from './appCanvasConstants';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import NoComponentCanvasContainer from './NoComponentCanvasContainer';
|
||||
import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants';
|
||||
import { isPDFSupported } from '@/_helpers/appUtils';
|
||||
import toast from 'react-hot-toast';
|
||||
import { ModuleContainerBlank } from '@/modules/Modules/components';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import useSortedComponents from '../_hooks/useSortedComponents';
|
||||
import { useDropVirtualMoveableGhost } from '@/AppBuilder/_hooks/useDropVirtualMoveableGhost';
|
||||
import { useCanvasDropHandler } from './useCanvasDropHandler';
|
||||
import { findNewParentIdFromMousePosition } from './Grid/gridUtils';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
//TODO: Revisit the logic of height (dropRef)
|
||||
|
||||
|
|
@ -36,72 +51,112 @@ 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);
|
||||
|
||||
// Initialize ghost moveable hook
|
||||
const { activateMoveableGhost, deactivateMoveableGhost } = useDropVirtualMoveableGhost();
|
||||
|
||||
// // Monitor drag layer to update ghost position continuously
|
||||
const { isDragging } = useDragLayer((monitor) => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
}));
|
||||
|
||||
// // // Cleanup ghost when drag ends
|
||||
useEffect(() => {
|
||||
if (!isDragging) {
|
||||
deactivateMoveableGhost();
|
||||
}
|
||||
}, [id, isDragging, deactivateMoveableGhost]);
|
||||
const setShowModuleBorder = useStore((state) => state.setShowModuleBorder, shallow) || noop;
|
||||
|
||||
const isContainerReadOnly = useMemo(() => {
|
||||
return (index !== 0 && (componentType === 'Listview' || componentType === 'Kanban')) || currentMode === 'view';
|
||||
}, [index, componentType, currentMode]);
|
||||
|
||||
const setCurrentDragCanvasId = useGridStore((state) => state.actions.setCurrentDragCanvasId);
|
||||
|
||||
const { handleDrop } = useCanvasDropHandler({
|
||||
appType,
|
||||
});
|
||||
|
||||
const [{ isOverCurrent }, drop] = useDrop({
|
||||
accept: 'box',
|
||||
hover: (item, monitor) => {
|
||||
const clientOffset = monitor.getClientOffset();
|
||||
hover: (item) => {
|
||||
item.canvasRef = realCanvasRef?.current;
|
||||
item.canvasId = id;
|
||||
item.canvasWidth = getContainerCanvasWidth();
|
||||
},
|
||||
drop: async ({ componentType, component }, monitor) => {
|
||||
setShowModuleBorder(false);
|
||||
if (currentMode === 'view' || (appType === 'module' && componentType !== 'ModuleContainer')) return;
|
||||
|
||||
const appCanvasWidth = realCanvasRef?.current?.offsetWidth || 0;
|
||||
const didDrop = monitor.didDrop();
|
||||
if (didDrop) return;
|
||||
|
||||
if (clientOffset) {
|
||||
const canvasId = findNewParentIdFromMousePosition(clientOffset.x, clientOffset.y, id);
|
||||
if (canvasId === id) {
|
||||
setCurrentDragCanvasId(id);
|
||||
const moduleInfo = component?.moduleId
|
||||
? {
|
||||
moduleId: component.moduleId,
|
||||
versionId: component.versionId,
|
||||
environmentId: component.environmentId,
|
||||
moduleName: component.displayName,
|
||||
moduleContainer: component.moduleContainer,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
let addedComponent;
|
||||
|
||||
if (WIDGETS_WITH_DEFAULT_CHILDREN.includes(componentType)) {
|
||||
let parentComponent = addNewWidgetToTheEditor(
|
||||
componentType,
|
||||
monitor,
|
||||
currentLayout,
|
||||
realCanvasRef,
|
||||
id,
|
||||
moduleInfo
|
||||
);
|
||||
const childComponents = addChildrenWidgetsToParent(componentType, parentComponent?.id, currentLayout);
|
||||
if (componentType === 'Form') {
|
||||
parentComponent = addDefaultButtonIdToForm(parentComponent, childComponents);
|
||||
}
|
||||
addedComponent = [parentComponent, ...childComponents];
|
||||
await addComponentToCurrentPage(addedComponent);
|
||||
} else {
|
||||
const newComponent = addNewWidgetToTheEditor(
|
||||
componentType,
|
||||
monitor,
|
||||
currentLayout,
|
||||
realCanvasRef,
|
||||
id,
|
||||
moduleInfo
|
||||
);
|
||||
addedComponent = [newComponent];
|
||||
await addComponentToCurrentPage(addedComponent);
|
||||
}
|
||||
// Calculate width based on the app canvas's grid
|
||||
let width = (appCanvasWidth * item.component?.defaultSize?.width) / NO_OF_GRIDS;
|
||||
const componentSize = {
|
||||
width,
|
||||
height: item.component?.defaultSize?.height,
|
||||
};
|
||||
if (clientOffset && id === 'canvas') {
|
||||
activateMoveableGhost(componentSize, clientOffset, realCanvasRef);
|
||||
|
||||
setActiveRightSideBarTab(RIGHT_SIDE_BAR_TAB.CONFIGURATION);
|
||||
|
||||
const canvas = document.querySelector('.canvas-container');
|
||||
const sidebar = document.querySelector('.editor-sidebar');
|
||||
const droppedElem = document.getElementById(addedComponent?.[0]?.id);
|
||||
|
||||
if (!canvas || !sidebar || !droppedElem) return;
|
||||
|
||||
const droppedRect = droppedElem.getBoundingClientRect();
|
||||
const sidebarRect = sidebar.getBoundingClientRect();
|
||||
|
||||
const isOverlapping = droppedRect.right > sidebarRect.left && droppedRect.left < sidebarRect.right;
|
||||
|
||||
if (isOverlapping) {
|
||||
const overlap = droppedRect.right - sidebarRect.left;
|
||||
canvas.scrollTo({
|
||||
left: canvas.scrollLeft + overlap,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
},
|
||||
drop: (item, monitor) => {
|
||||
handleDrop(item, id);
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isOverCurrent: monitor.isOver({ shallow: true }),
|
||||
}),
|
||||
});
|
||||
|
||||
const showEmptyContainer =
|
||||
|
|
@ -120,12 +175,34 @@ export const Container = React.memo(
|
|||
}
|
||||
|
||||
const gridWidth = getContainerCanvasWidth() / NO_OF_GRIDS;
|
||||
|
||||
useEffect(() => {
|
||||
useGridStore.getState().actions.setSubContainerWidths(id, gridWidth);
|
||||
// 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');
|
||||
|
|
@ -174,8 +251,8 @@ export const Container = React.memo(
|
|||
currentMode === 'view'
|
||||
? computeViewerBackgroundColor(darkMode, canvasBgColor)
|
||||
: id === 'canvas'
|
||||
? canvasBgColor
|
||||
: '#f0f0f0',
|
||||
? canvasBgColor
|
||||
: '#f0f0f0',
|
||||
width: '100%',
|
||||
maxWidth: (() => {
|
||||
// For Main Canvas
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// import '@/Editor/wdyr';
|
||||
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import Moveable from 'react-moveable';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
|
@ -10,8 +10,10 @@ import { useGridStore, useIsGroupHandleHoverd, useOpenModalWidgetId } from '@/_s
|
|||
import toast from 'react-hot-toast';
|
||||
import {
|
||||
individualGroupableProps,
|
||||
getMouseDistanceFromParentDiv,
|
||||
findChildrenAndGrandchildren,
|
||||
findHighestLevelofSelection,
|
||||
getOffset,
|
||||
hasParentWithClass,
|
||||
getPositionForGroupDrag,
|
||||
adjustWidth,
|
||||
|
|
@ -23,8 +25,7 @@ import {
|
|||
computeScrollDelta,
|
||||
computeScrollDeltaOnDrag,
|
||||
getDraggingWidgetWidth,
|
||||
positionGhostElement,
|
||||
findNewParentIdFromMousePosition,
|
||||
positionDragGhostWidget,
|
||||
} from './gridUtils';
|
||||
import { dragContextBuilder, getAdjustedDropPosition } from './helpers/dragEnd';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
|
|
@ -32,8 +33,6 @@ import './Grid.css';
|
|||
import { useGroupedTargetsScrollHandler } from './hooks/useGroupedTargetsScrollHandler';
|
||||
import { DROPPABLE_PARENTS, NO_OF_GRIDS, SUBCONTAINER_WIDGETS } from '../appCanvasConstants';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import { useElementGudelines } from './hooks/useElementGudelines';
|
||||
|
||||
const CANVAS_BOUNDS = { left: 0, top: 0, right: 0, position: 'css' };
|
||||
const RESIZABLE_CONFIG = {
|
||||
edge: ['nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se'],
|
||||
|
|
@ -74,15 +73,10 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const getTemporaryLayouts = useStore((state) => state.getTemporaryLayouts, shallow);
|
||||
const updateContainerAutoHeight = useStore((state) => state.updateContainerAutoHeight, shallow);
|
||||
const [canvasBounds, setCanvasBounds] = useState(CANVAS_BOUNDS);
|
||||
const draggingComponentId = useGridStore((state) => state.draggingComponentId, shallow);
|
||||
const resizingComponentId = useGridStore((state) => state.resizingComponentId, shallow);
|
||||
const [dragParentId, setDragParentId] = useState(null);
|
||||
const virtualTarget = useGridStore((state) => state.virtualTarget, shallow);
|
||||
const { elementGuidelines } = useElementGudelines(
|
||||
boxList,
|
||||
selectedComponents,
|
||||
dragParentId,
|
||||
getResolvedValue,
|
||||
virtualTarget
|
||||
);
|
||||
const [elementGuidelines, setElementGuidelines] = useState([]);
|
||||
const componentsSnappedTo = useRef(null);
|
||||
const prevDragParentId = useRef(null);
|
||||
const newDragParentId = useRef(null);
|
||||
|
|
@ -90,39 +84,42 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const checkIfAnyWidgetVisibilityChanged = useStore((state) => state.checkIfAnyWidgetVisibilityChanged(), shallow);
|
||||
const getExposedValueOfComponent = useStore((state) => state.getExposedValueOfComponent, shallow);
|
||||
const setReorderContainerChildren = useStore((state) => state.setReorderContainerChildren, shallow);
|
||||
const currentDragCanvasId = useGridStore((state) => state.currentDragCanvasId, shallow);
|
||||
const groupedTargets = [...findHighestLevelofSelection().map((component) => '.ele-' + component.id)];
|
||||
const [isVerticalExpansionRestricted, setIsVerticalExpansionRestricted] = useState(false);
|
||||
const toggleRightSidebar = useStore((state) => state.toggleRightSidebar, shallow);
|
||||
const draggingComponentId = useStore((state) => state.draggingComponentId, shallow);
|
||||
const resizingComponentId = useStore((state) => state.resizingComponentId, shallow);
|
||||
|
||||
const snapContainer = useMemo(() => {
|
||||
if (currentDragCanvasId) {
|
||||
return `#canvas-${currentDragCanvasId}`;
|
||||
}
|
||||
if (dragParentId) {
|
||||
return `#canvas-${dragParentId}`;
|
||||
}
|
||||
return '#real-canvas';
|
||||
}, [currentDragCanvasId, dragParentId]);
|
||||
|
||||
const getMoveableTarget = () => {
|
||||
if (virtualTarget) {
|
||||
return '#moveable-ghost-element';
|
||||
}
|
||||
return groupedTargets?.length > 1 ? groupedTargets : '.target';
|
||||
};
|
||||
|
||||
// Set moveable reference in grid store for access by other components
|
||||
useEffect(() => {
|
||||
if (moveableRef.current) {
|
||||
useGridStore.getState().setMoveableRef(moveableRef.current);
|
||||
}
|
||||
return () => {
|
||||
useGridStore.getState().setMoveableRef(null);
|
||||
};
|
||||
}, []);
|
||||
const selectedSet = new Set(selectedComponents);
|
||||
const draggingOrResizingId = draggingComponentId || resizingComponentId;
|
||||
const isGrouped = findHighestLevelofSelection().length > 1;
|
||||
const firstSelectedParent =
|
||||
selectedComponents.length > 0 ? boxList.find((b) => b.id === selectedComponents[0])?.parent : null;
|
||||
const selectedParent = dragParentId || firstSelectedParent;
|
||||
|
||||
const guidelines = boxList
|
||||
.filter((box) => {
|
||||
const isVisible =
|
||||
getResolvedValue(box?.component?.definition?.properties?.visibility?.value) ||
|
||||
getResolvedValue(box?.component?.definition?.styles?.visibility?.value);
|
||||
|
||||
// Early return for non-visible elements
|
||||
if (!isVisible) return false;
|
||||
|
||||
if (isGrouped) {
|
||||
// If component is selected, don't show its guidelines
|
||||
if (selectedSet.has(box.id)) return false;
|
||||
return selectedParent ? box.parent === selectedParent : !box.parent;
|
||||
}
|
||||
|
||||
if (draggingOrResizingId) {
|
||||
if (box.id === draggingOrResizingId) return false;
|
||||
return dragParentId ? box.parent === dragParentId : !box.parent;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((box) => `.ele-${box.id}`);
|
||||
setElementGuidelines(guidelines);
|
||||
}, [boxList, dragParentId, draggingComponentId, resizingComponentId, selectedComponents, getResolvedValue]);
|
||||
|
||||
useEffect(() => {
|
||||
setBoxList(
|
||||
|
|
@ -344,12 +341,13 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
});
|
||||
}, [selectedComponents]);
|
||||
|
||||
const groupedTargets = [...findHighestLevelofSelection().map((component) => '.ele-' + component.id)];
|
||||
|
||||
useEffect(() => {
|
||||
if (moveableRef.current) {
|
||||
moveableRef.current.updateTarget();
|
||||
}
|
||||
}, [temporaryHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadGrid();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -536,8 +534,9 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const _top = originalBox.top;
|
||||
|
||||
// Apply transform to return to original position
|
||||
ev.target.style.transform = `translate(${Math.round(_left / _gridWidth) * _gridWidth}px, ${Math.round(_top / GRID_HEIGHT) * GRID_HEIGHT
|
||||
}px)`;
|
||||
ev.target.style.transform = `translate(${Math.round(_left / _gridWidth) * _gridWidth}px, ${
|
||||
Math.round(_top / GRID_HEIGHT) * GRID_HEIGHT
|
||||
}px)`;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -600,16 +599,18 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const components = Array.from(document.querySelectorAll('.active-target')).filter(
|
||||
(component) => !selectedComponents.includes(component.getAttribute('widgetid'))
|
||||
);
|
||||
const draggingOrResizingComponentId = draggingComponentId || resizingComponentId;
|
||||
if (!draggingOrResizingComponentId && components.length > 0 && !virtualTarget) {
|
||||
const draggingOrResizing = draggingComponentId || resizingComponentId;
|
||||
if (!draggingOrResizing && components.length > 0) {
|
||||
for (const component of components) {
|
||||
component?.classList?.remove('active-target');
|
||||
}
|
||||
}
|
||||
}, [draggingComponentId, resizingComponentId, isGroupDragging, selectedComponents, virtualTarget]);
|
||||
}, [draggingComponentId, resizingComponentId, isGroupDragging, selectedComponents]);
|
||||
|
||||
useGroupedTargetsScrollHandler(groupedTargets, boxList, moveableRef);
|
||||
|
||||
if (mode !== 'edit') return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Moveable
|
||||
|
|
@ -622,9 +623,9 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
multiComponentHandle: groupedTargets.length > 1,
|
||||
}}
|
||||
flushSync={flushSync}
|
||||
target={getMoveableTarget()}
|
||||
target={groupedTargets?.length > 1 ? groupedTargets : '.target'}
|
||||
origin={false}
|
||||
individualGroupable={virtualTarget ? false : groupedTargets.length <= 1}
|
||||
individualGroupable={groupedTargets.length <= 1}
|
||||
draggable={!shouldFreeze && mode !== 'view'}
|
||||
resizable={
|
||||
!shouldFreeze
|
||||
|
|
@ -638,7 +639,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
onResize={(e) => {
|
||||
const temporaryLayouts = getTemporaryLayouts();
|
||||
if (resizingComponentId !== e.target.id) {
|
||||
useStore.getState().setResizingComponentId(e.target.id);
|
||||
useGridStore.getState().actions.setResizingComponentId(e.target.id);
|
||||
showGridLines();
|
||||
}
|
||||
|
||||
|
|
@ -647,8 +648,12 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
let _gridWidth = useGridStore.getState().subContainerWidths[currentWidget.component?.parent] || gridWidth;
|
||||
|
||||
// Show grid during resize
|
||||
showGridLines();
|
||||
|
||||
if (currentWidget.component?.parent) {
|
||||
document.getElementById('canvas-' + currentWidget.component?.parent)?.classList.add('show-grid');
|
||||
setDragParentId(currentWidget.component?.parent);
|
||||
} else {
|
||||
document.getElementById('real-canvas').classList.add('show-grid');
|
||||
}
|
||||
|
||||
handleActivateTargets(currentWidget.component?.parent);
|
||||
const currentWidth = currentWidget.width * _gridWidth;
|
||||
|
|
@ -692,7 +697,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
e.target.style.transform = `translate(${transformX}px, ${transformY}px)`;
|
||||
if (e.width > 0) e.target.style.width = `${e.width}px`;
|
||||
if (e.height > 0) e.target.style.height = `${e.height}px`;
|
||||
positionGhostElement(e.target, 'resize-ghost-widget');
|
||||
}}
|
||||
onResizeStart={(e) => {
|
||||
if (
|
||||
|
|
@ -711,7 +715,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}}
|
||||
onResizeEnd={(e) => {
|
||||
try {
|
||||
useStore.getState().setResizingComponentId(null);
|
||||
useGridStore.getState().actions.setResizingComponentId(null);
|
||||
const currentWidget = boxList.find(({ id }) => {
|
||||
return id === e.target.id;
|
||||
});
|
||||
|
|
@ -748,8 +752,9 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
|
||||
const roundedTransformY = Math.round(transformY / GRID_HEIGHT) * GRID_HEIGHT;
|
||||
transformY = transformY % GRID_HEIGHT === 5 ? roundedTransformY - GRID_HEIGHT : roundedTransformY;
|
||||
e.target.style.transform = `translate(${Math.round(transformX / _gridWidth) * _gridWidth}px, ${Math.round(transformY / GRID_HEIGHT) * GRID_HEIGHT
|
||||
}px)`;
|
||||
e.target.style.transform = `translate(${Math.round(transformX / _gridWidth) * _gridWidth}px, ${
|
||||
Math.round(transformY / GRID_HEIGHT) * GRID_HEIGHT
|
||||
}px)`;
|
||||
if (!maxWidthHit || e.width < e.target.clientWidth) {
|
||||
e.target.style.width = `${Math.round(e.lastEvent.width / _gridWidth) * _gridWidth}px`;
|
||||
}
|
||||
|
|
@ -862,9 +867,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}}
|
||||
checkInput
|
||||
onDragStart={(e) => {
|
||||
if (e.target.id === 'moveable-ghost-element') {
|
||||
return true;
|
||||
}
|
||||
// This is to prevent parent component from being dragged and the stop the propagation of the event
|
||||
if (getHoveredComponentForGrid() !== e.target.id) {
|
||||
return false;
|
||||
|
|
@ -917,9 +919,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}}
|
||||
onDragEnd={(e) => {
|
||||
handleDeactivateTargets();
|
||||
if (e.target.id === 'moveable-ghost-element') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (isDraggingRef.current) {
|
||||
useStore.getState().setDraggingComponentId(null);
|
||||
|
|
@ -932,6 +931,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
setDragParentId(null);
|
||||
|
||||
if (!e.lastEvent) return;
|
||||
|
||||
// Build the drag context from the event
|
||||
const dragContext = dragContextBuilder({ event: e, widgets: boxList, isModuleEditor });
|
||||
const { target, source, dragged } = dragContext;
|
||||
|
|
@ -978,21 +978,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
toggleCanvasUpdater();
|
||||
}}
|
||||
onDrag={(e) => {
|
||||
if (e.target.id === 'moveable-ghost-element') {
|
||||
showGridLines();
|
||||
const _gridWidth = useGridStore.getState().subContainerWidths[currentDragCanvasId] || gridWidth;
|
||||
let left = e.translate[0];
|
||||
let top = e.translate[1];
|
||||
|
||||
if (currentDragCanvasId === 'canvas') {
|
||||
left = Math.round(e.translate[0] / _gridWidth) * _gridWidth;
|
||||
top = Math.round(e.translate[1] / GRID_HEIGHT) * GRID_HEIGHT;
|
||||
}
|
||||
|
||||
useGridStore.getState().actions.setGhostDragPosition({ left, top, e });
|
||||
e.target.style.transform = `translate(${left}px, ${top}px)`;
|
||||
return false;
|
||||
}
|
||||
// Since onDrag is called multiple times when dragging, hence we are using isDraggingRef to prevent setting state again and again
|
||||
if (!isDraggingRef.current) {
|
||||
useStore.getState().setDraggingComponentId(e.target.id);
|
||||
|
|
@ -1061,7 +1046,16 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
|
||||
// This block is to show grid lines on the canvas when the dragged element is over a new canvas
|
||||
if (document.elementFromPoint(e.clientX, e.clientY)) {
|
||||
let newParentId = findNewParentIdFromMousePosition(e.clientX, e.clientY, e.target.id);
|
||||
const targetElems = document.elementsFromPoint(e.clientX, e.clientY);
|
||||
const draggedOverElements = targetElems.filter(
|
||||
(ele) =>
|
||||
(ele.id !== e.target.id && ele.classList.contains('target')) || ele.classList.contains('real-canvas')
|
||||
);
|
||||
const draggedOverElem = draggedOverElements.find((ele) => ele.classList.contains('target'));
|
||||
const draggedOverContainer = draggedOverElements.find((ele) => ele.classList.contains('real-canvas'));
|
||||
|
||||
// Determine potential new parent
|
||||
let newParentId = draggedOverContainer?.getAttribute('data-parentId') || draggedOverElem?.id;
|
||||
|
||||
if (newParentId === e.target.id) {
|
||||
newParentId = boxList.find((box) => box.id === e.target.id)?.component?.parent;
|
||||
|
|
@ -1089,7 +1083,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
`translate: ${e.translate[0]} | Round: ${Math.round(e.translate[0] / gridWidth) * gridWidth} | ${gridWidth}`
|
||||
);
|
||||
|
||||
positionGhostElement(e.target, 'moveable-drag-ghost');
|
||||
positionDragGhostWidget(e.target);
|
||||
}}
|
||||
onDragGroup={(ev) => {
|
||||
const { events } = ev;
|
||||
|
|
@ -1172,10 +1166,8 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
component.element.classList.add('active-target');
|
||||
}
|
||||
}}
|
||||
// snapGridAll={true}
|
||||
snapGridAll={true}
|
||||
scrollable={true}
|
||||
// snapContainer={snapContainer}
|
||||
// snapGridWidth={100}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -530,58 +530,24 @@ export const getDraggingWidgetWidth = (canvasParentId, widgetWidth) => {
|
|||
return draggingWidgetWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Positions a ghost/feedback element relative to the main canvas
|
||||
* @param {HTMLElement} targetElement - The element being dragged/resized
|
||||
* @param {string} ghostElementId - The ID of the ghost element to position
|
||||
* @param {Object} options - Additional positioning options
|
||||
* @param {boolean} options.includeSize - Whether to update width/height of ghost element
|
||||
*/
|
||||
export const positionGhostElement = (targetElement, ghostElementId) => {
|
||||
const ghostElement = document.getElementById(ghostElementId);
|
||||
export const positionDragGhostWidget = (draggedElement) => {
|
||||
const ghostElement = document.getElementById('moveable-drag-ghost');
|
||||
|
||||
if (!ghostElement || !targetElement) return;
|
||||
if (!ghostElement || !draggedElement) return;
|
||||
|
||||
const mainCanvas = document.getElementById('real-canvas');
|
||||
if (!mainCanvas) return;
|
||||
|
||||
const mainCanvasRect = mainCanvas.getBoundingClientRect();
|
||||
const targetRect = targetElement.getBoundingClientRect();
|
||||
const draggedRect = draggedElement.getBoundingClientRect();
|
||||
|
||||
// Calculate position relative to main canvas
|
||||
const relativeLeft = targetRect.left - mainCanvasRect.left;
|
||||
const relativeTop = targetRect.top - mainCanvasRect.top;
|
||||
const relativeLeft = draggedRect.left - mainCanvasRect.left;
|
||||
const relativeTop = draggedRect.top - mainCanvasRect.top;
|
||||
|
||||
// Apply the position
|
||||
ghostElement.style.left = `${relativeLeft}px`;
|
||||
ghostElement.style.top = `${relativeTop}px`;
|
||||
ghostElement.style.width = `${targetRect.width}px`;
|
||||
ghostElement.style.height = `${targetRect.height}px`;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the new parent ID based on the current mouse position during drag operations
|
||||
* @param {number} clientX - The X coordinate of the mouse position
|
||||
* @param {number} clientY - The Y coordinate of the mouse position
|
||||
* @param {string} currentTargetId - The ID of the currently dragged element to exclude from search
|
||||
* @returns {string|null} - The new parent ID or null if no valid parent is found
|
||||
*/
|
||||
export const findNewParentIdFromMousePosition = (clientX, clientY, currentTargetId) => {
|
||||
if (!document.elementFromPoint(clientX, clientY)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetElems = document.elementsFromPoint(clientX, clientY);
|
||||
const draggedOverElements = targetElems.filter(
|
||||
(ele) => (ele.id !== currentTargetId && ele.classList.contains('target')) || ele.classList.contains('real-canvas')
|
||||
);
|
||||
|
||||
const draggedOverElem = draggedOverElements.find((ele) => ele.classList.contains('target'));
|
||||
const draggedOverContainer = draggedOverElements.find((ele) => ele.classList.contains('real-canvas'));
|
||||
|
||||
// Determine potential new parent
|
||||
const newParentId = draggedOverContainer?.getAttribute('data-parentId') || draggedOverElem?.id;
|
||||
|
||||
return newParentId || null;
|
||||
ghostElement.style.width = `${draggedRect.width}px`;
|
||||
ghostElement.style.height = `${draggedRect.height}px`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { findHighestLevelofSelection } from '../gridUtils';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
|
||||
export const useElementGudelines = (boxList, selectedComponents, dragParentId, getResolvedValue, virtualTarget) => {
|
||||
const [elementGuidelines, setElementGuidelines] = useState([]);
|
||||
const draggingComponentId = useStore((state) => state.draggingComponentId);
|
||||
const resizingComponentId = useStore((state) => state.resizingComponentId);
|
||||
const currentDragCanvasId = useGridStore((state) => state.currentDragCanvasId);
|
||||
|
||||
useEffect(() => {
|
||||
const selectedSet = new Set(selectedComponents);
|
||||
const draggingOrResizingId = draggingComponentId || resizingComponentId;
|
||||
const isGrouped = findHighestLevelofSelection().length > 1;
|
||||
const firstSelectedParent =
|
||||
selectedComponents.length > 0 ? boxList.find((b) => b.id === selectedComponents[0])?.parent : null;
|
||||
const selectedParent = dragParentId || firstSelectedParent;
|
||||
const isAnyModalOpen = document.querySelector('#modal-container') ? true : false;
|
||||
|
||||
const guidelines = boxList
|
||||
.filter((box) => {
|
||||
const isVisible =
|
||||
getResolvedValue(box?.component?.definition?.properties?.visibility?.value) ||
|
||||
getResolvedValue(box?.component?.definition?.styles?.visibility?.value);
|
||||
|
||||
// Early return for non-visible elements
|
||||
if (!isVisible) return false;
|
||||
|
||||
// Don't show guidelines for components which are outside the modal specially on main canvas
|
||||
if (virtualTarget && isAnyModalOpen) {
|
||||
if (box.parent === 'canvas' || !box.parent) return false;
|
||||
}
|
||||
|
||||
// This block is for first time drop using react-dnd
|
||||
if (virtualTarget && currentDragCanvasId !== null) {
|
||||
if (currentDragCanvasId === 'canvas') {
|
||||
if (box.parent && box.parent !== 'canvas') return false;
|
||||
} else {
|
||||
// For sub-containers, only show components whose parent matches the canvasId
|
||||
if (box.parent !== currentDragCanvasId) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGrouped) {
|
||||
// If component is selected, don't show its guidelines
|
||||
if (selectedSet.has(box.id)) return false;
|
||||
return selectedParent ? box.parent === selectedParent : !box.parent;
|
||||
}
|
||||
|
||||
if (draggingOrResizingId) {
|
||||
if (box.id === draggingOrResizingId) return false;
|
||||
return dragParentId ? box.parent === dragParentId : !box.parent;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((box) => `.ele-${box.id}`);
|
||||
setElementGuidelines(guidelines);
|
||||
}, [
|
||||
boxList,
|
||||
dragParentId,
|
||||
draggingComponentId,
|
||||
resizingComponentId,
|
||||
selectedComponents,
|
||||
getResolvedValue,
|
||||
currentDragCanvasId,
|
||||
virtualTarget,
|
||||
]);
|
||||
|
||||
return { elementGuidelines };
|
||||
};
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import React, { memo } from 'react';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { ResizeGhostWidget } from './GhostWidgets';
|
||||
import { DragGhostWidget, ResizeGhostWidget } from './GhostWidgets';
|
||||
import { ConfigHandle } from './ConfigHandle/ConfigHandle';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import cx from 'classnames';
|
||||
import RenderWidget from './RenderWidget';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
|
|
@ -34,7 +35,7 @@ const WidgetWrapper = memo(
|
|||
const temporaryLayouts = useStore((state) => state.temporaryLayouts?.[id], shallow);
|
||||
const isWidgetActive = useStore((state) => state.selectedComponents.find((sc) => sc === id) && !readOnly, shallow);
|
||||
const isDragging = useStore((state) => state.draggingComponentId === id);
|
||||
const isResizing = useStore((state) => state.resizingComponentId === id);
|
||||
const isResizing = useGridStore((state) => state.resizingComponentId === id);
|
||||
const componentType = useStore(
|
||||
(state) => state.getComponentDefinition(id, moduleId)?.component?.component,
|
||||
shallow
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import useStore from '@/AppBuilder/_stores/store';
|
|||
import { toast } from 'react-hot-toast';
|
||||
import _, { debounce } from 'lodash';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import { findHighestLevelofSelection, getMouseDistanceFromParentDiv } from './Grid/gridUtils';
|
||||
import { findHighestLevelofSelection } from './Grid/gridUtils';
|
||||
import {
|
||||
CANVAS_WIDTHS,
|
||||
NO_OF_GRIDS,
|
||||
|
|
@ -29,32 +29,32 @@ 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,
|
||||
moduleInfo = undefined
|
||||
) => {
|
||||
const canvasBoundingRect = realCanvasRef?.getBoundingClientRect();
|
||||
const canvasBoundingRect = realCanvasRef?.current?.getBoundingClientRect();
|
||||
const componentMeta = componentTypes.find((component) => component.component === componentType);
|
||||
const componentName = computeComponentName(componentType, useStore.getState().getCurrentPageComponents());
|
||||
const parentCanvasType = realCanvasRef?.getAttribute('component-type');
|
||||
|
||||
const componentData = deepClone(componentMeta);
|
||||
const defaultWidth = componentData.defaultSize.width;
|
||||
const defaultHeight = componentData.defaultSize.height;
|
||||
|
||||
const { e } = useGridStore.getState().getGhostDragPosition();
|
||||
const offsetFromTopOfWindow = canvasBoundingRect?.top;
|
||||
const offsetFromLeftOfWindow = canvasBoundingRect?.left;
|
||||
const currentOffset = eventMonitorObject?.getSourceClientOffset();
|
||||
const subContainerWidth = canvasBoundingRect?.width;
|
||||
|
||||
const { left: _left, top: _top } = getMouseDistanceFromParentDiv(
|
||||
e,
|
||||
parentId === 'canvas' ? 'real-canvas' : parentId,
|
||||
parentCanvasType
|
||||
);
|
||||
let [left, top] = snapToGrid(subContainerWidth, _left, _top);
|
||||
let left = Math.round(currentOffset?.x - offsetFromLeftOfWindow);
|
||||
let top = Math.round(currentOffset?.y - offsetFromTopOfWindow);
|
||||
|
||||
[left, top] = snapToGrid(subContainerWidth, left, top);
|
||||
|
||||
const gridWidth = subContainerWidth / NO_OF_GRIDS;
|
||||
left = Math.round(left / gridWidth);
|
||||
|
||||
// Adjust widget width based on the dropping canvas width
|
||||
const mainCanvasWidth = useGridStore.getState().subContainerWidths['canvas'];
|
||||
let width = Math.round((defaultWidth * mainCanvasWidth) / gridWidth);
|
||||
|
|
@ -85,7 +85,6 @@ export const addNewWidgetToTheEditor = (
|
|||
left = Math.max(0, NO_OF_GRIDS - width);
|
||||
width = Math.min(width, NO_OF_GRIDS);
|
||||
}
|
||||
|
||||
if (currentLayout === 'mobile') {
|
||||
componentData.definition.others.showOnDesktop.value = `{{false}}`;
|
||||
componentData.definition.others.showOnMobile.value = `{{true}}`;
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { noop } from 'lodash';
|
||||
import {
|
||||
addChildrenWidgetsToParent,
|
||||
addNewWidgetToTheEditor,
|
||||
addDefaultButtonIdToForm,
|
||||
} from '../AppCanvas/appCanvasUtils';
|
||||
import { WIDGETS_WITH_DEFAULT_CHILDREN } from '../AppCanvas/appCanvasConstants';
|
||||
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 { handleDeactivateTargets, hideGridLines } from '../AppCanvas/Grid/gridUtils';
|
||||
|
||||
export const useCanvasDropHandler = ({ appType }) => {
|
||||
const { moduleId } = useModuleContext();
|
||||
|
||||
const addComponentToCurrentPage = useStore((state) => state.addComponentToCurrentPage, shallow);
|
||||
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab, shallow);
|
||||
const setShowModuleBorder = useStore((state) => state.setShowModuleBorder, shallow) || noop;
|
||||
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 handleDrop = async ({ componentType: draggedComponentType, component }, canvasId) => {
|
||||
const realCanvasRef =
|
||||
!canvasId || canvasId === 'canvas'
|
||||
? document.getElementById(`real-canvas`)
|
||||
: document.getElementById(`canvas-${canvasId}`);
|
||||
|
||||
handleDeactivateTargets();
|
||||
hideGridLines();
|
||||
|
||||
setShowModuleBorder(false); // Hide the module border when dropping
|
||||
|
||||
if (currentMode === 'view' || (appType === 'module' && draggedComponentType !== 'ModuleContainer')) {
|
||||
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.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT: This logic needs to be changed when we implement the module versioning
|
||||
const moduleInfo = component?.moduleId
|
||||
? {
|
||||
moduleId: component.moduleId,
|
||||
versionId: component.versionId,
|
||||
environmentId: component.environmentId,
|
||||
moduleName: component.displayName,
|
||||
moduleContainer: component.moduleContainer,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
let addedComponent;
|
||||
|
||||
if (WIDGETS_WITH_DEFAULT_CHILDREN.includes(draggedComponentType)) {
|
||||
let parentComponent = addNewWidgetToTheEditor(
|
||||
draggedComponentType,
|
||||
currentLayout,
|
||||
realCanvasRef,
|
||||
canvasId,
|
||||
moduleInfo
|
||||
);
|
||||
const childComponents = addChildrenWidgetsToParent(draggedComponentType, parentComponent?.id, currentLayout);
|
||||
if (draggedComponentType === 'Form') {
|
||||
parentComponent = addDefaultButtonIdToForm(parentComponent, childComponents);
|
||||
}
|
||||
addedComponent = [parentComponent, ...childComponents];
|
||||
await addComponentToCurrentPage(addedComponent);
|
||||
} else {
|
||||
const newComponent = addNewWidgetToTheEditor(
|
||||
draggedComponentType,
|
||||
currentLayout,
|
||||
realCanvasRef,
|
||||
canvasId,
|
||||
moduleInfo
|
||||
);
|
||||
addedComponent = [newComponent];
|
||||
await addComponentToCurrentPage(addedComponent);
|
||||
}
|
||||
|
||||
setActiveRightSideBarTab(RIGHT_SIDE_BAR_TAB.CONFIGURATION);
|
||||
|
||||
const canvas = document.querySelector('.canvas-container');
|
||||
const sidebar = document.querySelector('.editor-sidebar');
|
||||
const droppedElem = document.getElementById(addedComponent?.[0]?.id);
|
||||
|
||||
if (!canvas || !sidebar || !droppedElem) return;
|
||||
|
||||
const droppedRect = droppedElem.getBoundingClientRect();
|
||||
const sidebarRect = sidebar.getBoundingClientRect();
|
||||
|
||||
const isOverlapping = droppedRect.right > sidebarRect.left && droppedRect.left < sidebarRect.right;
|
||||
|
||||
if (isOverlapping) {
|
||||
const overlap = droppedRect.right - sidebarRect.left;
|
||||
canvas.scrollTo({
|
||||
left: canvas.scrollLeft + overlap,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
// Reset canvas ID when dropping
|
||||
setCurrentDragCanvasId(null);
|
||||
};
|
||||
|
||||
return { handleDrop };
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { WidgetBox } from '../WidgetBox';
|
||||
import { ModuleWidgetBox } from '@/modules/Modules/components';
|
||||
import { useDrag, useDragLayer } from 'react-dnd';
|
||||
|
|
@ -9,8 +9,6 @@ import useStore from '@/AppBuilder/_stores/store';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import { noop } from 'lodash';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import { useCanvasDropHandler } from '@/AppBuilder/AppCanvas/useCanvasDropHandler';
|
||||
|
||||
export const DragLayer = ({ index, component, isModuleTab = false, disabled = false }) => {
|
||||
const [isRightSidebarOpen, toggleRightSidebar] = useStore(
|
||||
|
|
@ -20,20 +18,11 @@ export const DragLayer = ({ index, component, isModuleTab = false, disabled = fa
|
|||
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 [{ isDragging }, drag, preview] = useDrag(
|
||||
() => ({
|
||||
type: 'box',
|
||||
item: { componentType: component.component, component },
|
||||
collect: (monitor) => ({ isDragging: monitor.isDragging() }),
|
||||
end: (item, monitor) => {
|
||||
const clientOffset = monitor.getClientOffset();
|
||||
const currentDragCanvasId = useGridStore.getState().currentDragCanvasId;
|
||||
if (clientOffset) {
|
||||
handleDrop(item, currentDragCanvasId);
|
||||
}
|
||||
},
|
||||
}),
|
||||
[component.component]
|
||||
);
|
||||
|
|
@ -57,14 +46,80 @@ export const DragLayer = ({ index, component, isModuleTab = false, disabled = fa
|
|||
// ? component.module_container.layouts[currentLayout]
|
||||
// : component.defaultSize || { width: 30, height: 40 };
|
||||
|
||||
const size = component.defaultSize || { width: 30, height: 40 };
|
||||
|
||||
return (
|
||||
<>
|
||||
{isDragging && <CustomDragLayer size={size} />}
|
||||
<div
|
||||
ref={disabled ? undefined : drag}
|
||||
className={`draggable-box${disabled ? ' disabled' : ''}`}
|
||||
style={{ height: '100%', width: isModuleTab && '100%' }}>
|
||||
{isModuleTab ? <ModuleWidgetBox module={component} /> : <WidgetBox index={index} component={component} />}
|
||||
style={{ height: '100%', width: isModuleTab && '100%' }}
|
||||
>
|
||||
{isModuleTab ? (
|
||||
<ModuleWidgetBox module={component} disabled={disabled} />
|
||||
) : (
|
||||
<WidgetBox index={index} component={component} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const CustomDragLayer = ({ size }) => {
|
||||
const { currentOffset, item } = useDragLayer((monitor) => ({
|
||||
currentOffset: monitor.getSourceClientOffset(),
|
||||
item: monitor.getItem(),
|
||||
}));
|
||||
console.log(currentOffset, 'currentOffset');
|
||||
if (!currentOffset) return null;
|
||||
|
||||
const canvasWidth = item?.canvasWidth;
|
||||
const canvasBounds = item?.canvasRef?.getBoundingClientRect();
|
||||
const height = size.height;
|
||||
|
||||
const appCanvasWidth = document.getElementById('real-canvas')?.offsetWidth || 0;
|
||||
|
||||
// Calculate width based on the app canvas's grid
|
||||
let width = (appCanvasWidth * size.width) / NO_OF_GRIDS;
|
||||
|
||||
// Calculate position relative to the current canvas (parent or child)
|
||||
const left = currentOffset.x - (canvasBounds?.left || 0);
|
||||
const top = currentOffset.y - (canvasBounds?.top || 0);
|
||||
|
||||
// Ensure width doesn't exceed the current container's width
|
||||
if (width > canvasWidth) {
|
||||
width = canvasWidth;
|
||||
}
|
||||
|
||||
// Snap width to grid (round to nearest grid unit)
|
||||
const gridUnitWidth = canvasWidth / NO_OF_GRIDS;
|
||||
const gridUnits = Math.round(width / gridUnitWidth);
|
||||
width = gridUnits * gridUnitWidth;
|
||||
|
||||
const [x, y] = snapToGrid(canvasWidth, left, top);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
pointerEvents: 'none',
|
||||
left: canvasBounds?.left || 0,
|
||||
top: canvasBounds?.top || 0,
|
||||
height: `${height}px`,
|
||||
width: `${width}px`,
|
||||
zIndex: -1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
transform: `translate(${x}px, ${y}px)`,
|
||||
background: '#D9E2FC',
|
||||
opacity: '0.7',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
outline: '1px solid #4af',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
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,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
import { useRef } from 'react';
|
||||
import { useGridStore } from '@/_stores/gridStore';
|
||||
import { NO_OF_GRIDS, GRID_HEIGHT } from '@/AppBuilder/AppCanvas/appCanvasConstants';
|
||||
import { snapToGrid } from '@/AppBuilder/AppCanvas/appCanvasUtils';
|
||||
|
||||
export const useGhostMoveable = () => {
|
||||
const ghostElementRef = useRef(null);
|
||||
const isActiveRef = useRef(false);
|
||||
|
||||
const getMoveableRef = useGridStore((state) => state.moveableRef);
|
||||
const setVirtualTarget = useGridStore((state) => state.actions.setVirtualTarget);
|
||||
|
||||
const createGhostElement = (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 updateGhostPosition = (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;
|
||||
|
||||
// Apply grid snapping
|
||||
// const gridWidth = canvasRef.current.offsetWidth / NO_OF_GRIDS;
|
||||
// const snappedX = Math.round(relativeX / gridWidth) * gridWidth;
|
||||
// const snappedY = Math.round(relativeY / GRID_HEIGHT) * GRID_HEIGHT;
|
||||
ghostElementRef.current.style.transform = `translate(${relativeX}px, ${relativeY}px)`;
|
||||
};
|
||||
|
||||
const activateGhost = (componentSize, mousePosition, canvasRef) => {
|
||||
if (isActiveRef.current) return;
|
||||
|
||||
isActiveRef.current = true;
|
||||
|
||||
const ghost = createGhostElement(componentSize, canvasRef);
|
||||
if (ghost && mousePosition) {
|
||||
updateGhostPosition(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 deactivateGhost = () => {
|
||||
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 {
|
||||
activateGhost,
|
||||
deactivateGhost,
|
||||
};
|
||||
};
|
||||
|
|
@ -10,12 +10,10 @@ const initialState = {
|
|||
lastCanvasClickPosition: null,
|
||||
temporaryLayouts: {},
|
||||
draggingComponentId: null,
|
||||
resizingComponentId: null,
|
||||
reorderContainerChildren: {
|
||||
containerId: null,
|
||||
triggerUpdate: 0,
|
||||
},
|
||||
shouldPreventDrop: false,
|
||||
};
|
||||
|
||||
export const createGridSlice = (set, get) => ({
|
||||
|
|
@ -37,7 +35,6 @@ export const createGridSlice = (set, get) => ({
|
|||
get().toggleCanvasUpdater();
|
||||
}, 200),
|
||||
setDraggingComponentId: (id) => set(() => ({ draggingComponentId: id })),
|
||||
setResizingComponentId: (id) => set(() => ({ resizingComponentId: id })),
|
||||
moveComponentPosition: (direction) => {
|
||||
const { setComponentLayout, currentLayout, getSelectedComponentsDefinition, debouncedToggleCanvasUpdater } = get();
|
||||
let layouts = {};
|
||||
|
|
@ -473,7 +470,4 @@ export const createGridSlice = (set, get) => ({
|
|||
reorderContainerChildren: { containerId, triggerUpdate: state.reorderContainerChildren.triggerUpdate + 1 },
|
||||
}));
|
||||
},
|
||||
setShouldPreventDrop: (shouldPreventDrop) => {
|
||||
set(() => ({ shouldPreventDrop }));
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,15 +12,11 @@ const initialState = {
|
|||
idGroupDragged: false,
|
||||
openModalWidgetId: null,
|
||||
subContainerWidths: {},
|
||||
moveableRef: null,
|
||||
virtualTarget: null,
|
||||
currentDragCanvasId: null,
|
||||
ghostDragPosition: null,
|
||||
};
|
||||
|
||||
export const useGridStore = create(
|
||||
zustandDevTools(
|
||||
(set, get) => ({
|
||||
(set) => ({
|
||||
...initialState,
|
||||
actions: {
|
||||
setResizingComponentId: (id) => set({ resizingComponentId: id }),
|
||||
|
|
@ -30,22 +26,7 @@ export const useGridStore = create(
|
|||
setOpenModalWidgetId: (openModalWidgetId) => set({ openModalWidgetId }),
|
||||
setSubContainerWidths: (id, width) =>
|
||||
set((state) => ({ subContainerWidths: { ...state.subContainerWidths, [id]: width } })),
|
||||
setVirtualTarget: (target) => set({ virtualTarget: target }),
|
||||
setCurrentDragCanvasId: (canvasId) => set({ currentDragCanvasId: canvasId }),
|
||||
setGhostDragPosition: (position) => set({ ghostDragPosition: position }),
|
||||
},
|
||||
addToElementGuidelines: (selector) =>
|
||||
set((state) => ({
|
||||
dynamicElementGuidelines: [...state.dynamicElementGuidelines, selector],
|
||||
})),
|
||||
removeFromElementGuidelines: (selector) =>
|
||||
set((state) => ({
|
||||
dynamicElementGuidelines: state.dynamicElementGuidelines.filter((item) => item !== selector),
|
||||
})),
|
||||
clearDynamicElementGuidelines: () => set({ dynamicElementGuidelines: [] }),
|
||||
setMoveableRef: (ref) => set({ moveableRef: ref }),
|
||||
|
||||
getGhostDragPosition: () => get().ghostDragPosition,
|
||||
}),
|
||||
{ name: 'Grid Store' }
|
||||
)
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ const BaseColorSwatches = ({
|
|||
);
|
||||
};
|
||||
const ColorPickerInputBox = () => {
|
||||
console.log('onReset', onReset);
|
||||
return (
|
||||
<div
|
||||
className="row mx-0 color-picker-input d-flex"
|
||||
|
|
|
|||
Loading…
Reference in a new issue