[feat] : Highlight when the component will be placed inside the container based components

This commit is contained in:
Nakul Nagargade 2025-02-27 15:04:25 +05:30
parent 3af5166422
commit 7b0fda5037
6 changed files with 135 additions and 65 deletions

View file

@ -1,17 +1,6 @@
.target, .nested-target {
position: absolute;
/* width: 100px;
height: 100px; */
/* top: 150px;
left: 100px; */
/* line-height: 100px; */
/* text-align: center; */
/* background: #ee8; */
/* color: #333; */
/* font-weight: bold; */
box-sizing: border-box;
/* transition: transform 0.1s; */
/* z-index: 3001; */
}
.target.hovered{
@ -76,43 +65,6 @@
background: #8DA4EF !important;
}
/* Hides all the control lines*/
/* .moveable-line {
color: transparent !important;
--moveable-color: transparent !important;
}
.moveable-control {
visibility: hidden;
}
.target {
outline: 1px solid #4af;
} */
.main-editor-canvas .widget-target:not(:has(.widget-target:hover)):hover {
outline: 1px solid #4af;
z-index: 4 !important;
}
.main-editor-canvas .widget-target:has(.nested-target:hover):hover {
outline: 0px solid #4af;
}
.main-editor-canvas .nested-target:not(:has(.nested-target:hover)):hover {
outline: 1px solid #4af;
z-index: 4 !important;
}
.active-target, .resizing-target {
outline: 1px solid #4af !important;
/* z-index: 1000000 !important; */
}
.moveable-control-box:not([data-able-groupable]) .moveable-control-box:not(:hover) {
opacity: 0;
}
@ -141,10 +93,6 @@
height: 0px !important;
}
.resizing-target * {
opacity: 0;
}
.moveable-control {
width: 8px !important;
@ -210,4 +158,19 @@
.moveable-guideline-group {
z-index: 9999;
}
}
.dragging-component-canvas {
outline: 1px solid var(--border-accent-strong) !important;
outline-offset: 0px; /* Creates space between element and outline */
z-index: 999 !important;
}
.non-dragging-component {
outline: 1px dotted var(--border-accent-weak) !important;
outline-offset: 0px; /* Creates space between element and outline */
z-index: 999 !important;
}

View file

@ -19,6 +19,9 @@ import {
adjustWidth,
hideGridLines,
showGridLines,
handleActivateTargets,
handleDeactivateTargets,
handleActivateNonDraggingComponents,
} from './gridUtils';
import { useAppVersionStore } from '@/_stores/appVersionStore';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
@ -580,7 +583,7 @@ export default function Grid({ gridWidth, currentLayout }) {
} else {
document.getElementById('real-canvas').classList.add('show-grid');
}
handleActivateTargets(currentWidget.component?.parent);
const currentWidth = currentWidget.width * _gridWidth;
const diffWidth = e.width - currentWidth;
const diffHeight = e.height - currentWidget.height;
@ -632,6 +635,7 @@ export default function Grid({ gridWidth, currentLayout }) {
if (!isComponentVisible(e.target.id)) {
return false;
}
handleActivateNonDraggingComponents();
useGridStore.getState().actions.setResizingComponentId(e.target.id);
e.setMin([gridWidth, GRID_HEIGHT]);
}}
@ -695,17 +699,19 @@ export default function Grid({ gridWidth, currentLayout }) {
} catch (error) {
console.error('ResizeEnd error ->', error);
}
handleDeactivateTargets();
setDragParentId(null);
toggleCanvasUpdater();
}}
onResizeGroupStart={({ events }) => {
showGridLines();
handleActivateNonDraggingComponents();
}}
onResizeGroup={({ events }) => {
const parentElm = events[0].target.closest('.real-canvas');
const parentWidth = parentElm?.clientWidth;
const parentHeight = parentElm?.clientHeight;
handleActivateTargets(parentElm?.id?.replace('canvas-', ''));
const { posRight, posLeft, posTop, posBottom } = getPositionForGroupDrag(events, parentWidth, parentHeight);
events.forEach((ev) => {
ev.target.style.width = `${ev.width}px`;
@ -774,6 +780,7 @@ export default function Grid({ gridWidth, currentLayout }) {
} catch (error) {
console.error('Error resizing group', error);
}
handleDeactivateTargets();
toggleCanvasUpdater();
}}
checkInput
@ -784,6 +791,7 @@ export default function Grid({ gridWidth, currentLayout }) {
}
newDragParentId.current = boxList.find((box) => box.id === e.target.id)?.parent;
e?.moveable?.controlBox?.removeAttribute('data-off-screen');
const box = boxList.find((box) => box.id === e.target.id);
// Prevent drag if shift is pressed for SUBCONTAINER_WIDGETS
if (SUBCONTAINER_WIDGETS.includes(box?.component?.component) && e.inputEvent.shiftKey) {
@ -817,7 +825,6 @@ export default function Grid({ gridWidth, currentLayout }) {
container.contains(e.inputEvent.target)
);
}
if (['RangeSlider', 'BoundedBox'].includes(box?.component?.component) || isDragOnInnerElement) {
const targetElems = document.elementsFromPoint(e.clientX, e.clientY);
const isHandle = targetElems.find((ele) => ele.classList.contains('handle-content'));
@ -825,8 +832,10 @@ export default function Grid({ gridWidth, currentLayout }) {
return false;
}
}
handleActivateNonDraggingComponents();
}}
onDragEnd={(e) => {
handleDeactivateTargets();
try {
if (isDraggingRef.current) {
useStore.getState().setDraggingComponentId(null);
@ -961,6 +970,7 @@ export default function Grid({ gridWidth, currentLayout }) {
setDragParentId(newParentId === 'canvas' ? null : newParentId);
newDragParentId.current = newParentId === 'canvas' ? null : newParentId;
prevDragParentId.current = newParentId;
handleActivateTargets(newParentId);
}
}
// Postion ghost element exactly as same at dragged element
@ -987,14 +997,17 @@ export default function Grid({ gridWidth, currentLayout }) {
ev.target.style.transform = `translate(${left}px, ${top}px)`;
});
handleActivateTargets(parentElm?.id?.replace('canvas-', ''));
updateNewPosition(events);
}}
onDragGroupStart={({ events }) => {
showGridLines();
setIsGroupDragging(true);
handleActivateNonDraggingComponents();
}}
onDragGroupEnd={(e) => {
handleDragGroupEnd(e);
handleDeactivateTargets();
toggleCanvasUpdater();
}}
onClickGroup={(e) => {

View file

@ -1,7 +1,7 @@
import { useGridStore } from '@/_stores/gridStore';
import { isEmpty } from 'lodash';
import useStore from '@/AppBuilder/_stores/store';
import { getTabId, getSubContainerIdWithSlots } from '../appCanvasUtils';
export function correctBounds(layout, bounds) {
layout = scaleLayouts(layout);
const collidesWith = [];
@ -414,3 +414,77 @@ export function hideGridLines() {
document.getElementById('real-canvas')?.classList.remove('show-grid');
document.getElementById('real-canvas')?.classList.add('hide-grid');
}
// Track previously active elements for efficient cleanup
let previousActiveWidgets = null;
let previousActiveCanvas = null;
export const handleActivateNonDraggingComponents = () => {
// Only add non-dragging class to visible components in viewport
document.querySelectorAll('.moveable-box:not(.active-target)').forEach((component) => {
// Check if element is visible in viewport
const rect = component.getBoundingClientRect();
const isVisible =
rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
if (isVisible) {
component.classList.add('non-dragging-component');
}
});
};
export const handleActivateTargets = (parentId) => {
const WIDGETS_WITH_CANVAS_OUTLINE = ['Container', 'Modal', 'Form', 'Listview', 'Kanban'];
const newParentType = document.getElementById('canvas-' + parentId)?.getAttribute('component-type');
let _parentId = parentId;
if (newParentType === 'Tabs') {
_parentId = getTabId(parentId);
} else if (WIDGETS_WITH_CANVAS_OUTLINE.includes(newParentType)) {
_parentId = getSubContainerIdWithSlots(parentId);
}
// Clean up previous active elements
if (previousActiveWidgets) {
previousActiveWidgets.classList.remove('dragging-component-canvas');
previousActiveWidgets = null;
}
if (previousActiveCanvas) {
previousActiveCanvas.classList.remove('dragging-component-canvas');
previousActiveCanvas = null;
}
const parentComponent = document.getElementById(_parentId);
if (!parentComponent) return;
if (WIDGETS_WITH_CANVAS_OUTLINE?.includes(newParentType)) {
// If it's multiple canvas in single widget, highlight the specific canvas
const canvasElm = document.getElementById('canvas-' + parentId);
if (canvasElm) {
canvasElm.classList.add('dragging-component-canvas');
previousActiveCanvas = canvasElm;
}
} else {
// Otherwise highlight the component box
parentComponent.classList.remove('non-dragging-component');
parentComponent.classList.add('dragging-component-canvas');
previousActiveWidgets = parentComponent;
}
};
export const handleDeactivateTargets = () => {
if (previousActiveWidgets) {
previousActiveWidgets.classList.remove('dragging-component-canvas');
previousActiveWidgets = null;
}
if (previousActiveCanvas) {
previousActiveCanvas.classList.remove('dragging-component-canvas');
previousActiveCanvas = null;
}
document.querySelectorAll('.non-dragging-component').forEach((component) => {
component.classList.remove('non-dragging-component');
});
};

View file

@ -69,6 +69,7 @@ const WidgetWrapper = memo(
data-id={`${id}`}
id={id}
widgetid={id}
component-type={componentType}
style={{
// zIndex: mode === 'view' && widget.component.component == 'Datepicker' ? 2 : null,
...styles,

View file

@ -690,3 +690,19 @@ export const getParentWidgetFromId = (parentType, parentId) => {
}
return parentType;
};
export const getTabId = (parentId) => {
return parentId.split('-').slice(0, -1).join('-');
};
export const getSubContainerIdWithSlots = (parentId) => {
let cleanParentId = parentId;
if (parentId) {
if (parentId.includes('header')) {
cleanParentId = parentId.replace('-header', '');
} else if (parentId.includes('footer')) {
cleanParentId = parentId.replace('-footer', '');
}
}
return cleanParentId;
};

View file

@ -3,15 +3,18 @@
}
.main-editor-canvas .widget-target:not(:has(.widget-target:hover)):hover {
z-index: 4 !important;
}
.main-editor-canvas .widget-target:has(.nested-target:hover):hover {
outline: 0px solid #4af;
}
.main-editor-canvas .nested-target:not(:has(.nested-target:hover)):hover {
outline: 1px solid #4af;
z-index: 4 !important;
}
.main-editor-canvas .nested-target:not(:has(.nested-target:hover)):hover {
// outline: 1px solid #4af;
z-index: 4 !important;
}
// .main-editor-canvas .widget-target:hover {
// outline: 1px solid #4af;
// }