diff --git a/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx b/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx index aaedf9deb9..45835c39de 100644 --- a/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx +++ b/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx @@ -48,7 +48,6 @@ export const ConfigHandle = ({ }, shallow); let height = visibility === false ? 10 : widgetHeight; - return (
{ diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx index 174c68b475..7043c78774 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Grid/Grid.jsx @@ -23,8 +23,6 @@ import { handleDeactivateTargets, handleActivateNonDraggingComponents, } from './gridUtils'; -import { useAppVersionStore } from '@/_stores/appVersionStore'; -import { resolveWidgetFieldValue } from '@/_helpers/utils'; import { dragContextBuilder, getAdjustedDropPosition } from './helpers/dragEnd'; import useStore from '@/AppBuilder/_stores/store'; import './Grid.css'; @@ -67,6 +65,8 @@ export default function Grid({ gridWidth, currentLayout }) { const prevDragParentId = useRef(null); const newDragParentId = useRef(null); const [isGroupDragging, setIsGroupDragging] = useState(false); + const checkIfAnyWidgetVisibilityChanged = useStore((state) => state.checkIfAnyWidgetVisibilityChanged(), shallow); + const getExposedValueOfComponent = useStore((state) => state.getExposedValueOfComponent, shallow); const setReorderContainerChildren = useStore((state) => state.setReorderContainerChildren, shallow); useEffect(() => { @@ -319,7 +319,7 @@ export default function Grid({ gridWidth, currentLayout }) { useEffect(() => { reloadGrid(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedComponents, openModalWidgetId, boxList, currentLayout]); + }, [selectedComponents, openModalWidgetId, boxList, currentLayout, checkIfAnyWidgetVisibilityChanged]); const updateNewPosition = (events, parent = null) => { const posWithParent = { @@ -331,6 +331,8 @@ export default function Grid({ gridWidth, currentLayout }) { const isComponentVisible = (id) => { const component = getResolvedComponent(id); + const componentExposedVisibility = getExposedValueOfComponent(id)?.isVisible; + if (componentExposedVisibility === false) return false; let visibility; if (isArray(component)) { visibility = component?.[0]?.properties?.visibility ?? component?.[0]?.styles?.visibility ?? null; @@ -633,13 +635,13 @@ export default function Grid({ gridWidth, currentLayout }) { // When clicked on widget boundary/resizer, select the component setSelectedComponents([e.target.id]); } - showGridLines(); if (!isComponentVisible(e.target.id)) { return false; } handleActivateNonDraggingComponents(); useGridStore.getState().actions.setResizingComponentId(e.target.id); e.setMin([gridWidth, GRID_HEIGHT]); + showGridLines(); }} onResizeEnd={(e) => { try { @@ -648,10 +650,12 @@ export default function Grid({ gridWidth, currentLayout }) { return id === e.target.id; }); hideGridLines(); + if (!e.lastEvent) { + return; + } let _gridWidth = useGridStore.getState().subContainerWidths[currentWidget.component?.parent] || gridWidth; let width = Math.round(e?.lastEvent?.width / _gridWidth) * _gridWidth; const height = Math.round(e?.lastEvent?.height / GRID_HEIGHT) * GRID_HEIGHT; - const currentWidth = currentWidget.width * _gridWidth; const diffWidth = e.lastEvent?.width - currentWidth; const diffHeight = e.lastEvent?.height - currentWidget?.height; @@ -882,7 +886,6 @@ export default function Grid({ gridWidth, currentLayout }) { left = dragged.left * sourcegridWidth; top = dragged.top; - !isModalToCanvas ?? toast.error(`${dragged.widgetType} is not compatible as a child component of ${target.widgetType}`); } diff --git a/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx b/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx index 1116c3fb42..7862db5676 100644 --- a/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx +++ b/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx @@ -7,6 +7,7 @@ import { renderTooltip } from '@/_helpers/appUtils'; import { useTranslation } from 'react-i18next'; import ErrorBoundary from '@/_ui/ErrorBoundary'; import { BOX_PADDING } from './appCanvasConstants'; + const shouldAddBoxShadowAndVisibility = [ 'Table', 'TextInput', diff --git a/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx b/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx index 0384d4cc6e..df78a8afa4 100644 --- a/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx +++ b/frontend/src/AppBuilder/AppCanvas/WidgetWrapper.jsx @@ -37,6 +37,8 @@ const WidgetWrapper = memo( }); const visibility = useStore((state) => { const component = state.getResolvedComponent(id, subContainerIndex); + const componentExposedVisibility = state.getExposedValueOfComponent(id)?.isVisible; + if (componentExposedVisibility === false) return false; if (component?.properties?.visibility === false || component?.styles?.visibility === false) return false; return true; }); @@ -52,7 +54,7 @@ const WidgetWrapper = memo( height: visibility === false ? '10px' : `${height}px`, transform: `translate(${layoutData.left * gridWidth}px, ${layoutData.top}px)`, WebkitFontSmoothing: 'antialiased', - border: visibility === false ? `1px solid var(--border-default)` : 'none', + border: visibility === false && mode === 'edit' ? `1px solid var(--border-default)` : 'none', }; if (!componentType) return null; diff --git a/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js b/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js index 8d5f2dfe62..913d3a22df 100644 --- a/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js +++ b/frontend/src/AppBuilder/AppCanvas/appCanvasUtils.js @@ -49,8 +49,16 @@ export const addNewWidgetToTheEditor = (componentType, eventMonitorObject, curre left = Math.round(left / gridWidth); // Adjust widget width based on the dropping canvas width const mainCanvasWidth = useGridStore.getState().subContainerWidths['canvas']; - const width = Math.round((defaultWidth * mainCanvasWidth) / gridWidth); + let width = Math.round((defaultWidth * mainCanvasWidth) / gridWidth); + // Ensure minimum width + width = Math.max(width, 1); + + // Adjust position and width if exceeding grid bounds + if (width + left > NO_OF_GRIDS) { + 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}}`; @@ -513,7 +521,7 @@ export function pasteComponents(targetParentId, copiedComponentObj) { targetParentId === key || (components?.[key]?.component.component === 'Tabs' && targetParentId?.split('-')?.slice(0, -1)?.join('-') === key) || - (['Container', 'Form', 'Modal'].includes(components?.[key]?.component.component) && + (['Container', 'Form', 'ModalV2'].includes(components?.[key]?.component.component) && ['header', 'footer'].some((section) => targetParentId.includes(section))) ) ) { @@ -525,6 +533,7 @@ export function pasteComponents(targetParentId, copiedComponentObj) { } pastedComponents.forEach((component) => { + component = deepClone(component); const newComponentId = isCut ? component.id : uuidv4(); const componentName = computeComponentName(component.component.component, { ...components, @@ -565,15 +574,28 @@ export function pasteComponents(targetParentId, copiedComponentObj) { componentData.definition.others.showOnMobile.value = currentLayout === 'mobile' ? `{{true}}` : `{{false}}`; // Adjust width if parent changed - let width = component.layouts.desktop.width; + let width = component.layouts[currentLayout].width; if (targetParentId !== component.component?.parent) { const containerWidth = useGridStore.getState().subContainerWidths[targetParentId || 'canvas']; const oldContainerWidth = useGridStore.getState().subContainerWidths[component?.component?.parent || 'canvas']; width = Math.round((width * oldContainerWidth) / containerWidth); + + // Ensure minimum width + width = Math.max(width, 1); + + // Adjust position and width if exceeding grid bounds + if (width + component.layouts[currentLayout].left > NO_OF_GRIDS) { + component.layouts[currentLayout].left = Math.max(0, NO_OF_GRIDS - width); + width = Math.min(width, NO_OF_GRIDS); + } } - component.layouts[currentLayout].width = width; + component.layouts[currentLayout] = { + ...component.layouts[currentLayout], + width, + }; + const newComponent = { component: { ...componentData, diff --git a/frontend/src/AppBuilder/AppCanvas/selecto.scss b/frontend/src/AppBuilder/AppCanvas/selecto.scss index 5602b35d5a..7366e2fd06 100644 --- a/frontend/src/AppBuilder/AppCanvas/selecto.scss +++ b/frontend/src/AppBuilder/AppCanvas/selecto.scss @@ -1,5 +1,5 @@ .active-target { - outline: 1px solid #4af; + outline: 1px solid #4af !important; } .main-editor-canvas .widget-target:not(:has(.widget-target:hover)):hover { diff --git a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/DragLayer.jsx b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/DragLayer.jsx index 77274cd658..9589ac145b 100644 --- a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/DragLayer.jsx +++ b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/DragLayer.jsx @@ -42,12 +42,18 @@ const CustomDragLayer = ({ size }) => { const canvasBounds = item?.canvasRef?.getBoundingClientRect(); const height = size.height; - const width = (canvasWidth * size.width) / NO_OF_GRIDS; + const mainCanvasWidth = document.getElementById('real-canvas')?.offsetWidth || 0; + let width = (mainCanvasWidth * 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); + // Adjust position and width if exceeding grid bounds + if (width >= canvasWidth) { + width = canvasWidth; + } + const [x, y] = snapToGrid(canvasWidth, left, top); return (
{ + const canvasElement = document.querySelector('.page-container.canvas-container'); + const realCanvasEl = document.getElementsByClassName('real-canvas')[0]; + const allModalContainers = realCanvasEl.querySelectorAll('.modal'); + const modalContainer = allModalContainers[allModalContainers.length - 1]; + + if (canvasElement && realCanvasEl && modalContainer) { + const currentScroll = canvasElement.scrollTop; + canvasElement.style.overflowY = 'hidden'; + + modalContainer.style.height = `${canvasElement.offsetHeight}px`; + modalContainer.style.top = `${currentScroll}px`; + fireEvent('onOpen'); + } + }; + + const onHideSideEffects = () => { + const canvasElement = document.querySelector('.page-container.canvas-container'); + const realCanvasEl = document.getElementsByClassName('real-canvas')[0]; + const allModalContainers = realCanvasEl.querySelectorAll('.modal'); + const modalContainer = allModalContainers[allModalContainers.length - 1]; + const hasManyModalsOpen = allModalContainers.length > 1; + + if (canvasElement && realCanvasEl && modalContainer) { + modalContainer.style.height = ``; + modalContainer.style.top = ``; + fireEvent('onClose'); + } + if (canvasElement && !hasManyModalsOpen) { + canvasElement.style.overflow = 'auto'; + } + }; + + // useEventListener('resize', onShowSideEffects, window); + + const onShowModal = () => { + openModal(); + onShowSideEffects(); + }; + + const onHideModal = () => { + onHideSideEffects(); + hideModal(); + }; + useEffect(() => { const exposedVariables = { open: async function () { @@ -93,58 +141,48 @@ export const Modal = function Modal({ setShowModal(true); } + // Add debounced version of handleModalOpen + const debouncedModalOpen = debounce(() => { + onShowSideEffects(); + }, 10); + useEffect(() => { - const handleModalOpen = () => { - openModal(); - const canvasElement = document.getElementsByClassName('canvas-container')[0]; - const modalBackdropEl = document.getElementsByClassName('modal-backdrop')[0]; - const realCanvasEl = document.getElementsByClassName('real-canvas')[0]; - const modalCanvasEl = document.getElementById(`canvas-${id}`); - if (canvasElement && modalBackdropEl && modalCanvasEl && realCanvasEl) { - realCanvasEl.style.height = '100vh'; - realCanvasEl.style.position = 'absolute'; - realCanvasEl.style.overflow = 'hidden'; + // Select the DOM element + const canvasElement = document.querySelector('.page-container.canvas-container'); - modalBackdropEl.style.height = '100vh'; - modalBackdropEl.style.minHeight = '100vh'; - modalBackdropEl.style.minHeight = '100vh'; - modalCanvasEl.style.height = modalHeight; - } + if (!canvasElement) return; // Ensure the element exists + + // Create a ResizeObserver + const resizeObserver = new ResizeObserver(() => { + debouncedModalOpen(); + }); + + // Observe the canvas element + resizeObserver.observe(canvasElement); + + return () => { + // Cleanup observer on component unmount + resizeObserver.disconnect(); }; + }, []); - // Add debounced version of handleModalOpen - const debouncedModalOpen = debounce(() => { - handleModalOpen(); - }, 10); - - const handleModalClose = () => { - const canvasElement = document.getElementsByClassName('canvas-container')[0]; - const realCanvasEl = document.getElementsByClassName('real-canvas')[0]; - const canvasHeight = realCanvasEl?.getAttribute('canvas-height'); - - if (canvasElement && realCanvasEl && canvasHeight) { - realCanvasEl.style.height = canvasHeight; - realCanvasEl.style.position = ''; - - realCanvasEl.style.overflow = 'auto'; - } - }; + useEffect(() => { if (showModal) { debouncedModalOpen(); } else { - // if (document.getElementsByClassName('modal-content')[0] == undefined) { - handleModalClose(); - // } + if (document.getElementsByClassName('modal-content')[0] == undefined) { + onHideModal(); + } } // Cleanup the effect return () => { if (document.getElementsByClassName('modal-content')[0] == undefined) { - handleModalClose(); + onHideModal(); } }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [showModal, modalHeight]); + }, [modalHeight, size]); useEffect(() => { if (isInitialRender.current) { @@ -245,7 +283,9 @@ export const Modal = function Modal({ keyboard={true} enforceFocus={false} animation={false} - onEscapeKeyDown={() => hideOnEsc && hideModal()} + onShow={() => onShowModal()} + onHide={() => onHideModal()} + onEscapeKeyDown={() => hideOnEsc && onHideModal()} id="modal-container" component-id={id} backdrop={'static'} @@ -258,7 +298,7 @@ export const Modal = function Modal({ titleAlignment, hideTitleBar, hideCloseButton, - hideModal, + hideModal: onHideModal, component, showConfigHandler: mode === 'edit', }} diff --git a/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/Markdown.jsx b/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/Markdown.jsx index a8bb593409..7ea9a7b3b0 100644 --- a/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/Markdown.jsx +++ b/frontend/src/AppBuilder/Widgets/NewTable/_components/DataTypes/Markdown.jsx @@ -85,7 +85,6 @@ export const MarkdownColumn = ({ className={`h-100 text-container long-text-input d-flex${ darkMode ? ' textarea-dark-theme' : '' } justify-content-${determineJustifyContentValue(horizontalAlignment)}`} - tabIndex={-1} style={{ color: cellTextColor ? cellTextColor : 'inherit', outline: 'none', diff --git a/frontend/src/AppBuilder/_stores/slices/gridSlice.js b/frontend/src/AppBuilder/_stores/slices/gridSlice.js index 37de5cf81a..8b2f61bd9a 100644 --- a/frontend/src/AppBuilder/_stores/slices/gridSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/gridSlice.js @@ -77,6 +77,22 @@ export const createGridSlice = (set, get) => ({ setLastCanvasClickPosition: (position) => { set({ lastCanvasClickPosition: position }); }, + checkIfAnyWidgetVisibilityChanged: () => { + // This is required to reload the grid if visibility is turned off using CSA + const { getExposedValueOfComponent, getCurrentPageComponents } = get(); + const currentPageComponents = getCurrentPageComponents(); + + const visibilityState = {}; + + Object.keys(currentPageComponents).forEach((componentId) => { + const componentExposedVisibility = getExposedValueOfComponent(componentId)?.isVisible; + + // Determine if component is visible + visibilityState[componentId] = !(componentExposedVisibility === false); + }); + + return visibilityState; + }, setReorderContainerChildren: (containerId) => { // Function to trigger reordering of specific container for tab navigation set((state) => ({